project-athena 0.2.1 → 0.4.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 (152) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +61 -12
  3. package/dist/activity-BKW7G4I5.js +77 -0
  4. package/dist/agents-DFSNEOP5.js +69 -0
  5. package/dist/ai-ASL4FFQ7.js +96 -0
  6. package/dist/analyze-ZY3MXBOI.js +50 -0
  7. package/dist/chunk-2DY4PUR4.js +465 -0
  8. package/dist/chunk-2R6PXY6Z.js +33 -0
  9. package/dist/chunk-3IS2X2Z7.js +89 -0
  10. package/dist/chunk-3XSDI5WF.js +179 -0
  11. package/dist/chunk-65TSRDQ3.js +124 -0
  12. package/dist/chunk-673EPYYH.js +105 -0
  13. package/dist/chunk-7DZK6WSQ.js +65 -0
  14. package/dist/chunk-AD4OZCU2.js +34 -0
  15. package/dist/chunk-DZDI2HRT.js +169 -0
  16. package/dist/chunk-ELKFRTZT.js +221 -0
  17. package/dist/chunk-EUPETUWA.js +14 -0
  18. package/dist/chunk-FQEGEVLY.js +38 -0
  19. package/dist/chunk-FVCDMQUQ.js +343 -0
  20. package/dist/chunk-GGXPG6OE.js +904 -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-KBO7XRBW.js +26 -0
  27. package/dist/chunk-KHMBNT66.js +399 -0
  28. package/dist/chunk-LEMPCNAY.js +209 -0
  29. package/dist/chunk-MPG3IEGB.js +2691 -0
  30. package/dist/chunk-OLPSEQS5.js +264 -0
  31. package/dist/chunk-PVCIGUIT.js +64 -0
  32. package/dist/chunk-QCC4VJUH.js +217 -0
  33. package/dist/chunk-QLW657NT.js +95 -0
  34. package/dist/chunk-RVTNSNGK.js +182 -0
  35. package/dist/chunk-RXNRMIZP.js +390 -0
  36. package/dist/chunk-SHKXELUX.js +295 -0
  37. package/dist/chunk-SJ4DQFIJ.js +379 -0
  38. package/dist/chunk-SVTEJYMS.js +227 -0
  39. package/dist/chunk-TDNZ2RYA.js +702 -0
  40. package/dist/chunk-UAZLPNVC.js +108 -0
  41. package/dist/chunk-VSYBC3VY.js +28 -0
  42. package/dist/chunk-WVQC2EGO.js +166 -0
  43. package/dist/chunk-XDCKZ5TV.js +436 -0
  44. package/dist/chunk-XFN7UZCS.js +35 -0
  45. package/dist/chunk-XW3MLHI2.js +32 -0
  46. package/dist/chunk-YRIS7ZS6.js +919 -0
  47. package/dist/chunk-Z6QWMJLK.js +124 -0
  48. package/dist/chunk-ZB7LC6FP.js +221 -0
  49. package/dist/clean-MDBYH62P.js +23 -0
  50. package/dist/cli.js +18 -10716
  51. package/dist/context-PLPCAPCO.js +161 -0
  52. package/dist/doctor-BX677KRE.js +69 -0
  53. package/dist/event-TNT6CQZR.js +101 -0
  54. package/dist/git-hook-YZNZGAAP.js +252 -0
  55. package/dist/init-WQCAOJ2R.js +38 -0
  56. package/dist/mcp-U7EPH43L.js +393 -0
  57. package/dist/memory-VSVNLEJC.js +393 -0
  58. package/dist/open-UJZRYF4E.js +1450 -0
  59. package/dist/program-CWJNGCTZ.js +165 -0
  60. package/dist/review-OOKXSYMP.js +117 -0
  61. package/dist/rules-4FXJUCXB.js +83 -0
  62. package/dist/security-LME3XFL3.js +177 -0
  63. package/dist/staged-check-X24C3YRD.js +502 -0
  64. package/dist/status-RR57ZNGI.js +81 -0
  65. package/dist/sync-KFJANRXF.js +218 -0
  66. package/dist/watch-CY7QHIYN.js +123 -0
  67. package/dist/web/assets/{Editor-DAXvniWB.js → Editor-Bnilgch0.js} +1 -1
  68. package/dist/web/assets/{abnfDiagram-O67JEVCF-Az7EW1BC.js → abnfDiagram-O67JEVCF-DW9LWr8f.js} +1 -1
  69. package/dist/web/assets/architecture-7GRP2DOG-BWDZV2yn.js +1 -0
  70. package/dist/web/assets/{architectureDiagram-NJMV4G6O-BxeXqSTn.js → architectureDiagram-NJMV4G6O-0eKeQRhj.js} +1 -1
  71. package/dist/web/assets/{blockDiagram-BEXU5L5S-aeKZl9lJ.js → blockDiagram-BEXU5L5S-D5i3-ogI.js} +1 -1
  72. package/dist/web/assets/{c4Diagram-YGBWAQC7-CgmntYsG.js → c4Diagram-YGBWAQC7-BAWQZrEu.js} +1 -1
  73. package/dist/web/assets/channel-Bkh05NKd.js +1 -0
  74. package/dist/web/assets/{chunk-3FUC2YCW-BpmRD2yo.js → chunk-3FUC2YCW-fKRGOZF3.js} +1 -1
  75. package/dist/web/assets/{chunk-5DYCD2WN-CRIFDwod.js → chunk-5DYCD2WN-C5jOlx42.js} +1 -1
  76. package/dist/web/assets/{chunk-742MDFTN-hRW9VeNV.js → chunk-742MDFTN-urY4RP_A.js} +1 -1
  77. package/dist/web/assets/{chunk-7INBJB4K-C6VEpT0Y.js → chunk-7INBJB4K-nk48sKqZ.js} +1 -1
  78. package/dist/web/assets/{chunk-7M6MHVWA-Dnajov2w.js → chunk-7M6MHVWA-BfBD9X0F.js} +1 -1
  79. package/dist/web/assets/{chunk-7PRAP22T-DSAc0_CZ.js → chunk-7PRAP22T-jYf59QlI.js} +1 -1
  80. package/dist/web/assets/{chunk-GTNCS2PH-CefpYUjb.js → chunk-GTNCS2PH-BOCxryKf.js} +1 -1
  81. package/dist/web/assets/{chunk-GWA4HPMP-MLNflMmF.js → chunk-GWA4HPMP-CFpaW0ru.js} +1 -1
  82. package/dist/web/assets/{chunk-MBY4JIJT-D3GmmEiU.js → chunk-MBY4JIJT-BIB8HctN.js} +1 -1
  83. package/dist/web/assets/{chunk-NETBCI7D-DzTj6xVk.js → chunk-NETBCI7D-CpUUK6rh.js} +1 -1
  84. package/dist/web/assets/{chunk-O7XYJQB3-D1d0hCko.js → chunk-O7XYJQB3-BNx_V4Oi.js} +1 -1
  85. package/dist/web/assets/{chunk-UA2S7LBM-C9hIFeWR.js → chunk-UA2S7LBM-D3tMiIOY.js} +1 -1
  86. package/dist/web/assets/{chunk-WEXAMYUT-BFGtcjsi.js → chunk-WEXAMYUT-CC-3C32l.js} +1 -1
  87. package/dist/web/assets/{chunk-Z7XXMR3K-BRZ8_wtE.js → chunk-Z7XXMR3K-BzhxKQUc.js} +1 -1
  88. package/dist/web/assets/{chunk-ZIGJFQKS-CTKOioal.js → chunk-ZIGJFQKS-D4tBHzxg.js} +1 -1
  89. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-DJGKtWJv.js → classDiagram-v2-NBCMYWYE-BbtyanXR.js} +1 -1
  90. package/dist/web/assets/{cynefin-OW5HDTMX-hNq1kGmA.js → cynefin-OW5HDTMX-BIArb8W1.js} +1 -1
  91. package/dist/web/assets/{cynefinDiagram-VND7K2PF-sODQM_ZZ.js → cynefinDiagram-VND7K2PF-BrAcfkXL.js} +1 -1
  92. package/dist/web/assets/{dagre-6A5THRUB-DEzTThIf.js → dagre-6A5THRUB-BcHXRl4s.js} +1 -1
  93. package/dist/web/assets/{diagram-22UHCM2B-s_eellsa.js → diagram-22UHCM2B-Dx9tSCdE.js} +1 -1
  94. package/dist/web/assets/{diagram-3UASUU5V-B2onY_Xw.js → diagram-3UASUU5V-CLLDP9yH.js} +1 -1
  95. package/dist/web/assets/{diagram-ATOU4E4O-B8V6s13s.js → diagram-ATOU4E4O-B46H9iPt.js} +1 -1
  96. package/dist/web/assets/{diagram-CDSNMT55-CJxiE6mf.js → diagram-CDSNMT55-CYtvB6aG.js} +1 -1
  97. package/dist/web/assets/{diagram-MLGK6HIB-m5OHPLAp.js → diagram-MLGK6HIB-Dar7ktoX.js} +1 -1
  98. package/dist/web/assets/{diagram-MPIPVDR6-Buvvrk_A.js → diagram-MPIPVDR6-C-DljnYN.js} +1 -1
  99. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-CpL7Wt3R.js → ebnfDiagram-ZINNZB2B-Dv_3jWnh.js} +1 -1
  100. package/dist/web/assets/{elk-276RUBZZ-CwYhbsXa.js → elk-276RUBZZ-DnHciE9k.js} +1 -1
  101. package/dist/web/assets/{erDiagram-OPXOYQCR-DNikci2r.js → erDiagram-OPXOYQCR-Bn46KrHz.js} +1 -1
  102. package/dist/web/assets/eventmodeling-NTZA5JFV-DYwoptMd.js +1 -0
  103. package/dist/web/assets/flowDiagram-KWPJA3E3-y4rS0gIB.js +1 -0
  104. package/dist/web/assets/{ganttDiagram-FUAMR5RP-d-ynDlvJ.js → ganttDiagram-FUAMR5RP-DOjjJtqz.js} +1 -1
  105. package/dist/web/assets/{gitGraph-4MIJSDKK-DwIeBI91.js → gitGraph-4MIJSDKK-BIoMXJRQ.js} +1 -1
  106. package/dist/web/assets/{gitGraphDiagram-X574FWY7-04RZccu6.js → gitGraphDiagram-X574FWY7-BsJ01R0v.js} +1 -1
  107. package/dist/web/assets/{index-C3-8uYXr.js → index-Cen0jOXj.js} +5 -4
  108. package/dist/web/assets/{index-DKE3ojTe.css → index-WwRY6n68.css} +1 -1
  109. package/dist/web/assets/{info-A6RAGUB7-xf4Nzgv-.js → info-A6RAGUB7-BmlXzRWj.js} +1 -1
  110. package/dist/web/assets/{infoDiagram-VRGFBTTK-BYjw0eiK.js → infoDiagram-VRGFBTTK-CYPJPmBP.js} +1 -1
  111. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-Bx9s4nPM.js → ishikawaDiagram-OU5B5YK6-YN96geky.js} +1 -1
  112. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-DrdHs7B-.js → journeyDiagram-ZHPQQLJL-20juojKY.js} +1 -1
  113. package/dist/web/assets/{kanban-definition-PNTS6WVX-CVd8v2Te.js → kanban-definition-PNTS6WVX-BhCKIEbP.js} +1 -1
  114. package/dist/web/assets/{line-V_WrJ-6y.js → line-BERsMFG6.js} +1 -1
  115. package/dist/web/assets/{mermaid-parser.core-B-ilyEpO.js → mermaid-parser.core-Bis_wTB-.js} +3 -3
  116. package/dist/web/assets/{mermaid.core-Bnyaa0nF.js → mermaid.core-DMsM3ZX7.js} +4 -4
  117. package/dist/web/assets/{mindmap-definition-NLK3R4M7-B3AbvHPR.js → mindmap-definition-NLK3R4M7-BkcGdOfv.js} +1 -1
  118. package/dist/web/assets/{packet-AYTQ26CC-CBAIUXIc.js → packet-AYTQ26CC-Bh30-JJX.js} +1 -1
  119. package/dist/web/assets/{pegDiagram-GJSIUBJH-DIdeELUM.js → pegDiagram-GJSIUBJH-DxnIo9RF.js} +1 -1
  120. package/dist/web/assets/{pie-WAS4IAKB-a1r3tzPR.js → pie-WAS4IAKB-ChaTr-GD.js} +1 -1
  121. package/dist/web/assets/{pieDiagram-5QR66LMP-pQBEio0x.js → pieDiagram-5QR66LMP-CzAp78FM.js} +1 -1
  122. package/dist/web/assets/{quadrantDiagram-O4NWA36T-CkJK5Y03.js → quadrantDiagram-O4NWA36T-BDSE-cfY.js} +1 -1
  123. package/dist/web/assets/{radar-RG4KPBEZ-BZfLYjx5.js → radar-RG4KPBEZ-DgJ-o_dq.js} +1 -1
  124. package/dist/web/assets/{railroad-74A4TZTK-CuIW1ZAU.js → railroad-74A4TZTK-C8MmcBKz.js} +1 -1
  125. package/dist/web/assets/railroad-abnf-HS5TGJTU-CTLXjcdP.js +1 -0
  126. package/dist/web/assets/railroad-ebnf-LZEXJU2U-ggWsTTL0.js +1 -0
  127. package/dist/web/assets/railroad-peg-WCYAUIDC-D1mCX705.js +1 -0
  128. package/dist/web/assets/{railroadDiagram-XR7U4H2S-_BpGE8aJ.js → railroadDiagram-XR7U4H2S-DfbcWRNh.js} +1 -1
  129. package/dist/web/assets/{requirementDiagram-PLB6GJNP-CnzF90GS.js → requirementDiagram-PLB6GJNP-DM3nksxs.js} +1 -1
  130. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-DYeisDj_.js → sankeyDiagram-IPEJSGJF-DthUpiiC.js} +1 -1
  131. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-ll9cBvca.js → sequenceDiagram-PO4LG4MO-DLA3nuvA.js} +1 -1
  132. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-CdKcOIKQ.js → stateDiagram-v2-GCMORJYK-B0X6ihBW.js} +1 -1
  133. package/dist/web/assets/{swimlanes-2SLR337P-y6piTWEW.js → swimlanes-2SLR337P-B4qWe6vE.js} +1 -1
  134. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-ClIk3e7S.js +8 -0
  135. package/dist/web/assets/{timeline-definition-EJHVYXUP-DuC_4xK0.js → timeline-definition-EJHVYXUP-F-cDzYrj.js} +1 -1
  136. package/dist/web/assets/{treeView-Q6P3EWNA-gCQFmND0.js → treeView-Q6P3EWNA-BaI30nLZ.js} +1 -1
  137. package/dist/web/assets/{treemap-WGGIJYW6-BQ2010OY.js → treemap-WGGIJYW6-D2jGGoMl.js} +1 -1
  138. package/dist/web/assets/{usecaseDiagram-POWQR4AR-CCTqzHz6.js → usecaseDiagram-POWQR4AR-B9qoAQCy.js} +1 -1
  139. package/dist/web/assets/{vennDiagram-UO4OBE2U-0_O7guNq.js → vennDiagram-UO4OBE2U-DGVNOaKO.js} +1 -1
  140. package/dist/web/assets/{wardley-WFR3VGLG-SsX7uMDs.js → wardley-WFR3VGLG-DbKqhNAh.js} +1 -1
  141. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-DlqH6Hox.js → wardleyDiagram-VNRHLVJA-BgZ1gprw.js} +1 -1
  142. package/dist/web/assets/{xychartDiagram-PMCCYNJV-nr4UYDtd.js → xychartDiagram-PMCCYNJV-D73RYid1.js} +1 -1
  143. package/dist/web/index.html +2 -2
  144. package/package.json +4 -2
  145. package/dist/web/assets/architecture-7GRP2DOG-blXBarB2.js +0 -1
  146. package/dist/web/assets/channel-f4QzewnN.js +0 -1
  147. package/dist/web/assets/eventmodeling-NTZA5JFV-CA_ZFPZ2.js +0 -1
  148. package/dist/web/assets/flowDiagram-KWPJA3E3-DObsBuWT.js +0 -1
  149. package/dist/web/assets/railroad-abnf-HS5TGJTU-6B4Ffbxn.js +0 -1
  150. package/dist/web/assets/railroad-ebnf-LZEXJU2U-BGL9lIQc.js +0 -1
  151. package/dist/web/assets/railroad-peg-WCYAUIDC-DcqxTem7.js +0 -1
  152. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-DFS8a3jO.js +0 -8
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ c,
4
+ dim,
5
+ getInterruptMessage,
6
+ isJson,
7
+ json,
8
+ line,
9
+ setOutputMode,
10
+ sym
11
+ } from "./chunk-Z6QWMJLK.js";
12
+ import {
13
+ ATHENA_VERSION
14
+ } from "./chunk-2R6PXY6Z.js";
15
+ import {
16
+ AthenaError,
17
+ EXIT
18
+ } from "./chunk-AD4OZCU2.js";
19
+
20
+ // src/cli/program.ts
21
+ import { Command, Option } from "commander";
22
+ var controller = new AbortController();
23
+ var interrupted = false;
24
+ function onSignal() {
25
+ if (interrupted) process.exit(EXIT.INTERRUPTED);
26
+ interrupted = true;
27
+ controller.abort(new Error("Interrupted"));
28
+ if (process.stdout.isTTY) process.stdout.write("\x1B[?25h");
29
+ if (!isJson()) process.stderr.write(`
30
+ ${c.yellow("Interrupted.")} ${dim(getInterruptMessage())}
31
+ `);
32
+ }
33
+ process.stdout.on("error", (err) => {
34
+ if (err.code === "EPIPE") {
35
+ if (process.env.ATHENA_DEBUG) process.stderr.write("athena: stdout was closed by the reader (EPIPE); exiting quietly\n");
36
+ process.exit(0);
37
+ }
38
+ throw err;
39
+ });
40
+ process.on("SIGINT", onSignal);
41
+ process.on("SIGTERM", onSignal);
42
+ var PLANNED = [];
43
+ function globals(cmd) {
44
+ return cmd.optsWithGlobals();
45
+ }
46
+ function run(fn) {
47
+ return async (...args) => {
48
+ const cmd = args[args.length - 1];
49
+ const g = globals(cmd);
50
+ setOutputMode({ json: g.json, quiet: g.quiet });
51
+ const code = await fn(...args);
52
+ if (typeof code === "number") process.exitCode = code;
53
+ };
54
+ }
55
+ function buildProgram() {
56
+ const program = new Command();
57
+ program.name("athena").description("Athena \u2014 local-first project intelligence for AI coding agents").version(ATHENA_VERSION, "-v, --version", "Print the Athena version").addOption(new Option("-C, --cwd <dir>", "Run as if started in <dir>")).option("--json", "Machine-readable JSON output").option("-q, --quiet", "Only print warnings, errors and results").option("--no-color", "Disable colors (also respects NO_COLOR)").showHelpAfterError("(run `athena --help` for usage)").configureHelp({ sortSubcommands: false });
58
+ program.command("init").description("Analyze the project and create .athena/ knowledge").option("--agents <list>", 'Agents to configure: comma-separated ids (claude-code, cursor, codex, copilot, gemini-cli, antigravity, windsurf, cline, agents-md), "all" or "none". Default: detected agents + AGENTS.md').option("--no-agents", "Do not configure any agent integration").option("--dry-run", "Analyze and show what would be written, without writing").action(
59
+ run(async (cmdOpts, cmd) => {
60
+ const g = globals(cmd);
61
+ await (await import("./init-WQCAOJ2R.js")).initCommand({ ...g, agents: typeof cmdOpts.agents === "string" ? cmdOpts.agents : void 0, noAgents: cmdOpts.agents === false, dryRun: cmdOpts.dryRun, signal: controller.signal });
62
+ })
63
+ );
64
+ program.command("analyze").description("Re-analyze the project and refresh generated knowledge (developer edits are preserved)").option("--force", "Regenerate sections even if they were edited by hand").option("--agents <list>", "Reconfigure these agent integrations").action(run(async (o, cmd) => (await import("./analyze-ZY3MXBOI.js")).analyzeCommand({ ...globals(cmd), ...o, signal: controller.signal })));
65
+ program.command("sync").description("Review and apply knowledge updates for changes since the last analysis").option("-y, --yes", "Apply without asking").option("--dry-run", "Show the plan without writing").option("--check", "Exit with code 1 if knowledge is out of date (for CI/hooks)").option("--staged", "With --check: check the staged knowledge against the staged code (the index), ignoring untracked and unstaged changes").option("--diff", "Print full diffs of proposed document changes").option("--force", "Also regenerate sections that were edited by hand").action(run(async (o, cmd) => (await import("./sync-KFJANRXF.js")).syncCommand({ ...globals(cmd), ...o, signal: controller.signal })));
66
+ program.command("watch").description("Watch the project and propose knowledge updates as files change").option("--auto-apply", "Apply updates automatically instead of proposing them").option("--debounce <ms>", "Quiet period before checking changes (default 1500)").action(run(async (o, cmd) => (await import("./watch-CY7QHIYN.js")).watchCommand({ ...globals(cmd), ...o, signal: controller.signal })));
67
+ program.command("context <task>").description("Show which knowledge an agent should read for a task (deterministic)").option("--full", "Print the full context an agent would receive").option("--max-chars <n>", "Character budget for selected sections").action(run(async (task, o, cmd) => (await import("./context-PLPCAPCO.js")).contextCommand(task, { ...globals(cmd), ...o, signal: controller.signal })));
68
+ program.command("graph").description("Inspect the project graph (packages, files, routes, entities and their relationships)").option("--build", "Rebuild the graph from the current code").option("--node <id>", "Show one node and its relationships").option("--kind <kind>", "Filter by node kind (package, file, route, entity, framework, command, infra)").option("--search <term>", "Filter nodes by name or path").action(run(async (o, cmd) => (await import("./context-PLPCAPCO.js")).graphCommand({ ...globals(cmd), ...o, signal: controller.signal })));
69
+ program.command("architecture").description("Summarize the project graph").action(run(async (_o, cmd) => (await import("./context-PLPCAPCO.js")).architectureCommand({ ...globals(cmd), signal: controller.signal })));
70
+ program.command("mcp").description("Serve Athena project intelligence over MCP (stdio) for AI agents").option("--allow-write", "Permit the update_knowledge tool to apply changes (read-only by default)").option("--no-memory-write", "Disable the remember tool (agents cannot record unreviewed memories)").action(run(async (o, cmd) => (await import("./mcp-U7EPH43L.js")).mcpCommand({ ...globals(cmd), ...o, signal: controller.signal })));
71
+ const ai = program.command("ai").description("Optional AI provider integration (never required)");
72
+ ai.command("status", { isDefault: true }).description("Show configured and available AI providers").action(run(async (_o, cmd) => (await import("./ai-ASL4FFQ7.js")).aiStatusCommand(globals(cmd))));
73
+ ai.command("enrich").description("Ask the configured provider for INFERRED suggestions based on your knowledge documents").option("--docs <list>", "Documents to send (default: project,architecture,database,api)").option("--consent", "Consent to sending redacted knowledge to a remote provider for this run").option("--dry-run", "Show exactly what would be sent, and send nothing").action(run(async (o, cmd) => (await import("./ai-ASL4FFQ7.js")).aiEnrichCommand({ ...globals(cmd), ...o, signal: controller.signal })));
74
+ program.command("security").description("Scan dependencies with the audit tools installed for this project, and report secret findings").option("--no-audit", "Skip dependency audits (report secrets only)").option("--fail-on <severity>", "Exit 1 when a finding at or above this severity exists (critical, high, moderate, low)").option("--last", "Show the last scan instead of running a new one").option("--unrated <policy>", 'With --fail-on: "fail" (default) also fails on findings the tool did not rate (pip-audit, govulncheck); "warn" only reports them', "fail").action(run(async (o, cmd) => (await import("./security-LME3XFL3.js")).securityCommand({ ...globals(cmd), noAudit: o.audit === false, failOn: o.failOn, unrated: o.unrated, last: o.last, signal: controller.signal })));
75
+ program.command("review").description("Check the current diff for facts worth reviewing, and list the project rules and checklist").option("--base <ref>", "Compare against this Git ref instead of the working tree").option("--staged", "Review only what is staged for commit (used by the pre-commit hook)").option("--no-sync", "Skip the knowledge freshness check").option("--no-fail", "Always exit 0, even when blockers are found").action(run(async (o, cmd) => (await import("./review-OOKXSYMP.js")).reviewCommand({ ...globals(cmd), base: o.base, staged: o.staged, noSync: o.sync === false, noFail: o.fail === false, signal: controller.signal })));
76
+ program.command("status").description("Show knowledge health and changes since the last analysis").action(run(async (_o, cmd) => (await import("./status-RR57ZNGI.js")).statusCommand({ ...globals(cmd), signal: controller.signal })));
77
+ program.command("doctor").description("Check the Athena installation, project and integrations").action(run(async (_o, cmd) => (await import("./doctor-BX677KRE.js")).doctorCommand(globals(cmd))));
78
+ const rules = program.command("rules").description("List and edit project rules (.athena/rules.md)");
79
+ rules.command("list", { isDefault: true }).description("List rules with their numbers").action(run(async (_o, cmd) => (await import("./rules-4FXJUCXB.js")).rulesListCommand(globals(cmd))));
80
+ rules.command("add <text>").description("Add a rule").option("-s, --section <name>", "Section heading", "General").action(run(async (text, o, cmd) => (await import("./rules-4FXJUCXB.js")).rulesAddCommand(text, { ...globals(cmd), ...o })));
81
+ rules.command("edit <number> <text>").description("Replace the text of a rule").action(run(async (n, text, _o, cmd) => (await import("./rules-4FXJUCXB.js")).rulesEditCommand(n, text, globals(cmd))));
82
+ rules.command("enable <number>").description("Enable a rule").action(run(async (n, _o, cmd) => (await import("./rules-4FXJUCXB.js")).rulesEnableCommand(n, globals(cmd))));
83
+ rules.command("disable <number>").description("Disable a rule without deleting it").action(run(async (n, _o, cmd) => (await import("./rules-4FXJUCXB.js")).rulesDisableCommand(n, globals(cmd))));
84
+ rules.command("remove <number>").description("Delete a rule").action(run(async (n, _o, cmd) => (await import("./rules-4FXJUCXB.js")).rulesRemoveCommand(n, globals(cmd))));
85
+ const repeat = (value, prev = []) => [...prev, ...value.split(",").map((s) => s.trim()).filter(Boolean)];
86
+ const mem = () => import("./memory-VSVNLEJC.js");
87
+ const memory = program.command("memory").description("Record, review and recall project memory (.athena/memory/)");
88
+ memory.command("list", { isDefault: true }).description("List memory entries with their label (FACT/INFERRED), status and staleness").option("--kind <kind>", "Only this kind (decision, gotcha, bug, convention, todo, fact)").option("--status <status>", "Only this status (unreviewed, confirmed, superseded)").option("--stale", "Only entries whose linked files changed since").action(run(async (o, cmd) => (await mem()).memoryListCommand({ ...globals(cmd), ...o })));
89
+ memory.command("show <id>").description("Show one entry in full: files, tags, evidence, staleness, warnings").action(run(async (id, _o, cmd) => (await mem()).memoryShowCommand(id, globals(cmd))));
90
+ memory.command("add").description("Record a memory as the developer (confirmed, FACT)").option("--kind <kind>", "decision, gotcha, bug, convention, todo or fact").option("--title <title>", "One-line summary").option("--details <text>", 'Longer explanation ("-" reads it from stdin)').option("--file <path>", "Linked project file (repeatable); the entry goes stale when it changes", repeat).option("--tag <tag>", "Tag (repeatable)", repeat).option("--evidence <text>", "Where this was observed (commit, issue, test, log)").option("--supersedes <id>", "Older memory this one replaces").action(run(async (o, cmd) => (await mem()).memoryAddCommand({ ...globals(cmd), ...o })));
91
+ memory.command("search <query>").description("Find entries whose title, details, tags or files contain the text").action(run(async (q, _o, cmd) => (await mem()).memorySearchCommand(q, globals(cmd))));
92
+ memory.command("recall <task...>").description("Rank the memory relevant to a task (what an agent would be given), with reasons").option("--file <path>", "File you are about to work on (repeatable)", repeat).option("--tag <tag>", "Tag to match (repeatable)", repeat).option("-n, --limit <count>", "Maximum entries (default 8)").action(run(async (task, o, cmd) => (await mem()).memoryRecallCommand(task, { ...globals(cmd), ...o })));
93
+ memory.command("confirm <ids...>").description("Confirm entries as FACT (and re-anchor them to the current files)").action(run(async (ids, _o, cmd) => (await mem()).memoryConfirmCommand(ids, globals(cmd))));
94
+ memory.command("supersede <id>").description("Mark an entry as replaced by a newer one (kept as history)").option("--by <id>", "The newer memory").action(run(async (id, o, cmd) => (await mem()).memorySupersedeCommand(id, { ...globals(cmd), ...o })));
95
+ memory.command("forget <ids...>").description("Delete entries").option("-y, --yes", "Skip confirmation").action(run(async (ids, o, cmd) => (await mem()).memoryForgetCommand(ids, { ...globals(cmd), ...o })));
96
+ memory.command("edit <id>").description("Change an entry (fields you pass replace the old values)").option("--title <title>", "New title").option("--details <text>", 'New details ("-" reads them from stdin)').option("--file <path>", "Linked files (repeatable; replaces the list)", repeat).option("--tag <tag>", "Tags (repeatable; replaces the list)", repeat).option("--evidence <text>", "New evidence").action(run(async (id, o, cmd) => (await mem()).memoryEditCommand(id, { ...globals(cmd), ...o })));
97
+ memory.command("stale").description("Entries whose linked files changed since they were recorded or confirmed").action(run(async (_o, cmd) => (await mem()).memoryStaleCommand(globals(cmd))));
98
+ memory.command("review").description("Walk through unreviewed (agent-written) entries: confirm, forget or skip").action(run(async (_o, cmd) => (await mem()).memoryReviewCommand(globals(cmd))));
99
+ const agents = program.command("agents").description("Manage AI agent integrations");
100
+ agents.command("list", { isDefault: true }).description("Show supported agents and integration status").action(run(async (_o, cmd) => (await import("./agents-DFSNEOP5.js")).agentsListCommand(globals(cmd))));
101
+ agents.command("add <agents...>").description('Configure integrations (claude-code, cursor, codex, copilot, gemini-cli, antigravity, windsurf, cline, agents-md, or "all")').action(run(async (names, _o, cmd) => (await import("./agents-DFSNEOP5.js")).agentsAddCommand(names, globals(cmd))));
102
+ agents.command("remove <agents...>").description("Remove Athena-managed integration files/blocks").action(run(async (names, _o, cmd) => (await import("./agents-DFSNEOP5.js")).agentsRemoveCommand(names, globals(cmd))));
103
+ const gitHook = program.command("git-hook").description("Install a Git pre-commit hook that blocks commits when knowledge is out of date");
104
+ gitHook.command("status", { isDefault: true }).description("Show whether the Athena pre-commit hook is installed").action(run(async (_o, cmd) => (await import("./git-hook-YZNZGAAP.js")).gitHookStatusCommand(globals(cmd))));
105
+ gitHook.command("install").description("Add the Athena pre-commit hook (existing hooks are kept; Athena adds a marked block)").option("--review", "Also run `athena review` and block commits with possible secrets or env files").action(run(async (o, cmd) => (await import("./git-hook-YZNZGAAP.js")).gitHookInstallCommand({ ...globals(cmd), review: o.review })));
106
+ gitHook.command("uninstall").description("Remove Athena's pre-commit hook or block").action(run(async (_o, cmd) => (await import("./git-hook-YZNZGAAP.js")).gitHookUninstallCommand(globals(cmd))));
107
+ program.command("open").description("Start (or reuse) the local web UI and open it in your browser").option("-p, --port <port>", "Port to bind (default: first free port from 7432)").option("--host <host>", "Interface to bind (default: 127.0.0.1)").option("--allow-remote", "Allow binding to a non-loopback interface").option("--no-open", "Do not launch a browser").option("--no-watch", "Do not watch the project for changes").action(run(async (o, cmd) => (await import("./open-UJZRYF4E.js")).openCommand({ ...globals(cmd), ...o, signal: controller.signal })));
108
+ program.command("activity").description("Show AI agent activity observed through hooks").option("-n, --limit <count>", "Number of events to show (default 30)").option("--agent <id>", "Only show events from this agent").action(run(async (o, cmd) => (await import("./activity-BKW7G4I5.js")).activityCommand({ ...globals(cmd), ...o })));
109
+ program.command("event").description("Record an agent hook event (called by AI coding agents, reads JSON on stdin)").option("--agent <id>", "Agent that fired the hook").option("--hook <name>", "Hook event name").option("--athena-hook", "Marker used to identify Athena-managed hook entries").option("--verbose-errors", "Print errors to stderr (hooks stay silent by default)").action(
110
+ run(async (o, cmd) => {
111
+ const g = globals(cmd);
112
+ await (await import("./event-TNT6CQZR.js")).eventCommand({ agent: o.agent, hook: o.hook, cwd: g.cwd, verbose: o.verboseErrors });
113
+ })
114
+ );
115
+ program.command("clean").description("Remove .athena/ and Athena agent integrations").option("-y, --yes", "Skip confirmation").option("--keep-agents", "Keep agent integration files/blocks").action(run(async (o, cmd) => (await import("./clean-MDBYH62P.js")).cleanCommand({ ...globals(cmd), ...o })));
116
+ program.command("version").description("Print the Athena version").action(run(async (_o, cmd) => {
117
+ if (globals(cmd).json) json({ version: ATHENA_VERSION, node: process.versions.node, platform: process.platform });
118
+ else line(ATHENA_VERSION);
119
+ }));
120
+ for (const p of PLANNED) {
121
+ program.command(p.name).description(`${p.description} ${dim(`(planned \u2014 Phase ${p.phase})`)}`).allowUnknownOption().allowExcessArguments().action(
122
+ run(async (_o, cmd) => {
123
+ if (globals(cmd).json) json({ available: false, command: p.name, plannedPhase: p.phase });
124
+ else line(`${c.yellow("Not available yet")} \u2014 \`athena ${p.name}\` is planned for Phase ${p.phase}.`);
125
+ return EXIT.NOT_AVAILABLE;
126
+ })
127
+ );
128
+ }
129
+ return program;
130
+ }
131
+ async function main() {
132
+ const program = buildProgram();
133
+ try {
134
+ await program.parseAsync(process.argv);
135
+ } catch (err) {
136
+ if (process.stdout.isTTY) process.stdout.write("\x1B[?25h");
137
+ if (controller.signal.aborted) {
138
+ process.exitCode = EXIT.INTERRUPTED;
139
+ return;
140
+ }
141
+ if (err instanceof AthenaError) {
142
+ if (isJson()) json({ error: err.message, hint: err.hint });
143
+ else {
144
+ process.stderr.write(`${c.red(`${sym.err} ${err.message}`)}
145
+ `);
146
+ if (err.hint) process.stderr.write(` ${dim(err.hint)}
147
+ `);
148
+ }
149
+ process.exitCode = err.exitCode;
150
+ return;
151
+ }
152
+ process.stderr.write(`${c.red(`${sym.err} Unexpected error: ${err.message}`)}
153
+ `);
154
+ if (process.env.ATHENA_DEBUG) process.stderr.write(`${err.stack}
155
+ `);
156
+ else process.stderr.write(` ${dim("Set ATHENA_DEBUG=1 for a stack trace. Please report this issue.")}
157
+ `);
158
+ process.exitCode = EXIT.ERROR;
159
+ }
160
+ }
161
+ export {
162
+ buildProgram,
163
+ main
164
+ };
165
+ //# sourceMappingURL=program-CWJNGCTZ.js.map
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ hasBlockers,
4
+ reviewChanges
5
+ } from "./chunk-FVCDMQUQ.js";
6
+ import "./chunk-3IS2X2Z7.js";
7
+ import {
8
+ c,
9
+ dim,
10
+ heading,
11
+ isJson,
12
+ json,
13
+ line,
14
+ ok,
15
+ spinner,
16
+ sym
17
+ } from "./chunk-Z6QWMJLK.js";
18
+ import {
19
+ requireProjectRoot
20
+ } from "./chunk-XFN7UZCS.js";
21
+ import "./chunk-KBO7XRBW.js";
22
+ import {
23
+ planSync,
24
+ staleForCheck
25
+ } from "./chunk-KHMBNT66.js";
26
+ import "./chunk-WVQC2EGO.js";
27
+ import "./chunk-GR4DL2VM.js";
28
+ import "./chunk-GGXPG6OE.js";
29
+ import "./chunk-MPG3IEGB.js";
30
+ import "./chunk-ELKFRTZT.js";
31
+ import "./chunk-2R6PXY6Z.js";
32
+ import "./chunk-PVCIGUIT.js";
33
+ import "./chunk-OLPSEQS5.js";
34
+ import "./chunk-SVTEJYMS.js";
35
+ import "./chunk-QCC4VJUH.js";
36
+ import "./chunk-JCGDR4UV.js";
37
+ import "./chunk-YRIS7ZS6.js";
38
+ import "./chunk-GHFRQAYT.js";
39
+ import "./chunk-INT3IGMQ.js";
40
+ import "./chunk-673EPYYH.js";
41
+ import "./chunk-FQEGEVLY.js";
42
+ import "./chunk-2DY4PUR4.js";
43
+ import "./chunk-IQQBAWSM.js";
44
+ import {
45
+ EXIT
46
+ } from "./chunk-AD4OZCU2.js";
47
+ import "./chunk-XW3MLHI2.js";
48
+ import "./chunk-EUPETUWA.js";
49
+
50
+ // src/cli/commands/review.ts
51
+ var LEVEL = {
52
+ blocker: { icon: () => c.red(sym.err), label: "blocker" },
53
+ warning: { icon: () => c.yellow(sym.warn), label: "warning" },
54
+ info: { icon: () => c.cyan(sym.info), label: "info" }
55
+ };
56
+ async function reviewCommand(opts) {
57
+ const root = await requireProjectRoot(opts);
58
+ const sp = spinner("Reviewing changes...");
59
+ let result;
60
+ try {
61
+ result = await reviewChanges(root, {
62
+ base: opts.base,
63
+ staged: opts.staged,
64
+ signal: opts.signal,
65
+ checkSync: opts.noSync ? void 0 : async (r) => staleForCheck(await planSync(r, { signal: opts.signal })).length === 0
66
+ });
67
+ sp.stop();
68
+ } catch (err) {
69
+ sp.stop();
70
+ throw err;
71
+ }
72
+ if (isJson()) {
73
+ json(result);
74
+ return !opts.noFail && hasBlockers(result) ? 1 : EXIT.OK;
75
+ }
76
+ heading("Athena Review");
77
+ line();
78
+ if (!result.changedFiles.length) {
79
+ line(dim(opts.base ? `No changes between ${opts.base} and HEAD.` : opts.staged ? "No staged changes." : "No uncommitted changes."));
80
+ return EXIT.OK;
81
+ }
82
+ line(`${result.stats.files} file${result.stats.files === 1 ? "" : "s"} changed ${dim(`(${c.green(`+${result.stats.added}`)} ${c.red(`\u2212${result.stats.removed}`)}${opts.base ? ` vs ${opts.base}` : opts.staged ? " staged" : " in the working tree"})`)}`);
83
+ line();
84
+ if (result.findings.length) {
85
+ heading("Automated checks");
86
+ for (const f of result.findings) {
87
+ line(`${LEVEL[f.level].icon()} ${c.bold(f.message)} ${dim(`[${f.check}]`)}`);
88
+ for (const file of f.files.slice(0, 6)) line(` ${dim(file)}`);
89
+ if (f.files.length > 6) line(dim(` +${f.files.length - 6} more`));
90
+ if (f.hint) line(` ${dim(f.hint)}`);
91
+ }
92
+ } else if (!result.incomplete) {
93
+ ok("Automated checks found nothing to flag.");
94
+ }
95
+ if (result.rules.length) {
96
+ line();
97
+ heading("Project rules to verify (from rules.md)");
98
+ for (const r of result.rules.slice(0, 20)) line(` ${dim("[ ]")} ${r.text} ${dim(`(${r.section})`)}`);
99
+ if (result.rules.length > 20) line(dim(` +${result.rules.length - 20} more`));
100
+ }
101
+ if (result.checklist.length) {
102
+ line();
103
+ heading("Review checklist (from code-review.md)");
104
+ for (const c2 of result.checklist.slice(0, 12)) line(` ${dim("[ ]")} ${c2}`);
105
+ if (result.checklist.length > 12) line(dim(` +${result.checklist.length - 12} more`));
106
+ }
107
+ line();
108
+ line(dim("Athena checks facts about the diff. Rules and checklist items are for you or your agent to apply \u2014 Athena does not judge whether they are met."));
109
+ const blockers = result.findings.filter((f) => f.level === "blocker").length;
110
+ if (blockers) line(c.red(`
111
+ ${blockers} blocker${blockers === 1 ? "" : "s"} found.`));
112
+ return !opts.noFail && hasBlockers(result) ? 1 : EXIT.OK;
113
+ }
114
+ export {
115
+ reviewCommand
116
+ };
117
+ //# sourceMappingURL=review-OOKXSYMP.js.map
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getRules,
4
+ mutateRules
5
+ } from "./chunk-LEMPCNAY.js";
6
+ import {
7
+ addRule,
8
+ removeRule,
9
+ updateRule
10
+ } from "./chunk-3IS2X2Z7.js";
11
+ import {
12
+ dim,
13
+ heading,
14
+ isJson,
15
+ json,
16
+ line,
17
+ ok
18
+ } from "./chunk-Z6QWMJLK.js";
19
+ import {
20
+ requireProjectRoot
21
+ } from "./chunk-XFN7UZCS.js";
22
+ import "./chunk-KBO7XRBW.js";
23
+ import "./chunk-OLPSEQS5.js";
24
+ import "./chunk-JCGDR4UV.js";
25
+ import "./chunk-673EPYYH.js";
26
+ import "./chunk-FQEGEVLY.js";
27
+ import "./chunk-2DY4PUR4.js";
28
+ import "./chunk-IQQBAWSM.js";
29
+ import {
30
+ AthenaError
31
+ } from "./chunk-AD4OZCU2.js";
32
+ import "./chunk-EUPETUWA.js";
33
+
34
+ // src/cli/commands/rules.ts
35
+ function parseIndex(raw) {
36
+ const n = Number(raw);
37
+ if (!Number.isInteger(n) || n < 1) throw new AthenaError(`Invalid rule number: ${raw}`, "Use `athena rules list` to see rule numbers.");
38
+ return n;
39
+ }
40
+ async function mutate(opts, fn, message) {
41
+ const root = await requireProjectRoot(opts);
42
+ const view = await mutateRules(root, void 0, fn);
43
+ if (isJson()) json({ ok: true, rules: view.rules });
44
+ else ok(message);
45
+ }
46
+ async function rulesListCommand(opts) {
47
+ const root = await requireProjectRoot(opts);
48
+ const { rules } = await getRules(root);
49
+ if (isJson()) {
50
+ json(rules);
51
+ return;
52
+ }
53
+ if (!rules.length) {
54
+ line(dim('No rules defined. Add one with `athena rules add "<rule>" --section <name>`.'));
55
+ return;
56
+ }
57
+ let section = "";
58
+ for (const r of rules) {
59
+ if (r.section !== section) {
60
+ section = r.section;
61
+ line();
62
+ heading(section);
63
+ }
64
+ const num = dim(`${String(r.index).padStart(3)}.`);
65
+ line(r.enabled ? `${num} ${r.text}` : `${num} ${dim(`[disabled] ${r.text}`)}`);
66
+ }
67
+ line();
68
+ line(dim(`${rules.filter((r) => r.enabled).length} enabled \xB7 ${rules.filter((r) => !r.enabled).length} disabled \xB7 source: .athena/rules.md`));
69
+ }
70
+ var rulesAddCommand = (text, opts) => mutate(opts, (d) => addRule(d, opts.section ?? "General", text), `Rule added to "${opts.section ?? "General"}".`);
71
+ var rulesEnableCommand = (n, opts) => mutate(opts, (d) => updateRule(d, parseIndex(n), { enabled: true }), `Rule ${n} enabled.`);
72
+ var rulesDisableCommand = (n, opts) => mutate(opts, (d) => updateRule(d, parseIndex(n), { enabled: false }), `Rule ${n} disabled.`);
73
+ var rulesEditCommand = (n, text, opts) => mutate(opts, (d) => updateRule(d, parseIndex(n), { text }), `Rule ${n} updated.`);
74
+ var rulesRemoveCommand = (n, opts) => mutate(opts, (d) => removeRule(d, parseIndex(n)), `Rule ${n} removed.`);
75
+ export {
76
+ rulesAddCommand,
77
+ rulesDisableCommand,
78
+ rulesEditCommand,
79
+ rulesEnableCommand,
80
+ rulesListCommand,
81
+ rulesRemoveCommand
82
+ };
83
+ //# sourceMappingURL=rules-4FXJUCXB.js.map
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runSecurityScan
4
+ } from "./chunk-SJ4DQFIJ.js";
5
+ import "./chunk-VSYBC3VY.js";
6
+ import {
7
+ c,
8
+ dim,
9
+ heading,
10
+ isJson,
11
+ json,
12
+ line,
13
+ ok,
14
+ relativeTime,
15
+ spinner,
16
+ sym,
17
+ warn
18
+ } from "./chunk-Z6QWMJLK.js";
19
+ import {
20
+ requireProjectRoot
21
+ } from "./chunk-XFN7UZCS.js";
22
+ import "./chunk-KBO7XRBW.js";
23
+ import {
24
+ projectSession
25
+ } from "./chunk-WVQC2EGO.js";
26
+ import {
27
+ RATED_SEVERITIES,
28
+ SEVERITY_ORDER,
29
+ analyzeProject,
30
+ meetsThreshold,
31
+ saveScan,
32
+ unratedFindings
33
+ } from "./chunk-MPG3IEGB.js";
34
+ import "./chunk-ELKFRTZT.js";
35
+ import "./chunk-PVCIGUIT.js";
36
+ import "./chunk-OLPSEQS5.js";
37
+ import "./chunk-SVTEJYMS.js";
38
+ import "./chunk-QCC4VJUH.js";
39
+ import "./chunk-JCGDR4UV.js";
40
+ import "./chunk-2DY4PUR4.js";
41
+ import "./chunk-IQQBAWSM.js";
42
+ import {
43
+ AthenaError,
44
+ EXIT
45
+ } from "./chunk-AD4OZCU2.js";
46
+ import "./chunk-XW3MLHI2.js";
47
+ import "./chunk-EUPETUWA.js";
48
+
49
+ // src/cli/commands/security.ts
50
+ var TONE = {
51
+ critical: c.red,
52
+ high: c.red,
53
+ moderate: c.yellow,
54
+ low: c.cyan,
55
+ unrated: c.magenta
56
+ };
57
+ async function loadModel(root, signal) {
58
+ return await projectSession(root).model() ?? (await analyzeProject(root, { signal })).model;
59
+ }
60
+ var toolLabel = (t) => t.target ? `${t.tool} (${t.target})` : t.tool;
61
+ function printScan(scan, model) {
62
+ heading("Secrets");
63
+ if (scan.secrets.count) {
64
+ warn(`${scan.secrets.count} potential hardcoded secret(s) in ${scan.secrets.files.length} file(s)`);
65
+ for (const f of scan.secrets.files.slice(0, 8)) line(` ${dim(f)}`);
66
+ line(dim(" Values are never stored. See .athena/security.md for types and locations."));
67
+ } else {
68
+ ok("No likely secrets matched Athena's patterns in scanned files");
69
+ line(dim(" Gitignored and oversized files are not scanned \u2014 this is not proof that none exist."));
70
+ }
71
+ if (scan.secrets.skippedLongLines) line(dim(` Generic keyword patterns were not applied to ${scan.secrets.skippedLongLines} line(s) longer than 4096 characters.`));
72
+ if (scan.secrets.truncated) warn("Secret scan stopped early after too many findings.");
73
+ line();
74
+ heading("Dependency audit");
75
+ if (!scan.tools.length) {
76
+ line(dim(" No auditable ecosystems detected (no lockfiles or supported manifests)."));
77
+ }
78
+ for (const t of scan.tools) {
79
+ if (t.status !== "ok") {
80
+ line(`${c.dim(sym.ring)} ${toolLabel(t)} ${dim(`\u2014 ${t.status}: ${t.message ?? ""}`)}`);
81
+ continue;
82
+ }
83
+ if (!t.findings.length) {
84
+ ok(`${toolLabel(t)} ${dim(`\u2014 no known vulnerable dependencies (${(t.durationMs / 1e3).toFixed(1)}s)`)}`);
85
+ continue;
86
+ }
87
+ const bySeverity = SEVERITY_ORDER.map((s) => [s, t.findings.filter((f) => f.severity === s).length]).filter(([, n]) => n > 0);
88
+ warn(`${toolLabel(t)} \u2014 ${t.findings.length} finding(s): ${bySeverity.map(([s, n]) => TONE[s](`${n} ${s}`)).join(", ")}`);
89
+ for (const f of t.findings.slice(0, 12)) {
90
+ line(` ${TONE[f.severity](f.severity.padEnd(8))} ${c.bold(f.package)} ${dim(f.title.slice(0, 70))}${f.fixAvailable ? c.green(" (fix available)") : ""}`);
91
+ }
92
+ if (t.findings.length > 12) line(dim(` +${t.findings.length - 12} more \u2014 see .athena/security-scan.json`));
93
+ }
94
+ if (scan.tools.some((t) => t.status === "ok" && t.findings.some((f) => f.severity === "unrated"))) {
95
+ line(dim(' "unrated": the tool reports vulnerabilities without a severity. Treat them as unreviewed, not as low.'));
96
+ }
97
+ line();
98
+ heading("Declared security controls");
99
+ line(model.security.controls.length ? ` ${model.security.controls.map((c2) => c2.name).join(", ")}` : dim(" None detected"));
100
+ line(dim(` Security tooling: ${model.security.tooling.length ? [...new Set(model.security.tooling.map((t) => t.name))].join(", ") : "none detected"}`));
101
+ line();
102
+ line(dim("Findings come from the tools above. Athena has no vulnerability database of its own, and a clean audit is not proof that the project is secure."));
103
+ }
104
+ var explain = (s) => process.stderr.write(`${s}
105
+ `);
106
+ async function securityCommand(opts) {
107
+ const root = await requireProjectRoot(opts);
108
+ let level = null;
109
+ if (opts.failOn) {
110
+ const raw = opts.failOn.toLowerCase();
111
+ const normalized = raw === "unknown" ? "unrated" : raw;
112
+ if (!SEVERITY_ORDER.includes(normalized)) throw new AthenaError(`Invalid --fail-on value: ${opts.failOn}`, `Use one of: ${RATED_SEVERITIES.join(", ")}`);
113
+ level = normalized;
114
+ }
115
+ const unratedPolicy = (opts.unrated ?? "fail").toLowerCase();
116
+ if (unratedPolicy !== "fail" && unratedPolicy !== "warn") throw new AthenaError(`Invalid --unrated value: ${opts.unrated}`, "Use one of: fail, warn");
117
+ let scan = null;
118
+ let model;
119
+ if (opts.last) {
120
+ scan = await projectSession(root).scan();
121
+ if (!scan) throw new AthenaError("No previous scan found.", "Run `athena security` to scan now.");
122
+ model = await loadModel(root, opts.signal);
123
+ } else {
124
+ const sp = spinner("Analyzing project...");
125
+ try {
126
+ model = await loadModel(root, opts.signal);
127
+ sp.update("Scanning files for secrets...");
128
+ scan = await runSecurityScan(root, model, { signal: opts.signal, skipAudit: opts.noAudit, onTool: (tool) => sp.update(`Running ${tool}...`) });
129
+ sp.succeed(`Security scan finished in ${(scan.durationMs / 1e3).toFixed(1)}s`);
130
+ } catch (err) {
131
+ sp.stop();
132
+ throw err;
133
+ }
134
+ await saveScan(root, scan);
135
+ }
136
+ if (isJson()) json(scan);
137
+ else {
138
+ heading("Athena Security");
139
+ line(dim(`Scanned ${relativeTime(scan.scannedAt)}${opts.last ? " (cached result)" : ""}`));
140
+ line();
141
+ printScan(scan, model);
142
+ if (!opts.last) line(dim("\nRun `athena sync` to record these results in .athena/security.md."));
143
+ }
144
+ if (level) {
145
+ const unrated = unratedFindings(scan);
146
+ const ratedHit = meetsThreshold(scan, level, { unrated: "warn" });
147
+ if (scan.secrets.count > 0) {
148
+ explain(`athena security: failing (--fail-on ${level}): ${scan.secrets.count} potential secret(s) found.`);
149
+ return 1;
150
+ }
151
+ if (ratedHit) {
152
+ explain(`athena security: failing (--fail-on ${level}): findings at or above ${level}.`);
153
+ return 1;
154
+ }
155
+ if (unrated.length) {
156
+ const tools = [...new Set(scan.tools.filter((t) => t.findings.some((f) => f.severity === "unrated")).map((t) => t.tool))].join(", ");
157
+ if (unratedPolicy === "fail") {
158
+ explain(`athena security: failing (--fail-on ${level}): ${unrated.length} unrated finding(s) from ${tools}. These tools do not report severity, so the finding may be at or above ${level}. Pass --unrated warn to not fail on unrated findings.`);
159
+ return 1;
160
+ }
161
+ explain(`athena security: warning: ${unrated.length} unrated finding(s) from ${tools} were not counted toward --fail-on ${level} (--unrated warn).`);
162
+ }
163
+ const broken = scan.tools.filter((t) => t.status === "failed" || t.status === "timeout");
164
+ if (broken.length) {
165
+ explain(`athena security: failing (--fail-on ${level}): ${broken.map((t) => `${t.tool} ${t.status}`).join(", ")}, so those dependencies were not checked.`);
166
+ return 1;
167
+ }
168
+ const missing = scan.tools.filter((t) => t.status === "unavailable");
169
+ if (missing.length) explain(`athena security: note: not checked because the tool is unavailable: ${missing.map((t) => t.tool).join(", ")}.`);
170
+ }
171
+ return EXIT.OK;
172
+ }
173
+ export {
174
+ printScan,
175
+ securityCommand
176
+ };
177
+ //# sourceMappingURL=security-LME3XFL3.js.map