@skill-map/cli 0.61.4 → 0.62.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 (43) hide show
  1. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/en/__PROVIDER__/agents/content-editor.md +1 -1
  2. package/dist/cli/tutorial/sm-tutorial/fixtures-data/sets/portfolio/es/__PROVIDER__/agents/content-editor.md +1 -1
  3. package/dist/cli/tutorial/sm-tutorial/references/_manifest.json +2 -7
  4. package/dist/cli/tutorial/sm-tutorial/references/_manifest.yml +1 -2
  5. package/dist/cli/tutorial/sm-tutorial/references/part-connect-harness.md +6 -0
  6. package/dist/cli/tutorial/sm-tutorial/references/part-daily-loop.md +40 -34
  7. package/dist/cli.js +1346 -479
  8. package/dist/index.js +368 -96
  9. package/dist/kernel/index.d.ts +232 -25
  10. package/dist/kernel/index.js +368 -96
  11. package/dist/migrations/001_initial.sql +18 -8
  12. package/dist/ui/{chunk-4N3NRZEH.js → chunk-276RLZR4.js} +1 -1
  13. package/dist/ui/{chunk-MGWGV4VD.js → chunk-34ZZDYNQ.js} +1 -1
  14. package/dist/ui/chunk-56CBK7LB.js +1 -0
  15. package/dist/ui/{chunk-OVVTCPBJ.js → chunk-7ANZW2OI.js} +1 -1
  16. package/dist/ui/chunk-BJ6X6WBO.js +4 -0
  17. package/dist/ui/{chunk-5SSKJ7AM.js → chunk-BOVJVOLH.js} +1 -1
  18. package/dist/ui/chunk-C42H2UHU.js +3 -0
  19. package/dist/ui/{chunk-GKQA75EF.js → chunk-CJURGJTN.js} +1 -1
  20. package/dist/ui/chunk-CM4YB7L4.js +2 -0
  21. package/dist/ui/{chunk-Q4PXVDJA.js → chunk-CZSLV6YD.js} +1 -1
  22. package/dist/ui/{chunk-7X3DZNG4.js → chunk-DLYJHLJX.js} +2 -2
  23. package/dist/ui/chunk-ECKRC6XD.js +1843 -0
  24. package/dist/ui/{chunk-JTCIY3SL.js → chunk-FC22ZJQZ.js} +1 -1
  25. package/dist/ui/{chunk-FRUHVCND.js → chunk-FYATUDAH.js} +1 -1
  26. package/dist/ui/chunk-IYC5ZW4L.js +2 -0
  27. package/dist/ui/{chunk-MBBJJEUX.js → chunk-JZ2YF7EL.js} +1 -1
  28. package/dist/ui/{chunk-HQ6M2HXK.js → chunk-LPDD2DHE.js} +1 -1
  29. package/dist/ui/{chunk-I52OQIZQ.js → chunk-NC3HOVDG.js} +1 -1
  30. package/dist/ui/{chunk-N6MUHKWR.js → chunk-UTRZTB6V.js} +1 -1
  31. package/dist/ui/chunk-VHEFRMK3.js +1 -0
  32. package/dist/ui/chunk-Y2Z26SRI.js +1 -0
  33. package/dist/ui/index.html +1 -1
  34. package/dist/ui/main-RW5YGD6H.js +4 -0
  35. package/migrations/001_initial.sql +18 -8
  36. package/package.json +2 -2
  37. package/dist/ui/chunk-4VAXWCM2.js +0 -3
  38. package/dist/ui/chunk-6WSKVPDL.js +0 -1843
  39. package/dist/ui/chunk-7VUEZZFJ.js +0 -1
  40. package/dist/ui/chunk-AKKFFP7Y.js +0 -1
  41. package/dist/ui/chunk-L34EUS75.js +0 -2
  42. package/dist/ui/chunk-ZYPXVXYF.js +0 -4
  43. package/dist/ui/main-36BUYCEI.js +0 -4
