project-athena 0.2.1 → 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 (147) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +15 -8
  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 -10716
  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-DAXvniWB.js → Editor-BI56-HX5.js} +1 -1
  64. package/dist/web/assets/{abnfDiagram-O67JEVCF-Az7EW1BC.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-BxeXqSTn.js → architectureDiagram-NJMV4G6O-lB6fcVzU.js} +1 -1
  67. package/dist/web/assets/{blockDiagram-BEXU5L5S-aeKZl9lJ.js → blockDiagram-BEXU5L5S-DcM5pEjM.js} +1 -1
  68. package/dist/web/assets/{c4Diagram-YGBWAQC7-CgmntYsG.js → c4Diagram-YGBWAQC7-CJgqxTY2.js} +1 -1
  69. package/dist/web/assets/channel-BvDD7AMO.js +1 -0
  70. package/dist/web/assets/{chunk-3FUC2YCW-BpmRD2yo.js → chunk-3FUC2YCW-B8l_VuWR.js} +1 -1
  71. package/dist/web/assets/{chunk-5DYCD2WN-CRIFDwod.js → chunk-5DYCD2WN-CZecgNhf.js} +1 -1
  72. package/dist/web/assets/{chunk-742MDFTN-hRW9VeNV.js → chunk-742MDFTN-BeZ7P5jB.js} +1 -1
  73. package/dist/web/assets/{chunk-7INBJB4K-C6VEpT0Y.js → chunk-7INBJB4K-IKy0pr5m.js} +1 -1
  74. package/dist/web/assets/{chunk-7M6MHVWA-Dnajov2w.js → chunk-7M6MHVWA-Dg_QqJEX.js} +1 -1
  75. package/dist/web/assets/{chunk-7PRAP22T-DSAc0_CZ.js → chunk-7PRAP22T-DqQNS8oq.js} +1 -1
  76. package/dist/web/assets/{chunk-GTNCS2PH-CefpYUjb.js → chunk-GTNCS2PH-x4sM51nJ.js} +1 -1
  77. package/dist/web/assets/{chunk-GWA4HPMP-MLNflMmF.js → chunk-GWA4HPMP-CE5GcwVN.js} +1 -1
  78. package/dist/web/assets/{chunk-MBY4JIJT-D3GmmEiU.js → chunk-MBY4JIJT-DA_D1hjF.js} +1 -1
  79. package/dist/web/assets/{chunk-NETBCI7D-DzTj6xVk.js → chunk-NETBCI7D-CeJ3XBZ2.js} +1 -1
  80. package/dist/web/assets/{chunk-O7XYJQB3-D1d0hCko.js → chunk-O7XYJQB3-BH2gESvO.js} +1 -1
  81. package/dist/web/assets/{chunk-UA2S7LBM-C9hIFeWR.js → chunk-UA2S7LBM-BWr0xCux.js} +1 -1
  82. package/dist/web/assets/{chunk-WEXAMYUT-BFGtcjsi.js → chunk-WEXAMYUT-B4QwUZPM.js} +1 -1
  83. package/dist/web/assets/{chunk-Z7XXMR3K-BRZ8_wtE.js → chunk-Z7XXMR3K-BxcA-y5B.js} +1 -1
  84. package/dist/web/assets/{chunk-ZIGJFQKS-CTKOioal.js → chunk-ZIGJFQKS-BZYvP63G.js} +1 -1
  85. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-DJGKtWJv.js → classDiagram-v2-NBCMYWYE-BS3P_NXb.js} +1 -1
  86. package/dist/web/assets/{cynefin-OW5HDTMX-hNq1kGmA.js → cynefin-OW5HDTMX-BALD4Pve.js} +1 -1
  87. package/dist/web/assets/{cynefinDiagram-VND7K2PF-sODQM_ZZ.js → cynefinDiagram-VND7K2PF-BM2y4W6d.js} +1 -1
  88. package/dist/web/assets/{dagre-6A5THRUB-DEzTThIf.js → dagre-6A5THRUB-PTfOrIdF.js} +1 -1
  89. package/dist/web/assets/{diagram-22UHCM2B-s_eellsa.js → diagram-22UHCM2B-CpXEHhUl.js} +1 -1
  90. package/dist/web/assets/{diagram-3UASUU5V-B2onY_Xw.js → diagram-3UASUU5V-DPHfXzr6.js} +1 -1
  91. package/dist/web/assets/{diagram-ATOU4E4O-B8V6s13s.js → diagram-ATOU4E4O-DMBNXvAC.js} +1 -1
  92. package/dist/web/assets/{diagram-CDSNMT55-CJxiE6mf.js → diagram-CDSNMT55-CDLe1M7G.js} +1 -1
  93. package/dist/web/assets/{diagram-MLGK6HIB-m5OHPLAp.js → diagram-MLGK6HIB-D6L8oE7j.js} +1 -1
  94. package/dist/web/assets/{diagram-MPIPVDR6-Buvvrk_A.js → diagram-MPIPVDR6-CP-S7rrQ.js} +1 -1
  95. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-CpL7Wt3R.js → ebnfDiagram-ZINNZB2B-CkUc63TK.js} +1 -1
  96. package/dist/web/assets/{elk-276RUBZZ-CwYhbsXa.js → elk-276RUBZZ-C1CAJLH_.js} +1 -1
  97. package/dist/web/assets/{erDiagram-OPXOYQCR-DNikci2r.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-d-ynDlvJ.js → ganttDiagram-FUAMR5RP-BVm8SiGg.js} +1 -1
  101. package/dist/web/assets/{gitGraph-4MIJSDKK-DwIeBI91.js → gitGraph-4MIJSDKK-Dm7xBrW0.js} +1 -1
  102. package/dist/web/assets/{gitGraphDiagram-X574FWY7-04RZccu6.js → gitGraphDiagram-X574FWY7-D_PIKKhx.js} +1 -1
  103. package/dist/web/assets/{index-C3-8uYXr.js → index-CeOIBpu2.js} +3 -3
  104. package/dist/web/assets/{info-A6RAGUB7-xf4Nzgv-.js → info-A6RAGUB7-CLGrx8pn.js} +1 -1
  105. package/dist/web/assets/{infoDiagram-VRGFBTTK-BYjw0eiK.js → infoDiagram-VRGFBTTK-BlBjaKm7.js} +1 -1
  106. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-Bx9s4nPM.js → ishikawaDiagram-OU5B5YK6-Kxe7GGS3.js} +1 -1
  107. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-DrdHs7B-.js → journeyDiagram-ZHPQQLJL-wCMoHK4M.js} +1 -1
  108. package/dist/web/assets/{kanban-definition-PNTS6WVX-CVd8v2Te.js → kanban-definition-PNTS6WVX-BxdCz9Ko.js} +1 -1
  109. package/dist/web/assets/{line-V_WrJ-6y.js → line-Zf11FnWF.js} +1 -1
  110. package/dist/web/assets/{mermaid-parser.core-B-ilyEpO.js → mermaid-parser.core-BvFFklev.js} +3 -3
  111. package/dist/web/assets/{mermaid.core-Bnyaa0nF.js → mermaid.core-Cd_Ssti9.js} +4 -4
  112. package/dist/web/assets/{mindmap-definition-NLK3R4M7-B3AbvHPR.js → mindmap-definition-NLK3R4M7-BGPQl-GY.js} +1 -1
  113. package/dist/web/assets/{packet-AYTQ26CC-CBAIUXIc.js → packet-AYTQ26CC-6lI3cEX_.js} +1 -1
  114. package/dist/web/assets/{pegDiagram-GJSIUBJH-DIdeELUM.js → pegDiagram-GJSIUBJH-BPIjlSAj.js} +1 -1
  115. package/dist/web/assets/{pie-WAS4IAKB-a1r3tzPR.js → pie-WAS4IAKB-D6Dzj3Ja.js} +1 -1
  116. package/dist/web/assets/{pieDiagram-5QR66LMP-pQBEio0x.js → pieDiagram-5QR66LMP-DtA-vZmv.js} +1 -1
  117. package/dist/web/assets/{quadrantDiagram-O4NWA36T-CkJK5Y03.js → quadrantDiagram-O4NWA36T-EettyqQn.js} +1 -1
  118. package/dist/web/assets/{radar-RG4KPBEZ-BZfLYjx5.js → radar-RG4KPBEZ-xmRc3Dl8.js} +1 -1
  119. package/dist/web/assets/{railroad-74A4TZTK-CuIW1ZAU.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-_BpGE8aJ.js → railroadDiagram-XR7U4H2S-PBw4We6b.js} +1 -1
  124. package/dist/web/assets/{requirementDiagram-PLB6GJNP-CnzF90GS.js → requirementDiagram-PLB6GJNP-DjQpaUFA.js} +1 -1
  125. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-DYeisDj_.js → sankeyDiagram-IPEJSGJF-CBW-_AoF.js} +1 -1
  126. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-ll9cBvca.js → sequenceDiagram-PO4LG4MO-DEoPYqY9.js} +1 -1
  127. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-CdKcOIKQ.js → stateDiagram-v2-GCMORJYK-UZq5rPud.js} +1 -1
  128. package/dist/web/assets/{swimlanes-2SLR337P-y6piTWEW.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-DuC_4xK0.js → timeline-definition-EJHVYXUP-CYJl7mP-.js} +1 -1
  131. package/dist/web/assets/{treeView-Q6P3EWNA-gCQFmND0.js → treeView-Q6P3EWNA-DtZ5nkPQ.js} +1 -1
  132. package/dist/web/assets/{treemap-WGGIJYW6-BQ2010OY.js → treemap-WGGIJYW6-m49GDM-M.js} +1 -1
  133. package/dist/web/assets/{usecaseDiagram-POWQR4AR-CCTqzHz6.js → usecaseDiagram-POWQR4AR-C2Vg20UK.js} +1 -1
  134. package/dist/web/assets/{vennDiagram-UO4OBE2U-0_O7guNq.js → vennDiagram-UO4OBE2U-C07v5OGI.js} +1 -1
  135. package/dist/web/assets/{wardley-WFR3VGLG-SsX7uMDs.js → wardley-WFR3VGLG-C9Z7EZtz.js} +1 -1
  136. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-DlqH6Hox.js → wardleyDiagram-VNRHLVJA-DdA8CcSC.js} +1 -1
  137. package/dist/web/assets/{xychartDiagram-PMCCYNJV-nr4UYDtd.js → xychartDiagram-PMCCYNJV-C9EZw07b.js} +1 -1
  138. package/dist/web/index.html +1 -1
  139. package/package.json +4 -2
  140. package/dist/web/assets/architecture-7GRP2DOG-blXBarB2.js +0 -1
  141. package/dist/web/assets/channel-f4QzewnN.js +0 -1
  142. package/dist/web/assets/eventmodeling-NTZA5JFV-CA_ZFPZ2.js +0 -1
  143. package/dist/web/assets/flowDiagram-KWPJA3E3-DObsBuWT.js +0 -1
  144. package/dist/web/assets/railroad-abnf-HS5TGJTU-6B4Ffbxn.js +0 -1
  145. package/dist/web/assets/railroad-ebnf-LZEXJU2U-BGL9lIQc.js +0 -1
  146. package/dist/web/assets/railroad-peg-WCYAUIDC-DcqxTem7.js +0 -1
  147. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-DFS8a3jO.js +0 -8
