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.
- package/CHANGELOG.md +63 -0
- package/README.md +61 -12
- package/dist/activity-BKW7G4I5.js +77 -0
- package/dist/agents-DFSNEOP5.js +69 -0
- package/dist/ai-ASL4FFQ7.js +96 -0
- package/dist/analyze-ZY3MXBOI.js +50 -0
- package/dist/chunk-2DY4PUR4.js +465 -0
- package/dist/chunk-2R6PXY6Z.js +33 -0
- package/dist/chunk-3IS2X2Z7.js +89 -0
- package/dist/chunk-3XSDI5WF.js +179 -0
- package/dist/chunk-65TSRDQ3.js +124 -0
- package/dist/chunk-673EPYYH.js +105 -0
- package/dist/chunk-7DZK6WSQ.js +65 -0
- package/dist/chunk-AD4OZCU2.js +34 -0
- package/dist/chunk-DZDI2HRT.js +169 -0
- package/dist/chunk-ELKFRTZT.js +221 -0
- package/dist/chunk-EUPETUWA.js +14 -0
- package/dist/chunk-FQEGEVLY.js +38 -0
- package/dist/chunk-FVCDMQUQ.js +343 -0
- package/dist/chunk-GGXPG6OE.js +904 -0
- package/dist/chunk-GHFRQAYT.js +112 -0
- package/dist/chunk-GR4DL2VM.js +47 -0
- package/dist/chunk-INT3IGMQ.js +111 -0
- package/dist/chunk-IQQBAWSM.js +90 -0
- package/dist/chunk-JCGDR4UV.js +182 -0
- package/dist/chunk-KBO7XRBW.js +26 -0
- package/dist/chunk-KHMBNT66.js +399 -0
- package/dist/chunk-LEMPCNAY.js +209 -0
- package/dist/chunk-MPG3IEGB.js +2691 -0
- package/dist/chunk-OLPSEQS5.js +264 -0
- package/dist/chunk-PVCIGUIT.js +64 -0
- package/dist/chunk-QCC4VJUH.js +217 -0
- package/dist/chunk-QLW657NT.js +95 -0
- package/dist/chunk-RVTNSNGK.js +182 -0
- package/dist/chunk-RXNRMIZP.js +390 -0
- package/dist/chunk-SHKXELUX.js +295 -0
- package/dist/chunk-SJ4DQFIJ.js +379 -0
- package/dist/chunk-SVTEJYMS.js +227 -0
- package/dist/chunk-TDNZ2RYA.js +702 -0
- package/dist/chunk-UAZLPNVC.js +108 -0
- package/dist/chunk-VSYBC3VY.js +28 -0
- package/dist/chunk-WVQC2EGO.js +166 -0
- package/dist/chunk-XDCKZ5TV.js +436 -0
- package/dist/chunk-XFN7UZCS.js +35 -0
- package/dist/chunk-XW3MLHI2.js +32 -0
- package/dist/chunk-YRIS7ZS6.js +919 -0
- package/dist/chunk-Z6QWMJLK.js +124 -0
- package/dist/chunk-ZB7LC6FP.js +221 -0
- package/dist/clean-MDBYH62P.js +23 -0
- package/dist/cli.js +18 -10716
- package/dist/context-PLPCAPCO.js +161 -0
- package/dist/doctor-BX677KRE.js +69 -0
- package/dist/event-TNT6CQZR.js +101 -0
- package/dist/git-hook-YZNZGAAP.js +252 -0
- package/dist/init-WQCAOJ2R.js +38 -0
- package/dist/mcp-U7EPH43L.js +393 -0
- package/dist/memory-VSVNLEJC.js +393 -0
- package/dist/open-UJZRYF4E.js +1450 -0
- package/dist/program-CWJNGCTZ.js +165 -0
- package/dist/review-OOKXSYMP.js +117 -0
- package/dist/rules-4FXJUCXB.js +83 -0
- package/dist/security-LME3XFL3.js +177 -0
- package/dist/staged-check-X24C3YRD.js +502 -0
- package/dist/status-RR57ZNGI.js +81 -0
- package/dist/sync-KFJANRXF.js +218 -0
- package/dist/watch-CY7QHIYN.js +123 -0
- package/dist/web/assets/{Editor-DAXvniWB.js → Editor-Bnilgch0.js} +1 -1
- package/dist/web/assets/{abnfDiagram-O67JEVCF-Az7EW1BC.js → abnfDiagram-O67JEVCF-DW9LWr8f.js} +1 -1
- package/dist/web/assets/architecture-7GRP2DOG-BWDZV2yn.js +1 -0
- package/dist/web/assets/{architectureDiagram-NJMV4G6O-BxeXqSTn.js → architectureDiagram-NJMV4G6O-0eKeQRhj.js} +1 -1
- package/dist/web/assets/{blockDiagram-BEXU5L5S-aeKZl9lJ.js → blockDiagram-BEXU5L5S-D5i3-ogI.js} +1 -1
- package/dist/web/assets/{c4Diagram-YGBWAQC7-CgmntYsG.js → c4Diagram-YGBWAQC7-BAWQZrEu.js} +1 -1
- package/dist/web/assets/channel-Bkh05NKd.js +1 -0
- package/dist/web/assets/{chunk-3FUC2YCW-BpmRD2yo.js → chunk-3FUC2YCW-fKRGOZF3.js} +1 -1
- package/dist/web/assets/{chunk-5DYCD2WN-CRIFDwod.js → chunk-5DYCD2WN-C5jOlx42.js} +1 -1
- package/dist/web/assets/{chunk-742MDFTN-hRW9VeNV.js → chunk-742MDFTN-urY4RP_A.js} +1 -1
- package/dist/web/assets/{chunk-7INBJB4K-C6VEpT0Y.js → chunk-7INBJB4K-nk48sKqZ.js} +1 -1
- package/dist/web/assets/{chunk-7M6MHVWA-Dnajov2w.js → chunk-7M6MHVWA-BfBD9X0F.js} +1 -1
- package/dist/web/assets/{chunk-7PRAP22T-DSAc0_CZ.js → chunk-7PRAP22T-jYf59QlI.js} +1 -1
- package/dist/web/assets/{chunk-GTNCS2PH-CefpYUjb.js → chunk-GTNCS2PH-BOCxryKf.js} +1 -1
- package/dist/web/assets/{chunk-GWA4HPMP-MLNflMmF.js → chunk-GWA4HPMP-CFpaW0ru.js} +1 -1
- package/dist/web/assets/{chunk-MBY4JIJT-D3GmmEiU.js → chunk-MBY4JIJT-BIB8HctN.js} +1 -1
- package/dist/web/assets/{chunk-NETBCI7D-DzTj6xVk.js → chunk-NETBCI7D-CpUUK6rh.js} +1 -1
- package/dist/web/assets/{chunk-O7XYJQB3-D1d0hCko.js → chunk-O7XYJQB3-BNx_V4Oi.js} +1 -1
- package/dist/web/assets/{chunk-UA2S7LBM-C9hIFeWR.js → chunk-UA2S7LBM-D3tMiIOY.js} +1 -1
- package/dist/web/assets/{chunk-WEXAMYUT-BFGtcjsi.js → chunk-WEXAMYUT-CC-3C32l.js} +1 -1
- package/dist/web/assets/{chunk-Z7XXMR3K-BRZ8_wtE.js → chunk-Z7XXMR3K-BzhxKQUc.js} +1 -1
- package/dist/web/assets/{chunk-ZIGJFQKS-CTKOioal.js → chunk-ZIGJFQKS-D4tBHzxg.js} +1 -1
- package/dist/web/assets/{classDiagram-v2-NBCMYWYE-DJGKtWJv.js → classDiagram-v2-NBCMYWYE-BbtyanXR.js} +1 -1
- package/dist/web/assets/{cynefin-OW5HDTMX-hNq1kGmA.js → cynefin-OW5HDTMX-BIArb8W1.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-VND7K2PF-sODQM_ZZ.js → cynefinDiagram-VND7K2PF-BrAcfkXL.js} +1 -1
- package/dist/web/assets/{dagre-6A5THRUB-DEzTThIf.js → dagre-6A5THRUB-BcHXRl4s.js} +1 -1
- package/dist/web/assets/{diagram-22UHCM2B-s_eellsa.js → diagram-22UHCM2B-Dx9tSCdE.js} +1 -1
- package/dist/web/assets/{diagram-3UASUU5V-B2onY_Xw.js → diagram-3UASUU5V-CLLDP9yH.js} +1 -1
- package/dist/web/assets/{diagram-ATOU4E4O-B8V6s13s.js → diagram-ATOU4E4O-B46H9iPt.js} +1 -1
- package/dist/web/assets/{diagram-CDSNMT55-CJxiE6mf.js → diagram-CDSNMT55-CYtvB6aG.js} +1 -1
- package/dist/web/assets/{diagram-MLGK6HIB-m5OHPLAp.js → diagram-MLGK6HIB-Dar7ktoX.js} +1 -1
- package/dist/web/assets/{diagram-MPIPVDR6-Buvvrk_A.js → diagram-MPIPVDR6-C-DljnYN.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-ZINNZB2B-CpL7Wt3R.js → ebnfDiagram-ZINNZB2B-Dv_3jWnh.js} +1 -1
- package/dist/web/assets/{elk-276RUBZZ-CwYhbsXa.js → elk-276RUBZZ-DnHciE9k.js} +1 -1
- package/dist/web/assets/{erDiagram-OPXOYQCR-DNikci2r.js → erDiagram-OPXOYQCR-Bn46KrHz.js} +1 -1
- package/dist/web/assets/eventmodeling-NTZA5JFV-DYwoptMd.js +1 -0
- package/dist/web/assets/flowDiagram-KWPJA3E3-y4rS0gIB.js +1 -0
- package/dist/web/assets/{ganttDiagram-FUAMR5RP-d-ynDlvJ.js → ganttDiagram-FUAMR5RP-DOjjJtqz.js} +1 -1
- package/dist/web/assets/{gitGraph-4MIJSDKK-DwIeBI91.js → gitGraph-4MIJSDKK-BIoMXJRQ.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-X574FWY7-04RZccu6.js → gitGraphDiagram-X574FWY7-BsJ01R0v.js} +1 -1
- package/dist/web/assets/{index-C3-8uYXr.js → index-Cen0jOXj.js} +5 -4
- package/dist/web/assets/{index-DKE3ojTe.css → index-WwRY6n68.css} +1 -1
- package/dist/web/assets/{info-A6RAGUB7-xf4Nzgv-.js → info-A6RAGUB7-BmlXzRWj.js} +1 -1
- package/dist/web/assets/{infoDiagram-VRGFBTTK-BYjw0eiK.js → infoDiagram-VRGFBTTK-CYPJPmBP.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-Bx9s4nPM.js → ishikawaDiagram-OU5B5YK6-YN96geky.js} +1 -1
- package/dist/web/assets/{journeyDiagram-ZHPQQLJL-DrdHs7B-.js → journeyDiagram-ZHPQQLJL-20juojKY.js} +1 -1
- package/dist/web/assets/{kanban-definition-PNTS6WVX-CVd8v2Te.js → kanban-definition-PNTS6WVX-BhCKIEbP.js} +1 -1
- package/dist/web/assets/{line-V_WrJ-6y.js → line-BERsMFG6.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-B-ilyEpO.js → mermaid-parser.core-Bis_wTB-.js} +3 -3
- package/dist/web/assets/{mermaid.core-Bnyaa0nF.js → mermaid.core-DMsM3ZX7.js} +4 -4
- package/dist/web/assets/{mindmap-definition-NLK3R4M7-B3AbvHPR.js → mindmap-definition-NLK3R4M7-BkcGdOfv.js} +1 -1
- package/dist/web/assets/{packet-AYTQ26CC-CBAIUXIc.js → packet-AYTQ26CC-Bh30-JJX.js} +1 -1
- package/dist/web/assets/{pegDiagram-GJSIUBJH-DIdeELUM.js → pegDiagram-GJSIUBJH-DxnIo9RF.js} +1 -1
- package/dist/web/assets/{pie-WAS4IAKB-a1r3tzPR.js → pie-WAS4IAKB-ChaTr-GD.js} +1 -1
- package/dist/web/assets/{pieDiagram-5QR66LMP-pQBEio0x.js → pieDiagram-5QR66LMP-CzAp78FM.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-O4NWA36T-CkJK5Y03.js → quadrantDiagram-O4NWA36T-BDSE-cfY.js} +1 -1
- package/dist/web/assets/{radar-RG4KPBEZ-BZfLYjx5.js → radar-RG4KPBEZ-DgJ-o_dq.js} +1 -1
- package/dist/web/assets/{railroad-74A4TZTK-CuIW1ZAU.js → railroad-74A4TZTK-C8MmcBKz.js} +1 -1
- package/dist/web/assets/railroad-abnf-HS5TGJTU-CTLXjcdP.js +1 -0
- package/dist/web/assets/railroad-ebnf-LZEXJU2U-ggWsTTL0.js +1 -0
- package/dist/web/assets/railroad-peg-WCYAUIDC-D1mCX705.js +1 -0
- package/dist/web/assets/{railroadDiagram-XR7U4H2S-_BpGE8aJ.js → railroadDiagram-XR7U4H2S-DfbcWRNh.js} +1 -1
- package/dist/web/assets/{requirementDiagram-PLB6GJNP-CnzF90GS.js → requirementDiagram-PLB6GJNP-DM3nksxs.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-IPEJSGJF-DYeisDj_.js → sankeyDiagram-IPEJSGJF-DthUpiiC.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-PO4LG4MO-ll9cBvca.js → sequenceDiagram-PO4LG4MO-DLA3nuvA.js} +1 -1
- package/dist/web/assets/{stateDiagram-v2-GCMORJYK-CdKcOIKQ.js → stateDiagram-v2-GCMORJYK-B0X6ihBW.js} +1 -1
- package/dist/web/assets/{swimlanes-2SLR337P-y6piTWEW.js → swimlanes-2SLR337P-B4qWe6vE.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-TC7HE7FX-ClIk3e7S.js +8 -0
- package/dist/web/assets/{timeline-definition-EJHVYXUP-DuC_4xK0.js → timeline-definition-EJHVYXUP-F-cDzYrj.js} +1 -1
- package/dist/web/assets/{treeView-Q6P3EWNA-gCQFmND0.js → treeView-Q6P3EWNA-BaI30nLZ.js} +1 -1
- package/dist/web/assets/{treemap-WGGIJYW6-BQ2010OY.js → treemap-WGGIJYW6-D2jGGoMl.js} +1 -1
- package/dist/web/assets/{usecaseDiagram-POWQR4AR-CCTqzHz6.js → usecaseDiagram-POWQR4AR-B9qoAQCy.js} +1 -1
- package/dist/web/assets/{vennDiagram-UO4OBE2U-0_O7guNq.js → vennDiagram-UO4OBE2U-DGVNOaKO.js} +1 -1
- package/dist/web/assets/{wardley-WFR3VGLG-SsX7uMDs.js → wardley-WFR3VGLG-DbKqhNAh.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-VNRHLVJA-DlqH6Hox.js → wardleyDiagram-VNRHLVJA-BgZ1gprw.js} +1 -1
- package/dist/web/assets/{xychartDiagram-PMCCYNJV-nr4UYDtd.js → xychartDiagram-PMCCYNJV-D73RYid1.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +4 -2
- package/dist/web/assets/architecture-7GRP2DOG-blXBarB2.js +0 -1
- package/dist/web/assets/channel-f4QzewnN.js +0 -1
- package/dist/web/assets/eventmodeling-NTZA5JFV-CA_ZFPZ2.js +0 -1
- package/dist/web/assets/flowDiagram-KWPJA3E3-DObsBuWT.js +0 -1
- package/dist/web/assets/railroad-abnf-HS5TGJTU-6B4Ffbxn.js +0 -1
- package/dist/web/assets/railroad-ebnf-LZEXJU2U-BGL9lIQc.js +0 -1
- package/dist/web/assets/railroad-peg-WCYAUIDC-DcqxTem7.js +0 -1
- package/dist/web/assets/swimlanesDiagram-TC7HE7FX-DFS8a3jO.js +0 -8
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
HISTORY_SECTIONS
|
|
4
|
+
} from "./chunk-KHMBNT66.js";
|
|
5
|
+
import "./chunk-WVQC2EGO.js";
|
|
6
|
+
import "./chunk-GR4DL2VM.js";
|
|
7
|
+
import {
|
|
8
|
+
planKnowledge
|
|
9
|
+
} from "./chunk-GGXPG6OE.js";
|
|
10
|
+
import {
|
|
11
|
+
REAL_ENV_NAMES,
|
|
12
|
+
SCAN_FILE,
|
|
13
|
+
analyzeProject,
|
|
14
|
+
redactDeep
|
|
15
|
+
} from "./chunk-MPG3IEGB.js";
|
|
16
|
+
import {
|
|
17
|
+
mapLimit
|
|
18
|
+
} from "./chunk-ELKFRTZT.js";
|
|
19
|
+
import "./chunk-2R6PXY6Z.js";
|
|
20
|
+
import {
|
|
21
|
+
readLocalConfig
|
|
22
|
+
} from "./chunk-PVCIGUIT.js";
|
|
23
|
+
import {
|
|
24
|
+
git,
|
|
25
|
+
gitInfo,
|
|
26
|
+
isGitRepo
|
|
27
|
+
} from "./chunk-OLPSEQS5.js";
|
|
28
|
+
import "./chunk-SVTEJYMS.js";
|
|
29
|
+
import "./chunk-QCC4VJUH.js";
|
|
30
|
+
import "./chunk-JCGDR4UV.js";
|
|
31
|
+
import "./chunk-YRIS7ZS6.js";
|
|
32
|
+
import "./chunk-GHFRQAYT.js";
|
|
33
|
+
import "./chunk-INT3IGMQ.js";
|
|
34
|
+
import "./chunk-673EPYYH.js";
|
|
35
|
+
import "./chunk-FQEGEVLY.js";
|
|
36
|
+
import {
|
|
37
|
+
cacheRoot,
|
|
38
|
+
loadConfig,
|
|
39
|
+
readFileIndex,
|
|
40
|
+
readState
|
|
41
|
+
} from "./chunk-2DY4PUR4.js";
|
|
42
|
+
import {
|
|
43
|
+
readTextIfExists,
|
|
44
|
+
writeFileAtomic
|
|
45
|
+
} from "./chunk-IQQBAWSM.js";
|
|
46
|
+
import {
|
|
47
|
+
AthenaError,
|
|
48
|
+
EXIT
|
|
49
|
+
} from "./chunk-AD4OZCU2.js";
|
|
50
|
+
import "./chunk-XW3MLHI2.js";
|
|
51
|
+
import {
|
|
52
|
+
athenaDir
|
|
53
|
+
} from "./chunk-EUPETUWA.js";
|
|
54
|
+
|
|
55
|
+
// src/services/staged-check.ts
|
|
56
|
+
import { promises as fs2 } from "fs";
|
|
57
|
+
import path2 from "path";
|
|
58
|
+
import crypto from "crypto";
|
|
59
|
+
|
|
60
|
+
// src/core/git/index-snapshot.ts
|
|
61
|
+
import { spawn } from "child_process";
|
|
62
|
+
import { lstatSync, promises as fs } from "fs";
|
|
63
|
+
import path from "path";
|
|
64
|
+
var IndexSnapshotError = class extends Error {
|
|
65
|
+
};
|
|
66
|
+
async function gitOrFail(cwd, args, signal) {
|
|
67
|
+
const r = await git(cwd, args, { signal, timeoutMs: 12e4, maxBytes: 512 * 1024 * 1024 });
|
|
68
|
+
signal?.throwIfAborted();
|
|
69
|
+
if (!r.ok) throw new IndexSnapshotError(`git ${args[0]} failed: ${r.stderr.trim() || `exit ${r.code}`}`);
|
|
70
|
+
return r.stdout;
|
|
71
|
+
}
|
|
72
|
+
async function readIndex(cwd, signal) {
|
|
73
|
+
const out = await gitOrFail(cwd, ["ls-files", "-s", "-v", "--debug", "-z"], signal);
|
|
74
|
+
const entries = [];
|
|
75
|
+
const unmerged = /* @__PURE__ */ new Set();
|
|
76
|
+
let i = 0;
|
|
77
|
+
while (i < out.length) {
|
|
78
|
+
const nul = out.indexOf("\0", i);
|
|
79
|
+
if (nul < 0) break;
|
|
80
|
+
const rec = out.slice(i, nul);
|
|
81
|
+
i = nul + 1;
|
|
82
|
+
const stat = {};
|
|
83
|
+
while (out.startsWith(" ", i)) {
|
|
84
|
+
const nl = out.indexOf("\n", i);
|
|
85
|
+
const end = nl < 0 ? out.length : nl;
|
|
86
|
+
for (const part of out.slice(i + 2, end).split(" ")) {
|
|
87
|
+
const c = part.indexOf(": ");
|
|
88
|
+
if (c > 0) stat[part.slice(0, c)] = part.slice(c + 2);
|
|
89
|
+
}
|
|
90
|
+
i = end + 1;
|
|
91
|
+
}
|
|
92
|
+
const m = /^(\S) (\d{6}) ([0-9a-f]{40,64}) (\d)\t([\s\S]+)$/.exec(rec);
|
|
93
|
+
if (!m) throw new IndexSnapshotError(`unexpected \`git ls-files\` output: ${JSON.stringify(rec.slice(0, 80))}`);
|
|
94
|
+
const [, tag, mode, sha, stage, p] = m;
|
|
95
|
+
if (stage !== "0") {
|
|
96
|
+
unmerged.add(p);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const mt = /^(\d+):(\d+)$/.exec(stat.mtime ?? "");
|
|
100
|
+
const size = /^\d+$/.test(stat.size ?? "") ? Number(stat.size) : void 0;
|
|
101
|
+
entries.push({ mode, sha, path: p, trustWorktree: tag === "H", size, mtimeMs: mt ? Number(mt[1]) * 1e3 + Number(mt[2]) / 1e6 : void 0 });
|
|
102
|
+
}
|
|
103
|
+
if (unmerged.size) throw new IndexSnapshotError(`the index has unresolved merge conflicts (${[...unmerged].slice(0, 3).join(", ")}${unmerged.size > 3 ? ", \u2026" : ""})`);
|
|
104
|
+
return entries;
|
|
105
|
+
}
|
|
106
|
+
function inside(dest, rel) {
|
|
107
|
+
const abs = path.resolve(dest, rel);
|
|
108
|
+
const r = path.relative(dest, abs);
|
|
109
|
+
if (!r || r.startsWith("..") || path.isAbsolute(r)) throw new IndexSnapshotError(`refusing index path outside the project: ${rel}`);
|
|
110
|
+
return abs;
|
|
111
|
+
}
|
|
112
|
+
function needsRealContent(rel) {
|
|
113
|
+
return rel.startsWith(".athena/") || rel === ".gitignore" || rel.endsWith("/.gitignore");
|
|
114
|
+
}
|
|
115
|
+
async function catFileBatch(cwd, shas, onBlob, signal) {
|
|
116
|
+
if (!shas.length) return;
|
|
117
|
+
const child = spawn("git", ["cat-file", "--batch"], { cwd, windowsHide: true, stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, GIT_TERMINAL_PROMPT: "0", LC_ALL: "C" } });
|
|
118
|
+
const onAbort = () => child.kill("SIGKILL");
|
|
119
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
120
|
+
let stderr = "";
|
|
121
|
+
child.stderr.on("data", (d) => {
|
|
122
|
+
if (stderr.length < 4096) stderr += d.toString("utf8");
|
|
123
|
+
});
|
|
124
|
+
const exited = new Promise((resolve) => {
|
|
125
|
+
child.on("close", (code) => resolve(code));
|
|
126
|
+
child.on("error", () => resolve(null));
|
|
127
|
+
});
|
|
128
|
+
child.stdin.on("error", () => {
|
|
129
|
+
});
|
|
130
|
+
child.stdin.end(`${shas.join("\n")}
|
|
131
|
+
`);
|
|
132
|
+
try {
|
|
133
|
+
let buf = Buffer.alloc(0);
|
|
134
|
+
let want = null;
|
|
135
|
+
let received = 0;
|
|
136
|
+
for await (const chunk of child.stdout) {
|
|
137
|
+
buf = buf.length ? Buffer.concat([buf, chunk]) : chunk;
|
|
138
|
+
for (; ; ) {
|
|
139
|
+
if (!want) {
|
|
140
|
+
const nl = buf.indexOf(10);
|
|
141
|
+
if (nl < 0) break;
|
|
142
|
+
const header = buf.subarray(0, nl).toString("utf8");
|
|
143
|
+
buf = buf.subarray(nl + 1);
|
|
144
|
+
const h = /^([0-9a-f]+) (\w+) (\d+)$/.exec(header);
|
|
145
|
+
if (!h) throw new IndexSnapshotError(`git cat-file: ${header}`);
|
|
146
|
+
want = { sha: h[1], size: Number(h[3]) };
|
|
147
|
+
}
|
|
148
|
+
if (buf.length < want.size + 1) break;
|
|
149
|
+
const data = Buffer.from(buf.subarray(0, want.size));
|
|
150
|
+
buf = buf.subarray(want.size + 1);
|
|
151
|
+
const done = want;
|
|
152
|
+
want = null;
|
|
153
|
+
received++;
|
|
154
|
+
await onBlob(done.sha, data);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const code = await exited;
|
|
158
|
+
signal?.throwIfAborted();
|
|
159
|
+
if (code !== 0 || received !== shas.length) throw new IndexSnapshotError(`git cat-file failed: ${stderr.trim() || `exit ${code}`}`);
|
|
160
|
+
} finally {
|
|
161
|
+
signal?.removeEventListener("abort", onAbort);
|
|
162
|
+
if (child.exitCode === null) child.kill("SIGKILL");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
var placeholderWant = (size, mtimeMs) => ({ kind: "placeholder", key: `p:${size}:${mtimeMs}`, size, mtimeMs });
|
|
166
|
+
async function submoduleFiles(root, rel, out) {
|
|
167
|
+
let ents;
|
|
168
|
+
try {
|
|
169
|
+
ents = await fs.readdir(path.join(root, rel), { withFileTypes: true });
|
|
170
|
+
} catch {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
for (const e of ents) {
|
|
174
|
+
if (e.name === ".git") continue;
|
|
175
|
+
const r = `${rel}/${e.name}`;
|
|
176
|
+
if (e.isDirectory()) await submoduleFiles(root, r, out);
|
|
177
|
+
else if (e.isFile()) {
|
|
178
|
+
const st = await fs.stat(path.join(root, r)).catch(() => null);
|
|
179
|
+
if (st) out.set(r, placeholderWant(st.size, st.mtimeMs));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function writePlaceholder(file, size, mtimeMs) {
|
|
184
|
+
const fh = await fs.open(file, "w");
|
|
185
|
+
try {
|
|
186
|
+
if (size) await fh.truncate(size);
|
|
187
|
+
const t = (Math.floor(mtimeMs) + 0.5) / 1e3;
|
|
188
|
+
await fh.utimes(t, t);
|
|
189
|
+
} finally {
|
|
190
|
+
await fh.close();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function place(file, make) {
|
|
194
|
+
try {
|
|
195
|
+
await make();
|
|
196
|
+
} catch (err) {
|
|
197
|
+
const code = err.code;
|
|
198
|
+
if (code !== "EISDIR" && code !== "EEXIST" && code !== "ENOTDIR" && code !== "ENOENT" && code !== "EPERM") throw err;
|
|
199
|
+
await fs.rm(file, { recursive: true, force: true });
|
|
200
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
201
|
+
await make();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function ensureDir(dir) {
|
|
205
|
+
try {
|
|
206
|
+
await fs.mkdir(dir, { recursive: true });
|
|
207
|
+
} catch (err) {
|
|
208
|
+
let d = dir;
|
|
209
|
+
for (; ; ) {
|
|
210
|
+
const st = await fs.lstat(d).catch(() => null);
|
|
211
|
+
if (st && !st.isDirectory()) break;
|
|
212
|
+
const up = path.dirname(d);
|
|
213
|
+
if (up === d) throw err;
|
|
214
|
+
d = up;
|
|
215
|
+
}
|
|
216
|
+
await fs.rm(d, { force: true });
|
|
217
|
+
await fs.mkdir(dir, { recursive: true });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function updateIndexSnapshot(root, dest, opts) {
|
|
221
|
+
const { signal } = opts;
|
|
222
|
+
const [entries, dirtyOut] = await Promise.all([readIndex(root, signal), gitOrFail(root, ["diff-files", "--name-only", "--relative", "-z"], signal)]);
|
|
223
|
+
const dirty = new Set(dirtyOut.split("\0").filter(Boolean));
|
|
224
|
+
const want = /* @__PURE__ */ new Map();
|
|
225
|
+
const needBlob = [];
|
|
226
|
+
for (const [i, e] of entries.entries()) {
|
|
227
|
+
if (i % 1024 === 0) signal?.throwIfAborted();
|
|
228
|
+
inside(dest, e.path);
|
|
229
|
+
if (e.mode === "120000") want.set(e.path, { kind: "symlink", key: `l:${e.sha}`, sha: e.sha });
|
|
230
|
+
else if (e.mode === "100644" || e.mode === "100755") {
|
|
231
|
+
if (e.trustWorktree && !dirty.has(e.path) && !needsRealContent(e.path)) {
|
|
232
|
+
if (e.size !== void 0 && e.mtimeMs !== void 0 && e.size < 2 ** 32 - 1) {
|
|
233
|
+
want.set(e.path, placeholderWant(e.size, e.mtimeMs));
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const st = lstatSync(path.join(root, e.path), { throwIfNoEntry: false });
|
|
237
|
+
if (st?.isFile()) {
|
|
238
|
+
want.set(e.path, placeholderWant(st.size, st.mtimeMs));
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
needBlob.push(e);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
for (const e of entries) if (e.mode === "160000") await submoduleFiles(root, e.path, want);
|
|
246
|
+
const prev = opts.previous ?? {};
|
|
247
|
+
const sizeFromKey = (key, sha) => key?.startsWith(`s:${sha}:`) ? Number(key.split(":")[2]) : key?.startsWith(`b:${sha}:`) ? Number(key.split(":")[2]) : void 0;
|
|
248
|
+
const unknown = [...new Set(needBlob.filter((e) => sizeFromKey(prev[e.path], e.sha) === void 0).map((e) => e.sha))];
|
|
249
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
250
|
+
if (unknown.length) {
|
|
251
|
+
const r = await git(root, ["cat-file", "--batch-check=%(objectname) %(objectsize)"], { signal, input: `${unknown.join("\n")}
|
|
252
|
+
`, timeoutMs: 12e4, maxBytes: 64 * 1024 * 1024 });
|
|
253
|
+
if (!r.ok) throw new IndexSnapshotError(`git cat-file failed: ${r.stderr.trim()}`);
|
|
254
|
+
for (const line of r.stdout.split("\n")) {
|
|
255
|
+
const m = /^([0-9a-f]+) (\d+)$/.exec(line);
|
|
256
|
+
if (m) sizes.set(m[1], Number(m[2]));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
for (const e of needBlob) {
|
|
260
|
+
const size = sizes.get(e.sha) ?? sizeFromKey(prev[e.path], e.sha);
|
|
261
|
+
if (size === void 0) throw new IndexSnapshotError(`object ${e.sha} for ${e.path} is missing from the repository`);
|
|
262
|
+
if (size > opts.maxFileBytes) want.set(e.path, { kind: "sparse", key: `s:${e.sha}:${size}`, size });
|
|
263
|
+
else want.set(e.path, { kind: "blob", key: `b:${e.sha}:${size}`, sha: e.sha });
|
|
264
|
+
}
|
|
265
|
+
const removals = Object.keys(prev).filter((p) => want.get(p)?.key !== prev[p]);
|
|
266
|
+
await mapLimit(removals, 64, (p) => fs.rm(inside(dest, p), { force: true, recursive: true }));
|
|
267
|
+
const todo = [...want].filter(([p, w]) => prev[p] !== w.key);
|
|
268
|
+
const dirs = new Set(todo.map(([p]) => path.dirname(inside(dest, p))));
|
|
269
|
+
for (const d of [...dirs].sort()) await ensureDir(d);
|
|
270
|
+
const byBlob = /* @__PURE__ */ new Map();
|
|
271
|
+
await mapLimit(todo, 64, async ([p, w]) => {
|
|
272
|
+
signal?.throwIfAborted();
|
|
273
|
+
const file = inside(dest, p);
|
|
274
|
+
if (w.kind === "placeholder") await place(file, () => writePlaceholder(file, w.size, w.mtimeMs));
|
|
275
|
+
else if (w.kind === "sparse") await place(file, () => writePlaceholder(file, w.size, Date.now()));
|
|
276
|
+
else byBlob.set(w.sha, [...byBlob.get(w.sha) ?? [], p]);
|
|
277
|
+
});
|
|
278
|
+
const links = [];
|
|
279
|
+
await catFileBatch(
|
|
280
|
+
root,
|
|
281
|
+
[...byBlob.keys()],
|
|
282
|
+
async (sha, data) => {
|
|
283
|
+
for (const p of byBlob.get(sha) ?? []) {
|
|
284
|
+
if (want.get(p).kind === "symlink") links.push([p, data.toString("utf8")]);
|
|
285
|
+
else {
|
|
286
|
+
const file = inside(dest, p);
|
|
287
|
+
await place(file, () => fs.writeFile(file, data));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
signal
|
|
292
|
+
);
|
|
293
|
+
for (const [p, target] of links) {
|
|
294
|
+
const at = inside(dest, p);
|
|
295
|
+
const rel = path.relative(dest, path.resolve(path.dirname(at), target));
|
|
296
|
+
if (path.isAbsolute(target) || !rel || rel.startsWith("..") || path.isAbsolute(rel)) continue;
|
|
297
|
+
await place(at, () => fs.symlink(target, at)).catch(() => {
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
const manifest = {};
|
|
301
|
+
const placeholders = /* @__PURE__ */ new Set();
|
|
302
|
+
let fromIndex = 0;
|
|
303
|
+
for (const [p, w] of want) {
|
|
304
|
+
manifest[p] = w.key;
|
|
305
|
+
if (w.kind === "placeholder") placeholders.add(p);
|
|
306
|
+
else if (w.kind === "blob") fromIndex++;
|
|
307
|
+
}
|
|
308
|
+
return { manifest, entries: entries.filter((e) => e.mode !== "160000").length, placeholders, fromIndex, written: todo.length, removed: removals.length };
|
|
309
|
+
}
|
|
310
|
+
function snapshotReader(snapRoot, workRoot, placeholders) {
|
|
311
|
+
return async (abs) => {
|
|
312
|
+
const real = await fs.realpath(abs).catch(() => abs);
|
|
313
|
+
const rel = path.relative(snapRoot, real).split(path.sep).join("/");
|
|
314
|
+
if (placeholders.has(rel)) return fs.readFile(path.join(workRoot, rel));
|
|
315
|
+
return fs.readFile(abs);
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/services/staged-check.ts
|
|
320
|
+
var STAGED_DIR = "staged";
|
|
321
|
+
var MANIFEST_VERSION = 1;
|
|
322
|
+
var STALE_MS = 60 * 60 * 1e3;
|
|
323
|
+
async function readManifest(file, root) {
|
|
324
|
+
try {
|
|
325
|
+
const m = JSON.parse(await fs2.readFile(file, "utf8"));
|
|
326
|
+
return m.v === MANIFEST_VERSION && m.root === root && m.entries && typeof m.entries === "object" ? m.entries : null;
|
|
327
|
+
} catch {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async function lock(file) {
|
|
332
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
333
|
+
try {
|
|
334
|
+
const fh = await fs2.open(file, "wx");
|
|
335
|
+
await fh.writeFile(String(process.pid));
|
|
336
|
+
await fh.close();
|
|
337
|
+
return () => fs2.rm(file, { force: true });
|
|
338
|
+
} catch (err) {
|
|
339
|
+
if (err.code !== "EEXIST") return null;
|
|
340
|
+
const [pid, st] = await Promise.all([fs2.readFile(file, "utf8").catch(() => ""), fs2.stat(file).catch(() => null)]);
|
|
341
|
+
let alive = Date.now() - (st?.mtimeMs ?? 0) < STALE_MS;
|
|
342
|
+
try {
|
|
343
|
+
if (alive && Number(pid) > 0) process.kill(Number(pid), 0);
|
|
344
|
+
} catch (e) {
|
|
345
|
+
if (e.code === "ESRCH") alive = false;
|
|
346
|
+
}
|
|
347
|
+
if (alive) return null;
|
|
348
|
+
await fs2.rm(file, { force: true });
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
async function linkOrCopy(src, dst) {
|
|
354
|
+
await fs2.mkdir(path2.dirname(dst), { recursive: true });
|
|
355
|
+
await fs2.link(src, dst).catch(() => fs2.copyFile(src, dst));
|
|
356
|
+
}
|
|
357
|
+
async function addLocalInputs(root, snapRoot, snap) {
|
|
358
|
+
const dir = athenaDir(root);
|
|
359
|
+
const snapAthena = athenaDir(snapRoot);
|
|
360
|
+
await fs2.rm(path2.join(snapRoot, ".git"), { recursive: true, force: true });
|
|
361
|
+
await Promise.all(REAL_ENV_NAMES.filter((n) => !snap.manifest[n]).map((n) => fs2.rm(path2.join(snapRoot, n), { force: true, recursive: true })));
|
|
362
|
+
const facts = path2.join(cacheRoot(dir), "facts");
|
|
363
|
+
const snapFacts = path2.join(cacheRoot(snapAthena), "facts");
|
|
364
|
+
const [names, old] = await Promise.all([fs2.readdir(facts).catch(() => []), fs2.readdir(snapFacts).catch(() => [])]);
|
|
365
|
+
const keep = new Set(names);
|
|
366
|
+
await Promise.all(old.filter((n) => !keep.has(n)).map((n) => fs2.rm(path2.join(snapFacts, n), { force: true, recursive: true })));
|
|
367
|
+
await Promise.all(
|
|
368
|
+
names.map(async (n) => {
|
|
369
|
+
const [a, b] = await Promise.all([fs2.stat(path2.join(facts, n)).catch(() => null), fs2.stat(path2.join(snapFacts, n)).catch(() => null)]);
|
|
370
|
+
if (!a?.isFile() || b && a.ino === b.ino && a.dev === b.dev && a.size === b.size && a.mtimeMs === b.mtimeMs) return;
|
|
371
|
+
await fs2.rm(path2.join(snapFacts, n), { force: true, recursive: true });
|
|
372
|
+
await linkOrCopy(path2.join(facts, n), path2.join(snapFacts, n)).catch(() => {
|
|
373
|
+
});
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
if (!snap.manifest[`.athena/${SCAN_FILE}`]) {
|
|
377
|
+
await fs2.rm(path2.join(snapAthena, SCAN_FILE), { force: true });
|
|
378
|
+
await linkOrCopy(path2.join(dir, SCAN_FILE), path2.join(snapAthena, SCAN_FILE)).catch(() => {
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
const exclude = await readTextIfExists(path2.join(root, ".git", "info", "exclude")).catch(() => null);
|
|
382
|
+
if (exclude) {
|
|
383
|
+
await fs2.mkdir(path2.join(snapRoot, ".git", "info"), { recursive: true });
|
|
384
|
+
await fs2.writeFile(path2.join(snapRoot, ".git", "info", "exclude"), exclude);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async function untrackedEnvFiles(root, snap) {
|
|
388
|
+
const out = [];
|
|
389
|
+
for (const name of REAL_ENV_NAMES) {
|
|
390
|
+
if (!snap.manifest[name] && await fs2.stat(path2.join(root, name)).then((s) => s.isFile(), () => false)) out.push(name);
|
|
391
|
+
}
|
|
392
|
+
return out;
|
|
393
|
+
}
|
|
394
|
+
async function removeStale(parent) {
|
|
395
|
+
const names = await fs2.readdir(parent).catch(() => []);
|
|
396
|
+
await Promise.all(
|
|
397
|
+
names.filter((n) => n.startsWith("tmp-")).map(async (n) => {
|
|
398
|
+
const st = await fs2.stat(path2.join(parent, n)).catch(() => null);
|
|
399
|
+
if (st && Date.now() - st.mtimeMs > STALE_MS) await fs2.rm(path2.join(parent, n), { recursive: true, force: true }).catch(() => {
|
|
400
|
+
});
|
|
401
|
+
})
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
async function checkStaged(rootInput, opts = {}) {
|
|
405
|
+
const started = Date.now();
|
|
406
|
+
const { signal } = opts;
|
|
407
|
+
const root = await fs2.realpath(rootInput);
|
|
408
|
+
const dir = athenaDir(root);
|
|
409
|
+
const st = await readState(dir);
|
|
410
|
+
if (st.kind === "missing" && !await readTextIfExists(path2.join(dir, "rules.md"))) {
|
|
411
|
+
throw new AthenaError("Athena is not initialized in this project.", "Run `athena init` first.", EXIT.NOT_INITIALIZED);
|
|
412
|
+
}
|
|
413
|
+
if (!await isGitRepo(root, { signal })) throw new AthenaError("`--staged` needs a Git repository.", "Use `athena sync --check` outside Git.", EXIT.NOT_AVAILABLE);
|
|
414
|
+
const parent = path2.join(cacheRoot(dir), "..", STAGED_DIR);
|
|
415
|
+
await fs2.mkdir(parent, { recursive: true });
|
|
416
|
+
const ignoreFile = path2.join(parent, ".gitignore");
|
|
417
|
+
if (await readTextIfExists(ignoreFile) !== "*\n") await fs2.writeFile(ignoreFile, "*\n");
|
|
418
|
+
await removeStale(parent);
|
|
419
|
+
const manifestFile = path2.join(parent, "manifest.json");
|
|
420
|
+
const unlock = await lock(path2.join(parent, "lock"));
|
|
421
|
+
const base = unlock ? path2.join(parent, "tree") : path2.join(parent, `tmp-${process.pid}-${crypto.randomBytes(4).toString("hex")}`);
|
|
422
|
+
const snapRoot = path2.join(base, path2.basename(root));
|
|
423
|
+
try {
|
|
424
|
+
const history = gitInfo(root, { signal });
|
|
425
|
+
history.catch(() => {
|
|
426
|
+
});
|
|
427
|
+
const { config } = await loadConfig(root);
|
|
428
|
+
let previous = unlock ? await readManifest(manifestFile, root) : null;
|
|
429
|
+
if (!previous) await fs2.rm(base, { recursive: true, force: true });
|
|
430
|
+
await fs2.rm(manifestFile, { force: true });
|
|
431
|
+
await fs2.mkdir(snapRoot, { recursive: true });
|
|
432
|
+
let snap;
|
|
433
|
+
try {
|
|
434
|
+
snap = await updateIndexSnapshot(root, snapRoot, { maxFileBytes: config.maxFileBytes, previous, signal });
|
|
435
|
+
const staged = await loadConfig(snapRoot);
|
|
436
|
+
if (staged.config.maxFileBytes !== config.maxFileBytes) {
|
|
437
|
+
previous = snap.manifest;
|
|
438
|
+
snap = await updateIndexSnapshot(root, snapRoot, { maxFileBytes: staged.config.maxFileBytes, previous, signal });
|
|
439
|
+
}
|
|
440
|
+
} catch (err) {
|
|
441
|
+
if (err instanceof IndexSnapshotError) throw new AthenaError(`Cannot read the staged files: ${err.message}.`, "Resolve it (for example, finish the merge), or check the working tree with `athena sync --check`.", EXIT.NOT_AVAILABLE);
|
|
442
|
+
throw err;
|
|
443
|
+
}
|
|
444
|
+
if (unlock) await writeFileAtomic(manifestFile, JSON.stringify({ v: MANIFEST_VERSION, root, entries: snap.manifest }));
|
|
445
|
+
if (!Object.keys(snap.manifest).some((p) => p.startsWith(".athena/"))) {
|
|
446
|
+
throw new AthenaError("No Athena knowledge is staged or committed (.athena/ is not in the index).", "Commit .athena/ to check knowledge before commits.", EXIT.NOT_INITIALIZED);
|
|
447
|
+
}
|
|
448
|
+
await addLocalInputs(root, snapRoot, snap);
|
|
449
|
+
const prevIndex = await readFileIndex(dir);
|
|
450
|
+
const reuse = {};
|
|
451
|
+
for (const p of snap.placeholders) if (prevIndex[p]) reuse[p] = prevIndex[p];
|
|
452
|
+
const salt = (await readLocalConfig(root)).salt;
|
|
453
|
+
const reader = snapshotReader(await fs2.realpath(snapRoot), root, snap.placeholders);
|
|
454
|
+
const analyze = async () => {
|
|
455
|
+
const analysis = await analyzeProject(snapRoot, { signal, reuse, fingerprintSalt: salt, readFile: reader });
|
|
456
|
+
return { ...analysis.model, root, git: redactDeep(await history) };
|
|
457
|
+
};
|
|
458
|
+
const prevState = st.kind === "ok" ? st.state : null;
|
|
459
|
+
const previousBlocks = prevState ? Object.fromEntries(Object.entries(prevState.documents).map(([k, v]) => [k, v.blocks])) : void 0;
|
|
460
|
+
const staleDocs = async (model2) => {
|
|
461
|
+
const out = [];
|
|
462
|
+
for (const p of await planKnowledge(athenaDir(snapRoot), model2, { previousBlocks })) {
|
|
463
|
+
if (p.status === "unchanged") continue;
|
|
464
|
+
if (p.status === "updated" && p.changedBlocks.length && p.changedBlocks.every((b) => HISTORY_SECTIONS.has(b))) continue;
|
|
465
|
+
const worktree = await readTextIfExists(path2.join(dir, p.file));
|
|
466
|
+
out.push({ file: p.file, status: p.status === "created" ? "missing" : "outdated", changedSections: p.changedBlocks, syncedButUnstaged: worktree === p.after });
|
|
467
|
+
}
|
|
468
|
+
return out;
|
|
469
|
+
};
|
|
470
|
+
const model = await analyze();
|
|
471
|
+
let stale = await staleDocs(model);
|
|
472
|
+
const localEnv = await untrackedEnvFiles(root, snap);
|
|
473
|
+
if (stale.length && localEnv.length) {
|
|
474
|
+
const withEnv = (names) => ({ ...model, env: { ...model.env, envFilesPresent: [.../* @__PURE__ */ new Set([...model.env.envFilesPresent, ...names])].sort() } });
|
|
475
|
+
const all = (1 << localEnv.length) - 1;
|
|
476
|
+
const masks = localEnv.length <= 3 ? Array.from({ length: all }, (_, i) => i + 1).reverse() : [all];
|
|
477
|
+
for (const mask of masks) {
|
|
478
|
+
if (!(await staleDocs(withEnv(localEnv.filter((_, i) => mask & 1 << i)))).length) {
|
|
479
|
+
stale = [];
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
if (stale.length) {
|
|
484
|
+
await Promise.all(localEnv.map((n) => linkOrCopy(path2.join(root, n), path2.join(snapRoot, n))));
|
|
485
|
+
try {
|
|
486
|
+
stale = await staleDocs(await analyze());
|
|
487
|
+
} finally {
|
|
488
|
+
await Promise.all(localEnv.map((n) => fs2.rm(path2.join(snapRoot, n), { force: true })));
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return { inSync: stale.length === 0, stale, files: snap.entries, fromIndex: snap.fromIndex, durationMs: Date.now() - started };
|
|
493
|
+
} finally {
|
|
494
|
+
if (unlock) await unlock();
|
|
495
|
+
else await fs2.rm(base, { recursive: true, force: true }).catch(() => {
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
export {
|
|
500
|
+
checkStaged
|
|
501
|
+
};
|
|
502
|
+
//# sourceMappingURL=staged-check-X24C3YRD.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
buildStatus
|
|
4
|
+
} from "./chunk-ZB7LC6FP.js";
|
|
5
|
+
import {
|
|
6
|
+
bullet,
|
|
7
|
+
c,
|
|
8
|
+
dim,
|
|
9
|
+
fail,
|
|
10
|
+
heading,
|
|
11
|
+
isJson,
|
|
12
|
+
json,
|
|
13
|
+
line,
|
|
14
|
+
ok,
|
|
15
|
+
relativeTime
|
|
16
|
+
} from "./chunk-Z6QWMJLK.js";
|
|
17
|
+
import {
|
|
18
|
+
requireProjectRoot
|
|
19
|
+
} from "./chunk-XFN7UZCS.js";
|
|
20
|
+
import "./chunk-KBO7XRBW.js";
|
|
21
|
+
import "./chunk-GR4DL2VM.js";
|
|
22
|
+
import "./chunk-ELKFRTZT.js";
|
|
23
|
+
import "./chunk-OLPSEQS5.js";
|
|
24
|
+
import "./chunk-673EPYYH.js";
|
|
25
|
+
import "./chunk-FQEGEVLY.js";
|
|
26
|
+
import "./chunk-2DY4PUR4.js";
|
|
27
|
+
import "./chunk-IQQBAWSM.js";
|
|
28
|
+
import "./chunk-AD4OZCU2.js";
|
|
29
|
+
import "./chunk-XW3MLHI2.js";
|
|
30
|
+
import "./chunk-EUPETUWA.js";
|
|
31
|
+
|
|
32
|
+
// src/cli/commands/status.ts
|
|
33
|
+
async function statusCommand(opts) {
|
|
34
|
+
const root = await requireProjectRoot(opts);
|
|
35
|
+
const report = await buildStatus(root, opts.signal);
|
|
36
|
+
const memory = await (await import("./memory-VSVNLEJC.js")).memorySummary(root);
|
|
37
|
+
if (isJson()) {
|
|
38
|
+
json(memory ? { ...report, memory: { total: memory.total, unreviewed: memory.unreviewed, stale: memory.stale } } : report);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const healthLabel = report.health === "healthy" ? c.green("Healthy") : report.health === "needs-update" ? c.yellow("Needs update") : c.red("Degraded (missing knowledge files)");
|
|
42
|
+
heading("Athena Status");
|
|
43
|
+
line();
|
|
44
|
+
line(`Project: ${c.bold(report.project)}`);
|
|
45
|
+
line(`Athena: ${healthLabel}`);
|
|
46
|
+
line(`Analyzed: ${relativeTime(report.analyzedAt)}${report.git.branch ? dim(` \xB7 branch ${report.git.branch}`) : ""}`);
|
|
47
|
+
line();
|
|
48
|
+
heading("Knowledge");
|
|
49
|
+
for (const d of report.documents) {
|
|
50
|
+
if (!d.present) fail(`${d.title} ${dim(`\u2014 ${d.file} missing`)}`);
|
|
51
|
+
else if (d.editedBlocks.length) ok(`${d.title} ${dim(`\u2014 developer-edited sections: ${d.editedBlocks.join(", ")}`)}`);
|
|
52
|
+
else ok(d.title);
|
|
53
|
+
}
|
|
54
|
+
line();
|
|
55
|
+
const total = report.changes.added.length + report.changes.modified.length + report.changes.deleted.length;
|
|
56
|
+
heading("Changes since last analysis");
|
|
57
|
+
if (!total) line(dim("No file changes detected."));
|
|
58
|
+
else {
|
|
59
|
+
line(`${total} file${total === 1 ? "" : "s"} changed ${dim(`(${report.changes.modified.length} modified, ${report.changes.added.length} added, ${report.changes.deleted.length} deleted)`)}`);
|
|
60
|
+
const sample = [...report.changes.modified, ...report.changes.added, ...report.changes.deleted].slice(0, 8);
|
|
61
|
+
for (const p of sample) line(` ${dim(p)}`);
|
|
62
|
+
if (total > sample.length) line(dim(` +${total - sample.length} more`));
|
|
63
|
+
}
|
|
64
|
+
if (report.git.uncommitted !== null) line(dim(`Git: ${report.git.uncommitted} uncommitted change(s) in the working tree`));
|
|
65
|
+
if (report.affectedDocuments.length) {
|
|
66
|
+
line();
|
|
67
|
+
heading("Potentially affected");
|
|
68
|
+
for (const a of report.affectedDocuments) bullet(`${a.file} ${dim(`(${a.reasons.join(", ")})`)}`);
|
|
69
|
+
}
|
|
70
|
+
if (memory?.total) {
|
|
71
|
+
line();
|
|
72
|
+
const notes = [memory.unreviewed ? c.yellow(`${memory.unreviewed} unreviewed`) : "", memory.stale ? c.yellow(`${memory.stale} stale`) : ""].filter(Boolean);
|
|
73
|
+
line(`Memory: ${memory.total} ${memory.total === 1 ? "entry" : "entries"}${notes.length ? ` \xB7 ${notes.join(" \xB7 ")} ${dim("\u2014 run `athena memory review` / `athena memory stale`")}` : ""}`);
|
|
74
|
+
}
|
|
75
|
+
line();
|
|
76
|
+
line(`Synchronization: ${report.sync === "up-to-date" ? c.green("Up to date") : `${c.yellow("Needs update")} ${dim("\u2014 run `athena sync` to review")}`}`);
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
statusCommand
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=status-RR57ZNGI.js.map
|