Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
2ae7f57
feat: initial release of Peekmem, a terminal client for PyMemoryEditor
JeanExtreme002 Aug 29, 2026
51dbf1a
chore: add the project's community and tooling files
JeanExtreme002 Aug 29, 2026
6adeac3
feat(help): generate each command's argument list from its own parser
JeanExtreme002 Aug 29, 2026
863050c
fix(scan): survive a region the target refuses to read, and pin the f…
JeanExtreme002 Aug 29, 2026
e590c74
feat(shell): quit on Ctrl+C at the prompt
JeanExtreme002 Aug 29, 2026
958f079
refactor(commands): organise the 34 commands into a namespace hierarchy
JeanExtreme002 Aug 29, 2026
081a86a
chore: stop tracking .tool-versions
JeanExtreme002 Aug 29, 2026
d0719a5
feat(help): make the help layered, and lift the shell's own commands out
JeanExtreme002 Aug 29, 2026
47a0c80
feat(shell): list a namespace from its bare name, and add 'clear'
JeanExtreme002 Aug 29, 2026
c65a7cf
feat(help): give each namespace a dokku-style help page
JeanExtreme002 Aug 29, 2026
537e03f
refactor(commands): cap the hierarchy at two levels
JeanExtreme002 Aug 29, 2026
15eaf39
refactor(commands): drop the short aliases, and make a namespace only…
JeanExtreme002 Aug 29, 2026
2a8278b
feat(commands): page every listing, and generate the usage line from …
JeanExtreme002 Aug 29, 2026
07099fe
refactor(commands): fold status into version, rename process: to ps: …
JeanExtreme002 Aug 29, 2026
4e4e8bb
docs(shell): drop the exit instruction from the welcome banner
JeanExtreme002 Aug 29, 2026
4f56246
refactor(help): present everything as a command, with ':help' on all …
JeanExtreme002 Aug 29, 2026
62b40ae
docs(help): list every command by its plain name in the overview
JeanExtreme002 Aug 29, 2026
de9f558
docs(help): show names only in the top-level listing
JeanExtreme002 Aug 29, 2026
f21801c
docs(help): advertise 'help <command>' in the overview
JeanExtreme002 Aug 30, 2026
b02212d
docs(help): teach one spelling — 'help <command>' — and say subcommand
JeanExtreme002 Aug 30, 2026
1b98d95
feat(commands): page listings by page number, not by offset
JeanExtreme002 Aug 30, 2026
cac7b3d
feat(shell): dim the attached target in the prompt
JeanExtreme002 Aug 30, 2026
f052420
feat(config): split config into config:list and config:set
JeanExtreme002 Aug 30, 2026
7941024
feat(help): dim the contents of every example block
JeanExtreme002 Aug 30, 2026
5935df6
feat(help): grey the example blocks, a shade lighter than the prompt
JeanExtreme002 Aug 30, 2026
e1210a6
feat(output): lighten both greys, and pick them explicitly
JeanExtreme002 Aug 30, 2026
e84b762
refactor(output): one shade for everything set apart from the output
JeanExtreme002 Aug 30, 2026
a38004d
style(help): widen the gap between a command and its description
JeanExtreme002 Aug 30, 2026
b79baab
feat(alias): add an alias command for naming commands yourself
JeanExtreme002 Aug 30, 2026
fd1baeb
feat(alias): remember aliases between runs
JeanExtreme002 Aug 30, 2026
1a952af
chore: rename the project from Peekmem to Picklock
JeanExtreme002 Aug 30, 2026
da76323
feat(config): remember settings between runs, and add config:reset
JeanExtreme002 Aug 30, 2026
d552dd5
docs(help): cover scan:results in the scanning walkthrough
JeanExtreme002 Aug 30, 2026
592084e
refactor(scan): make every comparison a flag
JeanExtreme002 Aug 30, 2026
644e9e2
refactor(config): config:list always shows the whole table
JeanExtreme002 Aug 30, 2026
28e874a
test: drive every command end-to-end against a real process
JeanExtreme002 Aug 30, 2026
4d4fef2
docs(issue-template): show what attaching prints, and widen the targe…
JeanExtreme002 Aug 30, 2026
62f79a7
feat(cli): make --version print the whole report
JeanExtreme002 Aug 30, 2026
1a7b95f
docs(help): show a scan in the overview's example, and drop a dead im…
JeanExtreme002 Aug 30, 2026
86de668
feat(commands): give the paging flags short forms
JeanExtreme002 Aug 30, 2026
96be54e
fix(memory): read a '#N' row with the type the scan used
JeanExtreme002 Aug 30, 2026
924e376
feat(memory): total up the regions, and name what a macOS TID really is
JeanExtreme002 Aug 30, 2026
a948175
feat(memory): rename dump to hex, watch on ENTER, export results as JSON
JeanExtreme002 Aug 30, 2026
a85c7e5
fix(export): write JSON as UTF-8 rather than escape sequences
JeanExtreme002 Aug 30, 2026
819c6a9
fix(memory): total the memory with access, not every reserved range
JeanExtreme002 Aug 30, 2026
b5ef061
chore(config): raise the max_results default to a million
JeanExtreme002 Aug 30, 2026
27af9bb
docs: rewrite the README plainly
JeanExtreme002 Aug 30, 2026
0e02b13
docs: tighten the README intro
JeanExtreme002 Aug 30, 2026
2786093
docs: show a real terminal session in the README
JeanExtreme002 Aug 30, 2026
e3fc82f
docs: shorten the terminal capture
JeanExtreme002 Aug 30, 2026
ec66fc8
docs: record the capture against a process named for the demo
JeanExtreme002 Aug 30, 2026
d4a61e4
docs: name the demo process game.exe
JeanExtreme002 Aug 30, 2026
a3a3aa0
chore: drop the beta development-status classifier
JeanExtreme002 Aug 30, 2026
c3e0a75
fix(help): memory:watch --count said Ctrl+C stops a watch
JeanExtreme002 Aug 30, 2026
a9a1a3b
docs: publish a documentation site and cut the README down to a landi…
JeanExtreme002 Aug 30, 2026
c4f03ed
docs: drop the command table and the licence line from the README
JeanExtreme002 Aug 30, 2026
f1db104
docs: shorten the note above the PyMemoryEditor dependency
JeanExtreme002 Aug 30, 2026
5fd8c09
fix(scan): --max capped every later scan, not just its own
JeanExtreme002 Aug 30, 2026
4e0afea
test: stop capping the end-to-end scans that assert completeness
JeanExtreme002 Aug 30, 2026
0ca4e33
test: stop three tests asserting one platform's behaviour as if it we…
JeanExtreme002 Aug 30, 2026
048ee51
fix(shell): a Windows path could not be typed at the prompt
JeanExtreme002 Aug 30, 2026
64933b8
fix(screenshot): the drop shadow was cut off at the edge of the image
JeanExtreme002 Aug 30, 2026
87ddc89
docs: describe the output style rather than naming what inspired it
JeanExtreme002 Aug 30, 2026
5e87000
docs: show a ps:list that actually needs picking through
JeanExtreme002 Aug 30, 2026
6f194a6
docs: explain how to find a command without the layers metaphor
JeanExtreme002 Aug 30, 2026
f3e8585
docs: teach one help pattern, not three spellings of it
JeanExtreme002 Aug 30, 2026
7d2af6b
docs: show what a bare ps:list actually prints
JeanExtreme002 Aug 30, 2026
c702191
docs: give Chrome one set of PIDs on the attaching page
JeanExtreme002 Aug 30, 2026
1f22321
refactor(commands): move threads from the memory namespace to ps
JeanExtreme002 Aug 30, 2026
bcec527
docs: allocating was explained twice
JeanExtreme002 Aug 30, 2026
2b5a73f
feat(scan): say when a result set never looked at read-only memory
JeanExtreme002 Aug 30, 2026
a77d6ff
docs: name the example command file setup.txt
JeanExtreme002 Aug 30, 2026
680493f
docs: add a Funding page, as PyMemoryEditor's docs have
JeanExtreme002 Aug 30, 2026
747032d
docs: drop the version number from the command reference heading
JeanExtreme002 Aug 30, 2026
247a54b
fix: four faults found reviewing the shell before merge
JeanExtreme002 Aug 30, 2026
2783a28
fix(paging): --limit accepted a negative number and printed a lie
JeanExtreme002 Aug 30, 2026
f1c94ae
fix(prompt): the target name came from what was typed, not from the OS
JeanExtreme002 Aug 30, 2026
ae816be
docs: put the claim under the image, not a caption about the image
JeanExtreme002 Aug 30, 2026
9992b5d
docs: drop the bold from the line under the image
JeanExtreme002 Aug 30, 2026
eef329d
docs: link Reading and writing from the README
JeanExtreme002 Aug 30, 2026
5a55214
docs: drop the 'pytest -m "not slow"' line from the markdown
JeanExtreme002 Aug 30, 2026
d84c2c3
docs: add Contributing and License sections to the README
JeanExtreme002 Aug 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[flake8]

