knodin 0.7.4 → 0.7.6

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 (104) hide show
  1. package/README.md +4 -1
  2. package/dist/bin/cli.js +108 -1859
  3. package/dist/bin/launcher.js +3 -10
  4. package/dist/chunks/chunk-DMQAGX77.js +654 -0
  5. package/dist/chunks/chunk-F4Z3Z766.js +4 -0
  6. package/dist/chunks/chunk-SIJAQVSX.js +3 -0
  7. package/dist/chunks/chunk-X6M4HUUE.js +2 -0
  8. package/dist/chunks/chunk-YPRMY2LP.js +8 -0
  9. package/dist/chunks/pure-compression-cli-4TA2TQD5.js +5 -0
  10. package/dist/chunks/server-7EDF4CBY.js +14 -0
  11. package/dist/chunks/structural-fast-path-KD5KQSPX.js +4 -0
  12. package/dist/src/init-progress-worker.js +2 -106
  13. package/dist/src/repair-progress-worker.js +2 -120
  14. package/docs/INSTALLATION.md +17 -1
  15. package/docs/releases/0.7.6.md +25 -0
  16. package/package.json +1 -110
  17. package/dist/src/agent-integration.js +0 -238
  18. package/dist/src/artifact-refresh.js +0 -81
  19. package/dist/src/cli-args.js +0 -274
  20. package/dist/src/cli-model.js +0 -358
  21. package/dist/src/codeflow-replay.js +0 -80
  22. package/dist/src/compact-structural.js +0 -96
  23. package/dist/src/competitive-cold-mcp.js +0 -40
  24. package/dist/src/competitive-constraints.js +0 -20
  25. package/dist/src/competitive-manifest.js +0 -411
  26. package/dist/src/competitive-measurement.js +0 -183
  27. package/dist/src/competitive-runner.js +0 -487
  28. package/dist/src/competitive-sandbox.js +0 -108
  29. package/dist/src/context-export.js +0 -422
  30. package/dist/src/context.js +0 -102
  31. package/dist/src/diagnostics.js +0 -449
  32. package/dist/src/docs-sections.js +0 -141
  33. package/dist/src/doctor.js +0 -380
  34. package/dist/src/engine/ann-hnsw.js +0 -271
  35. package/dist/src/engine/embeddings.js +0 -193
  36. package/dist/src/engine/file-walker.js +0 -43
  37. package/dist/src/engine/git-history.js +0 -289
  38. package/dist/src/engine/index.js +0 -13377
  39. package/dist/src/engine/perf.js +0 -115
  40. package/dist/src/engine/prune.js +0 -112
  41. package/dist/src/engine/scip-import.js +0 -408
  42. package/dist/src/engine/source-policy.js +0 -69
  43. package/dist/src/engine/sqlite.js +0 -71
  44. package/dist/src/engine/symbol-delete.js +0 -58
  45. package/dist/src/execution-profile.js +0 -203
  46. package/dist/src/failure-diagnosis.js +0 -649
  47. package/dist/src/fleet.js +0 -7
  48. package/dist/src/git-executable.js +0 -31
  49. package/dist/src/graph-query-health.js +0 -115
  50. package/dist/src/hook-manager-integration.js +0 -156
  51. package/dist/src/index-activity.js +0 -125
  52. package/dist/src/init-progress.js +0 -155
  53. package/dist/src/init.js +0 -1254
  54. package/dist/src/lifecycle-health.js +0 -250
  55. package/dist/src/lsp-readonly.js +0 -217
  56. package/dist/src/output-compression.js +0 -629
  57. package/dist/src/output-telemetry.js +0 -363
  58. package/dist/src/pr-triage.js +0 -638
  59. package/dist/src/progressive-evidence.js +0 -473
  60. package/dist/src/pure-compression-cli.js +0 -101
  61. package/dist/src/relationship-adapters.js +0 -370
  62. package/dist/src/release-attestation.js +0 -533
  63. package/dist/src/release-preflight.js +0 -510
  64. package/dist/src/repair-progress.js +0 -262
  65. package/dist/src/repository-init-process.js +0 -173
  66. package/dist/src/repository-management.js +0 -1231
  67. package/dist/src/response-budget.js +0 -194
  68. package/dist/src/server.js +0 -73
  69. package/dist/src/structural-fast-path.js +0 -338
  70. package/dist/src/structural-snapshot.js +0 -33
  71. package/dist/src/system-config.js +0 -615
  72. package/dist/src/terminal-help.js +0 -83
  73. package/dist/src/tools/knodin-tools.js +0 -1529
  74. package/dist/src/update-ceremony.js +0 -158
  75. package/dist/src/update-policy.js +0 -944
  76. package/dist/src/update-trust.js +0 -503
  77. package/dist/src/version.js +0 -13
  78. package/dist/src/visualization.js +0 -162
  79. package/dist/src/wait-for-fresh.js +0 -98
  80. package/dist/src/worktree-lifecycle.js +0 -231
  81. package/docs/CONTAINED-EXECUTION.md +0 -77
  82. package/docs/DIAGNOSTICS.md +0 -45
  83. package/docs/GIT-HISTORY-REVIEW.md +0 -39
  84. package/docs/PROGRESSIVE-EVIDENCE.md +0 -37
  85. package/docs/PT-ACCESS-RECOMMENDATION.md +0 -91
  86. package/docs/RELEASE-0.3-EVIDENCE.md +0 -73
  87. package/docs/SCIP-IMPORT.md +0 -57
  88. package/docs/SIGNED-UPDATES.md +0 -151
  89. package/docs/TELEMETRY.md +0 -46
  90. package/docs/assets/knodin-favicon.svg +0 -4
  91. package/docs/releases/0.3.0.md +0 -46
  92. package/docs/releases/0.4.0.md +0 -68
  93. package/docs/releases/0.4.1.md +0 -28
  94. package/docs/releases/0.4.2.md +0 -27
  95. package/docs/releases/0.4.3.md +0 -23
  96. package/docs/releases/0.5.0.md +0 -29
  97. package/docs/releases/0.5.1.md +0 -17
  98. package/docs/releases/0.6.0.md +0 -18
  99. package/docs/releases/0.7.0.md +0 -24
  100. package/docs/releases/0.7.1.md +0 -21
  101. package/docs/releases/0.7.2.md +0 -21
  102. package/docs/releases/0.7.3.md +0 -23
  103. package/docs/releases/0.7.4.md +0 -17
  104. package/schemas/release-attestation-v1.schema.json +0 -210
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knodin",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "description": "knodin — source-evidenced local code intelligence with known bounds. Stable identity, fresh evidence, truthful budgets, and recoverable bounded views.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -11,105 +11,13 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/knodin/knodin/issues"
13
13
  },
