@henols/c64-re-tools 1.1.0 → 2.0.0-rc.2

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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +190 -37
  3. package/distribution/package.ts +36 -0
  4. package/distribution/plugin.ts +120 -0
  5. package/package.json +34 -40
  6. package/skills/c64-assembler/SKILL.md +73 -0
  7. package/skills/c64-assembler/scripts/assemble.ts +103 -0
  8. package/skills/c64-basic/SKILL.md +67 -0
  9. package/skills/c64-basic/scripts/basic.ts +46 -0
  10. package/skills/c64-disk/SKILL.md +79 -0
  11. package/skills/c64-disk/scripts/disk.ts +65 -0
  12. package/skills/c64-emulator/SKILL.md +124 -0
  13. package/skills/c64-knowledge/SKILL.md +102 -0
  14. package/skills/c64-knowledge/scripts/knowledge.ts +257 -0
  15. package/skills/c64-memory-map/SKILL.md +76 -0
  16. package/skills/c64-memory-map/scripts/memmap.ts +81 -0
  17. package/skills/c64-memory-map/scripts/platform.ts +424 -0
  18. package/skills/c64-provenance/SKILL.md +76 -0
  19. package/skills/c64-provenance/scripts/compare.ts +92 -0
  20. package/skills/c64-provenance/scripts/provenance.ts +83 -0
  21. package/skills/c64-reverse-engineering/SKILL.md +90 -0
  22. package/skills/c64-static-analysis/SKILL.md +96 -0
  23. package/skills/c64-static-analysis/scripts/analyze.ts +175 -0
  24. package/skills/c64-static-analysis/scripts/findings.ts +73 -0
  25. package/skills/c64-testing/SKILL.md +79 -0
  26. package/skills/c64-testing/references/scenario-format.md +76 -0
  27. package/skills/c64-testing/scripts/checklist.ts +51 -0
  28. package/skills/c64-testing/scripts/scenario.ts +473 -0
  29. package/skills/c64-testing/scripts/test.ts +55 -0
  30. package/skills/c64-unpacker/SKILL.md +82 -0
  31. package/skills/c64-unpacker/scripts/evidence.ts +135 -0
  32. package/skills/c64-unpacker/scripts/unpack.ts +216 -0
  33. package/src/c64.ts +281 -0
  34. package/src/cli/ap-sdk.ts +44 -0
  35. package/src/cli/cleanup.ts +194 -0
  36. package/src/cli/main.ts +122 -0
  37. package/src/cli/status.ts +34 -0
  38. package/src/host/main.ts +121 -0
  39. package/src/host/server.ts +409 -0
  40. package/src/host/tools/c1541.ts +255 -0
  41. package/src/host/tools/index.ts +31 -0
  42. package/src/host/tools/petcat.ts +70 -0
  43. package/src/host/tools/status.ts +30 -0
  44. package/src/host/tools/version.ts +41 -0
  45. package/src/host/vice/adapter.ts +761 -0
  46. package/src/host/vice/binary-monitor.ts +389 -0
  47. package/src/host/vice/chips.ts +108 -0
  48. package/src/host/vice/connect.ts +34 -0
  49. package/src/host/vice/process.ts +402 -0
  50. package/src/host/vice/screen.ts +183 -0
  51. package/src/host/vice/session.ts +1316 -0
  52. package/src/host/vice/text-monitor.ts +223 -0
  53. package/src/host-client/connect.ts +241 -0
  54. package/src/host-client/tools.ts +89 -0
  55. package/src/host-client/transfer.ts +100 -0
  56. package/src/host-client/vice-session.ts +254 -0
  57. package/src/knowledge/database.ts +132 -0
  58. package/src/knowledge/history.ts +128 -0
  59. package/src/knowledge/import.ts +344 -0
  60. package/src/knowledge/read.ts +191 -0
  61. package/src/knowledge/schema.ts +140 -0
  62. package/src/knowledge/write.ts +348 -0
  63. package/src/mcp/main.ts +109 -0
  64. package/src/mcp/server.ts +307 -0
  65. package/src/mcp/tools/debug.ts +237 -0
  66. package/src/mcp/tools/execution.ts +116 -0
  67. package/src/mcp/tools/input.ts +69 -0
  68. package/src/mcp/tools/machine.ts +97 -0
  69. package/src/mcp/tools/media.ts +106 -0
  70. package/src/mcp/tools/memory.ts +203 -0
  71. package/src/mcp/tools/video.ts +260 -0
  72. package/src/native/acme.ts +116 -0
  73. package/src/native/discover.ts +141 -0
  74. package/src/native/dxa.ts +166 -0
  75. package/src/native/ghidra/analyze.ts +86 -0
  76. package/src/native/ghidra/index.ts +157 -0
  77. package/src/native/ghidra/language/c64rt_6510.cspec +34 -0
  78. package/src/native/ghidra/language/c64rt_6510.ldefs +15 -0
  79. package/src/native/ghidra/language/c64rt_6510.pspec +15 -0
  80. package/src/native/ghidra/language/c64rt_6510.slaspec +596 -0
  81. package/src/native/ghidra/scripts/C64Export.java +242 -0
  82. package/src/native/ghidra/scripts/C64Prepare.java +60 -0
  83. package/src/native/local.ts +34 -0
  84. package/src/native/processes.ts +350 -0
  85. package/src/native/run.ts +172 -0
  86. package/src/native/staging.ts +83 -0
  87. package/src/native/status.ts +101 -0
  88. package/src/native/types.ts +228 -0
  89. package/src/native/watchdog.ts +78 -0
  90. package/src/project.ts +39 -0
  91. package/src/protocol/framing.ts +132 -0
  92. package/src/protocol/messages.ts +252 -0
  93. package/src/protocol/tools.ts +247 -0
  94. package/src/protocol/vice.ts +1235 -0
  95. package/src/script.ts +59 -0
  96. package/src/time.ts +15 -0
  97. package/src/trace.ts +162 -0
  98. package/THIRD-PARTY-NOTICES.md +0 -26
  99. package/bin/cli.mjs +0 -283
  100. package/skills/acme-build/SKILL.md +0 -260
  101. package/skills/acme-build/scripts/acme.mjs +0 -341
  102. package/skills/acme-build/template.a +0 -53
  103. package/skills/c64-disk-access/SKILL.md +0 -159
  104. package/skills/c64-disk-access/scripts/c1541.mjs +0 -569
  105. package/skills/c64-memory-mapping/SKILL.md +0 -631
  106. package/skills/c64-memory-mapping/memmap.json +0 -8800
  107. package/skills/c64-memory-mapping/scripts/driver.mjs +0 -553
  108. package/skills/c64-petcat/SKILL.md +0 -87
  109. package/skills/c64-petcat/scripts/petcat.mjs +0 -221
  110. package/skills/c64-program-recon/SKILL.md +0 -706
  111. package/skills/c64-program-recon/references/control-flow.md +0 -180
  112. package/skills/c64-program-recon/references/graphics.md +0 -79
  113. package/skills/c64-program-recon/references/observation-hazards.md +0 -143
  114. package/skills/c64-program-recon/references/reconstruction.md +0 -133
  115. package/skills/c64-program-recon/references/sound-and-input.md +0 -74
  116. package/skills/c64-program-recon/references/tool-selection.md +0 -78
  117. package/skills/c64-program-recon/scripts/derive.mjs +0 -364
  118. package/skills/c64-program-recon/scripts/packer-finding.mjs +0 -709
  119. package/skills/c64-program-recon/templates/memory-map.template.md +0 -101
  120. package/skills/c64-provenance-diff/SKILL.md +0 -292
  121. package/skills/c64-provenance-diff/scripts/diff-images.mjs +0 -984
  122. package/skills/c64-provenance-diff/scripts/recovery-schema.mjs +0 -395
  123. package/skills/c64-ram-capture/RELEASES.json.example +0 -17
  124. package/skills/c64-ram-capture/SKILL.md +0 -474
  125. package/skills/c64-ram-capture/scripts/compare-cross-binary.mjs +0 -525
  126. package/skills/c64-ram-capture/scripts/compare.mjs +0 -258
  127. package/skills/c64-ram-capture/scripts/derive-transients.mjs +0 -575
  128. package/skills/c64-ram-capture/scripts/dump-artifacts.mjs +0 -317
  129. package/skills/c64-ram-capture/scripts/mcp-module.mjs +0 -174
  130. package/skills/c64-ram-capture/scripts/project-paths.mjs +0 -81
  131. package/skills/c64-ram-capture/scripts/releases.mjs +0 -109
  132. package/skills/c64-ram-capture/scripts/vsf-slice.mjs +0 -147
  133. package/skills/c64-ram-capture/scripts/watch-loads.mjs +0 -581
  134. package/skills/c64-ram-capture/templates/capture-record.template.md +0 -99
  135. package/skills/c64-ram-capture/transients/README.md +0 -137
  136. package/skills/routine-queue-walker/SKILL.md +0 -394
  137. package/skills/routine-queue-walker/scripts/completeness-report.mjs +0 -465
  138. 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,214 @@
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 and start
21
+
22
+ Requirements:
23
+
24
+ - Node.js 24 or newer with npx.
25
+ - On the machine with VICE: VICE 3.9 or later (`x64sc`, `c1541`, `petcat`) with its ROM files.
26
+ - Where the agent runs: ACME, DXA and Ghidra, for the skills that use them.
27
+
28
+ Everything runs through npx. Nothing is installed globally.
29
+
30
+ ### Release or prerelease
31
+
32
+ | Channel | Version |
33
+ | --- | --- |
34
+ | `latest` | The current release. |
35
+ | `next` | The current prerelease, for example `2.0.0-rc.1`. |
36
+
37
+ Until 2.0.0 is released, `latest` is the old 1.1.0. Use `next` until then. In the commands below, replace `<channel>` with `latest` or `next`. Use the same channel on both machines.
38
+
39
+ ### On one machine
40
+
41
+ Start the Host Runtime in its own terminal. Ctrl+C stops it and every emulator:
42
+
43
+ ```
44
+ npx -y --package=@henols/c64-re-tools@<channel> c64-re-tools-host
45
+ ```
46
+
47
+ Install into the project, check it, and start the agent:
48
+
49
+ ```
50
+ cd my-c64-project
51
+ npx -y @henols/c64-re-tools@<channel> install --target claude
52
+ npx -y @henols/c64-re-tools@<channel> status
53
+ claude
54
+ ```
55
+
56
+ ### In a dev container
57
+
58
+ On the host, start the Host Runtime on the container bridge with a shared secret:
59
+
60
+ ```
61
+ export C64RT_HOST_TOKEN=$(openssl rand -hex 16)
62
+ npx -y --package=@henols/c64-re-tools@<channel> c64-re-tools-host --listen 172.17.0.1
63
+ ```
64
+
65
+ In the container, set the same `C64RT_HOST_TOKEN`, then:
66
+
67
+ ```
68
+ npx -y @henols/c64-re-tools@<channel> install --target claude
69
+ npx -y @henols/c64-re-tools@<channel> status
70
+ claude
71
+ ```
72
+
73
+ The container reaches the host as `host.docker.internal`. `status` must show the Host Runtime as reachable.
74
+
75
+ ### Update and remove
11
76
 