@@ -1341,19 +1341,33 @@ interface ScanResult {
1341
1341
  */
1342
1342
  tokenizer?: string;
1343
1343
  /**
1344
- * Effective recommended cap on the number of files the walker accepted
1345
- * during this scan (`scan.maxNodes` from settings, default 256). The UI
1346
- * raises the "oversized graph" banner when
1347
- * `stats.filesWalked >= recommendedNodeLimit`. Absent on synthetic fixtures
1348
- * that bypass the walker.
1349
- */
1350
- recommendedNodeLimit?: number;
1351
- /**
1352
- * Override applied via `--max-nodes <N>` on the verb that ran the scan, or
1353
- * `null` when no override was passed (the value above came from the
1354
- * setting). Bidirectional: can raise OR lower the recommended limit.
1355
- */
1356
- overrideMaxNodes?: number | null;
1344
+ * Effective walk ceiling for this scan (`--max-scan <N>` override on
1345
+ * `sm scan` / `sm watch` / `sm serve`, else `scan.maxScan` from
1346
+ * settings, default 50000). The scan walks, parses, analyzes, and
1347
+ * reference-validates the full corpus up to this number, so references
1348
+ * resolve across the whole project regardless of how many nodes the
1349
+ * map renders. Mirrors `scan_meta.scan_ceiling`. Absent on synthetic
1350
+ * fixtures that bypass the walker.
1351
+ */
1352
+ scanCeiling?: number;
1353
+ /**
1354
+ * True when the walker reached `scanCeiling` and dropped files in
1355
+ * stable provider-walker order, false otherwise. Drives the CLI "scan
1356
+ * truncated" notice and the UI persistent banner pointing at the
1357
+ * `.skillmapignore` editor. Mirrors `scan_meta.scan_truncated`. Absent
1358
+ * on synthetic fixtures that bypass the walker.
1359
+ */
1360
+ scanTruncated?: boolean;
1361
+ /**
1362
+ * Effective map render cap for this scan (`--max-nodes <N>` override,
1363
+ * else `scan.maxNodes` from settings, default 256). Does NOT bound the
1364
+ * scan (the full corpus up to `scanCeiling` is persisted and the
1365
+ * folders tree shows all of it); it only bounds the graph projection.
1366
+ * The UI projects the selected folder branch capped at this number and
1367
+ * raises an in-view banner when a branch exceeds it. Mirrors
1368
+ * `scan_meta.max_render_nodes`. Absent on synthetic fixtures.
1369
+ */
1370
+ maxRenderNodes?: number;
1357
1371
  /**
1358
1372
  * Files the walker skipped because their on-disk size exceeded
1359
1373
  * `scan.maxFileSizeBytes` (default 1 MiB). Each entry is the
@@ -1779,6 +1793,59 @@ interface INodeCounts {
1779
1793
  links: number;
1780
1794
  issues: number;
1781
1795
  }
1796
+ /**
1797
+ * Lightweight per-node projection for the BFF `/api/folders` endpoint.
1798
+ * Carries only the cheap scalar columns the SPA folders tree needs
1799
+ * (`path`, `kind`, the two link counts, total tokens, mtime), never the
1800
+ * full `Node` (no frontmatter, body, links, signals, contributions).
1801
+ * Pushed straight from `scan_nodes` so a 50K corpus does not hydrate the
1802
+ * whole `ScanResult` into memory.
1803
+ */
1804
+ interface ILiteNode {
1805
+ path: string;
1806
+ kind: string;
1807
+ linksInCount: number;
1808
+ linksOutCount: number;
1809
+ tokensTotal: number | null;
1810
+ modifiedAtMs: number | null;
1811
+ /**
1812
+ * The persisted `scan_nodes.sidecar_status`, null when there is no
1813
+ * parseable sidecar. Lets the folders rail flag staleness corpus-wide,
1814
+ * sibling of the issue counts.
1815
+ */
1816
+ sidecarStatus: string | null;
1817
+ }
1818
+ /**
1819
+ * Per-node issue incidence counts by severity, output of
1820
+ * `port.scans.issueCountsByPath()`. One entry per node that has at least
1821
+ * one error- or warn-severity issue whose `nodeIds` array includes the
1822
+ * path; nodes with no error / warn issues are absent from the map. The
1823
+ * `info` severity is intentionally ignored (the SPA badges only error /
1824
+ * warn). Counts are issue incidence (one per matching issue), the same
1825
+ * semantics the UI's `countIssuesByPath` rolls up per node.
1826
+ */
1827
+ interface IIssueIncidenceCount {
1828
+ error: number;
1829
+ warn: number;
1830
+ }
1831
+ /**
1832
+ * Output of `port.scans.loadBranch(...)`, the prefix-union + capped
1833
+ * graph projection the BFF `/api/branch` endpoint returns. `nodes` is
1834
+ * the first `LIMIT` nodes of the union (every requested prefix's
1835
+ * subtree) in stable path order (`ORDER BY path`); `links` carries only
1836
+ * edges whose source AND target are both in that node set; `issues`
1837
+ * carries only those whose `nodeIds` intersect it. `total` is the count
1838
+ * of nodes in the union BEFORE the cap (so the route can compute
1839
+ * `truncated`). `paths` echoes the (de-duped) requested prefixes; the
1840
+ * whole-corpus case (no prefix) echoes `[]`.
1841
+ */
1842
+ interface IBranchProjection {
1843
+ nodes: Node[];
1844
+ links: Link[];
1845
+ issues: Issue[];
1846
+ total: number;
1847
+ paths: string[];
1848
+ }
1782
1849
  /**
1783
1850
  * Lightweight option bag for `port.scans.persist`. Mirrors the optional
1784
1851
  * inputs of the `persistScanResult(db, result, inputs)` free function
@@ -2310,6 +2377,26 @@ interface IRawNode {
2310
2377
  * Empty / undefined on the happy path.
2311
2378
  */
2312
2379
  parseIssues?: readonly IParseIssue[];
2380
+ /**
2381
+ * Incremental-walk fast path. `true` when the walker matched this
2382
+ * file's on-disk `mtime` against the prior scan snapshot (via
2383
+ * `IProviderWalkOptions.priorMtimes`) and SKIPPED reading + parsing the
2384
+ * body, the dominant per-file cost. For such a record `body` /
2385
+ * `frontmatter` / `frontmatterRaw` are empty placeholders: the
2386
+ * orchestrator reuses the prior node verbatim and reads the body
2387
+ * (through `reread`) ONLY when a sidecar change forces re-extraction.
2388
+ * Absent / `false` means a normal record whose body was read eagerly.
2389
+ */
2390
+ unchanged?: boolean;
2391
+ /**
2392
+ * Present only on an `unchanged` record: a lazy reader that performs
2393
+ * the deferred `readFile` + parse and returns the body / frontmatter
2394
+ * the walker skipped. The orchestrator calls it only when it must
2395
+ * actually re-extract (a sidecar edit on an otherwise-unchanged file).
2396
+ * Keeps the read + parse logic in the walker (single source) rather
2397
+ * than duplicating it in the orchestrator.
2398
+ */
2399
+ reread?: () => Promise<Pick<IRawNode, 'body' | 'frontmatterRaw' | 'frontmatter' | 'parseIssues'>>;
2313
2400
  }
