@henols/c64-re-tools 1.1.0 → 2.0.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +154 -37
- package/distribution/package.ts +21 -0
- package/distribution/plugin.ts +122 -0
- package/package.json +34 -40
- package/skills/c64-assembler/SKILL.md +76 -0
- package/skills/c64-assembler/scripts/assemble.ts +110 -0
- package/skills/c64-assembler/scripts/compare.ts +84 -0
- package/skills/c64-basic/SKILL.md +66 -0
- package/skills/c64-basic/scripts/basic.ts +46 -0
- package/skills/c64-disk/SKILL.md +93 -0
- package/skills/c64-disk/scripts/containers.ts +124 -0
- package/skills/c64-disk/scripts/disk.ts +120 -0
- package/skills/c64-emulator/SKILL.md +134 -0
- package/skills/c64-knowledge/SKILL.md +192 -0
- package/skills/c64-knowledge/scripts/knowledge.ts +613 -0
- package/skills/c64-memory-map/SKILL.md +79 -0
- package/skills/c64-memory-map/scripts/memmap.ts +92 -0
- package/skills/c64-provenance/SKILL.md +110 -0
- package/skills/c64-provenance/scripts/compare.ts +152 -0
- package/skills/c64-provenance/scripts/provenance.ts +334 -0
- package/skills/c64-provenance/scripts/runtime.ts +191 -0
- package/skills/c64-provenance/scripts/separate.ts +140 -0
- package/skills/c64-reverse-engineering/SKILL.md +144 -0
- package/skills/c64-source/SKILL.md +133 -0
- package/skills/c64-source/scripts/generate.ts +503 -0
- package/skills/c64-source/scripts/loader.ts +589 -0
- package/skills/c64-source/scripts/opcodes.ts +61 -0
- package/skills/c64-source/scripts/source.ts +673 -0
- package/skills/c64-static-analysis/SKILL.md +120 -0
- package/skills/c64-static-analysis/scripts/analyze.ts +293 -0
- package/skills/c64-static-analysis/scripts/coverage.ts +78 -0
- package/skills/c64-static-analysis/scripts/findings.ts +75 -0
- package/skills/c64-static-analysis/scripts/idioms.ts +149 -0
- package/skills/c64-static-analysis/scripts/instructions.ts +105 -0
- package/skills/c64-static-analysis/scripts/pointers.ts +108 -0
- package/skills/c64-testing/SKILL.md +103 -0
- package/skills/c64-testing/references/scenario-format.md +116 -0
- package/skills/c64-testing/scripts/checklist.ts +51 -0
- package/skills/c64-testing/scripts/run.ts +189 -0
- package/skills/c64-testing/scripts/scenario.ts +493 -0
- package/skills/c64-testing/scripts/test.ts +65 -0
- package/skills/c64-unpacker/SKILL.md +135 -0
- package/skills/c64-unpacker/scripts/evidence.ts +207 -0
- package/skills/c64-unpacker/scripts/loads.ts +209 -0
- package/skills/c64-unpacker/scripts/machine.ts +80 -0
- package/skills/c64-unpacker/scripts/stages.ts +130 -0
- package/skills/c64-unpacker/scripts/unpack.ts +683 -0
- package/src/c64.ts +411 -0
- package/src/cli/ap-sdk.ts +44 -0
- package/src/cli/cleanup.ts +194 -0
- package/src/cli/main.ts +125 -0
- package/src/cli/status.ts +61 -0
- package/src/host/main.ts +134 -0
- package/src/host/server.ts +392 -0
- package/src/host/tools/c1541.ts +284 -0
- package/src/host/tools/index.ts +35 -0
- package/src/host/tools/petcat.ts +77 -0
- package/src/host/tools/status.ts +47 -0
- package/src/host/tools/version.ts +41 -0
- package/src/host/vice/adapter.ts +804 -0
- package/src/host/vice/binary-monitor.ts +389 -0
- package/src/host/vice/chips.ts +118 -0
- package/src/host/vice/connect.ts +34 -0
- package/src/host/vice/process.ts +413 -0
- package/src/host/vice/screen.ts +184 -0
- package/src/host/vice/session.ts +1426 -0
- package/src/host/vice/text-monitor.ts +223 -0
- package/src/host-client/autostart.ts +55 -0
- package/src/host-client/connect.ts +241 -0
- package/src/host-client/coverage.ts +63 -0
- package/src/host-client/image.ts +63 -0
- package/src/host-client/steps.ts +170 -0
- package/src/host-client/tools.ts +89 -0
- package/src/host-client/transfer.ts +126 -0
- package/src/host-client/vice-session.ts +316 -0
- package/src/knowledge/database.ts +132 -0
- package/src/knowledge/expression.ts +209 -0
- package/src/knowledge/history.ts +169 -0
- package/src/knowledge/import.ts +392 -0
- package/src/knowledge/read.ts +392 -0
- package/src/knowledge/schema.ts +250 -0
- package/src/knowledge/write.ts +837 -0
- package/src/mcp/main.ts +134 -0
- package/src/mcp/output.ts +32 -0
- package/src/mcp/server.ts +316 -0
- package/src/mcp/tools/debug.ts +327 -0
- package/src/mcp/tools/execution.ts +117 -0
- package/src/mcp/tools/input.ts +72 -0
- package/src/mcp/tools/machine.ts +100 -0
- package/src/mcp/tools/media.ts +129 -0
- package/src/mcp/tools/memory.ts +250 -0
- package/src/mcp/tools/video.ts +271 -0
- package/src/native/acme.ts +116 -0
- package/src/native/discover.ts +179 -0
- package/src/native/dxa.ts +166 -0
- package/src/native/ghidra/analyze.ts +86 -0
- package/src/native/ghidra/index.ts +173 -0
- package/src/native/ghidra/language/c64rt_6510.cspec +34 -0
- package/src/native/ghidra/language/c64rt_6510.ldefs +15 -0
- package/src/native/ghidra/language/c64rt_6510.pspec +15 -0
- package/src/native/ghidra/language/c64rt_6510.slaspec +596 -0
- package/src/native/ghidra/scripts/C64Export.java +274 -0
- package/src/native/ghidra/scripts/C64Prepare.java +60 -0
- package/src/native/local.ts +34 -0
- package/src/native/processes.ts +350 -0
- package/src/native/run.ts +172 -0
- package/src/native/staging.ts +83 -0
- package/src/native/status.ts +113 -0
- package/src/native/types.ts +257 -0
- package/src/native/watchdog.ts +78 -0
- package/src/package.ts +21 -0
- package/src/platform.ts +441 -0
- package/src/project.ts +39 -0
- package/src/protocol/framing.ts +132 -0
- package/src/protocol/messages.ts +259 -0
- package/src/protocol/tools.ts +266 -0
- package/src/protocol/vice.ts +1348 -0
- package/src/script.ts +60 -0
- package/src/time.ts +15 -0
- package/src/trace.ts +194 -0
- package/THIRD-PARTY-NOTICES.md +0 -26
- package/bin/cli.mjs +0 -283
- package/skills/acme-build/SKILL.md +0 -260
- package/skills/acme-build/scripts/acme.mjs +0 -341
- package/skills/acme-build/template.a +0 -53
- package/skills/c64-disk-access/SKILL.md +0 -159
- package/skills/c64-disk-access/scripts/c1541.mjs +0 -569
- package/skills/c64-memory-mapping/SKILL.md +0 -631
- package/skills/c64-memory-mapping/memmap.json +0 -8800
- package/skills/c64-memory-mapping/scripts/driver.mjs +0 -553
- package/skills/c64-petcat/SKILL.md +0 -87
- package/skills/c64-petcat/scripts/petcat.mjs +0 -221
- package/skills/c64-program-recon/SKILL.md +0 -706
- package/skills/c64-program-recon/references/control-flow.md +0 -180
- package/skills/c64-program-recon/references/graphics.md +0 -79
- package/skills/c64-program-recon/references/observation-hazards.md +0 -143
- package/skills/c64-program-recon/references/reconstruction.md +0 -133
- package/skills/c64-program-recon/references/sound-and-input.md +0 -74
- package/skills/c64-program-recon/references/tool-selection.md +0 -78
- package/skills/c64-program-recon/scripts/derive.mjs +0 -364
- package/skills/c64-program-recon/scripts/packer-finding.mjs +0 -709
- package/skills/c64-program-recon/templates/memory-map.template.md +0 -101
- package/skills/c64-provenance-diff/SKILL.md +0 -292
- package/skills/c64-provenance-diff/scripts/diff-images.mjs +0 -984
- package/skills/c64-provenance-diff/scripts/recovery-schema.mjs +0 -395
- package/skills/c64-ram-capture/RELEASES.json.example +0 -17
- package/skills/c64-ram-capture/SKILL.md +0 -474
- package/skills/c64-ram-capture/scripts/compare-cross-binary.mjs +0 -525
- package/skills/c64-ram-capture/scripts/compare.mjs +0 -258
- package/skills/c64-ram-capture/scripts/derive-transients.mjs +0 -575
- package/skills/c64-ram-capture/scripts/dump-artifacts.mjs +0 -317
- package/skills/c64-ram-capture/scripts/mcp-module.mjs +0 -174
- package/skills/c64-ram-capture/scripts/project-paths.mjs +0 -81
- package/skills/c64-ram-capture/scripts/releases.mjs +0 -109
- package/skills/c64-ram-capture/scripts/vsf-slice.mjs +0 -147
- package/skills/c64-ram-capture/scripts/watch-loads.mjs +0 -581
- package/skills/c64-ram-capture/templates/capture-record.template.md +0 -99
- package/skills/c64-ram-capture/transients/README.md +0 -137
- package/skills/routine-queue-walker/SKILL.md +0 -394
- package/skills/routine-queue-walker/scripts/completeness-report.mjs +0 -465
- package/skills/vice-wedge-triage/SKILL.md +0 -269
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Henrik Olsson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,61 +1,178 @@
|
|
|
1
|
-
#
|
|
1
|
+
# c64-re-tools
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
emulator for reverse-engineering work.
|
|
3
|
+
An agent-friendly Commodore 64 engineering toolkit for reverse engineering
|
|
4
|
+
unknown applications and for developing, debugging, building and testing new
|
|
5
|
+
C64 applications.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
An agent drives a real VICE emulator through an MCP server, and uses skills
|
|
8
|
+
for the rest: disk images, BASIC, static analysis with DXA and Ghidra, a project
|
|
9
|
+
knowledge database, the ACME assembler, unpacking, release comparison and
|
|
10
|
+
functional-equivalence tests.
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
Architecture in one line: one MCP process owns one VICE instance and is
|
|
13
|
+
VICE-only; a Host Runtime on the graphical host owns VICE and the tools that
|
|
14
|
+
come with it (c1541, petcat); skill scripts call the Host Runtime for those,
|
|
15
|
+
run ACME, DXA and Ghidra themselves, and keep project knowledge local in
|
|
16
|
+
`.c64-re-tools/knowledge.db`.
|
|
17
|
+
The design is in [`docs/redesign/`](docs/redesign/README.md), and
|
|
18
|
+
[`docs/plan.md`](docs/plan.md) tracks the implementation.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
You need Node.js 24 or newer, and VICE 3.9 or later (`x64sc`, `c1541`, `petcat`) with its ROM files; use the latest VICE release. `status` and the host name the known flaws of an older VICE. ACME, DXA and Ghidra are needed only for the skills that use them: [docs/install-tools.md](docs/install-tools.md) has the steps for each system.
|
|
23
|
+
|
|
24
|
+
Start the Host Runtime and leave it running:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
npx -y --package=@henols/c64-re-tools@latest c64-re-tools-host
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The host logs each call from a skill script. Add `--log-console` to print also each MCP call and each c1541 or petcat run, or `--log-dir <directory>` to write them to a file. Both are off by default.
|
|
31
|
+
|
|
32
|
+
Install into your project and start the agent:
|
|
11
33
|
|
|
12
|
-
```sh
|
|
13
|
-
npx @henols/c64-re-tools
|
|
14
34
|
```
|
|
35
|
+
cd my-c64-project
|
|
36
|
+
npx -y @henols/c64-re-tools@latest install --target claude
|
|
37
|
+
claude
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`npx -y @henols/c64-re-tools@latest status` shows what works.
|
|
41
|
+
|
|
42
|
+
Until 2.0.0 is released, `@latest` is the old 1.1.0. Use the prerelease instead. For the prerelease, a dev container and debugging, see [docs/debug-install.md](docs/debug-install.md).
|
|
43
|
+
|
|
44
|
+
### Dev container
|
|
45
|
+
|
|
46
|
+
The agent runs in the container, VICE and the Host Runtime on the host. In `.devcontainer/devcontainer.json`:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
"features": {
|
|
50
|
+
"https://github.com/henols/c64-re-tools/releases/latest/download/devcontainer-feature-c64-re-tools.tgz": { "target": "claude" }
|
|
51
|
+
},
|
|
52
|
+
"runArgs": ["--add-host=host.docker.internal:host-gateway"]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Until 2.0.0 is released, the `latest` URL has no Feature. Use the prerelease: `https://github.com/henols/c64-re-tools/releases/download/v2.0.0-rc.4/devcontainer-feature-c64-re-tools.tgz` with `{ "target": "claude", "channel": "next" }`.
|
|
56
|
+
|
|
57
|
+
The Feature installs ACME, dxa, Java 21 and Ghidra, puts the toolkit into the project when the container is created, and updates it at each start. `target` is a harness or a comma list. `channel` is `latest` (default) or `next`. Debian and Ubuntu images only. `runArgs` is for Docker on Linux. Start the Host Runtime on the host as above.
|
|
58
|
+
|
|
59
|
+
### Settings
|
|
60
|
+
|
|
61
|
+
| Variable | Read by | Meaning |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| `C64RT_VICE` | host | Full path of `x64sc` when it is not on `PATH`. |
|
|
64
|
+
| `C64RT_C1541`, `C64RT_PETCAT` | host | Full path of the tool when it is not on `PATH`. |
|
|
65
|
+
| `C64RT_ACME`, `C64RT_DXA` | skills, CLI | Full path of the tool when it is not on `PATH`. |
|
|
66
|
+
| `C64RT_GHIDRA` | skills, CLI | The Ghidra installation directory, when `analyzeHeadless` is not on `PATH`. |
|
|
67
|
+
| `C64RT_HOST` | MCP, skills, CLI | `host:port` of the Host Runtime when it is not on 127.0.0.1, `host.docker.internal` or `host.containers.internal` at 6464. |
|
|
68
|
+
| `C64RT_VIDEO` | MCP | `pal` (default) or `ntsc`, fixed for the life of the MCP process. |
|
|
69
|
+
| `C64RT_TRACE` | MCP, skills, CLI | A directory for a trace of what the programs do, for a person who debugs a run. See [docs/debug-install.md](docs/debug-install.md). Nothing of the trace reaches the agent. The host uses `--log-dir` instead. |
|
|
15
70
|
|
|
16
|
-
|
|
71
|
+
Ghidra runs with its own settings directory for each request. The toolkit
|
|
72
|
+
brings its own NMOS 6510 language (all 256 opcodes); your Ghidra installation
|
|
73
|
+
and settings stay unchanged.
|
|
17
74
|
|
|
18
|
-
|
|
19
|
-
2. Add a `vice` server to `<project>/.mcp.json` (existing servers are preserved),
|
|
20
|
-
launched via `npx -y @henols/vice-mcp`
|
|
75
|
+
## Use it with an agent
|
|
21
76
|
|
|
22
|
-
|
|
77
|
+
1. Install for your harness: `--target claude`, `codex`, `pi`, `opencode`, `gemini`, `copilot`, `cursor` or `windsurf`. Without `--target`, `install` installs for all of them.
|
|
78
|
+
2. Start the Host Runtime and leave it running.
|
|
79
|
+
3. Start the harness in the project, for example `claude` or `codex`, or open the project in Cursor or Windsurf. The harness starts the MCP server and finds the skills by itself.
|
|
80
|
+
4. Ask for the result you want, in plain words. The agent picks the skills and the `c64_*` tools.
|
|
23
81
|
|
|
24
|
-
|
|
82
|
+
Examples:
|
|
25
83
|
|
|
26
|
-
|
|
84
|
+
```
|
|
85
|
+
Reverse engineer original/game.d64. Find how it starts, unpack it if it is packed,
|
|
86
|
+
and name the main routines in project knowledge.
|
|
87
|
+
|
|
88
|
+
Find the routine that draws the score, and explain how it works.
|
|
89
|
+
|
|
90
|
+
Write a program in ACME that scrolls a text at the bottom of the screen.
|
|
91
|
+
Build it, run it in the emulator, and write a test for it.
|
|
92
|
+
|
|
93
|
+
Show me the emulator window.
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The agent keeps what it learns about the program in `.c64-re-tools/knowledge.db`. Commit that file with the project.
|
|
97
|
+
|
|
98
|
+
## Skills
|
|
99
|
+
|
|
100
|
+
| Skill | Use |
|
|
27
101
|
| --- | --- |
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
102
|
+
| c64-reverse-engineering | Coordinates an investigation and picks the specialist for the next unknown. |
|
|
103
|
+
| c64-emulator | Runs and observes the program in VICE through the MCP tools. |
|
|
104
|
+
| c64-static-analysis | A fast first pass with DXA and a deeper pass with Ghidra into knowledge, with seeds from knowledge and decompilation. |
|
|
105
|
+
| c64-knowledge | Reads and writes symbols, regions, comments and references, with history. |
|
|
106
|
+
| c64-disk | Directory, BAM, entries, sector chains and extraction of disk images. |
|
|
107
|
+
| c64-basic | BASIC V2 listings and the SYS/USR handoff to machine code. |
|
|
108
|
+
| c64-unpacker | Packing evidence, and capture of the unpacked program from the emulator. |
|
|
109
|
+
| c64-memory-map | Platform meaning of addresses and register values. |
|
|
110
|
+
| c64-provenance | Byte comparison of releases. |
|
|
111
|
+
| c64-source | ACME source from the image and the knowledge, checked byte by byte, and a loader for the entry state. |
|
|
112
|
+
| c64-assembler | ACME builds with diagnostics and symbols. |
|
|
113
|
+
| c64-testing | Original-against-rebuild scenarios with PASS, FAIL or INCONCLUSIVE. |
|
|
114
|
+
|
|
115
|
+
## Examples
|
|
116
|
+
|
|
117
|
+
Reverse engineering, from an unknown disk to a verified rebuild (the commands
|
|
118
|
+
that the skills run for the agent; `<skills>` is the installed skills
|
|
119
|
+
directory):
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
node <skills>/c64-disk/scripts/disk.ts directory original/game.d64
|
|
123
|
+
node <skills>/c64-disk/scripts/disk.ts read original/game.d64 GAME --out extracted/game.prg
|
|
124
|
+
node <skills>/c64-basic/scripts/basic.ts extracted/game.prg # the SYS address, e.g. $080d
|
|
125
|
+
node <skills>/c64-unpacker/scripts/unpack.ts inspect extracted/game.prg
|
|
126
|
+
node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --analyzer dxa --listing analysis/game.lst
|
|
127
|
+
node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --entry '$080d'
|
|
128
|
+
node <skills>/c64-knowledge/scripts/knowledge.ts rename '$080d' main --kind routine --reason 'entry from BASIC'
|
|
129
|
+
node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --entry '$080d' --decompile '$080d'
|
|
130
|
+
node <skills>/c64-assembler/scripts/assemble.ts --source-root src --entry game.a --out build/game.prg > build/game.json
|
|
131
|
+
node <skills>/c64-testing/scripts/test.ts tests/counts.json # PASS, FAIL or INCONCLUSIVE
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Development: build with the c64-assembler skill, then load and debug with the
|
|
135
|
+
MCP tools (`c64_program_load`, `c64_breakpoint`, `c64_run_until`,
|
|
136
|
+
`c64_memory_read`, `c64_screen`), and test with the c64-testing skill.
|
|
33
137
|
|
|
34
|
-
|
|
35
|
-
you pass `--force`.
|
|
138
|
+
## Developing
|
|
36
139
|
|
|
37
|
-
|
|
140
|
+
To work on the toolkit itself, Node 24 or newer and pnpm are required; install pnpm yourself (for example
|
|
141
|
+
`corepack enable pnpm`). `packageManager` in `package.json` pins its version.
|
|
38
142
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
143
|
+
```
|
|
144
|
+
pnpm install --frozen-lockfile
|
|
145
|
+
pnpm typecheck # tsc checks everything; it emits nothing
|
|
146
|
+
pnpm test # node --test on src/**/*.test.ts and test/**/*.test.ts
|
|
147
|
+
```
|
|
42
148
|
|
|
43
|
-
|
|
149
|
+
Unit tests sit beside their source as `src/**/*.test.ts`; `test/` holds
|
|
150
|
+
integration and end-to-end tests. Node runs the TypeScript directly (type
|
|
151
|
+
stripping), so relative imports name the `.ts` file. Skill scripts import
|
|
152
|
+
`src/` as `#src/...` (the `imports` map in `package.json`); an installed skill
|
|
153
|
+
gets the `src/` modules it reaches and its own `package.json` with that map.
|
|
154
|
+
|
|
155
|
+
Tests that need real tools are opt-in. Without them they are reported as
|
|
156
|
+
skipped, never as passed:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
C64RT_LIVE_VICE=/usr/bin/x64sc C64RT_GHIDRA=/opt/ghidra pnpm test
|
|
160
|
+
```
|
|
44
161
|
|
|
45
|
-
|
|
46
|
-
- **The VICE MCP server** it wires up requires **Node ≥ 24**, and a
|
|
47
|
-
host with VICE (`x64sc`) reachable from the MCP client. See the
|
|
48
|
-
[`@henols/vice-mcp`](https://www.npmjs.com/package/@henols/vice-mcp) readme.
|
|
162
|
+
ACME, DXA, c1541 and petcat tests run when the tools are on `PATH`.
|
|
49
163
|
|
|
50
|
-
|
|
164
|
+
To release, push a tag. See [docs/releasing.md](docs/releasing.md).
|
|
51
165
|
|
|
52
|
-
|
|
166
|
+
## Layout
|
|
53
167
|
|
|
54
168
|
```
|
|
55
|
-
/
|
|
56
|
-
/
|
|
169
|
+
src/ # mcp, host, host-client, native, knowledge, cli; unit tests beside the source
|
|
170
|
+
skills/ # the skills: SKILL.md, scripts (TypeScript) and references
|
|
171
|
+
distribution/ # the AP SDK plugin definition: skills with the src/ modules they reach
|
|
172
|
+
test/ # fixtures, integration and end-to-end tests
|
|
173
|
+
docs/redesign/ # the design
|
|
57
174
|
```
|
|
58
175
|
|
|
59
176
|
## License
|
|
60
177
|
|
|
61
|
-
MIT
|
|
178
|
+
MIT. See [`LICENSE`](./LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The npm package and the VICE MCP declaration that starts it. This module
|
|
2
|
+
// only reads package.json, so a caller that needs the declaration does not
|
|
3
|
+
// build the plugin.
|
|
4
|
+
|
|
5
|
+
import { PACKAGE } from "../src/package.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The agent starts the MCP server through npx with the newest version of the
|
|
9
|
+
* channel it was installed from, so it updates itself and an install from a
|
|
10
|
+
* prerelease does not start a release; nothing is linked or installed globally.
|
|
11
|
+
* The entry point is TypeScript, run by tsx: Node does not strip types under
|
|
12
|
+
* node_modules.
|
|
13
|
+
*/
|
|
14
|
+
export function mcpServerFor(platform: NodeJS.Platform): { command: string; args: string[] } {
|
|
15
|
+
const npx = ["-y", `--package=${PACKAGE.name}@${PACKAGE.tag}`, "c64-re-tools-mcp"];
|
|
16
|
+
// On native Windows npx is a .cmd shim, which a harness starts only through cmd /c.
|
|
17
|
+
return platform === "win32" ? { command: "cmd", args: ["/c", "npx", ...npx] } : { command: "npx", args: npx };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The declaration for the machine where install runs: the plugin is evaluated there. */
|
|
21
|
+
export const MCP_SERVER = mcpServerFor(process.platform);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// The portable c64-re-tools plugin for @jalco/ap-sdk: the skills
|
|
2
|
+
// with their TypeScript scripts, the src/ modules those scripts reach, their
|
|
3
|
+
// references, and the VICE MCP declaration. AP SDK maps it to each harness's
|
|
4
|
+
// native layout; nothing at run time depends on it.
|
|
5
|
+
//
|
|
6
|
+
// Nothing is built: an installed skill holds the same .ts files as this
|
|
7
|
+
// repository, and Node runs them by type stripping. Skill scripts import
|
|
8
|
+
// src/ through the "#src/*" subpath import; each installed skill gets a
|
|
9
|
+
// package.json that maps it to its own copy of those modules.
|
|
10
|
+
|
|
11
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
12
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
13
|
+
|
|
14
|
+
import { definePlugin, defineSkill, type Skill } from "@jalco/ap-sdk";
|
|
15
|
+
|
|
16
|
+
import { PACKAGE } from "../src/package.ts";
|
|
17
|
+
import { MCP_SERVER } from "./package.ts";
|
|
18
|
+
|
|
19
|
+
const root = resolve(import.meta.dirname, "..");
|
|
20
|
+
const SOURCE = join(root, "src");
|
|
21
|
+
const SCRIPT_REFERENCE = /scripts\/([a-z][a-z0-9-]*)\.ts/g;
|
|
22
|
+
/**
|
|
23
|
+
* The modules a file reaches: static and dynamic imports, re-exports, and
|
|
24
|
+
* new URL("...", import.meta.url) for a module that is spawned or an asset
|
|
25
|
+
* directory. Only relative and #src specifiers; packages are node: built-ins.
|
|
26
|
+
*/
|
|
27
|
+
const SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\bnew URL\(\s*)(["'])((?:\.\.?\/|#src\/)[^"']*)\1/g;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The package.json of an installed skill: ES modules, #src/* resolved inside
|
|
31
|
+
* the skill, and the name and version of c64-re-tools that the copy comes
|
|
32
|
+
* from, which src/package.ts reads.
|
|
33
|
+
*/
|
|
34
|
+
export const SKILL_PACKAGE = `${JSON.stringify({ name: PACKAGE.name, type: "module", version: PACKAGE.version, imports: { "#src/*": "./src/*" } }, null, 2)}\n`;
|
|
35
|
+
|
|
36
|
+
/** The scripts that a SKILL.md runs, by file name. */
|
|
37
|
+
export function scriptsOf(skillMarkdown: string): string[] {
|
|
38
|
+
return [...new Set([...skillMarkdown.matchAll(SCRIPT_REFERENCE)].map((match) => match[1]!))].sort();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function filesBelow(directory: string): string[] {
|
|
42
|
+
if (!existsSync(directory)) return [];
|
|
43
|
+
return readdirSync(directory, { recursive: true, encoding: "utf8" })
|
|
44
|
+
.map((path) => join(directory, path))
|
|
45
|
+
.filter((path) => statSync(path).isFile())
|
|
46
|
+
.sort();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const inside = (directory: string, path: string) => path === directory || path.startsWith(directory + sep);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Every file that the given scripts reach, transitively: the scripts, their
|
|
53
|
+
* sibling modules, and the src/ modules and asset directories below them.
|
|
54
|
+
* A reach out of the skill directory and src/ is an error.
|
|
55
|
+
*/
|
|
56
|
+
export function closureOf(skillDirectory: string, scripts: readonly string[]): string[] {
|
|
57
|
+
const found = new Set<string>();
|
|
58
|
+
const pending = [...scripts];
|
|
59
|
+
while (pending.length > 0) {
|
|
60
|
+
const file = pending.pop()!;
|
|
61
|
+
if (found.has(file)) continue;
|
|
62
|
+
if (!inside(SOURCE, file) && !inside(skillDirectory, file)) throw new Error(`${relative(root, file)} is outside the skill and src/`);
|
|
63
|
+
if (!existsSync(file)) throw new Error(`${relative(root, file)} does not exist`);
|
|
64
|
+
if (statSync(file).isDirectory()) {
|
|
65
|
+
pending.push(...filesBelow(file));
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
found.add(file);
|
|
69
|
+
if (!file.endsWith(".ts")) continue;
|
|
70
|
+
for (const match of readFileSync(file, "utf8").matchAll(SPECIFIER)) {
|
|
71
|
+
const specifier = match[2]!;
|
|
72
|
+
pending.push(specifier.startsWith("#src/") ? join(SOURCE, specifier.slice("#src/".length)) : resolve(dirname(file), specifier));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return [...found].sort();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** One skill: SKILL.md frontmatter and body, scripts (executable), the src/ modules they reach, and references. */
|
|
79
|
+
function loadSkill(directory: string): Skill {
|
|
80
|
+
const text = readFileSync(join(directory, "SKILL.md"), "utf8");
|
|
81
|
+
const frontmatter = /^---\n([\s\S]*?)\n---\n/.exec(text);
|
|
82
|
+
if (frontmatter === null) throw new Error(`${directory}/SKILL.md has no frontmatter`);
|
|
83
|
+
const field = (name: string) => {
|
|
84
|
+
const value = new RegExp(`^${name}: (.+)$`, "m").exec(frontmatter[1]!)?.[1];
|
|
85
|
+
if (value === undefined) throw new Error(`${directory}/SKILL.md has no ${name}`);
|
|
86
|
+
return value;
|
|
87
|
+
};
|
|
88
|
+
const scripts = scriptsOf(text).map((script) => join(directory, "scripts", `${script}.ts`));
|
|
89
|
+
const code = closureOf(directory, scripts).map((path) => ({
|
|
90
|
+
path: (inside(SOURCE, path) ? relative(root, path) : relative(directory, path)).split("\\").join("/"),
|
|
91
|
+
content: readFileSync(path, "utf8"),
|
|
92
|
+
...(scripts.includes(path) ? { executable: true } : {}),
|
|
93
|
+
}));
|
|
94
|
+
const references = filesBelow(join(directory, "references")).map((path) => ({ path: relative(directory, path).split("\\").join("/"), content: readFileSync(path, "utf8") }));
|
|
95
|
+
const resources = [...code, ...references, ...(scripts.length > 0 ? [{ path: "package.json", content: SKILL_PACKAGE }] : [])];
|
|
96
|
+
return defineSkill({
|
|
97
|
+
name: field("name"),
|
|
98
|
+
description: field("description"),
|
|
99
|
+
instructions: text.slice(frontmatter[0].length).replace(/^\n+/, ""),
|
|
100
|
+
...(resources.length > 0 ? { resources } : {}),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function c64Plugin() {
|
|
105
|
+
const skills = readdirSync(join(root, "skills"), { withFileTypes: true })
|
|
106
|
+
.filter((entry) => entry.isDirectory())
|
|
107
|
+
.map((entry) => entry.name)
|
|
108
|
+
.sort()
|
|
109
|
+
.map((name) => loadSkill(join(root, "skills", name)));
|
|
110
|
+
return definePlugin({
|
|
111
|
+
id: "c64-re-tools",
|
|
112
|
+
version: PACKAGE.version,
|
|
113
|
+
description: "Commodore 64 reverse engineering and development: a live VICE emulator, static analysis, disk and BASIC tools, project knowledge, building and testing.",
|
|
114
|
+
author: { name: "Henrik Olsson" },
|
|
115
|
+
homepage: "https://github.com/henols/c64-re-tools",
|
|
116
|
+
license: "MIT",
|
|
117
|
+
skills,
|
|
118
|
+
mcpServers: { "c64-re-tools": MCP_SERVER },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default c64Plugin();
|
package/package.json
CHANGED
|
@@ -1,55 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henols/c64-re-tools",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0-rc.11",
|
|
4
|
+
"description": "Agent-friendly Commodore 64 engineering toolkit for reverse engineering, debugging, development and testing.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Henrik Olsson",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/henols/c64-re-tools.git"
|
|
10
|
+
},
|
|
5
11
|
"type": "module",
|
|
12
|
+
"packageManager": "pnpm@12.9.1+sha512.06b055fff5cd20dc12781de173ced3310ce0951bf2ec620211a16045d5444d5c93a663210764ef29a653a610459ba0368f0e74f84d80c54723cf95aae7bc0d6d",
|
|
13
|
+
"imports": {
|
|
14
|
+
"#src/*": "./src/*"
|
|
15
|
+
},
|
|
6
16
|
"bin": {
|
|
7
|
-
"c64-re-tools": "
|
|
17
|
+
"c64-re-tools": "src/cli/main.ts",
|
|
18
|
+
"c64-re-tools-mcp": "src/mcp/main.ts",
|
|
19
|
+
"c64-re-tools-host": "src/host/main.ts"
|
|
8
20
|
},
|
|
9
21
|
"files": [
|
|
10
|
-
"
|
|
22
|
+
"src/",
|
|
23
|
+
"!src/**/*.test.ts",
|
|
24
|
+
"!src/**/*.testkit.ts",
|
|
11
25
|
"skills/",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
26
|
+
"distribution/package.ts",
|
|
27
|
+
"distribution/plugin.ts",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"README.md"
|
|
14
30
|
],
|
|
15
31
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
17
|
-
},
|
|
18
|
-
"publishConfig": {
|
|
19
|
-
"access": "public"
|
|
20
|
-
},
|
|
21
|
-
"license": "MIT",
|
|
22
|
-
"author": {
|
|
23
|
-
"name": "Henrik Olsson",
|
|
24
|
-
"url": "https://github.com/henols"
|
|
32
|
+
"node": ">=24.0.0"
|
|
25
33
|
},
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
34
|
+
"scripts": {
|
|
35
|
+
"host": "node src/host/main.ts",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
37
|
+
"test": "node --test \"src/**/*.test.ts\" \"test/**/*.test.ts\""
|
|
30
38
|
},
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
"
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "24.13.3",
|
|
41
|
+
"typescript": "7.0.2"
|
|
34
42
|
},
|
|
35
|
-
"keywords": [
|
|
36
|
-
"c64",
|
|
37
|
-
"commodore-64",
|
|
38
|
-
"vice",
|
|
39
|
-
"6502",
|
|
40
|
-
"6510",
|
|
41
|
-
"acme",
|
|
42
|
-
"reverse-engineering",
|
|
43
|
-
"mcp",
|
|
44
|
-
"claude",
|
|
45
|
-
"installer"
|
|
46
|
-
],
|
|
47
43
|
"dependencies": {
|
|
48
|
-
"@
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"prepack": "node scripts/sync-skills.mjs",
|
|
53
|
-
"test": "node --test '*.test.mjs'"
|
|
44
|
+
"@modelcontextprotocol/sdk": "1.32.0",
|
|
45
|
+
"zod": "4.6.5",
|
|
46
|
+
"@jalco/ap-sdk": "0.4.1",
|
|
47
|
+
"tsx": "4.23.15"
|
|
54
48
|
}
|
|
55
49
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: c64-assembler
|
|
3
|
+
description: Use this skill to assemble C64 6510 source code with the ACME assembler and write the program (PRG) into the project. It reports source errors with file and line, and the symbols with their addresses or values. Use it to build a rebuilt or new program. Do not use it to run the program. Give the PRG to the c64-emulator skill for that.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# c64-assembler
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
Assemble C64 source code with ACME. Write the program into the project. Report diagnostics and symbols.
|
|
11
|
+
|
|
12
|
+
This skill does not run the program and does not change project knowledge.
|
|
13
|
+
|
|
14
|
+
## Inputs
|
|
15
|
+
|
|
16
|
+
- The source root: the project directory that holds all source files of the program.
|
|
17
|
+
- The entry source: the main file, relative to the source root.
|
|
18
|
+
- The output path: where to write the PRG, relative to the project directory.
|
|
19
|
+
- Optional include directories, symbol definitions and a start address.
|
|
20
|
+
|
|
21
|
+
## Tools / execution path
|
|
22
|
+
|
|
23
|
+
The script runs ACME on this machine. It copies the source root to a temporary directory, assembles it there and writes the program into the project. The host runtime is not necessary.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
node <skill>/scripts/assemble.ts --source-root src --entry main.a --out build/game.prg
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Option | Meaning |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `--include lib` | An include directory, relative to the source root. Use it again for more directories. |
|
|
32
|
+
| `--define DEBUG=1` | An assembler symbol: an integer, also as `$` hex, or `true` or `false`. Use it again for more symbols. |
|
|
33
|
+
| `--set-pc '$0801'` | The start address when the source does not set one. |
|
|
34
|
+
| `--compare unpacked/image.json` | Check the program byte by byte against a reference. The reference is the `image.json` of the c64-unpacker skill, a PRG, or 64 KiB of memory. |
|
|
35
|
+
|
|
36
|
+
The script copies the full source root. Put all files that the program includes under it. Version control directories stay out.
|
|
37
|
+
|
|
38
|
+
The script prints one JSON object. The target CPU is the 6510 and the output is always a C64 PRG.
|
|
39
|
+
|
|
40
|
+
## Workflow
|
|
41
|
+
|
|
42
|
+
1. Find the source root and the entry source. The entry source sets its own start address with `* = $0801` or similar.
|
|
43
|
+
2. Run the script.
|
|
44
|
+
3. If `assembled` is `true`, the PRG is at `output`. `loadRange` tells where it loads. `symbols` gives each symbol as an `address` or a `value`.
|
|
45
|
+
4. If `assembled` is `false`, read `diagnostics`. Each one has a severity, a file, a line and a message. Correct the source and run the script again.
|
|
46
|
+
5. To run the program, give the PRG path to the c64-emulator skill.
|
|
47
|
+
|
|
48
|
+
## Knowledge
|
|
49
|
+
|
|
50
|
+
Automatic write:
|
|
51
|
+
- none.
|
|
52
|
+
|
|
53
|
+
Do not store:
|
|
54
|
+
- diagnostics
|
|
55
|
+
- build output
|
|
56
|
+
|
|
57
|
+
A symbol from the rebuilt source can give the address of a routine in the rebuild. Use it to compare runs with the c64-testing skill.
|
|
58
|
+
|
|
59
|
+
## Result
|
|
60
|
+
|
|
61
|
+
The PRG in the project at the output path, with its load range and symbols. Or the source diagnostics when the assembly failed.
|
|
62
|
+
|
|
63
|
+
With `--compare`, `compare` gives `identical` and one check for each segment of the reference. A check is `identical`, `different` with the first different address, or `not covered` when the program does not hold the segment.
|
|
64
|
+
|
|
65
|
+
The exit status is 0 for a PRG. The exit status is 1 for `assembled: false`, for a `--compare` that is not identical, and for an error. The exit status is 2 for a wrong argument.
|
|
66
|
+
|
|
67
|
+
## Failure and conflicts
|
|
68
|
+
|
|
69
|
+
- `assembled: false`: the source has errors. This is a normal result. Correct the source.
|
|
70
|
+
- `installation-incomplete`, or `machine-unavailable` with a message that the host runtime does not run: stop the task. Give the message to the user and wait for the user. The message gives the remedy. Do not make a replacement for the tool or the host runtime, for example your own disk parser, disassembler or emulator script.
|
|
71
|
+
- `invalid-input`: an option or a path is wrong. Read the message and correct the call.
|
|
72
|
+
|
|
73
|
+
## Handoffs
|
|
74
|
+
|
|
75
|
+
- Run or debug the program → c64-emulator
|
|
76
|
+
- Compare the rebuild with the original → c64-testing
|