14
- "publishConfig": {
15
- "access": "public",
16
- "registry": "https://registry.npmjs.org/"
17
- },
18
14
  "type": "module",
19
15
  "bin": {
20
16
  "knodin": "dist/bin/launcher.js"
21
17
  },
22
- "files": [
23
- "dist",
24
- "docs/DEAD-CODE-AND-IMPACT.md",
25
- "docs/DOCTOR-AND-UPDATES.md",
26
- "docs/DIAGNOSTICS.md",
27
- "docs/INDEXING-POLICY-AND-PROVENANCE.md",
28
- "docs/INSTALLATION.md",
29
- "docs/MCP.md",
30
- "docs/COMMAND-OUTPUT-COMPRESSION.md",
31
- "docs/CONTAINED-EXECUTION.md",
32
- "docs/PROGRESSIVE-EVIDENCE.md",
33
- "docs/GIT-HISTORY-REVIEW.md",
34
- "docs/SCIP-IMPORT.md",
35
- "docs/CLI.md",
36
- "docs/PT-ACCESS-RECOMMENDATION.md",
37
- "docs/REPOSITORIES-AND-WORKTREES.md",
38
- "docs/RELEASE-0.3-EVIDENCE.md",
39
- "docs/SIGNED-UPDATES.md",
40
- "docs/releases/0.3.0.md",
41
- "docs/releases/0.4.0.md",
42
- "docs/releases/0.4.1.md",
43
- "docs/releases/0.4.2.md",
44
- "docs/releases/0.4.3.md",
45
- "docs/releases/0.5.0.md",
46
- "docs/releases/0.5.1.md",
47
- "docs/releases/0.6.0.md",
48
- "docs/releases/0.7.0.md",
49
- "docs/releases/0.7.1.md",
50
- "docs/releases/0.7.2.md",
51
- "docs/releases/0.7.3.md",
52
- "docs/releases/0.7.4.md",
53
- "docs/assets/knodin-favicon.svg",
54
- "docs/SYSTEMS-AND-RELATIONSHIPS.md",
55
- "docs/TELEMETRY.md",
56
- "schemas/release-attestation-v1.schema.json",
57
- "*.wasm"
58
- ],
59
18
  "engines": {
60
19
  "node": ">=24.0.0"
61
20
  },
