@laitszkin/apollo-toolkit 4.0.11 → 4.1.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.
Files changed (134) hide show
  1. package/AGENTS.md +37 -27
  2. package/CHANGELOG.md +31 -0
  3. package/CLAUDE.md +37 -27
  4. package/README.md +15 -2
  5. package/assets/spec/rg13-1780435029246/test-questions.json +1 -0
  6. package/assets/spec/rg13-1780468345132/test-questions.json +1 -0
  7. package/package.json +3 -3
  8. package/packages/cli/dist/tool-registration.js +1 -0
  9. package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
  10. package/packages/cli/tool-registration.ts +1 -0
  11. package/packages/tools/architecture/dist/index.js +539 -2
  12. package/packages/tools/architecture/dist/index.test.d.ts +1 -0
  13. package/packages/tools/architecture/dist/index.test.js +229 -0
  14. package/packages/tools/architecture/dist/tsconfig.tsbuildinfo +1 -1
  15. package/packages/tools/architecture/index.test.ts +329 -0
  16. package/packages/tools/architecture/index.ts +607 -5
  17. package/packages/tools/codegraph/dist/index.d.ts +3 -0
  18. package/packages/tools/codegraph/dist/index.js +157 -0
  19. package/packages/tools/codegraph/dist/lib/cg-instance.d.ts +29 -0
  20. package/packages/tools/codegraph/dist/lib/cg-instance.js +59 -0
  21. package/packages/tools/codegraph/dist/lib/cg-instance.test.d.ts +1 -0
  22. package/packages/tools/codegraph/dist/lib/cg-instance.test.js +27 -0
  23. package/packages/tools/codegraph/dist/lib/cmd-explore.d.ts +5 -0
  24. package/packages/tools/codegraph/dist/lib/cmd-explore.js +95 -0
  25. package/packages/tools/codegraph/dist/lib/cmd-explore.test.d.ts +1 -0
  26. package/packages/tools/codegraph/dist/lib/cmd-explore.test.js +133 -0
  27. package/packages/tools/codegraph/dist/lib/cmd-flag-splice.test.d.ts +1 -0
  28. package/packages/tools/codegraph/dist/lib/cmd-flag-splice.test.js +83 -0
  29. package/packages/tools/codegraph/dist/lib/cmd-init.d.ts +5 -0
  30. package/packages/tools/codegraph/dist/lib/cmd-init.js +50 -0
  31. package/packages/tools/codegraph/dist/lib/cmd-init.test.d.ts +1 -0
  32. package/packages/tools/codegraph/dist/lib/cmd-init.test.js +51 -0
  33. package/packages/tools/codegraph/dist/lib/cmd-list-apis.d.ts +5 -0
  34. package/packages/tools/codegraph/dist/lib/cmd-list-apis.js +64 -0
  35. package/packages/tools/codegraph/dist/lib/cmd-list-apis.test.d.ts +1 -0
  36. package/packages/tools/codegraph/dist/lib/cmd-list-apis.test.js +69 -0
  37. package/packages/tools/codegraph/dist/lib/cmd-search.d.ts +5 -0
  38. package/packages/tools/codegraph/dist/lib/cmd-search.js +21 -0
  39. package/packages/tools/codegraph/dist/lib/cmd-search.test.d.ts +1 -0
  40. package/packages/tools/codegraph/dist/lib/cmd-search.test.js +30 -0
  41. package/packages/tools/codegraph/dist/lib/cmd-status.d.ts +4 -0
  42. package/packages/tools/codegraph/dist/lib/cmd-status.js +44 -0
  43. package/packages/tools/codegraph/dist/lib/cmd-status.test.d.ts +1 -0
  44. package/packages/tools/codegraph/dist/lib/cmd-status.test.js +72 -0
  45. package/packages/tools/codegraph/dist/lib/cmd-survey.d.ts +36 -0
  46. package/packages/tools/codegraph/dist/lib/cmd-survey.js +142 -0
  47. package/packages/tools/codegraph/dist/lib/cmd-survey.test.d.ts +1 -0
  48. package/packages/tools/codegraph/dist/lib/cmd-survey.test.js +136 -0
  49. package/packages/tools/codegraph/dist/lib/cmd-sync.d.ts +4 -0
  50. package/packages/tools/codegraph/dist/lib/cmd-sync.js +51 -0
  51. package/packages/tools/codegraph/dist/lib/cmd-sync.test.d.ts +1 -0
  52. package/packages/tools/codegraph/dist/lib/cmd-sync.test.js +30 -0
  53. package/packages/tools/codegraph/dist/lib/cmd-verify.d.ts +4 -0
  54. package/packages/tools/codegraph/dist/lib/cmd-verify.js +134 -0
  55. package/packages/tools/codegraph/dist/lib/cmd-verify.test.d.ts +1 -0
  56. package/packages/tools/codegraph/dist/lib/cmd-verify.test.js +139 -0
  57. package/packages/tools/codegraph/dist/lib/formatter.d.ts +67 -0
  58. package/packages/tools/codegraph/dist/lib/formatter.js +107 -0
  59. package/packages/tools/codegraph/dist/lib/formatter.test.d.ts +1 -0
  60. package/packages/tools/codegraph/dist/lib/formatter.test.js +41 -0
  61. package/packages/tools/codegraph/dist/lib/survey/grouper.d.ts +19 -0
  62. package/packages/tools/codegraph/dist/lib/survey/grouper.js +194 -0
  63. package/packages/tools/codegraph/dist/lib/survey/grouper.test.d.ts +1 -0
  64. package/packages/tools/codegraph/dist/lib/survey/grouper.test.js +62 -0
  65. package/packages/tools/codegraph/dist/lib/survey/scanner.d.ts +31 -0
  66. package/packages/tools/codegraph/dist/lib/survey/scanner.js +50 -0
  67. package/packages/tools/codegraph/dist/lib/verify/checker.d.ts +32 -0
  68. package/packages/tools/codegraph/dist/lib/verify/checker.js +146 -0
  69. package/packages/tools/codegraph/dist/lib/verify/checker.test.d.ts +1 -0
  70. package/packages/tools/codegraph/dist/lib/verify/checker.test.js +128 -0
  71. package/packages/tools/codegraph/dist/tsconfig.tsbuildinfo +1 -0
  72. package/packages/tools/codegraph/env.d.ts +56 -0
  73. package/packages/tools/codegraph/index.ts +173 -0
  74. package/packages/tools/codegraph/lib/cg-instance.test.ts +36 -0
  75. package/packages/tools/codegraph/lib/cg-instance.ts +66 -0
  76. package/packages/tools/codegraph/lib/cmd-explore.test.ts +195 -0
  77. package/packages/tools/codegraph/lib/cmd-explore.ts +129 -0
  78. package/packages/tools/codegraph/lib/cmd-flag-splice.test.ts +94 -0
  79. package/packages/tools/codegraph/lib/cmd-init.test.ts +68 -0
  80. package/packages/tools/codegraph/lib/cmd-init.ts +60 -0
  81. package/packages/tools/codegraph/lib/cmd-list-apis.test.ts +80 -0
  82. package/packages/tools/codegraph/lib/cmd-list-apis.ts +90 -0
  83. package/packages/tools/codegraph/lib/cmd-search.test.ts +37 -0
  84. package/packages/tools/codegraph/lib/cmd-search.ts +32 -0
  85. package/packages/tools/codegraph/lib/cmd-status.test.ts +86 -0
  86. package/packages/tools/codegraph/lib/cmd-status.ts +53 -0
  87. package/packages/tools/codegraph/lib/cmd-survey.test.ts +161 -0
  88. package/packages/tools/codegraph/lib/cmd-survey.ts +199 -0
  89. package/packages/tools/codegraph/lib/cmd-sync.test.ts +41 -0
  90. package/packages/tools/codegraph/lib/cmd-sync.ts +62 -0
  91. package/packages/tools/codegraph/lib/cmd-verify.test.ts +162 -0
  92. package/packages/tools/codegraph/lib/cmd-verify.ts +145 -0
  93. package/packages/tools/codegraph/lib/formatter.test.ts +47 -0
  94. package/packages/tools/codegraph/lib/formatter.ts +130 -0
  95. package/packages/tools/codegraph/lib/survey/grouper.test.ts +72 -0
  96. package/packages/tools/codegraph/lib/survey/grouper.ts +226 -0
  97. package/packages/tools/codegraph/lib/survey/scanner.ts +89 -0
  98. package/packages/tools/codegraph/lib/verify/checker.test.ts +140 -0
  99. package/packages/tools/codegraph/lib/verify/checker.ts +172 -0
  100. package/packages/tools/codegraph/package.json +23 -0
  101. package/packages/tools/codegraph/tsconfig.json +22 -0
  102. package/resources/project-architecture/atlas/atlas.history.log +32 -0
  103. package/resources/project-architecture/atlas/atlas.history.undo.json +356 -28
  104. package/resources/project-architecture/atlas/atlas.history.undo.stack.json +14350 -0
  105. package/resources/project-architecture/atlas/atlas.index.yaml +76 -12
  106. package/resources/project-architecture/atlas/features/codegraph.yaml +95 -0
  107. package/resources/project-architecture/atlas/features/eval-ci-gate.yaml +6 -1
  108. package/resources/project-architecture/atlas/features/eval-cli.yaml +16 -1
  109. package/resources/project-architecture/atlas/features/eval-executor.yaml +12 -2
  110. package/resources/project-architecture/atlas/features/eval-isolation.yaml +6 -1
  111. package/resources/project-architecture/atlas/features/eval-optimizer.yaml +17 -2
  112. package/resources/project-architecture/atlas/features/eval-question.yaml +12 -2
  113. package/resources/project-architecture/atlas/features/eval-reporter.yaml +6 -1
  114. package/resources/project-architecture/atlas/features/eval-scorer.yaml +12 -2
  115. package/resources/project-architecture/features/codegraph/cg-discovery.html +47 -0
  116. package/resources/project-architecture/features/codegraph/cg-lifecycle.html +48 -0
  117. package/resources/project-architecture/features/codegraph/cg-validation.html +47 -0
  118. package/resources/project-architecture/features/codegraph/index.html +58 -0
  119. package/resources/project-architecture/features/eval-ci-gate/workflow-trigger.html +6 -1
  120. package/resources/project-architecture/features/eval-cli/cli-handler.html +8 -1
  121. package/resources/project-architecture/features/eval-executor/exec-api-client.html +6 -1
  122. package/resources/project-architecture/features/eval-executor/trace-recorder.html +6 -1
  123. package/resources/project-architecture/features/eval-isolation/tool-dispatcher.html +6 -1
  124. package/resources/project-architecture/features/eval-optimizer/dedup-engine.html +6 -1
  125. package/resources/project-architecture/features/eval-optimizer/issue-extractor.html +7 -1
  126. package/resources/project-architecture/features/eval-question/question-loader.html +6 -1
  127. package/resources/project-architecture/features/eval-question/variant-generator.html +6 -1
  128. package/resources/project-architecture/features/eval-reporter/report-composer.html +6 -1
  129. package/resources/project-architecture/features/eval-scorer/judge-api-client.html +6 -1
  130. package/resources/project-architecture/features/eval-scorer/judge-prompt-builder.html +6 -1
  131. package/resources/project-architecture/index.html +200 -94
  132. package/skills/design/SKILL.md +33 -0
  133. package/skills/init-project-html/SKILL.md +12 -11
  134. package/tsconfig.json +1 -0