2314
2401
  /**
2315
2402
  * Runtime descriptor of one Provider kind, populated by the loader from
@@ -2809,6 +2896,30 @@ interface IProviderWalkOptions {
2809
2896
  path: string;
2810
2897
  bytes: number;
2811
2898
  }) => void;
2899
+ /**
2900
+ * Incremental-walk hint: prior-scan file mtimes keyed by root-relative
2901
+ * path (the same form as `IRawNode.path`). When supplied, the kernel
2902
+ * walker compares each file's on-disk `mtime` against this map and, on
2903
+ * a match, yields a lightweight `unchanged` record WITHOUT reading or
2904
+ * parsing the body (the dominant cost on a re-scan). The orchestrator
2905
+ * builds this from the prior snapshot only when cache reuse is on and
2906
+ * the tokenizer is unchanged; absent means "read every file" (the
2907
+ * full-scan default). A Provider shipping its own `walk()` MAY honour
2908
+ * it for the same speedup but is not required to.
2909
+ */
2910
+ priorMtimes?: ReadonlyMap<string, number>;
2911
+ /**
2912
+ * Scoped-walk hint for the watcher's incremental path: an explicit
2913
+ * list of ABSOLUTE file paths to read instead of traversing the
2914
+ * roots. When supplied, the kernel walker skips traversal entirely and
2915
+ * reads ONLY these paths (those matching the provider's `extensions`,
2916
+ * existing on disk, passing the size guard), yielding a normal
2917
+ * `IRawNode` per match. Built by the orchestrator from chokidar's
2918
+ * changed-path list; absent means "traverse the roots" (the full-scan
2919
+ * default). A Provider shipping its own `walk()` MAY honour it for the
2920
+ * same speedup but is not required to.
2921
+ */
2922
+ scopedPaths?: readonly string[];
2812
2923
  }
2813
2924
  /**
2814
2925
  * Declarative read config a Provider declares via `IProvider.read`.
@@ -4081,21 +4192,35 @@ interface RunScanOptions {
4081
4192
  */
4082
4193
  activeProvider?: string | null;