62
- "scripts": {
63
- "prepack": "npm run build",
64
- "hooks:install": "tsx scripts/prepare-hooks.ts",
65
- "dev": "tsx watch src/server.ts",
66
- "start": "tsx src/server.ts",
67
- "cli": "tsx bin/cli.ts",
68
- "clean": "node scripts/clean-dist.mjs",
69
- "build": "npm run clean && tsc -p tsconfig.build.json",
70
- "typecheck": "tsc --noEmit",
71
- "test": "tsx scripts/run-vitest-shards.ts",
72
- "test:handles": "vitest run --reporter=verbose --reporter=hanging-process",
73
- "test:watch": "vitest --watch",
74
- "test:coverage": "tsx scripts/run-vitest-shards.ts --coverage",
75
- "scan:sonar": "npm run test:coverage && sh scripts/run-sonar-scan.sh",
76
- "mirror:sync": "sh tools/hooks/sync-ghes-mirror.sh",
77
- "test:pack-install": "tsx scripts/pack-install-smoke.ts",
78
- "test:package-managers": "tsx scripts/package-manager-smoke.ts",
79
- "test:naming-screen": "node --test scripts/naming-screen.test.js",
80
- "measure:schema": "tsx scripts/measure-tool-schema.ts",
81
- "check:dependency-deprecations": "tsx scripts/check-dependency-deprecations.ts",
82
- "check:hygiene": "node scripts/check-repository-hygiene.mjs",
83
- "check:roadmap": "tsx scripts/check-roadmap.ts",
84
- "verify:c79": "tsx scripts/verify-c79.ts",
85
- "bench:c80": "tsx benchmarks/evaluations/c80-structural-routing/runner.ts",
86
- "verify:c80": "tsx scripts/verify-c80.ts",
87
- "bench:c81": "tsx benchmarks/evaluations/c81-powered-benchmark/runner.ts",
88
- "verify:c81": "tsx scripts/verify-c81.ts",
89
- "bench:c82": "tsx benchmarks/evaluations/c82-compress-diagnose/runner.ts",
90
- "verify:c82": "tsx scripts/verify-c82.ts",
91
- "bench:c83": "tsx benchmarks/evaluations/c83-progressive-evidence/runner.ts",
92
- "verify:c83": "tsx scripts/verify-c83.ts",
93
- "bench:c84": "tsx benchmarks/evaluations/c84-scip-import/runner.ts",
94
- "verify:c84": "tsx scripts/verify-c84.ts",
95
- "bench:c85": "tsx benchmarks/evaluations/c85-git-history/runner.ts",
96
- "verify:c85": "tsx scripts/verify-c85.ts",
97
- "bench:c86": "tsx benchmarks/evaluations/c86-static-embeddings/runner.ts",
98
- "verify:c86": "tsx scripts/verify-c86.ts",
99
- "bench:c87": "tsx benchmarks/evaluations/c87-structural-cold-start/runner.ts",
100
- "verify:c87": "tsx scripts/verify-c87.ts",
101
- "bench:c88": "tsx benchmarks/evaluations/c88-contained-execution/runner.ts",
102
- "verify:c88": "tsx scripts/verify-c88.ts",
103
- "bench:ann": "bun scripts/ann-bench.ts",
104
- "bench:perf": "tsx scripts/perf-bench.ts",
105
- "bench:competitive": "tsx scripts/competitive-bakeoff.ts",
106
- "bench:token-optimizer": "tsx benchmarks/evaluations/token-optimizer-20260730/runner.ts",
107
- "audit:token-optimizer": "tsx scripts/token-optimizer-source-audit.ts",
108
- "release:attestation": "tsx scripts/release-attestation.ts",
109
- "release:preflight": "tsx scripts/release-preflight.ts",
110
- "lint": "biome check .",
111
- "lint:fix": "biome check --write ."
112
- },
113
21
  "dependencies": {
114
22
  "@huggingface/transformers": "^4.2.0",
115
23
  "@modelcontextprotocol/sdk": "^1.30.0",
@@ -123,22 +31,5 @@
123
31
  "web-tree-sitter-sfapex": "2.4.1",
124
32
  "yaml": "2.9.0",
125
33
  "zod": "^4.3.6"
126
- },
127
- "overrides": {
128
- "@hono/node-server": "^2.0.5",
129
- "adm-zip": "^0.6.0",
130
- "fast-uri": "^4.1.1",
131
- "sharp": "^0.35.3"
132
- },
133
- "devDependencies": {
134
- "@biomejs/biome": "2.4.6",
135
- "@types/bun": "^1.3.14",
136
- "@types/node": "^24.10.0",
137
- "@vitest/coverage-v8": "^4.1.10",
138
- "ajv": "8.20.0",
139
- "lefthook": "^2.1.5",
140
- "tsx": "^4.21.0",
141
- "typescript": "^5.9.3",
142
- "vitest": "^4.1.3"
143
34
  }
