@dadado/agent-kit-cli 5.16.0 → 5.18.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/dashboard/README.md +10 -1
- package/dashboard/dashboard-data.mjs +12 -2
- package/dashboard/dashboard.html +58 -0
- package/dashboard/lib/run-logs.d.mts +34 -0
- package/dashboard/lib/run-logs.mjs +218 -0
- package/dist/{add-7UMSGVWC.js → add-KQJE3ZTG.js} +3 -3
- package/dist/{chunk-KD2MYBB6.js → chunk-3J3I2W22.js} +1 -1
- package/dist/{chunk-YGBBHKBE.js → chunk-6FXQINND.js} +2 -2
- package/dist/{chunk-OUQZC744.js → chunk-B5CH47OS.js} +852 -74
- package/dist/chunk-C26I6HTO.js +39 -0
- package/dist/{chunk-6QV2BFGA.js → chunk-GSCXFJB7.js} +1 -1
- package/dist/{chunk-KI6ZHBPV.js → chunk-HRILMD5N.js} +312 -11
- package/dist/{chunk-OZ4QV6LM.js → chunk-KIXK7KOW.js} +42 -148
- package/dist/chunk-P6PMKYWU.js +50 -0
- package/dist/{chunk-WW3YZKHW.js → chunk-RZ5TNBHU.js} +25 -4
- package/dist/{chunk-TFZ2JUXH.js → chunk-TOWH3XCJ.js} +520 -70
- package/dist/{chunk-CGTE7DHN.js → chunk-TZYKLR5J.js} +135 -20
- package/dist/{chunk-5OXPNVCF.js → chunk-UEKWZ7BN.js} +2 -2
- package/dist/chunk-WGLPZXNU.js +45 -0
- package/dist/{chunk-VS2JD2XC.js → chunk-WSJ77EHO.js} +5 -2
- package/dist/{chunk-VGZBARQC.js → chunk-YJMZ6UM2.js} +2 -2
- package/dist/{chunk-XZZAVHH4.js → chunk-YMYLO5LG.js} +2 -2
- package/dist/{chunk-XWPO3VYQ.js → chunk-ZDCEAGD4.js} +1 -1
- package/dist/{contribute-YKK6YCSI.js → contribute-LFT762Y4.js} +4 -4
- package/dist/{diff-YTR7VOAN.js → diff-56KMRCER.js} +5 -5
- package/dist/doctor-AA64XVEY.js +237 -0
- package/dist/{handoff-3UI6VKVG.js → handoff-34SDU4FF.js} +2 -2
- package/dist/{hook-MUV6Y7UW.js → hook-KRIXBLNW.js} +7 -7
- package/dist/index.js +19 -17
- package/dist/{init-TTTKGC4C.js → init-XP5EAUSJ.js} +12 -11
- package/dist/{install-ZMFJBPOI.js → install-SHDEX54F.js} +12 -11
- package/dist/{plan-index-JMC7Z4DW.js → plan-index-O3545OA7.js} +2 -2
- package/dist/{run-I3BFGULT.js → run-FADX6QCP.js} +6 -5
- package/dist/{run-plan-FO7CL4M3.js → run-plan-MO4IFAKV.js} +36 -5
- package/dist/{setup-global-JUVMJXJ5.js → setup-global-CBWLXOQG.js} +6 -6
- package/dist/{status-MVCUNCWX.js → status-BSIXWZMY.js} +19 -7
- package/dist/{update-Q4EBIRUB.js → update-FJX3DA6X.js} +33 -10
- package/package.json +3 -3
- package/dist/chunk-5VCOWTTP.js +0 -6
- package/dist/doctor-TA2FWREU.js +0 -497
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/utils/kit-paths.ts
|
|
2
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var HANDOFF_REL = ".cursor/HANDOFF.md";
|
|
5
|
+
var STATE_ROOT_ENV = "AGENT_KIT_STATE_ROOT";
|
|
6
|
+
var STATE_ROOT_DIRS = [".cursor", ".agent-kit"];
|
|
7
|
+
function resolveStateDir(env = process.env) {
|
|
8
|
+
const raw = (env[STATE_ROOT_ENV] ?? "").trim().replace(/\/+$/, "");
|
|
9
|
+
if (!raw) return { stateDir: ".cursor" };
|
|
10
|
+
if (STATE_ROOT_DIRS.includes(raw))
|
|
11
|
+
return { stateDir: raw };
|
|
12
|
+
return { stateDir: ".cursor", invalid: raw };
|
|
13
|
+
}
|
|
14
|
+
function runnerStatePaths(root, env = process.env) {
|
|
15
|
+
const { stateDir, invalid } = resolveStateDir(env);
|
|
16
|
+
const base = path.join(root, stateDir);
|
|
17
|
+
return {
|
|
18
|
+
stateDir,
|
|
19
|
+
loopLogsDir: path.join(base, "loop-logs"),
|
|
20
|
+
stopFile: path.join(base, "loop.stop"),
|
|
21
|
+
...invalid !== void 0 ? { invalid } : {}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function ensureLoopLogsDir(paths) {
|
|
25
|
+
await mkdir(paths.loopLogsDir, { recursive: true });
|
|
26
|
+
if (paths.stateDir === ".cursor") return;
|
|
27
|
+
const ignore = path.join(paths.loopLogsDir, ".gitignore");
|
|
28
|
+
try {
|
|
29
|
+
await writeFile(ignore, "*\n", { flag: "wx" });
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
HANDOFF_REL,
|
|
36
|
+
STATE_ROOT_ENV,
|
|
37
|
+
runnerStatePaths,
|
|
38
|
+
ensureLoopLogsDir
|
|
39
|
+
};
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLAUDE_COMMANDS_DIR_REL,
|
|
3
|
+
CLAUDE_SETTINGS_REL,
|
|
4
|
+
GUARD_SHELL_HOOK_MARKER,
|
|
5
|
+
RESOLVE_AGENT_KIT_REL,
|
|
6
|
+
SESSION_START_HOOK_MARKER,
|
|
7
|
+
discoverInstalledCommands
|
|
8
|
+
} from "./chunk-TZYKLR5J.js";
|
|
9
|
+
import {
|
|
10
|
+
findOnPath
|
|
11
|
+
} from "./chunk-WGLPZXNU.js";
|
|
1
12
|
import {
|
|
2
13
|
createReadinessReport
|
|
3
14
|
} from "./chunk-PNFF6OU3.js";
|
|
@@ -19,9 +30,297 @@ import {
|
|
|
19
30
|
writeJson
|
|
20
31
|
} from "./chunk-77V75A7O.js";
|
|
21
32
|
|
|
22
|
-
// src/
|
|
23
|
-
import {
|
|
33
|
+
// src/invariants/hooks-health.ts
|
|
34
|
+
import { execFile } from "child_process";
|
|
35
|
+
import { constants, access, readFile, readdir, stat } from "fs/promises";
|
|
24
36
|
import path from "path";
|
|
37
|
+
import { promisify } from "util";
|
|
38
|
+
var execFileAsync = promisify(execFile);
|
|
39
|
+
var GIT_HOOK_CANONICAL_NAMES = ["pre-commit", "pre-push", "prepare-commit-msg"];
|
|
40
|
+
var EXPECTED_EVENTS = [
|
|
41
|
+
"sessionStart",
|
|
42
|
+
"preCompact",
|
|
43
|
+
"beforeShellExecution",
|
|
44
|
+
"afterFileEdit",
|
|
45
|
+
"beforeSubmitPrompt"
|
|
46
|
+
];
|
|
47
|
+
async function exists(p) {
|
|
48
|
+
try {
|
|
49
|
+
await access(p);
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function isExecutable(p) {
|
|
56
|
+
try {
|
|
57
|
+
await access(p, constants.X_OK);
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function resolveAgentKitCli(rootDir) {
|
|
64
|
+
const root = path.resolve(rootDir);
|
|
65
|
+
const candidates = [
|
|
66
|
+
path.join(root, "node_modules", ".bin", "agent-kit"),
|
|
67
|
+
path.join(root, "packages", "cli", "dist", "index.js")
|
|
68
|
+
];
|
|
69
|
+
for (const c of candidates) {
|
|
70
|
+
if (await exists(c)) return c;
|
|
71
|
+
}
|
|
72
|
+
return findOnPath("agent-kit");
|
|
73
|
+
}
|
|
74
|
+
function commandLooksLikeAdapter(command) {
|
|
75
|
+
const trimmed = command.trim();
|
|
76
|
+
if (!trimmed) return null;
|
|
77
|
+
const m = trimmed.match(/(\.cursor\/hooks\/agent\/[A-Za-z0-9._-]+\.sh)\b/);
|
|
78
|
+
return m?.[1] ?? null;
|
|
79
|
+
}
|
|
80
|
+
async function assessGitHooksInstallDrift(rootDir) {
|
|
81
|
+
const root = path.resolve(rootDir);
|
|
82
|
+
const canonicalDir = path.join(root, "git-hooks");
|
|
83
|
+
if (!await exists(canonicalDir)) return [];
|
|
84
|
+
let hooksPathCfg = "";
|
|
85
|
+
try {
|
|
86
|
+
const { stdout } = await execFileAsync(
|
|
87
|
+
"git",
|
|
88
|
+
["-C", root, "config", "--get", "core.hooksPath"],
|
|
89
|
+
{ encoding: "utf8" }
|
|
90
|
+
);
|
|
91
|
+
hooksPathCfg = stdout.trim();
|
|
92
|
+
} catch {
|
|
93
|
+
}
|
|
94
|
+
if (hooksPathCfg) {
|
|
95
|
+
const resolved = path.isAbsolute(hooksPathCfg) ? path.normalize(hooksPathCfg) : path.normalize(path.join(root, hooksPathCfg));
|
|
96
|
+
if (resolved === path.normalize(canonicalDir)) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const gitDir = path.join(root, ".git");
|
|
101
|
+
if (!await exists(gitDir)) return [];
|
|
102
|
+
try {
|
|
103
|
+
const st = await stat(gitDir);
|
|
104
|
+
if (!st.isDirectory()) return [];
|
|
105
|
+
} catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
const installedDir = path.join(gitDir, "hooks");
|
|
109
|
+
const advisories = [];
|
|
110
|
+
const installHint = "Install or refresh with: `cp git-hooks/<name> .git/hooks/<name> && chmod +x .git/hooks/<name>` (see git-hooks/README.md)";
|
|
111
|
+
let names = [...GIT_HOOK_CANONICAL_NAMES];
|
|
112
|
+
try {
|
|
113
|
+
const listed = await readdir(canonicalDir);
|
|
114
|
+
const fromDisk = listed.filter(
|
|
115
|
+
(n) => GIT_HOOK_CANONICAL_NAMES.includes(n)
|
|
116
|
+
);
|
|
117
|
+
if (fromDisk.length > 0) names = fromDisk;
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
for (const name of names) {
|
|
121
|
+
const canonical = path.join(canonicalDir, name);
|
|
122
|
+
const installed = path.join(installedDir, name);
|
|
123
|
+
if (!await exists(canonical)) continue;
|
|
124
|
+
if (!await exists(installed)) {
|
|
125
|
+
advisories.push(
|
|
126
|
+
`git-hooks drift: \`.git/hooks/${name}\` missing (canonical \`git-hooks/${name}\` present). ${installHint}`
|
|
127
|
+
);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const [a, b] = await Promise.all([readFile(canonical, "utf8"), readFile(installed, "utf8")]);
|
|
132
|
+
if (a !== b) {
|
|
133
|
+
advisories.push(
|
|
134
|
+
`git-hooks drift: \`.git/hooks/${name}\` differs from \`git-hooks/${name}\`. ${installHint}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
advisories.push(
|
|
139
|
+
`git-hooks drift: could not compare \`git-hooks/${name}\` with \`.git/hooks/${name}\`. ${installHint}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return advisories;
|
|
144
|
+
}
|
|
145
|
+
async function assessSignatureGateHook(rootDir) {
|
|
146
|
+
const root = path.resolve(rootDir);
|
|
147
|
+
if (!await exists(path.join(root, "autogit", "gitupdate.md"))) return [];
|
|
148
|
+
if (await exists(path.join(root, "git-hooks", "prepare-commit-msg"))) return [];
|
|
149
|
+
return [
|
|
150
|
+
"signature gate hook missing: `git-hooks/prepare-commit-msg` (/git-staging runs `sh git-hooks/prepare-commit-msg --check -`; a missing file exits 127 and reads as red). Run `agent-kit update` to restore it."
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
async function assessClaudeCommandAdapters(rootDir) {
|
|
154
|
+
const root = path.resolve(rootDir);
|
|
155
|
+
if (!await exists(path.join(root, ".claude"))) return [];
|
|
156
|
+
const sources = await discoverInstalledCommands(root);
|
|
157
|
+
if (sources.length === 0) return [];
|
|
158
|
+
const missing = [];
|
|
159
|
+
for (const { name } of sources) {
|
|
160
|
+
if (!await exists(path.join(root, CLAUDE_COMMANDS_DIR_REL, `${name}.md`))) missing.push(name);
|
|
161
|
+
}
|
|
162
|
+
if (missing.length === 0) return [];
|
|
163
|
+
const shown = missing.slice(0, 3).map((n) => `/${n}`);
|
|
164
|
+
const more = missing.length > shown.length ? `, +${missing.length - shown.length} more` : "";
|
|
165
|
+
return [
|
|
166
|
+
`Claude Code command adapters missing: ${missing.length} of ${sources.length} installed kit commands have no \`.claude/commands/<name>.md\` (${shown.join(", ")}${more}), so they are not recognized as slash commands. Run \`agent-kit update --claude\` to generate them (writes only \`.claude/commands/*\`).`
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
async function resolveGuardProbeArgv(root) {
|
|
170
|
+
const hookBin = process.env.AGENT_KIT_HOOK_BIN;
|
|
171
|
+
if (hookBin && await isExecutable(hookBin)) return [hookBin];
|
|
172
|
+
const local = path.join(root, "node_modules", ".bin", "agent-kit");
|
|
173
|
+
if (await isExecutable(local)) return [local];
|
|
174
|
+
const dist = path.join(root, "packages", "cli", "dist", "index.js");
|
|
175
|
+
if (await exists(dist)) return [process.execPath, dist];
|
|
176
|
+
const hit = await findOnPath("agent-kit");
|
|
177
|
+
return hit ? [hit] : null;
|
|
178
|
+
}
|
|
179
|
+
async function assessClaudeGuardProbe(rootDir) {
|
|
180
|
+
const root = path.resolve(rootDir);
|
|
181
|
+
let text;
|
|
182
|
+
try {
|
|
183
|
+
text = await readFile(path.join(root, CLAUDE_SETTINGS_REL), "utf8");
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
if (!text.includes(GUARD_SHELL_HOOK_MARKER)) {
|
|
188
|
+
return text.includes(SESSION_START_HOOK_MARKER) ? [
|
|
189
|
+
"Claude SessionStart hook is installed but the PreToolUse guard hook is absent from `.claude/settings.json`. Run `agent-kit install --claude`."
|
|
190
|
+
] : [];
|
|
191
|
+
}
|
|
192
|
+
const stale = "Claude guard hook active but resolved CLI is too old or stale dist; update the CLI / rebuild.";
|
|
193
|
+
const argv = await resolveGuardProbeArgv(root);
|
|
194
|
+
if (!argv) return [`${stale} (agent-kit CLI not resolvable)`];
|
|
195
|
+
const [bin, ...lead] = argv;
|
|
196
|
+
try {
|
|
197
|
+
const { stdout } = await execFileAsync(
|
|
198
|
+
bin,
|
|
199
|
+
[
|
|
200
|
+
...lead,
|
|
201
|
+
"guard",
|
|
202
|
+
"shell",
|
|
203
|
+
"--format",
|
|
204
|
+
"claude",
|
|
205
|
+
"--command",
|
|
206
|
+
"git push --force origin main"
|
|
207
|
+
],
|
|
208
|
+
{
|
|
209
|
+
cwd: root,
|
|
210
|
+
encoding: "utf8",
|
|
211
|
+
timeout: 1e4,
|
|
212
|
+
env: { ...process.env, ALLOW_MAIN_PUSH: "" }
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
const decision = JSON.parse(stdout).hookSpecificOutput?.permissionDecision;
|
|
216
|
+
return decision === "deny" ? [] : [stale];
|
|
217
|
+
} catch {
|
|
218
|
+
return [stale];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async function assessHooksHealth(rootDir) {
|
|
222
|
+
const root = path.resolve(rootDir);
|
|
223
|
+
const hooksJsonPath = ".cursor/hooks.json";
|
|
224
|
+
const hooksJsonAbs = path.join(root, hooksJsonPath);
|
|
225
|
+
const reasons = [];
|
|
226
|
+
const wiredEvents = [];
|
|
227
|
+
const advisories = [
|
|
228
|
+
...await assessGitHooksInstallDrift(root),
|
|
229
|
+
...await assessSignatureGateHook(root),
|
|
230
|
+
...await assessClaudeGuardProbe(root),
|
|
231
|
+
...await assessClaudeCommandAdapters(root)
|
|
232
|
+
];
|
|
233
|
+
if (!await exists(hooksJsonAbs)) {
|
|
234
|
+
return {
|
|
235
|
+
status: "missing",
|
|
236
|
+
reasons: ["`.cursor/hooks.json` not found"],
|
|
237
|
+
advisories,
|
|
238
|
+
hooksJsonPath,
|
|
239
|
+
expectedEvents: [...EXPECTED_EVENTS],
|
|
240
|
+
wiredEvents
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
let parsed;
|
|
244
|
+
try {
|
|
245
|
+
parsed = JSON.parse(await readFile(hooksJsonAbs, "utf8"));
|
|
246
|
+
} catch {
|
|
247
|
+
return {
|
|
248
|
+
status: "degraded",
|
|
249
|
+
reasons: ["`.cursor/hooks.json` is not valid JSON"],
|
|
250
|
+
advisories,
|
|
251
|
+
hooksJsonPath,
|
|
252
|
+
expectedEvents: [...EXPECTED_EVENTS],
|
|
253
|
+
wiredEvents
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const hooks = parsed.hooks ?? {};
|
|
257
|
+
const adapterRels = /* @__PURE__ */ new Set();
|
|
258
|
+
for (const event of EXPECTED_EVENTS) {
|
|
259
|
+
const list = hooks[event];
|
|
260
|
+
if (Array.isArray(list) && list.length > 0) {
|
|
261
|
+
wiredEvents.push(event);
|
|
262
|
+
for (const entry of list) {
|
|
263
|
+
if (!entry || typeof entry !== "object") continue;
|
|
264
|
+
const command = String(entry.command ?? "");
|
|
265
|
+
if (command.endsWith(".py") || command.includes("python")) {
|
|
266
|
+
reasons.push(`${event} still points at a Python script (${command})`);
|
|
267
|
+
}
|
|
268
|
+
const rel = commandLooksLikeAdapter(command);
|
|
269
|
+
if (rel) adapterRels.add(rel);
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
reasons.push(`missing hook event: ${event}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const resolveLib = path.join(root, RESOLVE_AGENT_KIT_REL);
|
|
276
|
+
if (!await exists(resolveLib)) {
|
|
277
|
+
reasons.push(`missing \`${RESOLVE_AGENT_KIT_REL}\` (thin adapter resolver)`);
|
|
278
|
+
} else if (!await isExecutable(resolveLib)) {
|
|
279
|
+
reasons.push(`\`${RESOLVE_AGENT_KIT_REL}\` is not executable (chmod +x)`);
|
|
280
|
+
}
|
|
281
|
+
for (const rel of adapterRels) {
|
|
282
|
+
const abs = path.join(root, rel);
|
|
283
|
+
if (!await exists(abs)) {
|
|
284
|
+
reasons.push(`missing adapter script: \`${rel}\``);
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
const st = await stat(abs);
|
|
289
|
+
if (!st.isFile()) {
|
|
290
|
+
reasons.push(`adapter path is not a file: \`${rel}\``);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
} catch {
|
|
294
|
+
reasons.push(`unreadable adapter script: \`${rel}\``);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (!await isExecutable(abs)) {
|
|
298
|
+
reasons.push(`adapter not executable: \`${rel}\` (chmod +x)`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const cli = await resolveAgentKitCli(root);
|
|
302
|
+
if (!cli) {
|
|
303
|
+
reasons.push(
|
|
304
|
+
"agent-kit CLI not resolvable (node_modules/.bin/agent-kit, packages/cli/dist, or PATH)"
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
if (Array.isArray(hooks.stop) && hooks.stop.length > 0) {
|
|
308
|
+
reasons.push("`stop` hook is registered (forbidden; remove it)");
|
|
309
|
+
}
|
|
310
|
+
const status = reasons.length === 0 && wiredEvents.length === EXPECTED_EVENTS.length ? "active" : "degraded";
|
|
311
|
+
return {
|
|
312
|
+
status,
|
|
313
|
+
reasons,
|
|
314
|
+
advisories,
|
|
315
|
+
hooksJsonPath,
|
|
316
|
+
expectedEvents: [...EXPECTED_EVENTS],
|
|
317
|
+
wiredEvents
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/scanner/safe-fixes.ts
|
|
322
|
+
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
323
|
+
import path2 from "path";
|
|
25
324
|
var CONTEXT_CONFIG_REL_POSIX = CONTEXT_CONFIG_REL.replace(/\\/g, "/");
|
|
26
325
|
var ESSENTIAL_DIRECTORIES = [
|
|
27
326
|
".cursor",
|
|
@@ -207,7 +506,7 @@ async function refreshRepositoryProfile(rootDir, options) {
|
|
|
207
506
|
generatedAt
|
|
208
507
|
});
|
|
209
508
|
const desiredProfile = createProfile(scan, report, generatedAt);
|
|
210
|
-
const profilePath =
|
|
509
|
+
const profilePath = path2.join(scan.rootDir, REPOSITORY_PROFILE_REL);
|
|
211
510
|
const existingProfile = await readJson(profilePath) ?? {};
|
|
212
511
|
const reconciled = reconcileFreshOverExisting(desiredProfile, existingProfile);
|
|
213
512
|
const meaningfulChange = !jsonEqual(
|
|
@@ -230,21 +529,21 @@ async function executeSafeReadinessFixes(rootDir, options) {
|
|
|
230
529
|
});
|
|
231
530
|
const changes = [];
|
|
232
531
|
for (const relativePath of ESSENTIAL_DIRECTORIES) {
|
|
233
|
-
const absolutePath =
|
|
234
|
-
const
|
|
235
|
-
if (!
|
|
532
|
+
const absolutePath = path2.join(beforeScan.rootDir, relativePath);
|
|
533
|
+
const exists2 = await fileExists(absolutePath);
|
|
534
|
+
if (!exists2 && !dryRun) await ensureDir(absolutePath);
|
|
236
535
|
recordChange(
|
|
237
536
|
changes,
|
|
238
537
|
"ensure-agent-kit-directory",
|
|
239
538
|
relativePath,
|
|
240
|
-
!
|
|
539
|
+
!exists2,
|
|
241
540
|
dryRun,
|
|
242
|
-
relativeEvidence(relativePath,
|
|
541
|
+
relativeEvidence(relativePath, exists2 ? "already exists" : "missing directory")
|
|
243
542
|
);
|
|
244
543
|
}
|
|
245
544
|
const gitignoreRelativePath = ".gitignore";
|
|
246
|
-
const gitignorePath =
|
|
247
|
-
const existingGitignore = await fileExists(gitignorePath) ? await
|
|
545
|
+
const gitignorePath = path2.join(beforeScan.rootDir, gitignoreRelativePath);
|
|
546
|
+
const existingGitignore = await fileExists(gitignorePath) ? await readFile2(gitignorePath, "utf8") : "";
|
|
248
547
|
const gitignoreAfterSecrets = mergeSecretIgnores(existingGitignore);
|
|
249
548
|
const secretsChanged = gitignoreAfterSecrets !== existingGitignore;
|
|
250
549
|
const mergedGitignore = mergeKitOwnedIgnores(gitignoreAfterSecrets);
|
|
@@ -273,7 +572,7 @@ async function executeSafeReadinessFixes(rootDir, options) {
|
|
|
273
572
|
kitOwnedChanged ? "kit-owned session/derived paths are missing" : "kit-owned session/derived paths are present"
|
|
274
573
|
)
|
|
275
574
|
);
|
|
276
|
-
const profilePath =
|
|
575
|
+
const profilePath = path2.join(beforeScan.rootDir, REPOSITORY_PROFILE_REL);
|
|
277
576
|
const existingProfile = await readJson(profilePath) ?? {};
|
|
278
577
|
const desiredProfile = createProfile(beforeScan, before, generatedAt);
|
|
279
578
|
const mergedProfile = mergeMissing(existingProfile, desiredProfile);
|
|
@@ -327,6 +626,8 @@ async function executeSafeReadinessFixes(rootDir, options) {
|
|
|
327
626
|
}
|
|
328
627
|
|
|
329
628
|
export {
|
|
629
|
+
assessClaudeCommandAdapters,
|
|
630
|
+
assessHooksHealth,
|
|
330
631
|
refreshRepositoryProfile,
|
|
331
632
|
executeSafeReadinessFixes
|
|
332
633
|
};
|