@laitszkin/apollo-toolkit 4.1.0 → 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.
@@ -19,13 +19,21 @@ export async function codegraphHandler(args: string[], context: ToolContext): Pr
19
19
  const isJson = jsonIndex >= 0;
20
20
  if (jsonIndex >= 0) args.splice(jsonIndex, 1);
21
21
 
22
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
22
+ // Main help: no args, --help, -h, or "help" subcommand
23
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
23
24
  printHelp(stdout);
24
25
  return 0;
25
26
  }
26
27
 
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)
28
32
  const rest = args.slice(1);
33
+ if (rest.includes('--help') || rest.includes('-h')) {
34
+ printSubcommandHelp(subcommand, stdout, stderr);
35
+ return 0;
36
+ }
29
37
 
30
38
  // Parse --spec <dir> for verify
31
39
  const specIndex = rest.indexOf('--spec');
@@ -129,42 +137,223 @@ export async function codegraphHandler(args: string[], context: ToolContext): Pr
129
137
  function printHelp(stream: NodeJS.WriteStream): void {
130
138
  stream.write(`Usage: apltk codegraph <subcommand> [options]
131
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
+
132
146
  Subcommands:
133
147
 
134
148
  lifecycle:
135
149
  init Initialize CodeGraph for the project
136
- --index Run initial indexing after init
150
+ --index Run initial indexing immediately after init
151
+ --json JSON output
137
152
 
138
153
  sync Sync the index with current file state
154
+ --json JSON output
139
155
 
140
- status Show index statistics (files, nodes, edges)
156
+ status Show index statistics (files, nodes, edges, languages)
157
+ --json JSON output
141
158
 
142
159
  discovery:
143
- search <query> Search the code graph for symbols
144
- --limit N Max results (default: 20)
145
-
146
- explore <query> Deep-dive on a symbol (callers, callees, source)
160
+ search <query> Search the code graph for symbols via FTS5
161
+ --limit N Max results (default: 20, max: 100)
147
162
  --json JSON output
148
163
 
149
- survey [dir] Scan a directory and suggest submodule groupings
150
- --feature <name> Feature context
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
151
171
  --json JSON output
152
172
 
153
- list-apis [path] List public APIs in the project or a sub-path
154
- --all Include non-exported symbols
173
+ list-apis [path] List public APIs in the project or within a sub-path
174
+ --all Include non-exported (internal) symbols
155
175
  --json JSON output
156
176
 
157
177
  validation:
158
- verify Verify a spec overlay against the actual code
159
- --spec <dir> Spec directory (required)
160
- --json JSON output
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
161
183
 
162
184
  Global options:
163
185
  --json Output as JSON instead of human-readable format
164
- --help Show this help message
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
165
201
  `);
166
202
  }
