@dadado/agent-kit-cli 5.15.1 → 5.17.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/dashboard-data.mjs +69 -65
- package/dashboard/lib/guards.d.mts +5 -0
- package/dashboard/lib/guards.mjs +31 -0
- package/dashboard/lib/identity-probe.mjs +56 -0
- package/dashboard/lib/pipeline-cache.mjs +58 -0
- package/dashboard/lib/processes.mjs +71 -0
- package/dashboard/serve.mjs +43 -1
- package/dashboard/start-broadcast.mjs +2 -12
- package/dashboard/start.mjs +2 -13
- package/dist/add-HTYIFKMN.js +150 -0
- package/dist/chunk-2IPXQFJE.js +253 -0
- package/dist/chunk-2RDRMGD3.js +82 -0
- package/dist/chunk-2YU2H7I6.js +42 -0
- package/dist/chunk-4IWG5CVE.js +214 -0
- package/dist/chunk-5VCOWTTP.js +6 -0
- package/dist/chunk-77V75A7O.js +39 -0
- package/dist/chunk-7HDQEK6T.js +26 -0
- package/dist/chunk-AW74INUW.js +1456 -0
- package/dist/chunk-B73KQ5KX.js +972 -0
- package/dist/chunk-CBCW5R5J.js +484 -0
- package/dist/chunk-DE3LQFSJ.js +18 -0
- package/dist/chunk-DQA434Y4.js +106 -0
- package/dist/chunk-F4VMYR7U.js +318 -0
- package/dist/chunk-F6C3GFKV.js +57 -0
- package/dist/chunk-HFUH3HVS.js +282 -0
- package/dist/chunk-HS7LVVLO.js +74 -0
- package/dist/chunk-IHROBTNM.js +135 -0
- package/dist/chunk-KD2MYBB6.js +213 -0
- package/dist/chunk-MAPOQEA2.js +43 -0
- package/dist/chunk-N3HTJVBR.js +341 -0
- package/dist/chunk-NQ7IQ5XT.js +26 -0
- package/dist/chunk-OUQZC744.js +1315 -0
- package/dist/chunk-PCCQUWE5.js +817 -0
- package/dist/chunk-PNFF6OU3.js +273 -0
- package/dist/chunk-PNG7LO2F.js +53 -0
- package/dist/chunk-QTON2GQX.js +203 -0
- package/dist/chunk-S6GMFDKV.js +50 -0
- package/dist/chunk-SMGFN3Z4.js +503 -0
- package/dist/chunk-STGFBAYT.js +641 -0
- package/dist/chunk-TFZ2JUXH.js +1081 -0
- package/dist/chunk-VY7MTPJ2.js +205 -0
- package/dist/chunk-W24CMQ32.js +525 -0
- package/dist/chunk-WW3YZKHW.js +165 -0
- package/dist/chunk-ZB43YMHW.js +20 -0
- package/dist/chunk-ZCTSUQRR.js +874 -0
- package/dist/chunk-ZXKZBTXE.js +17 -0
- package/dist/contribute-C6AW2P6J.js +367 -0
- package/dist/cursor-awareness-KTL2RGKD.js +77 -0
- package/dist/dashboard-YSNUGBFY.js +21 -0
- package/dist/dashboard-broadcast-M4CMW7RX.js +119 -0
- package/dist/diff-VHNR62SI.js +74 -0
- package/dist/doctor-PET2HRRJ.js +236 -0
- package/dist/dogfood-KKFVRWT6.js +366 -0
- package/dist/guard-EHXF76ID.js +530 -0
- package/dist/handoff-3UI6VKVG.js +302 -0
- package/dist/hook-DYIMKJYL.js +1074 -0
- package/dist/index.js +62 -14068
- package/dist/init-Q4LBM6KT.js +127 -0
- package/dist/install-RHOS3OYZ.js +34 -0
- package/dist/mission-control-LJACEVVN.js +89 -0
- package/dist/monitors-QV2IS3MP.js +176 -0
- package/dist/plan-index-JMC7Z4DW.js +44 -0
- package/dist/public-inbound-radar-WP3XF7GS.js +68 -0
- package/dist/remote-issues-SZ4HDQL5.js +44 -0
- package/dist/run-I3BFGULT.js +243 -0
- package/dist/run-plan-FO7CL4M3.js +118 -0
- package/dist/scan-NI2SWJ2E.js +33 -0
- package/dist/setup-global-M42GEODI.js +340 -0
- package/dist/status-XPVQVQMX.js +150 -0
- package/dist/update-FJ2EBZVO.js +283 -0
- package/dist/validate-V2CW7J4B.js +201 -0
- package/package.json +3 -3
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NON_INTERACTIVE_ROOT_ARGS,
|
|
3
|
+
installL0,
|
|
4
|
+
syncFromManifest
|
|
5
|
+
} from "./chunk-DQA434Y4.js";
|
|
6
|
+
import {
|
|
7
|
+
SkippedSymlinkError,
|
|
8
|
+
formatPersonalizationSkippedSymlinks,
|
|
9
|
+
logApplyStats
|
|
10
|
+
} from "./chunk-HS7LVVLO.js";
|
|
11
|
+
import {
|
|
12
|
+
REGISTRY_CLI_ARGS,
|
|
13
|
+
resolveRegistryFromCli
|
|
14
|
+
} from "./chunk-PNG7LO2F.js";
|
|
15
|
+
import {
|
|
16
|
+
assessClaudeCommandAdapters,
|
|
17
|
+
executeSafeReadinessFixes
|
|
18
|
+
} from "./chunk-STGFBAYT.js";
|
|
19
|
+
import {
|
|
20
|
+
writeReadinessSnapshot
|
|
21
|
+
} from "./chunk-ZXKZBTXE.js";
|
|
22
|
+
import {
|
|
23
|
+
CLAUDE_HITL_FALLBACK,
|
|
24
|
+
generateClaudeCommandAdapters,
|
|
25
|
+
writeClaudeSessionStartHook
|
|
26
|
+
} from "./chunk-W24CMQ32.js";
|
|
27
|
+
import {
|
|
28
|
+
shouldUseWelcomeColor,
|
|
29
|
+
withCliProgress
|
|
30
|
+
} from "./chunk-QTON2GQX.js";
|
|
31
|
+
import {
|
|
32
|
+
createReadinessReport
|
|
33
|
+
} from "./chunk-PNFF6OU3.js";
|
|
34
|
+
import {
|
|
35
|
+
REPOSITORY_PROFILE_REL,
|
|
36
|
+
detectIde,
|
|
37
|
+
runScanner
|
|
38
|
+
} from "./chunk-ZCTSUQRR.js";
|
|
39
|
+
import {
|
|
40
|
+
GIT_PLATFORM_META
|
|
41
|
+
} from "./chunk-F6C3GFKV.js";
|
|
42
|
+
import {
|
|
43
|
+
logger
|
|
44
|
+
} from "./chunk-ZB43YMHW.js";
|
|
45
|
+
import {
|
|
46
|
+
RootRefusedError,
|
|
47
|
+
classifyInstallError,
|
|
48
|
+
confirmProjectRoot,
|
|
49
|
+
isNonInteractive,
|
|
50
|
+
npxPinned,
|
|
51
|
+
pathCliStatus,
|
|
52
|
+
syncPathCliToRuntime
|
|
53
|
+
} from "./chunk-F4VMYR7U.js";
|
|
54
|
+
import {
|
|
55
|
+
assessEnvironment
|
|
56
|
+
} from "./chunk-4IWG5CVE.js";
|
|
57
|
+
import {
|
|
58
|
+
warnIfRunningCliBehindNpm
|
|
59
|
+
} from "./chunk-SMGFN3Z4.js";
|
|
60
|
+
import {
|
|
61
|
+
DOMAIN_PACK_IDS,
|
|
62
|
+
allPacks,
|
|
63
|
+
allSkills,
|
|
64
|
+
buildManifest,
|
|
65
|
+
emptyStats,
|
|
66
|
+
installPack,
|
|
67
|
+
installSkill,
|
|
68
|
+
loadAgentKitManifest,
|
|
69
|
+
loadPackManifest,
|
|
70
|
+
loadRegistry,
|
|
71
|
+
mergeStats,
|
|
72
|
+
packMemberFileTargets,
|
|
73
|
+
resolveProtectedGlobs,
|
|
74
|
+
saveManifest,
|
|
75
|
+
skillTargetDir
|
|
76
|
+
} from "./chunk-AW74INUW.js";
|
|
77
|
+
import {
|
|
78
|
+
KIT_VERSION,
|
|
79
|
+
pinnedCliSpec
|
|
80
|
+
} from "./chunk-7HDQEK6T.js";
|
|
81
|
+
import {
|
|
82
|
+
ensureDir,
|
|
83
|
+
fileExists,
|
|
84
|
+
writeJson
|
|
85
|
+
} from "./chunk-77V75A7O.js";
|
|
86
|
+
|
|
87
|
+
// src/commands/install.ts
|
|
88
|
+
import path4 from "path";
|
|
89
|
+
import { defineCommand } from "citty";
|
|
90
|
+
import { bold, cyan, green, options as koloristOptions } from "kolorist";
|
|
91
|
+
|
|
92
|
+
// src/generator/personalization.ts
|
|
93
|
+
import { readFile, writeFile as writeFile3 } from "fs/promises";
|
|
94
|
+
import path3 from "path";
|
|
95
|
+
|
|
96
|
+
// src/generator/claude-kit-load.ts
|
|
97
|
+
import { writeFile } from "fs/promises";
|
|
98
|
+
import path from "path";
|
|
99
|
+
var CLAUDE_MD_REL = "CLAUDE.md";
|
|
100
|
+
var AGENT_KIT_COMMAND_REL = ".claude/commands/agent-kit.md";
|
|
101
|
+
function renderClaudeMd() {
|
|
102
|
+
return `# Agent Kit (Claude Code)
|
|
103
|
+
|
|
104
|
+
This repository uses Agent Kit / Mission Control. Before rediscovering the tree, read the shared sources of truth (paths below are pointers; do not treat this file as a second rulebook).
|
|
105
|
+
|
|
106
|
+
## Read first
|
|
107
|
+
|
|
108
|
+
1. \`AGENTS.md\` - cross-IDE contract
|
|
109
|
+
2. \`.cursor/project-context.md\` - verified repository facts (derived; prefer code, tests, SHAs when docs conflict)
|
|
110
|
+
3. \`.cursor/HANDOFF.md\` - if present: active plan, next to-do, queue fields
|
|
111
|
+
4. \`.cursor/commands/\` - slash catalog (Cursor). Follow the same HITL contracts here.
|
|
112
|
+
|
|
113
|
+
Mid-session refresh: \`/agent-kit\`.
|
|
114
|
+
|
|
115
|
+
## HITL
|
|
116
|
+
|
|
117
|
+
${CLAUDE_HITL_FALLBACK} Skip or cancel means stop. Never \`/git-prod\` without an explicit operator yes.
|
|
118
|
+
|
|
119
|
+
## Commit messages
|
|
120
|
+
|
|
121
|
+
Never append \`Co-Authored-By: Claude ...\` or \`Claude-Session: https://claude.ai/code/...\` (or any session-ID trailer) to commit messages, PR descriptions, or PR bodies in this repo. This overrides the harness's default git-commit template. Plain Conventional Commits messages only.
|
|
122
|
+
|
|
123
|
+
## Non-goals
|
|
124
|
+
|
|
125
|
+
Do not clone other Cursor hooks (Claude gets only the SessionStart context and PreToolUse(Bash) guard hooks); never \`/git-prod\` from a headless tick.
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
128
|
+
function renderAgentKitCommand() {
|
|
129
|
+
return `---
|
|
130
|
+
description: Load Agent Kit session context (HANDOFF, project-context, commands). Manual refresh only.
|
|
131
|
+
disable-model-invocation: true
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
Read these files if they exist. Prefer a one-shot ASCII Mission Control snapshot over a plain HANDOFF paraphrase.
|
|
135
|
+
|
|
136
|
+
1. Run \`agent-kit mission-control --once\` (or \`npx @dadado/agent-kit-cli mission-control --once\`) in the project root and paste the stdout frame as the snapshot. Do not start a live loop: Claude Code cannot sustain one across turns.
|
|
137
|
+
2. If that command is missing or fails, fall back to reading:
|
|
138
|
+
- \`AGENTS.md\`
|
|
139
|
+
- \`.cursor/project-context.md\`
|
|
140
|
+
- \`.cursor/HANDOFF.md\`
|
|
141
|
+
- The plan file named in HANDOFF \`- **Plan:**\` under \`.cursor/plans/\`
|
|
142
|
+
|
|
143
|
+
If HANDOFF is missing, say so and point at \`/agent-kit-onboard\` or \`/start-project\` rather than inventing a plan.
|
|
144
|
+
|
|
145
|
+
HITL: numbered-list fallback for Ask questions labels. Never \`/git-prod\` from this skill.
|
|
146
|
+
|
|
147
|
+
Non-goals: not audits / \`/plan-external-review\`, not a second tick dialect, not Cursor hook clones beyond the SessionStart and PreToolUse(Bash) adapters, not a continuous TUI loop.
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
async function writeUnlessExists(rootDir, relativePath, content) {
|
|
151
|
+
const target = path.join(rootDir, relativePath);
|
|
152
|
+
if (await fileExists(target)) {
|
|
153
|
+
return { relativePath, status: "skipped-customized" };
|
|
154
|
+
}
|
|
155
|
+
await ensureDir(path.dirname(target));
|
|
156
|
+
await writeFile(target, content, "utf8");
|
|
157
|
+
return { relativePath, status: "applied" };
|
|
158
|
+
}
|
|
159
|
+
async function generateClaudeKitLoadArtifacts(rootDir) {
|
|
160
|
+
return Promise.all([
|
|
161
|
+
writeUnlessExists(rootDir, CLAUDE_MD_REL, renderClaudeMd()),
|
|
162
|
+
writeUnlessExists(rootDir, AGENT_KIT_COMMAND_REL, renderAgentKitCommand())
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/generator/vscode.ts
|
|
167
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
168
|
+
import path2 from "path";
|
|
169
|
+
|
|
170
|
+
// src/generator/platform.ts
|
|
171
|
+
function gitProviderLabel(profile) {
|
|
172
|
+
const p = profile.git.provider;
|
|
173
|
+
if (!p || p === "other") return "Git";
|
|
174
|
+
const labels = {
|
|
175
|
+
github: "GitHub",
|
|
176
|
+
gitlab: "GitLab",
|
|
177
|
+
bitbucket: "Bitbucket",
|
|
178
|
+
"azure-devops": "Azure DevOps",
|
|
179
|
+
gitea: "Gitea"
|
|
180
|
+
};
|
|
181
|
+
return labels[p] ?? "Git";
|
|
182
|
+
}
|
|
183
|
+
function prTerminology(profile) {
|
|
184
|
+
const p = profile.git.provider;
|
|
185
|
+
if (!p || p === "other") return "PR/MR";
|
|
186
|
+
return GIT_PLATFORM_META[p]?.prTerm ?? "PR/MR";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/generator/vscode.ts
|
|
190
|
+
async function generateVSCodeArtifacts(profile) {
|
|
191
|
+
const results = [];
|
|
192
|
+
const vscodeDir = path2.join(profile.rootDir, ".vscode");
|
|
193
|
+
const githubDir = path2.join(profile.rootDir, ".github");
|
|
194
|
+
await Promise.all([ensureDir(vscodeDir), ensureDir(githubDir)]);
|
|
195
|
+
const settingsPath = path2.join(vscodeDir, "settings.json");
|
|
196
|
+
if (await fileExists(settingsPath)) {
|
|
197
|
+
results.push({ relativePath: ".vscode/settings.json", status: "skipped-customized" });
|
|
198
|
+
} else {
|
|
199
|
+
await writeFile2(
|
|
200
|
+
settingsPath,
|
|
201
|
+
`${JSON.stringify(
|
|
202
|
+
{
|
|
203
|
+
"editor.formatOnSave": true,
|
|
204
|
+
"editor.codeActionsOnSave": {
|
|
205
|
+
"source.fixAll": "explicit"
|
|
206
|
+
},
|
|
207
|
+
"files.autoSave": "afterDelay"
|
|
208
|
+
},
|
|
209
|
+
null,
|
|
210
|
+
2
|
|
211
|
+
)}
|
|
212
|
+
`,
|
|
213
|
+
"utf8"
|
|
214
|
+
);
|
|
215
|
+
results.push({ relativePath: ".vscode/settings.json", status: "applied" });
|
|
216
|
+
}
|
|
217
|
+
const provider = gitProviderLabel(profile);
|
|
218
|
+
const prTerm = prTerminology(profile);
|
|
219
|
+
const copilotPath = path2.join(githubDir, "copilot-instructions.md");
|
|
220
|
+
if (await fileExists(copilotPath)) {
|
|
221
|
+
results.push({ relativePath: ".github/copilot-instructions.md", status: "skipped-customized" });
|
|
222
|
+
} else {
|
|
223
|
+
await writeFile2(
|
|
224
|
+
copilotPath,
|
|
225
|
+
`# Copilot Instructions
|
|
226
|
+
|
|
227
|
+
- Keep code changes small and testable.
|
|
228
|
+
- Use Conventional Commits (feat:, fix:, docs:, etc.).
|
|
229
|
+
- Prefer security-safe defaults.
|
|
230
|
+
- Git platform: ${provider}. Always create a ${prTerm} for review.
|
|
231
|
+
`,
|
|
232
|
+
"utf8"
|
|
233
|
+
);
|
|
234
|
+
results.push({ relativePath: ".github/copilot-instructions.md", status: "applied" });
|
|
235
|
+
}
|
|
236
|
+
if (profile.ide.plan === "vscode-pro") {
|
|
237
|
+
const securityPath = path2.join(vscodeDir, "security-review.agent.md");
|
|
238
|
+
if (await fileExists(securityPath)) {
|
|
239
|
+
results.push({
|
|
240
|
+
relativePath: ".vscode/security-review.agent.md",
|
|
241
|
+
status: "skipped-customized"
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
await writeFile2(
|
|
245
|
+
securityPath,
|
|
246
|
+
"# Security Review Agent\n\nSpecialized mode for security review.\n",
|
|
247
|
+
"utf8"
|
|
248
|
+
);
|
|
249
|
+
results.push({ relativePath: ".vscode/security-review.agent.md", status: "applied" });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return results;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/generator/personalization.ts
|
|
256
|
+
var PERSONALIZATION_CONTRACT_VERSION = 1;
|
|
257
|
+
var CONTEXT_PATH = ".cursor/project-context.md";
|
|
258
|
+
var AGENTS_PATH = "AGENTS.md";
|
|
259
|
+
var RESULT_PATH = ".cursor/context/personalization.json";
|
|
260
|
+
function uniqueEvidence(evidence) {
|
|
261
|
+
const seen = /* @__PURE__ */ new Set();
|
|
262
|
+
return evidence.filter((item) => {
|
|
263
|
+
const key = `${item.source}:${item.value}`;
|
|
264
|
+
if (seen.has(key)) return false;
|
|
265
|
+
seen.add(key);
|
|
266
|
+
return true;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function purposeEvidence(profile) {
|
|
270
|
+
if (profile.purpose.confidence === "low") return [];
|
|
271
|
+
return profile.purpose.evidence;
|
|
272
|
+
}
|
|
273
|
+
function contextEvidence(profile, pattern) {
|
|
274
|
+
return profile.context.sources.filter((item) => pattern.test(item.value));
|
|
275
|
+
}
|
|
276
|
+
function readinessEvidence(report, checkId) {
|
|
277
|
+
return report.pillars.flatMap((pillar) => pillar.checks).filter((check) => check.id === checkId).flatMap((check) => check.evidence);
|
|
278
|
+
}
|
|
279
|
+
function hasPurpose(profile, purposes) {
|
|
280
|
+
return purposes.some(
|
|
281
|
+
(purpose) => profile.purpose.value === purpose || profile.purpose.categories.includes(purpose)
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
function candidate(kind, id, evidence, status) {
|
|
285
|
+
const verified = uniqueEvidence(evidence);
|
|
286
|
+
return verified.length > 0 ? { kind, id, status, evidence: verified } : null;
|
|
287
|
+
}
|
|
288
|
+
function componentAvailable(index, item) {
|
|
289
|
+
if (item.kind === "skill") return allSkills(index).some((entry) => entry.id === item.id);
|
|
290
|
+
if (item.kind === "pack") return allPacks(index).some((entry) => entry.id === item.id);
|
|
291
|
+
if (item.kind === "agent" || item.kind === "command") {
|
|
292
|
+
return (index.artifacts ?? []).some(
|
|
293
|
+
(entry) => entry.kind === item.kind && entry.id === item.id
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
function buildPersonalizationPlan(profile, report, registry) {
|
|
299
|
+
const items = [];
|
|
300
|
+
const purpose = purposeEvidence(profile);
|
|
301
|
+
const packageEvidence = profile.stack.packageManagerEvidence ?? [];
|
|
302
|
+
const n8nEvidence = [
|
|
303
|
+
...contextEvidence(profile, /(^|[/.-])n8n([/.-]|$)/i),
|
|
304
|
+
...purpose.filter((item) => /n8n/i.test(item.value))
|
|
305
|
+
];
|
|
306
|
+
const sqlEvidence = [
|
|
307
|
+
...contextEvidence(profile, /(^|[/.-])(sql|schema|migration)([/.-]|$)/i),
|
|
308
|
+
...purpose.filter((item) => /\bsql\b/i.test(item.value))
|
|
309
|
+
];
|
|
310
|
+
const promptEvidence = contextEvidence(profile, /(^|[/.-])prompts?([/.-]|$)/i);
|
|
311
|
+
const ciEvidence = profile.infra.ciFiles.map((value) => ({ source: "file", value }));
|
|
312
|
+
const infraEvidence = [
|
|
313
|
+
...ciEvidence,
|
|
314
|
+
...profile.infra.infrastructureFiles.map((value) => ({ source: "file", value })),
|
|
315
|
+
...profile.infra.deploymentFiles.map((value) => ({ source: "file", value }))
|
|
316
|
+
];
|
|
317
|
+
if (hasPurpose(profile, ["documentation", "knowledge"])) {
|
|
318
|
+
items.push(candidate("skill", "docs-repo", purpose, "applied"));
|
|
319
|
+
}
|
|
320
|
+
if (profile.stack.language.toLowerCase() === "node" && packageEvidence.length > 0) {
|
|
321
|
+
items.push(candidate("skill", "cursor-skills-node", packageEvidence, "applied"));
|
|
322
|
+
}
|
|
323
|
+
if (n8nEvidence.length > 0) {
|
|
324
|
+
items.push(candidate("skill", "n8n-workflows", n8nEvidence, "applied"));
|
|
325
|
+
} else if (hasPurpose(profile, ["automation"])) {
|
|
326
|
+
items.push(candidate("skill", "n8n-workflows", purpose, "recommended-confirmation"));
|
|
327
|
+
}
|
|
328
|
+
if (sqlEvidence.length > 0) {
|
|
329
|
+
items.push(candidate("skill", "sql-postgres", sqlEvidence, "applied"));
|
|
330
|
+
} else if (profile.services.database?.toLowerCase().includes("postgres")) {
|
|
331
|
+
items.push(
|
|
332
|
+
candidate(
|
|
333
|
+
"skill",
|
|
334
|
+
"sql-postgres",
|
|
335
|
+
[{ source: "configuration", value: `database:${profile.services.database}` }],
|
|
336
|
+
"recommended-confirmation"
|
|
337
|
+
)
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
if (promptEvidence.length > 0) {
|
|
341
|
+
items.push(candidate("skill", "prompts-markdown", promptEvidence, "applied"));
|
|
342
|
+
}
|
|
343
|
+
if (infraEvidence.length > 0) {
|
|
344
|
+
items.push(candidate("pack", "devops", infraEvidence, "applied"));
|
|
345
|
+
}
|
|
346
|
+
if (profile.stack.testCommands.length > 0 && packageEvidence.length > 0) {
|
|
347
|
+
items.push(candidate("pack", "quality", packageEvidence, "applied"));
|
|
348
|
+
items.push(candidate("agent", "test-suites", packageEvidence, "applied"));
|
|
349
|
+
}
|
|
350
|
+
const pmTools = (profile.services.projectManagement ?? []).filter((tool) => tool !== "none");
|
|
351
|
+
if (pmTools.length > 0) {
|
|
352
|
+
items.push(
|
|
353
|
+
candidate(
|
|
354
|
+
"pack",
|
|
355
|
+
"project-management",
|
|
356
|
+
pmTools.map((tool) => ({ source: "configuration", value: `projectManagement:${tool}` })),
|
|
357
|
+
"recommended-confirmation"
|
|
358
|
+
)
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
const safetyEvidence = readinessEvidence(report, "safety.secrets").filter(
|
|
362
|
+
(item) => item.value.startsWith("tracked:")
|
|
363
|
+
);
|
|
364
|
+
if (safetyEvidence.length > 0) {
|
|
365
|
+
items.push(candidate("pack", "cybersec", safetyEvidence, "recommended-confirmation"));
|
|
366
|
+
}
|
|
367
|
+
items.push(
|
|
368
|
+
candidate(
|
|
369
|
+
"command",
|
|
370
|
+
"start-project",
|
|
371
|
+
readinessEvidence(report, "agent-kit.context"),
|
|
372
|
+
"applied"
|
|
373
|
+
)
|
|
374
|
+
);
|
|
375
|
+
const availableItems = items.filter((item) => item !== null);
|
|
376
|
+
return availableItems.map(
|
|
377
|
+
(item) => componentAvailable(registry, item) ? item : { ...item, status: "unavailable" }
|
|
378
|
+
).sort((left, right) => `${left.kind}:${left.id}`.localeCompare(`${right.kind}:${right.id}`));
|
|
379
|
+
}
|
|
380
|
+
var INSTALLED_SKILL_STATUSES = /* @__PURE__ */ new Set(["applied", "skipped-customized"]);
|
|
381
|
+
function installedSkillItems(items, installedIds = []) {
|
|
382
|
+
const rows = [];
|
|
383
|
+
const seen = /* @__PURE__ */ new Set();
|
|
384
|
+
for (const item of items) {
|
|
385
|
+
if (item.kind !== "skill" || !INSTALLED_SKILL_STATUSES.has(item.status)) continue;
|
|
386
|
+
if (seen.has(item.id)) continue;
|
|
387
|
+
seen.add(item.id);
|
|
388
|
+
rows.push(item);
|
|
389
|
+
}
|
|
390
|
+
for (const id of installedIds) {
|
|
391
|
+
if (seen.has(id)) continue;
|
|
392
|
+
seen.add(id);
|
|
393
|
+
rows.push({
|
|
394
|
+
kind: "skill",
|
|
395
|
+
id,
|
|
396
|
+
status: "applied",
|
|
397
|
+
evidence: [{ source: "configuration", value: `.cursor/agent-kit.json skills[]:${id}` }]
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return rows.sort((left, right) => left.id.localeCompare(right.id));
|
|
401
|
+
}
|
|
402
|
+
function relevantSkillTableRows(skills) {
|
|
403
|
+
if (skills.length === 0) {
|
|
404
|
+
return ["| (none yet) | No installed or project-owned skills detected | \u2014 |"];
|
|
405
|
+
}
|
|
406
|
+
return skills.map((skill) => {
|
|
407
|
+
const label = skill.path ?? skill.id;
|
|
408
|
+
const role = skill.status === "skipped-customized" ? "Already present (customized)" : "Installed by personalization";
|
|
409
|
+
const evidence = skill.path ?? skill.evidence[0]?.value ?? "personalization";
|
|
410
|
+
return `| ${label} | ${role} | ${evidence} |`;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
function renderProjectContext(profile, skillItems = []) {
|
|
414
|
+
const sections = ["# Project Context", "", "Verified repository facts:"];
|
|
415
|
+
const purpose = purposeEvidence(profile);
|
|
416
|
+
if (purpose.length > 0 && profile.purpose.value !== "unknown") {
|
|
417
|
+
sections.push(`- Purpose: ${profile.purpose.value}.`);
|
|
418
|
+
}
|
|
419
|
+
const packageEvidence = profile.stack.packageManagerEvidence ?? [];
|
|
420
|
+
if (packageEvidence.length > 0) {
|
|
421
|
+
sections.push(`- Runtime family: ${profile.stack.language}.`);
|
|
422
|
+
if (profile.stack.packageManager) {
|
|
423
|
+
sections.push(`- Package manager: ${profile.stack.packageManager}.`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (profile.git.provider && (profile.git.providerEvidence?.length ?? 0) > 0) {
|
|
427
|
+
sections.push(`- Git provider: ${profile.git.provider}.`);
|
|
428
|
+
}
|
|
429
|
+
if (profile.infra.ci !== "none" && profile.infra.ciFiles.length > 0) {
|
|
430
|
+
sections.push(`- CI: ${profile.infra.ci}.`);
|
|
431
|
+
}
|
|
432
|
+
sections.push(
|
|
433
|
+
"",
|
|
434
|
+
"## Relevant skills",
|
|
435
|
+
"",
|
|
436
|
+
"Installed and project-owned skills for this repository. Prefer `.cursor/agent-kit.json` `skills[]` as the install index; project-only domain skills may live under `.cursor/skills/domain/`.",
|
|
437
|
+
"",
|
|
438
|
+
"| Skill / path | Role | Evidence |",
|
|
439
|
+
"|--------------|------|----------|",
|
|
440
|
+
...relevantSkillTableRows(installedSkillItems(skillItems))
|
|
441
|
+
);
|
|
442
|
+
if (profile.context.sources.length > 0) {
|
|
443
|
+
sections.push("", "## Sources", ...profile.context.sources.map((item) => `- ${item.value}`));
|
|
444
|
+
}
|
|
445
|
+
return `${sections.join("\n")}
|
|
446
|
+
`;
|
|
447
|
+
}
|
|
448
|
+
async function createOwnedFile(rootDir, relativePath, content, evidence) {
|
|
449
|
+
const target = path3.join(rootDir, relativePath);
|
|
450
|
+
if (await fileExists(target)) {
|
|
451
|
+
return {
|
|
452
|
+
kind: "file",
|
|
453
|
+
id: relativePath,
|
|
454
|
+
path: relativePath,
|
|
455
|
+
status: "skipped-customized",
|
|
456
|
+
evidence
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
await ensureDir(path3.dirname(target));
|
|
460
|
+
await writeFile3(target, content, "utf8");
|
|
461
|
+
return {
|
|
462
|
+
kind: "file",
|
|
463
|
+
id: relativePath,
|
|
464
|
+
path: relativePath,
|
|
465
|
+
status: "applied",
|
|
466
|
+
evidence
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
async function packTargets(registryRoot, packId) {
|
|
470
|
+
const manifest = await loadPackManifest(registryRoot, packId);
|
|
471
|
+
const targets = [];
|
|
472
|
+
for (const member of manifest.members) {
|
|
473
|
+
for (const { targetRel } of await packMemberFileTargets(registryRoot, member)) {
|
|
474
|
+
targets.push(targetRel);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return targets;
|
|
478
|
+
}
|
|
479
|
+
async function existingTargets(projectRoot, targets) {
|
|
480
|
+
const checks = await Promise.all(
|
|
481
|
+
targets.map(
|
|
482
|
+
async (target) => await fileExists(path3.join(projectRoot, target)) ? target : null
|
|
483
|
+
)
|
|
484
|
+
);
|
|
485
|
+
return checks.filter((target) => target !== null);
|
|
486
|
+
}
|
|
487
|
+
async function applyPersonalization(input) {
|
|
488
|
+
const planned = buildPersonalizationPlan(input.profile, input.report, input.registry);
|
|
489
|
+
const stats = emptyStats();
|
|
490
|
+
const componentResults = [];
|
|
491
|
+
const protectedPaths = new Set(input.manifest.protected ?? []);
|
|
492
|
+
const packs = new Set(input.manifest.packs ?? []);
|
|
493
|
+
const skills = new Set(input.manifest.skills ?? []);
|
|
494
|
+
const notRecorded = [];
|
|
495
|
+
for (const item of planned) {
|
|
496
|
+
if (item.status !== "applied") {
|
|
497
|
+
componentResults.push(item);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (item.kind === "skill") {
|
|
501
|
+
const skill = allSkills(input.registry).find((entry) => entry.id === item.id);
|
|
502
|
+
if (!skill) {
|
|
503
|
+
componentResults.push({ ...item, status: "unavailable" });
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
const target = path3.posix.join(skillTargetDir(skill.path, skill.id), "SKILL.md");
|
|
507
|
+
if (await fileExists(path3.join(input.rootDir, target))) {
|
|
508
|
+
componentResults.push({ ...item, status: "skipped-customized", path: target });
|
|
509
|
+
protectedPaths.add(target);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
const skillStats = await installSkill(input.registryRoot, input.rootDir, skill);
|
|
513
|
+
mergeStats(stats, skillStats);
|
|
514
|
+
if (skillStats.skippedSymlink.length > 0) {
|
|
515
|
+
notRecorded.push(item.id);
|
|
516
|
+
componentResults.push({ ...item, status: "unavailable", path: target });
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
skills.add(item.id);
|
|
520
|
+
componentResults.push({ ...item, path: target });
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
if (item.kind === "pack") {
|
|
524
|
+
const targets = await packTargets(input.registryRoot, item.id);
|
|
525
|
+
const customizedTargets = await existingTargets(input.rootDir, targets);
|
|
526
|
+
const packStats = await installPack(input.registryRoot, input.rootDir, item.id, {
|
|
527
|
+
protectedGlobs: customizedTargets
|
|
528
|
+
});
|
|
529
|
+
mergeStats(stats, packStats);
|
|
530
|
+
for (const target of customizedTargets) protectedPaths.add(target);
|
|
531
|
+
if (packStats.skippedSymlink.length > 0) {
|
|
532
|
+
notRecorded.push(item.id);
|
|
533
|
+
componentResults.push({ ...item, status: "unavailable" });
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
packs.add(item.id);
|
|
537
|
+
componentResults.push(
|
|
538
|
+
customizedTargets.length > 0 ? { ...item, status: "skipped-customized" } : item
|
|
539
|
+
);
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
componentResults.push(item);
|
|
543
|
+
}
|
|
544
|
+
const profileEvidence = uniqueEvidence([
|
|
545
|
+
...purposeEvidence(input.profile),
|
|
546
|
+
...input.profile.stack.packageManagerEvidence ?? [],
|
|
547
|
+
...input.profile.git.providerEvidence ?? [],
|
|
548
|
+
...input.profile.context.sources
|
|
549
|
+
]);
|
|
550
|
+
const fileResults = await Promise.all([
|
|
551
|
+
createOwnedFile(
|
|
552
|
+
input.rootDir,
|
|
553
|
+
CONTEXT_PATH,
|
|
554
|
+
renderProjectContext(input.profile, installedSkillItems(componentResults, skills)),
|
|
555
|
+
profileEvidence
|
|
556
|
+
),
|
|
557
|
+
createOwnedFile(
|
|
558
|
+
input.rootDir,
|
|
559
|
+
AGENTS_PATH,
|
|
560
|
+
"# Repository Agent Guidance\n\nUse `.cursor/project-context.md` for verified repository facts. Preserve existing project-owned guidance and request confirmation before adding optional integrations.\n",
|
|
561
|
+
profileEvidence
|
|
562
|
+
)
|
|
563
|
+
]);
|
|
564
|
+
protectedPaths.add(CONTEXT_PATH);
|
|
565
|
+
protectedPaths.add(AGENTS_PATH);
|
|
566
|
+
const claudeResults = await generateClaudeKitLoadArtifacts(input.rootDir);
|
|
567
|
+
const claudeItems = claudeResults.map((artifact) => {
|
|
568
|
+
protectedPaths.add(artifact.relativePath);
|
|
569
|
+
return {
|
|
570
|
+
kind: "file",
|
|
571
|
+
id: artifact.relativePath,
|
|
572
|
+
path: artifact.relativePath,
|
|
573
|
+
status: artifact.status,
|
|
574
|
+
evidence: profileEvidence
|
|
575
|
+
};
|
|
576
|
+
});
|
|
577
|
+
const claudeCommandItems = [];
|
|
578
|
+
let claudeSessionStartInstructions;
|
|
579
|
+
if (input.claudeAdapters) {
|
|
580
|
+
const adapterResults = await generateClaudeCommandAdapters(input.rootDir);
|
|
581
|
+
for (const artifact of adapterResults) {
|
|
582
|
+
protectedPaths.add(artifact.relativePath);
|
|
583
|
+
claudeCommandItems.push({
|
|
584
|
+
kind: "file",
|
|
585
|
+
id: artifact.relativePath,
|
|
586
|
+
path: artifact.relativePath,
|
|
587
|
+
// Overlay statuses (applied/unchanged/refreshed/preserved-customized)
|
|
588
|
+
// fold onto the shared PersonalizationStatus union: anything written
|
|
589
|
+
// or already current reads as "applied"; a hand-edited adapter left
|
|
590
|
+
// alone reads as "skipped-customized" (same meaning as elsewhere in
|
|
591
|
+
// this file — never silently clobbered).
|
|
592
|
+
status: artifact.status === "skipped-symlink" ? "unavailable" : artifact.status === "preserved-customized" ? "skipped-customized" : "applied",
|
|
593
|
+
evidence: profileEvidence
|
|
594
|
+
});
|
|
595
|
+
if (artifact.status === "skipped-symlink") stats.skippedSymlink.push(artifact.relativePath);
|
|
596
|
+
}
|
|
597
|
+
const hookResult = await writeClaudeSessionStartHook(input.rootDir);
|
|
598
|
+
protectedPaths.add(hookResult.relativePath);
|
|
599
|
+
claudeCommandItems.push({
|
|
600
|
+
kind: "file",
|
|
601
|
+
id: hookResult.relativePath,
|
|
602
|
+
path: hookResult.relativePath,
|
|
603
|
+
// "unavailable" (existing .claude/settings.json unparseable) is a real
|
|
604
|
+
// PersonalizationStatus value already; every other hook status folds
|
|
605
|
+
// onto "applied" the same way the command-adapter statuses do above.
|
|
606
|
+
status: hookResult.status === "unavailable" || hookResult.status === "skipped-symlink" ? "unavailable" : "applied",
|
|
607
|
+
evidence: profileEvidence
|
|
608
|
+
});
|
|
609
|
+
if (hookResult.status === "skipped-symlink") stats.skippedSymlink.push(hookResult.relativePath);
|
|
610
|
+
if (hookResult.ledgerPath) {
|
|
611
|
+
protectedPaths.add(hookResult.ledgerPath);
|
|
612
|
+
claudeCommandItems.push({
|
|
613
|
+
kind: "file",
|
|
614
|
+
id: hookResult.ledgerPath,
|
|
615
|
+
path: hookResult.ledgerPath,
|
|
616
|
+
status: "applied",
|
|
617
|
+
evidence: profileEvidence
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
if (hookResult.status === "unavailable" && hookResult.instructions) {
|
|
621
|
+
claudeSessionStartInstructions = hookResult.instructions;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
const ideDetection = await detectIde(input.rootDir);
|
|
625
|
+
if (ideDetection.ide === "vscode" || ideDetection.ide === "other") {
|
|
626
|
+
const git = {
|
|
627
|
+
providerKind: input.profile.git.providerKind ?? "unknown",
|
|
628
|
+
providerConfidence: input.profile.git.providerConfidence ?? "low",
|
|
629
|
+
providerEvidence: input.profile.git.providerEvidence ?? [],
|
|
630
|
+
remotes: input.profile.git.remotes ?? [],
|
|
631
|
+
mode: input.profile.git.mode ?? "none",
|
|
632
|
+
workflow: input.profile.git.workflow ?? "unknown",
|
|
633
|
+
isDirty: input.profile.git.isDirty ?? false,
|
|
634
|
+
hasLocalStaging: input.profile.git.hasLocalStaging ?? false,
|
|
635
|
+
hasRemoteStaging: input.profile.git.hasRemoteStaging ?? false,
|
|
636
|
+
provider: input.profile.git.provider,
|
|
637
|
+
remoteUrl: input.profile.git.remoteUrl,
|
|
638
|
+
remoteName: input.profile.git.remoteName,
|
|
639
|
+
currentBranch: input.profile.git.currentBranch,
|
|
640
|
+
defaultBranch: input.profile.git.defaultBranch
|
|
641
|
+
};
|
|
642
|
+
const projectProfile = {
|
|
643
|
+
rootDir: input.rootDir,
|
|
644
|
+
stack: input.profile.stack,
|
|
645
|
+
git,
|
|
646
|
+
ide: ideDetection,
|
|
647
|
+
infra: input.profile.infra,
|
|
648
|
+
services: input.profile.services
|
|
649
|
+
};
|
|
650
|
+
const vscodeResults = await generateVSCodeArtifacts(projectProfile);
|
|
651
|
+
const ideEvidence = [
|
|
652
|
+
{ source: "derived", value: `ide:${ideDetection.ide}` }
|
|
653
|
+
];
|
|
654
|
+
for (const artifact of vscodeResults) {
|
|
655
|
+
protectedPaths.add(artifact.relativePath);
|
|
656
|
+
componentResults.push({
|
|
657
|
+
kind: "file",
|
|
658
|
+
id: artifact.relativePath,
|
|
659
|
+
path: artifact.relativePath,
|
|
660
|
+
status: artifact.status,
|
|
661
|
+
evidence: ideEvidence
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
const result = {
|
|
666
|
+
contractVersion: PERSONALIZATION_CONTRACT_VERSION,
|
|
667
|
+
generatorVersion: input.generatorVersion,
|
|
668
|
+
repositoryFingerprint: input.report.repositoryFingerprint,
|
|
669
|
+
items: [...fileResults, ...claudeItems, ...claudeCommandItems, ...componentResults],
|
|
670
|
+
protectedPaths: [...protectedPaths].sort(),
|
|
671
|
+
...claudeSessionStartInstructions ? { claudeSessionStartInstructions } : {}
|
|
672
|
+
};
|
|
673
|
+
await writeJson(path3.join(input.rootDir, RESULT_PATH), result);
|
|
674
|
+
return {
|
|
675
|
+
result,
|
|
676
|
+
stats,
|
|
677
|
+
notRecorded,
|
|
678
|
+
manifest: {
|
|
679
|
+
...input.manifest,
|
|
680
|
+
packs: [...packs].sort(),
|
|
681
|
+
skills: [...skills].sort(),
|
|
682
|
+
protected: result.protectedPaths,
|
|
683
|
+
personalization: {
|
|
684
|
+
contractVersion: PERSONALIZATION_CONTRACT_VERSION,
|
|
685
|
+
generatorVersion: input.generatorVersion,
|
|
686
|
+
origin: "repository-profile",
|
|
687
|
+
resultPath: RESULT_PATH
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
async function readRepositoryProfile(rootDir) {
|
|
693
|
+
const target = path3.join(rootDir, REPOSITORY_PROFILE_REL);
|
|
694
|
+
if (!await fileExists(target)) return null;
|
|
695
|
+
return JSON.parse(await readFile(target, "utf8"));
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// src/commands/install.ts
|
|
699
|
+
function parsePackList(raw) {
|
|
700
|
+
if (!raw?.trim()) return [];
|
|
701
|
+
return [
|
|
702
|
+
...new Set(
|
|
703
|
+
raw.split(",").map((s) => s.trim()).filter(Boolean)
|
|
704
|
+
)
|
|
705
|
+
];
|
|
706
|
+
}
|
|
707
|
+
function nextStepAfterInstall(pendingActions) {
|
|
708
|
+
return pendingActions > 0 ? "Next: run /agent-kit-onboard in Cursor to resolve the first pending action." : "Next: run /start-project in Cursor when you have a deliverable.";
|
|
709
|
+
}
|
|
710
|
+
function printReadinessNarrative(result) {
|
|
711
|
+
const { summary, pendingActions } = result.readiness;
|
|
712
|
+
const fixed = result.safeChanges.filter((change) => change.status === "applied").length;
|
|
713
|
+
console.log("\nRepository readiness");
|
|
714
|
+
console.log(
|
|
715
|
+
` ready: ${summary.ready}, choices: ${summary.needs_choice}, manual: ${summary.manual}, blocked: ${summary.blocked}`
|
|
716
|
+
);
|
|
717
|
+
console.log(` safe fixes applied: ${fixed}`);
|
|
718
|
+
console.log(` pending actions: ${pendingActions.length}`);
|
|
719
|
+
console.log(nextStepAfterInstall(pendingActions.length));
|
|
720
|
+
}
|
|
721
|
+
function paint(fn, text) {
|
|
722
|
+
const prevEnabled = koloristOptions.enabled;
|
|
723
|
+
const prevSupportLevel = koloristOptions.supportLevel;
|
|
724
|
+
koloristOptions.enabled = true;
|
|
725
|
+
koloristOptions.supportLevel = 3;
|
|
726
|
+
try {
|
|
727
|
+
return fn(text);
|
|
728
|
+
} finally {
|
|
729
|
+
koloristOptions.enabled = prevEnabled;
|
|
730
|
+
koloristOptions.supportLevel = prevSupportLevel;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
function printInstallEpilogue(env, options = {}) {
|
|
734
|
+
const print = options.print ?? ((line) => console.log(line));
|
|
735
|
+
const color = options.color ?? shouldUseWelcomeColor();
|
|
736
|
+
const runtimeVersion = options.runtimeVersion ?? KIT_VERSION;
|
|
737
|
+
const status = pathCliStatus(env, runtimeVersion);
|
|
738
|
+
if (status === "current") {
|
|
739
|
+
const line = `PATH \`agent-kit\` is v${env.binVersion ?? runtimeVersion}. Run it directly, e.g. \`agent-kit doctor\`.`;
|
|
740
|
+
print(color ? paint(green, line) : line);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (status === "behind" || status === "unknown") {
|
|
744
|
+
const line = `Keep using ${npxPinned(runtimeVersion, "<subcommand>")} until PATH is v${runtimeVersion}.`;
|
|
745
|
+
print(color ? paint(green, line) : line);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const divider = "\u2500".repeat(60);
|
|
749
|
+
const body = [
|
|
750
|
+
"You ran this through npx, so a bare `agent-kit` isn't on PATH yet.",
|
|
751
|
+
'If you try `agent-kit <subcommand>` next, you will see "command not',
|
|
752
|
+
'found". Pick one:',
|
|
753
|
+
"",
|
|
754
|
+
" 1. Keep using npx (works right now, no action needed)",
|
|
755
|
+
` ${npxPinned(runtimeVersion, "<subcommand>")}`,
|
|
756
|
+
"",
|
|
757
|
+
" 2. Put a bare `agent-kit` on PATH",
|
|
758
|
+
` ${npxPinned(runtimeVersion, "setup-global")}`,
|
|
759
|
+
" (fixes a root-owned npm prefix if that's the blocker, or just installs)",
|
|
760
|
+
"",
|
|
761
|
+
" 3. Manual steps",
|
|
762
|
+
" See docs/getting-started.md (Troubleshooting npm failures), or:",
|
|
763
|
+
" mkdir -p ~/.npm-global",
|
|
764
|
+
' npm config set prefix "~/.npm-global"',
|
|
765
|
+
' export PATH="~/.npm-global/bin:$PATH"',
|
|
766
|
+
` npm i -g ${pinnedCliSpec(runtimeVersion)}`
|
|
767
|
+
];
|
|
768
|
+
print(color ? paint(cyan, divider) : divider);
|
|
769
|
+
const heading = "Heads up: a bare `agent-kit` command won't work yet";
|
|
770
|
+
print(color ? paint(bold, paint(cyan, heading)) : heading);
|
|
771
|
+
for (const line of body) print(line);
|
|
772
|
+
print(color ? paint(cyan, divider) : divider);
|
|
773
|
+
}
|
|
774
|
+
async function printPostInstallSummary(result) {
|
|
775
|
+
printReadinessNarrative(result);
|
|
776
|
+
const env = await assessEnvironment();
|
|
777
|
+
const sync = await syncPathCliToRuntime({ runtimeVersion: KIT_VERSION, env });
|
|
778
|
+
for (const line of sync.lines) console.log(line);
|
|
779
|
+
printInstallEpilogue(sync.env, { runtimeVersion: KIT_VERSION });
|
|
780
|
+
}
|
|
781
|
+
async function performInstall(options) {
|
|
782
|
+
const projectRoot = path4.resolve(options.cwd);
|
|
783
|
+
const packs = parsePackList(options.pack);
|
|
784
|
+
const existing = await loadAgentKitManifest(projectRoot, { onWarning: logger.warn });
|
|
785
|
+
const registry = await resolveRegistryFromCli({
|
|
786
|
+
cwd: projectRoot,
|
|
787
|
+
registry: options.registry,
|
|
788
|
+
url: options.url,
|
|
789
|
+
ref: options.ref,
|
|
790
|
+
refresh: options.refresh,
|
|
791
|
+
manifest: existing
|
|
792
|
+
});
|
|
793
|
+
try {
|
|
794
|
+
const draft = buildManifest({
|
|
795
|
+
version: KIT_VERSION,
|
|
796
|
+
profile: options.profile ?? existing?.profile ?? "default",
|
|
797
|
+
packs: packs.length > 0 ? packs : existing?.packs,
|
|
798
|
+
skills: existing?.skills,
|
|
799
|
+
protected: existing?.protected,
|
|
800
|
+
personalization: existing?.personalization,
|
|
801
|
+
registryUrl: registry.url ?? existing?.registry?.url,
|
|
802
|
+
registryRef: registry.ref ?? existing?.registry?.ref
|
|
803
|
+
});
|
|
804
|
+
if (existing?.unknownFields) draft.unknownFields = existing.unknownFields;
|
|
805
|
+
const stats = (draft.packs?.length ?? 0) > 0 || (draft.skills?.length ?? 0) > 0 ? await syncFromManifest(registry.root, projectRoot, draft) : await installL0(registry.root, projectRoot, resolveProtectedGlobs(draft));
|
|
806
|
+
if (stats.skippedSymlink.length > 0) throw new SkippedSymlinkError(stats);
|
|
807
|
+
const manifestPath = await saveManifest(projectRoot, draft);
|
|
808
|
+
const readinessExecution = await executeSafeReadinessFixes(projectRoot, {
|
|
809
|
+
generatorVersion: KIT_VERSION
|
|
810
|
+
});
|
|
811
|
+
let readiness = readinessExecution.after;
|
|
812
|
+
let claudeSessionStartInstructions;
|
|
813
|
+
const profile = await readRepositoryProfile(projectRoot);
|
|
814
|
+
if (profile) {
|
|
815
|
+
const registryIndex = await loadRegistry(registry.root);
|
|
816
|
+
const personalization = await applyPersonalization({
|
|
817
|
+
rootDir: projectRoot,
|
|
818
|
+
registryRoot: registry.root,
|
|
819
|
+
profile,
|
|
820
|
+
report: readinessExecution.after,
|
|
821
|
+
registry: registryIndex,
|
|
822
|
+
manifest: draft,
|
|
823
|
+
generatorVersion: KIT_VERSION,
|
|
824
|
+
claudeAdapters: options.claudeAdapters
|
|
825
|
+
});
|
|
826
|
+
await saveManifest(projectRoot, personalization.manifest);
|
|
827
|
+
claudeSessionStartInstructions = personalization.result.claudeSessionStartInstructions;
|
|
828
|
+
readiness = createReadinessReport(await runScanner(projectRoot), {
|
|
829
|
+
generatorVersion: KIT_VERSION
|
|
830
|
+
});
|
|
831
|
+
readiness.appliedSafeFixes = readinessExecution.after.appliedSafeFixes;
|
|
832
|
+
if (personalization.stats.skippedSymlink.length > 0) {
|
|
833
|
+
await writeReadinessSnapshot(projectRoot, readiness);
|
|
834
|
+
throw new SkippedSymlinkError(
|
|
835
|
+
mergeStats(stats, personalization.stats),
|
|
836
|
+
formatPersonalizationSkippedSymlinks(
|
|
837
|
+
personalization.stats.skippedSymlink,
|
|
838
|
+
personalization.notRecorded
|
|
839
|
+
),
|
|
840
|
+
claudeSessionStartInstructions
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
await writeReadinessSnapshot(projectRoot, readiness);
|
|
845
|
+
const claudeAdapterHint = options.claudeAdapters ? void 0 : (await assessClaudeCommandAdapters(projectRoot))[0];
|
|
846
|
+
return {
|
|
847
|
+
projectRoot,
|
|
848
|
+
manifestPath,
|
|
849
|
+
stats,
|
|
850
|
+
readiness,
|
|
851
|
+
safeChanges: readinessExecution.changes,
|
|
852
|
+
...claudeSessionStartInstructions ? { claudeSessionStartInstructions } : {},
|
|
853
|
+
...claudeAdapterHint ? { claudeAdapterHint } : {}
|
|
854
|
+
};
|
|
855
|
+
} finally {
|
|
856
|
+
await registry.unlock?.();
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
var installCommand = defineCommand({
|
|
860
|
+
meta: {
|
|
861
|
+
name: "install",
|
|
862
|
+
description: "Bootstrap L0 (+ optional packs) and write .cursor/agent-kit.json."
|
|
863
|
+
},
|
|
864
|
+
args: {
|
|
865
|
+
profile: {
|
|
866
|
+
type: "positional",
|
|
867
|
+
description: "Install profile name stored in the manifest (default: default)",
|
|
868
|
+
required: false
|
|
869
|
+
},
|
|
870
|
+
pack: {
|
|
871
|
+
type: "string",
|
|
872
|
+
description: `Comma-separated L1 pack ids (e.g. cybersec,devops). Known: ${DOMAIN_PACK_IDS.join(", ")}`
|
|
873
|
+
},
|
|
874
|
+
...NON_INTERACTIVE_ROOT_ARGS,
|
|
875
|
+
claude: {
|
|
876
|
+
type: "boolean",
|
|
877
|
+
description: "Opt-in: generate .claude/commands/*.md thin pointer adapters for the installed .cursor/commands set, and merge a SessionStart context hook into .claude/settings.json (default install behavior is unchanged without this flag)",
|
|
878
|
+
default: false
|
|
879
|
+
},
|
|
880
|
+
cwd: {
|
|
881
|
+
type: "string",
|
|
882
|
+
default: process.cwd()
|
|
883
|
+
},
|
|
884
|
+
...REGISTRY_CLI_ARGS
|
|
885
|
+
},
|
|
886
|
+
async run({ args }) {
|
|
887
|
+
const nonInteractive = args.yes || isNonInteractive();
|
|
888
|
+
if (nonInteractive) {
|
|
889
|
+
logger.info("Non-interactive mode: skipping prompts, using defaults.");
|
|
890
|
+
}
|
|
891
|
+
let projectRoot;
|
|
892
|
+
try {
|
|
893
|
+
projectRoot = await confirmProjectRoot(args.cwd, {
|
|
894
|
+
nonInteractive,
|
|
895
|
+
command: "install",
|
|
896
|
+
forceRoot: args["force-root"]
|
|
897
|
+
});
|
|
898
|
+
} catch (err) {
|
|
899
|
+
if (err instanceof RootRefusedError) {
|
|
900
|
+
logger.error(err.message);
|
|
901
|
+
if (err.recovery) console.error(`
|
|
902
|
+
${err.recovery}
|
|
903
|
+
`);
|
|
904
|
+
process.exitCode = 1;
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
throw err;
|
|
908
|
+
}
|
|
909
|
+
logger.info(`Installing into: ${projectRoot}`);
|
|
910
|
+
logger.info(`CLI v${KIT_VERSION}`);
|
|
911
|
+
await warnIfRunningCliBehindNpm(projectRoot, { warn: (message) => logger.warn(message) });
|
|
912
|
+
const packs = parsePackList(args.pack);
|
|
913
|
+
for (const id of packs) {
|
|
914
|
+
if (!DOMAIN_PACK_IDS.includes(id)) {
|
|
915
|
+
logger.warn(`Pack '${id}' is not in the known L1 list; will still try registry.`);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
try {
|
|
919
|
+
const result = await withCliProgress(
|
|
920
|
+
"install",
|
|
921
|
+
() => performInstall({
|
|
922
|
+
cwd: projectRoot,
|
|
923
|
+
profile: args.profile,
|
|
924
|
+
pack: args.pack,
|
|
925
|
+
registry: args.registry,
|
|
926
|
+
url: args.url,
|
|
927
|
+
ref: args.ref,
|
|
928
|
+
refresh: args.refresh,
|
|
929
|
+
claudeAdapters: args.claude
|
|
930
|
+
})
|
|
931
|
+
);
|
|
932
|
+
logApplyStats(result.stats);
|
|
933
|
+
logger.success(`Manifest written: ${result.manifestPath}`);
|
|
934
|
+
logger.success("Readiness snapshot written: .cursor/context/readiness.json");
|
|
935
|
+
await printPostInstallSummary(result);
|
|
936
|
+
if (result.claudeSessionStartInstructions) {
|
|
937
|
+
logger.warn("Could not merge the Claude Code SessionStart hook automatically:");
|
|
938
|
+
console.log(`
|
|
939
|
+
${result.claudeSessionStartInstructions}
|
|
940
|
+
`);
|
|
941
|
+
}
|
|
942
|
+
if (result.claudeAdapterHint) logger.info(result.claudeAdapterHint);
|
|
943
|
+
} catch (err) {
|
|
944
|
+
if (err instanceof SkippedSymlinkError) {
|
|
945
|
+
logApplyStats(err.stats);
|
|
946
|
+
logger.error(err.message);
|
|
947
|
+
if (err.claudeSessionStartInstructions) {
|
|
948
|
+
logger.warn("Could not merge the Claude Code SessionStart hook automatically:");
|
|
949
|
+
console.log(`
|
|
950
|
+
${err.claudeSessionStartInstructions}
|
|
951
|
+
`);
|
|
952
|
+
}
|
|
953
|
+
process.exitCode = 1;
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
const hint = classifyInstallError(err);
|
|
957
|
+
logger.error(hint.message);
|
|
958
|
+
console.error(`
|
|
959
|
+
${hint.recovery}
|
|
960
|
+
`);
|
|
961
|
+
process.exitCode = 1;
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
export {
|
|
968
|
+
nextStepAfterInstall,
|
|
969
|
+
printInstallEpilogue,
|
|
970
|
+
performInstall,
|
|
971
|
+
installCommand
|
|
972
|
+
};
|