@@ -0,0 +1,465 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ readTextIfExists,
4
+ writeFileAtomic
5
+ } from "./chunk-IQQBAWSM.js";
6
+ import {
7
+ ATHENA_DIR
8
+ } from "./chunk-EUPETUWA.js";
9
+
10
+ // src/core/config.ts
11
+ import path from "path";
12
+ import { z } from "zod";
13
+ var DEFAULT_IGNORES = [
14
+ ".git",
15
+ ".hg",
16
+ ".svn",
17
+ "node_modules",
18
+ "bower_components",
19
+ "dist",
20
+ "build",
21
+ "out",
22
+ ".next",
23
+ ".nuxt",
24
+ ".svelte-kit",
25
+ ".turbo",
26
+ ".cache",
27
+ ".parcel-cache",
28
+ ".dart_tool",
29
+ ".venv",
30
+ "venv",
31
+ ".tox",
32
+ "__pycache__",
33
+ ".pytest_cache",
34
+ ".mypy_cache",
35
+ "target",
36
+ "coverage",
37
+ ".gradle",
38
+ ".idea",
39
+ "vendor",
40
+ "Pods",
41
+ ".terraform",
42
+ ".serverless",
43
+ ATHENA_DIR,
44
+ `${ATHENA_DIR}.tmp-*`
45
+ ];
46
+ var AthenaConfig = z.object({
47
+ /** Additional gitignore-style patterns to exclude. */
48
+ ignore: z.array(z.string()).default([]),
49
+ /** Patterns (gitignore-style) to force-include even if a default ignore matches. */
50
+ include: z.array(z.string()).default([]),
51
+ /** Files larger than this are indexed by metadata only. */
52
+ maxFileBytes: z.number().int().positive().default(1e6),
53
+ /** Hard cap on files scanned; beyond this analysis stops and warns. */
54
+ maxFiles: z.number().int().positive().default(2e5),
55
+ /** Agent adapters to configure. Empty = auto. */
56
+ agents: z.array(z.string()).optional(),
57
+ /** Optional AI provider settings (see `athena ai status`). AI is never required. */
58
+ ai: z.object({
59
+ provider: z.enum(["anthropic", "openai", "google", "ollama"]).optional(),
60
+ model: z.string().optional(),
61
+ mode: z.enum(["cloud", "local"]).optional(),
62
+ consent: z.boolean().optional(),
63
+ maxChars: z.number().int().min(500).max(2e5).optional(),
64
+ baseUrl: z.string().optional()
65
+ }).optional()
66
+ });
67
+ async function loadConfig(root) {
68
+ const file = path.join(root, ATHENA_DIR, "config.json");
69
+ const raw = await readTextIfExists(file);
70
+ if (raw === null) return { config: AthenaConfig.parse({}) };
71
+ try {
72
+ const parsed = AthenaConfig.safeParse(JSON.parse(raw));
73
+ if (parsed.success) return { config: parsed.data };
74
+ return { config: AthenaConfig.parse({}), warning: `.athena/config.json is invalid (${parsed.error.issues[0]?.message}); using defaults` };
75
+ } catch {
76
+ return { config: AthenaConfig.parse({}), warning: ".athena/config.json is not valid JSON; using defaults" };
77
+ }
78
+ }
79
+
80
+ // src/core/cache/facts-cache.ts
81
+ import { promises as fs } from "fs";
82
+ import path2 from "path";
83
+ var CACHE_DIR = path2.join("cache", "v1");
84
+ var FACTS_DIR = "facts";
85
+ var VERSION_FILE = "version.json";
86
+ var FACTS_FORMAT = 1;
87
+ var shardOf = (hash) => hash.slice(0, 2);
88
+ function cacheRoot(athenaDir2) {
89
+ return path2.join(athenaDir2, CACHE_DIR);
90
+ }
91
+ var FactsCache = class _FactsCache {
92
+ constructor(dir, identity, reset) {
93
+ this.dir = dir;
94
+ this.identity = identity;
95
+ this.reset = reset;
96
+ this.stats.reset = reset;
97
+ }
98
+ dir;
99
+ identity;
100
+ reset;
101
+ shards = /* @__PURE__ */ new Map();
102
+ loading = /* @__PURE__ */ new Map();
103
+ dirty = /* @__PURE__ */ new Set();
104
+ /** Facts that must not be persisted (e.g. keyed with an ephemeral per-process key). */
105
+ transient = /* @__PURE__ */ new Map();
106
+ stats = { shardsLoaded: 0, corruptShards: 0, shardsWritten: 0, reset: false };
107
+ static async open(athenaDir2, identity) {
108
+ if (!athenaDir2) return new _FactsCache(null, identity, false);
109
+ const dir = cacheRoot(athenaDir2);
110
+ let reset = true;
111
+ try {
112
+ const v = JSON.parse(await fs.readFile(path2.join(dir, FACTS_DIR, VERSION_FILE), "utf8"));
113
+ reset = v.format !== FACTS_FORMAT || v.detectors !== identity.detectors || v.config !== identity.config;
114
+ } catch {
115
+ }
116
+ return new _FactsCache(reset ? null : dir, identity, reset);
117
+ }
118
+ shard(prefix) {
119
+ const loaded = this.shards.get(prefix);
120
+ if (loaded) return loaded;
121
+ let p = this.loading.get(prefix);
122
+ if (!p) {
123
+ p = this.loadShard(prefix);
124
+ this.loading.set(prefix, p);
125
+ }
126
+ return p;
127
+ }
128
+ async loadShard(prefix) {
129
+ let data = {};
130
+ if (this.dir) {
131
+ try {
132
+ const raw = await fs.readFile(path2.join(this.dir, FACTS_DIR, `${prefix}.json`), "utf8");
133
+ const parsed = JSON.parse(raw);
134
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
135
+ data = parsed;
136
+ this.stats.shardsLoaded++;
137
+ } else throw new Error("bad shard");
138
+ } catch (err) {
139
+ if (err.code !== "ENOENT") {
140
+ this.stats.corruptShards++;
141
+ data = {};
142
+ this.dirty.add(prefix);
143
+ }
144
+ }
145
+ }
146
+ this.shards.set(prefix, data);
147
+ this.loading.delete(prefix);
148
+ return data;
149
+ }
150
+ /** Load the shards for these hashes (in parallel) so get/has can run synchronously. */
151
+ async preload(hashes) {
152
+ const prefixes = /* @__PURE__ */ new Set();
153
+ for (const h of hashes) if (isContentHash(h)) prefixes.add(shardOf(h));
154
+ await Promise.all([...prefixes].map((p) => this.shard(p)));
155
+ }
156
+ /** Synchronous lookup; the shard must have been loaded (see preload / getAsync). */
157
+ entry(hash) {
158
+ return this.shards.get(shardOf(hash))?.[hash];
159
+ }
160
+ has(hash, id) {
161
+ if (this.transient.get(hash)?.[id] !== void 0) return true;
162
+ const e = this.entry(hash);
163
+ return !!e && e[id] !== void 0;
164
+ }
165
+ /** Synchronous lookup when the shard is already loaded; `undefined` otherwise (or when absent). */
166
+ peek(hash, id) {
167
+ const t = this.transient.get(hash)?.[id];
168
+ if (t !== void 0) return t;
169
+ return this.shards.get(shardOf(hash))?.[hash]?.[id];
170
+ }
171
+ async get(hash, id) {
172
+ const t = this.transient.get(hash)?.[id];
173
+ if (t !== void 0) return t;
174
+ if (!isContentHash(hash)) return void 0;
175
+ const s = await this.shard(shardOf(hash));
176
+ return s[hash]?.[id];
177
+ }
178
+ /**
179
+ * Store a fact. Values are normalized through JSON so a value read back from disk
180
+ * is indistinguishable from a freshly computed one.
181
+ */
182
+ async set(hash, id, value, opts = {}) {
183
+ const normalized = value === void 0 ? null : JSON.parse(JSON.stringify(value));
184
+ if (opts.persist === false || !isContentHash(hash)) {
185
+ const e2 = this.transient.get(hash) ?? {};
186
+ e2[id] = normalized;
187
+ this.transient.set(hash, e2);
188
+ return;
189
+ }
190
+ const prefix = shardOf(hash);
191
+ const s = await this.shard(prefix);
192
+ const e = s[hash] ??= {};
193
+ if (opts.replacePrefix) {
194
+ for (const k of Object.keys(e)) if (k !== id && k.startsWith(opts.replacePrefix)) delete e[k];
195
+ }
196
+ e[id] = normalized;
197
+ this.dirty.add(prefix);
198
+ }
199
+ /**
200
+ * Persist changed shards under `athenaDir` (default: where the cache was opened).
201
+ * With `live`, entries for content no longer present in the project are dropped
202
+ * from every loaded shard.
203
+ */
204
+ async save(athenaDir2, live) {
205
+ const dir = cacheRoot(athenaDir2);
206
+ const factsDir = path2.join(dir, FACTS_DIR);
207
+ const retarget = this.dir !== dir;
208
+ if (this.reset || retarget) {
209
+ await fs.rm(factsDir, { recursive: true, force: true });
210
+ for (const p of this.shards.keys()) this.dirty.add(p);
211
+ }
212
+ if (live) {
213
+ for (const [prefix, shard] of this.shards) {
214
+ for (const h of Object.keys(shard)) {
215
+ if (!live.has(h)) {
216
+ delete shard[h];
217
+ this.dirty.add(prefix);
218
+ }
219
+ }
220
+ }
221
+ }
222
+ await fs.mkdir(factsDir, { recursive: true });
223
+ const writes = [...this.dirty].map(async (prefix) => {
224
+ const shard = this.shards.get(prefix) ?? {};
225
+ const file = path2.join(factsDir, `${prefix}.json`);
226
+ if (!Object.keys(shard).length) await fs.rm(file, { force: true });
227
+ else await writeFileAtomic(file, JSON.stringify(shard));
228
+ this.stats.shardsWritten++;
229
+ });
230
+ await Promise.all(writes);
231
+ if (this.reset || retarget || writes.length) {
232
+ await writeFileAtomic(path2.join(factsDir, VERSION_FILE), JSON.stringify({ format: FACTS_FORMAT, ...this.identity }));
233
+ }
234
+ if (live) {
235
+ const livePrefixes = new Set([...live].filter(isContentHash).map(shardOf));
236
+ const names = await fs.readdir(factsDir).catch(() => []);
237
+ await Promise.all(names.filter((n) => /^[0-9a-f]{2}\.json$/.test(n) && !livePrefixes.has(n.slice(0, 2)) && !this.shards.has(n.slice(0, 2))).map((n) => fs.rm(path2.join(factsDir, n), { force: true })));
238
+ }
239
+ this.dirty.clear();
240
+ this.reset = false;
241
+ this.dir = dir;
242
+ }
243
+ };
244
+ function isContentHash(h) {
245
+ return h.length >= 16 && h.length <= 64 && !h.startsWith("meta:");
246
+ }
247
+
248
+ // src/core/state/state.ts
249
+ import { promises as fs2 } from "fs";
250
+ import path3 from "path";
251
+ import { z as z2 } from "zod";
252
+ var STATE_SCHEMA_VERSION = 2;
253
+ var DocumentState = z2.object({
254
+ file: z2.string(),
255
+ blocks: z2.array(z2.string()),
256
+ lastGeneratedAt: z2.string(),
257
+ /** When the file content last changed due to Athena. */
258
+ lastChangedAt: z2.string(),
259
+ contentHash: z2.string(),
260
+ preservedModified: z2.array(z2.string())
261
+ });
262
+ var AgentState = z2.object({
263
+ configured: z2.boolean(),
264
+ files: z2.array(z2.string()),
265
+ configuredAt: z2.string().optional()
266
+ });
267
+ var StateFields = {
268
+ athenaVersion: z2.string(),
269
+ projectName: z2.string(),
270
+ createdAt: z2.string(),
271
+ analyzedAt: z2.string(),
272
+ analysisDurationMs: z2.number(),
273
+ git: z2.object({ isRepo: z2.boolean(), head: z2.string().optional(), branch: z2.string().optional() }),
274
+ documents: z2.record(z2.string(), DocumentState),
275
+ detectors: z2.record(z2.string(), z2.number()),
276
+ agents: z2.record(z2.string(), AgentState),
277
+ warnings: z2.array(z2.string())
278
+ };
279
+ var AthenaStateSchema = z2.object({ schemaVersion: z2.literal(STATE_SCHEMA_VERSION), ...StateFields });
280
+ var AthenaStateV1 = z2.object({ schemaVersion: z2.literal(1), ...StateFields, fileIndex: z2.record(z2.string(), z2.unknown()) });
281
+ var MAX_STATE_WARNINGS = 100;
282
+ function athenaDir(root) {
283
+ return path3.join(root, ATHENA_DIR);
284
+ }
285
+ function fileIndexPath(dir) {
286
+ return path3.join(dir, CACHE_DIR, "files.json");
287
+ }
288
+ var stampOf = (st) => `${st.ino}:${st.size}:${st.mtimeMs}`;
289
+ var stateMemo = /* @__PURE__ */ new Map();
290
+ var indexMemo = /* @__PURE__ */ new Map();
291
+ var pendingV1Index = /* @__PURE__ */ new Map();
292
+ function isIndexEntry(v) {
293
+ if (!v || typeof v !== "object") return false;
294
+ const e = v;
295
+ return typeof e.h === "string" && typeof e.s === "number" && typeof e.m === "number" && (e.b === void 0 || e.b === 1);
296
+ }
297
+ async function readState(dir) {
298
+ const file = path3.join(dir, "state.json");
299
+ let st;
300
+ try {
301
+ st = await fs2.stat(file);
302
+ } catch (err) {
303
+ if (err.code === "ENOENT") return { kind: "missing" };
304
+ return { kind: "corrupted", reason: `unreadable (${err.code})` };
305
+ }
306
+ const memo = stateMemo.get(file);
307
+ if (memo && memo.stamp === stampOf(st)) return structuredClone(memo.result);
308
+ let raw;
309
+ try {
310
+ raw = await fs2.readFile(file, "utf8");
311
+ } catch (err) {
312
+ if (err.code === "ENOENT") return { kind: "missing" };
313
+ return { kind: "corrupted", reason: `unreadable (${err.code})` };
314
+ }
315
+ let json;
316
+ try {
317
+ json = JSON.parse(raw);
318
+ } catch {
319
+ return { kind: "corrupted", reason: "invalid JSON" };
320
+ }
321
+ let result;
322
+ if (json && typeof json === "object" && json.schemaVersion === 1) {
323
+ result = await migrateV1(dir, json);
324
+ st = await fs2.stat(file).catch(() => st);
325
+ } else {
326
+ const parsed = AthenaStateSchema.safeParse(json);
327
+ if (!parsed.success) {
328
+ const issue = parsed.error.issues[0];
329
+ result = { kind: "corrupted", reason: `schema mismatch at ${issue?.path.join(".") || "(root)"}: ${issue?.message}` };
330
+ } else result = { kind: "ok", state: parsed.data };
331
+ }
332
+ stateMemo.set(file, { stamp: stampOf(st), result: structuredClone(result) });
333
+ return result;
334
+ }
335
+ async function migrateV1(dir, json) {
336
+ const parsed = AthenaStateV1.safeParse(json);
337
+ if (!parsed.success) {
338
+ const issue = parsed.error.issues[0];
339
+ return { kind: "corrupted", reason: `schema mismatch at ${issue?.path.join(".") || "(root)"}: ${issue?.message}` };
340
+ }
341
+ const { fileIndex: rawIndex, schemaVersion: _v, ...rest } = parsed.data;
342
+ const index = {};
343
+ for (const [p, v] of Object.entries(rawIndex)) {
344
+ if (!isIndexEntry(v)) return { kind: "corrupted", reason: `schema mismatch at fileIndex.${p}: invalid entry` };
345
+ index[p] = v;
346
+ }
347
+ const state = { schemaVersion: STATE_SCHEMA_VERSION, ...rest };
348
+ try {
349
+ await writeFileIndex(dir, index);
350
+ await writeStateFile(dir, state);
351
+ pendingV1Index.delete(dir);
352
+ } catch {
353
+ pendingV1Index.set(dir, index);
354
+ }
355
+ return { kind: "ok", state };
356
+ }
357
+ async function writeStateFile(dir, state) {
358
+ const { fileIndex: _index, ...rest } = state;
359
+ const out = { ...rest, schemaVersion: STATE_SCHEMA_VERSION, warnings: rest.warnings.slice(0, MAX_STATE_WARNINGS) };
360
+ await writeFileAtomic(path3.join(dir, "state.json"), `${JSON.stringify(out, null, 2)}
361
+ `);
362
+ stateMemo.delete(path3.join(dir, "state.json"));
363
+ }
364
+ async function writeState(dir, state) {
365
+ if (state.fileIndex) await writeFileIndex(dir, state.fileIndex);
366
+ await writeStateFile(dir, state);
367
+ }
368
+ async function readFileIndex(dir) {
369
+ const file = fileIndexPath(dir);
370
+ let st;
371
+ try {
372
+ st = await fs2.stat(file);
373
+ } catch {
374
+ const pending = pendingV1Index.get(dir);
375
+ if (pending) return pending;
376
+ const s = await readState(dir);
377
+ return s.kind === "ok" ? pendingV1Index.get(dir) ?? await readIndexFile(file) : {};
378
+ }
379
+ const memo = indexMemo.get(file);
380
+ if (memo && memo.stamp === stampOf(st)) return memo.index;
381
+ const index = await readIndexFile(file);
382
+ indexMemo.set(file, { stamp: stampOf(st), index });
383
+ return index;
384
+ }
385
+ async function readIndexFile(file) {
386
+ let json;
387
+ try {
388
+ json = JSON.parse(await fs2.readFile(file, "utf8"));
389
+ } catch {
390
+ return {};
391
+ }
392
+ const out = {};
393
+ const files = json?.files;
394
+ if (json?.v !== 1 || !files || typeof files !== "object") return out;
395
+ for (const [p, t] of Object.entries(files)) {
396
+ if (!Array.isArray(t) || typeof t[0] !== "string" || typeof t[1] !== "number" || typeof t[2] !== "number") continue;
397
+ out[p] = t[3] === 1 ? { h: t[0], s: t[1], m: t[2], b: 1 } : { h: t[0], s: t[1], m: t[2] };
398
+ }
399
+ return out;
400
+ }
401
+ var RACY_WINDOW_MS = 2e3;
402
+ async function writeFileIndex(dir, index, now = Date.now()) {
403
+ const files = {};
404
+ for (const [p, e] of Object.entries(index)) {
405
+ const m = now - e.m < RACY_WINDOW_MS ? -1 : e.m;
406
+ files[p] = e.b === 1 ? [e.h, e.s, m, 1] : [e.h, e.s, m];
407
+ }
408
+ const file = fileIndexPath(dir);
409
+ await writeFileAtomic(file, JSON.stringify({ v: 1, files }));
410
+ indexMemo.delete(file);
411
+ }
412
+ async function backupCorruptedState(dir) {
413
+ const src = path3.join(dir, "state.json");
414
+ const backupDir = path3.join(dir, ".backup");
415
+ const dest = path3.join(backupDir, `state.${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`);
416
+ try {
417
+ await fs2.mkdir(backupDir, { recursive: true });
418
+ await fs2.rename(src, dest);
419
+ stateMemo.delete(src);
420
+ return dest;
421
+ } catch {
422
+ return null;
423
+ }
424
+ }
425
+ function buildFileIndex(files) {
426
+ const idx = {};
427
+ for (const f of files) idx[f.path] = { h: f.hash, s: f.size, m: f.mtimeMs, ...f.binary ? { b: 1 } : {} };
428
+ return idx;
429
+ }
430
+ function sameHash(a, b) {
431
+ if (a === b) return true;
432
+ if (a.startsWith("meta:") || b.startsWith("meta:")) return false;
433
+ const n = Math.min(a.length, b.length);
434
+ return n >= 16 && a.slice(0, n) === b.slice(0, n);
435
+ }
436
+ function diffFileIndex(prev, next) {
437
+ const diff = { added: [], modified: [], deleted: [] };
438
+ for (const [p, v] of Object.entries(next)) {
439
+ const old = prev[p];
440
+ if (!old) diff.added.push(p);
441
+ else if (!sameHash(old.h, v.h)) diff.modified.push(p);
442
+ }
443
+ for (const p of Object.keys(prev)) if (!(p in next)) diff.deleted.push(p);
444
+ return diff;
445
+ }
446
+
447
+ export {
448
+ DEFAULT_IGNORES,
449
+ loadConfig,
450
+ CACHE_DIR,
451
+ cacheRoot,
452
+ FactsCache,
453
+ STATE_SCHEMA_VERSION,
454
+ athenaDir,
455
+ fileIndexPath,
456
+ readState,
457
+ writeState,
458
+ readFileIndex,
459
+ RACY_WINDOW_MS,
460
+ backupCorruptedState,
461
+ buildFileIndex,
462
+ sameHash,
463
+ diffFileIndex
464
+ };
465
+ //# sourceMappingURL=chunk-2DY4PUR4.js.map
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/services/version.ts
4
+ import { createRequire } from "module";
5
+ var PACKAGE_NAME = "project-athena";
6
+ var AUTHOR = "Sabarivasan";
7
+ var NPM_URL = `https://www.npmjs.com/package/${PACKAGE_NAME}`;
8
+ var REPO_URL = "https://github.com/Sabari-Vasan-SM/athena";
9
+ var WEBSITE_URL = "https://athena.sabari.me";
10
+ function readVersion() {
11
+ try {
12
+ const require2 = createRequire(import.meta.url);
13
+ for (const p of ["../package.json", "../../package.json"]) {
14
+ try {
15
+ const pkg = require2(p);
16
+ if (pkg.name === PACKAGE_NAME && pkg.version) return pkg.version;
17
+ } catch {
18
+ }
19
+ }
20
+ } catch {
21
+ }
22
+ return "0.0.0-unknown";
23
+ }
24
+ var ATHENA_VERSION = readVersion();
25
+
26
+ export {
27
+ AUTHOR,
28
+ NPM_URL,
29
+ REPO_URL,
30
+ WEBSITE_URL,
31
+ ATHENA_VERSION
32
+ };
33
+ //# sourceMappingURL=chunk-2R6PXY6Z.js.map