@cesarandreslopez/occ 0.9.2 → 0.10.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 (69) hide show
  1. package/README.md +62 -47
  2. package/dist/src/code/check.d.ts +39 -0
  3. package/dist/src/code/check.js +242 -0
  4. package/dist/src/code/check.js.map +1 -0
  5. package/dist/src/code/command.js +64 -0
  6. package/dist/src/code/command.js.map +1 -1
  7. package/dist/src/code/isolated-chunk-runner.d.ts +5 -0
  8. package/dist/src/code/isolated-chunk-runner.js +96 -0
  9. package/dist/src/code/isolated-chunk-runner.js.map +1 -0
  10. package/dist/src/code/isolated.d.ts +30 -2
  11. package/dist/src/code/isolated.js +110 -2
  12. package/dist/src/code/isolated.js.map +1 -1
  13. package/dist/src/code/languages.d.ts +13 -0
  14. package/dist/src/code/languages.js +115 -2
  15. package/dist/src/code/languages.js.map +1 -1
  16. package/dist/src/code/map.d.ts +9 -0
  17. package/dist/src/code/map.js +6 -0
  18. package/dist/src/code/map.js.map +1 -1
  19. package/dist/src/code/parsers.js +262 -0
  20. package/dist/src/code/parsers.js.map +1 -1
  21. package/dist/src/code/session.d.ts +2 -0
  22. package/dist/src/code/session.js +4 -0
  23. package/dist/src/code/session.js.map +1 -1
  24. package/dist/src/code/source.d.ts +44 -0
  25. package/dist/src/code/source.js +102 -0
  26. package/dist/src/code/source.js.map +1 -0
  27. package/dist/src/code/store.d.ts +10 -0
  28. package/dist/src/code/store.js +11 -0
  29. package/dist/src/code/store.js.map +1 -1
  30. package/dist/src/code/tree-sitter.d.ts +20 -0
  31. package/dist/src/code/tree-sitter.js +127 -0
  32. package/dist/src/code/tree-sitter.js.map +1 -0
  33. package/dist/src/index.d.ts +11 -4
  34. package/dist/src/index.js +7 -2
  35. package/dist/src/index.js.map +1 -1
  36. package/dist/src/progress-event.d.ts +8 -0
  37. package/dist/src/workspace/bundle-io.d.ts +9 -1
  38. package/dist/src/workspace/bundle-io.js +59 -18
  39. package/dist/src/workspace/bundle-io.js.map +1 -1
  40. package/dist/src/workspace/bundle.js +55 -1
  41. package/dist/src/workspace/bundle.js.map +1 -1
  42. package/package.json +18 -2
  43. package/skills/occ/SKILL.md +16 -5
  44. package/skills/occ/reference/code.md +35 -2
  45. package/skills/occ/reference/commands.md +3 -1
  46. package/vendor/tree-sitter/c/grammar.wasm +0 -0
  47. package/vendor/tree-sitter/c/tags.scm +4 -0
  48. package/vendor/tree-sitter/c-sharp/grammar.wasm +0 -0
  49. package/vendor/tree-sitter/c-sharp/tags.scm +6 -0
  50. package/vendor/tree-sitter/cpp/grammar.wasm +0 -0
  51. package/vendor/tree-sitter/cpp/tags.scm +5 -0
  52. package/vendor/tree-sitter/go/grammar.wasm +0 -0
  53. package/vendor/tree-sitter/go/tags.scm +4 -0
  54. package/vendor/tree-sitter/java/grammar.wasm +0 -0
  55. package/vendor/tree-sitter/java/tags.scm +7 -0
  56. package/vendor/tree-sitter/kotlin/grammar.wasm +0 -0
  57. package/vendor/tree-sitter/kotlin/tags.scm +4 -0
  58. package/vendor/tree-sitter/php/grammar.wasm +0 -0
  59. package/vendor/tree-sitter/php/tags.scm +8 -0
  60. package/vendor/tree-sitter/python/grammar.wasm +0 -0
  61. package/vendor/tree-sitter/python/tags.scm +3 -0
  62. package/vendor/tree-sitter/ruby/grammar.wasm +0 -0
  63. package/vendor/tree-sitter/ruby/tags.scm +5 -0
  64. package/vendor/tree-sitter/rust/grammar.wasm +0 -0
  65. package/vendor/tree-sitter/rust/tags.scm +5 -0
  66. package/vendor/tree-sitter/scala/grammar.wasm +0 -0
  67. package/vendor/tree-sitter/scala/tags.scm +5 -0
  68. package/vendor/tree-sitter/swift/grammar.wasm +0 -0
  69. package/vendor/tree-sitter/swift/tags.scm +4 -0
package/README.md CHANGED
@@ -25,7 +25,7 @@ OCC started as a way to make office documents visible in the same workflows that
25
25
  - analyze workspaces for combined code, document, and structure metrics (`occ workspace analyze`) and cross-document references (`occ workspace documents`)
26
26
  - quickly describe whether a directory is a code, office, documentation, data, or mixed project (`occ describe`, `occ workspace describe`)
27
27
  - summarize code metrics through `scc`
28
- - explore JavaScript, TypeScript, Vue Single-File Components, and Python repositories with symbol search, call analysis, dependency inspection, and inheritance queries (`occ code`)
28
+ - explore JavaScript, TypeScript, Vue Single-File Components, Python, Go, Rust, Java, C/C++, Kotlin, and other tree-sitter-backed repositories with symbol search, exact `nodeId` source reads, syntax checks, call analysis, dependency inspection, and inheritance queries (`occ code`)
29
29
  - track git changes, diffs, commit history, and churn — and map a diff to the code symbols it touches (`occ git`, `occ code diff`, `occ code history`)
