@henols/c64-re-tools 1.0.0 → 2.0.0-rc.10

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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -37
  3. package/distribution/package.ts +21 -0
  4. package/distribution/plugin.ts +122 -0
  5. package/package.json +34 -40
  6. package/skills/c64-assembler/SKILL.md +76 -0
  7. package/skills/c64-assembler/scripts/assemble.ts +110 -0
  8. package/skills/c64-assembler/scripts/compare.ts +84 -0
  9. package/skills/c64-basic/SKILL.md +66 -0
  10. package/skills/c64-basic/scripts/basic.ts +46 -0
  11. package/skills/c64-disk/SKILL.md +93 -0
  12. package/skills/c64-disk/scripts/containers.ts +124 -0
  13. package/skills/c64-disk/scripts/disk.ts +120 -0
  14. package/skills/c64-emulator/SKILL.md +134 -0
  15. package/skills/c64-knowledge/SKILL.md +166 -0
  16. package/skills/c64-knowledge/scripts/knowledge.ts +492 -0
  17. package/skills/c64-memory-map/SKILL.md +79 -0
  18. package/skills/c64-memory-map/scripts/memmap.ts +92 -0
  19. package/skills/c64-provenance/SKILL.md +110 -0
  20. package/skills/c64-provenance/scripts/compare.ts +152 -0
  21. package/skills/c64-provenance/scripts/provenance.ts +334 -0
  22. package/skills/c64-provenance/scripts/runtime.ts +191 -0
  23. package/skills/c64-provenance/scripts/separate.ts +140 -0
  24. package/skills/c64-reverse-engineering/SKILL.md +144 -0
  25. package/skills/c64-source/SKILL.md +126 -0
  26. package/skills/c64-source/scripts/generate.ts +490 -0
  27. package/skills/c64-source/scripts/loader.ts +571 -0
  28. package/skills/c64-source/scripts/opcodes.ts +61 -0
  29. package/skills/c64-source/scripts/source.ts +595 -0
  30. package/skills/c64-static-analysis/SKILL.md +119 -0
  31. package/skills/c64-static-analysis/scripts/analyze.ts +275 -0
  32. package/skills/c64-static-analysis/scripts/coverage.ts +78 -0
  33. package/skills/c64-static-analysis/scripts/findings.ts +75 -0
  34. package/skills/c64-static-analysis/scripts/idioms.ts +149 -0
  35. package/skills/c64-static-analysis/scripts/instructions.ts +105 -0
  36. package/skills/c64-testing/SKILL.md +103 -0
  37. package/skills/c64-testing/references/scenario-format.md +116 -0
  38. package/skills/c64-testing/scripts/checklist.ts +51 -0
  39. package/skills/c64-testing/scripts/run.ts +189 -0
  40. package/skills/c64-testing/scripts/scenario.ts +493 -0
  41. package/skills/c64-testing/scripts/test.ts +65 -0
  42. package/skills/c64-unpacker/SKILL.md +135 -0
  43. package/skills/c64-unpacker/scripts/evidence.ts +207 -0
  44. package/skills/c64-unpacker/scripts/loads.ts +209 -0
  45. package/skills/c64-unpacker/scripts/machine.ts +80 -0
  46. package/skills/c64-unpacker/scripts/stages.ts +130 -0
  47. package/skills/c64-unpacker/scripts/unpack.ts +683 -0
  48. package/src/c64.ts +411 -0
  49. package/src/cli/ap-sdk.ts +44 -0
  50. package/src/cli/cleanup.ts +194 -0
  51. package/src/cli/main.ts +125 -0
  52. package/src/cli/status.ts +61 -0
  53. package/src/host/main.ts +134 -0
  54. package/src/host/server.ts +392 -0
  55. package/src/host/tools/c1541.ts +284 -0
  56. package/src/host/tools/index.ts +35 -0
  57. package/src/host/tools/petcat.ts +77 -0
  58. package/src/host/tools/status.ts +47 -0
  59. package/src/host/tools/version.ts +41 -0
  60. package/src/host/vice/adapter.ts +804 -0
  61. package/src/host/vice/binary-monitor.ts +389 -0
  62. package/src/host/vice/chips.ts +118 -0
  63. package/src/host/vice/connect.ts +34 -0
  64. package/src/host/vice/process.ts +413 -0
  65. package/src/host/vice/screen.ts +184 -0
  66. package/src/host/vice/session.ts +1426 -0
  67. package/src/host/vice/text-monitor.ts +223 -0
  68. package/src/host-client/autostart.ts +55 -0
  69. package/src/host-client/connect.ts +241 -0
  70. package/src/host-client/coverage.ts +63 -0
  71. package/src/host-client/steps.ts +170 -0
  72. package/src/host-client/tools.ts +89 -0
  73. package/src/host-client/transfer.ts +126 -0
  74. package/src/host-client/vice-session.ts +316 -0
  75. package/src/knowledge/database.ts +132 -0
  76. package/src/knowledge/history.ts +163 -0
  77. package/src/knowledge/import.ts +391 -0
  78. package/src/knowledge/read.ts +342 -0
  79. package/src/knowledge/schema.ts +229 -0
  80. package/src/knowledge/write.ts +663 -0
  81. package/src/mcp/main.ts +134 -0
  82. package/src/mcp/output.ts +32 -0
  83. package/src/mcp/server.ts +316 -0
  84. package/src/mcp/tools/debug.ts +327 -0
  85. package/src/mcp/tools/execution.ts +117 -0
  86. package/src/mcp/tools/input.ts +72 -0
  87. package/src/mcp/tools/machine.ts +100 -0
  88. package/src/mcp/tools/media.ts +129 -0
  89. package/src/mcp/tools/memory.ts +250 -0
  90. package/src/mcp/tools/video.ts +271 -0
  91. package/src/native/acme.ts +116 -0
  92. package/src/native/discover.ts +179 -0
  93. package/src/native/dxa.ts +166 -0
  94. package/src/native/ghidra/analyze.ts +86 -0
  95. package/src/native/ghidra/index.ts +173 -0
  96. package/src/native/ghidra/language/c64rt_6510.cspec +34 -0
  97. package/src/native/ghidra/language/c64rt_6510.ldefs +15 -0
  98. package/src/native/ghidra/language/c64rt_6510.pspec +15 -0
  99. package/src/native/ghidra/language/c64rt_6510.slaspec +596 -0
  100. package/src/native/ghidra/scripts/C64Export.java +274 -0
  101. package/src/native/ghidra/scripts/C64Prepare.java +60 -0
  102. package/src/native/local.ts +34 -0
  103. package/src/native/processes.ts +350 -0
  104. package/src/native/run.ts +172 -0
  105. package/src/native/staging.ts +83 -0
  106. package/src/native/status.ts +113 -0
  107. package/src/native/types.ts +257 -0
  108. package/src/native/watchdog.ts +78 -0
  109. package/src/package.ts +21 -0
  110. package/src/platform.ts +441 -0
  111. package/src/project.ts +39 -0
  112. package/src/protocol/framing.ts +132 -0
  113. package/src/protocol/messages.ts +259 -0
  114. package/src/protocol/tools.ts +266 -0
  115. package/src/protocol/vice.ts +1348 -0
  116. package/src/script.ts +60 -0
  117. package/src/time.ts +15 -0
  118. package/src/trace.ts +194 -0
  119. package/THIRD-PARTY-NOTICES.md +0 -26
  120. package/bin/cli.mjs +0 -283
  121. package/skills/acme-build/SKILL.md +0 -260
  122. package/skills/acme-build/scripts/acme.mjs +0 -341
  123. package/skills/acme-build/template.a +0 -53
  124. package/skills/c64-disk-access/SKILL.md +0 -159
  125. package/skills/c64-disk-access/scripts/c1541.mjs +0 -569
  126. package/skills/c64-memory-mapping/SKILL.md +0 -631
  127. package/skills/c64-memory-mapping/memmap.json +0 -8800
  128. package/skills/c64-memory-mapping/scripts/driver.mjs +0 -553
  129. package/skills/c64-petcat/SKILL.md +0 -87
  130. package/skills/c64-petcat/scripts/petcat.mjs +0 -221
  131. package/skills/c64-program-recon/SKILL.md +0 -706
  132. package/skills/c64-program-recon/references/control-flow.md +0 -180
  133. package/skills/c64-program-recon/references/graphics.md +0 -79
  134. package/skills/c64-program-recon/references/observation-hazards.md +0 -143
  135. package/skills/c64-program-recon/references/reconstruction.md +0 -133
  136. package/skills/c64-program-recon/references/sound-and-input.md +0 -74
  137. package/skills/c64-program-recon/references/tool-selection.md +0 -78
  138. package/skills/c64-program-recon/scripts/derive.mjs +0 -364
  139. package/skills/c64-program-recon/scripts/packer-finding.mjs +0 -709
  140. package/skills/c64-program-recon/templates/memory-map.template.md +0 -101
  141. package/skills/c64-provenance-diff/SKILL.md +0 -292
  142. package/skills/c64-provenance-diff/scripts/diff-images.mjs +0 -984
  143. package/skills/c64-provenance-diff/scripts/recovery-schema.mjs +0 -395
  144. package/skills/c64-ram-capture/RELEASES.json.example +0 -17
  145. package/skills/c64-ram-capture/SKILL.md +0 -474
  146. package/skills/c64-ram-capture/scripts/compare-cross-binary.mjs +0 -525
  147. package/skills/c64-ram-capture/scripts/compare.mjs +0 -258
  148. package/skills/c64-ram-capture/scripts/derive-transients.mjs +0 -575
  149. package/skills/c64-ram-capture/scripts/dump-artifacts.mjs +0 -317
  150. package/skills/c64-ram-capture/scripts/mcp-module.mjs +0 -174
  151. package/skills/c64-ram-capture/scripts/project-paths.mjs +0 -81
  152. package/skills/c64-ram-capture/scripts/releases.mjs +0 -109
  153. package/skills/c64-ram-capture/scripts/vsf-slice.mjs +0 -147
  154. package/skills/c64-ram-capture/scripts/watch-loads.mjs +0 -581
  155. package/skills/c64-ram-capture/templates/capture-record.template.md +0 -99
  156. package/skills/c64-ram-capture/transients/README.md +0 -137
  157. package/skills/routine-queue-walker/SKILL.md +0 -394
  158. package/skills/routine-queue-walker/scripts/completeness-report.mjs +0 -465
  159. 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
