project-athena 0.2.0 → 0.3.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 (148) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +19 -11
  3. package/dist/activity-P4UUFHWV.js +77 -0
  4. package/dist/agents-3HTSQGL2.js +69 -0
  5. package/dist/ai-ASL4FFQ7.js +96 -0
  6. package/dist/analyze-F3YOILNL.js +49 -0
  7. package/dist/chunk-2DY4PUR4.js +465 -0
  8. package/dist/chunk-2R6PXY6Z.js +33 -0
  9. package/dist/chunk-2RS6TA3D.js +908 -0
  10. package/dist/chunk-32PRS2MG.js +169 -0
  11. package/dist/chunk-3IS2X2Z7.js +89 -0
  12. package/dist/chunk-65TSRDQ3.js +124 -0
  13. package/dist/chunk-673EPYYH.js +105 -0
  14. package/dist/chunk-AD4OZCU2.js +34 -0
  15. package/dist/chunk-CXUTYCZX.js +238 -0
  16. package/dist/chunk-DOQMAZL5.js +436 -0
  17. package/dist/chunk-EF6GHOIN.js +343 -0
  18. package/dist/chunk-ELKFRTZT.js +221 -0
  19. package/dist/chunk-EUPETUWA.js +14 -0
  20. package/dist/chunk-FQEGEVLY.js +38 -0
  21. package/dist/chunk-GHFRQAYT.js +112 -0
  22. package/dist/chunk-GR4DL2VM.js +47 -0
  23. package/dist/chunk-INT3IGMQ.js +111 -0
  24. package/dist/chunk-IQQBAWSM.js +90 -0
  25. package/dist/chunk-JCGDR4UV.js +182 -0
  26. package/dist/chunk-JY6BFIJJ.js +95 -0
  27. package/dist/chunk-KBO7XRBW.js +26 -0
  28. package/dist/chunk-LEMPCNAY.js +209 -0
  29. package/dist/chunk-LMOFZNTQ.js +379 -0
  30. package/dist/chunk-OLPSEQS5.js +264 -0
  31. package/dist/chunk-PANVPNYF.js +2892 -0
  32. package/dist/chunk-PDFQ5BFG.js +378 -0
  33. package/dist/chunk-PR53DT7I.js +179 -0
  34. package/dist/chunk-PVCIGUIT.js +64 -0
  35. package/dist/chunk-RXNRMIZP.js +390 -0
  36. package/dist/chunk-S7T6VX56.js +61 -0
  37. package/dist/chunk-SHKXELUX.js +295 -0
  38. package/dist/chunk-SJOOXO34.js +904 -0
  39. package/dist/chunk-TGJUUB44.js +399 -0
  40. package/dist/chunk-VSYBC3VY.js +28 -0
  41. package/dist/chunk-XB66PZ6Z.js +182 -0
  42. package/dist/chunk-XFN7UZCS.js +35 -0
  43. package/dist/chunk-XW3MLHI2.js +32 -0
  44. package/dist/chunk-Z6QWMJLK.js +124 -0
  45. package/dist/chunk-ZB7LC6FP.js +221 -0
  46. package/dist/clean-S7PKCOLM.js +23 -0
  47. package/dist/cli.js +18 -9795
  48. package/dist/context-AH5MFHRG.js +147 -0
  49. package/dist/doctor-RSJMLE2P.js +62 -0
  50. package/dist/event-TNT6CQZR.js +101 -0
  51. package/dist/git-hook-YZNZGAAP.js +252 -0
  52. package/dist/init-2V34DVRQ.js +37 -0
  53. package/dist/mcp-T2GRTEX5.js +255 -0
  54. package/dist/open-2QSZKKUG.js +1259 -0
  55. package/dist/program-V2USHGXN.js +148 -0
  56. package/dist/review-R3R2J5BJ.js +115 -0
  57. package/dist/rules-4FXJUCXB.js +83 -0
  58. package/dist/security-PI4LXCOB.js +175 -0
  59. package/dist/staged-check-HHIBKKMR.js +500 -0
  60. package/dist/status-6M73WX5I.js +75 -0
  61. package/dist/sync-3ZNJDSSU.js +216 -0
  62. package/dist/watch-USN5CQIV.js +121 -0
  63. package/dist/web/assets/{Editor-CL6A82dQ.js → Editor-BI56-HX5.js} +1 -1
  64. package/dist/web/assets/{abnfDiagram-O67JEVCF-BuFfjTfx.js → abnfDiagram-O67JEVCF-y_9px2ob.js} +1 -1
  65. package/dist/web/assets/architecture-7GRP2DOG-DyxkwYiW.js +1 -0
  66. package/dist/web/assets/{architectureDiagram-NJMV4G6O-BTsv87pA.js → architectureDiagram-NJMV4G6O-lB6fcVzU.js} +1 -1
  67. package/dist/web/assets/{blockDiagram-BEXU5L5S-CNXqyKIx.js → blockDiagram-BEXU5L5S-DcM5pEjM.js} +1 -1
  68. package/dist/web/assets/{c4Diagram-YGBWAQC7-kksFXQoJ.js → c4Diagram-YGBWAQC7-CJgqxTY2.js} +1 -1
  69. package/dist/web/assets/channel-BvDD7AMO.js +1 -0
  70. package/dist/web/assets/{chunk-3FUC2YCW-BY99XYBs.js → chunk-3FUC2YCW-B8l_VuWR.js} +1 -1
  71. package/dist/web/assets/{chunk-5DYCD2WN-h-PRThMP.js → chunk-5DYCD2WN-CZecgNhf.js} +1 -1
  72. package/dist/web/assets/{chunk-742MDFTN-BIxfyBUj.js → chunk-742MDFTN-BeZ7P5jB.js} +1 -1
  73. package/dist/web/assets/{chunk-7INBJB4K-DZtctDdk.js → chunk-7INBJB4K-IKy0pr5m.js} +1 -1
  74. package/dist/web/assets/{chunk-7M6MHVWA-BX9fUUbL.js → chunk-7M6MHVWA-Dg_QqJEX.js} +1 -1
  75. package/dist/web/assets/{chunk-7PRAP22T-X1s3e81n.js → chunk-7PRAP22T-DqQNS8oq.js} +1 -1
  76. package/dist/web/assets/{chunk-GTNCS2PH-Bh8eluU-.js → chunk-GTNCS2PH-x4sM51nJ.js} +1 -1
  77. package/dist/web/assets/{chunk-GWA4HPMP-D1KATAi0.js → chunk-GWA4HPMP-CE5GcwVN.js} +1 -1
  78. package/dist/web/assets/{chunk-MBY4JIJT-CEErv5kP.js → chunk-MBY4JIJT-DA_D1hjF.js} +1 -1
  79. package/dist/web/assets/{chunk-NETBCI7D-BXiH4aPa.js → chunk-NETBCI7D-CeJ3XBZ2.js} +1 -1
  80. package/dist/web/assets/{chunk-O7XYJQB3-Dede62yi.js → chunk-O7XYJQB3-BH2gESvO.js} +1 -1
  81. package/dist/web/assets/{chunk-UA2S7LBM-KZ9aWRTa.js → chunk-UA2S7LBM-BWr0xCux.js} +1 -1
  82. package/dist/web/assets/{chunk-WEXAMYUT-xf-3L2zU.js → chunk-WEXAMYUT-B4QwUZPM.js} +1 -1
  83. package/dist/web/assets/{chunk-Z7XXMR3K-DLpRdZ8n.js → chunk-Z7XXMR3K-BxcA-y5B.js} +1 -1
  84. package/dist/web/assets/{chunk-ZIGJFQKS-BdJ0_J-U.js → chunk-ZIGJFQKS-BZYvP63G.js} +1 -1
  85. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-C5VzGDGq.js → classDiagram-v2-NBCMYWYE-BS3P_NXb.js} +1 -1
  86. package/dist/web/assets/{cynefin-OW5HDTMX-BqOgbE7U.js → cynefin-OW5HDTMX-BALD4Pve.js} +1 -1
  87. package/dist/web/assets/{cynefinDiagram-VND7K2PF-CCC_feG5.js → cynefinDiagram-VND7K2PF-BM2y4W6d.js} +1 -1
  88. package/dist/web/assets/{dagre-6A5THRUB-SLP-94sd.js → dagre-6A5THRUB-PTfOrIdF.js} +1 -1
  89. package/dist/web/assets/{diagram-22UHCM2B-DpHwEBYM.js → diagram-22UHCM2B-CpXEHhUl.js} +1 -1
  90. package/dist/web/assets/{diagram-3UASUU5V-iGFwuMEk.js → diagram-3UASUU5V-DPHfXzr6.js} +1 -1
  91. package/dist/web/assets/{diagram-ATOU4E4O-Csx9hj0z.js → diagram-ATOU4E4O-DMBNXvAC.js} +1 -1
  92. package/dist/web/assets/{diagram-CDSNMT55-yGKrLRO5.js → diagram-CDSNMT55-CDLe1M7G.js} +1 -1
  93. package/dist/web/assets/{diagram-MLGK6HIB-mDpCormD.js → diagram-MLGK6HIB-D6L8oE7j.js} +1 -1
  94. package/dist/web/assets/{diagram-MPIPVDR6-BJHdatrF.js → diagram-MPIPVDR6-CP-S7rrQ.js} +1 -1
  95. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-ClJbCBBu.js → ebnfDiagram-ZINNZB2B-CkUc63TK.js} +1 -1
  96. package/dist/web/assets/{elk-276RUBZZ-C5tFZMYm.js → elk-276RUBZZ-C1CAJLH_.js} +1 -1
  97. package/dist/web/assets/{erDiagram-OPXOYQCR-u7_UFZot.js → erDiagram-OPXOYQCR-DXBnaTHm.js} +1 -1
  98. package/dist/web/assets/eventmodeling-NTZA5JFV-cLIzvgDp.js +1 -0
  99. package/dist/web/assets/flowDiagram-KWPJA3E3-D7Zq63VQ.js +1 -0
  100. package/dist/web/assets/{ganttDiagram-FUAMR5RP-B4hrLcLf.js → ganttDiagram-FUAMR5RP-BVm8SiGg.js} +1 -1
  101. package/dist/web/assets/{gitGraph-4MIJSDKK-Dxx-Bl-U.js → gitGraph-4MIJSDKK-Dm7xBrW0.js} +1 -1
  102. package/dist/web/assets/{gitGraphDiagram-X574FWY7-DKjDV5Kr.js → gitGraphDiagram-X574FWY7-D_PIKKhx.js} +1 -1
  103. package/dist/web/assets/index-CeOIBpu2.js +45 -0
  104. package/dist/web/assets/{info-A6RAGUB7-CgqIdgQA.js → info-A6RAGUB7-CLGrx8pn.js} +1 -1
  105. package/dist/web/assets/{infoDiagram-VRGFBTTK-D-qjWkwy.js → infoDiagram-VRGFBTTK-BlBjaKm7.js} +1 -1
  106. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-CzpqV6WN.js → ishikawaDiagram-OU5B5YK6-Kxe7GGS3.js} +1 -1
  107. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-BBnOUDzw.js → journeyDiagram-ZHPQQLJL-wCMoHK4M.js} +1 -1
  108. package/dist/web/assets/{kanban-definition-PNTS6WVX-Di9sTFB1.js → kanban-definition-PNTS6WVX-BxdCz9Ko.js} +1 -1
  109. package/dist/web/assets/{line-B8ehGiwF.js → line-Zf11FnWF.js} +1 -1
  110. package/dist/web/assets/{mermaid-parser.core-DWsT24e5.js → mermaid-parser.core-BvFFklev.js} +3 -3
  111. package/dist/web/assets/{mermaid.core-DzYpaVzI.js → mermaid.core-Cd_Ssti9.js} +4 -4
  112. package/dist/web/assets/{mindmap-definition-NLK3R4M7-CxagBsH3.js → mindmap-definition-NLK3R4M7-BGPQl-GY.js} +1 -1
  113. package/dist/web/assets/{packet-AYTQ26CC-CmxbS0qf.js → packet-AYTQ26CC-6lI3cEX_.js} +1 -1
  114. package/dist/web/assets/{pegDiagram-GJSIUBJH-_3jnD3q8.js → pegDiagram-GJSIUBJH-BPIjlSAj.js} +1 -1
  115. package/dist/web/assets/{pie-WAS4IAKB-DppCWOqP.js → pie-WAS4IAKB-D6Dzj3Ja.js} +1 -1
  116. package/dist/web/assets/{pieDiagram-5QR66LMP-DTVThTqz.js → pieDiagram-5QR66LMP-DtA-vZmv.js} +1 -1
  117. package/dist/web/assets/{quadrantDiagram-O4NWA36T-BJ_OFnht.js → quadrantDiagram-O4NWA36T-EettyqQn.js} +1 -1
  118. package/dist/web/assets/{radar-RG4KPBEZ-B6quSMjs.js → radar-RG4KPBEZ-xmRc3Dl8.js} +1 -1
  119. package/dist/web/assets/{railroad-74A4TZTK-CErU7RG3.js → railroad-74A4TZTK-BLB7dhOn.js} +1 -1
  120. package/dist/web/assets/railroad-abnf-HS5TGJTU-DBFym6g8.js +1 -0
  121. package/dist/web/assets/railroad-ebnf-LZEXJU2U-CkG28ig8.js +1 -0
  122. package/dist/web/assets/railroad-peg-WCYAUIDC-Df_Ifaad.js +1 -0
  123. package/dist/web/assets/{railroadDiagram-XR7U4H2S-Zu0N_D-D.js → railroadDiagram-XR7U4H2S-PBw4We6b.js} +1 -1
  124. package/dist/web/assets/{requirementDiagram-PLB6GJNP-Bl6CN3ul.js → requirementDiagram-PLB6GJNP-DjQpaUFA.js} +1 -1
  125. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-DdpKzZV8.js → sankeyDiagram-IPEJSGJF-CBW-_AoF.js} +1 -1
  126. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-BwUqrl9F.js → sequenceDiagram-PO4LG4MO-DEoPYqY9.js} +1 -1
  127. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-C3vZGpAi.js → stateDiagram-v2-GCMORJYK-UZq5rPud.js} +1 -1
  128. package/dist/web/assets/{swimlanes-2SLR337P-BjoMa2HW.js → swimlanes-2SLR337P-CACfuXJR.js} +1 -1
  129. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-CDSrqdgx.js +8 -0
  130. package/dist/web/assets/{timeline-definition-EJHVYXUP-0apcf2l2.js → timeline-definition-EJHVYXUP-CYJl7mP-.js} +1 -1
  131. package/dist/web/assets/{treeView-Q6P3EWNA-BOPWzyg3.js → treeView-Q6P3EWNA-DtZ5nkPQ.js} +1 -1
  132. package/dist/web/assets/{treemap-WGGIJYW6-CC6rU6dD.js → treemap-WGGIJYW6-m49GDM-M.js} +1 -1
  133. package/dist/web/assets/{usecaseDiagram-POWQR4AR-BFclVNBG.js → usecaseDiagram-POWQR4AR-C2Vg20UK.js} +1 -1
  134. package/dist/web/assets/{vennDiagram-UO4OBE2U-D2XPv-zb.js → vennDiagram-UO4OBE2U-C07v5OGI.js} +1 -1
  135. package/dist/web/assets/{wardley-WFR3VGLG-Btz6ID8P.js → wardley-WFR3VGLG-C9Z7EZtz.js} +1 -1
  136. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-DFYPvGpS.js → wardleyDiagram-VNRHLVJA-DdA8CcSC.js} +1 -1
  137. package/dist/web/assets/{xychartDiagram-PMCCYNJV-DMkQD-sV.js → xychartDiagram-PMCCYNJV-C9EZw07b.js} +1 -1
  138. package/dist/web/index.html +1 -1
  139. package/package.json +5 -2
  140. package/dist/web/assets/architecture-7GRP2DOG-CGSEE8po.js +0 -1
  141. package/dist/web/assets/channel-Dneho6u-.js +0 -1
  142. package/dist/web/assets/eventmodeling-NTZA5JFV-CA-vzJb3.js +0 -1
  143. package/dist/web/assets/flowDiagram-KWPJA3E3-Ca_1cbbS.js +0 -1
  144. package/dist/web/assets/index-s6lwoj84.js +0 -45
  145. package/dist/web/assets/railroad-abnf-HS5TGJTU-CWsEvTRR.js +0 -1
  146. package/dist/web/assets/railroad-ebnf-LZEXJU2U-Cyq9ELNn.js +0 -1
  147. package/dist/web/assets/railroad-peg-WCYAUIDC-BX1hI1hV.js +0 -1
  148. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-BGNz1HGG.js +0 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,81 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 — 2026-09-30