4083
4194
  /**
4084
- * Recommended cap on the number of nodes the walker classifies
4085
- * (mirror of `scan.maxNodes` in settings, default 256). Threaded
4086
- * through to `walkAndExtract` so the cap can fire and so
4087
- * `ScanResult.recommendedNodeLimit` is populated. Absent → the
4088
- * orchestrator falls back to 256 (the design default), keeping
4089
- * out-of-band callers and synthetic fixtures safe.
4195
+ * Walk-intake ceiling (mirror of `scan.maxScan` in settings, default
4196
+ * 50000). Threaded through to `walkAndExtract` so the ceiling can fire
4197
+ * (dropping extra files in stable order) and so `ScanResult.scanCeiling`
4198
+ * / `ScanResult.scanTruncated` are populated. Absent → the orchestrator
4199
+ * falls back to 50000 (the design default), keeping out-of-band callers
4200
+ * and synthetic fixtures safe.
4090
4201
  */
4091
- recommendedNodeLimit?: number;
4202
+ scanCeiling?: number;
4092
4203
  /**
4093
- * Per-invocation override of the recommended cap (when `--max-nodes
4094
- * <N>` was passed). `null` (or absent) means no override; the
4095
- * recommended limit applies. Bidirectional: any positive integer
4096
- * replaces the recommended limit for the duration of this scan.
4204
+ * Per-invocation override of the walk ceiling (when `--max-scan <N>`
4205
+ * was passed). `null` (or absent) means no override; the configured
4206
+ * ceiling applies. Bidirectional: any positive integer replaces the
4207
+ * ceiling for the duration of this scan.
4097
4208
  */
4098
- overrideMaxNodes?: number | null;
4209
+ overrideScanCeiling?: number | null;
4210
+ /**
4211
+ * Map render cap (mirror of `scan.maxNodes` in settings, default 256).
4212
+ * Pure metadata: it does NOT bound the walk. Threaded through to
4213
+ * `walkAndExtract` only so `ScanResult.maxRenderNodes` is populated and
4214
+ * the UI knows how many nodes to project onto the canvas. Absent → the
4215
+ * orchestrator falls back to 256.
4216
+ */
4217
+ maxRenderNodes?: number;
4218
+ /**
4219
+ * Per-invocation override of the render cap (when `--max-nodes <N>`
4220
+ * was passed). `null` (or absent) means no override; the configured
4221
+ * render cap applies. Bidirectional. Never bounds the walk.
4222
+ */
4223
+ overrideMaxRenderNodes?: number | null;
4099
4224
  /**
4100
4225
  * Mirror of `scan.maxFileSizeBytes` (default 1 MiB). Threaded into
4101
4226
  * `walkAndExtract` so the walker skips any file larger than this
@@ -4104,6 +4229,38 @@ interface RunScanOptions {
4104
4229
  * size limit (out-of-band callers and synthetic fixtures stay safe).
4105
4230
  */
4106
4231
  maxFileSizeBytes?: number;
4232
+ /**
4233
+ * Watcher-only incremental fast path (pure perf, identical output to a
4234
+ * full scan). When supplied AND a prior snapshot exists AND
4235
+ * `enableCache` is on AND the tokenizer is unchanged, the orchestrator
4236
+ * does NOT traverse the directory tree. Instead it:
4237
+ *
4238
+ * - re-reads + re-extracts ONLY the files in `changed` (scoped read,
4239
+ * no `readdir`), and
4240
+ * - injects every other prior node as an `unchanged` record through
4241
+ * the SAME cache machinery the mtime-gate uses (`applyFullCacheHit`),
4242
+ * reusing its node + links + extractor runs verbatim, and
4243
+ * - drops the files in `removed` (the rename / orphan heuristic over
4244
+ * prior-vs-merged handles the disappearance + any rename).
4245
+ *
4246
+ * Paths are root-relative POSIX (the same form as `node.path`). A
4247
+ * sidecar (`.sm`) path in either set is mapped to its `.md` node so a
4248
+ * sidecar edit re-processes the node. The downstream analysis phases
4249
+ * (resolver, post-walk transforms, analyzers, broken-ref,
4250
+ * name-collision) always run over the fully-merged graph, so global
4251
+ * validation stays correct (a changed file's new link to an unchanged
4252
+ * file resolves; an unchanged file's link to a removed file breaks).
4253
+ *
4254
+ * `filesWalked` reflects only the scoped reads (far fewer than the
4255
+ * corpus), `scanTruncated` stays `false` (the ceiling never fires on a
4256
+ * scoped read). Absent (boot, `sm scan`, `sm scan --changed`,
4257
+ * meta-file change) falls back to the full-traversal + mtime-gate path,
4258
+ * byte-identical to today.
4259
+ */
4260
+ incrementalChangedPaths?: {
4261
+ changed: ReadonlySet<string>;
4262
+ removed: ReadonlySet<string>;
4263
+ };
4107
4264
  }