167
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
+
168
357
  export const tool: ToolDefinition = {
169
358
  name: 'codegraph',
170
359
  category: 'Code analysis',
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ # Split test runner — isolates mock.module tests from the rest to avoid
3
+ # a Node.js 24.x test runner IPC deserialization issue that can make
4
+ # tests flaky when --experimental-test-module-mocks is active globally.
5
+ # See: https://github.com/nodejs/node/issues (test_runner IPC clone)
6
+
7
+ EXIT=0
8
+
9
+ run_test_group() {
10
+ local label="$1"
11
+ shift
12
+ echo ""
13
+ echo "==> $label"
14
+ if "$@"; then
15
+ echo " PASS"
16
+ else
17
+ echo " FAIL"
18
+ EXIT=1
19
+ fi
20
+ }
21
+
22
+ # Group 1: stable non-mock tests (test/)
23
+ run_test_group "Stable tests (test/)" \
24
+ node --test 'test/**/*.test.js'
25
+
26
+ # Group 2: package .test.js files that do NOT need mock.module
27
+ EXCLUDE='(cmd-init|cmd-list-apis|cmd-survey)'
28
+ PACKAGE_TEST_FILES=$(find packages -name '*.test.js' -not -path '*/node_modules/*' | grep -v -E "$EXCLUDE" | sort | tr '\n' ' ')
29
+ run_test_group "Package tests (no mock.module)" \
30
+ node --test $PACKAGE_TEST_FILES
31
+
32
+ # Group 3: mock-dependent tests — isolated with --experimental-test-module-mocks
33
+ run_test_group "Package tests (mock.module)" \
34
+ node --experimental-test-module-mocks --test \
35
+ 'packages/tools/codegraph/dist/lib/cmd-init.test.js' \
36
+ 'packages/tools/codegraph/dist/lib/cmd-list-apis.test.js' \
37
+ 'packages/tools/codegraph/dist/lib/cmd-survey.test.js'
38
+
39
+ exit $EXIT
@@ -1,31 +1,30 @@
1
1
  ---
2
2
  name: init-project-html
3
- description: 為項目初始化架構圖。透過 apltk CLI 將功能模塊與子模塊的關係轉化為可渲染的 HTML 架構圖,採用 C4 模型層級(Context → Container → Component → Code)。
3
+ description: Initialize the project architecture atlas. Use the apltk CLI to map feature and submodule relationships into a renderable HTML architecture diagram following the C4 model (Context → Container → Component → Code).
4
4
  ---
5
5
 
6
- ## 技能目標
6
+ ## Goal
7
7
 
8
- 透過 `apltk` CLI 製作項目架構圖。
9
- 幫助用戶理解專案的軟體架構。
8
+ Produce a project architecture diagram via the `apltk` CLI.
9
+ Help users understand the project's software architecture.
10
10
 
11
- ## 驗收條件
11
+ ## Acceptance Criteria
12
12
 
13
- - 所有子模塊之間的 edge 被完整定義
14
- - 所有子模塊內部的 edge 被完整定義
15
- - 架構圖完整展示整個系統之中子模塊之間的關係以及功能模塊之間的關係
16
- - 每個宣告的 component 皆附有來源程式碼證據;無法確定的標記為 inferred
17
- - 架構圖層級對應 C4 model:功能模塊(Container 層級)→ 子模塊(Component 層級)
13
+ - The diagram covers all four C4 levels: System Context → Container (feature) → Component (submodule) → Code (function row)
14
+ - All cross-module and intra-module edges are fully defined
15
+ - Every declared component is backed by source code evidence (`evidence.sourceFile:sourceLine`); unresolved ones are tagged `inferred`
16
+ - Every submodule must declare its `functions` and `variables` arrays (mandatory, may not be left empty)
18
17
 
19
- ## C4 模型對照
18
+ ## C4 Mapping
20
19
 
21
- 本技能的「功能模塊」與「子模塊」對應到 C4 model 的以下層級:
20
+ This skill's "feature" and "submodule" map to the C4 model as follows:
22
21
 
23
- | C4 層級 | 本技能對應 | 說明 | 使用時機 |
24
- |---------|-----------|------|---------|
25
- | System Context | 整體系統 | 系統與外部 actor、外部系統的關係 | 步驟 1 建立基線認知 |
26
- | Container | 功能模塊 | 高階功能邊界(如登入功能、支付功能) | 主要抽象層級 |
27
- | Component | 子模塊 | 功能內部的實作單元(如 controllerservicerepository | 主要詳細層級 |
28
- | Code | function | 函式層級細節 | 只在特定關鍵路徑需要 |
22
+ | C4 Level | Skill Equivalent | Description | When to Use |
23
+ |----------|-----------------|-------------|-------------|
24
+ | System Context | Whole system | System boundaries, external actors, and external systems | Step 1 establish baseline awareness |
25
+ | Container | Feature | High-level functional boundary (e.g. Login, Payment) | Primary abstraction level |
26
+ | Component | Submodule | Implementation unit inside a feature (controller, service, repository) | Primary detail level |
27
+ | Code | Function row | Function-level detail with source file and line evidence | Mandatory — every submodule must declare its functions with `evidence.sourceFile:sourceLine` |
29
28
 
30
29
  ## Mode Detection
31
30
 
@@ -48,59 +47,69 @@ At load time, check the project state to select the correct mode:
48
47
  directory already exists and is non-empty, pause and ask the user whether to:
49
48
  (a) overwrite the existing atlas, (b) switch to update mode, or (c) abort.
50
49
 
51
- ## 工作流程
50
+ ## Workflow
52
51
 
53
- 適用模式:design(完整初始化)、record(快速記錄)
52
+ Applicable modes: design (full initialization), record (quick recording)
54
53
 
55
- ### 1. 使用 codegraph survey 取得專案結構
54
+ ### 1. Survey the project with `apltk codegraph survey`
56
55
 
57
- 在深入程式碼前,先建立系統的宏觀認知:
58
- - 系統與哪些外部 actor 互動(使用者、第三方服務、其他系統)
59
- - 系統提供哪些高階能力
56
+ Before diving into the code, establish a high-level understanding:
57
+ - Which external actors interact with the system (users, third-party services, other systems)
58
+ - What high-level capabilities the system provides
60
59
 
61
- 然後閱讀 `sample-demo/` 了解預期的輸出格式與抽象層級。
60
+ Then read `sample-demo/` to understand the expected output format and abstraction level.
62
61
 
63
- 接著使用 `apltk codegraph survey` 取得專案的結構化調查報告:
64
- - 專案目錄下的所有檔案清單與函式數量
65
- - Entry points(被外部檔案呼叫的公開函式)
66
- - 建議的 submodule 分組與跨邊界 edge
67
- - 支援 `--json` 輸出供 LLM 程式化消費
62
+ Next, run `apltk codegraph survey` to get a structured survey report:
63
+ - List of all files in the project directory with function counts
64
+ - Entry points (public functions called from external files)
65
+ - Suggested submodule groupings and cross-boundary edges
66
+ - Supports `--json` output for programmatic consumption by the LLM
68
67
 
69
- 根據 survey 報告,決定功能模塊的劃分(對應 C4 Container 層級):
70
- - 將高度互相呼叫的函式群歸類為同一功能模塊的子模塊
71
- - 識別功能模塊之間的邊界與跨模塊呼叫關係
72
- - 記錄每個功能模塊對應的目錄路徑與 entry point
68
+ Based on the survey report, decide how to partition features (C4 Container level):
69
+ - Group closely interconnected function clusters into the same feature's submodules
70
+ - Identify feature boundaries and cross-feature call relationships
71
+ - Record the directory path and entry points for each feature
73
72
 
74
- ### 2. 使用 `apltk architecture apply` 批次寫入 atlas
73
+ ### 2. Write the atlas with `apltk architecture apply`
75
74
 
76
- 依照 C4 層級逐步產生:
77
- 在操作 CLI 前先閱讀 `references/architecture.md` 了解所有參數與 mutation 系列的使用方式。
75
+ Generate the atlas incrementally by C4 level:
76
+ Consult `references/architecture.md` for CLI flag details when needed (parameter reference, mutation series).
78
77
 
79
- 1. **System Context**:定義外部 actor、系統邊界、跨系統 edge
80
- 2. **Container 層級**:定義功能模塊(feature)及其之間的 edge
81
- 3. **Component 層級**:定義子模塊(submodule)及其內部元素(functionvariabledataflowerror
82
- 4. **Code 層級**(選擇性):對關鍵路徑補充函式層級細節
78
+ 1. **System Context**: Define external actors, system boundaries, and cross-system edges
79
+ 2. **Container level**: Define features and their inter-feature edges
80
+ 3. **Component level**: Define submodules with their internal elements (function, variable, dataflow, error)
81
+ 4. **Code level**: Declare `functions` and `variables` for every submodule, attaching `evidence` (source file and line number via `--evidence observed:path/file.ts:42`)
83
82
 
84
- 使用 `apltk architecture apply <proposal.yaml>` 進行批次 atlas 寫入(取代逐一手動 mutation)。
85
- 將前一步獲取的代碼庫知識透過 CLI 工具轉化為清晰的架構圖。
86
- 完成後驗證架構圖格式正確且可渲染。
83
+ Use `apltk architecture apply <proposal.yaml>` for batch atlas writes (replaces manual per-mutation CLI calls).
84
+ Transform the codebase knowledge gathered in the previous step into a clear architecture diagram.
85
+ After completion, verify the atlas format is valid and renders correctly.
87
86
 
88
- ## 證據追溯
87
+ ### 3. Self-review
89
88
 
90
- 每個透過 CLI 宣告的 component 應附上對應的來源證據:
89
+ Confirm the following before finishing:
91
90
 
92
- - 功能模塊(feature)→ 對應的目錄路徑或 entry point 檔案
93
- - 子模塊(submodule)→ 實作該模組的檔案列表
94
- - function 函式定義的檔案與行號
95
- - edge 觸發該呼叫關係的程式碼位置
91
+ - [ ] All four C4 levels are populated (Context → Container → Component → Code)
92
+ - [ ] Every submodule has at least one function declared with source evidence
93
+ - [ ] All cross-feature and intra-feature edges are defined
94
+ - [ ] Evidence level is correctly set (`observed` for source-confirmed, `inferred` otherwise)
95
+ - [ ] Atlas passes `apltk architecture validate`
96
96
 
97
- 若因時間或上下文限制無法完整追溯,在 `meta.summary` 中記錄已掃描範圍與已知遺漏。
97
+ ## Evidence Traceability
98
98
 
99
- ## 參考資料
99
+ Every component declared via the CLI must carry source evidence:
100
100
 
101
- - `references/architecture.md` apltk architecture 工具的完整參數說明。在步驟 2 產生架構圖前閱讀。
102
- - `references/TEMPLATE_SPEC.md`:atlas 欄位、列舉和 CLI 寫入形狀速查表。
103
- - `references/definition.md`: 功能模塊和子模塊的詳細定義。
104
- - `assets/architecture-page.template.html`: 模板 html。
105
- - `references/architecture.css`: 風格模板。
106
- - `sample-demo/`:完整示例輸出,用於理解基礎 atlas 的最終形態與 C4 層級對應。
101
+ - Feature corresponding directory path or entry point file
102
+ - Submodule list of files implementing the module
103
+ - Function row → source file and line number (`evidence.sourceFile` + `evidence.sourceLine`)
104
+ - Edge code location triggering the call relationship
105
+
106
+ If time or context constraints prevent full traceability, record the scanned scope and known gaps in `meta.summary`.
107
+
108
+ ## References
109
+
110
+ - `references/architecture.md` — Full parameter reference for the apltk architecture tool (consult when CLI flag details are needed).
111
+ - `references/TEMPLATE_SPEC.md` — Atlas field reference, enum values, and CLI shape cheat sheet.
112
+ - `references/definition.md` — Detailed definitions of feature and submodule.
113
+ - `assets/architecture-page.template.html` — HTML template.
114
+ - `references/architecture.css` — Style template.
115
+ - `sample-demo/` — Complete example output for understanding the final atlas shape and C4 level mapping.
@@ -759,7 +759,8 @@ p { line-height: 1.6; color: var(--vellum-soft); }
759
759
  /* =================================================================
760
760
  evidence badges
761
761
  ================================================================= */
762
- .evi { display: inline-block; padding: 0 6px; border-radius: 3px; font-size: 11px; font-weight: 600; line-height: 18px; cursor: help; }
762
+ .evi { display: inline-block; padding: 0 6px; border-radius: 3px; font-size: 11px; font-weight: 600; line-height: 18px; cursor: help; white-space: nowrap; }
763
+ .evi__source { font-size: 10px; font-weight: 400; margin-left: 4px; padding: 1px 5px; border-radius: 3px; background: rgba(0,0,0,0.15); color: inherit; }
763
764
  .evi--observed { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
764
765
  .evi--inferred { background: #fff3cd; color: #856404; border: 1px solid #ffeeba; }
765
766
  .evi--assumed { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
@@ -309,7 +309,17 @@ function renderEvidenceBadge(ev) {
309
309
  const label = EVI_LABEL[ev.level] || ev.level;
310
310
  const title = ev.source ? htmlEscape(ev.source) : '';
311
311
  const titleAttr = title ? ` title="${title}"` : '';
312
- return `<span class="evi evi--${ev.level}"${titleAttr}>${label}</span>`;
312
+
313
+ // Append source location when structured file:line data is available
314
+ let locHtml = '';
315
+ if (ev.sourceFile && ev.sourceFile !== ev.source && /[/\\]|\.[a-zA-Z0-9]+$/.test(ev.sourceFile)) {
316
+ const loc = ev.sourceLine
317
+ ? `${htmlEscape(ev.sourceFile)}:${ev.sourceLine}`
318
+ : htmlEscape(ev.sourceFile);
319
+ locHtml = ` <code class="evi__source">${loc}</code>`;
320
+ }
321
+
322
+ return `<span class="evi evi--${ev.level}"${titleAttr}>${label}${locHtml}</span>`;
313
323
  }
314
324
 
315
325
  function renderSubmoduleTable(headers, rows, evidences) {
@@ -69,17 +69,54 @@ function parseEvidence(value) {
69
69
  const str = String(value);
70
70
  const colon = str.indexOf(':');
71
71
  let level, source;
72
+
72
73
  if (colon === -1) {
73
- level = 'inferred';
74
- source = str;
74
+ if (EVIDENCE_LEVELS.includes(str)) {
75
+ level = str;
76
+ source = '';
77
+ } else {
78
+ level = 'inferred';
79
+ source = str;
80
+ }
75
81
  } else {
76
- level = str.slice(0, colon);
77
- source = str.slice(colon + 1);
78
- if (!EVIDENCE_LEVELS.includes(level)) {
79
- throw new Error(`Invalid evidence level: "${level}". Must be one of: ${EVIDENCE_LEVELS.join(', ')}`);
82
+ const candidateLevel = str.slice(0, colon);
83
+ if (EVIDENCE_LEVELS.includes(candidateLevel)) {
84
+ level = candidateLevel;
85
+ source = str.slice(colon + 1);
86
+ } else {
87
+ level = 'inferred';
88
+ source = str;
89
+ }
90
+ }
91
+
92
+ // Extract structured sourceFile & sourceLine from the source string.
93
+ // Heuristic: source ends with ":<number>" and the part before the last
94
+ // colon resembles a file path (contains '/' or '.'), so parse it as
95
+ // "path/to/file.ext:lineNumber".
96
+ let sourceFile = null;
97
+ let sourceLine = null;
98
+ if (source) {
99
+ const lastColon = source.lastIndexOf(':');
100
+ if (lastColon !== -1) {
101
+ const afterColon = source.slice(lastColon + 1).trim();
102
+ const lineNum = parseInt(afterColon, 10);
103
+ const filePart = source.slice(0, lastColon);
104
+ if (
105
+ !isNaN(lineNum) && lineNum > 0 &&
106
+ String(lineNum) === afterColon &&
107
+ (/[/\\]/.test(filePart) || /\.[a-zA-Z0-9]+$/.test(filePart))
108
+ ) {
109
+ sourceFile = filePart;
110
+ sourceLine = lineNum;
111
+ } else {
112
+ sourceFile = source;
113
+ }
114
+ } else {
115
+ sourceFile = source;
80
116
  }
81
117
  }
82
- return { level, source };
118
+
119
+ return { level, source, sourceFile, sourceLine };
83
120
  }
84
121
 
85
122
  function noFix(message) {
@@ -129,6 +129,26 @@ CLI: `apltk architecture error add --feature X --submodule Y --name ErrCode --wh
129
129
 
130
130
  CLI: `apltk architecture edge add --from <feature>[/sub] --to <feature>[/sub] --kind call --label "..."`
131
131
 
132
+ ### `evidence` (shared, optional on every entity above)
133
+
134
+ | Field | Type | Required | Notes |
135
+ | ---------- | ---- | -------- | ----- |
136
+ | level | enum `observed` `inferred` `assumed` | yes | Quality tier — drives badge colour (green/amber/red). |
137
+ | source | string | no | Free-text evidence description. |
138
+ | sourceFile | string | no | Extracted file path (e.g. `src/auth/controller.ts`). Auto-parsed from `--evidence observed:path/file.ts:42`. |
139
+ | sourceLine | number | no | Extracted line number. Auto-parsed from `--evidence observed:path/file.ts:42`. |
140
+
141
+ CLI: `--evidence observed:path/to/file.ts:42` (line number parsed automatically when source ends with `:N` and the preceding segment resembles a file path).
142
+
143
+ In YAML:
144
+ ```yaml
145
+ evidence:
146
+ level: observed
147
+ sourceFile: src/auth/controller.ts
148
+ sourceLine: 42
149
+ source: src/auth/controller.ts:42
150
+ ```
151
+
132
152
  ## Class hooks on rendered HTML
133
153
 
134
154
  These are emitted automatically by `lib/atlas/render.js`. Agents do **not** write them by hand — they are listed here only so reviewers know which selectors are stable.