@laitszkin/apollo-toolkit 4.0.11 → 4.1.1
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/AGENTS.md +37 -27
- package/CHANGELOG.md +47 -0
- package/CLAUDE.md +37 -27
- package/README.md +15 -2
- package/assets/spec/rg13-1780435029246/test-questions.json +1 -0
- package/assets/spec/rg13-1780468345132/test-questions.json +1 -0
- package/package.json +3 -3
- package/packages/cli/dist/tool-registration.js +1 -0
- package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/cli/tool-registration.ts +1 -0
- package/packages/tools/architecture/dist/index.js +549 -2
- package/packages/tools/architecture/dist/index.test.d.ts +1 -0
- package/packages/tools/architecture/dist/index.test.js +229 -0
- package/packages/tools/architecture/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/architecture/index.test.ts +329 -0
- package/packages/tools/architecture/index.ts +613 -5
- package/packages/tools/codegraph/dist/index.d.ts +3 -0
- package/packages/tools/codegraph/dist/index.js +343 -0
- package/packages/tools/codegraph/dist/lib/cg-instance.d.ts +29 -0
- package/packages/tools/codegraph/dist/lib/cg-instance.js +59 -0
- package/packages/tools/codegraph/dist/lib/cg-instance.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cg-instance.test.js +27 -0
- package/packages/tools/codegraph/dist/lib/cmd-explore.d.ts +5 -0
- package/packages/tools/codegraph/dist/lib/cmd-explore.js +95 -0
- package/packages/tools/codegraph/dist/lib/cmd-explore.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-explore.test.js +133 -0
- package/packages/tools/codegraph/dist/lib/cmd-flag-splice.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-flag-splice.test.js +83 -0
- package/packages/tools/codegraph/dist/lib/cmd-init.d.ts +5 -0
- package/packages/tools/codegraph/dist/lib/cmd-init.js +50 -0
- package/packages/tools/codegraph/dist/lib/cmd-init.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-init.test.js +51 -0
- package/packages/tools/codegraph/dist/lib/cmd-list-apis.d.ts +5 -0
- package/packages/tools/codegraph/dist/lib/cmd-list-apis.js +64 -0
- package/packages/tools/codegraph/dist/lib/cmd-list-apis.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-list-apis.test.js +69 -0
- package/packages/tools/codegraph/dist/lib/cmd-search.d.ts +5 -0
- package/packages/tools/codegraph/dist/lib/cmd-search.js +21 -0
- package/packages/tools/codegraph/dist/lib/cmd-search.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-search.test.js +30 -0
- package/packages/tools/codegraph/dist/lib/cmd-status.d.ts +4 -0
- package/packages/tools/codegraph/dist/lib/cmd-status.js +44 -0
- package/packages/tools/codegraph/dist/lib/cmd-status.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-status.test.js +72 -0
- package/packages/tools/codegraph/dist/lib/cmd-survey.d.ts +36 -0
- package/packages/tools/codegraph/dist/lib/cmd-survey.js +142 -0
- package/packages/tools/codegraph/dist/lib/cmd-survey.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-survey.test.js +136 -0
- package/packages/tools/codegraph/dist/lib/cmd-sync.d.ts +4 -0
- package/packages/tools/codegraph/dist/lib/cmd-sync.js +51 -0
- package/packages/tools/codegraph/dist/lib/cmd-sync.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-sync.test.js +30 -0
- package/packages/tools/codegraph/dist/lib/cmd-verify.d.ts +4 -0
- package/packages/tools/codegraph/dist/lib/cmd-verify.js +134 -0
- package/packages/tools/codegraph/dist/lib/cmd-verify.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/cmd-verify.test.js +139 -0
- package/packages/tools/codegraph/dist/lib/formatter.d.ts +67 -0
- package/packages/tools/codegraph/dist/lib/formatter.js +107 -0
- package/packages/tools/codegraph/dist/lib/formatter.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/formatter.test.js +41 -0
- package/packages/tools/codegraph/dist/lib/survey/grouper.d.ts +19 -0
- package/packages/tools/codegraph/dist/lib/survey/grouper.js +194 -0
- package/packages/tools/codegraph/dist/lib/survey/grouper.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/survey/grouper.test.js +62 -0
- package/packages/tools/codegraph/dist/lib/survey/scanner.d.ts +31 -0
- package/packages/tools/codegraph/dist/lib/survey/scanner.js +50 -0
- package/packages/tools/codegraph/dist/lib/verify/checker.d.ts +32 -0
- package/packages/tools/codegraph/dist/lib/verify/checker.js +146 -0
- package/packages/tools/codegraph/dist/lib/verify/checker.test.d.ts +1 -0
- package/packages/tools/codegraph/dist/lib/verify/checker.test.js +128 -0
- package/packages/tools/codegraph/dist/tsconfig.tsbuildinfo +1 -0
- package/packages/tools/codegraph/env.d.ts +56 -0
- package/packages/tools/codegraph/index.ts +362 -0
- package/packages/tools/codegraph/lib/cg-instance.test.ts +36 -0
- package/packages/tools/codegraph/lib/cg-instance.ts +66 -0
- package/packages/tools/codegraph/lib/cmd-explore.test.ts +195 -0
- package/packages/tools/codegraph/lib/cmd-explore.ts +129 -0
- package/packages/tools/codegraph/lib/cmd-flag-splice.test.ts +94 -0
- package/packages/tools/codegraph/lib/cmd-init.test.ts +68 -0
- package/packages/tools/codegraph/lib/cmd-init.ts +60 -0
- package/packages/tools/codegraph/lib/cmd-list-apis.test.ts +80 -0
- package/packages/tools/codegraph/lib/cmd-list-apis.ts +90 -0
- package/packages/tools/codegraph/lib/cmd-search.test.ts +37 -0
- package/packages/tools/codegraph/lib/cmd-search.ts +32 -0
- package/packages/tools/codegraph/lib/cmd-status.test.ts +86 -0
- package/packages/tools/codegraph/lib/cmd-status.ts +53 -0
- package/packages/tools/codegraph/lib/cmd-survey.test.ts +161 -0
- package/packages/tools/codegraph/lib/cmd-survey.ts +199 -0
- package/packages/tools/codegraph/lib/cmd-sync.test.ts +41 -0
- package/packages/tools/codegraph/lib/cmd-sync.ts +62 -0
- package/packages/tools/codegraph/lib/cmd-verify.test.ts +162 -0
- package/packages/tools/codegraph/lib/cmd-verify.ts +145 -0
- package/packages/tools/codegraph/lib/formatter.test.ts +47 -0
- package/packages/tools/codegraph/lib/formatter.ts +130 -0
- package/packages/tools/codegraph/lib/survey/grouper.test.ts +72 -0
- package/packages/tools/codegraph/lib/survey/grouper.ts +226 -0
- package/packages/tools/codegraph/lib/survey/scanner.ts +89 -0
- package/packages/tools/codegraph/lib/verify/checker.test.ts +140 -0
- package/packages/tools/codegraph/lib/verify/checker.ts +172 -0
- package/packages/tools/codegraph/package.json +23 -0
- package/packages/tools/codegraph/tsconfig.json +22 -0
- package/resources/project-architecture/atlas/atlas.history.log +32 -0
- package/resources/project-architecture/atlas/atlas.history.undo.json +356 -28
- package/resources/project-architecture/atlas/atlas.history.undo.stack.json +14350 -0
- package/resources/project-architecture/atlas/atlas.index.yaml +76 -12
- package/resources/project-architecture/atlas/features/codegraph.yaml +95 -0
- package/resources/project-architecture/atlas/features/eval-ci-gate.yaml +6 -1
- package/resources/project-architecture/atlas/features/eval-cli.yaml +16 -1
- package/resources/project-architecture/atlas/features/eval-executor.yaml +12 -2
- package/resources/project-architecture/atlas/features/eval-isolation.yaml +6 -1
- package/resources/project-architecture/atlas/features/eval-optimizer.yaml +17 -2
- package/resources/project-architecture/atlas/features/eval-question.yaml +12 -2
- package/resources/project-architecture/atlas/features/eval-reporter.yaml +6 -1
- package/resources/project-architecture/atlas/features/eval-scorer.yaml +12 -2
- package/resources/project-architecture/features/codegraph/cg-discovery.html +47 -0
- package/resources/project-architecture/features/codegraph/cg-lifecycle.html +48 -0
- package/resources/project-architecture/features/codegraph/cg-validation.html +47 -0
- package/resources/project-architecture/features/codegraph/index.html +58 -0
- package/resources/project-architecture/features/eval-ci-gate/workflow-trigger.html +6 -1
- package/resources/project-architecture/features/eval-cli/cli-handler.html +8 -1
- package/resources/project-architecture/features/eval-executor/exec-api-client.html +6 -1
- package/resources/project-architecture/features/eval-executor/trace-recorder.html +6 -1
- package/resources/project-architecture/features/eval-isolation/tool-dispatcher.html +6 -1
- package/resources/project-architecture/features/eval-optimizer/dedup-engine.html +6 -1
- package/resources/project-architecture/features/eval-optimizer/issue-extractor.html +7 -1
- package/resources/project-architecture/features/eval-question/question-loader.html +6 -1
- package/resources/project-architecture/features/eval-question/variant-generator.html +6 -1
- package/resources/project-architecture/features/eval-reporter/report-composer.html +6 -1
- package/resources/project-architecture/features/eval-scorer/judge-api-client.html +6 -1
- package/resources/project-architecture/features/eval-scorer/judge-prompt-builder.html +6 -1
- package/resources/project-architecture/index.html +200 -94
- package/scripts/test.sh +39 -0
- package/skills/design/SKILL.md +33 -0
- package/skills/init-project-html/SKILL.md +66 -56
- package/skills/init-project-html/lib/atlas/assets/architecture.css +2 -1
- package/skills/init-project-html/lib/atlas/render.js +11 -1
- package/skills/init-project-html/lib/atlas/schema.js +44 -7
- package/skills/init-project-html/references/TEMPLATE_SPEC.md +20 -0
- package/skills/init-project-html/references/architecture.md +35 -35
- package/skills/init-project-html/references/definition.md +12 -17
- package/skills/update-project-html/README.md +16 -27
- package/skills/update-project-html/SKILL.md +54 -41
- package/skills/update-project-html/references/architecture.md +35 -35
- package/skills/update-project-html/references/definition.md +12 -17
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import type { ToolDefinition, ToolContext } from '@laitszkin/tool-registry';
|
|
2
|
+
import { findProjectRoot } from './lib/cg-instance.js';
|
|
3
|
+
import { handleInit } from './lib/cmd-init.js';
|
|
4
|
+
import { handleSync } from './lib/cmd-sync.js';
|
|
5
|
+
import { handleStatus } from './lib/cmd-status.js';
|
|
6
|
+
import { handleSearch } from './lib/cmd-search.js';
|
|
7
|
+
import { handleExplore } from './lib/cmd-explore.js';
|
|
8
|
+
import { handleSurvey } from './lib/cmd-survey.js';
|
|
9
|
+
import { handleListApis } from './lib/cmd-list-apis.js';
|
|
10
|
+
import { handleVerify } from './lib/cmd-verify.js';
|
|
11
|
+
|
|
12
|
+
export async function codegraphHandler(args: string[], context: ToolContext): Promise<number> {
|
|
13
|
+
const stdout = context.stdout || process.stdout;
|
|
14
|
+
const stderr = context.stderr || process.stderr;
|
|
15
|
+
const projectRoot = findProjectRoot(context.cwd || process.cwd());
|
|
16
|
+
|
|
17
|
+
// Parse --json flag early (can appear anywhere)
|
|
18
|
+
const jsonIndex = args.indexOf('--json');
|
|
19
|
+
const isJson = jsonIndex >= 0;
|
|
20
|
+
if (jsonIndex >= 0) args.splice(jsonIndex, 1);
|
|
21
|
+
|
|
22
|
+
// Main help: no args, --help, -h, or "help" subcommand
|
|
23
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
|
|
24
|
+
printHelp(stdout);
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const subcommand = args[0];
|
|
29
|
+
|
|
30
|
+
// Per-subcommand help: e.g., "apltk codegraph search --help"
|
|
31
|
+
// Check for --help/-h at position 1 (after the subcommand name)
|
|
32
|
+
const rest = args.slice(1);
|
|
33
|
+
if (rest.includes('--help') || rest.includes('-h')) {
|
|
34
|
+
printSubcommandHelp(subcommand, stdout, stderr);
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Parse --spec <dir> for verify
|
|
39
|
+
const specIndex = rest.indexOf('--spec');
|
|
40
|
+
let specDir: string | undefined;
|
|
41
|
+
if (specIndex >= 0 && specIndex + 1 < rest.length) {
|
|
42
|
+
specDir = rest[specIndex + 1];
|
|
43
|
+
rest.splice(specIndex, 2);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Parse --all flag for list-apis
|
|
47
|
+
const allIndex = rest.indexOf('--all');
|
|
48
|
+
const isAll = allIndex >= 0;
|
|
49
|
+
if (allIndex >= 0) rest.splice(allIndex, 1);
|
|
50
|
+
|
|
51
|
+
// Parse --index flag for init
|
|
52
|
+
const shouldIndex = rest.includes('--index');
|
|
53
|
+
const indexIdx = rest.indexOf('--index');
|
|
54
|
+
if (indexIdx >= 0) rest.splice(indexIdx, 1);
|
|
55
|
+
|
|
56
|
+
// Parse --feature <name> for survey
|
|
57
|
+
const featureIndex = rest.indexOf('--feature');
|
|
58
|
+
let featureName: string | undefined;
|
|
59
|
+
if (featureIndex >= 0 && featureIndex + 1 < rest.length) {
|
|
60
|
+
featureName = rest[featureIndex + 1];
|
|
61
|
+
rest.splice(featureIndex, 2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Parse limit for search
|
|
65
|
+
const limitIndex = rest.indexOf('--limit');
|
|
66
|
+
let limit: number | undefined;
|
|
67
|
+
if (limitIndex >= 0 && limitIndex + 1 < rest.length) {
|
|
68
|
+
limit = parseInt(rest[limitIndex + 1], 10);
|
|
69
|
+
rest.splice(limitIndex, 2);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
switch (subcommand) {
|
|
74
|
+
case 'init':
|
|
75
|
+
return await handleInit(projectRoot, { index: shouldIndex, json: isJson });
|
|
76
|
+
|
|
77
|
+
case 'sync':
|
|
78
|
+
return await handleSync(projectRoot, { json: isJson });
|
|
79
|
+
|
|
80
|
+
case 'status':
|
|
81
|
+
return await handleStatus(projectRoot, { json: isJson });
|
|
82
|
+
|
|
83
|
+
case 'search': {
|
|
84
|
+
const query = rest.join(' ');
|
|
85
|
+
if (!query) {
|
|
86
|
+
stderr.write('Usage: apltk codegraph search <query> [--limit N] [--json]\n');
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
return await handleSearch(projectRoot, query, { limit, json: isJson });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case 'explore': {
|
|
93
|
+
const query = rest.join(' ');
|
|
94
|
+
if (!query) {
|
|
95
|
+
stderr.write('Usage: apltk codegraph explore <query> [--json]\n');
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
return await handleExplore(projectRoot, query, { json: isJson, feature: featureName });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'survey': {
|
|
102
|
+
const dirPath = rest[0] || '.';
|
|
103
|
+
return await handleSurvey(projectRoot, dirPath, { feature: featureName, json: isJson });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
case 'list-apis': {
|
|
107
|
+
const pathArg = rest[0];
|
|
108
|
+
const combinedPath = featureName
|
|
109
|
+
? (pathArg ? `${featureName}/${pathArg.replace(/^\//, '')}` : featureName)
|
|
110
|
+
: pathArg;
|
|
111
|
+
return await handleListApis(projectRoot, combinedPath, { all: isAll, json: isJson });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case 'verify': {
|
|
115
|
+
if (!specDir) {
|
|
116
|
+
stderr.write('Usage: apltk codegraph verify --spec <spec-dir> [--json]\n');
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
return await handleVerify(projectRoot, specDir, { json: isJson });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
default:
|
|
123
|
+
stderr.write(`Unknown subcommand: ${subcommand}\n\n`);
|
|
124
|
+
printHelp(stderr);
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
} catch (error: any) {
|
|
128
|
+
if (error.code === 'MODULE_NOT_FOUND' || (error.message && error.message.includes('Cannot find module'))) {
|
|
129
|
+
stderr.write('`@colbymchenry/codegraph` is not installed. Run `npm install @colbymchenry/codegraph` in your project directory.\n');
|
|
130
|
+
} else {
|
|
131
|
+
stderr.write(`Error running codegraph ${subcommand}: ${error.message}\n`);
|
|
132
|
+
}
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function printHelp(stream: NodeJS.WriteStream): void {
|
|
138
|
+
stream.write(`Usage: apltk codegraph <subcommand> [options]
|
|
139
|
+
|
|
140
|
+
CodeGraph code intelligence — parse source code into a knowledge graph
|
|
141
|
+
of symbols (functions, classes) and relationships (call edges), backed by
|
|
142
|
+
a local SQLite database with FTS5 full-text search.
|
|
143
|
+
|
|
144
|
+
Powered by @colbymchenry/codegraph (tree-sitter-backed code knowledge graph).
|
|
145
|
+
|
|
146
|
+
Subcommands:
|
|
147
|
+
|
|
148
|
+
lifecycle:
|
|
149
|
+
init Initialize CodeGraph for the project
|
|
150
|
+
--index Run initial indexing immediately after init
|
|
151
|
+
--json JSON output
|
|
152
|
+
|
|
153
|
+
sync Sync the index with current file state
|
|
154
|
+
--json JSON output
|
|
155
|
+
|
|
156
|
+
status Show index statistics (files, nodes, edges, languages)
|
|
157
|
+
--json JSON output
|
|
158
|
+
|
|
159
|
+
discovery:
|
|
160
|
+
search <query> Search the code graph for symbols via FTS5
|
|
161
|
+
--limit N Max results (default: 20, max: 100)
|
|
162
|
+
--json JSON output
|
|
163
|
+
|
|
164
|
+
explore <query> Deep-dive on a symbol — show callers, callees, and source
|
|
165
|
+
--feature <name> Only show results within this feature
|
|
166
|
+
--json JSON output
|
|
167
|
+
|
|
168
|
+
survey [dir] Scan a directory, suggest submodule groupings and
|
|
169
|
+
cross-boundary edges for atlas modelling
|
|
170
|
+
--feature <name> Feature context for grouping
|
|
171
|
+
--json JSON output
|
|
172
|
+
|
|
173
|
+
list-apis [path] List public APIs in the project or within a sub-path
|
|
174
|
+
--all Include non-exported (internal) symbols
|
|
175
|
+
--json JSON output
|
|
176
|
+
|
|
177
|
+
validation:
|
|
178
|
+
verify --spec <dir>
|
|
179
|
+
Verify a spec overlay against actual code —
|
|
180
|
+
checks that every declared feature, submodule,
|
|
181
|
+
function, and edge exists in the code graph
|
|
182
|
+
--json JSON output
|
|
183
|
+
|
|
184
|
+
Global options:
|
|
185
|
+
--json Output as JSON instead of human-readable format
|
|
186
|
+
--help, -h Show this help message
|
|
187
|
+
|
|
188
|
+
Use "apltk codegraph <subcommand> --help" for per-subcommand details.
|
|
189
|
+
|
|
190
|
+
Examples:
|
|
191
|
+
apltk codegraph init
|
|
192
|
+
apltk codegraph init --index
|
|
193
|
+
apltk codegraph status --json
|
|
194
|
+
apltk codegraph search getUser
|
|
195
|
+
apltk codegraph search getUser --limit 5 --json
|
|
196
|
+
apltk codegraph explore handleLogin
|
|
197
|
+
apltk codegraph survey src/
|
|
198
|
+
apltk codegraph survey src/ --feature auth --json
|
|
199
|
+
apltk codegraph list-apis --all
|
|
200
|
+
apltk codegraph verify --spec docs/plans/2026-05-11/add-2fa
|
|
201
|
+
`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function printSubcommandHelp(subcommand: string, stream: NodeJS.WriteStream, errStream: NodeJS.WriteStream): void {
|
|
205
|
+
const PAD = ' ';
|
|
206
|
+
|
|
207
|
+
const helps: Record<string, string> = {
|
|
208
|
+
init: `Usage: apltk codegraph init [--index] [--json]
|
|
209
|
+
|
|
210
|
+
Initialize the CodeGraph knowledge graph for the project.
|
|
211
|
+
Creates the .codegraph/ directory and SQLite database.
|
|
212
|
+
|
|
213
|
+
Flags:
|
|
214
|
+
--index Run initial indexing immediately after init, so the
|
|
215
|
+
knowledge graph is ready for queries right away.
|
|
216
|
+
Without this flag, you need to run "apltk codegraph sync"
|
|
217
|
+
separately before searching or exploring.
|
|
218
|
+
--json Output confirmation as JSON.
|
|
219
|
+
|
|
220
|
+
Examples:
|
|
221
|
+
apltk codegraph init
|
|
222
|
+
apltk codegraph init --index
|
|
223
|
+
`,
|
|
224
|
+
sync: `Usage: apltk codegraph sync [--json]
|
|
225
|
+
|
|
226
|
+
Sync the code graph index with the current state of files on disk.
|
|
227
|
+
Parses changed files and updates the SQLite database.
|
|
228
|
+
|
|
229
|
+
This is needed after you modify source files if you want queries
|
|
230
|
+
to reflect the latest code. Runs incrementally — only reprocesses
|
|
231
|
+
files whose mtime has changed.
|
|
232
|
+
|
|
233
|
+
Flags:
|
|
234
|
+
--json Output sync results (files added/removed/updated) as JSON.
|
|
235
|
+
|
|
236
|
+
Examples:
|
|
237
|
+
apltk codegraph sync
|
|
238
|
+
apltk codegraph sync --json
|
|
239
|
+
`,
|
|
240
|
+
status: `Usage: apltk codegraph status [--json]
|
|
241
|
+
|
|
242
|
+
Show index statistics: file count, symbol (node) count, edge count,
|
|
243
|
+
languages detected, and last-sync timestamp.
|
|
244
|
+
|
|
245
|
+
Flags:
|
|
246
|
+
--json Output full statistics as a JSON object.
|
|
247
|
+
|
|
248
|
+
Examples:
|
|
249
|
+
apltk codegraph status
|
|
250
|
+
apltk codegraph status --json
|
|
251
|
+
`,
|
|
252
|
+
search: `Usage: apltk codegraph search <query> [--limit N] [--json]
|
|
253
|
+
|
|
254
|
+
Full-text search the code graph for symbols (functions, classes, variables).
|
|
255
|
+
Uses FTS5 (SQLite full-text search) under the hood.
|
|
256
|
+
|
|
257
|
+
Arguments:
|
|
258
|
+
query Search term (required). Matches against symbol names and
|
|
259
|
+
source code content.
|
|
260
|
+
|
|
261
|
+
Flags:
|
|
262
|
+
--limit N Max results to return (default: 20, max: 100).
|
|
263
|
+
--json Output results as a JSON array.
|
|
264
|
+
|
|
265
|
+
Examples:
|
|
266
|
+
apltk codegraph search getUser
|
|
267
|
+
apltk codegraph search handleLogin --limit 5
|
|
268
|
+
apltk codegraph search "class.*Handler" --limit 10 --json
|
|
269
|
+
`,
|
|
270
|
+
explore: `Usage: apltk codegraph explore <query> [--feature <name>] [--json]
|
|
271
|
+
|
|
272
|
+
Deep-dive on a symbol — shows who calls it, what it calls, and its
|
|
273
|
+
full source code. Useful for understanding how a function or class
|
|
274
|
+
fits into the broader codebase.
|
|
275
|
+
|
|
276
|
+
Arguments:
|
|
277
|
+
query Symbol name to explore (required).
|
|
278
|
+
|
|
279
|
+
Flags:
|
|
280
|
+
--feature <name>
|
|
281
|
+
Scope results to only show callers/callees within a
|
|
282
|
+
specific feature directory (e.g. "auth", "billing").
|
|
283
|
+
--json Output full exploration data as JSON (callers, callees,
|
|
284
|
+
source code, file path, line numbers).
|
|
285
|
+
|
|
286
|
+
Examples:
|
|
287
|
+
apltk codegraph explore handleLogin
|
|
288
|
+
apltk codegraph explore authenticate --feature auth
|
|
289
|
+
apltk codegraph explore sendEmail --json
|
|
290
|
+
`,
|
|
291
|
+
survey: `Usage: apltk codegraph survey [dir] [--feature <name>] [--json]
|
|
292
|
+
|
|
293
|
+
Scan a directory and produce a structured survey report with suggested
|
|
294
|
+
submodule groupings, cross-boundary edges, and entry points.
|
|
295
|
+
|
|
296
|
+
This is the primary input for the "init-project-html" skill's Step 1 —
|
|
297
|
+
it tells the LLM how to model features and submodules for the atlas.
|
|
298
|
+
|
|
299
|
+
Arguments:
|
|
300
|
+
dir Directory to scan (default: current directory ".").
|
|
301
|
+
|
|
302
|
+
Flags:
|
|
303
|
+
--feature <name>
|
|
304
|
+
Feature context: scope the survey to only one feature's
|
|
305
|
+
boundary. Helps the grouper cluster symbols more accurately.
|
|
306
|
+
--json Output survey results as JSON — best for LLM consumption.
|
|
307
|
+
|
|
308
|
+
Examples:
|
|
309
|
+
apltk codegraph survey
|
|
310
|
+
apltk codegraph survey src/
|
|
311
|
+
apltk codegraph survey src/auth --feature auth --json
|
|
312
|
+
`,
|
|
313
|
+
'list-apis': `Usage: apltk codegraph list-apis [path] [--all] [--json]
|
|
314
|
+
|
|
315
|
+
List public (exported) symbols in the project or a specific sub-path.
|
|
316
|
+
Useful for understanding the public surface area of a module.
|
|
317
|
+
|
|
318
|
+
Arguments:
|
|
319
|
+
path Sub-path to scan (e.g. "src/auth"). When omitted, scans
|
|
320
|
+
the entire project.
|
|
321
|
+
|
|
322
|
+
Flags:
|
|
323
|
+
--all Include non-exported (internal) symbols in the listing.
|
|
324
|
+
--json Output API list as JSON.
|
|
325
|
+
|
|
326
|
+
Examples:
|
|
327
|
+
apltk codegraph list-apis
|
|
328
|
+
apltk codegraph list-apis src/auth
|
|
329
|
+
apltk codegraph list-apis --all --json
|
|
330
|
+
`,
|
|
331
|
+
verify: `Usage: apltk codegraph verify --spec <spec-dir> [--json]
|
|
332
|
+
|
|
333
|
+
Verify a spec overlay's architecture proposals against actual code.
|
|
334
|
+
Checks that every feature, submodule, function, and edge referenced
|
|
335
|
+
in the overlay's atlas state actually exists in the code graph.
|
|
336
|
+
|
|
337
|
+
Flags:
|
|
338
|
+
--spec <spec-dir>
|
|
339
|
+
Path to the spec directory containing an architecture_diff/
|
|
340
|
+
overlay (required). For example, "docs/plans/2026-05-11/add-2fa".
|
|
341
|
+
--json Output verification results as JSON (passed/failed checks).
|
|
342
|
+
|
|
343
|
+
Examples:
|
|
344
|
+
apltk codegraph verify --spec docs/plans/2026-05-11/add-2fa
|
|
345
|
+
apltk codegraph verify --spec docs/plans/2026-05-11/add-2fa --json
|
|
346
|
+
`,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const text = helps[subcommand];
|
|
350
|
+
if (text) {
|
|
351
|
+
stream.write(text);
|
|
352
|
+
} else {
|
|
353
|
+
errStream.write(`Unknown subcommand: "${subcommand}". Use "apltk codegraph --help" for the list of available subcommands.\n`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export const tool: ToolDefinition = {
|
|
358
|
+
name: 'codegraph',
|
|
359
|
+
category: 'Code analysis',
|
|
360
|
+
description: 'CodeGraph code intelligence — init, sync, status, search, explore, survey, list-apis, verify',
|
|
361
|
+
handler: codegraphHandler,
|
|
362
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, it, before, after } 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
|
+
|
|
7
|
+
import { createOrOpenIndex } from './cg-instance.js';
|
|
8
|
+
|
|
9
|
+
describe('createOrOpenIndex', () => {
|
|
10
|
+
let tmpDir: string;
|
|
11
|
+
|
|
12
|
+
before(() => {
|
|
13
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-test-'));
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
after(() => {
|
|
17
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should throw when project is already initialized', async () => {
|
|
21
|
+
// Arrange: create .codegraph/ and codegraph.db to simulate an initialized project
|
|
22
|
+
const codegraphDir = path.join(tmpDir, '.codegraph');
|
|
23
|
+
fs.mkdirSync(codegraphDir, { recursive: true });
|
|
24
|
+
fs.writeFileSync(path.join(codegraphDir, 'codegraph.db'), '');
|
|
25
|
+
|
|
26
|
+
// Act & Assert
|
|
27
|
+
await assert.rejects(
|
|
28
|
+
() => createOrOpenIndex(tmpDir),
|
|
29
|
+
(err: unknown) => {
|
|
30
|
+
assert.ok(err instanceof Error);
|
|
31
|
+
assert.match(err.message, /sync/);
|
|
32
|
+
return true;
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
|
|
6
|
+
let _codeGraphModule: any = null;
|
|
7
|
+
export function getCodeGraphModule(): { CodeGraph: any; findNearestCodeGraphRoot: any } {
|
|
8
|
+
if (!_codeGraphModule) {
|
|
9
|
+
_codeGraphModule = require('@colbymchenry/codegraph');
|
|
10
|
+
}
|
|
11
|
+
return _codeGraphModule;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Locate the project root by walking up from the given directory.
|
|
16
|
+
* Returns the nearest parent containing `.codegraph/`, or falls back
|
|
17
|
+
* to the nearest parent containing `package.json`.
|
|
18
|
+
*/
|
|
19
|
+
export function findProjectRoot(startPath?: string): string {
|
|
20
|
+
const cwd = startPath || process.cwd();
|
|
21
|
+
const codegraphRoot = getCodeGraphModule().findNearestCodeGraphRoot(cwd);
|
|
22
|
+
if (codegraphRoot) return codegraphRoot;
|
|
23
|
+
|
|
24
|
+
// Fallback: walk up looking for package.json
|
|
25
|
+
let dir = path.resolve(cwd);
|
|
26
|
+
while (true) {
|
|
27
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
|
|
28
|
+
const parent = path.dirname(dir);
|
|
29
|
+
if (parent === dir) return cwd; // hit filesystem root
|
|
30
|
+
dir = parent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Initialize a CodeGraph index for the given project root.
|
|
36
|
+
*
|
|
37
|
+
* If the project is already initialized, throws an error suggesting
|
|
38
|
+
* `apltk codegraph sync` instead. Otherwise, initializes a new CodeGraph
|
|
39
|
+
* project. When `options.index` is true, runs initial indexing after init.
|
|
40
|
+
*
|
|
41
|
+
* Note: `CodeGraph.init()` supports an `{ index: true }` shorthand that
|
|
42
|
+
* runs initial indexing inline -- this deviates from a two-step init-then-index
|
|
43
|
+
* pattern but is the supported API through the npm package.
|
|
44
|
+
*/
|
|
45
|
+
export async function createOrOpenIndex(
|
|
46
|
+
projectRoot: string,
|
|
47
|
+
options?: { index?: boolean; onProgress?: (progress: any) => void },
|
|
48
|
+
): Promise<any> {
|
|
49
|
+
const isInit = getCodeGraphModule().CodeGraph.isInitialized(projectRoot);
|
|
50
|
+
if (isInit) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Project is already initialized at ${projectRoot}. Use \`apltk codegraph sync\` to update the index.`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return getCodeGraphModule().CodeGraph.init(projectRoot, {
|
|
56
|
+
index: options?.index ?? false,
|
|
57
|
+
onProgress: options?.onProgress,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Close a CodeGraph instance and release resources.
|
|
63
|
+
*/
|
|
64
|
+
export function closeIndex(cg: any): void {
|
|
65
|
+
cg.close();
|
|
66
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { describe, it, mock, before } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Shared mutable mock state — each test sets its own data before calling the
|
|
7
|
+
// handler. The mock instance's searchNodes captures this array by reference,
|
|
8
|
+
// so mutations are reflected in every invocation.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
interface MockSymbolNode {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
kind: string;
|
|
14
|
+
filePath: string;
|
|
15
|
+
startLine: number;
|
|
16
|
+
endLine: number;
|
|
17
|
+
qualifiedName: string;
|
|
18
|
+
signature?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface MockSearchResult {
|
|
22
|
+
node: MockSymbolNode;
|
|
23
|
+
score: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const mockSearchResults: MockSearchResult[] = [];
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Pre-load @colbymchenry/codegraph so we can mock its methods before
|
|
30
|
+
// cmd-explore.js performs its own require().
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
const require = createRequire(import.meta.url);
|
|
33
|
+
const { CodeGraph } = require('@colbymchenry/codegraph');
|
|
34
|
+
|
|
35
|
+
let mockInstance: {
|
|
36
|
+
searchNodes: () => { node: MockSymbolNode; score: number }[];
|
|
37
|
+
getCallers: () => never[];
|
|
38
|
+
getCallees: () => never[];
|
|
39
|
+
getCode: () => Promise<null>;
|
|
40
|
+
close: () => void;
|
|
41
|
+
} | undefined;
|
|
42
|
+
|
|
43
|
+
before(() => {
|
|
44
|
+
mockInstance = {
|
|
45
|
+
searchNodes: () => mockSearchResults.map(r => ({ node: r.node, score: r.score })),
|
|
46
|
+
getCallers: () => [],
|
|
47
|
+
getCallees: () => [],
|
|
48
|
+
getCode: async () => null,
|
|
49
|
+
close: () => {},
|
|
50
|
+
};
|
|
51
|
+
mock.method(CodeGraph, 'open', async () => mockInstance);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Import the module under test (same cached CodeGraph is used internally)
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
let handleExplore: (
|
|
58
|
+
projectRoot: string,
|
|
59
|
+
query: string,
|
|
60
|
+
options?: Record<string, unknown>,
|
|
61
|
+
) => Promise<number>;
|
|
62
|
+
|
|
63
|
+
before(async () => {
|
|
64
|
+
const mod = await import('./cmd-explore.js');
|
|
65
|
+
handleExplore = mod.handleExplore;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// =========================================================================
|
|
69
|
+
// REGTEST-7: Explore output should group symbols by file
|
|
70
|
+
// =========================================================================
|
|
71
|
+
describe('REGTEST-7: Explore grouping by file', () => {
|
|
72
|
+
it('should group symbols under a single file header', async () => {
|
|
73
|
+
// Arrange: two symbols in the same file
|
|
74
|
+
mockSearchResults.length = 0;
|
|
75
|
+
mockSearchResults.push(
|
|
76
|
+
{
|
|
77
|
+
node: {
|
|
78
|
+
id: '1',
|
|
79
|
+
name: 'addUser',
|
|
80
|
+
kind: 'function',
|
|
81
|
+
filePath: 'src/utils.ts',
|
|
82
|
+
startLine: 10,
|
|
83
|
+
endLine: 25,
|
|
84
|
+
qualifiedName: 'utils.addUser',
|
|
85
|
+
signature: '(name: string, age: number): User',
|
|
86
|
+
},
|
|
87
|
+
score: 0.95,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
node: {
|
|
91
|
+
id: '2',
|
|
92
|
+
name: 'deleteUser',
|
|
93
|
+
kind: 'function',
|
|
94
|
+
filePath: 'src/utils.ts',
|
|
95
|
+
startLine: 30,
|
|
96
|
+
endLine: 40,
|
|
97
|
+
qualifiedName: 'utils.deleteUser',
|
|
98
|
+
signature: '(id: number): void',
|
|
99
|
+
},
|
|
100
|
+
score: 0.85,
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Capture stdout
|
|
105
|
+
const stdoutChunks: string[] = [];
|
|
106
|
+
const origWrite = process.stdout.write;
|
|
107
|
+
process.stdout.write = ((chunk: unknown) => {
|
|
108
|
+
stdoutChunks.push(String(chunk));
|
|
109
|
+
return true;
|
|
110
|
+
}) as typeof process.stdout.write;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
// Act
|
|
114
|
+
const exitCode = await handleExplore('/fake/project', 'utils', {});
|
|
115
|
+
|
|
116
|
+
// Assert
|
|
117
|
+
assert.strictEqual(exitCode, 0);
|
|
118
|
+
const output = stdoutChunks.join('');
|
|
119
|
+
|
|
120
|
+
// Exactly one file header for the shared filePath
|
|
121
|
+
const headerMatches = output.match(/=== src\/utils\.ts ===/g);
|
|
122
|
+
assert.strictEqual(
|
|
123
|
+
headerMatches?.length,
|
|
124
|
+
1,
|
|
125
|
+
'Expected exactly one file header for src/utils.ts, ' +
|
|
126
|
+
`got ${headerMatches?.length ?? 0}`,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// Both symbols appear in the output
|
|
130
|
+
assert.ok(output.includes('addUser'), 'Output should contain addUser');
|
|
131
|
+
assert.ok(output.includes('deleteUser'), 'Output should contain deleteUser');
|
|
132
|
+
|
|
133
|
+
// File header precedes both symbols (not duplicated)
|
|
134
|
+
const headerIdx = output.indexOf('=== src/utils.ts ===');
|
|
135
|
+
assert.ok(
|
|
136
|
+
headerIdx < output.indexOf('addUser'),
|
|
137
|
+
'File header should appear before addUser',
|
|
138
|
+
);
|
|
139
|
+
assert.ok(
|
|
140
|
+
headerIdx < output.indexOf('deleteUser'),
|
|
141
|
+
'File header should appear before deleteUser',
|
|
142
|
+
);
|
|
143
|
+
} finally {
|
|
144
|
+
process.stdout.write = origWrite;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// =========================================================================
|
|
150
|
+
// REGTEST-8: Explore --feature acceptance
|
|
151
|
+
// =========================================================================
|
|
152
|
+
describe('REGTEST-8: Explore --feature acceptance', () => {
|
|
153
|
+
it('should accept feature parameter without error', async () => {
|
|
154
|
+
// Arrange: at least one result so the feature line is emitted
|
|
155
|
+
mockSearchResults.length = 0;
|
|
156
|
+
mockSearchResults.push({
|
|
157
|
+
node: {
|
|
158
|
+
id: '3',
|
|
159
|
+
name: 'authLogin',
|
|
160
|
+
kind: 'function',
|
|
161
|
+
filePath: 'src/auth.ts',
|
|
162
|
+
startLine: 5,
|
|
163
|
+
endLine: 20,
|
|
164
|
+
qualifiedName: 'auth.login',
|
|
165
|
+
signature: '(credentials: Record<string, unknown>): Session',
|
|
166
|
+
},
|
|
167
|
+
score: 0.9,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const stdoutChunks: string[] = [];
|
|
171
|
+
const origWrite = process.stdout.write;
|
|
172
|
+
process.stdout.write = ((chunk: unknown) => {
|
|
173
|
+
stdoutChunks.push(String(chunk));
|
|
174
|
+
return true;
|
|
175
|
+
}) as typeof process.stdout.write;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
// Act — pass feature without json, expect no error
|
|
179
|
+
const exitCode = await handleExplore('/fake/project', 'authLogin', {
|
|
180
|
+
feature: 'auth',
|
|
181
|
+
json: false,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Assert
|
|
185
|
+
assert.strictEqual(exitCode, 0, 'Should return exit code 0');
|
|
186
|
+
const output = stdoutChunks.join('');
|
|
187
|
+
assert.ok(
|
|
188
|
+
output.includes('Feature: auth'),
|
|
189
|
+
'Output should include "Feature: auth" header',
|
|
190
|
+
);
|
|
191
|
+
} finally {
|
|
192
|
+
process.stdout.write = origWrite;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|