12
- ```sh
13
- npx @henols/c64-re-tools
14
77
  ```
78
+ npx -y @henols/c64-re-tools@<channel> update # the newest skills into the project
79
+ npx -y @henols/c64-re-tools@<channel> uninstall
80
+ ```
81
+
82
+ Restart the Host Runtime after a new release. `--global` installs into your home directory.
83
+
84
+ ### Debugging
85
+
86
+ See [docs/debugging.md](docs/debugging.md) for the trace and the field test.
87
+
88
+ ### Settings
89
+
90
+ | Variable | Read by | Meaning |
91
+ | --- | --- | --- |
92
+ | `C64RT_VICE` | host | Full path of `x64sc` when it is not on `PATH`. |
93
+ | `C64RT_C1541`, `C64RT_PETCAT` | host | Full path of the tool when it is not on `PATH`. |
94
+ | `C64RT_ACME`, `C64RT_DXA` | skills, CLI | Full path of the tool when it is not on `PATH`. |
95
+ | `C64RT_GHIDRA` | skills, CLI | The Ghidra installation directory, when `analyzeHeadless` is not on `PATH`. |
96
+ | `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. |
97
+ | `C64RT_HOST_TOKEN` | host, MCP, skills, CLI | The shared secret for a Host Runtime that listens beyond loopback. |
98
+ | `C64RT_VIDEO` | MCP | `pal` (default) or `ntsc`, fixed for the life of the MCP process. |
99
+ | `C64RT_TRACE` | host, MCP, skills, CLI | A directory for a trace of what the programs do, for a person who debugs a run. See [docs/debugging.md](docs/debugging.md). Nothing of the trace reaches the agent. |
100
+
101
+ Ghidra runs with its own settings directory for each request. The toolkit
102
+ brings its own NMOS 6510 language (all 256 opcodes); your Ghidra installation
103
+ and settings stay unchanged.
15
104
 