30
30
 
31
31
  ## Agent Skills
@@ -69,7 +69,7 @@ After install the skill lands at `<skills-dir>/occ/SKILL.md` and your agent surf
69
69
  - **Presentation inspection via `occ slide inspect`** — metadata, risk flags, per-slide inventory, and content preview for PPTX and ODP
70
70
  - **Table extraction via `occ table inspect`** — structured table content from DOCX, XLSX, PPTX, ODT, and ODP with auto-detected headers, sample row limits, and merged cell support
71
71
  - **Code metrics via scc** — auto-detects code files and integrates scc output
72
- - **Code exploration via `occ code`** — JS/TS, Vue SFC, and Python-first symbol lookup, content search, callers/callees, dependency categories, inheritance, module coupling, and ambiguity-aware chains
72
+ - **Code exploration via `occ code`** — JS/TS, Vue SFC, Python, Go, Rust, Java, C/C++, Kotlin, and other tree-sitter-backed symbol lookup, exact `nodeId` source reads with `occ code source`, syntax checks with `occ code check`, content search, callers/callees, dependency categories, inheritance, module coupling, and ambiguity-aware chains
73
73
  - **Repository map / pack via `occ code map` / `occ code pack`** — token-budgeted, PageRank-ranked repo map (top symbol signatures per file) or content pack, with a pluggable heuristic or BPE (`o200k_base` / `cl100k_base`) tokenizer for exact budgets, and opt-in query/path focus (`--query`, `--focus-path`, `--focus-depth`) to bias the map toward a specific task
74
74
  - **Workspace analysis via `occ workspace`** — fast directory description plus combined code, document, and structure analysis with versioned JSON contracts, per-document summaries, and cross-reference detection
75
75
  - **Git context via `occ git`** — changed files, diff summaries and token-bounded patches, commit/file history, and churn hotspots over a typed, shell-injection-safe git client; plus git-aware code (`occ code diff`, `occ code history`, `occ code map --changed-from`) that maps a diff to the code symbols it touches
@@ -148,6 +148,11 @@ occ code analyze callers createUser --path .
148
148
  occ code analyze deps src/deps --path .
149
149
  occ code analyze chain ambiguousCaller duplicate --path .
150
150
 
151
+ # Read one symbol body by nodeId, then check changed-file syntax
152
+ occ code map --path . --map-format json --map-tokens 4096
153
+ occ code source --path . --node-id "function:/abs/repo/src/main.ts:bootstrap:3" --format json
154
+ occ code check src/main.ts --path . --format json
155
+
151
156
  # Module coupling metrics
152
157
  occ code analyze coupling src/code --path .
153
158
 
@@ -267,7 +272,7 @@ Scanned 23 documents (56,750 words, 201 pages) in 120ms
267
272
 
268
273
  `occ code` adds on-demand code exploration without changing the existing document-scan workflow. It builds an in-memory repository graph for each command and does not require a database, daemon, or background indexer.
269
274
 
270
- The first-class support path is **JavaScript, TypeScript, Vue Single-File Components, and Python**. Other languages may be discovered and partially parsed, but the current resolver, fixtures, and output contracts are intentionally optimized around JS/TS, Vue SFC, and Python behavior.
275
+ The type-accurate path is **JavaScript, TypeScript, and Vue Single-File Components** (parsed with the TypeScript compiler). **Python, Go, Rust, Java, C/C++, C#, Kotlin, Scala, Swift, Ruby, and PHP** are parsed with vendored tree-sitter grammars for real symbols, calls, and inheritance. Any extension without a vendored grammar still parses name-only without failing.
271
276
 
