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,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/git/git.ts
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
var DEFAULT_MAX_BYTES = 32 * 1024 * 1024;
|
|
6
|
+
var MAX_STDERR_BYTES = 64 * 1024;
|
|
7
|
+
function git(cwd, args, opts = {}) {
|
|
8
|
+
const o = typeof opts === "number" ? { timeoutMs: opts } : opts;
|
|
9
|
+
const timeoutMs = o.timeoutMs ?? 15e3;
|
|
10
|
+
const maxBytes = o.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
if (o.signal?.aborted) {
|
|
13
|
+
resolve({ ok: false, code: null, stdout: "", stderr: "", aborted: true });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const out = [];
|
|
17
|
+
const err = [];
|
|
18
|
+
let outBytes = 0;
|
|
19
|
+
let errBytes = 0;
|
|
20
|
+
let overflow = false;
|
|
21
|
+
let timedOut = false;
|
|
22
|
+
let aborted = false;
|
|
23
|
+
let settled = false;
|
|
24
|
+
const child = spawn("git", args, {
|
|
25
|
+
cwd,
|
|
26
|
+
windowsHide: true,
|
|
27
|
+
stdio: [o.input === void 0 ? "ignore" : "pipe", "pipe", "pipe"],
|
|
28
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", LC_ALL: "C" }
|
|
29
|
+
});
|
|
30
|
+
const kill = () => {
|
|
31
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
32
|
+
};
|
|
33
|
+
const timer = setTimeout(() => {
|
|
34
|
+
timedOut = true;
|
|
35
|
+
kill();
|
|
36
|
+
}, timeoutMs);
|
|
37
|
+
const onAbort = () => {
|
|
38
|
+
aborted = true;
|
|
39
|
+
kill();
|
|
40
|
+
};
|
|
41
|
+
o.signal?.addEventListener("abort", onAbort, { once: true });
|
|
42
|
+
const finish = (code, spawnError) => {
|
|
43
|
+
if (settled) return;
|
|
44
|
+
settled = true;
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
o.signal?.removeEventListener("abort", onAbort);
|
|
47
|
+
let stderr = Buffer.concat(err).toString("utf8");
|
|
48
|
+
if (spawnError) stderr = stderr || spawnError.message;
|
|
49
|
+
if (overflow) stderr = `${stderr}${stderr ? "\n" : ""}output exceeded ${maxBytes} bytes`;
|
|
50
|
+
if (timedOut) stderr = `${stderr}${stderr ? "\n" : ""}timed out after ${timeoutMs} ms`;
|
|
51
|
+
const failed = overflow || timedOut || aborted || Boolean(spawnError);
|
|
52
|
+
resolve({
|
|
53
|
+
ok: !failed && code === 0,
|
|
54
|
+
code: failed ? null : code,
|
|
55
|
+
stdout: Buffer.concat(out).toString("utf8"),
|
|
56
|
+
stderr,
|
|
57
|
+
...overflow ? { overflow } : {},
|
|
58
|
+
...aborted ? { aborted } : {},
|
|
59
|
+
...timedOut ? { timedOut } : {}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
child.stdout.on("data", (chunk) => {
|
|
63
|
+
if (overflow) return;
|
|
64
|
+
outBytes += chunk.length;
|
|
65
|
+
if (outBytes > maxBytes) {
|
|
66
|
+
overflow = true;
|
|
67
|
+
kill();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
out.push(chunk);
|
|
71
|
+
});
|
|
72
|
+
child.stderr.on("data", (chunk) => {
|
|
73
|
+
if (errBytes >= MAX_STDERR_BYTES) return;
|
|
74
|
+
errBytes += chunk.length;
|
|
75
|
+
err.push(chunk);
|
|
76
|
+
});
|
|
77
|
+
child.on("error", (e) => finish(null, e));
|
|
78
|
+
child.on("close", (code) => finish(code));
|
|
79
|
+
if (o.input !== void 0) {
|
|
80
|
+
child.stdin.on("error", () => {
|
|
81
|
+
});
|
|
82
|
+
child.stdin.end(o.input);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
var GitCommandError = class extends Error {
|
|
87
|
+
constructor(args, result) {
|
|
88
|
+
const detail = result.stderr.trim().split("\n").filter(Boolean).slice(-1)[0]?.slice(0, 300);
|
|
89
|
+
super(`git ${args.find((a) => !a.startsWith("-") && !a.includes("=")) ?? args[0]} failed${detail ? `: ${detail}` : result.code !== null ? ` (exit ${result.code})` : ""}`);
|
|
90
|
+
this.args = args;
|
|
91
|
+
this.result = result;
|
|
92
|
+
this.name = "GitCommandError";
|
|
93
|
+
}
|
|
94
|
+
args;
|
|
95
|
+
result;
|
|
96
|
+
};
|
|
97
|
+
async function gitOrThrow(cwd, args, opts = {}) {
|
|
98
|
+
const r = await git(cwd, args, opts);
|
|
99
|
+
if (r.aborted) throw opts.signal?.reason ?? new Error("Aborted");
|
|
100
|
+
if (!r.ok) throw new GitCommandError(args, r);
|
|
101
|
+
return r.stdout;
|
|
102
|
+
}
|
|
103
|
+
var availability = null;
|
|
104
|
+
function gitAvailable(opts = {}) {
|
|
105
|
+
if (availability) return availability;
|
|
106
|
+
const p = git(process.cwd(), ["--version"], { signal: opts.signal }).then((r) => {
|
|
107
|
+
if (r.aborted && availability === p) availability = null;
|
|
108
|
+
return r.ok;
|
|
109
|
+
});
|
|
110
|
+
availability = p;
|
|
111
|
+
return p;
|
|
112
|
+
}
|
|
113
|
+
async function isGitRepo(cwd, opts = {}) {
|
|
114
|
+
const r = await git(cwd, ["rev-parse", "--is-inside-work-tree"], { signal: opts.signal });
|
|
115
|
+
return r.ok && r.stdout.trim() === "true";
|
|
116
|
+
}
|
|
117
|
+
var HISTORY_DAYS = 180;
|
|
118
|
+
var HISTORY_MAX_COMMITS = 2e3;
|
|
119
|
+
var AUTHOR_MARK = "";
|
|
120
|
+
async function gitInfo(cwd, opts = {}) {
|
|
121
|
+
const { signal } = opts;
|
|
122
|
+
const days = opts.historyDays ?? HISTORY_DAYS;
|
|
123
|
+
const maxCommits = opts.maxCommits ?? HISTORY_MAX_COMMITS;
|
|
124
|
+
const run = (args) => git(cwd, args, { signal });
|
|
125
|
+
const [available, isRepo, branch, head, date, log] = await Promise.all([
|
|
126
|
+
gitAvailable({ signal }),
|
|
127
|
+
isGitRepo(cwd, { signal }),
|
|
128
|
+
run(["rev-parse", "--abbrev-ref", "HEAD"]),
|
|
129
|
+
run(["rev-parse", "HEAD"]),
|
|
130
|
+
run(["log", "-1", "--format=%cI"]),
|
|
131
|
+
run(["log", `--since=${days}.days`, `--max-count=${maxCommits}`, "--no-merges", `--format=${AUTHOR_MARK}%aN`, "--name-only", "--relative", "--", "."])
|
|
132
|
+
]);
|
|
133
|
+
signal?.throwIfAborted();
|
|
134
|
+
const info = { available, isRepo: false, hotspots: [] };
|
|
135
|
+
if (!info.available) return info;
|
|
136
|
+
info.isRepo = isRepo;
|
|
137
|
+
if (!info.isRepo) return info;
|
|
138
|
+
if (branch.ok) info.branch = branch.stdout.trim();
|
|
139
|
+
if (head.ok) info.head = head.stdout.trim();
|
|
140
|
+
if (date.ok && date.stdout.trim()) info.lastCommitDate = date.stdout.trim();
|
|
141
|
+
if (log.ok) {
|
|
142
|
+
const authors = /* @__PURE__ */ new Set();
|
|
143
|
+
const counts = /* @__PURE__ */ new Map();
|
|
144
|
+
for (const line of log.stdout.split("\n")) {
|
|
145
|
+
if (line.startsWith(AUTHOR_MARK)) {
|
|
146
|
+
authors.add(line.slice(1));
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const p = line.trim();
|
|
150
|
+
if (!p || p === ".athena" || p.startsWith(".athena/")) continue;
|
|
151
|
+
const dir = p.includes("/") ? p.split("/").slice(0, 2).join("/") : ".";
|
|
152
|
+
counts.set(dir, (counts.get(dir) ?? 0) + 1);
|
|
153
|
+
}
|
|
154
|
+
info.contributorCount = authors.size;
|
|
155
|
+
info.hotspots = [...counts.entries()].sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)).slice(0, 10).map(([path, commits]) => ({ path, commits }));
|
|
156
|
+
}
|
|
157
|
+
return info;
|
|
158
|
+
}
|
|
159
|
+
async function workingChanges(cwd, opts = {}) {
|
|
160
|
+
const [r, prefix] = await Promise.all([git(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", "."], { signal: opts.signal }), repoPrefix(cwd, opts)]);
|
|
161
|
+
if (!r.ok) return null;
|
|
162
|
+
const out = [];
|
|
163
|
+
const parts = r.stdout.split("\0");
|
|
164
|
+
for (let i = 0; i < parts.length; i++) {
|
|
165
|
+
const entry = parts[i];
|
|
166
|
+
if (entry.length < 4) continue;
|
|
167
|
+
const code = entry.slice(0, 2);
|
|
168
|
+
const p = entry.slice(3);
|
|
169
|
+
if (code.includes("R")) {
|
|
170
|
+
out.push({ path: p, status: "renamed", from: parts[++i] });
|
|
171
|
+
} else if (code === "??") out.push({ path: p, status: "untracked" });
|
|
172
|
+
else if (code.includes("D")) out.push({ path: p, status: "deleted" });
|
|
173
|
+
else if (code.includes("A")) out.push({ path: p, status: "added" });
|
|
174
|
+
else if (code.includes("M")) out.push({ path: p, status: "modified" });
|
|
175
|
+
else out.push({ path: p, status: "other" });
|
|
176
|
+
}
|
|
177
|
+
return stripPrefix(out, prefix);
|
|
178
|
+
}
|
|
179
|
+
async function repoPrefix(cwd, opts = {}) {
|
|
180
|
+
const r = await git(cwd, ["rev-parse", "--show-prefix"], { signal: opts.signal });
|
|
181
|
+
return r.ok ? r.stdout.trim() : "";
|
|
182
|
+
}
|
|
183
|
+
function stripPrefix(files, prefix) {
|
|
184
|
+
if (!prefix) return files;
|
|
185
|
+
return files.filter((f) => f.path.startsWith(prefix)).map((f) => ({ ...f, path: f.path.slice(prefix.length), from: f.from?.startsWith(prefix) ? f.from.slice(prefix.length) : f.from }));
|
|
186
|
+
}
|
|
187
|
+
function parseNameStatusZ(stdout) {
|
|
188
|
+
const out = [];
|
|
189
|
+
const parts = stdout.split("\0").filter(Boolean);
|
|
190
|
+
for (let i = 0; i < parts.length; i++) {
|
|
191
|
+
const code = parts[i];
|
|
192
|
+
if (code.startsWith("R") || code.startsWith("C")) {
|
|
193
|
+
const from = parts[++i];
|
|
194
|
+
const to = parts[++i];
|
|
195
|
+
if (to === void 0) break;
|
|
196
|
+
out.push(code.startsWith("R") ? { path: to, status: "renamed", from } : { path: to, status: "added" });
|
|
197
|
+
} else {
|
|
198
|
+
const p = parts[++i];
|
|
199
|
+
if (p === void 0) break;
|
|
200
|
+
out.push({ path: p, status: code === "A" ? "added" : code === "D" ? "deleted" : code === "M" ? "modified" : "other" });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
function isSafeRef(ref) {
|
|
206
|
+
return /^[A-Za-z0-9][\w./@^~-]{0,119}$/.test(ref);
|
|
207
|
+
}
|
|
208
|
+
async function headMovement(cwd, from, to, limit = 20, opts = {}) {
|
|
209
|
+
if (!/^[0-9a-f]{7,64}$/i.test(from) || !/^[0-9a-f]{7,64}$/i.test(to)) return null;
|
|
210
|
+
if (from === to) return { commits: [], diverged: false, truncated: false };
|
|
211
|
+
const { signal } = opts;
|
|
212
|
+
const exists = await git(cwd, ["cat-file", "-e", `${from}^{commit}`], { signal });
|
|
213
|
+
if (!exists.ok) return { commits: [], diverged: true, truncated: false };
|
|
214
|
+
const [ancestor, log] = await Promise.all([git(cwd, ["merge-base", "--is-ancestor", from, to], { signal }), git(cwd, ["log", `-n${limit + 1}`, "--format=%H%x1f%s", `${from}..${to}`], { signal })]);
|
|
215
|
+
if (!log.ok) return null;
|
|
216
|
+
const commits = log.stdout.split("\n").filter(Boolean).map((l) => {
|
|
217
|
+
const [sha, subject] = l.split("");
|
|
218
|
+
return { sha, subject: subject ?? "" };
|
|
219
|
+
});
|
|
220
|
+
return { commits: commits.slice(0, limit), diverged: !ancestor.ok, truncated: commits.length > limit };
|
|
221
|
+
}
|
|
222
|
+
function parseStatusV2Z(stdout) {
|
|
223
|
+
const out = [];
|
|
224
|
+
const parts = stdout.split("\0");
|
|
225
|
+
for (let i = 0; i < parts.length; i++) {
|
|
226
|
+
const rec = parts[i];
|
|
227
|
+
if (!rec) continue;
|
|
228
|
+
const kind = rec[0];
|
|
229
|
+
if (kind === "?" || kind === "!") {
|
|
230
|
+
out.push({ kind, path: rec.slice(2), submodule: false });
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (kind === "#") continue;
|
|
234
|
+
if (kind !== "1" && kind !== "2" && kind !== "u") return null;
|
|
235
|
+
const fields = kind === "1" ? 8 : kind === "2" ? 9 : 10;
|
|
236
|
+
let at = 0;
|
|
237
|
+
for (let f = 0; f < fields && at >= 0; f++) at = rec.indexOf(" ", at + 1);
|
|
238
|
+
if (at < 0) return null;
|
|
239
|
+
const sub = rec.split(" ", 3)[2] ?? "N...";
|
|
240
|
+
const entry = { kind, path: rec.slice(at + 1), submodule: sub.startsWith("S") };
|
|
241
|
+
if (kind === "2") entry.from = parts[++i];
|
|
242
|
+
out.push(entry);
|
|
243
|
+
}
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
async function statusV2(cwd, opts = {}) {
|
|
247
|
+
const r = await git(cwd, ["status", "--porcelain=v2", "-z", "--untracked-files=all", "--ignore-submodules=none", "--", "."], { signal: opts.signal });
|
|
248
|
+
return r.ok ? parseStatusV2Z(r.stdout) : null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export {
|
|
252
|
+
git,
|
|
253
|
+
GitCommandError,
|
|
254
|
+
gitOrThrow,
|
|
255
|
+
gitAvailable,
|
|
256
|
+
isGitRepo,
|
|
257
|
+
gitInfo,
|
|
258
|
+
workingChanges,
|
|
259
|
+
parseNameStatusZ,
|
|
260
|
+
isSafeRef,
|
|
261
|
+
headMovement,
|
|
262
|
+
statusV2
|
|
263
|
+
};
|
|
264
|
+
//# sourceMappingURL=chunk-OLPSEQS5.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
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/local-config.ts
|
|
11
|
+
import crypto from "crypto";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
var LOCAL_CONFIG_FILE = "local.json";
|
|
15
|
+
var LocalConfig = z.object({
|
|
16
|
+
/** Random per-machine salt for keyed fingerprints of secret findings. Never leaves this machine. */
|
|
17
|
+
salt: z.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
18
|
+
/** AI settings trusted only from machine-local sources (see src/services/ai.ts). */
|
|
19
|
+
ai: z.object({
|
|
20
|
+
baseUrl: z.string().optional(),
|
|
21
|
+
consent: z.boolean().optional()
|
|
22
|
+
}).optional()
|
|
23
|
+
}).passthrough();
|
|
24
|
+
function localPath(root) {
|
|
25
|
+
return path.join(root, ATHENA_DIR, LOCAL_CONFIG_FILE);
|
|
26
|
+
}
|
|
27
|
+
async function readLocalConfig(root) {
|
|
28
|
+
const text = await readTextIfExists(localPath(root)).catch(() => null);
|
|
29
|
+
if (text === null) return {};
|
|
30
|
+
try {
|
|
31
|
+
const parsed = LocalConfig.safeParse(JSON.parse(text));
|
|
32
|
+
return parsed.success ? parsed.data : {};
|
|
33
|
+
} catch {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function writeLocalConfig(root, cfg) {
|
|
38
|
+
const ignoreFile = path.join(root, ATHENA_DIR, ".gitignore");
|
|
39
|
+
const ignore = await readTextIfExists(ignoreFile);
|
|
40
|
+
if (!ignore?.split(/\r?\n/).includes(LOCAL_CONFIG_FILE)) {
|
|
41
|
+
await writeFileAtomic(ignoreFile, `${(ignore ?? "").replace(/\s*$/, "")}${ignore ? "\n" : ""}${LOCAL_CONFIG_FILE}
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
44
|
+
await writeFileAtomic(localPath(root), `${JSON.stringify(cfg, null, 2)}
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
async function getOrCreateSalt(root) {
|
|
48
|
+
const cfg = await readLocalConfig(root);
|
|
49
|
+
if (cfg.salt) return cfg.salt;
|
|
50
|
+
const salt = crypto.randomBytes(32).toString("hex");
|
|
51
|
+
await writeLocalConfig(root, { ...cfg, salt });
|
|
52
|
+
return salt;
|
|
53
|
+
}
|
|
54
|
+
function keyedFingerprint(salt, value, length = 16) {
|
|
55
|
+
return crypto.createHmac("sha256", salt).update(value).digest("hex").slice(0, length);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
LOCAL_CONFIG_FILE,
|
|
60
|
+
readLocalConfig,
|
|
61
|
+
getOrCreateSalt,
|
|
62
|
+
keyedFingerprint
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=chunk-PVCIGUIT.js.map
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/model/project-model.ts
|
|
4
|
+
import { z as z2 } from "zod";
|
|
5
|
+
|
|
6
|
+
// src/core/model/fact.ts
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
var FactStatus = z.enum(["FACT", "DETECTED", "INFERRED", "UNKNOWN"]);
|
|
9
|
+
var Confidence = z.enum(["high", "medium", "low"]);
|
|
10
|
+
var FactSource = z.enum(["code", "config", "filesystem", "git", "developer", "ai"]);
|
|
11
|
+
var Evidence = z.object({
|
|
12
|
+
/** Repo-relative POSIX path. */
|
|
13
|
+
file: z.string(),
|
|
14
|
+
line: z.number().int().positive().optional(),
|
|
15
|
+
detail: z.string().optional()
|
|
16
|
+
});
|
|
17
|
+
var Provenance = z.object({
|
|
18
|
+
status: FactStatus,
|
|
19
|
+
confidence: Confidence,
|
|
20
|
+
source: FactSource,
|
|
21
|
+
evidence: z.array(Evidence)
|
|
22
|
+
});
|
|
23
|
+
function detected(source, evidence, confidence = "high") {
|
|
24
|
+
return { status: "DETECTED", confidence, source, evidence };
|
|
25
|
+
}
|
|
26
|
+
function inferred(source, evidence, confidence = "medium") {
|
|
27
|
+
return { status: "INFERRED", confidence, source, evidence };
|
|
28
|
+
}
|
|
29
|
+
function fact(source, evidence) {
|
|
30
|
+
return { status: "FACT", confidence: "high", source, evidence };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/core/model/project-model.ts
|
|
34
|
+
var Named = z2.object({ name: z2.string(), provenance: Provenance });
|
|
35
|
+
var Language = z2.object({
|
|
36
|
+
name: z2.string(),
|
|
37
|
+
files: z2.number().int(),
|
|
38
|
+
bytes: z2.number().int()
|
|
39
|
+
});
|
|
40
|
+
var Framework = Named.extend({
|
|
41
|
+
category: z2.enum(["frontend", "backend", "fullstack", "mobile", "desktop", "orm", "testing", "other"]),
|
|
42
|
+
version: z2.string().optional(),
|
|
43
|
+
/** Workspace package root (repo-relative) this belongs to; "." for root. */
|
|
44
|
+
root: z2.string()
|
|
45
|
+
});
|
|
46
|
+
var Manifest = z2.object({
|
|
47
|
+
path: z2.string(),
|
|
48
|
+
ecosystem: z2.string(),
|
|
49
|
+
name: z2.string().optional(),
|
|
50
|
+
version: z2.string().optional(),
|
|
51
|
+
dependencies: z2.array(z2.string()),
|
|
52
|
+
devDependencies: z2.array(z2.string())
|
|
53
|
+
});
|
|
54
|
+
var WorkspacePackage = z2.object({
|
|
55
|
+
name: z2.string(),
|
|
56
|
+
path: z2.string(),
|
|
57
|
+
kind: z2.enum(["app", "package", "root", "unknown"]),
|
|
58
|
+
ecosystem: z2.string(),
|
|
59
|
+
/** Names of other workspace packages this one depends on. */
|
|
60
|
+
internalDependencies: z2.array(z2.string())
|
|
61
|
+
});
|
|
62
|
+
var Command = z2.object({
|
|
63
|
+
name: z2.string(),
|
|
64
|
+
command: z2.string(),
|
|
65
|
+
source: z2.string(),
|
|
66
|
+
purpose: z2.enum(["dev", "build", "test", "lint", "format", "start", "migrate", "deploy", "other"])
|
|
67
|
+
});
|
|
68
|
+
var Route = z2.object({
|
|
69
|
+
method: z2.string(),
|
|
70
|
+
path: z2.string(),
|
|
71
|
+
framework: z2.string(),
|
|
72
|
+
file: z2.string(),
|
|
73
|
+
line: z2.number().int().optional(),
|
|
74
|
+
provenance: Provenance
|
|
75
|
+
});
|
|
76
|
+
var DbEntity = z2.object({
|
|
77
|
+
name: z2.string(),
|
|
78
|
+
kind: z2.enum(["table", "model", "collection"]),
|
|
79
|
+
fields: z2.array(z2.object({ name: z2.string(), type: z2.string().optional(), attributes: z2.array(z2.string()) })),
|
|
80
|
+
indexes: z2.array(z2.string()),
|
|
81
|
+
relations: z2.array(z2.string()),
|
|
82
|
+
file: z2.string(),
|
|
83
|
+
provenance: Provenance
|
|
84
|
+
});
|
|
85
|
+
var Database = Named.extend({ kind: z2.enum(["engine", "orm", "driver", "service"]) });
|
|
86
|
+
var EnvVar = z2.object({
|
|
87
|
+
name: z2.string(),
|
|
88
|
+
/** Where the name was seen. Values are NEVER recorded. */
|
|
89
|
+
references: z2.array(z2.string()),
|
|
90
|
+
secretLike: z2.boolean()
|
|
91
|
+
});
|
|
92
|
+
var SecretFinding = z2.object({
|
|
93
|
+
type: z2.string(),
|
|
94
|
+
file: z2.string(),
|
|
95
|
+
line: z2.number().int(),
|
|
96
|
+
/**
|
|
97
|
+
* Keyed fingerprint (HMAC with the machine-local salt in .athena/local.json, or a
|
|
98
|
+
* per-process key) — allows dedup without storing the secret. Never a plain hash
|
|
99
|
+
* of the value, and never written to committed files.
|
|
100
|
+
*/
|
|
101
|
+
fingerprint: z2.string()
|
|
102
|
+
});
|
|
103
|
+
var CiJob = z2.object({ system: z2.string(), file: z2.string(), name: z2.string(), commands: z2.array(z2.string()) });
|
|
104
|
+
var ContainerService = z2.object({
|
|
105
|
+
name: z2.string(),
|
|
106
|
+
image: z2.string().optional(),
|
|
107
|
+
ports: z2.array(z2.string()),
|
|
108
|
+
dependsOn: z2.array(z2.string()),
|
|
109
|
+
file: z2.string()
|
|
110
|
+
});
|
|
111
|
+
var TestInfo = z2.object({
|
|
112
|
+
frameworks: z2.array(Named),
|
|
113
|
+
testFileCount: z2.number().int(),
|
|
114
|
+
testDirs: z2.array(z2.string()),
|
|
115
|
+
coverageConfigs: z2.array(z2.string()),
|
|
116
|
+
/** Workspace packages that contain source files but zero detected test files (structural, not coverage). */
|
|
117
|
+
packagesWithoutTests: z2.array(z2.string())
|
|
118
|
+
});
|
|
119
|
+
var GitInfo = z2.object({
|
|
120
|
+
available: z2.boolean(),
|
|
121
|
+
isRepo: z2.boolean(),
|
|
122
|
+
branch: z2.string().optional(),
|
|
123
|
+
head: z2.string().optional(),
|
|
124
|
+
lastCommitDate: z2.string().optional(),
|
|
125
|
+
contributorCount: z2.number().int().optional(),
|
|
126
|
+
hotspots: z2.array(z2.object({ path: z2.string(), commits: z2.number().int() }))
|
|
127
|
+
});
|
|
128
|
+
var ProjectModel = z2.object({
|
|
129
|
+
schemaVersion: z2.literal(1),
|
|
130
|
+
name: z2.string(),
|
|
131
|
+
description: z2.string().optional(),
|
|
132
|
+
root: z2.string(),
|
|
133
|
+
stats: z2.object({
|
|
134
|
+
filesScanned: z2.number().int(),
|
|
135
|
+
skippedBinary: z2.number().int(),
|
|
136
|
+
skippedLarge: z2.number().int(),
|
|
137
|
+
skippedUnreadable: z2.number().int()
|
|
138
|
+
}),
|
|
139
|
+
topLevelDirs: z2.array(z2.string()),
|
|
140
|
+
languages: z2.array(Language),
|
|
141
|
+
manifests: z2.array(Manifest),
|
|
142
|
+
packageManagers: z2.array(Named),
|
|
143
|
+
buildSystems: z2.array(Named),
|
|
144
|
+
workspace: z2.object({
|
|
145
|
+
isMonorepo: z2.boolean(),
|
|
146
|
+
tool: z2.string().optional(),
|
|
147
|
+
packages: z2.array(WorkspacePackage)
|
|
148
|
+
}),
|
|
149
|
+
frameworks: z2.array(Framework),
|
|
150
|
+
entryPoints: z2.array(z2.object({ path: z2.string(), provenance: Provenance })),
|
|
151
|
+
commands: z2.array(Command),
|
|
152
|
+
databases: z2.array(Database),
|
|
153
|
+
dbEntities: z2.array(DbEntity),
|
|
154
|
+
migrations: z2.array(z2.object({ path: z2.string(), tool: z2.string(), count: z2.number().int() })),
|
|
155
|
+
routes: z2.array(Route),
|
|
156
|
+
apiSpecs: z2.array(z2.object({ path: z2.string(), kind: z2.enum(["openapi", "graphql", "grpc"]) })),
|
|
157
|
+
auth: z2.array(Named.extend({ kind: z2.enum(["library", "provider", "hashing", "middleware", "token"]) })),
|
|
158
|
+
containers: z2.object({ dockerfiles: z2.array(z2.object({ path: z2.string(), baseImages: z2.array(z2.string()), exposedPorts: z2.array(z2.string()) })), services: z2.array(ContainerService) }),
|
|
159
|
+
infrastructure: z2.array(Named.extend({ kind: z2.string(), file: z2.string() })),
|
|
160
|
+
ci: z2.array(CiJob),
|
|
161
|
+
tests: TestInfo,
|
|
162
|
+
env: z2.object({ vars: z2.array(EnvVar), envFilesPresent: z2.array(z2.string()) }),
|
|
163
|
+
security: z2.object({
|
|
164
|
+
secrets: z2.array(SecretFinding),
|
|
165
|
+
tooling: z2.array(Named),
|
|
166
|
+
/** Libraries/middleware relevant to web security controls (helmet, cors, csrf...). */
|
|
167
|
+
controls: z2.array(Named)
|
|
168
|
+
}),
|
|
169
|
+
caching: z2.array(Named),
|
|
170
|
+
queues: z2.array(Named),
|
|
171
|
+
observability: z2.array(Named),
|
|
172
|
+
git: GitInfo,
|
|
173
|
+
warnings: z2.array(z2.string())
|
|
174
|
+
});
|
|
175
|
+
function emptyModel(name, root) {
|
|
176
|
+
return {
|
|
177
|
+
schemaVersion: 1,
|
|
178
|
+
name,
|
|
179
|
+
root,
|
|
180
|
+
stats: { filesScanned: 0, skippedBinary: 0, skippedLarge: 0, skippedUnreadable: 0 },
|
|
181
|
+
topLevelDirs: [],
|
|
182
|
+
languages: [],
|
|
183
|
+
manifests: [],
|
|
184
|
+
packageManagers: [],
|
|
185
|
+
buildSystems: [],
|
|
186
|
+
workspace: { isMonorepo: false, packages: [] },
|
|
187
|
+
frameworks: [],
|
|
188
|
+
entryPoints: [],
|
|
189
|
+
commands: [],
|
|
190
|
+
databases: [],
|
|
191
|
+
dbEntities: [],
|
|
192
|
+
migrations: [],
|
|
193
|
+
routes: [],
|
|
194
|
+
apiSpecs: [],
|
|
195
|
+
auth: [],
|
|
196
|
+
containers: { dockerfiles: [], services: [] },
|
|
197
|
+
infrastructure: [],
|
|
198
|
+
ci: [],
|
|
199
|
+
tests: { frameworks: [], testFileCount: 0, testDirs: [], coverageConfigs: [], packagesWithoutTests: [] },
|
|
200
|
+
env: { vars: [], envFilesPresent: [] },
|
|
201
|
+
security: { secrets: [], tooling: [], controls: [] },
|
|
202
|
+
caching: [],
|
|
203
|
+
queues: [],
|
|
204
|
+
observability: [],
|
|
205
|
+
git: { available: false, isRepo: false, hotspots: [] },
|
|
206
|
+
warnings: []
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export {
|
|
211
|
+
detected,
|
|
212
|
+
inferred,
|
|
213
|
+
fact,
|
|
214
|
+
ProjectModel,
|
|
215
|
+
emptyModel
|
|
216
|
+
};
|
|
217
|
+
//# sourceMappingURL=chunk-QCC4VJUH.js.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
readRecentEvents,
|
|
4
|
+
summarizeSessions
|
|
5
|
+
} from "./chunk-65TSRDQ3.js";
|
|
6
|
+
import {
|
|
7
|
+
ADAPTERS,
|
|
8
|
+
applyChanges,
|
|
9
|
+
resolveAdapters
|
|
10
|
+
} from "./chunk-YRIS7ZS6.js";
|
|
11
|
+
import {
|
|
12
|
+
athenaDir,
|
|
13
|
+
readState,
|
|
14
|
+
writeState
|
|
15
|
+
} from "./chunk-2DY4PUR4.js";
|
|
16
|
+
import {
|
|
17
|
+
AthenaError
|
|
18
|
+
} from "./chunk-AD4OZCU2.js";
|
|
19
|
+
|
|
20
|
+
// src/services/agents.ts
|
|
21
|
+
function resolve(names) {
|
|
22
|
+
try {
|
|
23
|
+
return names.length === 1 && names[0] === "all" ? ADAPTERS : resolveAdapters(names);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
throw new AthenaError(err.message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function listAgents(root) {
|
|
29
|
+
const st = await readState(athenaDir(root));
|
|
30
|
+
const sessions = summarizeSessions(await readRecentEvents(root, 500));
|
|
31
|
+
const out = [];
|
|
32
|
+
for (const a of ADAPTERS) {
|
|
33
|
+
const presence = await a.detectPresence(root);
|
|
34
|
+
const s = st.kind === "ok" ? st.state.agents[a.id] : void 0;
|
|
35
|
+
const checks = s?.configured ? await a.check(root) : [];
|
|
36
|
+
const hooksInstalled = a.capabilities.hooks && checks.some((c) => c.ok && /hooks installed/i.test(c.message));
|
|
37
|
+
const mine = sessions.filter((x) => x.agent === a.id);
|
|
38
|
+
out.push({
|
|
39
|
+
id: a.id,
|
|
40
|
+
name: a.displayName,
|
|
41
|
+
configured: Boolean(s?.configured),
|
|
42
|
+
configuredAt: s?.configuredAt ?? null,
|
|
43
|
+
files: s?.files ?? [],
|
|
44
|
+
detectedInProject: presence.detectedInProject,
|
|
45
|
+
evidence: presence.evidence,
|
|
46
|
+
note: a.supportNote,
|
|
47
|
+
capabilities: a.capabilities,
|
|
48
|
+
checks,
|
|
49
|
+
activityObservation: !a.capabilities.hooks ? "unsupported" : hooksInstalled ? "hooks" : "not-configured",
|
|
50
|
+
lastActivityAt: mine[0]?.lastEventAt ?? null,
|
|
51
|
+
observedEvents: mine.reduce((n, x) => n + x.events, 0)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
async function configureAgents(root, names) {
|
|
57
|
+
const st = await readState(athenaDir(root));
|
|
58
|
+
if (st.kind !== "ok") throw new AthenaError("state.json is missing or corrupted.", "Run `athena analyze` first.");
|
|
59
|
+
const adapters = resolve(names);
|
|
60
|
+
const results = [];
|
|
61
|
+
for (const a of adapters) {
|
|
62
|
+
const planned = await a.plan({ root, projectName: st.state.projectName });
|
|
63
|
+
await applyChanges(root, planned);
|
|
64
|
+
st.state.agents[a.id] = { configured: true, files: planned.map((p) => p.path), configuredAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
65
|
+
results.push({ id: a.id, files: planned.map((p) => ({ path: p.path, action: p.action })) });
|
|
66
|
+
}
|
|
67
|
+
await writeState(athenaDir(root), st.state);
|
|
68
|
+
return results;
|
|
69
|
+
}
|
|
70
|
+
async function removeAgents(root, names) {
|
|
71
|
+
const st = await readState(athenaDir(root));
|
|
72
|
+
const removed = [];
|
|
73
|
+
const targets = resolve(names);
|
|
74
|
+
for (const a of targets) {
|
|
75
|
+
removed.push(...await a.remove(root));
|
|
76
|
+
if (st.kind === "ok") delete st.state.agents[a.id];
|
|
77
|
+
}
|
|
78
|
+
if (st.kind === "ok") {
|
|
79
|
+
for (const a of ADAPTERS) {
|
|
80
|
+
const s = st.state.agents[a.id];
|
|
81
|
+
if (!s?.configured || targets.includes(a) || !s.files.some((f) => removed.includes(f))) continue;
|
|
82
|
+
const planned = await a.plan({ root, projectName: st.state.projectName });
|
|
83
|
+
await applyChanges(root, planned.filter((p) => removed.includes(p.path)));
|
|
84
|
+
}
|
|
85
|
+
await writeState(athenaDir(root), st.state);
|
|
86
|
+
}
|
|
87
|
+
return removed;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
listAgents,
|
|
92
|
+
configureAgents,
|
|
93
|
+
removeAgents
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=chunk-QLW657NT.js.map
|