16
- That will:
105
+ ## Use it with an agent
17
106
 
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`
107
+ 1. Install for your harness: `--target claude`, `codex`, `pi`, `opencode`, `gemini`, `copilot`, `cursor` or `windsurf`. Without `--target`, `install` installs for all of them.
108
+ 2. Start the Host Runtime and leave it running.
109
+ 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.
110
+ 4. Ask for the result you want, in plain words. The agent picks the skills and the `c64_*` tools.
111
+
112
+ Examples:
113
+
114
+ ```
115
+ Reverse engineer original/game.d64. Find how it starts, unpack it if it is packed,
116
+ and name the main routines in project knowledge.
117
+
118
+ Find the routine that draws the score, and explain how it works.
119
+
120
+ Write a program in ACME that scrolls a text at the bottom of the screen.
121
+ Build it, run it in the emulator, and write a test for it.
122
+
123
+ Show me the emulator window.
124
+ ```
21
125
 
22
- Then restart Claude Code in that project.
126
+ The agent keeps what it learns about the program in `.c64-re-tools/knowledge.db`. Commit that file with the project.
23
127
 
24
- ### Options
128
+ ## Skills
25
129
 
26
- | Option | Effect |
130
+ | Skill | Use |
27
131
  | --- | --- |
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. |
132
+ | c64-reverse-engineering | Coordinates an investigation and picks the specialist for the next unknown. |
133
+ | c64-emulator | Runs and observes the program in VICE through the MCP tools. |
134
+ | c64-static-analysis | A fast first pass with DXA and a deeper pass with Ghidra into knowledge, with seeds from knowledge and decompilation. |
135
+ | c64-knowledge | Reads and writes symbols, regions, comments and references, with history. |
136
+ | c64-disk | Directory, BAM, entries, sector chains and extraction of disk images. |
137
+ | c64-basic | BASIC V2 listings and the SYS/USR handoff to machine code. |
138
+ | c64-unpacker | Packing evidence, and capture of the unpacked program from the emulator. |
139
+ | c64-memory-map | Platform meaning of addresses and register values. |
140
+ | c64-provenance | Byte comparison of releases. |
141
+ | c64-assembler | ACME builds with diagnostics and symbols. |
142
+ | c64-testing | Original-against-rebuild scenarios with PASS, FAIL or INCONCLUSIVE. |
143
+
144
+ ## Examples
145
+
146
+ Reverse engineering, from an unknown disk to a verified rebuild (the commands
147
+ that the skills run for the agent; `<skills>` is the installed skills
148
+ directory):
33
149
 
34
- Re-running is safe: existing skills and an existing `vice` entry are kept unless
35
- you pass `--force`.
150
+ ```
151
+ node <skills>/c64-disk/scripts/disk.ts directory original/game.d64
152
+ node <skills>/c64-disk/scripts/disk.ts read original/game.d64 GAME --out extracted/game.prg
153
+ node <skills>/c64-basic/scripts/basic.ts extracted/game.prg # the SYS address, e.g. $080d
154
+ node <skills>/c64-unpacker/scripts/unpack.ts inspect extracted/game.prg
155
+ node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --analyzer dxa --listing analysis/game.lst
156
+ node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --entry '$080d'
157
+ node <skills>/c64-knowledge/scripts/knowledge.ts rename '$080d' main --kind routine --reason 'entry from BASIC'
158
+ node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --entry '$080d' --decompile '$080d'
159
+ node <skills>/c64-assembler/scripts/assemble.ts --source-root src --entry game.a --out build/game.prg > build/game.json
160
+ node <skills>/c64-testing/scripts/test.ts tests/counts.json # PASS, FAIL or INCONCLUSIVE
161
+ ```
162
+
163
+ Development: build with the c64-assembler skill, then load and debug with the
164
+ MCP tools (`c64_program_load`, `c64_breakpoint`, `c64_run_until`,
165
+ `c64_memory_read`, `c64_screen`), and test with the c64-testing skill.
166
+
167
+ ## Developing
168
+
169
+ To work on the toolkit itself, Node 24 or newer and pnpm are required; install pnpm yourself (for example
170
+ `corepack enable pnpm`). `packageManager` in `package.json` pins its version.
171
+
172
+ ```
173
+ pnpm install --frozen-lockfile
174
+ pnpm typecheck # tsc checks everything; it emits nothing
175
+ pnpm test # node --test on src/**/*.test.ts and test/**/*.test.ts
176
+ ```
177
+
178
+ Unit tests sit beside their source as `src/**/*.test.ts`; `test/` holds
179
+ integration and end-to-end tests. Node runs the TypeScript directly (type
180
+ stripping), so relative imports name the `.ts` file. Skill scripts import
181
+ `src/` as `#src/...` (the `imports` map in `package.json`); an installed skill
182
+ gets the `src/` modules it reaches and its own `package.json` with that map.
36
183
 