max-line-length = 130
# E203: whitespace before ':' (black-compatible — black puts spaces around the
# colon in slices like data[i : i + n], which conflicts with PEP 8).
# E701: multiple statements on one line (colon) — used pervasively as a style choice.
# W503: line break before binary operator (black-compatible).
ignore = E203, E701, W503

per-file-ignores =
# __init__.py files are allowed to have unused imports and lines-too-long.
*/__init__.py:F401, E501
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# These are supported funding model platforms

github: JeanExtreme002
45 changes: 45 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
The exact commands you ran, and what happened:

```console
$ picklock
picklock> ps:open 91434
Attached to process.exe (PID 91434, 64-bit). (0.00 sec)
picklock> ...
```

**Expected behavior**
A clear and concise description of what you expected to happen instead.

**Versions**
Paste the output of `picklock --version` — it covers Picklock, PyMemoryEditor,
Python and the platform:

```
Picklock: 0.1.0
PyMemoryEditor: 2.2.0
Python: 3.12.0
Platform: Linux 6.8.0 (x86_64)
```

**Environment**
- Were you running elevated (`sudo` / Administrator)? [yes / no]
- Terminal (e.g. Windows Terminal, iTerm2, GNOME Terminal, plain SSH):
- On Linux, the value of `/proc/sys/kernel/yama/ptrace_scope`:
- Target process (e.g. a game, browser, another script), if that matters:

**Additional context**
Add any other context about the problem here. If the target was a process you
cannot share, a minimal script that reproduces the same behaviour helps a lot.
35 changes: 35 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
- A clear and concise description of what the problem is. Ex. I always have to
type [...] to do [...]
- A clear and concise description of what you want to happen.
- Any alternative commands or workflows you have considered.

**Is not your feature request related to a problem? Please describe**
A clear and concise description of how your feature can positively impact the
project.

**What would it look like?**
If it is a new command or a new flag, sketch the session:

```console
picklock> mycommand 0x1000 --flag
```

**Is this Picklock or PyMemoryEditor?**
Picklock is a client — it parses commands and formats results, while
[PyMemoryEditor](https://github.com/JeanExtreme002/PyMemoryEditor) performs
every read, write and scan. If the feature needs a memory capability that does
not exist yet, it may belong upstream. Say which you think it is; being wrong
is fine, the issue will be routed.

**Additional context**
Add any other context or screenshots about the feature request here.
34 changes: 34 additions & 0 deletions .github/ISSUE_TEMPLATE/questioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
name: Questioning
about: Ask a question about the project
title: ''
labels: question
assignees: ''

---

**Did you check the built-in help? If so, please describe**
Picklock documents itself: `help` lists every command, `help <command>` explains
one in full, and `help types`, `help address` and `help scanning` cover what
several commands share. If one of those was confusing, say which and how — that
is a documentation bug worth fixing.

**Describe your question**
A clear and concise description of what you are trying to do.

**What did you try?**
The commands you ran and the output you got:

```console
picklock> ...
```

**Versions**
Paste the output of `picklock --version`, if applicable.

**Environment**
- Were you running elevated (`sudo` / Administrator)? [yes / no]
- On Linux, the value of `/proc/sys/kernel/yama/ptrace_scope`:

**Additional context**
Add any other context about your question here.
21 changes: 21 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
#
# `open-pull-requests-limit: 0` disables routine version-update PRs (no weekly
# bump churn). Dependabot security advisories still surface via the Security
# tab and security-update PRs are unaffected by this limit, so a vulnerability
# in PyMemoryEditor or an action remains visible.

version: 2

updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 0

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 0
48 changes: 48 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Path-based labels applied to PRs by .github/workflows/labeler.yml.
# Uses actions/labeler@v5 match rules.

# The command layer — where a new command or a change to an existing one lands.
commands:
- changed-files:
- any-glob-to-any-file:
- "picklock/commands/**"

# The shell itself: line parsing, dispatch, readline, the CLI front end.
shell:
- changed-files:
- any-glob-to-any-file:
- "picklock/shell.py"
- "picklock/cli.py"

# Everything that decides how a result looks on screen.
output:
- changed-files:
- any-glob-to-any-file:
- "picklock/output.py"
- "picklock/valuetypes.py"

# Any change inside the package.
core:
- changed-files:
- any-glob-to-any-file:
- "picklock/**"

tests:
- changed-files:
- any-glob-to-any-file:
- "tests/**"

docs:
- changed-files:
- any-glob-to-any-file:
- "README.md"
- "CONTRIBUTING.md"
- "SECURITY.md"
- "CODE_OF_CONDUCT.md"

ci:
- changed-files:
- any-glob-to-any-file:
- ".github/**"
- "Makefile"
- "codecov.yml"
27 changes: 27 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
**Why is this PR necessary, what does it do?**

<!--
IMPORTANT! Explain the **motivation** for making this change: what existing
problem the pull request solves or what new features it implements.
-->

**Checklist (complete all items)**:

- [ ] Added tests as necessary.
- [ ] There is no breaking change for existing features.

**References:**

<!-- (Nice to have)
Link tasks, issues, PRs that are related to this pull request, etc.
-->

No references to be shared.

**Notes:**

<!-- (Optional)
Explain some changes that can be useful when reviewing this pull request.
-->

No notes to be shared.
41 changes: 41 additions & 0 deletions .github/workflows/delete-pr-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Delete PR branch

on:
pull_request:
types: [closed]

permissions:
contents: write

jobs:
delete:
name: Delete head branch after PR close
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const ref = pr.head.ref;

const protectedBranches = new Set(["main", "gh-pages"]);
if (protectedBranches.has(ref)) {
core.info(`Refusing to delete protected branch: ${ref}`);
return;
}

try {
await github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${ref}`,
});
core.info(`Deleted branch: ${ref}`);
} catch (err) {
if (err.status === 422 || err.status === 404) {
core.info(`Branch already gone: ${ref}`);
return;
}
throw err;
}
27 changes: 27 additions & 0 deletions .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Labeler

on:
pull_request_target:
types:
- opened
- synchronize
- reopened

concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write

jobs:
label:
name: Label PR by changed files
runs-on: ubuntu-latest
steps:
- name: Apply labels
uses: actions/labeler@v5
with:
configuration-path: .github/labeler.yml
sync-labels: true
46 changes: 46 additions & 0 deletions .github/workflows/lint-pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Lint PR title

on:
pull_request_target:
types:
- opened
- edited
- synchronize
- reopened

concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
cancel-in-progress: true

permissions:
pull-requests: read

jobs:
lint:
name: Conventional commit title
runs-on: ubuntu-latest
steps:
- name: Lint PR title
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Conventional commit types accepted in titles. Mirrors the set the
# PR labeler recognizes in .github/workflows/labeler.yml.
types: |
feat
fix
perf
refactor
revert
docs
ci
build
chore
test
style
# Subject must start lowercase and not end with a period.
subjectPattern: ^(?![A-Z])(?!.*\.$).+$
subjectPatternError: |
The subject "{subject}" found in "{title}" must start with a
lowercase letter and must not end with a period.
28 changes: 28 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Publish to PyPI

on:
release:
types: [published]

jobs:
publish:
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # Required for trusted publishing (OIDC)
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install build tools
run: python -m pip install --upgrade pip build

- name: Build package
run: python -m build

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
Loading
Loading