272
277
  ```bash
273
278
  # Exact symbol lookup
@@ -279,6 +284,14 @@ occ code find pattern service --path .
279
284
  # Full-text content search
280
285
  occ code find content normalize_name --path .
281
286
 
287
+ # Read exactly one symbol body by nodeId (from code map/find/index JSON) or by name
288
+ occ code map --path . --map-format json --map-tokens 4096
289
+ occ code source --path . --node-id "function:/abs/repo/src/main.ts:bootstrap:3" --format json
290
+ occ code source bootstrap --path . --file src/main.ts --type function
291
+
292
+ # Check syntax without a language server (exits nonzero only when invalid)
293
+ occ code check src/main.ts --path .
294
+
282
295
  # Outgoing and incoming call analysis
283
296
  occ code analyze calls bootstrap --path test/fixtures/code-explore
284
297
  occ code analyze callers createUser --path test/fixtures/code-explore
@@ -314,6 +327,8 @@ Highlights of the current code exploration behavior:
314
327
  - **Focused maps** via `--query`, `--focus-path` (repeatable), and `--focus-depth` — opt-in, additive relevance scoring (query + path + graph neighbors) blended with the global rank so task-relevant files surface within the budget; omitting the flags keeps the global PageRank overview unchanged
315
328
  - **Change-aware exploration** via `occ code diff` (symbols touched by a git diff range, with removed-symbol recovery), `occ code history <symbol>` (git line history for a symbol), and `occ code map --changed-from <ref>` (diff-focused repo maps)
316
329
  - **Exact, pattern, type, and content search** over the repository graph
330
+ - **Single-symbol source reads** via `occ code source` — read one symbol body by stable `nodeId` (from `occ code map` / `find` / `index` JSON) or a disambiguated name, reading file content on demand and refusing paths that escape the repo root
331
+ - **Language-server-free syntax checks** via `occ code check` — TS/JS, Vue SFC, Python, and the vendored tree-sitter languages; exits nonzero only when `valid` is false
317
332
  - **Call analysis** with explicit `resolved`, `ambiguous`, and `unresolved` states
318
333
  - **Receiver-aware method resolution** for `this`, `super`, `self`, and `cls`
319
334
  - **Dependency analysis** grouped into local, external, and unresolved imports
@@ -473,50 +488,50 @@ const callGraph = session.toMermaid('call-graph', 'bootstrap');
473
488
 
474
489
  Available subpath exports:
475
490
 
476
- | Import path | Description |
477
- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
478
- | `@cesarandreslopez/occ/code/build` | `buildCodebaseIndex` — graph construction (with optional `maxFiles`/`maxBytes`/`onBudgetExceeded` budget controls and `CodeIndexBudgetExceededError`); also `parseCodeFiles` (bounded-concurrency parse pool) + `assembleCodebaseIndex` for staged indexing |
479
- | `@cesarandreslopez/occ/code/types` | TypeScript types (`CodebaseIndex`, `CodeNode`, `CodeEdge`, `IndexTruncation`, etc.) |
480
- | `@cesarandreslopez/occ/code/query` | Query functions (`findByName`, `analyzeCalls`, `analyzeDeps`, etc.) |
481
- | `@cesarandreslopez/occ/code/discover` | `discoverCodeFiles` — file discovery |
482
- | `@cesarandreslopez/occ/code/preview` | `previewCodebaseSize` — cheap size + per-language estimate without parsing, with budget pressure reporting |
483
- | `@cesarandreslopez/occ/code/isolated` | `buildCodebaseIndexIsolated` — runs `buildCodebaseIndex` in a forked subprocess, optionally returning a slim index |
484
- | `@cesarandreslopez/occ/code/incremental` | `computeManifestDiff`, `classifyChangedFiles`, `findResolutionImpactedFiles`, `spliceIndexInputs` — primitives behind incremental `update()`/`ensureFresh()` (manifest diff + resolution-impact + graph splice) |
485
- | `@cesarandreslopez/occ/code/slim` | `slimifyIndex`, `parseSlimIndex`, `validateSlimIndex` — content-free `CodebaseIndexSlim` for graph-only consumers |
486
- | `@cesarandreslopez/occ/code/chunk` | `chunkCodebase`, `chunkFromIndex`, `chunkFromIndexCooperative`, `chunkFromIndexStreaming` — semantic code chunking (word- or token-based via `maxTokens`/`countTokens`; the cooperative variant yields the event loop, the streaming variant reads each file on demand from a slim/`contentMode: 'none'` index) |
487
- | `@cesarandreslopez/occ/code/session` | `createCodeQuerySession` — stateful code query session (sync `chunk()` plus async `chunkCooperative()` that yields the event loop while chunking) |
488
- | `@cesarandreslopez/occ/code/store` | `openCodeIndexStore`, `openChunkCodeIndexStore` — persistent index store with cache strategies (the chunk variant pins `contentMode: 'full'`; `update(changedFiles?)` does a true incremental refresh — manifest diff + targeted re-parse + graph splice) |
489
- | `@cesarandreslopez/occ/code/cache` | Index caching utilities (legacy — prefer `./code/store`) |
490
- | `@cesarandreslopez/occ/code/diff` | `computeCodeDiff`, `joinHunksToSymbols` — map a git diff range to changed code symbols (added/removed/modified, new/removed exports, removed-symbol recovery) |
491
- | `@cesarandreslopez/occ/git` | Git context API — `getGitChangedFiles`, `getGitDiffSummary`, `getGitDiffPatch`, `getGitDiffHunks`, `getGitDiffFunctionContext`, `getGitCommitHistory`, `getGitFileHistory`, `getGitChurnSummary`, `resolveGitRange` (also `./git/types`, `./git/api`, `./git/client`, `./git/parse`) |
492
- | `@cesarandreslopez/occ/doc/inspect` | `inspectDocument` — document metadata and content extraction |
493
- | `@cesarandreslopez/occ/doc/types` | Document inspection types |
494
- | `@cesarandreslopez/occ/doc/discover` | Document file discovery (now includes prose formats by default; opt into data files via `includeDataFiles`; new `discoverDocumentSet` returns `{ documents, skipped }`) |
495
- | `@cesarandreslopez/occ/doc/batch` | Batch document inspection |
496
- | `@cesarandreslopez/occ/doc/chunk` | `chunkDocument` / `chunkDocumentFromMarkdown` — heading-aware, token-budgeted document chunker for DOCX/PDF/PPTX/XLSX/ODT/ODS/ODP/MD/MDX/TXT/RST/AsciiDoc (the `FromMarkdown` variant chunks pre-converted markdown without re-parsing) |
497
- | `@cesarandreslopez/occ/doc/entities` | Entity and keyword extraction |
498
- | `@cesarandreslopez/occ/doc/references` | Cross-reference detection |
499
- | `@cesarandreslopez/occ/workspace/analyze` | `analyzeWorkspace` — workspace-level analysis |
500
- | `@cesarandreslopez/occ/workspace/describe` | `describeWorkspace` — fast directory classification |
501
- | `@cesarandreslopez/occ/workspace/documents` | `inspectWorkspaceDocumentSet` — document summaries and cross-references |
502
- | `@cesarandreslopez/occ/workspace/types` | Workspace analysis types |
503
- | `@cesarandreslopez/occ/workspace/prepare` | `prepareWorkspaceContext` — combined code indexing + document inspection |
504
- | `@cesarandreslopez/occ/workspace/prepare-types` | Workspace preparation types (`WorkspacePrepareOptions`, `WorkspacePreparedContext`, etc.) |
505
- | `@cesarandreslopez/occ/workspace/bundle` | `bundleWorkspace` — single-call versioned `WorkspaceBundle` (description + analysis + documents + slim code index + outline + cross-refs + optional document / code / unified corpus chunks via `WorkspaceCorpusChunk`) |
506
- | `@cesarandreslopez/occ/markdown/convert` | `documentToMarkdown` — document-to-markdown conversion (now reads MD/MDX/TXT/RST/AsciiDoc/YAML/JSON/TOML directly) |
507
- | `@cesarandreslopez/occ/structure/extract` | `extractFromMarkdown` — heading tree extraction |
508
- | `@cesarandreslopez/occ/structure/types` | Structure types and helpers |
509
- | `@cesarandreslopez/occ/sheet/inspect` | `inspectWorkbook` — XLSX workbook inspection |
510
- | `@cesarandreslopez/occ/sheet/types` | Sheet inspection types |
511
- | `@cesarandreslopez/occ/slide/inspect` | `inspectPresentation` — presentation inspection |
512
- | `@cesarandreslopez/occ/table/inspect` | Table extraction from documents |
513
- | `@cesarandreslopez/occ/table/types` | Table extraction types |
514
- | `@cesarandreslopez/occ/health` | `health()` — liveness probe with version + capability flags |
515
- | `@cesarandreslopez/occ/errors` | `OccAbortError`, `OCC_ABORTED`, `isOccAbortError` — typed abort error that survives `instanceof` across forked subprocesses |
516
- | `@cesarandreslopez/occ/types` | Shared types (`ConfidenceLevel`, `ParseResult`, `ParserOutput`, etc.) |
517
- | `@cesarandreslopez/occ/tokens` | Token estimation utilities |
518
- | `@cesarandreslopez/occ/progress-event` | Progress event types (`ProgressPhase`, `ProgressEvent` with optional `scope`/`currentPath`/`bytesProcessed`/`totalBytes`/`startedAt`/`elapsedMs`) |
519
- | `@cesarandreslopez/occ/stats` | Stats types (`StatsRow`, `AggregateResult`) and `aggregate()` |
491
+ | Import path | Description |
492
+ | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
493
+ | `@cesarandreslopez/occ/code/build` | `buildCodebaseIndex` — graph construction (with optional `maxFiles`/`maxBytes`/`onBudgetExceeded` budget controls and `CodeIndexBudgetExceededError`); also `parseCodeFiles` (bounded-concurrency parse pool) + `assembleCodebaseIndex` for staged indexing |
494
+ | `@cesarandreslopez/occ/code/types` | TypeScript types (`CodebaseIndex`, `CodeNode`, `CodeEdge`, `IndexTruncation`, etc.) |
495
+ | `@cesarandreslopez/occ/code/query` | Query functions (`findByName`, `analyzeCalls`, `analyzeDeps`, etc.) |
496
+ | `@cesarandreslopez/occ/code/discover` | `discoverCodeFiles` — file discovery |
497
+ | `@cesarandreslopez/occ/code/preview` | `previewCodebaseSize` — cheap size + per-language estimate without parsing, with budget pressure reporting |
498
+ | `@cesarandreslopez/occ/code/isolated` | `buildCodebaseIndexIsolated` — runs `buildCodebaseIndex` in a forked subprocess, optionally returning a slim index; also `chunkFromStoreIsolated` — runs store-backed code chunking in a forked subprocess (options `ChunkFromStoreIsolatedOptions`, `SerializableCodeChunkOptions`) |
499
+ | `@cesarandreslopez/occ/code/incremental` | `computeManifestDiff`, `classifyChangedFiles`, `findResolutionImpactedFiles`, `spliceIndexInputs` — primitives behind incremental `update()`/`ensureFresh()` (manifest diff + resolution-impact + graph splice) |
500
+ | `@cesarandreslopez/occ/code/slim` | `slimifyIndex`, `parseSlimIndex`, `validateSlimIndex` — content-free `CodebaseIndexSlim` for graph-only consumers |
501
+ | `@cesarandreslopez/occ/code/chunk` | `chunkCodebase`, `chunkFromIndex`, `chunkFromIndexCooperative`, `chunkFromIndexStreaming` — semantic code chunking (word- or token-based via `maxTokens`/`countTokens`; the cooperative variant yields the event loop, the streaming variant reads each file on demand from a slim/`contentMode: 'none'` index) |
502
+ | `@cesarandreslopez/occ/code/session` | `createCodeQuerySession` — stateful code query session (sync `chunk()` plus async `chunkCooperative()` that yields the event loop while chunking) |
503
+ | `@cesarandreslopez/occ/code/store` | `openCodeIndexStore`, `openChunkCodeIndexStore` — persistent index store with cache strategies (the chunk variant pins `contentMode: 'full'`; `update(changedFiles?)` does a true incremental refresh — manifest diff + targeted re-parse + graph splice; `chunkIsolated(options?)` chunks the cached index in a forked subprocess) |
504
+ | `@cesarandreslopez/occ/code/cache` | Index caching utilities (legacy — prefer `./code/store`) |
505
+ | `@cesarandreslopez/occ/code/diff` | `computeCodeDiff`, `joinHunksToSymbols` — map a git diff range to changed code symbols (added/removed/modified, new/removed exports, removed-symbol recovery) |
506
+ | `@cesarandreslopez/occ/git` | Git context API — `getGitChangedFiles`, `getGitDiffSummary`, `getGitDiffPatch`, `getGitDiffHunks`, `getGitDiffFunctionContext`, `getGitCommitHistory`, `getGitFileHistory`, `getGitChurnSummary`, `resolveGitRange` (also `./git/types`, `./git/api`, `./git/client`, `./git/parse`) |
507
+ | `@cesarandreslopez/occ/doc/inspect` | `inspectDocument` — document metadata and content extraction |
508
+ | `@cesarandreslopez/occ/doc/types` | Document inspection types |
509
+ | `@cesarandreslopez/occ/doc/discover` | Document file discovery (now includes prose formats by default; opt into data files via `includeDataFiles`; new `discoverDocumentSet` returns `{ documents, skipped }`) |
510
+ | `@cesarandreslopez/occ/doc/batch` | Batch document inspection |
511
+ | `@cesarandreslopez/occ/doc/chunk` | `chunkDocument` / `chunkDocumentFromMarkdown` — heading-aware, token-budgeted document chunker for DOCX/PDF/PPTX/XLSX/ODT/ODS/ODP/MD/MDX/TXT/RST/AsciiDoc (the `FromMarkdown` variant chunks pre-converted markdown without re-parsing) |
512
+ | `@cesarandreslopez/occ/doc/entities` | Entity and keyword extraction |
513
+ | `@cesarandreslopez/occ/doc/references` | Cross-reference detection |
514
+ | `@cesarandreslopez/occ/workspace/analyze` | `analyzeWorkspace` — workspace-level analysis |
515
+ | `@cesarandreslopez/occ/workspace/describe` | `describeWorkspace` — fast directory classification |
516
+ | `@cesarandreslopez/occ/workspace/documents` | `inspectWorkspaceDocumentSet` — document summaries and cross-references |
517
+ | `@cesarandreslopez/occ/workspace/types` | Workspace analysis types |
518
+ | `@cesarandreslopez/occ/workspace/prepare` | `prepareWorkspaceContext` — combined code indexing + document inspection |
519
+ | `@cesarandreslopez/occ/workspace/prepare-types` | Workspace preparation types (`WorkspacePrepareOptions`, `WorkspacePreparedContext`, etc.) |
520
+ | `@cesarandreslopez/occ/workspace/bundle` | `bundleWorkspace` — single-call versioned `WorkspaceBundle` (description + analysis + documents + slim code index + outline + cross-refs + optional document / code / unified corpus chunks via `WorkspaceCorpusChunk`) |
521
+ | `@cesarandreslopez/occ/markdown/convert` | `documentToMarkdown` — document-to-markdown conversion (now reads MD/MDX/TXT/RST/AsciiDoc/YAML/JSON/TOML directly) |
522
+ | `@cesarandreslopez/occ/structure/extract` | `extractFromMarkdown` — heading tree extraction |
523
+ | `@cesarandreslopez/occ/structure/types` | Structure types and helpers |
524
+ | `@cesarandreslopez/occ/sheet/inspect` | `inspectWorkbook` — XLSX workbook inspection |
525
+ | `@cesarandreslopez/occ/sheet/types` | Sheet inspection types |
526
+ | `@cesarandreslopez/occ/slide/inspect` | `inspectPresentation` — presentation inspection |
527
+ | `@cesarandreslopez/occ/table/inspect` | Table extraction from documents |
528
+ | `@cesarandreslopez/occ/table/types` | Table extraction types |
529
+ | `@cesarandreslopez/occ/health` | `health()` — liveness probe with version + capability flags |
530
+ | `@cesarandreslopez/occ/errors` | `OccAbortError`, `OCC_ABORTED`, `isOccAbortError` — typed abort error that survives `instanceof` across forked subprocesses |
531
+ | `@cesarandreslopez/occ/types` | Shared types (`ConfidenceLevel`, `ParseResult`, `ParserOutput`, etc.) |
532
+ | `@cesarandreslopez/occ/tokens` | Token estimation utilities |
533
+ | `@cesarandreslopez/occ/progress-event` | Progress event types (`ProgressPhase`, `ProgressEvent` with optional `scope`/`currentPath`/`bytesProcessed`/`totalBytes`/`startedAt`/`elapsedMs`, plus off-thread bundle-read timing fields `section`/`parentReadStartedAt`/`parentReadFinishedAt`/`parentReadDurationMs`/`sectionStartedAt`/`sectionFinishedAt`/`sectionDurationMs`/`cleanupDurationMs`) |
534
+ | `@cesarandreslopez/occ/stats` | Stats types (`StatsRow`, `AggregateResult`) and `aggregate()` |
520
535
 
521
536
  TypeScript ships with OCC as a direct dependency, so the code exploration module works after a normal install. You only need a separate TypeScript setup if your own project uses `tsc`.
522
537
 
@@ -0,0 +1,39 @@
1
+ export interface CheckCodeSyntaxOptions {
2
+ repoRoot: string;
3
+ file: string;
4
+ }
5
+ export interface CodeSyntaxDiagnostic {
6
+ message: string;
7
+ line?: number;
8
+ column?: number;
9
+ code?: string | number;
10
+ source?: string;
11
+ severity: 'error' | 'warning';
12
+ }
13
+ export type CheckCodeSyntaxResult = {
14
+ status: 'checked';
15
+ supported: true;
16
+ valid: boolean;
17
+ path: string;
18
+ relativePath: string;
19
+ language: string;
20
+ diagnostics: CodeSyntaxDiagnostic[];
21
+ } | {
22
+ status: 'unsupported';
23
+ supported: false;
24
+ valid?: undefined;
25
+ path: string;
26
+ relativePath: string;
27
+ language: string;
28
+ diagnostics: [];
29
+ } | {
30
+ status: 'error';
31
+ supported: false;
32
+ valid: false;
33
+ path?: string;
34
+ relativePath?: string;
35
+ language?: string;
36
+ diagnostics: CodeSyntaxDiagnostic[];
37
+ message: string;
38
+ };
39
+ export declare function checkCodeSyntax(options: CheckCodeSyntaxOptions): Promise<CheckCodeSyntaxResult>;
@@ -0,0 +1,242 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { readFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { promisify } from 'node:util';
6
+ import { getLanguageSpec } from './languages.js';
7
+ import { collectTreeSitterErrorNodes, parseTreeSitterContent } from './tree-sitter.js';
8
+ const execFileAsync = promisify(execFile);
9
+ let typescriptModulePromise;
10
+ function loadTypescript() {
11
+ typescriptModulePromise ??= import('typescript');
12
+ return typescriptModulePromise;
13
+ }
14
+ function resolveWithinRepo(repoRoot, file) {
15
+ const root = path.resolve(repoRoot);
16
+ const resolved = path.isAbsolute(file) ? path.resolve(file) : path.resolve(root, file);
17
+ const relativePath = path.relative(root, resolved);
18
+ if (relativePath === '' || relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
19
+ return `Resolved path "${file}" is outside repo root "${root}".`;
20
+ }
21
+ return { path: resolved, relativePath: relativePath.split(path.sep).join('/') };
22
+ }
23
+ function languageForExtension(filePath) {
24
+ const extension = path.extname(filePath).toLowerCase();
25
+ switch (extension) {
26
+ case '.ts':
27
+ case '.tsx':
28
+ return 'typescript';
29
+ case '.js':
30
+ case '.jsx':
31
+ case '.mjs':
32
+ case '.cjs':
33
+ return 'javascript';
34
+ case '.vue':
35
+ return 'vue';
36
+ case '.py':
37
+ return 'python';
38
+ default:
39
+ return extension.replace(/^\./, '') || 'unknown';
40
+ }
41
+ }
42
+ function diagnosticFromError(error, source) {
43
+ const maybeError = error instanceof Error ? error : new Error(String(error));
44
+ const loc = error;
45
+ const start = loc.loc?.start ?? loc.loc;
46
+ const line = typeof start?.line === 'number' ? start.line : undefined;
47
+ const column = typeof start?.column === 'number' ? start.column + 1 : undefined;
48
+ return {
49
+ message: maybeError.message,
50
+ line,
51
+ column,
52
+ source,
53
+ severity: 'error',
54
+ };
55
+ }
56
+ async function checkTypeScriptLike(filePath, content, language) {
57
+ const ts = await loadTypescript();
58
+ const extension = path.extname(filePath).toLowerCase();
59
+ const scriptKind = extension === '.tsx'
60
+ ? ts.ScriptKind.TSX
61
+ : extension === '.jsx'
62
+ ? ts.ScriptKind.JSX
63
+ : language === 'javascript'
64
+ ? ts.ScriptKind.JS
65
+ : ts.ScriptKind.TS;
66
+ const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, scriptKind);
67
+ const parseDiagnostics = sourceFile.parseDiagnostics;
68
+ return parseDiagnostics.map((diagnostic) => {
69
+ const position = diagnostic.start == null ? undefined : sourceFile.getLineAndCharacterOfPosition(diagnostic.start);
70
+ return {
71
+ message: ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
72
+ line: position ? position.line + 1 : undefined,
73
+ column: position ? position.character + 1 : undefined,
74
+ code: diagnostic.code,
75
+ source: 'typescript',
76
+ severity: 'error',
77
+ };
78
+ });
79
+ }
80
+ async function checkVue(filePath, content) {
81
+ const compiler = await import('@vue/compiler-sfc');
82
+ const parsed = compiler.parse(content, { filename: filePath });
83
+ const diagnostics = parsed.errors.map(error => diagnosticFromError(error, 'vue'));
84
+ if (diagnostics.length > 0)
85
+ return diagnostics;
86
+ if (!parsed.descriptor.script && !parsed.descriptor.scriptSetup)
87
+ return diagnostics;
88
+ try {
89
+ const compiled = compiler.compileScript(parsed.descriptor, { id: 'occ-check' });
90
+ diagnostics.push(...(await checkTypeScriptLike(filePath, compiled.content, 'typescript')));
91
+ }
92
+ catch (error) {
93
+ diagnostics.push(diagnosticFromError(error, 'vue'));
94
+ }
95
+ return diagnostics;
96
+ }
97
+ function isMissingExecutable(error) {
98
+ return error instanceof Error && 'code' in error && error.code === 'ENOENT';
99
+ }
100
+ function pythonDiagnostic(stderr) {
101
+ const lineMatch = stderr.match(/line (\d+)/);
102
+ const message = stderr
103
+ .split(/\r?\n/)
104
+ .map(line => line.trim())
105
+ .filter(Boolean)
106
+ .at(-1);
107
+ return {
108
+ message: message ?? (stderr.trim() || 'Python syntax error'),
109
+ line: lineMatch ? Number(lineMatch[1]) : undefined,
110
+ source: 'python',
111
+ severity: 'error',
112
+ };
113
+ }
114
+ async function checkPython(filePath) {
115
+ const script = [
116
+ 'import ast, pathlib, sys',
117
+ 'path = sys.argv[1]',
118
+ "source = pathlib.Path(path).read_text(encoding='utf8')",
119
+ 'ast.parse(source, filename=path)',
120
+ ].join('\n');
121
+ try {
122
+ await execFileAsync('python3', ['-c', script, filePath], { timeout: 5_000, maxBuffer: 1_000_000 });
123
+ return [];
124
+ }
125
+ catch (error) {
126
+ if (isMissingExecutable(error))
127
+ return 'unsupported';
128
+ const execError = error;
129
+ const stderr = typeof execError.stderr === 'string' ? execError.stderr : String(execError.stderr ?? '');
130
+ return [pythonDiagnostic(stderr)];
131
+ }
132
+ }
133
+ async function checkTreeSitter(grammar, content) {
134
+ const tree = await parseTreeSitterContent(grammar, content);
135
+ if (!tree)
136
+ return 'unsupported';
137
+ // Free the WASM tree handle once the error nodes are copied into plain diagnostics.
138
+ try {
139
+ return collectTreeSitterErrorNodes(tree.rootNode).map(error => ({
140
+ message: error.type === 'ERROR' ? 'Syntax error' : `Missing syntax node: ${error.text || error.type}`,
141
+ line: error.line,
142
+ column: error.column,
143
+ source: 'tree-sitter',
144
+ severity: 'error',
145
+ }));
146
+ }
147
+ finally {
148
+ tree.delete();
149
+ }
150
+ }
151
+ export async function checkCodeSyntax(options) {
152
+ const resolved = resolveWithinRepo(options.repoRoot, options.file);
153
+ if (typeof resolved === 'string') {
154
+ return {
155
+ status: 'error',
156
+ supported: false,
157
+ valid: false,
158
+ diagnostics: [{ message: resolved, severity: 'error', source: 'occ' }],
159
+ message: resolved,
160
+ };
161
+ }
162
+ if (!existsSync(resolved.path)) {
163
+ const message = `File not found: ${resolved.relativePath}`;
164
+ return {
165
+ status: 'error',
166
+ supported: false,
167
+ valid: false,
168
+ path: resolved.path,
169
+ relativePath: resolved.relativePath,
170
+ diagnostics: [{ message, severity: 'error', source: 'occ' }],
171
+ message,
172
+ };
173
+ }
174
+ const language = languageForExtension(resolved.path);
175
+ const extension = path.extname(resolved.path).toLowerCase();
176
+ const spec = getLanguageSpec(resolved.path);
177
+ if (extension === '.py') {
178
+ let diagnostics = await checkPython(resolved.path);
179
+ if (diagnostics === 'unsupported' && spec?.treeSitter) {
180
+ // python3 isn't on PATH (e.g. Windows) — fall back to the vendored tree-sitter grammar.
181
+ const content = await readFile(resolved.path, 'utf8');
182
+ diagnostics = await checkTreeSitter(spec.treeSitter.grammar, content);
183
+ }
184
+ if (diagnostics === 'unsupported') {
185
+ return {
186
+ status: 'unsupported',
187
+ supported: false,
188
+ path: resolved.path,
189
+ relativePath: resolved.relativePath,
190
+ language,
191
+ diagnostics: [],
192
+ };
193
+ }
194
+ return {
195
+ status: 'checked',
196
+ supported: true,
197
+ valid: diagnostics.length === 0,
198
+ path: resolved.path,
199
+ relativePath: resolved.relativePath,
200
+ language,
201
+ diagnostics,
202
+ };
203
+ }
204
+ if (!['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.vue'].includes(extension) && !spec?.treeSitter) {
205
+ return {
206
+ status: 'unsupported',
207
+ supported: false,
208
+ path: resolved.path,
209
+ relativePath: resolved.relativePath,
210
+ language,
211
+ diagnostics: [],
212
+ };
213
+ }
214
+ const content = await readFile(resolved.path, 'utf8');
215
+ const diagnostics = extension === '.vue'
216
+ ? await checkVue(resolved.path, content)
217
+ : ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'].includes(extension)
218
+ ? await checkTypeScriptLike(resolved.path, content, language)
219
+ : spec?.treeSitter
220
+ ? await checkTreeSitter(spec.treeSitter.grammar, content)
221
+ : 'unsupported';
222
+ if (diagnostics === 'unsupported') {
223
+ return {
224
+ status: 'unsupported',
225
+ supported: false,
226
+ path: resolved.path,
227
+ relativePath: resolved.relativePath,
228
+ language,
229
+ diagnostics: [],
230
+ };
231
+ }
232
+ return {
233
+ status: 'checked',
234
+ supported: true,
235
+ valid: diagnostics.length === 0,
236
+ path: resolved.path,
237
+ relativePath: resolved.relativePath,
238
+ language,
239
+ diagnostics,
240
+ };
241
+ }
242
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/code/check.ts"],"names":[],"mappings":"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"}
@@ -5,10 +5,12 @@ import { z } from 'zod';
5
5
  import { buildCodebaseIndex } from './build.js';
6
6
  import { writeStream } from '../utils.js';
7
7
  import { parsePositiveInt } from '../cli-validation.js';
8
+ import { checkCodeSyntax } from './check.js';
8
9
  import { analyzeCallChain, analyzeCallers, analyzeCalls, analyzeDeps, analyzeModuleCoupling, analyzeTree, createPayload, findByName, findByPattern, findByType, findContent, fusedSearch, } from './query.js';
9
10
  import { formatCodeDiff, formatCodeDiffJson, formatChains, formatClassTree, formatContentResults, formatDependencies, formatFusedResults, formatModuleCoupling, formatPayloadJson, formatRelationResults, formatSearchResults, renderRepoMap, } from './output.js';
10
11
  import { computeCodeDiff } from './diff.js';
11
12
  import { buildRepoMap } from './map.js';
13
+ import { readCodeSource } from './source.js';
12
14
  import { createTokenizer, resolveTokenizerName } from '../tokens.js';
13
15
  import { getGitDiffSummary, getGitFileHistory } from '../git/api.js';
14
16
  const CodeCommandOptionsSchema = z
@@ -44,6 +46,7 @@ const CodeCommandOptionsSchema = z
44
46
  staged: z.boolean().optional(),
45
47
  changedFrom: z.string().optional(),
46
48
  changedTo: z.string().optional(),
49
+ nodeId: z.string().optional(),
47
50
  })
48
51
  .passthrough();
49
52
  function getOptions(command) {
@@ -89,6 +92,31 @@ async function emit(output, options) {
89
92
  }
90
93
  await writeStream(process.stdout, output);
91
94
  }
95
+ function formatSourceResult(result) {
96
+ if (result.status === 'resolved')
97
+ return result.source;
98
+ const candidates = 'candidates' in result && result.candidates.length > 0
99
+ ? `\n${result.candidates
100
+ .map(candidate => `- ${candidate.name} ${candidate.relativePath ?? candidate.path}:${candidate.line ?? '?'}`)
101
+ .join('\n')}`
102
+ : '';
103
+ return `${result.status}: ${result.message}${candidates}`;
104
+ }
105
+ function formatCheckResult(result) {
106
+ if (result.status === 'unsupported')
107
+ return `unsupported: ${result.relativePath}`;
108
+ if (result.status === 'error')
109
+ return `error: ${result.message}`;
110
+ if (result.valid)
111
+ return `valid: ${result.relativePath}`;
112
+ return [
113
+ `invalid: ${result.relativePath}`,
114
+ ...result.diagnostics.map(diagnostic => {
115
+ const location = diagnostic.line ? `:${diagnostic.line}${diagnostic.column ? `:${diagnostic.column}` : ''}` : '';
116
+ return `- ${diagnostic.source ?? 'syntax'}${location}: ${diagnostic.message}`;
117
+ }),
118
+ ].join('\n');
119
+ }
92
120
  function resolveContentMode(value, fallback) {
93
121
  if (!value)
94
122
  return fallback;
@@ -260,6 +288,42 @@ export function registerCodeCommands(program) {
260
288
  const output = options.format === 'json' ? formatPayloadJson(payload) : formatContentResults(results, options.ci);
261
289
  await emit(output.endsWith('\n') ? output : `${output}\n`, options);
262
290
  });
291
+ const sourceCmd = code.command('source [name]').description('Read one symbol body by node id or disambiguated name');
292
+ addSharedOptions(sourceCmd, { includeFile: true, includeType: true });
293
+ sourceCmd.option('--node-id <id>', 'exact node id from code map/find/index output');
294
+ sourceCmd.action(async (name, _options, command) => {
295
+ const options = getOptions(command);
296
+ if (options.nodeId && name) {
297
+ throw new Error('Use either --node-id or a symbol name, not both.');
298
+ }
299
+ if (!options.nodeId && !name) {
300
+ throw new Error('Provide --node-id <id> or a symbol name.');
301
+ }
302
+ const index = await withIndex(options, 'none');
303
+ const query = options.nodeId
304
+ ? { nodeId: options.nodeId }
305
+ : { name: name ?? '', file: options.file, type: parseType(options.type) };
306
+ const result = await readCodeSource(index, query);
307
+ // Signal failure to scripts/agents when the symbol can't be uniquely resolved
308
+ // (not_found / ambiguous / error), mirroring `check`'s valid===false exit gate.
309
+ if (result.status !== 'resolved') {
310
+ process.exitCode = 1;
311
+ }
312
+ const output = options.format === 'json' ? JSON.stringify(result, null, 2) : formatSourceResult(result);
313
+ await emit(output.endsWith('\n') ? output : `${output}\n`, options);
314
+ });
315
+ const checkCmd = code.command('check <file>').description('Check syntax for a source file without a language server');
316
+ addSharedOptions(checkCmd);
317
+ checkCmd.action(async (file, _options, command) => {
318
+ const options = getOptions(command);
319
+ const repoRoot = path.resolve(options.path ?? process.cwd());
320
+ const result = await checkCodeSyntax({ repoRoot, file });
321
+ if (result.valid === false) {
322
+ process.exitCode = 1;
323
+ }
324
+ const output = options.format === 'json' ? JSON.stringify(result, null, 2) : formatCheckResult(result);
325
+ await emit(output.endsWith('\n') ? output : `${output}\n`, options);
326
+ });
263
327
  const analyzeCallsCmd = analyze.command('calls <functionName>').description('Show what a function calls');
264
328
  addSharedOptions(analyzeCallsCmd, { includeFile: true });
265
329
  analyzeCallsCmd.action(async (functionName, _options, command) => {