@@ -0,0 +1,329 @@
1
+ import { describe, it, before, after, mock } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ import type { ToolContext } from '@laitszkin/tool-registry';
7
+ import { tool } from './index.js';
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Helpers
11
+ // ---------------------------------------------------------------------------
12
+
13
+ interface Io {
14
+ stdout: { write: (s: string) => void };
15
+ stderr: { write: (s: string) => void };
16
+ stdoutText: string;
17
+ stderrText: string;
18
+ }
19
+
20
+ function makeIo(): Io {
21
+ let stdoutBuf = '';
22
+ let stderrBuf = '';
23
+ return {
24
+ stdout: { write: (s: string) => { stdoutBuf += s; } },
25
+ stderr: { write: (s: string) => { stderrBuf += s; } },
26
+ get stdoutText() { return stdoutBuf; },
27
+ get stderrText() { return stderrBuf; },
28
+ };
29
+ }
30
+
31
+ function makeContext(io: Io, extra?: Partial<ToolContext>): ToolContext {
32
+ return {
33
+ stdout: io.stdout as any,
34
+ stderr: io.stderr as any,
35
+ ...extra,
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Write mock atlas CLI + state modules under `sourceRoot/skills/init-project-html/lib/atlas/`.
41
+ * Also writes a package.json with `"type": "module"` so Node treats .js files as ESM.
42
+ *
43
+ * The state module's `load()` returns `stateReturn`. If `onSave` is provided it is
44
+ * called back whenever the real code calls `stateLib.save(dir, state)`.
45
+ */
46
+ function writeMockAtlasModules(
47
+ tmpDir: string,
48
+ stateReturn: Record<string, any>,
49
+ onSave?: (dir: string, state: Record<string, any>) => void,
50
+ ): void {
51
+ const atlasDir = path.join(tmpDir, 'skills', 'init-project-html', 'lib', 'atlas');
52
+ fs.mkdirSync(atlasDir, { recursive: true });
53
+
54
+ fs.mkdirSync(path.join(tmpDir, 'resources', 'project-architecture'), {
55
+ recursive: true,
56
+ });
57
+
58
+ // cli.js
59
+ const cliProjectRoot = tmpDir;
60
+ const cliAtlasDir = path.join(tmpDir, 'resources', 'project-architecture');
61
+ fs.writeFileSync(
62
+ path.join(atlasDir, 'cli.js'),
63
+ [
64
+ `const projectRoot = ${JSON.stringify(cliProjectRoot)};`,
65
+ `const atlasDir = ${JSON.stringify(cliAtlasDir)};`,
66
+ 'export default {',
67
+ ' resolveProjectRoot: () => projectRoot,',
68
+ ' baseAtlasDir: () => atlasDir,',
69
+ ' runRender: async () => {},',
70
+ '};',
71
+ '',
72
+ ].join('\n'),
73
+ 'utf-8',
74
+ );
75
+
76
+ // state.js — delegates to (globalThis as any).__rg_onSave if set
77
+ const json = JSON.stringify(stateReturn);
78
+ fs.writeFileSync(
79
+ path.join(atlasDir, 'state.js'),
80
+ [
81
+ `const initialState = ${json};`,
82
+ 'const g = /** @type {any} */ (globalThis);',
83
+ 'const onSave = typeof g.__rg_onSave === "function"',
84
+ ' ? g.__rg_onSave',
85
+ ' : () => {};',
86
+ 'export default {',
87
+ ' load: () => JSON.parse(JSON.stringify(initialState)),',
88
+ ' loadOverlay: () => ({ features: [], edges: [] }),',
89
+ ' mergeOverlay: (base, overlay) => ({',
90
+ ' features: [...base.features, ...overlay.features],',
91
+ ' edges: [...base.edges, ...overlay.edges],',
92
+ ' }),',
93
+ ' save: (dir, state) => { onSave(dir, state); },',
94
+ ' saveOverlay: () => {},',
95
+ ' writeUndoSnapshot: () => {},',
96
+ ' appendHistory: () => {},',
97
+ ' deriveOverlay: (base, merged) => merged,',
98
+ '};',
99
+ '',
100
+ ].join('\n'),
101
+ 'utf-8',
102
+ );
103
+
104
+ // package.json to force ESM interpretation of .js
105
+ fs.writeFileSync(
106
+ path.join(tmpDir, 'package.json'),
107
+ JSON.stringify({ type: 'module' }),
108
+ 'utf-8',
109
+ );
110
+ }
111
+
112
+ // =========================================================================
113
+ // REGTEST-15: Wrong spec path error (Unit test)
114
+ // =========================================================================
115
+ describe('REGTEST-15: Wrong spec path error', () => {
116
+ it('should exit code 1 with diagnostic when SPEC.md not found', async () => {
117
+ mock.method(fs, 'existsSync', () => false);
118
+ try {
119
+ const io = makeIo();
120
+ const handler = tool.handler;
121
+ if (!handler) throw new Error('tool.handler is undefined');
122
+ const exitCode = await handler(
123
+ ['template', '--spec', '/nonexistent/spec-dir', '--output', '/tmp/rg15-out'],
124
+ makeContext(io),
125
+ );
126
+ assert.equal(exitCode, 1, 'Expected exit code 1 for missing spec path');
127
+ assert.ok(
128
+ io.stderrText.includes('not found'),
129
+ `stderr should contain "not found": got ${JSON.stringify(io.stderrText)}`,
130
+ );
131
+ } finally {
132
+ mock.restoreAll();
133
+ }
134
+ });
135
+ });
136
+
137
+ // =========================================================================
138
+ // REGTEST-16: Submodule remove cascade (Integration test)
139
+ // =========================================================================
140
+ describe('REGTEST-16: Submodule remove cascade', () => {
141
+ let tmpDir: string;
142
+ let yamlPath: string;
143
+ let savedStates: Record<string, any>[];
144
+ const io = makeIo();
145
+
146
+ before(() => {
147
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rg16-'));
148
+
149
+ savedStates = [];
150
+ (globalThis as any).__rg_onSave = (_dir: string, state: Record<string, any>) => {
151
+ savedStates.push(state);
152
+ };
153
+
154
+ writeMockAtlasModules(
155
+ tmpDir,
156
+ {
157
+ features: [
158
+ {
159
+ slug: 'feature-a',
160
+ title: 'Feature A',
161
+ submodules: [
162
+ { slug: 'sub-a1', kind: 'service' },
163
+ { slug: 'sub-a2', kind: 'service' },
164
+ ],
165
+ edges: [],
166
+ },
167
+ {
168
+ slug: 'feature-b',
169
+ title: 'Feature B',
170
+ submodules: [{ slug: 'sub-b1', kind: 'service' }],
171
+ edges: [],
172
+ },
173
+ ],
174
+ edges: [
175
+ {
176
+ id: 'e1',
177
+ from: { feature: 'feature-a', submodule: 'sub-a1' },
178
+ to: { feature: 'feature-b', submodule: 'sub-b1' },
179
+ kind: 'call',
180
+ },
181
+ {
182
+ id: 'e2',
183
+ from: { feature: 'feature-a', submodule: 'sub-a2' },
184
+ to: { feature: 'feature-b', submodule: 'sub-b1' },
185
+ kind: 'call',
186
+ },
187
+ ],
188
+ },
189
+ );
190
+
191
+ // Write YAML batch: remove sub-a1
192
+ yamlPath = path.join(tmpDir, 'batch.yaml');
193
+ fs.writeFileSync(
194
+ yamlPath,
195
+ [
196
+ 'features:',
197
+ ' - slug: feature-a',
198
+ ' action: modify',
199
+ ' submodules:',
200
+ ' - slug: sub-a1',
201
+ ' action: remove',
202
+ 'edges: []',
203
+ '',
204
+ ].join('\n'),
205
+ 'utf-8',
206
+ );
207
+ });
208
+
209
+ after(() => {
210
+ delete (globalThis as any).__rg_onSave;
211
+ if (tmpDir) fs.rmSync(tmpDir, { recursive: true, force: true });
212
+ });
213
+
214
+ it('should not have edges referencing the removed submodule after apply', async () => {
215
+ const handler = tool.handler;
216
+ if (!handler) throw new Error('tool.handler is undefined');
217
+ const exitCode = await handler(
218
+ ['apply', yamlPath, '--no-render'],
219
+ makeContext(io, { sourceRoot: tmpDir }),
220
+ );
221
+
222
+ assert.equal(
223
+ exitCode,
224
+ 0,
225
+ `Expected exit code 0, got ${exitCode}. stderr: ${JSON.stringify(io.stderrText)}`,
226
+ );
227
+
228
+ assert.equal(
229
+ savedStates.length,
230
+ 1,
231
+ 'stateLib.save should have been called exactly once',
232
+ );
233
+
234
+ const saved = savedStates[0];
235
+
236
+ // — Check that no edge in merged.edges references the removed sub-a1 —
237
+ for (const edge of saved.edges) {
238
+ const fromSub =
239
+ typeof edge.from === 'object' && edge.from ? edge.from.submodule : null;
240
+ const toSub =
241
+ typeof edge.to === 'object' && edge.to ? edge.to.submodule : null;
242
+ assert.notEqual(
243
+ fromSub,
244
+ 'sub-a1',
245
+ `Edge ${edge.id} from-submodule should not be "sub-a1"`,
246
+ );
247
+ assert.notEqual(
248
+ toSub,
249
+ 'sub-a1',
250
+ `Edge ${edge.id} to-submodule should not be "sub-a1"`,
251
+ );
252
+ }
253
+
254
+ // — Verify e2 (sub-a2 → sub-b1) survives —
255
+ const e2 = saved.edges.find((e: any) => e.id === 'e2');
256
+ assert.ok(e2, 'Edge e2 (feature-a/sub-a2 → feature-b/sub-b1) should remain');
257
+
258
+ // — Verify e1 (sub-a1 → sub-b1) is gone —
259
+ const e1 = saved.edges.find((e: any) => e.id === 'e1');
260
+ assert.ok(
261
+ !e1,
262
+ 'Edge e1 (feature-a/sub-a1 → feature-b/sub-b1) should be removed',
263
+ );
264
+ });
265
+ });
266
+
267
+ // =========================================================================
268
+ // REGTEST-17: Edge referential integrity (Unit test)
269
+ // =========================================================================
270
+ describe('REGTEST-17: Edge referential integrity', () => {
271
+ let tmpDir: string;
272
+ let yamlPath: string;
273
+ const io = makeIo();
274
+
275
+ before(() => {
276
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rg17-'));
277
+
278
+ // state has feature-a/sub-a1 but NOT non-existent-feature
279
+ writeMockAtlasModules(tmpDir, {
280
+ features: [
281
+ {
282
+ slug: 'feature-a',
283
+ title: 'Feature A',
284
+ submodules: [{ slug: 'sub-a1', kind: 'service' }],
285
+ edges: [],
286
+ },
287
+ ],
288
+ edges: [],
289
+ });
290
+
291
+ // Write YAML batch: add edge from non-existent-feature/sub → feature-a/sub-a1
292
+ yamlPath = path.join(tmpDir, 'batch.yaml');
293
+ fs.writeFileSync(
294
+ yamlPath,
295
+ [
296
+ 'edges:',
297
+ ' - from: non-existent-feature/sub',
298
+ ' to: feature-a/sub-a1',
299
+ ' action: add',
300
+ ' kind: call',
301
+ '',
302
+ ].join('\n'),
303
+ 'utf-8',
304
+ );
305
+ });
306
+
307
+ after(() => {
308
+ if (tmpDir) fs.rmSync(tmpDir, { recursive: true, force: true });
309
+ });
310
+
311
+ it('should reject edge add with error referencing the missing feature slug', async () => {
312
+ const handler = tool.handler;
313
+ if (!handler) throw new Error('tool.handler is undefined');
314
+ const exitCode = await handler(
315
+ ['apply', yamlPath, '--no-render'],
316
+ makeContext(io, { sourceRoot: tmpDir }),
317
+ );
318
+
319
+ assert.equal(exitCode, 1, 'Expected exit code 1 for edge targeting missing feature');
320
+ assert.ok(
321
+ io.stderrText.includes('non-existent-feature'),
322
+ `stderr should contain "non-existent-feature": got ${JSON.stringify(io.stderrText)}`,
323
+ );
324
+ assert.ok(
325
+ io.stderrText.includes('Batch aborted'),
326
+ `stderr should contain "Batch aborted": got ${JSON.stringify(io.stderrText)}`,
327
+ );
328
+ });
329
+ });