4108
4265
  /**
4109
4266
  * Same as `runScan` but also returns the rename heuristic's `RenameOp[]`
@@ -4608,6 +4765,15 @@ interface StoragePort {
4608
4765
  * `node.kind` is open string per `node.schema.json`).
4609
4766
  */
4610
4767
  load(): Promise<ScanResult>;
4768
+ /**
4769
+ * Metadata-only `ScanResult`: every scalar field plus real
4770
+ * `COUNT(*)` stats, but empty `nodes` / `links` / `issues` arrays.
4771
+ * Reads only the single `scan_meta` row plus the counts, never the
4772
+ * node / link / issue tables, so the BFF `GET /api/scan?meta=1` boot
4773
+ * fetch stays cheap on a large corpus. The SPA pairs it with
4774
+ * `/api/folders` (tree) and `/api/branch` (map).
4775
+ */
4776
+ loadMeta(): Promise<ScanResult>;
4611
4777
  /**
4612
4778
  * Spec § A.9, fine-grained extractor-runs cache breadcrumbs.
4613
4779
  * Returns `Map<nodePath, Map<qualifiedExtractorId, IPriorExtractorRun>>`.
@@ -4629,6 +4795,47 @@ interface StoragePort {
4629
4795
  * is not in the persisted scan.
4630
4796
  */
4631
4797
  findNode(path: string): Promise<INodeBundle | null>;
4798
+ /**
4799
+ * Lightweight full-corpus node list `{ path, kind }[]`, ordered by
4800
+ * `path` ASC. Backs the BFF `/api/folders` endpoint: the SPA folders
4801
+ * tree renders the whole scanned corpus (up to `scan.maxScan`)
4802
+ * without hydrating the full `ScanResult`. Pushes the projection to
4803
+ * SQL (`SELECT path, kind`), never loads the rest of the row.
4804
+ */
4805
+ listLiteNodes(): Promise<ILiteNode[]>;
4806
+ /**
4807
+ * Per-node issue incidence counts by severity, keyed by node path.
4808
+ * Expands every `scan_issues.node_ids_json` array with SQLite
4809
+ * `json_each` and groups by `(value, severity)` so the count is
4810
+ * computed in SQL, not by loading every issue into memory. Only
4811
+ * error / warn severities are tallied (the SPA badges ignore
4812
+ * `info`); nodes with no error / warn issue are absent from the
4813
+ * map. Backs the `errorCount` / `warnCount` fields on `/api/folders`.
4814
+ */
4815
+ issueCountsByPath(): Promise<Map<string, IIssueIncidenceCount>>;
4816
+ /**
4817
+ * Effective map-render cap recorded by the latest scan
4818
+ * (`scan_meta.max_render_nodes`). Returns the design default (256)
4819
+ * when no `scan_meta` row exists (DB freshly migrated / never
4820
+ * scanned). Backs the `/api/branch` cap default + clamp ceiling.
4821
+ */
4822
+ effectiveMaxRenderNodes(): Promise<number>;
4823
+ /**
4824
+ * Prefix-union, capped graph projection for the BFF `/api/branch`
4825
+ * endpoint. A node is in the branch when, for ANY prefix in
4826
+ * `prefixes`, its `path === prefix` or starts with `prefix + '/'`;
4827
+ * the per-prefix subtrees are UNIONed. An empty `prefixes` array
4828
+ * selects the whole corpus. Identical prefixes are de-duped
4829
+ * defensively. `nodes` is the first `limit` matching nodes of the
4830
+ * union in stable path order (`ORDER BY path LIMIT`); `links`
4831
+ * carries only edges whose source AND target are both in `nodes`;
4832
+ * `issues` carries only those whose `nodeIds` intersect `nodes`.
4833
+ * `total` is the count of union nodes BEFORE the cap (so the route
4834
+ * can compute `truncated`); `paths` echoes the de-duped prefixes.
4835
+ * All scoping + capping happens in SQL so a 50K corpus never
4836
+ * hydrates into memory.
4837
+ */
4838
+ loadBranch(prefixes: string[], limit: number): Promise<IBranchProjection>;
4632
4839
  };
4633
4840
  /**
4634
4841
  * Phase 3 / View contribution system, read access to