37
- ## What gets installed
184
+ Tests that need real tools are opt-in. Without them they are reported as
185
+ skipped, never as passed:
38
186
 
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.
187
+ ```
188
+ C64RT_LIVE_VICE=/usr/bin/x64sc C64RT_GHIDRA=/opt/ghidra pnpm test
189
+ ```
42
190
 
43
- ## Requirements
191
+ ACME, DXA, c1541 and petcat tests run when the tools are on `PATH`.
44
192
 
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.
193
+ To release, push a tag on a commit of `main`. The tag gives the version:
194
+
195
+ ```
196
+ git tag v2.0.0-rc.2 && git push origin v2.0.0-rc.2 # a prerelease, to npm next
197
+ git tag v2.0.0 && git push origin v2.0.0 # a release, to npm latest
198
+ ```
49
199
 
50
- ## Alternative: Claude Code plugin
200
+ CI checks and tests the tagged commit, publishes it to npm and makes a GitHub Release. The repository keeps the version `0.0.0-development`.
51
201
 
52
- If you use Claude Code, you can instead install everything as a plugin:
202
+ ## Layout
53
203
 
54
204
  ```
55
- /plugin marketplace add henols/c64-re-tools
56
- /plugin install c64-re-tools@c64-re-tools
205
+ src/ # mcp, host, host-client, native, knowledge, cli; unit tests beside the source
206
+ skills/ # the skills: SKILL.md, scripts (TypeScript) and references
207
+ distribution/ # the AP SDK plugin definition: skills with the src/ modules they reach
208
+ test/ # fixtures, integration and end-to-end tests
209
+ docs/redesign/ # the design
57
210
  ```
