c64bridge 0.5.1 → 0.6.0
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/README.md +17 -5
- package/data/assembly/assembly-spec.md +24 -0
- package/data/assembly/examples/video/hello_world.asm +39 -0
- package/data/basic/basic-pitfalls.md +236 -0
- package/data/basic/basic-spec.md +507 -198
- package/data/video/petscii-style-guide.md +105 -0
- package/data/video/sprite-charset-best-practices.md +292 -0
- package/dist/c64Client.js +1 -0
- package/dist/prompts/registry.js +10 -2
- package/dist/rag/knowledgeIndex.js +111 -24
- package/dist/rag/retriever.js +110 -4
- package/dist/tools/audio.js +26 -13
- package/dist/tools/developer.js +34 -0
- package/dist/tools/graphics.js +6 -5
- package/dist/tools/memory.js +1 -1
- package/dist/tools/pollValidator.js +256 -0
- package/dist/tools/programRunners.js +352 -12
- package/dist/tools/rag.js +1 -7
- package/doc/MCP_SETUP.md +7 -1
- package/doc/bun/baseline-metrics.md +16 -0
- package/doc/bun/migration-checklist.md +55 -0
- package/doc/bun/migration.md +83 -0
- package/doc/developer.md +72 -10
- package/doc/improve-knowledge/notes.md +29 -0
- package/doc/improve-knowledge/plan.md +28 -3
- package/doc/improve-knowledge/rollout.md +39 -5
- package/doc/meta-tools/README.md +101 -0
- package/doc/meta-tools/analysis.md +255 -0
- package/doc/{meta-tools.md → meta-tools/catalog.md} +2 -2
- package/doc/meta-tools/notes.md +174 -0
- package/doc/meta-tools/rollout.md +343 -0
- package/mcp.json +1 -1
- package/package.json +17 -10
- package/scripts/c64-cli.mjs +42 -7
- package/scripts/c64-tool.sh +7 -1
- package/scripts/check-node-compat.mjs +107 -0
- package/scripts/invoke-bun.mjs +58 -0
- package/scripts/rag-rebuild.mjs +11 -13
- package/scripts/run-mcp-check.sh +10 -2
- package/scripts/run-tests.mjs +3 -3
- package/scripts/run-tests.ts +187 -0
- package/scripts/start.mjs +3 -26
- package/scripts/update-readme.ts +17 -1
- package/scripts/verify-package.mjs +0 -1
- package/data/assembly/examples/effects/sinewave.asm +0 -5
- package/doc/meta-tools-rollout.md +0 -211
- package/scripts/register-ts-node.mjs +0 -6
- /package/data/assembly/examples/{graphics/sprite/mover.asm → video/sprite_mover.asm} +0 -0
package/scripts/rag-rebuild.mjs
CHANGED
|
@@ -21,24 +21,22 @@ async function main() {
|
|
|
21
21
|
let LocalMiniHashEmbedding;
|
|
22
22
|
let buildAllIndexes;
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const isBun = typeof globalThis.Bun !== 'undefined';
|
|
25
|
+
|
|
26
|
+
if (!isBun && await fileExists(distEmbeddings) && await fileExists(distIndexer)) {
|
|
25
27
|
const em = await import(pathToFileURL(distEmbeddings).href);
|
|
26
28
|
const ix = await import(pathToFileURL(distIndexer).href);
|
|
27
29
|
LocalMiniHashEmbedding = em.LocalMiniHashEmbedding;
|
|
28
30
|
buildAllIndexes = ix.buildAllIndexes;
|
|
31
|
+
} else if (isBun) {
|
|
32
|
+
const em = await import(pathToFileURL(path.join(projectRoot, 'src', 'rag', 'embeddings.ts')).href);
|
|
33
|
+
const ix = await import(pathToFileURL(path.join(projectRoot, 'src', 'rag', 'indexer.ts')).href);
|
|
34
|
+
LocalMiniHashEmbedding = em.LocalMiniHashEmbedding;
|
|
35
|
+
buildAllIndexes = ix.buildAllIndexes;
|
|
29
36
|
} else {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const em = await import(pathToFileURL(path.join(projectRoot, 'src', 'rag', 'embeddings.ts')).href);
|
|
34
|
-
const ix = await import(pathToFileURL(path.join(projectRoot, 'src', 'rag', 'indexer.ts')).href);
|
|
35
|
-
LocalMiniHashEmbedding = em.LocalMiniHashEmbedding;
|
|
36
|
-
buildAllIndexes = ix.buildAllIndexes;
|
|
37
|
-
} catch (err) {
|
|
38
|
-
console.error('[rag-rebuild] Unable to load RAG modules from dist or src:', err);
|
|
39
|
-
process.exit(1);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
37
|
+
console.error('[rag-rebuild] Unable to locate compiled artifacts. Run `bun run build` (or `npm run build`) before invoking this script under Node.');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
return;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
const dim = Number(process.env.RAG_EMBED_DIM ?? 384);
|
package/scripts/run-mcp-check.sh
CHANGED
|
@@ -15,6 +15,14 @@ TMPDIR=$(mktemp -d)
|
|
|
15
15
|
MOCK_INFO="$TMPDIR/mock-info.json"
|
|
16
16
|
CFG_FILE="$TMPDIR/.c64bridge.json"
|
|
17
17
|
|
|
18
|
+
if command -v bun >/dev/null 2>&1; then
|
|
19
|
+
BUILD_CMD=(bun run build)
|
|
20
|
+
elif [[ -x "${HOME}/.bun/bin/bun" ]]; then
|
|
21
|
+
BUILD_CMD=("${HOME}/.bun/bin/bun" run build)
|
|
22
|
+
else
|
|
23
|
+
BUILD_CMD=(npm run build)
|
|
24
|
+
fi
|
|
25
|
+
|
|
18
26
|
cleanup() {
|
|
19
27
|
set +e
|
|
20
28
|
[[ -n "${MCP_PID:-}" ]] && kill -TERM "$MCP_PID" 2>/dev/null || true
|
|
@@ -68,7 +76,7 @@ echo "==> Starting MCP server in '$MODE' mode for $DURATION seconds..."
|
|
|
68
76
|
echo "==> Logs will be written to: $(realpath "$LOGFILE")"
|
|
69
77
|
|
|
70
78
|
if [[ "$MODE" == "local" ]]; then
|
|
71
|
-
(cd "$ROOT_DIR" &&
|
|
79
|
+
(cd "$ROOT_DIR" && "${BUILD_CMD[@]}")
|
|
72
80
|
VERSION=$(node -p "require('$ROOT_DIR/package.json').version")
|
|
73
81
|
echo "==> Running local build version $VERSION"
|
|
74
82
|
(cd "$ROOT_DIR" && node dist/index.js) 2>&1 | tee "$LOGFILE" &
|
|
@@ -101,4 +109,4 @@ else
|
|
|
101
109
|
exit 1
|
|
102
110
|
fi
|
|
103
111
|
|
|
104
|
-
echo "==> Run complete. Logs saved to: $(realpath "$LOGFILE")"
|
|
112
|
+
echo "==> Run complete. Logs saved to: $(realpath "$LOGFILE")"
|
package/scripts/run-tests.mjs
CHANGED
|
@@ -3,15 +3,15 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { fileURLToPath
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
8
|
const DEFAULT_TARGET = "mock";
|
|
9
9
|
|
|
10
10
|
let target = DEFAULT_TARGET;
|
|
11
11
|
let explicitBaseUrl = null;
|
|
12
12
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
-
const
|
|
14
|
-
const nodeArgs = ["--import",
|
|
13
|
+
const registerDataUri = "data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));";
|
|
14
|
+
const nodeArgs = ["--import", registerDataUri, "--test"];
|
|
15
15
|
|
|
16
16
|
for (const arg of process.argv.slice(2)) {
|
|
17
17
|
if (arg === "--mock") {
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const DEFAULT_TARGET = "mock";
|
|
9
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
const defaultEmbeddingsDir = path.join(repoRoot, "artifacts", "test-embeddings");
|
|
11
|
+
|
|
12
|
+
let target = DEFAULT_TARGET;
|
|
13
|
+
let explicitBaseUrl: string | null = null;
|
|
14
|
+
let runCoverage = false;
|
|
15
|
+
const passthrough: string[] = [];
|
|
16
|
+
|
|
17
|
+
for (const arg of process.argv.slice(2)) {
|
|
18
|
+
if (arg === "--mock") {
|
|
19
|
+
target = "mock";
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (arg === "--real") {
|
|
23
|
+
target = "real";
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (arg.startsWith("--target=")) {
|
|
27
|
+
target = arg.split("=", 2)[1] ?? DEFAULT_TARGET;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (arg.startsWith("--base-url=")) {
|
|
31
|
+
explicitBaseUrl = arg.split("=", 2)[1] ?? null;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (arg === "--coverage") {
|
|
35
|
+
runCoverage = true;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
passthrough.push(arg);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!fs.existsSync(defaultEmbeddingsDir)) {
|
|
42
|
+
fs.mkdirSync(defaultEmbeddingsDir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const env: Record<string, string> = { ...process.env } as Record<string, string>;
|
|
46
|
+
env.C64_TEST_TARGET = target;
|
|
47
|
+
if (!env.RAG_EMBEDDINGS_DIR) {
|
|
48
|
+
env.RAG_EMBEDDINGS_DIR = defaultEmbeddingsDir;
|
|
49
|
+
}
|
|
50
|
+
if (explicitBaseUrl) {
|
|
51
|
+
env.C64_TEST_BASE_URL = explicitBaseUrl;
|
|
52
|
+
}
|
|
53
|
+
if (target === "real" && !env.C64_TEST_BASE_URL) {
|
|
54
|
+
env.C64_TEST_BASE_URL = resolveBaseUrlFromConfig() ?? "http://c64u";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const bunExecutable = process.execPath;
|
|
58
|
+
const cmd = [
|
|
59
|
+
bunExecutable,
|
|
60
|
+
"test",
|
|
61
|
+
...(runCoverage
|
|
62
|
+
? [
|
|
63
|
+
"--coverage",
|
|
64
|
+
// Ensure LCOV is emitted for Codecov
|
|
65
|
+
"--coverage-reporter=lcov",
|
|
66
|
+
// Use supported console reporter in Bun
|
|
67
|
+
"--coverage-reporter=text",
|
|
68
|
+
]
|
|
69
|
+
: []),
|
|
70
|
+
...passthrough,
|
|
71
|
+
];
|
|
72
|
+
const child = Bun.spawn({
|
|
73
|
+
cmd,
|
|
74
|
+
cwd: repoRoot,
|
|
75
|
+
env,
|
|
76
|
+
stdout: "inherit",
|
|
77
|
+
stderr: "inherit",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const exitCode = await child.exited;
|
|
81
|
+
process.exit(typeof exitCode === "number" ? exitCode : 1);
|
|
82
|
+
|
|
83
|
+
function resolveBaseUrlFromConfig(): string | null {
|
|
84
|
+
const configPathEnv = env.C64BRIDGE_CONFIG;
|
|
85
|
+
const homeConfig = os.homedir() ? path.join(os.homedir(), ".c64bridge.json") : null;
|
|
86
|
+
const repoConfig = path.join(repoRoot, ".c64bridge.json");
|
|
87
|
+
for (const candidate of [configPathEnv, homeConfig, repoConfig]) {
|
|
88
|
+
if (!candidate) continue;
|
|
89
|
+
try {
|
|
90
|
+
const raw = JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
91
|
+
const base = resolveBaseUrlFromJson(raw);
|
|
92
|
+
if (base) return base;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (isSystemError(error) && error.code === "ENOENT") {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
console.warn(`[run-tests] Failed to read config at ${candidate}:`, error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function resolveBaseUrlFromJson(raw: unknown): string | null {
|
|
104
|
+
if (!raw || typeof raw !== "object") return null;
|
|
105
|
+
const root = raw as Record<string, unknown>;
|
|
106
|
+
const c64u = root.c64u;
|
|
107
|
+
if (c64u && typeof c64u === "object") {
|
|
108
|
+
const base = stringIfSet((c64u as Record<string, unknown>).baseUrl);
|
|
109
|
+
if (base) return normalizeBase(base);
|
|
110
|
+
const hostEntry = firstDefined(
|
|
111
|
+
stringIfSet((c64u as Record<string, unknown>).host),
|
|
112
|
+
stringIfSet((c64u as Record<string, unknown>).hostname),
|
|
113
|
+
);
|
|
114
|
+
const parsed = parseEndpoint(hostEntry);
|
|
115
|
+
const port = firstDefined(numberIfPort((c64u as Record<string, unknown>).port), parsed.port, DEFAULT_PORT);
|
|
116
|
+
if (parsed.hostname) {
|
|
117
|
+
return buildBaseUrl(parsed.hostname, port);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const legacyBase = stringIfSet(root.baseUrl);
|
|
121
|
+
if (legacyBase) return normalizeBase(legacyBase);
|
|
122
|
+
const legacyHost = firstDefined(stringIfSet(root.c64_host), stringIfSet(root.c64_ip));
|
|
123
|
+
if (legacyHost) {
|
|
124
|
+
const parsed = parseEndpoint(legacyHost);
|
|
125
|
+
const port = firstDefined(parsed.port, DEFAULT_PORT);
|
|
126
|
+
const hostname = parsed.hostname ?? legacyHost;
|
|
127
|
+
return buildBaseUrl(hostname, port);
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function stringIfSet(value: unknown): string | null {
|
|
133
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function numberIfPort(value: unknown): number | null {
|
|
137
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535) {
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
if (typeof value === "string" && value.trim()) {
|
|
141
|
+
const parsed = Number(value.trim());
|
|
142
|
+
if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) {
|
|
143
|
+
return parsed;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeBase(input: string): string {
|
|
150
|
+
return /^[a-z][a-z0-9+.-]*:\/\//i.test(input) ? input.replace(/\/+$/, "") : `http://${input}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function parseEndpoint(value: string | null): { hostname?: string; port?: number } {
|
|
154
|
+
const input = value && value.trim() ? value.trim() : null;
|
|
155
|
+
if (!input) return {};
|
|
156
|
+
try {
|
|
157
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(input);
|
|
158
|
+
const url = new URL(hasScheme ? input : `http://${input}`);
|
|
159
|
+
const hostname = url.hostname || undefined;
|
|
160
|
+
const port = url.port ? numberIfPort(url.port) ?? undefined : undefined;
|
|
161
|
+
return { hostname, port };
|
|
162
|
+
} catch {
|
|
163
|
+
return {};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const DEFAULT_PORT = 80;
|
|
168
|
+
|
|
169
|
+
function buildBaseUrl(host: string, port: number | undefined): string {
|
|
170
|
+
const normalizedPort = Number.isInteger(port) && (port as number) > 0 ? (port as number) : DEFAULT_PORT;
|
|
171
|
+
const hostPart = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
172
|
+
const suffix = normalizedPort === DEFAULT_PORT ? "" : `:${normalizedPort}`;
|
|
173
|
+
return `http://${hostPart}${suffix}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function firstDefined<T>(...values: Array<T | undefined | null>): T | undefined {
|
|
177
|
+
for (const value of values) {
|
|
178
|
+
if (value !== undefined && value !== null) {
|
|
179
|
+
return value;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function isSystemError(error: unknown): error is NodeJS.ErrnoException {
|
|
186
|
+
return Boolean(error && typeof error === "object" && "code" in error);
|
|
187
|
+
}
|
package/scripts/start.mjs
CHANGED
|
@@ -27,36 +27,13 @@ async function fileExists(filePath) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function isModuleNotFound(error) {
|
|
31
|
-
if (!error || typeof error !== 'object') {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
const err = error;
|
|
35
|
-
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
const message = typeof err.message === 'string' ? err.message : '';
|
|
39
|
-
return message.includes('Cannot find module');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
30
|
async function launch() {
|
|
43
31
|
const srcEntry = path.resolve(projectRoot, 'src/index.ts');
|
|
44
32
|
const distEntry = path.resolve(projectRoot, 'dist/index.js');
|
|
45
33
|
|
|
46
|
-
if (await fileExists(srcEntry)) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
await import(pathToFileURL(srcEntry).href);
|
|
50
|
-
return;
|
|
51
|
-
} catch (error) {
|
|
52
|
-
if (isModuleNotFound(error)) {
|
|
53
|
-
console.warn('[start] ts-node not available; falling back to compiled output.');
|
|
54
|
-
} else {
|
|
55
|
-
console.error('[start] Failed to launch TypeScript sources:', error);
|
|
56
|
-
process.exitCode = 1;
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
34
|
+
if (typeof globalThis.Bun !== 'undefined' && await fileExists(srcEntry)) {
|
|
35
|
+
await import(pathToFileURL(srcEntry).href);
|
|
36
|
+
return;
|
|
60
37
|
}
|
|
61
38
|
|
|
62
39
|
if (await fileExists(distEntry)) {
|
package/scripts/update-readme.ts
CHANGED
|
@@ -126,8 +126,24 @@ function renderPromptsSection(): string[] {
|
|
|
126
126
|
];
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
function renderSummarySection(): string[] {
|
|
130
|
+
const modules = describeToolModules();
|
|
131
|
+
const resources = listKnowledgeResources();
|
|
132
|
+
const promptRegistry = createPromptRegistry();
|
|
133
|
+
const prompts = promptRegistry.list();
|
|
134
|
+
|
|
135
|
+
const toolCount = modules.reduce((sum, module) => sum + module.tools.length, 0);
|
|
136
|
+
const resourceCount = resources.length;
|
|
137
|
+
const promptCount = prompts.length;
|
|
138
|
+
|
|
139
|
+
return [
|
|
140
|
+
`This MCP server exposes **${toolCount} tools**, **${resourceCount} resources**, and **${promptCount} prompts** for controlling your Commodore 64.`,
|
|
141
|
+
"",
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
|
|
129
145
|
function buildDocumentation(): string {
|
|
130
|
-
const sections = [renderToolsSection(), renderResourcesSection(), renderPromptsSection()];
|
|
146
|
+
const sections = [renderSummarySection(), renderToolsSection(), renderResourcesSection(), renderPromptsSection()];
|
|
131
147
|
return sections.flat().join("\n").trim();
|
|
132
148
|
}
|
|
133
149
|
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
## Meta Tools Rollout Plan for c64bridge
|
|
2
|
-
|
|
3
|
-
Purpose: Provide a concrete, dependency-aware rollout approach for the meta tools defined in `doc/meta-tools.md`. This plan gives ordered, checkable task lists and high-level implementation notes. It is written so another LLM can execute the rollout autonomously.
|
|
4
|
-
|
|
5
|
-
### Required reading
|
|
6
|
-
|
|
7
|
-
The following documents need to be read and understood before starting the rollout:
|
|
8
|
-
|
|
9
|
-
- `doc/meta-tools.md` — Meta tools definitions, effort/benefit/dependencies matrix
|
|
10
|
-
- `doc/agent-state-spec.md` — Background task registry and persistence model
|
|
11
|
-
- `doc/developer.md` — Architecture overview and contribution guidelines
|
|
12
|
-
|
|
13
|
-
### Operator rules for the LLM performing the rollout
|
|
14
|
-
|
|
15
|
-
- Always work tasks strictly in order as listed. Do not skip ahead unless a task is cancelled.
|
|
16
|
-
- After each checklist item, run the full build and test suite and proceed only if it passes:
|
|
17
|
-
- Preferred: `npm run check` (build + tests) or `npm test` if already built.
|
|
18
|
-
- If tests fail:
|
|
19
|
-
- Fix the cause if obvious and retry once.
|
|
20
|
-
- If still failing, revert the last change and leave a short note in the status log, then stop.
|
|
21
|
-
- Keep edits minimal and focused. Prefer small, incremental changes.
|
|
22
|
-
- Update documentation in the same change when tool behavior or surface changes.
|
|
23
|
-
- For any new code, add/extend tests that cover basic happy path and one failure path.
|
|
24
|
-
- Respect device-affecting safeguards already in the codebase (pause/resume, stream mutual exclusion, time limits).
|
|
25
|
-
|
|
26
|
-
### Global implementation template (apply per meta tool)
|
|
27
|
-
|
|
28
|
-
- [ ] Read the tool’s entry in `doc/meta-tools.md` and its Effort/Benefit/Dependencies.
|
|
29
|
-
- [ ] Verify prerequisites (dependencies implemented). If missing, pause and complete dependencies first.
|
|
30
|
-
- [ ] Implement the meta tool wrapper in the MCP server (compose existing low-level tools; avoid firmware-only assumptions).
|
|
31
|
-
- [ ] Add or extend unit/integration tests for the meta tool’s happy path and a basic failure case.
|
|
32
|
-
- [ ] Update docs: ensure the tool’s documentation is aligned; add usage hints if applicable.
|
|
33
|
-
- [ ] Run full build + tests: `npm run check` must pass.
|
|
34
|
-
|
|
35
|
-
Notes:
|
|
36
|
-
|
|
37
|
-
- Do not expand the REST surface; meta tools must compose existing MCP tools.
|
|
38
|
-
- Ensure mutual exclusion where streams and debug capture conflict.
|
|
39
|
-
- Enforce timeouts and guardrails for background/scheduled operations.
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
### Phase 0 — Foundations (low-effort, high-benefit, unlocks others)
|
|
44
|
-
|
|
45
|
-
- [x] `firmware_info_and_healthcheck` — Implementation notes:
|
|
46
|
-
- Compose version, info, and a tiny `readmem` probe. Return structured readiness.
|
|
47
|
-
- Use short, bounded timeouts; capture latency metrics.
|
|
48
|
-
- [x] `wait_for_screen_text` — Implementation notes:
|
|
49
|
-
- Poll screen RAM via existing helper; support regex and plain text. Provide timeout and interval.
|
|
50
|
-
- Return match bounds and elapsed time.
|
|
51
|
-
- [x] `verify_and_write_memory` — Implementation notes:
|
|
52
|
-
- Pause → read → verify → write → read-back → resume. Abort on mismatch.
|
|
53
|
-
- Parameterize verification masks and expected buffer; return a diff when failing.
|
|
54
|
-
- [x] `start_background_task` / `stop_background_task` / `list_background_tasks` / `stop_all_background_tasks` — Implementation notes:
|
|
55
|
-
- Implement a persistent registry (in-memory with serialized state to disk if available).
|
|
56
|
-
- Support fixed-interval scheduling and a maximum iterations cap.
|
|
57
|
-
- [x] `find_paths_by_name` — Implementation notes:
|
|
58
|
-
- Container-aware discovery using wildcard info endpoints; return host and logical paths.
|
|
59
|
-
- Cache recent results with a short TTL to avoid repeated scans.
|
|
60
|
-
- [x] `memory_dump_to_file` — Implementation notes:
|
|
61
|
-
- Chunked reads with retries; optional pause/resume. Support hex and binary formats.
|
|
62
|
-
- Emit a checksum and write a manifest alongside the dump.
|
|
63
|
-
- [x] `config_snapshot_and_restore` — Implementation notes:
|
|
64
|
-
- Read all categories, persist versioned JSON; support batch restore and diff preview.
|
|
65
|
-
|
|
66
|
-
Requirement: After each checkbox completion, run `npm run check` and proceed only on success.
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
### Phase 1 — Orchestration and assertions
|
|
71
|
-
|
|
72
|
-
- [x] `program_shuffle` — Implementation notes:
|
|
73
|
-
- Discover PRG/CRT under root; run each for a duration; capture screen; soft reset.
|
|
74
|
-
- Persist a light run log per program.
|
|
75
|
-
- [x] `batch_run_with_assertions` — Implementation notes:
|
|
76
|
-
- Accept assertions: screen contains text, memory equals byte, SID silent.
|
|
77
|
-
- Produce junit-like results; stop on failure or continue-on-error per flag.
|
|
78
|
-
- [x] `bundle_run_artifacts` — Implementation notes:
|
|
79
|
-
- Gather screen, memory snapshot, debugreg; structure outputs per run id.
|
|
80
|
-
- [x] `compile_run_verify_cycle` — Implementation notes:
|
|
81
|
-
- Support BASIC/ASM/SID build routes; then run and verify via screen/audio.
|
|
82
|
-
- Archive artifacts in a run folder.
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
### Phase 2 — Filesystem and drives
|
|
87
|
-
|
|
88
|
-
- [ ] `drive_mount_and_verify` — Implementation notes:
|
|
89
|
-
- Power on if needed; mount; reset; verify via drives list. Retry with backoff.
|
|
90
|
-
- [ ] `create_and_mount_blank_d64` — Implementation notes:
|
|
91
|
-
- Create blank D64; mount; optional BASIC header writer.
|
|
92
|
-
- [ ] `container_aware_walk_and_classify` — Implementation notes:
|
|
93
|
-
- Recurse host and container entries; classify BASIC vs PRG-MC via load + read.
|
|
94
|
-
- [ ] `classify_prg_basic_or_mc` — Implementation notes:
|
|
95
|
-
- For a single PRG; detect BASIC tokenization from header bytes.
|
|
96
|
-
- [ ] `filesystem_stats_by_extension` — Implementation notes:
|
|
97
|
-
- Compute counts and sizes; rollups per extension and per-folder/container.
|
|
98
|
-
- [ ] `dedup_scan` — Implementation notes:
|
|
99
|
-
- Group by size and extension; optional normalized names; optional hash when available.
|
|
100
|
-
- [ ] `dedup_plan_and_apply` — Implementation notes:
|
|
101
|
-
- Produce reversible quarantine plan; default dry-run; require explicit confirm to apply.
|
|
102
|
-
|
|
103
|
-
Optional in this phase:
|
|
104
|
-
|
|
105
|
-
- [ ] `drive_mode_profile_switch`
|
|
106
|
-
- [ ] `eject_and_poweroff_drive`
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
### Phase 3 — Graphics and screen pipelines
|
|
111
|
-
|
|
112
|
-
- [ ] `extract_sprites_from_ram` — Implementation notes:
|
|
113
|
-
- Search with stride for 63-byte sprite patterns; export `.spr`/hex/base64.
|
|
114
|
-
- [ ] `rip_charset_from_ram` — Implementation notes:
|
|
115
|
-
- Locate 2KB charsets by structure; export binary and a PNG preview.
|
|
116
|
-
- [ ] `screen_capture_timeline` — Implementation notes:
|
|
117
|
-
- Timed captures; export timestamped log and optional GIF.
|
|
118
|
-
- [ ] `sprite_preview_prg_batch` — Implementation notes:
|
|
119
|
-
- Generate+run preview PRGs for many sprite blobs; capture screens.
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
### Phase 4 — SID and audio
|
|
124
|
-
|
|
125
|
-
- [ ] `silence_and_verify` — Implementation notes:
|
|
126
|
-
- Write SID reset/silence; record short audio; verify threshold.
|
|
127
|
-
- [ ] `music_compile_play_analyze` — Implementation notes:
|
|
128
|
-
- Compile SIDWAVE→PRG or SID; play; record; analyze; save JSON.
|
|
129
|
-
- [ ] `sid_param_sweep` — Implementation notes:
|
|
130
|
-
- Sweep ADSR/waveform/pulse width; schedule notes; analyze; score.
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
### Phase 5 — Debug stream workflows (post‑GA)
|
|
135
|
-
|
|
136
|
-
- Prerequisites (must be satisfied before starting this phase):
|
|
137
|
-
- UDP ingest pipeline with backpressure and packet‑loss handling.
|
|
138
|
-
- Sampling/windowing design for high‑rate streams and summary accuracy.
|
|
139
|
-
- Performance targets validated via load and soak tests.
|
|
140
|
-
- Observability and failure‑mode coverage (metrics, logs, alerts, drop counters).
|
|
141
|
-
- [ ] `debug_stream_watch` — Implementation notes:
|
|
142
|
-
- Start/stop; summarize rates; enforce mutual exclusion with video stream.
|
|
143
|
-
- [ ] `debug_loop_run_and_capture` — Implementation notes:
|
|
144
|
-
- Pause → start debug → resume → action → pause → stop → summarize packets.
|
|
145
|
-
- Support modes: 6510, VIC, 1541; choose minimal scope by default.
|
|
146
|
-
- [ ] `verify_raster_irq_line` — Implementation notes:
|
|
147
|
-
- Correlate writes to $D012/$D011 and IRQ ack; configurable tolerance.
|
|
148
|
-
- [ ] `debug_trace_until_cpu_write` — Implementation notes:
|
|
149
|
-
- Watch address set; freeze a pre/post window when first write observed.
|
|
150
|
-
- [ ] `action_latency_measure` — Implementation notes:
|
|
151
|
-
- Timestamp action issue and first matching event; compute cycles/µs.
|
|
152
|
-
|
|
153
|
-
Optional follow-ons in this phase (implement after above pass):
|
|
154
|
-
|
|
155
|
-
- [ ] `verify_irq_jitter`
|
|
156
|
-
- [ ] `sid_register_write_profile`
|
|
157
|
-
- [ ] `iec_bus_handshake_probe`
|
|
158
|
-
- [ ] `time_bounded_trace_around_event`
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
### Phase 6 — Developer and recovery loops
|
|
163
|
-
|
|
164
|
-
- [ ] `red_green_refactor_loop` — Implementation notes:
|
|
165
|
-
- Iterate: run → capture screen → write_memory fixes → rerun until pass.
|
|
166
|
-
- [ ] `multi_range_guardrails` — Implementation notes:
|
|
167
|
-
- Continuously verify invariants; auto-restore from snapshot on violations.
|
|
168
|
-
- [ ] `safe_reset_sequence` — Implementation notes:
|
|
169
|
-
- Snapshot select ranges; reset; compare persistence; resume.
|
|
170
|
-
- [ ] `drive_recovery_sequence` — Implementation notes:
|
|
171
|
-
- Detect error; reset; power cycle; remount last image; verify.
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
### RAG-coupled meta tools
|
|
176
|
-
|
|
177
|
-
- [ ] `ask_and_apply_memory_fix` — Implementation notes:
|
|
178
|
-
- Retrieve assembly guidance (RAG), compute targeted writes, then `verify_and_write_memory`.
|
|
179
|
-
- [ ] `sprite_program_from_prompt` — Implementation notes:
|
|
180
|
-
- Generate PETSCII/sprite asset from text; preview PRG; capture screen.
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
### Optional follow-ups and experiments
|
|
185
|
-
|
|
186
|
-
- [ ] `memory_heatmap_over_time`
|
|
187
|
-
- [ ] `irq_latency_probe`
|
|
188
|
-
- [ ] `sid_voice_stuck_guard`
|
|
189
|
-
- [ ] `auto_benchmark_suite`
|
|
190
|
-
- [ ] `firmware_compat_matrix`
|
|
191
|
-
- [ ] `ultimate_config_migrate`
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
### Per-task Done Definition (LLM must verify before ticking)
|
|
196
|
-
|
|
197
|
-
- Implementation compiles with no TypeScript errors (where applicable).
|
|
198
|
-
- New/updated tests exist and are passing locally.
|
|
199
|
-
- `npm run check` passes end-to-end.
|
|
200
|
-
- Documentation updated if tool behavior changed.
|
|
201
|
-
|
|
202
|
-
### How to tick off checkboxes (LLM instructions)
|
|
203
|
-
|
|
204
|
-
- After completing a checklist item and confirming the Done Definition, update the corresponding `- [ ]` line to `- [x]` in `doc/meta-tools-rollout.md`.
|
|
205
|
-
- Only tick one line at a time, from top to bottom, respecting phases and dependencies.
|
|
206
|
-
- After ticking, immediately run `npm run check` again to ensure that the documentation update did not break formatting-sensitive scripts.
|
|
207
|
-
|
|
208
|
-
### Notes on sequencing
|
|
209
|
-
|
|
210
|
-
- Respect the dependency graph from `doc/meta-tools.md` (Effort/Benefit/Dependencies Matrix).
|
|
211
|
-
- Within a phase, process items in order. If an item depends on another not yet done, jump to the dependency’s phase, complete it, then resume.
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { register } from "node:module";
|
|
2
|
-
|
|
3
|
-
// Register ts-node ESM loader using the recommended `register` API to avoid
|
|
4
|
-
// the experimental loader warning shown during CI runs.
|
|
5
|
-
const projectRootUrl = new URL("../", import.meta.url);
|
|
6
|
-
register("ts-node/esm", projectRootUrl);
|
|
File without changes
|