ai-engineering-kit 0.3.0 → 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/dist/cli.js +108 -55
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { basename as
|
|
5
|
-
import { intro, outro, confirm, select, isCancel as isCancel2, cancel as cancel2,
|
|
4
|
+
import { basename as basename3 } from "path";
|
|
5
|
+
import { intro, outro, confirm, select as select2, isCancel as isCancel2, cancel as cancel2, note } from "@clack/prompts";
|
|
6
6
|
|
|
7
7
|
// src/catalog/load.ts
|
|
8
8
|
import { readdirSync, readFileSync } from "fs";
|
|
@@ -162,6 +162,31 @@ function resolveSelection(catalog, selection) {
|
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
// src/menu/summary.ts
|
|
166
|
+
import { basename } from "path";
|
|
167
|
+
var labelOf = (id) => COMPONENTS.find((c) => c.id === id)?.label ?? id;
|
|
168
|
+
function summarize(files, plan) {
|
|
169
|
+
const byPath = new Map(files.map((f) => [f.targetPath, f]));
|
|
170
|
+
const groups = /* @__PURE__ */ new Map();
|
|
171
|
+
const conflicts = [];
|
|
172
|
+
let written = 0;
|
|
173
|
+
for (const action of plan.actions) {
|
|
174
|
+
if (action.type === "conflict") {
|
|
175
|
+
conflicts.push(action.path);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (action.type !== "add" && action.type !== "refresh") continue;
|
|
179
|
+
const file = byPath.get(action.path);
|
|
180
|
+
if (!file) continue;
|
|
181
|
+
written++;
|
|
182
|
+
const group = groups.get(file.component) ?? groups.set(file.component, { component: file.component, label: labelOf(file.component), count: 0, names: [] }).get(file.component);
|
|
183
|
+
group.count++;
|
|
184
|
+
if (file.component === "entry-files") group.names.push(basename(file.targetPath));
|
|
185
|
+
}
|
|
186
|
+
const ordered = COMPONENTS.map((c) => groups.get(c.id)).filter((g) => g !== void 0);
|
|
187
|
+
return { groups: ordered, conflicts, written };
|
|
188
|
+
}
|
|
189
|
+
|
|
165
190
|
// src/planner/planner.ts
|
|
166
191
|
function planInstall(input) {
|
|
167
192
|
const files = resolveSelection(input.catalog, input.selection);
|
|
@@ -338,8 +363,8 @@ On Windows, enable Developer Mode (or run as admin) and re-run, or point Claude
|
|
|
338
363
|
}
|
|
339
364
|
|
|
340
365
|
// src/menu/prompt.ts
|
|
341
|
-
import { basename } from "path";
|
|
342
|
-
import { multiselect, text, isCancel, cancel } from "@clack/prompts";
|
|
366
|
+
import { basename as basename2 } from "path";
|
|
367
|
+
import { select, multiselect, text, isCancel, cancel } from "@clack/prompts";
|
|
343
368
|
|
|
344
369
|
// src/menu/menu.ts
|
|
345
370
|
function componentOptions() {
|
|
@@ -351,6 +376,13 @@ function categoryOptions() {
|
|
|
351
376
|
function agentOptions() {
|
|
352
377
|
return AGENTS.map((a) => ({ value: a.id, label: a.label, hint: a.hint }));
|
|
353
378
|
}
|
|
379
|
+
function presetSelection(preset) {
|
|
380
|
+
return {
|
|
381
|
+
components: COMPONENTS.map((c) => c.id),
|
|
382
|
+
categories: preset === "everything" ? CATEGORIES.map((c) => c.id) : ["core-workflow"],
|
|
383
|
+
agents: AGENTS.map((a) => a.id)
|
|
384
|
+
};
|
|
385
|
+
}
|
|
354
386
|
function buildSelection(answers) {
|
|
355
387
|
return {
|
|
356
388
|
components: answers.components,
|
|
@@ -367,35 +399,31 @@ function required(value) {
|
|
|
367
399
|
}
|
|
368
400
|
return value;
|
|
369
401
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
})
|
|
379
|
-
);
|
|
402
|
+
async function pick(message, options) {
|
|
403
|
+
return required(await multiselect({ message, options, required: true }));
|
|
404
|
+
}
|
|
405
|
+
async function chooseSelection() {
|
|
406
|
+
const components = await pick("Select parts to install", componentOptions());
|
|
407
|
+
const categories = components.includes("skills") ? await pick("Select skill categories", categoryOptions()) : [];
|
|
408
|
+
const agents = components.includes("entry-files") ? await pick("Select coding agents", agentOptions()) : [];
|
|
409
|
+
return buildSelection({ components, categories, agents });
|
|
380
410
|
}
|
|
381
411
|
async function promptForSelection(projectDir) {
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
const projectName = required(
|
|
392
|
-
await text({
|
|
393
|
-
message: "Project name",
|
|
394
|
-
defaultValue: basename(projectDir),
|
|
395
|
-
placeholder: basename(projectDir)
|
|
412
|
+
const mode = required(
|
|
413
|
+
await select({
|
|
414
|
+
message: `How do you want to set up ${basename2(projectDir)}?`,
|
|
415
|
+
options: [
|
|
416
|
+
{ value: "everything", label: "Everything", hint: "all skills, docs, and workspace \u2014 Claude Code + Codex" },
|
|
417
|
+
{ value: "core", label: "Core skills only", hint: "core-workflow skills, docs, and workspace \u2014 both agents" },
|
|
418
|
+
{ value: "choose", label: "Choose what to install\u2026", hint: "pick components, skill categories, and agents" }
|
|
419
|
+
]
|
|
396
420
|
})
|
|
397
421
|
);
|
|
398
|
-
|
|
422
|
+
const selection = mode === "choose" ? await chooseSelection() : presetSelection(mode);
|
|
423
|
+
const projectName = selection.components.includes("entry-files") ? required(
|
|
424
|
+
await text({ message: "Project name", defaultValue: basename2(projectDir), placeholder: basename2(projectDir) })
|
|
425
|
+
) : basename2(projectDir);
|
|
426
|
+
return { selection, projectName };
|
|
399
427
|
}
|
|
400
428
|
|
|
401
429
|
// src/applier/update.ts
|
|
@@ -547,7 +575,29 @@ function planFor(projectDir, selection) {
|
|
|
547
575
|
onDisk: readOnDiskHashes(projectDir, files),
|
|
548
576
|
manifest: readManifest(projectDir)
|
|
549
577
|
});
|
|
550
|
-
return { catalog, plan };
|
|
578
|
+
return { catalog, files, plan };
|
|
579
|
+
}
|
|
580
|
+
function formatSummary(summary) {
|
|
581
|
+
if (summary.written === 0 && summary.conflicts.length === 0) return "Everything is already up to date.";
|
|
582
|
+
const width = Math.max(0, ...summary.groups.map((g) => g.label.length));
|
|
583
|
+
const lines = summary.groups.map((g) => {
|
|
584
|
+
const detail = g.names.length > 0 ? g.names.join(", ") : String(g.count);
|
|
585
|
+
return ` ${g.label.padEnd(width)} ${detail}`;
|
|
586
|
+
});
|
|
587
|
+
if (summary.conflicts.length > 0) {
|
|
588
|
+
lines.push("", `Conflicts \u2014 you edited these (you'll choose per file):`);
|
|
589
|
+
for (const path of summary.conflicts) lines.push(` ${path}`);
|
|
590
|
+
}
|
|
591
|
+
return lines.join("\n");
|
|
592
|
+
}
|
|
593
|
+
function successOutro(verb, written, selection) {
|
|
594
|
+
if (selection.components.includes("skills")) {
|
|
595
|
+
note(
|
|
596
|
+
"Open your coding agent in this project and run a skill:\n setup-foundations fill in docs/foundations\n kickoff brainstorm an MVP",
|
|
597
|
+
"Next steps"
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
outro(`${verb} ${written} file${written === 1 ? "" : "s"}.`);
|
|
551
601
|
}
|
|
552
602
|
function reportConflict(error) {
|
|
553
603
|
if (!(error instanceof ApplyConflictError)) return false;
|
|
@@ -567,7 +617,7 @@ function dryRun(args) {
|
|
|
567
617
|
function init(args) {
|
|
568
618
|
const projectDir = process.cwd();
|
|
569
619
|
const selection = selectionFromArgs(args);
|
|
570
|
-
const projectName = parseValue(args, "--name") ??
|
|
620
|
+
const projectName = parseValue(args, "--name") ?? basename3(projectDir);
|
|
571
621
|
try {
|
|
572
622
|
const result = runInit({ projectDir, selection, projectName });
|
|
573
623
|
console.log(`ai-engineering-kit ${kitVersion()} installed into ${projectDir}`);
|
|
@@ -587,7 +637,7 @@ async function runReRun(projectDir) {
|
|
|
587
637
|
const manifest = readManifest(projectDir);
|
|
588
638
|
intro(`ai-engineering-kit ${kitVersion()} \u2014 already installed`);
|
|
589
639
|
const mode = bail(
|
|
590
|
-
await
|
|
640
|
+
await select2({
|
|
591
641
|
message: "What would you like to do?",
|
|
592
642
|
options: [
|
|
593
643
|
{ value: "update", label: "Update installed parts to latest" },
|
|
@@ -597,7 +647,7 @@ async function runReRun(projectDir) {
|
|
|
597
647
|
})
|
|
598
648
|
);
|
|
599
649
|
let addParts = EMPTY_SELECTION;
|
|
600
|
-
let projectName =
|
|
650
|
+
let projectName = basename3(projectDir);
|
|
601
651
|
if (mode === "add" || mode === "both") {
|
|
602
652
|
const picked = await promptForSelection(projectDir);
|
|
603
653
|
addParts = picked.selection;
|
|
@@ -607,24 +657,27 @@ async function runReRun(projectDir) {
|
|
|
607
657
|
const applicable = {
|
|
608
658
|
actions: prepared.plan.actions.filter((a) => prepared.applyTypes.has(a.type))
|
|
609
659
|
};
|
|
610
|
-
|
|
660
|
+
const pending = applicable.actions.filter((a) => a.type !== "skip").length;
|
|
661
|
+
if (pending === 0) {
|
|
662
|
+
outro("Already up to date \u2014 nothing to do.");
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
note(formatSummary(summarize(prepared.files, applicable)), mode === "add" ? "This will add" : "This will update");
|
|
611
666
|
const resolutions = /* @__PURE__ */ new Map();
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}
|
|
667
|
+
const conflicts = applicable.actions.filter((a) => a.type === "conflict");
|
|
668
|
+
for (const [index, action] of conflicts.entries()) {
|
|
669
|
+
const choice = bail(
|
|
670
|
+
await select2({
|
|
671
|
+
message: `Conflict ${index + 1}/${conflicts.length}: you edited ${action.path}, and a newer version exists.`,
|
|
672
|
+
options: [
|
|
673
|
+
{ value: "keep-mine", label: "Keep mine", hint: "discard the update for this file" },
|
|
674
|
+
{ value: "overwrite", label: "Use the new version", hint: "replace your file" },
|
|
675
|
+
{ value: "keep-theirs", label: "Keep mine + save theirs", hint: `writes ${basename3(action.path)}.new` }
|
|
676
|
+
]
|
|
677
|
+
})
|
|
678
|
+
);
|
|
679
|
+
resolutions.set(action.path, choice);
|
|
626
680
|
}
|
|
627
|
-
const pending = applicable.actions.filter((a) => a.type !== "skip").length;
|
|
628
681
|
const proceed = await confirm({ message: `Apply ${pending} changes in ${projectDir}?` });
|
|
629
682
|
if (isCancel2(proceed) || !proceed) {
|
|
630
683
|
outro("Nothing written.");
|
|
@@ -642,24 +695,24 @@ async function runReRun(projectDir) {
|
|
|
642
695
|
projectName,
|
|
643
696
|
applyTypes: prepared.applyTypes
|
|
644
697
|
});
|
|
645
|
-
|
|
698
|
+
successOutro(mode === "add" ? "Added" : "Updated", result.written.length, prepared.selection);
|
|
646
699
|
}
|
|
647
700
|
async function runInteractive() {
|
|
648
701
|
const projectDir = process.cwd();
|
|
649
702
|
if (readManifest(projectDir)) return runReRun(projectDir);
|
|
650
703
|
intro(`ai-engineering-kit ${kitVersion()}`);
|
|
651
704
|
const { selection, projectName } = await promptForSelection(projectDir);
|
|
652
|
-
const { plan } = planFor(projectDir, selection);
|
|
653
|
-
|
|
705
|
+
const { files, plan } = planFor(projectDir, selection);
|
|
706
|
+
note(formatSummary(summarize(files, plan)), "This will add");
|
|
654
707
|
const pending = plan.actions.filter((a) => a.type !== "skip").length;
|
|
655
|
-
const proceed = await confirm({ message: `
|
|
708
|
+
const proceed = await confirm({ message: `Install ${pending} files into ${projectDir}?` });
|
|
656
709
|
if (isCancel2(proceed) || !proceed) {
|
|
657
710
|
outro("Nothing written.");
|
|
658
711
|
return;
|
|
659
712
|
}
|
|
660
713
|
try {
|
|
661
714
|
const result = runInit({ projectDir, selection, projectName });
|
|
662
|
-
|
|
715
|
+
successOutro("Installed", result.written.length, selection);
|
|
663
716
|
} catch (error) {
|
|
664
717
|
if (!reportConflict(error)) throw error;
|
|
665
718
|
}
|
|
@@ -682,7 +735,7 @@ function performReRun(projectDir, mode, addParts) {
|
|
|
682
735
|
priorManifest: manifest,
|
|
683
736
|
selection: prepared.selection,
|
|
684
737
|
version: prepared.version,
|
|
685
|
-
projectName:
|
|
738
|
+
projectName: basename3(projectDir),
|
|
686
739
|
applyTypes: prepared.applyTypes
|
|
687
740
|
});
|
|
688
741
|
console.log(`${result.written.length} files written. Edited files were kept (use the interactive flow to resolve).`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-engineering-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "An opinionated, agent-agnostic AI-development kit — disciplined skills plus a structured workspace — installed and updated through one npx command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|