- # @henols/c64-re-tools
1
+ # c64-re-tools
2
2
 
3
- One-command installer that adds the **C64 reverse-engineering skills** and the
4
- **VICE emulator MCP server** ([`@henols/vice-mcp`](https://www.npmjs.com/package/@henols/vice-mcp))
5
- to a project, so an MCP client such as Claude Code can drive a Commodore 64
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
- ## Usage
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
- From the project you want to set up:
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
- That will:
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
- 1. Copy the bundled skills into `<project>/.claude/skills/`
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
- Then restart Claude Code in that project.
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
- ### Options
82
+ Examples:
25
83
 
26
- | Option | Effect |
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
- | `[targetDir]` | Install into this directory instead of the current one. |
29
- | `--force` | Overwrite existing skills and an existing `vice` MCP entry. |
30
- | `--vendor` | Also `npm install -D @henols/vice-mcp` into the project and wire `.mcp.json` to the local copy (pinned / offline), instead of `npx`. |
31
- | `--dry-run`, `-n` | Show what would change without writing anything. |
32
- | `--help`, `-h` | Show help. |
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
- Re-running is safe: existing skills and an existing `vice` entry are kept unless
35
- you pass `--force`.
138
+ ## Developing
36
139
 
37
- ## What gets installed
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
- - **Skills** — `acme-build`, `c64-memory-mapping`, `c64-program-recon`,
40
- `c64-provenance-diff`, `c64-ram-capture`, `vice-wedge-triage`.
41
- - **MCP server** — `@henols/vice-mcp`, exposing the `vice` tools.
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
- ## Requirements
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
- - **This installer** runs on Node ≥ 18.
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
- ## Alternative: Claude Code plugin
164
+ To release, push a tag. See [docs/releasing.md](docs/releasing.md).
51
165
 
52
- If you use Claude Code, you can instead install everything as a plugin:
166
+ ## Layout
53
167
 
54
168
  ```
55
- /plugin marketplace add henols/c64-re-tools
56
- /plugin install c64-re-tools@c64-re-tools
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 © Henrik Olsson
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": "1.0.0",
4
- "description": "Installer that adds the C64 reverse-engineering skills and the VICE emulator MCP server (@henols/vice-mcp) to a project.",
3
+ "version": "2.0.0-rc.10",
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": "bin/cli.mjs"
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
- "bin/",
22
+ "src/",
23
+ "!src/**/*.test.ts",
24
+ "!src/**/*.testkit.ts",
11
25
  "skills/",
12
- "README.md",
13
- "THIRD-PARTY-NOTICES.md"
26
+ "distribution/package.ts",
27
+ "distribution/plugin.ts",
28
+ "LICENSE",
29
+ "README.md"
14
30
  ],
15
31
  "engines": {
16
- "node": ">=18"
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
- "repository": {
27
- "type": "git",
28
- "url": "git+https://github.com/henols/c64-re-tools.git",
29
- "directory": "installer"
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
- "homepage": "https://github.com/henols/c64-re-tools#readme",
32
- "bugs": {
33
- "url": "https://github.com/henols/c64-re-tools/issues"
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
- "@henols/vice-mcp": "1.0.0"
49
- },
50
- "scripts": {
51
- "sync-skills": "node scripts/sync-skills.mjs",
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