58
211
 
59
212
  ## License
60
213
 
61
- MIT © Henrik Olsson
214
+ MIT. See [`LICENSE`](./LICENSE).
@@ -0,0 +1,36 @@
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 { readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+
8
+ /**
9
+ * The npm dist-tag that a version is published under: a prerelease
10
+ * (2.0.0-rc.1) goes to "next", a release to "latest".
11
+ */
12
+ export function channelOf(version: string): "latest" | "next" {
13
+ return version.includes("-") ? "next" : "latest";
14
+ }
15
+
16
+ /** The name, version and dist-tag of this npm package, from its package.json. */
17
+ export const PACKAGE = (() => {
18
+ const metadata = JSON.parse(readFileSync(join(import.meta.dirname, "..", "package.json"), "utf8")) as { name: string; version: string };
19
+ return { name: metadata.name, version: metadata.version, tag: channelOf(metadata.version) };
20
+ })();
21
+
22
+ /**
23
+ * The agent starts the MCP server through npx with the newest version of the
24
+ * channel it was installed from, so it updates itself and an install from a
25
+ * prerelease does not start a release; nothing is linked or installed globally.
26
+ * The entry point is TypeScript, run by tsx: Node does not strip types under
27
+ * node_modules.
28
+ */
29
+ export function mcpServerFor(platform: NodeJS.Platform): { command: string; args: string[] } {
30
+ const npx = ["-y", `--package=${PACKAGE.name}@${PACKAGE.tag}`, "c64-re-tools-mcp"];
31
+ // On native Windows npx is a .cmd shim, which a harness starts only through cmd /c.
32
+ return platform === "win32" ? { command: "cmd", args: ["/c", "npx", ...npx] } : { command: "npx", args: npx };
33
+ }
34
+
35
+ /** The declaration for the machine where install runs: the plugin is evaluated there. */
36
+ export const MCP_SERVER = mcpServerFor(process.platform);
@@ -0,0 +1,120 @@
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 { MCP_SERVER, PACKAGE } from "./package.ts";
17
+
18
+ const root = resolve(import.meta.dirname, "..");
19
+ const SOURCE = join(root, "src");
20
+ const SCRIPT_REFERENCE = /scripts\/([a-z][a-z0-9-]*)\.ts/g;
21
+ /**
22
+ * The modules a file reaches: static and dynamic imports, re-exports, and
23
+ * new URL("...", import.meta.url) for a module that is spawned or an asset
24
+ * directory. Only relative and #src specifiers; packages are node: built-ins.
25
+ */
26
+ const SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\bnew URL\(\s*)(["'])((?:\.\.?\/|#src\/)[^"']*)\1/g;
27
+
28
+ /**
29
+ * The package.json of an installed skill: ES modules, #src/* resolved inside
30
+ * the skill, and the version of c64-re-tools that the copy comes from.
31
+ */
32
+ export const SKILL_PACKAGE = `${JSON.stringify({ type: "module", version: PACKAGE.version, imports: { "#src/*": "./src/*" } }, null, 2)}\n`;
33
+
34
+ /** The scripts that a SKILL.md runs, by file name. */
35
+ export function scriptsOf(skillMarkdown: string): string[] {
36
+ return [...new Set([...skillMarkdown.matchAll(SCRIPT_REFERENCE)].map((match) => match[1]!))].sort();
37
+ }
38
+
39
+ function filesBelow(directory: string): string[] {
40
+ if (!existsSync(directory)) return [];
41
+ return readdirSync(directory, { recursive: true, encoding: "utf8" })
42
+ .map((path) => join(directory, path))
43
+ .filter((path) => statSync(path).isFile())
44
+ .sort();
45
+ }
46
+
47
+ const inside = (directory: string, path: string) => path === directory || path.startsWith(directory + sep);
48
+
49
+ /**
50
+ * Every file that the given scripts reach, transitively: the scripts, their
51
+ * sibling modules, and the src/ modules and asset directories below them.
52
+ * A reach out of the skill directory and src/ is an error.
53
+ */
54
+ export function closureOf(skillDirectory: string, scripts: readonly string[]): string[] {
55
+ const found = new Set<string>();
56
+ const pending = [...scripts];
57
+ while (pending.length > 0) {
58
+ const file = pending.pop()!;
59
+ if (found.has(file)) continue;
60
+ if (!inside(SOURCE, file) && !inside(skillDirectory, file)) throw new Error(`${relative(root, file)} is outside the skill and src/`);
61
+ if (!existsSync(file)) throw new Error(`${relative(root, file)} does not exist`);
62
+ if (statSync(file).isDirectory()) {
63
+ pending.push(...filesBelow(file));
64
+ continue;
65
+ }
66
+ found.add(file);
67
+ if (!file.endsWith(".ts")) continue;
68
+ for (const match of readFileSync(file, "utf8").matchAll(SPECIFIER)) {
69
+ const specifier = match[2]!;
70
+ pending.push(specifier.startsWith("#src/") ? join(SOURCE, specifier.slice("#src/".length)) : resolve(dirname(file), specifier));
71
+ }
72
+ }
73
+ return [...found].sort();
74
+ }
75
+
76
+ /** One skill: SKILL.md frontmatter and body, scripts (executable), the src/ modules they reach, and references. */
77
+ function loadSkill(directory: string): Skill {
78
+ const text = readFileSync(join(directory, "SKILL.md"), "utf8");
79
+ const frontmatter = /^---\n([\s\S]*?)\n---\n/.exec(text);
80
+ if (frontmatter === null) throw new Error(`${directory}/SKILL.md has no frontmatter`);
81
+ const field = (name: string) => {
82
+ const value = new RegExp(`^${name}: (.+)$`, "m").exec(frontmatter[1]!)?.[1];
83
+ if (value === undefined) throw new Error(`${directory}/SKILL.md has no ${name}`);
84
+ return value;
85
+ };
86
+ const scripts = scriptsOf(text).map((script) => join(directory, "scripts", `${script}.ts`));
87
+ const code = closureOf(directory, scripts).map((path) => ({
88
+ path: (inside(SOURCE, path) ? relative(root, path) : relative(directory, path)).split("\\").join("/"),
89
+ content: readFileSync(path, "utf8"),
90
+ ...(scripts.includes(path) ? { executable: true } : {}),
91
+ }));
92
+ const references = filesBelow(join(directory, "references")).map((path) => ({ path: relative(directory, path).split("\\").join("/"), content: readFileSync(path, "utf8") }));
93
+ const resources = [...code, ...references, ...(scripts.length > 0 ? [{ path: "package.json", content: SKILL_PACKAGE }] : [])];
94
+ return defineSkill({
95
+ name: field("name"),
96
+ description: field("description"),
97
+ instructions: text.slice(frontmatter[0].length).replace(/^\n+/, ""),
98
+ ...(resources.length > 0 ? { resources } : {}),
99
+ });
100
+ }
101
+
102
+ function c64Plugin() {
103
+ const skills = readdirSync(join(root, "skills"), { withFileTypes: true })
104
+ .filter((entry) => entry.isDirectory())
105
+ .map((entry) => entry.name)
106
+ .sort()
107
+ .map((name) => loadSkill(join(root, "skills", name)));
108
+ return definePlugin({
109
+ id: "c64-re-tools",
110
+ version: PACKAGE.version,
111
+ description: "Commodore 64 reverse engineering and development: a live VICE emulator, static analysis, disk and BASIC tools, project knowledge, building and testing.",
112
+ author: { name: "Henrik Olsson" },
113
+ homepage: "https://github.com/henols/c64-re-tools",
114
+ license: "MIT",
115
+ skills,
116
+ mcpServers: { "c64-re-tools": MCP_SERVER },
117
+ });
118
+ }
119
+
120
+ export default c64Plugin();
package/package.json CHANGED
@@ -1,55 +1,49 @@
1
1
  {
2
2
  "name": "@henols/c64-re-tools",
3
- "version": "1.1.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.2",
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.1.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,73 @@
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
+
35
+ The script copies the full source root. Put all files that the program includes under it. Version control directories stay out.
36
+
37
+ The script prints one JSON object. The target CPU is the 6510 and the output is always a C64 PRG.
38
+
39
+ ## Workflow
40
+
41
+ 1. Find the source root and the entry source. The entry source sets its own start address with `* = $0801` or similar.
42
+ 2. Run the script.
43
+ 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`.
44
+ 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.
45
+ 5. To run the program, give the PRG path to the c64-emulator skill.
46
+
47
+ ## Knowledge
48
+
49
+ Automatic write:
50
+ - none.
51
+
52
+ Do not store:
53
+ - diagnostics
54
+ - build output
55
+
56
+ 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.
57
+
58
+ ## Result
59
+
60
+ The PRG in the project at the output path, with its load range and symbols. Or the source diagnostics when the assembly failed.
61
+
62
+ The exit status is 0 for a PRG. The exit status is 1 for `assembled: false` and for an error. The exit status is 2 for a wrong argument.
63
+
64
+ ## Failure and conflicts
65
+
66
+ - `assembled: false`: the source has errors. This is a normal result. Correct the source.
67
+ - `installation-incomplete`: ACME is not installed on this machine. Tell the user the message.
68
+ - `invalid-input`: an option or a path is wrong. Read the message and correct the call.
69
+
70
+ ## Handoffs
71
+
72
+ - Run or debug the program → c64-emulator
73
+ - Compare the rebuild with the original → c64-testing