4
+
5
+ Performance release: work is now proportional to what changed. On a 20,000-file repository (vs 0.2.1): a `sync --check` with nothing changed takes ~0.42 s instead of 2.9 s, `status` 0.14 s instead of 0.34 s, agent hooks start in ~28 ms instead of 112 ms, and peak memory is 20–52% lower in every scenario. The generated `model.json` is unchanged (golden-tested against the 0.2.1 analyzer). Benchmarks: `npm run bench`.
6
+
7
+ After upgrading, the first run rebuilds `.athena/state.json` in the new format automatically. If you use the pre-commit hook, run `athena git-hook install` again so it checks staged content.
8
+
9
+ ### Performance
10
+
11
+ - `athena open` reuses parsed `.athena` artifacts: a new `ProjectSession` loads `model.json`, `graph.json`, `security-scan.json` and `state.json` once and re-reads them only when the file changes (inode, size or mtime). The overview, graph, context, security and `athena security` paths share it, and the graph summary checks for `model.json` with a stat instead of parsing and validating the whole model.
12
+ - The event stream (`/api/events`) coalesces bursts into one write per 250 ms (intermediate activity states collapse to the latest), serializes each event once for all clients, and applies backpressure: a client that stops reading gets nothing more until its socket drains, keeps at most 100 queued events (oldest dropped) plus the latest activity, and then receives a `resync` frame with the number of events it missed. At most 16 event streams are open at once (503 beyond that).
13
+ - `GET /api/sync` no longer includes each proposed document's full diff; the Sync page fetches a diff from the new `GET /api/sync/:doc` only when the document is expanded.
14
+ - `athena event` (run by agent hooks on every tool call) starts about 2.5× faster and uses about 40% less memory (p50 112 ms / 100 MB → ~45 ms / 57 MB on the 20k-file benchmark): it no longer loads commander or any other command's dependencies. All other commands load their code on demand, which also lowers memory for `status`, `review` and `sync`.
15
+ - `athena event` no longer loads zod: the paths it needs moved to a dependency-free module (`src/core/paths.ts`). p50 of 10 on an Apple M-series laptop: ~43 ms / 57 MB → ~26 ms / 49 MB. The startup test now fails if zod reaches the event chunk.
16
+ - The CLI is now built with code splitting: `dist/cli.js` is a small entry that loads chunks from `dist/` as needed.
17
+ - Project graph building is linear in the number of edges (edge de-duplication, import traversal and package lookups no longer rescan everything); 50k import edges now build in about 0.1 s instead of 3.2 s, and 100k in about 0.25 s. Graph output is unchanged. `athena graph --node` and task-context expansion use a per-graph index.
18
+ - Git metadata is collected with concurrent `git` processes, the `git --version` check runs once per process, and history is read once, bounded to the last 180 days / 2,000 commits. The contributor count in `model.json` (`git.contributorCount`) now counts authors in that window instead of the whole history.
19
+ - Nested `.gitignore` matching only consults the path's own ancestor directories instead of every `.gitignore` in the project.
20
+ - Analysis is staged and incremental: a stat-only walk, one content pass in which every new or changed file is read exactly once (hashing and all per-file detector work share the same bytes), then an aggregate stage that builds the model from cached per-file facts. Unchanged files are not read at all. The generated `model.json` is unchanged (guarded by a golden test against the 0.2.1 analyzer).
21
+ - 20,000-file benchmark (Apple M-series, vs 0.2.1): warm `sync --check` 2.9 s → 0.50 s (×5.8), `sync --check` after a one-file change 2.9 s → 0.49 s, cold `init` 3.5 s → 2.3 s; peak memory lower in every scenario (warm sync 195 → 162 MB). 200,000 files: warm `sync --check` 46 s → 4.1 s, cold `init` 51 s → 40 s, peak memory 730 → 589 MB (init).
22
+ - File text is no longer cached without limit during analysis: a 64 MB byte-budgeted LRU serves the rare aggregate-stage lookup, and text is dropped as soon as a file's facts are computed.
23
+ - Sync planning goes through one scheduler per project (`src/services/scheduler.ts`): at most one analysis runs at a time, requests that arrive meanwhile share a single follow-up run, and new file changes can cancel a plan that is still in progress (planning is read-only). The file watcher, the local server (`POST /api/sync/check`, review, re-plans after edits) and the MCP server use it, so concurrent checks no longer start concurrent full analyses; applying a proposal and a full re-analysis from the web UI never interleave with planning.
24
+ - While `athena open` (or another watcher) is running, a plan after file changes re-checks only the paths the watcher reported instead of walking the whole tree (about 265 → 170 ms for a one-file change in the 20k-file benchmark, in-process). It walks the whole tree on the first plan, after a `.gitignore`, `.athena/config.json` or `.git/info/exclude` change, a directory add/delete, a watcher error, when the project contains symbolic links, and at least every 5 minutes.
25
+ - `athena status` (and the server's status, the MCP `get_project_changes` tool) no longer walks the tree: it stats the files in the cached index and asks Git for new files (`git ls-files`, `git status --porcelain=v2 -z`). 254 → ~140 ms on the 20k-file benchmark; ~0.85 s for 200k files. The output is unchanged; it falls back to walking when Git can't give the same answer (no Git or no index, `include` patterns, ignore rules changed since the last analysis, submodules, nested repositories, symlinked directories).
26
+ - The server's status report is cached until the watcher reports a change (with a 30 s safety TTL, instead of a flat 3 s), and concurrent requests share one computation.
27
+
28
+ ### Changed
29
+
30
+ - **The pre-commit hook checks what is being committed.** New `athena sync --check --staged` compares the `.athena/` knowledge as staged with what Athena generates from the code as staged, so untracked files (a scratch file, a local `.env`) and unstaged edits no longer block a commit, and a staged change reverted in the working tree still counts. It mirrors the index into `.athena/cache/staged/` (gitignored, updated incrementally): files that match the working tree are sparse stand-ins that reuse the last analysis's hashes and the facts cache, so only staged changes are read (20k files, warm: ~0.54 s vs ~0.44 s for the working-tree check). When `athena sync` already wrote a document that isn't staged, the output says which files to `git add`; JSON output has `staged`, `inSync`, `stale` and per-document details. Exit code 3 when `.athena/` is not in the index; 2 when the index has unresolved conflicts. `athena git-hook install` writes the new command (re-run it to update an existing hook; `athena git-hook status` shows `staged: false` and a hint for old hooks), and the suggested husky/lefthook line is now `npx --no-install athena sync --check --staged`.
31
+ - The manifest detector is split into one module per ecosystem (`src/core/analyzer/detectors/manifests/`); `model.json` output is byte-identical.
32
+ - Files modified within 2 seconds of the file index being written are re-read on the next run instead of trusted by size and mtime (Git's "racily clean" rule), so a same-size edit in the same clock tick is never missed.
33
+ - The local server is split into route modules (`src/server/routes/*`) sharing one server context; routes, authentication, Host/Origin checks, security headers, CSP and rate limiting are unchanged.
34
+ - `GET /api/sync` returns the proposal without `documents[].diff` (reasons, changed and preserved sections, additions/deletions and `diffTruncated` are still included). New `GET /api/sync/:doc[?planId=…]` returns one proposed document's diff; the id must be a knowledge document in the current proposal (404 otherwise, 409 when `planId` is stale). `POST /api/sync/check` still returns the full plan.
35
+ - A corrupted `model.json` now gives a clear error with a hint (`model.json is corrupted (…)`, run `athena analyze`) instead of an internal error when starting a security scan from the web UI.
36
+ - Test, source, auth, API and schema path patterns now come from one module (`src/core/patterns.ts`). `athena review` recognizes more test layouts (`integration_tests/`, `androidTest/`, `test_driver/`, `*_test.dart`), and change impact recognizes the same test files as review plus test runner configs.
37
+ - New per-file facts cache in `.athena/cache/v1/` (gitignored; `cache/` is added to `.athena/.gitignore`, also for existing projects). Facts are keyed by content hash and discarded automatically when detector versions or the `ignore`/`include`/`maxFileBytes` settings change. Safe to delete; `athena clean` removes it with `.athena/`.
38
+ - `state.json` schema 2: the file index moved to `.athena/cache/v1/files.json` (full SHA-256 hashes), so `state.json` stays small regardless of repository size. Version-1 files are migrated automatically on first read; older Athena versions will treat a v2 `state.json` as corrupted and rebuild it.
39
+ - `athena init` creates the machine-local fingerprint salt (`.athena/local.json`) together with `.athena/`, so secret-finding fingerprints in the first `model.json` already use the project's stable key.
40
+
41
+ ## 0.2.1 — 2026-09-30
42
+
43
+ Security release. Upgrading is recommended for everyone. After upgrading, run `athena sync` once (the `security.md` format changed) and, if you use the pre-commit hook with `--review`, run `athena git-hook install --review` again so it reviews staged changes.
44
+
45
+ ### Breaking
46
+
47
+ - `athena review --base <ref>` exits with code 2 when the base can't be resolved or has no merge base with HEAD. Shallow CI clones need full history, e.g. `actions/checkout` with `fetch-depth: 0`.
48
+ - `ai.baseUrl` and `ai.consent` in the committed `.athena/config.json` are now ignored (with a warning in `athena ai status`, `athena ai enrich` and `athena doctor`). Move them to the environment (`ATHENA_AI_BASE_URL`, `ATHENA_AI_CONSENT=1`), `.athena/local.json` (gitignored), or your user config (`~/.config/athena/config.json`, `$XDG_CONFIG_HOME/athena/config.json`, or `%APPDATA%\athena\config.json` on Windows). `provider`, `model`, `mode` and `maxChars` still work from `.athena/config.json`.
49
+ - `--fail-on` now fails on unrated findings by default (`--unrated warn` to relax).
50
+ - `security-scan.json` and `athena security --json`: severity `unknown` has been renamed to `unrated`, in both findings and `counts`. Old scan files are still read. Tools that run once per input have a new optional `target` field (for example `pip-audit` per requirements file).
51
+ - The `security.md` format changed: the Fingerprint column was removed from Secret Management. Run `athena sync` to update it.
52
+
53
+ ### Security
54
+
55
+ - `athena security --fail-on` now also fails when an audit tool ran but failed or timed out, since nothing was checked. A tool that isn't installed is still only reported. The web UI shows `unrated` findings.
56
+ - **AI credential exfiltration**: a repository could set its own `ai.baseUrl` and `ai.consent` in `.athena/config.json` and receive your API key and knowledge. Both are now read only from machine-local sources (env > `.athena/local.json` > user config), providers refuse a base URL without a trusted source, and an Ollama endpoint that isn't loopback counts as remote and needs consent.
57
+ - **Local server**: per-client rate limiting on `/api/*` (token bucket, 30 req/s, burst 120, 429 when exceeded); `--allow-remote` keeps the Host/Origin allowlist (bind address plus loopback) instead of disabling it, and warns that the connection is plain HTTP; `/api/context` clamps `maxChars` to 200,000; a failed security-scan start (e.g. missing `model.json`) no longer leaves every later scan stuck at 423; shutdown no longer hangs on open event streams.
58
+ - **MCP**: every tool that returns repository content redacts secret-looking values and wraps the text in `<athena-document path="…" trust="untrusted-data">` delimiters with a note that it is project data, not instructions (embedded delimiters are neutralized).
59
+ - **Agent hooks**: generated hook scripts quote `ATHENA_HOOK_COMMAND`, the agent name and the hook argument (POSIX single quotes; cmd.exe double quotes with `%` doubled), so paths with spaces, quotes, `$` or `&` can't break or inject into the script.
60
+ - **Agent activity log**: commands and messages are additionally masked for `Authorization: Bearer|Basic …`, `--password …`, `mysql -p…` and `scheme://user:password@` credentials; rotation renames the log to `.agent-events.1.jsonl` (one generation kept) instead of rewriting it, so concurrent appends are never lost, and recent-event reads include the previous generation.
61
+ - **GitHub Action**: the PR comment is only updated when it was written by `github-actions[bot]` and carries the versioned `<!-- athena-review:v1 -->` marker (the old marker is still recognized from the same bot), so another user's comment can't be hijacked. Review findings now also produce `::error`/`::warning` file annotations (with line numbers where available, escaped, never secret values).
62
+ - **Web UI**: the Security and graph views refresh after `security.*` and `graph.*` events.
63
+ - `athena review` scans each run of consecutive added lines as one block, so multi-line secrets such as PEM private keys are now flagged (at the line where they start, never with the value).
64
+ - `athena review` fails closed on Git errors: a failing `git` command, an unknown `--base`, or a missing merge base (shallow clone) is now an error (exit 2) instead of an empty, "clean" review. Ctrl+C stops running `git` processes.
65
+ - The diff is streamed with a 64 MB budget; a larger diff fails the review instead of being silently truncated.
66
+ - `--base` now compares `merge-base(base, HEAD)..HEAD` for both the file list and the patch, so commits that exist only on the base branch no longer appear in the review.
67
+ - The dependency check uses the analyzer's manifest parsers (`name =` / `version =` are no longer reported as dependencies), and a new manifest reports all of its dependencies.
68
+ - New `athena review --staged` reviews the index (contents read from Git, untracked files ignored). The `--review` pre-commit hook now runs `athena review --staged --no-sync`, so unstaged or untracked files (such as a local `.env`) no longer block commits while staged secrets still do, and a review that can't run blocks the commit with its own message. Re-run `athena git-hook install --review` to update an installed hook.
69
+ - Untracked files are read once, never through a symlink that points outside the project, and only up to 512 KB. Files that could not be checked are listed in a `skipped` warning (JSON: `incomplete`, `skipped`), and the review no longer says it found nothing to flag.
70
+ - `athena security --fail-on` now counts findings the tool did not rate. pip-audit and govulncheck never report severity, so these findings were stored as `unknown` and never failed the build. They are now `unrated` and fail by default; `--unrated warn` reports them without failing.
71
+ - pnpm audit output is parsed in pnpm's own advisories format. Before this, every pnpm project was reported as having no vulnerable dependencies.
72
+ - An npm audit that reports an error (for example when offline) is now marked `failed` with npm's message. Before, it was reported as a clean audit. The same applies to empty or unrecognised output from any audit tool. npm exiting 1 because it found vulnerabilities still counts as a successful run.
73
+ - govulncheck's multi-line JSON output is now parsed, and a vulnerability is reported only when govulncheck returns a finding for it. The title says whether the vulnerable code is called, only imported, or only required.
74
+ - pip-audit now audits each `requirements*.txt` (`-r <file>`, at most 10), or a PEP 621 `pyproject.toml` as a project path. It no longer audits whichever Python environment is active. With nothing to audit, pip-audit is reported as `unavailable` with the reason "no requirements file to audit".
75
+ - `athena security` scans the project files for secrets on every run. Before, it took the secret count from the cached `model.json`, so the count could be out of date.
76
+ - Rewrote the secret patterns to remove catastrophic backtracking (ReDoS). `generic-secret-assignment` took over 1 s on 40 KB of crafted input. It now finds a bounded quoted value first, then checks the identifier before it (at most 64 chars) for a keyword. Private keys are matched by finding the BEGIN header and then the END within 16 KB. Open-ended token patterns now use bounded or lookbehind-anchored matches. Generic keyword patterns skip lines longer than 4096 characters. CI checks every pattern for time on crafted input and with the `recheck` ReDoS analyzer.
77
+ - `security.md`, which is committed, no longer lists a fingerprint for each secret. The old value was an unsalted, truncated SHA-256 of the secret, so anyone could test guessed values against it offline. Secret fingerprints in `model.json` are now an HMAC keyed with the per-machine salt in `.athena/local.json` (gitignored), or with a random per-process key.
78
+
3
79
  ## 0.2.0 — 2026-09-26
4
80
 
5
81
  - **Website link**: the terminal header, footer and next steps now link to https://athena.sabari.me, which is also the package homepage.
package/README.md CHANGED
@@ -104,6 +104,7 @@ Apply updates to 2 documents? [y/N]
104
104
  - **Your edits are still safe.** Developer-edited sections are preserved and listed. Applying is refused if a document changed after the proposal was made.
105
105
  - When files change but no knowledge is affected, only the local index is refreshed.
106
106
  - `athena sync --check` exits with 1 when knowledge is out of date, which is useful in CI or a pre-commit hook (see [Use in CI and git hooks](#use-in-ci-and-git-hooks)). Change hotspots come from Git history and move with every commit, so the check ignores them; `athena sync` still refreshes them.
107
+ - `athena sync --check --staged` checks what a commit would contain instead of the working tree: the `.athena/` knowledge as staged against the code as staged. Untracked files and unstaged edits don't count. This is what the pre-commit hook runs.
107
108
  - Ignore rules: defaults, root and nested `.gitignore` files, `.git/info/exclude`, and `.athena/config.json`.
108
109
 
109
110
  ## Watching what agents do
@@ -192,16 +193,22 @@ A complete workflow is in [examples/github-workflow.yml](examples/github-workflo
192
193
  ```text
193
194
  $ athena git-hook install
194
195
  ✓ Installed the pre-commit hook (knowledge check) at .git/hooks/pre-commit
195
- Commits are blocked when .athena/ knowledge is out of date. Teammates without Athena installed are not blocked.
196
- Using husky, lefthook or pre-commit? Add `npx --no-install athena sync --check` to its pre-commit config instead.
196
+ Commits are blocked when the staged .athena/ knowledge is out of date for the staged code (untracked and unstaged files are ignored). Teammates without Athena installed are not blocked.
197
+ Using husky, lefthook or pre-commit? Add `npx --no-install athena sync --check --staged` to its pre-commit config instead.
197
198
 
198
199
  $ git commit -m "Add refunds"
199
- Proposed: api.md, security.md
200
- Knowledge is out of date. Run `athena sync` to review and apply.
201
- athena: commit blocked: .athena/ knowledge is out of date. Run `athena sync`, stage .athena/ and commit again (or skip once with `git commit --no-verify`).
200
+ ~ api.md endpoints
201
+ ~ security.md attack-surface
202
+ Staged knowledge is out of date for the staged code. Run `athena sync`, then stage .athena/.
203
+ athena: commit blocked: staged .athena/ knowledge is out of date. Run `athena sync`, stage .athena/ and commit again (or skip once with `git commit --no-verify`).
202
204
  ```
203
205
 
204
- - `athena git-hook install --review` also runs `athena review` and blocks commits that add possible secrets or env files. It checks the whole working tree (staged, unstaged and untracked files that aren't ignored), not only what is staged.
206
+ - The hook checks the commit, not the working tree: an untracked scratch file, a local `.env` or an unstaged edit never blocks a commit, and a change you staged and then reverted in the working tree still counts. If `athena sync` already updated a document but it isn't staged, the output says which files to `git add`.
207
+ - The check mirrors the index into `.athena/cache/staged/` (gitignored). Files that match the working tree are stand-ins that reuse the last analysis and the facts cache, so only staged changes are read; the directory is updated incrementally and is safe to delete. Submodules are checked as they are in the working tree. Local env files that aren't committed (such as `.env`) are machine-local: the check accepts staged knowledge written with or without them.
208
+ - Knowledge generated by `athena sync` describes the working tree. If untracked files that are not gitignored change it (a new route in a file you don't commit), the staged check reports the difference; commit, ignore or stash those files before syncing.
209
+ - Hooks installed by earlier versions check the working tree; `athena git-hook status` points them out and `athena git-hook install` updates them.
210
+
211
+ - `athena git-hook install --review` also runs `athena review --staged` and blocks commits that add possible secrets or env files. Like the knowledge check, it only looks at what is staged.
205
212
  - The hook is a `#!/bin/sh` script in the directory Git actually uses (`git rev-parse --git-path hooks`), so `core.hooksPath` and linked worktrees work. For a project in a subfolder of the repository, the hook runs Athena there.
206
213
  - If `athena` is not on `PATH`, the hook prints a note and lets the commit through, so it never blocks teammates who don't use Athena.
207
214
  - **An existing pre-commit hook is kept.** If it is a shell script, Athena adds its block between `# athena:start` and `# athena:end` right after the shebang, so it runs before your own commands. Athena refuses to touch hooks written in other languages and hooks it recognizes as managed by husky, lefthook or pre-commit (from `core.hooksPath` or the hook's contents), and prints the line to add instead.
@@ -211,7 +218,7 @@ athena: commit blocked: .athena/ knowledge is out of date. Run `athena sync`, st
211
218
 
212
219
  ```bash
213
220
  # .husky/pre-commit
214
- npx --no-install athena sync --check
221
+ npx --no-install athena sync --check --staged
215
222
  ```
216
223
 
217
224
  ```yaml
@@ -219,7 +226,7 @@ npx --no-install athena sync --check
219
226
  pre-commit:
220
227
  commands:
221
228
  athena:
222
- run: npx --no-install athena sync --check
229
+ run: npx --no-install athena sync --check --staged
223
230
  ```
224
231
 
225
232
  ### Example project
@@ -246,7 +253,7 @@ Documents to read
246
253
 
247
254
  **MCP.** `athena mcp` serves this to any MCP-capable agent over stdio, and `athena agents add` registers it (`.mcp.json` for Claude Code, `.cursor/mcp.json` for Cursor, `[mcp_servers.athena]` in `.codex/config.toml` for Codex, `servers.athena` in `.vscode/mcp.json` for GitHub Copilot in VS Code, `mcpServers.athena` in `.gemini/settings.json` for Gemini CLI). Tools: `get_relevant_context`, `get_project_context`, `get_architecture`, `get_database_schema`, `get_api_context`, `get_security_context`, `get_project_rules`, `get_knowledge_document`, `get_project_changes`, `get_project_graph`, `get_athena_status`, `update_knowledge`.
248
255
 
249
- The server is **read-only by default**: `update_knowledge` reports what would change and refuses to write unless you start it with `--allow-write`.
256
+ The server is **read-only by default**: `update_knowledge` reports what would change and refuses to write unless you start it with `--allow-write`. Repository content it returns is passed through the secret redactor and wrapped in `<athena-document … trust="untrusted-data">` delimiters, so agents can tell project data from instructions.
250
257
 
251
258
  ## Optional AI
252
259
 
@@ -258,8 +265,9 @@ athena ai enrich --dry-run # show exactly what would be sent
258
265
  athena ai enrich --consent # ask for suggestions
259
266
  ```
260
267
 
261
- - **Providers:** Anthropic, OpenAI, Google, and Ollama for a fully local setup. Configure in `.athena/config.json` under `"ai"`; API keys come from environment variables only and are never written to disk by Athena.
262
- - **Consent first.** Nothing leaves your machine without `--consent` (or `"consent": true`). `--dry-run` prints the provider, endpoint, documents and exact size first.
268
+ - **Providers:** Anthropic, OpenAI, Google, and Ollama for a fully local setup. Choose `provider`, `model` and `maxChars` in `.athena/config.json` under `"ai"`; API keys come from environment variables only and are never written to disk by Athena.
269
+ - **Trusted settings stay on your machine.** `.athena/config.json` is committed, so anyone who can push to the repository controls it. The two settings that decide where your API key and knowledge go — `baseUrl` (a custom endpoint or remote Ollama) and `consent` — are therefore only read from machine-local sources, in this order: environment variables `ATHENA_AI_BASE_URL` and `ATHENA_AI_CONSENT=1`; `.athena/local.json` (gitignored), e.g. `{ "ai": { "baseUrl": "https://proxy.internal", "consent": true } }`; your user config at `~/.config/athena/config.json` (`$XDG_CONFIG_HOME/athena/config.json`, or `%APPDATA%\athena\config.json` on Windows) with the same `"ai"` shape. If `.athena/config.json` sets them, Athena ignores them and says so in `athena ai status`, `athena ai enrich` and `athena doctor`.
270
+ - **Consent first.** Nothing leaves your machine without consent: `athena ai enrich --consent`, `ATHENA_AI_CONSENT=1`, or `"consent": true` in `.athena/local.json` or your user config. An Ollama endpoint that isn't `localhost`/`127.0.0.1`/`::1` counts as remote and needs consent too. `--dry-run` prints the provider, endpoint, documents and exact size first.
263
271
  - **Knowledge only, redacted.** Athena sends your `.athena` documents — never source code — after the secret redactor, and refuses to send anything that still looks like a secret.
264
272
  - **Output is INFERRED.** Suggestions land in `.athena/ai-suggestions.md` (gitignored), clearly labeled and attributed to the model. Nothing is added to your knowledge base automatically.
265
273
 
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ listAgents
4
+ } from "./chunk-JY6BFIJJ.js";
5
+ import {
6
+ readRecentEvents,
7
+ summarizeSessions
8
+ } from "./chunk-65TSRDQ3.js";
9
+ import {
10
+ c,
11
+ dim,
12
+ heading,
13
+ isJson,
14
+ json,
15
+ line,
16
+ relativeTime,
17
+ sym
18
+ } from "./chunk-Z6QWMJLK.js";
19
+ import {
20
+ requireProjectRoot
21
+ } from "./chunk-XFN7UZCS.js";
22
+ import "./chunk-KBO7XRBW.js";
23
+ import "./chunk-JCGDR4UV.js";
24
+ import "./chunk-2RS6TA3D.js";
25
+ import "./chunk-GHFRQAYT.js";
26
+ import "./chunk-673EPYYH.js";
27
+ import "./chunk-FQEGEVLY.js";
28
+ import "./chunk-2DY4PUR4.js";
29
+ import "./chunk-IQQBAWSM.js";
30
+ import "./chunk-AD4OZCU2.js";
31
+ import "./chunk-INT3IGMQ.js";
32
+ import "./chunk-XW3MLHI2.js";
33
+ import "./chunk-EUPETUWA.js";
34
+
35
+ // src/cli/commands/activity.ts
36
+ var STATE_COLOR = {
37
+ ANALYZING: c.cyan,
38
+ PLANNING: c.magenta,
39
+ CODING: c.blue,
40
+ TESTING: c.yellow,
41
+ REVIEWING: c.cyan,
42
+ SUCCESS: c.green,
43
+ IDLE: c.dim
44
+ };
45
+ async function activityCommand(opts) {
46
+ const root = await requireProjectRoot(opts);
47
+ const limit = Math.min(Math.max(Number(opts.limit ?? 30) || 30, 1), 500);
48
+ const all = await readRecentEvents(root, 500);
49
+ const events = (opts.agent ? all.filter((e) => e.agent === opts.agent) : all).slice(-limit);
50
+ if (isJson()) {
51
+ json({ events, sessions: summarizeSessions(all) });
52
+ return;
53
+ }
54
+ heading("AI Agent Activity");
55
+ line();
56
+ if (!events.length) {
57
+ const agents = await listAgents(root);
58
+ const reporting = agents.filter((a) => a.activityObservation === "hooks");
59
+ line(dim("No agent activity recorded yet."));
60
+ line(dim(reporting.length ? `Hooks are installed for: ${reporting.map((a) => a.name).join(", ")}. Events appear once an agent runs in this project.` : "Install hooks with `athena agents add claude-code` (or cursor, codex, copilot, gemini-cli) to record agent activity."));
61
+ return;
62
+ }
63
+ for (const e of events) {
64
+ const time = new Date(e.ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false });
65
+ const color = STATE_COLOR[e.state] ?? ((s) => s);
66
+ line(`${dim(time)} ${color(sym.dot)} ${c.bold(e.agent)} ${dim("\xB7")} ${e.message}`);
67
+ }
68
+ line();
69
+ for (const s of summarizeSessions(all).slice(0, 5)) {
70
+ line(dim(`${s.agent} ${s.session ? `(${s.session.slice(0, 8)})` : ""}: ${s.events} events, last ${relativeTime(s.lastEventAt)}`));
71
+ }
72
+ line(dim("Only hook-reported tool use is shown. Athena cannot see agent reasoning."));
73
+ }
74
+ export {
75
+ activityCommand
76
+ };
77
+ //# sourceMappingURL=activity-P4UUFHWV.js.map
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ configureAgents,
4
+ listAgents,
5
+ removeAgents
6
+ } from "./chunk-JY6BFIJJ.js";
7
+ import "./chunk-65TSRDQ3.js";
8
+ import {
9
+ c,
10
+ dim,
11
+ heading,
12
+ isJson,
13
+ json,
14
+ line,
15
+ ok,
16
+ sym
17
+ } from "./chunk-Z6QWMJLK.js";
18
+ import {
19
+ requireProjectRoot
20
+ } from "./chunk-XFN7UZCS.js";
21
+ import "./chunk-KBO7XRBW.js";
22
+ import "./chunk-JCGDR4UV.js";
23
+ import "./chunk-2RS6TA3D.js";
24
+ import "./chunk-GHFRQAYT.js";
25
+ import "./chunk-673EPYYH.js";
26
+ import "./chunk-FQEGEVLY.js";
27
+ import "./chunk-2DY4PUR4.js";
28
+ import "./chunk-IQQBAWSM.js";
29
+ import "./chunk-AD4OZCU2.js";
30
+ import "./chunk-INT3IGMQ.js";
31
+ import "./chunk-XW3MLHI2.js";
32
+ import "./chunk-EUPETUWA.js";
33
+
34
+ // src/cli/commands/agents.ts
35
+ async function agentsListCommand(opts) {
36
+ const root = await requireProjectRoot(opts);
37
+ const rows = await listAgents(root);
38
+ if (isJson()) {
39
+ json(rows);
40
+ return;
41
+ }
42
+ heading("AI Agent Integrations");
43
+ line();
44
+ for (const r of rows) {
45
+ const icon = r.configured ? c.green(sym.dot) : c.dim(sym.ring);
46
+ line(`${icon} ${c.bold(r.name)} ${dim(`(${r.id})`)} \u2014 ${r.configured ? "configured" : "not configured"}${r.detectedInProject ? dim(` \xB7 detected: ${r.evidence.join(", ")}`) : ""}`);
47
+ line(` ${dim(r.note)}`);
48
+ }
49
+ line();
50
+ line(dim('"Configured" means Athena instruction files are in place. Agents with hooks also report their tool use \u2014 see `athena activity`.'));
51
+ }
52
+ async function agentsAddCommand(names, opts) {
53
+ const root = await requireProjectRoot(opts);
54
+ const results = await configureAgents(root, names);
55
+ if (isJson()) json({ ok: true, agents: results });
56
+ else for (const r of results) ok(`${r.id}: ${r.files.map((f) => `${f.path} (${f.action})`).join(", ")}`);
57
+ }
58
+ async function agentsRemoveCommand(names, opts) {
59
+ const root = await requireProjectRoot(opts);
60
+ const removed = await removeAgents(root, names);
61
+ if (isJson()) json({ ok: true, removed });
62
+ else ok(removed.length ? `Removed Athena integration from ${removed.join(", ")}` : "Nothing to remove");
63
+ }
64
+ export {
65
+ agentsAddCommand,
66
+ agentsListCommand,
67
+ agentsRemoveCommand
68
+ };
69
+ //# sourceMappingURL=agents-3HTSQGL2.js.map
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ aiStatus,
4
+ buildEnrichPayload,
5
+ enrich
6
+ } from "./chunk-RXNRMIZP.js";
7
+ import {
8
+ c,
9
+ dim,
10
+ heading,
11
+ isJson,
12
+ json,
13
+ line,
14
+ spinner,
15
+ sym,
16
+ warn
17
+ } from "./chunk-Z6QWMJLK.js";
18
+ import {
19
+ requireProjectRoot
20
+ } from "./chunk-XFN7UZCS.js";
21
+ import "./chunk-KBO7XRBW.js";
22
+ import "./chunk-PVCIGUIT.js";
23
+ import "./chunk-JCGDR4UV.js";
24
+ import "./chunk-FQEGEVLY.js";
25
+ import "./chunk-2DY4PUR4.js";
26
+ import "./chunk-IQQBAWSM.js";
27
+ import "./chunk-AD4OZCU2.js";
28
+ import "./chunk-EUPETUWA.js";
29
+
30
+ // src/cli/commands/ai.ts
31
+ var CONSENT_SOURCE = { env: "ATHENA_AI_CONSENT", local: ".athena/local.json", user: "user config" };
32
+ async function aiStatusCommand(opts) {
33
+ const root = await requireProjectRoot(opts);
34
+ const status = await aiStatus(root);
35
+ if (isJson()) {
36
+ json(status);
37
+ return;
38
+ }
39
+ heading("AI providers");
40
+ line();
41
+ for (const w of status.warnings) warn(c.yellow(w));
42
+ if (status.warnings.length) line();
43
+ for (const p of status.providers) {
44
+ const icon = p.availability.ok ? c.green(sym.ok) : c.dim(sym.ring);
45
+ line(`${icon} ${c.bold(p.name)}${p.selected ? c.cyan(" (selected)") : ""}`);
46
+ line(` ${dim(`${p.remote ? "sends data to" : "runs locally at"} ${p.availability.endpoint} \xB7 default model ${p.defaultModel}`)}`);
47
+ if (!p.availability.ok) line(` ${dim(p.availability.reason ?? "")}`);
48
+ }
49
+ line();
50
+ line(`Consent to send project knowledge: ${status.consent ? c.green(`granted (${CONSENT_SOURCE[status.consentSource ?? "env"]})`) : c.yellow("not granted")}`);
51
+ line(dim("Athena never needs AI: analysis, sync, context and MCP are deterministic. AI only produces INFERRED suggestions you review."));
52
+ line(dim('Configure in .athena/config.json \u2192 "ai": { "provider": "ollama" } for a fully local setup.'));
53
+ line(dim("Endpoint (baseUrl) and consent are machine-local only: ATHENA_AI_BASE_URL / ATHENA_AI_CONSENT=1, .athena/local.json, or ~/.config/athena/config.json."));
54
+ }
55
+ async function aiEnrichCommand(opts) {
56
+ const root = await requireProjectRoot(opts);
57
+ const docs = opts.docs?.split(",").map((d) => d.trim()).filter(Boolean);
58
+ const payload = await buildEnrichPayload(root, { docs });
59
+ if (!isJson()) {
60
+ heading("AI enrichment");
61
+ line();
62
+ for (const w of payload.warnings) warn(c.yellow(w));
63
+ if (payload.warnings.length) line();
64
+ line(`Provider: ${c.bold(payload.provider.name)} ${dim(`(${payload.model})`)}`);
65
+ line(`Sends to: ${payload.remote ? c.yellow(payload.endpoint) : c.green(`${payload.endpoint} (local)`)}`);
66
+ line(`Content: ${payload.documents.join(", ")} ${dim(`(${payload.chars} characters, redacted knowledge only \u2014 no source code)`)}`);
67
+ line();
68
+ }
69
+ if (opts.dryRun) {
70
+ if (isJson()) json({ ...payload, provider: payload.provider.id });
71
+ else line(dim("Dry run \u2014 nothing was sent."));
72
+ return 0;
73
+ }
74
+ const sp = spinner(`Asking ${payload.provider.name}...`);
75
+ let result;
76
+ try {
77
+ result = await enrich(root, { docs, consent: opts.consent, signal: opts.signal });
78
+ sp.succeed(`Suggestions written to .athena/${result.file}`);
79
+ } catch (err) {
80
+ sp.stop();
81
+ throw err;
82
+ }
83
+ if (isJson()) json(result);
84
+ else {
85
+ line();
86
+ line(result.markdown.split("\n").slice(0, 40).join("\n"));
87
+ line();
88
+ line(dim(`Full suggestions: .athena/${result.file} (gitignored). These are INFERRED \u2014 nothing was added to your knowledge base.`));
89
+ }
90
+ return 0;
91
+ }
92
+ export {
93
+ aiEnrichCommand,
94
+ aiStatusCommand
95
+ };
96
+ //# sourceMappingURL=ai-ASL4FFQ7.js.map
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseAgentsOption,
4
+ printSummary,
5
+ runWithProgress,
6
+ summarize
7
+ } from "./chunk-DOQMAZL5.js";
8
+ import {
9
+ printHeader
10
+ } from "./chunk-SHKXELUX.js";
11
+ import {
12
+ isJson,
13
+ json
14
+ } from "./chunk-Z6QWMJLK.js";
15
+ import {
16
+ requireProjectRoot
17
+ } from "./chunk-XFN7UZCS.js";
18
+ import "./chunk-KBO7XRBW.js";
19
+ import "./chunk-SJOOXO34.js";
20
+ import "./chunk-PANVPNYF.js";
21
+ import "./chunk-ELKFRTZT.js";
22
+ import "./chunk-2R6PXY6Z.js";
23
+ import "./chunk-PVCIGUIT.js";
24
+ import "./chunk-OLPSEQS5.js";
25
+ import "./chunk-JCGDR4UV.js";
26
+ import "./chunk-2RS6TA3D.js";
27
+ import "./chunk-GHFRQAYT.js";
28
+ import "./chunk-673EPYYH.js";
29
+ import "./chunk-FQEGEVLY.js";
30
+ import "./chunk-2DY4PUR4.js";
31
+ import "./chunk-IQQBAWSM.js";
32
+ import "./chunk-AD4OZCU2.js";
33
+ import "./chunk-INT3IGMQ.js";
34
+ import "./chunk-XW3MLHI2.js";
35
+ import "./chunk-EUPETUWA.js";
36
+
37
+ // src/cli/commands/analyze.ts
38
+ async function analyzeCommand(opts) {
39
+ const root = await requireProjectRoot(opts);
40
+ const agents = parseAgentsOption(opts.agents, false);
41
+ printHeader();
42
+ const result = await runWithProgress(root, { mode: "analyze", force: opts.force, agents, signal: opts.signal });
43
+ if (isJson()) json(summarize(result, root, false));
44
+ else printSummary(result, false, "analyze");
45
+ }
46
+ export {
47
+ analyzeCommand
48
+ };
49
+ //# sourceMappingURL=analyze-F3YOILNL.js.map