thomas-agentkit 0.5.3 → 0.6.1
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/README.md +7 -0
- package/dist/cli.js +213 -245
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,13 @@ Install templates into the current directory:
|
|
|
14
14
|
npx thomas-agentkit init
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
If the package is installed in a project, use the local `agentkit` binary:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -D thomas-agentkit
|
|
21
|
+
npx agentkit init
|
|
22
|
+
```
|
|
23
|
+
|
|
17
24
|
By default, `init` opens a short interactive setup flow in terminals. It asks where to install files, what project type or preset to use, which AI tools you use, which template set to install, how to handle existing files, and whether to personalize repository-level placeholders such as project name, description, issue tracker, docs paths, stack summary, and project commands.
|
|
18
25
|
|
|
19
26
|
Install into another directory:
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { confirm, intro, isCancel, multiselect, select, text } from "@clack/prompts";
|
|
3
3
|
import { Command } from "commander";
|
|
4
|
-
import { constants as fsConstants } from "node:fs";
|
|
4
|
+
import { constants as fsConstants, realpathSync } from "node:fs";
|
|
5
5
|
import { access, mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -212,49 +212,68 @@ function assertKnownKeys(value, validKeys, name) {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
+
function optionalConfigString(rawConfig, key) {
|
|
216
|
+
const value = rawConfig[key];
|
|
217
|
+
if (value === undefined) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
if (typeof value !== "string") {
|
|
221
|
+
throw new Error(`${configFileName} ${key} must be a string.`);
|
|
222
|
+
}
|
|
223
|
+
return value;
|
|
224
|
+
}
|
|
225
|
+
function readConfigAiTools(value) {
|
|
226
|
+
if (value === undefined) {
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
if (!Array.isArray(value)) {
|
|
230
|
+
throw new Error(`${configFileName} aiTools must be an array.`);
|
|
231
|
+
}
|
|
232
|
+
return value.map((aiTool) => {
|
|
233
|
+
if (typeof aiTool !== "string" || !isAiToolName(aiTool)) {
|
|
234
|
+
throw new Error(`Unknown AI tool "${String(aiTool)}". Valid AI tools: ${validAiTools.join(", ")}.`);
|
|
235
|
+
}
|
|
236
|
+
return aiTool;
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function readConfigPersonalization(value) {
|
|
240
|
+
if (value === undefined) {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
assertPlainObject(value, `${configFileName} personalization`);
|
|
244
|
+
assertKnownKeys(value, personalizationKeys, `${configFileName} personalization`);
|
|
245
|
+
const personalization = {};
|
|
246
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
247
|
+
if (typeof entry !== "string") {
|
|
248
|
+
throw new Error(`${configFileName} personalization.${key} must be a string.`);
|
|
249
|
+
}
|
|
250
|
+
personalization[key] = entry;
|
|
251
|
+
}
|
|
252
|
+
return personalization;
|
|
253
|
+
}
|
|
215
254
|
function parseConfig(rawConfig, configPath) {
|
|
216
255
|
assertPlainObject(rawConfig, configFileName);
|
|
217
256
|
assertKnownKeys(rawConfig, configKeys, configFileName);
|
|
257
|
+
const preset = optionalConfigString(rawConfig, "preset");
|
|
258
|
+
const templateSet = optionalConfigString(rawConfig, "templateSet");
|
|
259
|
+
const designSystem = optionalConfigString(rawConfig, "designSystem");
|
|
218
260
|
const config = {};
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
throw new Error(`${configFileName} preset must be a string.`);
|
|
222
|
-
}
|
|
223
|
-
config.preset = resolvePreset(rawConfig.preset);
|
|
261
|
+
if (preset !== undefined) {
|
|
262
|
+
config.preset = resolvePreset(preset);
|
|
224
263
|
}
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
throw new Error(`${configFileName} templateSet must be a string.`);
|
|
228
|
-
}
|
|
229
|
-
config.templateSet = resolveTemplateSet(rawConfig.templateSet);
|
|
264
|
+
if (templateSet !== undefined) {
|
|
265
|
+
config.templateSet = resolveTemplateSet(templateSet);
|
|
230
266
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
config.aiTools = rawConfig.aiTools.map((aiTool) => {
|
|
236
|
-
if (typeof aiTool !== "string" || !isAiToolName(aiTool)) {
|
|
237
|
-
throw new Error(`Unknown AI tool "${String(aiTool)}". Valid AI tools: ${validAiTools.join(", ")}.`);
|
|
238
|
-
}
|
|
239
|
-
return aiTool;
|
|
240
|
-
});
|
|
267
|
+
const aiTools = readConfigAiTools(rawConfig.aiTools);
|
|
268
|
+
if (aiTools !== undefined) {
|
|
269
|
+
config.aiTools = aiTools;
|
|
241
270
|
}
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (rawConfig.personalization !== undefined) {
|
|
249
|
-
assertPlainObject(rawConfig.personalization, `${configFileName} personalization`);
|
|
250
|
-
assertKnownKeys(rawConfig.personalization, personalizationKeys, `${configFileName} personalization`);
|
|
251
|
-
config.personalization = {};
|
|
252
|
-
for (const [key, value] of Object.entries(rawConfig.personalization)) {
|
|
253
|
-
if (typeof value !== "string") {
|
|
254
|
-
throw new Error(`${configFileName} personalization.${key} must be a string.`);
|
|
255
|
-
}
|
|
256
|
-
config.personalization[key] = value;
|
|
257
|
-
}
|
|
271
|
+
if (designSystem !== undefined) {
|
|
272
|
+
config.designSystem = resolveDesignSystem(designSystem);
|
|
273
|
+
}
|
|
274
|
+
const personalization = readConfigPersonalization(rawConfig.personalization);
|
|
275
|
+
if (personalization !== undefined) {
|
|
276
|
+
config.personalization = personalization;
|
|
258
277
|
}
|
|
259
278
|
if (config.preset === undefined && rawConfig.preset !== undefined) {
|
|
260
279
|
throw new Error(`Invalid preset in ${configPath}.`);
|
|
@@ -483,161 +502,96 @@ export function personalizeTemplateContent(file, content, values) {
|
|
|
483
502
|
}
|
|
484
503
|
return personalized;
|
|
485
504
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
initialValue: Boolean(defaults),
|
|
490
|
-
});
|
|
491
|
-
if (isCancel(shouldPersonalize)) {
|
|
492
|
-
process.exit(130);
|
|
493
|
-
}
|
|
494
|
-
if (!shouldPersonalize) {
|
|
495
|
-
return undefined;
|
|
496
|
-
}
|
|
497
|
-
const projectName = await text({
|
|
498
|
-
message: "Project name",
|
|
499
|
-
placeholder: "[Project Name]",
|
|
500
|
-
defaultValue: defaults?.projectName,
|
|
501
|
-
});
|
|
502
|
-
if (isCancel(projectName)) {
|
|
503
|
-
process.exit(130);
|
|
504
|
-
}
|
|
505
|
-
const projectDescription = await text({
|
|
506
|
-
message: "Short project description",
|
|
507
|
-
placeholder: "[short project description]",
|
|
508
|
-
defaultValue: defaults?.projectDescription,
|
|
509
|
-
});
|
|
510
|
-
if (isCancel(projectDescription)) {
|
|
511
|
-
process.exit(130);
|
|
512
|
-
}
|
|
513
|
-
const issueTracker = await text({
|
|
514
|
-
message: "Issue tracker name",
|
|
515
|
-
placeholder: "Linear or GitHub Issues",
|
|
516
|
-
defaultValue: defaults?.issueTracker,
|
|
517
|
-
});
|
|
518
|
-
if (isCancel(issueTracker)) {
|
|
519
|
-
process.exit(130);
|
|
520
|
-
}
|
|
521
|
-
const designSystemPath = await text({
|
|
522
|
-
message: "Design system path",
|
|
523
|
-
placeholder: "docs/design-system.md",
|
|
524
|
-
defaultValue: defaults?.designSystemPath,
|
|
525
|
-
});
|
|
526
|
-
if (isCancel(designSystemPath)) {
|
|
527
|
-
process.exit(130);
|
|
528
|
-
}
|
|
529
|
-
const briefsPath = await text({
|
|
530
|
-
message: "Briefs path",
|
|
531
|
-
placeholder: "docs/briefs",
|
|
532
|
-
defaultValue: defaults?.briefsPath,
|
|
533
|
-
});
|
|
534
|
-
if (isCancel(briefsPath)) {
|
|
535
|
-
process.exit(130);
|
|
505
|
+
export function shouldPromptForInit(options, streams) {
|
|
506
|
+
if (options.yes) {
|
|
507
|
+
return false;
|
|
536
508
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
placeholder: "npm test",
|
|
540
|
-
defaultValue: defaults?.testCommand,
|
|
541
|
-
});
|
|
542
|
-
if (isCancel(testCommand)) {
|
|
543
|
-
process.exit(130);
|
|
509
|
+
if (options.interactive) {
|
|
510
|
+
return true;
|
|
544
511
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
placeholder: "npm run lint",
|
|
548
|
-
defaultValue: defaults?.lintCommand,
|
|
549
|
-
});
|
|
550
|
-
if (isCancel(lintCommand)) {
|
|
551
|
-
process.exit(130);
|
|
512
|
+
if (options.dryRun) {
|
|
513
|
+
return false;
|
|
552
514
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
});
|
|
558
|
-
if (isCancel(buildCommand)) {
|
|
515
|
+
return Boolean(streams.stdin?.isTTY || streams.stdout?.isTTY);
|
|
516
|
+
}
|
|
517
|
+
function resolvePrompt(value) {
|
|
518
|
+
if (isCancel(value)) {
|
|
559
519
|
process.exit(130);
|
|
560
520
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
521
|
+
return value;
|
|
522
|
+
}
|
|
523
|
+
async function promptForTextValue(message, placeholder, defaultValue) {
|
|
524
|
+
return resolvePrompt(await text({ message, placeholder, defaultValue }));
|
|
525
|
+
}
|
|
526
|
+
async function promptForPersonalization(defaults) {
|
|
527
|
+
const shouldPersonalize = resolvePrompt(await confirm({
|
|
528
|
+
message: "Personalize template placeholders?",
|
|
529
|
+
initialValue: Boolean(defaults),
|
|
530
|
+
}));
|
|
531
|
+
if (!shouldPersonalize) {
|
|
532
|
+
return undefined;
|
|
568
533
|
}
|
|
569
534
|
return {
|
|
570
|
-
projectName,
|
|
571
|
-
projectDescription,
|
|
572
|
-
issueTracker,
|
|
573
|
-
designSystemPath,
|
|
574
|
-
briefsPath,
|
|
575
|
-
testCommand,
|
|
576
|
-
lintCommand,
|
|
577
|
-
buildCommand,
|
|
578
|
-
stackSummary,
|
|
535
|
+
projectName: await promptForTextValue("Project name", "[Project Name]", defaults?.projectName),
|
|
536
|
+
projectDescription: await promptForTextValue("Short project description", "[short project description]", defaults?.projectDescription),
|
|
537
|
+
issueTracker: await promptForTextValue("Issue tracker name", "Linear or GitHub Issues", defaults?.issueTracker),
|
|
538
|
+
designSystemPath: await promptForTextValue("Design system path", "docs/design-system.md", defaults?.designSystemPath),
|
|
539
|
+
briefsPath: await promptForTextValue("Briefs path", "docs/briefs", defaults?.briefsPath),
|
|
540
|
+
testCommand: await promptForTextValue("Test command", "npm test", defaults?.testCommand),
|
|
541
|
+
lintCommand: await promptForTextValue("Lint command", "npm run lint", defaults?.lintCommand),
|
|
542
|
+
buildCommand: await promptForTextValue("Build/check command", "npm run build", defaults?.buildCommand),
|
|
543
|
+
stackSummary: await promptForTextValue("Stack summary", "Next.js, TypeScript, Tailwind CSS, Vitest", defaults?.stackSummary),
|
|
579
544
|
};
|
|
580
545
|
}
|
|
581
546
|
async function buildInitTemplateContent(file, preset, personalization, designSystem) {
|
|
582
547
|
const content = await buildTemplateContent(file, preset, designSystem);
|
|
583
548
|
return personalizeTemplateContent(file, content, personalization);
|
|
584
549
|
}
|
|
550
|
+
async function installFileIfAllowed(targetDir, file, options, result, getContent) {
|
|
551
|
+
const destination = path.join(targetDir, file);
|
|
552
|
+
if ((await exists(destination)) && !options.force) {
|
|
553
|
+
result.skipped.push(file);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
result.created.push(file);
|
|
557
|
+
if (options.dryRun) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
561
|
+
await writeFile(destination, await getContent());
|
|
562
|
+
}
|
|
563
|
+
async function resolveInitTemplateFiles(options) {
|
|
564
|
+
const allTemplateFiles = await getTemplateFiles();
|
|
565
|
+
if (options.files) {
|
|
566
|
+
return options.files;
|
|
567
|
+
}
|
|
568
|
+
if (options.templateSet || options.aiTools) {
|
|
569
|
+
return getSelectedTemplateFiles(options.templateSet ?? "full", options.aiTools ?? [], allTemplateFiles);
|
|
570
|
+
}
|
|
571
|
+
return allTemplateFiles;
|
|
572
|
+
}
|
|
585
573
|
async function installTemplates(targetArg, options) {
|
|
586
574
|
const targetDir = path.resolve(process.cwd(), targetArg || ".");
|
|
587
|
-
const
|
|
588
|
-
const files = options.files ??
|
|
589
|
-
(options.templateSet || options.aiTools
|
|
590
|
-
? getSelectedTemplateFiles(options.templateSet ?? "full", options.aiTools ?? [], allTemplateFiles)
|
|
591
|
-
: allTemplateFiles);
|
|
575
|
+
const files = await resolveInitTemplateFiles(options);
|
|
592
576
|
const preset = resolvePreset(options.preset);
|
|
593
577
|
const designSystem = effectiveDesignSystem(options.designSystem);
|
|
594
|
-
const
|
|
595
|
-
const skipped = [];
|
|
578
|
+
const result = { targetDir, created: [], skipped: [] };
|
|
596
579
|
if (!options.dryRun) {
|
|
597
580
|
await mkdir(targetDir, { recursive: true });
|
|
598
581
|
}
|
|
599
582
|
if (options.writeConfig) {
|
|
600
|
-
|
|
601
|
-
const destinationExists = await exists(destination);
|
|
602
|
-
if (destinationExists && !options.force) {
|
|
603
|
-
skipped.push(configFileName);
|
|
604
|
-
}
|
|
605
|
-
else {
|
|
606
|
-
created.push(configFileName);
|
|
607
|
-
if (!options.dryRun) {
|
|
608
|
-
await writeFile(destination, serializeConfig(getResolvedConfig(options)));
|
|
609
|
-
}
|
|
610
|
-
}
|
|
583
|
+
await installFileIfAllowed(targetDir, configFileName, options, result, () => serializeConfig(getResolvedConfig(options)));
|
|
611
584
|
}
|
|
612
585
|
for (const file of files) {
|
|
613
|
-
|
|
614
|
-
const destinationExists = await exists(destination);
|
|
615
|
-
if (destinationExists && !options.force) {
|
|
616
|
-
skipped.push(file);
|
|
617
|
-
continue;
|
|
618
|
-
}
|
|
619
|
-
created.push(file);
|
|
620
|
-
if (!options.dryRun) {
|
|
621
|
-
await mkdir(path.dirname(destination), { recursive: true });
|
|
586
|
+
await installFileIfAllowed(targetDir, file, options, result, async () => {
|
|
622
587
|
const content = await buildInitTemplateContent(file, preset, options.personalization, designSystem);
|
|
623
|
-
|
|
624
|
-
}
|
|
588
|
+
return wrapManagedBlock(file, content);
|
|
589
|
+
});
|
|
625
590
|
}
|
|
626
591
|
if (preset) {
|
|
627
|
-
|
|
628
|
-
const destination = path.join(targetDir, stackFile);
|
|
629
|
-
const destinationExists = await exists(destination);
|
|
630
|
-
if (destinationExists && !options.force) {
|
|
631
|
-
skipped.push(stackFile);
|
|
632
|
-
}
|
|
633
|
-
else {
|
|
634
|
-
created.push(stackFile);
|
|
635
|
-
if (!options.dryRun) {
|
|
636
|
-
await writeFile(destination, wrapManagedBlock(stackFile, getStackGuidance(preset)));
|
|
637
|
-
}
|
|
638
|
-
}
|
|
592
|
+
await installFileIfAllowed(targetDir, "STACK.md", options, result, () => wrapManagedBlock("STACK.md", getStackGuidance(preset)));
|
|
639
593
|
}
|
|
640
|
-
return
|
|
594
|
+
return result;
|
|
641
595
|
}
|
|
642
596
|
function replaceManagedBlock(file, existingContent, nextContent) {
|
|
643
597
|
const id = getTemplateId(file);
|
|
@@ -758,110 +712,113 @@ function printUpdateResult(result, dryRun = false) {
|
|
|
758
712
|
console.log("All managed AgentKit files are current.");
|
|
759
713
|
}
|
|
760
714
|
}
|
|
761
|
-
async function
|
|
762
|
-
|
|
763
|
-
const shouldPrompt = !options.yes && (options.interactive || process.stdin.isTTY);
|
|
764
|
-
if (!shouldPrompt) {
|
|
715
|
+
async function promptForTarget(target) {
|
|
716
|
+
if (target && target !== ".") {
|
|
765
717
|
return target;
|
|
766
718
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
{ label: "Fullstack app", value: "fullstack" },
|
|
791
|
-
],
|
|
792
|
-
});
|
|
793
|
-
if (isCancel(projectTypeResponse)) {
|
|
794
|
-
process.exit(130);
|
|
795
|
-
}
|
|
796
|
-
options.preset = resolveProjectPreset(projectTypeResponse);
|
|
797
|
-
}
|
|
798
|
-
const aiToolResponse = await multiselect({
|
|
719
|
+
return (resolvePrompt(await text({
|
|
720
|
+
message: "Where should AgentKit install files?",
|
|
721
|
+
placeholder: ".",
|
|
722
|
+
defaultValue: ".",
|
|
723
|
+
})) || ".");
|
|
724
|
+
}
|
|
725
|
+
async function promptForProjectPreset() {
|
|
726
|
+
const projectType = resolvePrompt(await select({
|
|
727
|
+
message: "What type of project is this?",
|
|
728
|
+
initialValue: "generic",
|
|
729
|
+
options: [
|
|
730
|
+
{ label: "Generic TypeScript project", value: "generic" },
|
|
731
|
+
{ label: "Next.js app", value: "next" },
|
|
732
|
+
{ label: "SvelteKit app", value: "sveltekit" },
|
|
733
|
+
{ label: "Express API", value: "express" },
|
|
734
|
+
{ label: "Convex app", value: "convex" },
|
|
735
|
+
{ label: "Fullstack app", value: "fullstack" },
|
|
736
|
+
],
|
|
737
|
+
}));
|
|
738
|
+
return resolveProjectPreset(projectType);
|
|
739
|
+
}
|
|
740
|
+
async function promptForAiTools(defaults) {
|
|
741
|
+
return resolvePrompt(await multiselect({
|
|
799
742
|
message: "Which AI tools do you use?",
|
|
800
|
-
initialValues:
|
|
743
|
+
initialValues: defaults ?? ["codex", "cursor", "claude"],
|
|
801
744
|
options: [
|
|
802
745
|
{ label: "Codex", value: "codex" },
|
|
803
746
|
{ label: "Cursor", value: "cursor" },
|
|
804
747
|
{ label: "Claude Code", value: "claude" },
|
|
805
748
|
{ label: "GitHub Copilot", value: "copilot" },
|
|
806
749
|
],
|
|
807
|
-
});
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
const templateSetResponse = await select({
|
|
750
|
+
}));
|
|
751
|
+
}
|
|
752
|
+
async function promptForTemplateSet(defaultValue) {
|
|
753
|
+
return resolvePrompt(await select({
|
|
812
754
|
message: "Which template set do you want?",
|
|
813
|
-
initialValue:
|
|
755
|
+
initialValue: defaultValue ?? "standard",
|
|
814
756
|
options: [
|
|
815
757
|
{ label: "Minimal", value: "minimal" },
|
|
816
758
|
{ label: "Standard", value: "standard" },
|
|
817
759
|
{ label: "Full", value: "full" },
|
|
818
760
|
],
|
|
819
|
-
});
|
|
820
|
-
|
|
821
|
-
|
|
761
|
+
}));
|
|
762
|
+
}
|
|
763
|
+
async function promptForDesignSystem(defaultValue) {
|
|
764
|
+
return resolvePrompt(await select({
|
|
765
|
+
message: "Which design system guidance?",
|
|
766
|
+
initialValue: effectiveDesignSystem(defaultValue),
|
|
767
|
+
options: validDesignSystems.map((value) => ({ label: designSystemLabels[value], value })),
|
|
768
|
+
}));
|
|
769
|
+
}
|
|
770
|
+
async function findExistingInstallFiles(target, files) {
|
|
771
|
+
const targetDir = path.resolve(process.cwd(), target || ".");
|
|
772
|
+
const existingFiles = [];
|
|
773
|
+
for (const file of files) {
|
|
774
|
+
if (await exists(path.join(targetDir, file))) {
|
|
775
|
+
existingFiles.push(file);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return existingFiles;
|
|
779
|
+
}
|
|
780
|
+
async function promptForConflictStrategy(existingFiles) {
|
|
781
|
+
return resolvePrompt(await select({
|
|
782
|
+
message: `Existing files found: ${existingFiles.join(", ")}. How should AgentKit handle conflicts?`,
|
|
783
|
+
initialValue: "skip",
|
|
784
|
+
options: [
|
|
785
|
+
{ label: "Skip existing files", value: "skip" },
|
|
786
|
+
{ label: "Overwrite existing files", value: "overwrite" },
|
|
787
|
+
],
|
|
788
|
+
}));
|
|
789
|
+
}
|
|
790
|
+
async function applyInteractiveSelections(resolvedTarget, providedPreset, options) {
|
|
791
|
+
if (!providedPreset) {
|
|
792
|
+
options.preset = await promptForProjectPreset();
|
|
822
793
|
}
|
|
794
|
+
const aiTools = await promptForAiTools(options.aiTools);
|
|
795
|
+
const templateSet = await promptForTemplateSet(options.templateSet);
|
|
823
796
|
const templateFiles = await getTemplateFiles();
|
|
824
|
-
options.templateSet =
|
|
825
|
-
options.aiTools =
|
|
826
|
-
options.files = getSelectedTemplateFiles(
|
|
827
|
-
if (
|
|
828
|
-
|
|
829
|
-
message: "Which design system guidance?",
|
|
830
|
-
initialValue: effectiveDesignSystem(options.designSystem),
|
|
831
|
-
options: validDesignSystems.map((value) => ({ label: designSystemLabels[value], value })),
|
|
832
|
-
});
|
|
833
|
-
if (isCancel(designSystemResponse)) {
|
|
834
|
-
process.exit(130);
|
|
835
|
-
}
|
|
836
|
-
options.designSystem = designSystemResponse;
|
|
797
|
+
options.templateSet = templateSet;
|
|
798
|
+
options.aiTools = aiTools;
|
|
799
|
+
options.files = getSelectedTemplateFiles(templateSet, aiTools, templateFiles);
|
|
800
|
+
if (templateSet === "standard" || templateSet === "full") {
|
|
801
|
+
options.designSystem = await promptForDesignSystem(options.designSystem);
|
|
837
802
|
}
|
|
838
803
|
if (!options.force) {
|
|
839
804
|
const preset = resolvePreset(options.preset);
|
|
840
805
|
const installFiles = preset ? [...options.files, "STACK.md"] : options.files;
|
|
841
|
-
const
|
|
842
|
-
const existingFiles = [];
|
|
843
|
-
for (const file of installFiles) {
|
|
844
|
-
if (await exists(path.join(targetDir, file))) {
|
|
845
|
-
existingFiles.push(file);
|
|
846
|
-
}
|
|
847
|
-
}
|
|
806
|
+
const existingFiles = await findExistingInstallFiles(resolvedTarget, installFiles);
|
|
848
807
|
if (existingFiles.length > 0) {
|
|
849
|
-
|
|
850
|
-
message: `Existing files found: ${existingFiles.join(", ")}. How should AgentKit handle conflicts?`,
|
|
851
|
-
initialValue: "skip",
|
|
852
|
-
options: [
|
|
853
|
-
{ label: "Skip existing files", value: "skip" },
|
|
854
|
-
{ label: "Overwrite existing files", value: "overwrite" },
|
|
855
|
-
],
|
|
856
|
-
});
|
|
857
|
-
if (isCancel(conflictResponse)) {
|
|
858
|
-
process.exit(130);
|
|
859
|
-
}
|
|
860
|
-
options.force = conflictResponse === "overwrite";
|
|
808
|
+
options.force = (await promptForConflictStrategy(existingFiles)) === "overwrite";
|
|
861
809
|
}
|
|
862
810
|
}
|
|
811
|
+
}
|
|
812
|
+
async function resolveInteractiveTarget(target, options) {
|
|
813
|
+
const providedPreset = resolvePreset(options.preset);
|
|
814
|
+
if (!shouldPromptForInit(options, process)) {
|
|
815
|
+
return target;
|
|
816
|
+
}
|
|
817
|
+
intro("Welcome to AgentKit");
|
|
818
|
+
const resolvedTarget = await promptForTarget(target);
|
|
819
|
+
await applyInteractiveSelections(resolvedTarget, providedPreset, options);
|
|
863
820
|
options.personalization = await promptForPersonalization(options.personalization);
|
|
864
|
-
return resolvedTarget
|
|
821
|
+
return resolvedTarget;
|
|
865
822
|
}
|
|
866
823
|
async function main() {
|
|
867
824
|
if (process.argv.slice(2).includes("--list")) {
|
|
@@ -932,7 +889,18 @@ Examples:
|
|
|
932
889
|
});
|
|
933
890
|
await program.parseAsync(process.argv);
|
|
934
891
|
}
|
|
935
|
-
|
|
892
|
+
function resolveCliPath(filePath) {
|
|
893
|
+
try {
|
|
894
|
+
return realpathSync(filePath);
|
|
895
|
+
}
|
|
896
|
+
catch {
|
|
897
|
+
return path.resolve(filePath);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
function isDirectCliInvocation(argvPath) {
|
|
901
|
+
return Boolean(argvPath && resolveCliPath(argvPath) === resolveCliPath(__filename));
|
|
902
|
+
}
|
|
903
|
+
if (isDirectCliInvocation(process.argv[1])) {
|
|
936
904
|
main().catch((error) => {
|
|
937
905
|
const message = error instanceof Error ? error.message : String(error);
|
|
938
906
|
console.error(message);
|