144
35
  }
@@ -1,238 +0,0 @@
1
- import child_process from "node:child_process";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- export function parseInitScope(value) {
5
- if (value === "personal" || value === "team" || value === "cli-only")
6
- return value;
7
- throw new Error("knodin: --scope must be personal, team, or cli-only");
8
- }
9
- const MCP_NAME = "knodin";
10
- const TOML_START = "# knodin:start";
11
- const TOML_END = "# knodin:end";
12
- const TOML_BLOCK = `${TOML_START}
13
- [mcp_servers."${MCP_NAME}"]
14
- command = "knodin"
15
- args = ["serve"]
16
- ${TOML_END}`;
17
- function errorMessage(error) {
18
- return error instanceof Error ? error.message : String(error);
19
- }
20
- function commandExists(command) {
21
- try {
22
- child_process.execFileSync("which", [command], { stdio: "ignore" });
23
- return true;
24
- }
25
- catch {
26
- return false;
27
- }
28
- }
29
- /** Detect primary MCP-capable coding agents without launching them. */
30
- export function detectSupportedAgents() {
31
- const detected = [];
32
- if (commandExists("claude"))
33
- detected.push("claude");
34
- if (commandExists("codex"))
35
- detected.push("codex");
36
- if (commandExists("gemini"))
37
- detected.push("gemini");
38
- if (commandExists("antigravity") || commandExists("agy"))
39
- detected.push("antigravity");
40
- return detected;
41
- }
42
- function defaultRunner(command) {
43
- try {
44
- child_process.execFileSync(command.executable, command.args, {
45
- cwd: command.cwd,
46
- stdio: ["ignore", "ignore", "pipe"],
47
- });
48
- }
49
- catch (error) {
50
- if (command.executable !== "claude" || !command.args.includes("add"))
51
- throw error;
52
- child_process.execFileSync("claude", ["mcp", "get", MCP_NAME], {
53
- cwd: command.cwd,
54
- stdio: ["ignore", "ignore", "pipe"],
55
- });
56
- }
57
- }
58
- /**
59
- * Configure command-backed personal integrations. Claude stores local scope
60
- * outside the checkout; Codex/Gemini/Antigravity use excluded project files.
61
- */
62
- export function configurePersonalAgents(options) {
63
- const run = options.run ?? defaultRunner;
64
- const configured = [];
65
- const removed = [];
66
- const failed = [];
67
- for (const agent of options.detected) {
68
- if (agent !== "claude")
69
- continue;
70
- const command = options.remove
71
- ? {
72
- executable: "claude",
73
- args: ["mcp", "remove", MCP_NAME, "--scope", "local"],
74
- }
75
- : {
76
- executable: "claude",
77
- args: ["mcp", "add", MCP_NAME, "--scope", "local", "--", "knodin", "serve"],
78
- };
79
- try {
80
- run(command);
81
- (options.remove ? removed : configured).push(agent);
82
- }
83
- catch (error) {
84
- // Claude reports a missing local registration as a remove failure. Keep
85
- // this backward-compatible result conservative at the presentation layer:
86
- // it means removal was attempted, not that absence was verified.
87
- if (options.remove)
88
- removed.push(agent);
89
- else
90
- failed.push({ agent, message: errorMessage(error) });
91
- }
92
- }
93
- return { configured, removed, failed, projectFiles: [] };
94
- }
95
- function readJsonObject(filePath) {
96
- try {
97
- const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
98
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
99
- throw new Error("expected a JSON object");
100
- return parsed;
101
- }
102
- catch (error) {
103
- if (error.code === "ENOENT")
104
- return {};
105
- throw new Error(`${filePath}: ${errorMessage(error)}`);
106
- }
107
- }
108
- function assertSafeProjectPath(repo, filePath) {
109
- const relative = path.relative(repo, filePath);
110
- if (relative.startsWith("..") || path.isAbsolute(relative))
111
- throw new Error(`${filePath}: agent configuration must stay inside the repository`);
112
- let current = repo;
113
- for (const part of relative.split(path.sep)) {
114
- current = path.join(current, part);
115
- try {
116
- if (fs.lstatSync(current).isSymbolicLink())
117
- throw new Error(`${filePath}: refusing to follow a symbolic link`);
118
- }
119
- catch (error) {
120
- if (error.code === "ENOENT")
121
- break;
122
- throw error;
123
- }
124
- }
125
- }
126
- function formatProjectJson(document) {
127
- return `${JSON.stringify(document, null, "\t").replace(/"args": \[\n(\t+)"serve"\n\t+\]/g, '"args": ["serve"]')}\n`;
128
- }
129
- function updateJsonMcp(filePath, remove) {
130
- const document = readJsonObject(filePath);
131
- const existing = document.mcpServers;
132
- const servers = typeof existing === "object" && existing !== null && !Array.isArray(existing)
133
- ? { ...existing }
134
- : {};
135
- if (remove)
136
- delete servers[MCP_NAME];
137
- else
138
- servers[MCP_NAME] = { command: "knodin", args: ["serve"] };
139
- if (Object.keys(servers).length > 0)
140
- document.mcpServers = servers;
141
- else
142
- delete document.mcpServers;
143
- if (remove && Object.keys(document).length === 0) {
144
- fs.rmSync(filePath, { force: true });
145
- return false;
146
- }
147
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
148
- fs.writeFileSync(filePath, formatProjectJson(document), "utf-8");
149
- return true;
150
- }
151
- function updateTomlMcp(filePath, remove) {
152
- let existing = "";
153
- try {
154
- existing = fs.readFileSync(filePath, "utf-8");
155
- }
156
- catch (error) {
157
- if (error.code !== "ENOENT")
158
- throw error;
159
- }
160
- function removeManagedBlock(content, startMarker, endMarker) {
161
- const start = content.indexOf(startMarker);
162
- const end = content.indexOf(endMarker);
163
- return start >= 0 && end >= start
164
- ? `${content.slice(0, start)}${content.slice(end + endMarker.length)}`.trim()
165
- : content.trim();
166
- }
167
- const without = removeManagedBlock(existing, TOML_START, TOML_END);
168
- const next = remove ? without : [without, TOML_BLOCK].filter(Boolean).join("\n\n");
169
- if (!next) {
170
- fs.rmSync(filePath, { force: true });
171
- return false;
172
- }
173
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
174
- fs.writeFileSync(filePath, `${next}\n`, "utf-8");
175
- return true;
176
- }
177
- /** Merge or remove knodin's project-local agent adapters without replacing user configuration. */
178
- export function configureProjectAgents(options) {
179
- const repo = path.resolve(options.repo);
180
- const remove = options.scope === "cli-only";
181
- const agents = options.scope === "team"
182
- ? ["claude", "codex", "gemini", "antigravity"]
183
- : options.agents;
184
- const commandResult = options.scope === "team"
185
- ? { configured: [], removed: [], failed: [], projectFiles: [] }
186
- : configurePersonalAgents({
187
- detected: agents,
188
- remove,
189
- run: options.run ?? ((command) => defaultRunner({ ...command, cwd: repo })),
190
- });
191
- const configured = [...commandResult.configured];
192
- const removed = [...commandResult.removed];
193
- const failed = [...commandResult.failed];
194
- const projectFiles = [];
195
- for (const agent of agents) {
196
- try {
197
- let relative = null;
198
- let remains = false;
199
- if (agent === "claude" && options.scope === "team") {
200
- relative = ".mcp.json";
201
- assertSafeProjectPath(repo, path.join(repo, relative));
202
- remains = updateJsonMcp(path.join(repo, relative), false);
203
- }
204
- else if (agent === "claude") {
205
- relative = ".mcp.json";
206
- assertSafeProjectPath(repo, path.join(repo, relative));
207
- remains = updateJsonMcp(path.join(repo, relative), true);
208
- }
209
- else if (agent === "codex") {
210
- relative = ".codex/config.toml";
211
- assertSafeProjectPath(repo, path.join(repo, relative));
212
- remains = updateTomlMcp(path.join(repo, relative), remove);
213
- }
214
- else if (agent === "gemini") {
215
- relative = ".gemini/settings.json";
216
- assertSafeProjectPath(repo, path.join(repo, relative));
217
- remains = updateJsonMcp(path.join(repo, relative), remove);
218
- }
219
- else if (agent === "antigravity") {
220
- relative = ".agents/mcp_config.json";
221
- assertSafeProjectPath(repo, path.join(repo, relative));
222
- remains = updateJsonMcp(path.join(repo, relative), remove);
223
- }
224
- if (relative && remains)
225
- projectFiles.push(relative);
226
- if (agent === "claude" && options.scope === "team" && !configured.includes(agent))
227
- configured.push(agent);
228
- if (agent !== "claude" && !remove && !configured.includes(agent))
229
- configured.push(agent);
230
- if (remove && !removed.includes(agent))
231
- removed.push(agent);
232
- }
233
- catch (error) {
234
- failed.push({ agent, message: errorMessage(error) });
235
- }
236
- }
237
- return { configured, removed, failed, projectFiles };
238
- }
@@ -1,81 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- const REFRESH_TIMEOUT_MS = 30_000;
5
- function defaultRuntime() {
6
- return {
7
- commandExists: (command) => {
8
- const probe = spawnSync(command, ["--help"], { stdio: "ignore", timeout: 5_000 });
9
- return (probe.status !== null ||
10
- probe.error?.code !== "ENOENT");
11
- },
12
- run: (command, cwd) => {
13
- const startedAt = performance.now();
14
- const result = spawnSync(command[0], command.slice(1), {
15
- stdio: "ignore",
16
- timeout: REFRESH_TIMEOUT_MS,
17
- cwd,
18
- });
19
- return {
20
- exitCode: result.status,
21
- elapsedMs: Math.round(performance.now() - startedAt),
22
- timedOut: result.signal === "SIGTERM" || result.signal === "SIGKILL",
23
- };
24
- },
25
- };
26
- }
27
- /**
28
- * Explicitly refresh supported local, external graph artifacts. This is never a
29
- * commit hook: callers opt in, each rebuild is time-bounded, and unavailable
30
- * tools are recorded as skipped rather than being represented as fresh.
31
- */
32
- export function refreshExternalGraphArtifacts(repoPath, event, runtime = defaultRuntime()) {
33
- const repo = path.resolve(repoPath);
34
- const gitNexusRunner = path.join(repo, ".gitnexus", "run.cjs");
35
- const commands = [
36
- fs.existsSync(gitNexusRunner)
37
- ? {
38
- artifact: "gitnexus",
39
- command: ["node", gitNexusRunner, "analyze"],
40
- requiredCommand: "node",
41
- }
42
- : {
43
- artifact: "gitnexus",
44
- command: ["gitnexus", "analyze", repo],
45
- requiredCommand: "gitnexus",
46
- },
47
- { artifact: "graphify", command: ["graphify", "update", repo], requiredCommand: "graphify" },
48
- ];
49
- const artifacts = commands.map(({ artifact, command, requiredCommand }) => {
50
- if (!runtime.commandExists(requiredCommand)) {
51
- return { artifact, state: "skipped", reason: `${artifact} is not installed` };
52
- }
53
- const result = runtime.run(command, repo);
54
- if (result.exitCode === 0) {
55
- return { artifact, state: "success", command, elapsedMs: result.elapsedMs };
56
- }
57
- return {
58
- artifact,
59
- state: "failed",
60
- command,
61
- elapsedMs: result.elapsedMs,
62
- reason: result.timedOut
63
- ? `timed out after ${REFRESH_TIMEOUT_MS}ms`
64
- : `exited ${result.exitCode ?? "unknown"}`,
65
- };
66
- });
67
- const successes = artifacts.filter((artifact) => artifact.state === "success").length;
68
- const failures = artifacts.filter((artifact) => artifact.state === "failed").length;
69
- const status = failures > 0 && successes === 0
70
- ? "failed"
71
- : failures > 0 || successes > 0
72
- ? "partial"
73
- : "skipped";
74
- return { event, status, artifacts };
75
- }
76
- /** Persist an audit record locally; no source content or tool output is retained. */
77
- export function writeArtifactRefreshRecord(repoPath, result) {
78
- const knodinDir = path.join(path.resolve(repoPath), ".knodin");
79
- fs.mkdirSync(knodinDir, { recursive: true });
80
- fs.appendFileSync(path.join(knodinDir, "artifact-refresh.jsonl"), `${JSON.stringify({ at: new Date().toISOString(), ...result })}\n`, "utf8");
81
- }