thomas-agentkit 0.6.0 → 0.7.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  AgentKit CLI bootstraps AI-agent-ready repositories.
4
4
 
5
- It installs reusable instructions, planning templates, review docs, design system rules, and workflow guidance for developers using Codex, Cursor, GitHub Copilot, Claude Code, and similar coding agents.
5
+ It installs reusable agent instructions, workflow guides, quality checklists, design system rules, planning templates, and tool adapters for developers using Codex, Cursor, GitHub Copilot, Claude Code, and similar coding agents.
6
6
 
7
7
  AgentKit is not an AI agent. It is a small scaffolding tool for making repositories easier and safer to work on with AI-assisted development.
8
8
 
@@ -35,7 +35,7 @@ Accept defaults without prompts:
35
35
  npx thomas-agentkit init --yes
36
36
  ```
37
37
 
38
- `--yes` keeps installed templates generic and leaves placeholders for later editing.
38
+ `--yes` installs the `standard` template set, keeps templates generic, and leaves placeholders for later editing.
39
39
 
40
40
  Preview changes without writing files:
41
41
 
@@ -127,14 +127,17 @@ npx thomas-agentkit --list-design-systems
127
127
 
128
128
  ## Installed Files
129
129
 
130
- AgentKit copies these bundled files into the target project:
130
+ AgentKit can copy these bundled files into the target project:
131
131
 
132
132
  - `AGENTS.md`
133
+ - `CHANGE-EXPLANATION.md`
133
134
  - `CLAUDE.md`
134
135
  - `CODE-QUALITY.md`
135
136
  - `DESIGN-SYSTEM.md`
136
137
  - `IMPLEMENTATION-BRIEF-TEMPLATE.md`
137
138
  - `PRD-TEMPLATE.md`
139
+ - `SECURITY-CHECKLIST.md`
140
+ - `TESTING.md`
138
141
  - `WORKFLOWS.md`
139
142
  - `.cursor/rules/agentkit.md`
140
143
  - `.github/copilot-instructions.md`
@@ -163,8 +166,8 @@ Interactive personalization only applies during `agentkit init` when files are c
163
166
  Template set mappings:
164
167
 
165
168
  - `minimal`: `AGENTS.md`
166
- - `standard`: `AGENTS.md`, `CODE-QUALITY.md`, `DESIGN-SYSTEM.md`, `WORKFLOWS.md`
167
- - `full`: all bundled templates
169
+ - `standard`: `AGENTS.md`, `CHANGE-EXPLANATION.md`, `CODE-QUALITY.md`, `DESIGN-SYSTEM.md`, `WORKFLOWS.md`, `.github/pull_request_template.md`
170
+ - `full`: all bundled templates, including AI tool adapters and planning/testing/security guides
168
171
 
169
172
  AI tool mappings:
170
173
 
@@ -173,6 +176,8 @@ AI tool mappings:
173
176
  - `claude`: `CLAUDE.md`
174
177
  - `copilot`: `.github/copilot-instructions.md`
175
178
 
179
+ AI tool adapters are thin compatibility files that point back to `AGENTS.md` as the source of truth. They are installed when selected through AI tools or when using the `full` template set.
180
+
176
181
  ## Presets
177
182
 
178
183
  Presets add stack-specific guidance without scaffolding framework app files.
package/dist/cli.js CHANGED
@@ -47,7 +47,14 @@ const aiToolFiles = {
47
47
  };
48
48
  const templateSetFiles = {
49
49
  minimal: ["AGENTS.md"],
50
- standard: ["AGENTS.md", "CODE-QUALITY.md", "DESIGN-SYSTEM.md", "WORKFLOWS.md"],
50
+ standard: [
51
+ "AGENTS.md",
52
+ "CHANGE-EXPLANATION.md",
53
+ "CODE-QUALITY.md",
54
+ "DESIGN-SYSTEM.md",
55
+ "WORKFLOWS.md",
56
+ ".github/pull_request_template.md",
57
+ ],
51
58
  full: [],
52
59
  };
53
60
  const stackGuidance = {
@@ -212,49 +219,68 @@ function assertKnownKeys(value, validKeys, name) {
212
219
  }
213
220
  }
214
221
  }
222
+ function optionalConfigString(rawConfig, key) {
223
+ const value = rawConfig[key];
224
+ if (value === undefined) {
225
+ return undefined;
226
+ }
227
+ if (typeof value !== "string") {
228
+ throw new Error(`${configFileName} ${key} must be a string.`);
229
+ }
230
+ return value;
231
+ }
232
+ function readConfigAiTools(value) {
233
+ if (value === undefined) {
234
+ return undefined;
235
+ }
236
+ if (!Array.isArray(value)) {
237
+ throw new Error(`${configFileName} aiTools must be an array.`);
238
+ }
239
+ return value.map((aiTool) => {
240
+ if (typeof aiTool !== "string" || !isAiToolName(aiTool)) {
241
+ throw new Error(`Unknown AI tool "${String(aiTool)}". Valid AI tools: ${validAiTools.join(", ")}.`);
242
+ }
243
+ return aiTool;
244
+ });
245
+ }
246
+ function readConfigPersonalization(value) {
247
+ if (value === undefined) {
248
+ return undefined;
249
+ }
250
+ assertPlainObject(value, `${configFileName} personalization`);
251
+ assertKnownKeys(value, personalizationKeys, `${configFileName} personalization`);
252
+ const personalization = {};
253
+ for (const [key, entry] of Object.entries(value)) {
254
+ if (typeof entry !== "string") {
255
+ throw new Error(`${configFileName} personalization.${key} must be a string.`);
256
+ }
257
+ personalization[key] = entry;
258
+ }
259
+ return personalization;
260
+ }
215
261
  function parseConfig(rawConfig, configPath) {
216
262
  assertPlainObject(rawConfig, configFileName);
217
263
  assertKnownKeys(rawConfig, configKeys, configFileName);
264
+ const preset = optionalConfigString(rawConfig, "preset");
265
+ const templateSet = optionalConfigString(rawConfig, "templateSet");
266
+ const designSystem = optionalConfigString(rawConfig, "designSystem");
218
267
  const config = {};
219
- if (rawConfig.preset !== undefined) {
220
- if (typeof rawConfig.preset !== "string") {
221
- throw new Error(`${configFileName} preset must be a string.`);
222
- }
223
- config.preset = resolvePreset(rawConfig.preset);
268
+ if (preset !== undefined) {
269
+ config.preset = resolvePreset(preset);
224
270
  }
225
- if (rawConfig.templateSet !== undefined) {
226
- if (typeof rawConfig.templateSet !== "string") {
227
- throw new Error(`${configFileName} templateSet must be a string.`);
228
- }
229
- config.templateSet = resolveTemplateSet(rawConfig.templateSet);
271
+ if (templateSet !== undefined) {
272
+ config.templateSet = resolveTemplateSet(templateSet);
230
273
  }
231
- if (rawConfig.aiTools !== undefined) {
232
- if (!Array.isArray(rawConfig.aiTools)) {
233
- throw new Error(`${configFileName} aiTools must be an array.`);
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
- });
274
+ const aiTools = readConfigAiTools(rawConfig.aiTools);
275
+ if (aiTools !== undefined) {
276
+ config.aiTools = aiTools;
241
277
  }
242
- if (rawConfig.designSystem !== undefined) {
243
- if (typeof rawConfig.designSystem !== "string") {
244
- throw new Error(`${configFileName} designSystem must be a string.`);
245
- }
246
- config.designSystem = resolveDesignSystem(rawConfig.designSystem);
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
- }
278
+ if (designSystem !== undefined) {
279
+ config.designSystem = resolveDesignSystem(designSystem);
280
+ }
281
+ const personalization = readConfigPersonalization(rawConfig.personalization);
282
+ if (personalization !== undefined) {
283
+ config.personalization = personalization;
258
284
  }
259
285
  if (config.preset === undefined && rawConfig.preset !== undefined) {
260
286
  throw new Error(`Invalid preset in ${configPath}.`);
@@ -371,7 +397,7 @@ function cleanPersonalizationValue(value) {
371
397
  }
372
398
  function getResolvedConfig(options) {
373
399
  const config = {
374
- templateSet: options.templateSet ?? "full",
400
+ templateSet: options.templateSet ?? "standard",
375
401
  aiTools: options.aiTools ?? [],
376
402
  designSystem: effectiveDesignSystem(options.designSystem),
377
403
  };
@@ -495,161 +521,81 @@ export function shouldPromptForInit(options, streams) {
495
521
  }
496
522
  return Boolean(streams.stdin?.isTTY || streams.stdout?.isTTY);
497
523
  }
524
+ function resolvePrompt(value) {
525
+ if (isCancel(value)) {
526
+ process.exit(130);
527
+ }
528
+ return value;
529
+ }
530
+ async function promptForTextValue(message, placeholder, defaultValue) {
531
+ return resolvePrompt(await text({ message, placeholder, defaultValue }));
532
+ }
498
533
  async function promptForPersonalization(defaults) {
499
- const shouldPersonalize = await confirm({
534
+ const shouldPersonalize = resolvePrompt(await confirm({
500
535
  message: "Personalize template placeholders?",
501
536
  initialValue: Boolean(defaults),
502
- });
503
- if (isCancel(shouldPersonalize)) {
504
- process.exit(130);
505
- }
537
+ }));
506
538
  if (!shouldPersonalize) {
507
539
  return undefined;
508
540
  }
509
- const projectName = await text({
510
- message: "Project name",
511
- placeholder: "[Project Name]",
512
- defaultValue: defaults?.projectName,
513
- });
514
- if (isCancel(projectName)) {
515
- process.exit(130);
516
- }
517
- const projectDescription = await text({
518
- message: "Short project description",
519
- placeholder: "[short project description]",
520
- defaultValue: defaults?.projectDescription,
521
- });
522
- if (isCancel(projectDescription)) {
523
- process.exit(130);
524
- }
525
- const issueTracker = await text({
526
- message: "Issue tracker name",
527
- placeholder: "Linear or GitHub Issues",
528
- defaultValue: defaults?.issueTracker,
529
- });
530
- if (isCancel(issueTracker)) {
531
- process.exit(130);
532
- }
533
- const designSystemPath = await text({
534
- message: "Design system path",
535
- placeholder: "docs/design-system.md",
536
- defaultValue: defaults?.designSystemPath,
537
- });
538
- if (isCancel(designSystemPath)) {
539
- process.exit(130);
540
- }
541
- const briefsPath = await text({
542
- message: "Briefs path",
543
- placeholder: "docs/briefs",
544
- defaultValue: defaults?.briefsPath,
545
- });
546
- if (isCancel(briefsPath)) {
547
- process.exit(130);
548
- }
549
- const testCommand = await text({
550
- message: "Test command",
551
- placeholder: "npm test",
552
- defaultValue: defaults?.testCommand,
553
- });
554
- if (isCancel(testCommand)) {
555
- process.exit(130);
556
- }
557
- const lintCommand = await text({
558
- message: "Lint command",
559
- placeholder: "npm run lint",
560
- defaultValue: defaults?.lintCommand,
561
- });
562
- if (isCancel(lintCommand)) {
563
- process.exit(130);
564
- }
565
- const buildCommand = await text({
566
- message: "Build/check command",
567
- placeholder: "npm run build",
568
- defaultValue: defaults?.buildCommand,
569
- });
570
- if (isCancel(buildCommand)) {
571
- process.exit(130);
572
- }
573
- const stackSummary = await text({
574
- message: "Stack summary",
575
- placeholder: "Next.js, TypeScript, Tailwind CSS, Vitest",
576
- defaultValue: defaults?.stackSummary,
577
- });
578
- if (isCancel(stackSummary)) {
579
- process.exit(130);
580
- }
581
541
  return {
582
- projectName,
583
- projectDescription,
584
- issueTracker,
585
- designSystemPath,
586
- briefsPath,
587
- testCommand,
588
- lintCommand,
589
- buildCommand,
590
- stackSummary,
542
+ projectName: await promptForTextValue("Project name", "[Project Name]", defaults?.projectName),
543
+ projectDescription: await promptForTextValue("Short project description", "[short project description]", defaults?.projectDescription),
544
+ issueTracker: await promptForTextValue("Issue tracker name", "Linear or GitHub Issues", defaults?.issueTracker),
545
+ designSystemPath: await promptForTextValue("Design system path", "docs/design-system.md", defaults?.designSystemPath),
546
+ briefsPath: await promptForTextValue("Briefs path", "docs/briefs", defaults?.briefsPath),
547
+ testCommand: await promptForTextValue("Test command", "npm test", defaults?.testCommand),
548
+ lintCommand: await promptForTextValue("Lint command", "npm run lint", defaults?.lintCommand),
549
+ buildCommand: await promptForTextValue("Build/check command", "npm run build", defaults?.buildCommand),
550
+ stackSummary: await promptForTextValue("Stack summary", "Next.js, TypeScript, Tailwind CSS, Vitest", defaults?.stackSummary),
591
551
  };
592
552
  }
593
553
  async function buildInitTemplateContent(file, preset, personalization, designSystem) {
594
554
  const content = await buildTemplateContent(file, preset, designSystem);
595
555
  return personalizeTemplateContent(file, content, personalization);
596
556
  }
557
+ async function installFileIfAllowed(targetDir, file, options, result, getContent) {
558
+ const destination = path.join(targetDir, file);
559
+ if ((await exists(destination)) && !options.force) {
560
+ result.skipped.push(file);
561
+ return;
562
+ }
563
+ result.created.push(file);
564
+ if (options.dryRun) {
565
+ return;
566
+ }
567
+ await mkdir(path.dirname(destination), { recursive: true });
568
+ await writeFile(destination, await getContent());
569
+ }
570
+ async function resolveInitTemplateFiles(options) {
571
+ const allTemplateFiles = await getTemplateFiles();
572
+ if (options.files) {
573
+ return options.files;
574
+ }
575
+ return getSelectedTemplateFiles(options.templateSet ?? "standard", options.aiTools ?? [], allTemplateFiles);
576
+ }
597
577
  async function installTemplates(targetArg, options) {
598
578
  const targetDir = path.resolve(process.cwd(), targetArg || ".");
599
- const allTemplateFiles = await getTemplateFiles();
600
- const files = options.files ??
601
- (options.templateSet || options.aiTools
602
- ? getSelectedTemplateFiles(options.templateSet ?? "full", options.aiTools ?? [], allTemplateFiles)
603
- : allTemplateFiles);
579
+ const files = await resolveInitTemplateFiles(options);
604
580
  const preset = resolvePreset(options.preset);
605
581
  const designSystem = effectiveDesignSystem(options.designSystem);
606
- const created = [];
607
- const skipped = [];
582
+ const result = { targetDir, created: [], skipped: [] };
608
583
  if (!options.dryRun) {
609
584
  await mkdir(targetDir, { recursive: true });
610
585
  }
611
586
  if (options.writeConfig) {
612
- const destination = path.join(targetDir, configFileName);
613
- const destinationExists = await exists(destination);
614
- if (destinationExists && !options.force) {
615
- skipped.push(configFileName);
616
- }
617
- else {
618
- created.push(configFileName);
619
- if (!options.dryRun) {
620
- await writeFile(destination, serializeConfig(getResolvedConfig(options)));
621
- }
622
- }
587
+ await installFileIfAllowed(targetDir, configFileName, options, result, () => serializeConfig(getResolvedConfig(options)));
623
588
  }
624
589
  for (const file of files) {
625
- const destination = path.join(targetDir, file);
626
- const destinationExists = await exists(destination);
627
- if (destinationExists && !options.force) {
628
- skipped.push(file);
629
- continue;
630
- }
631
- created.push(file);
632
- if (!options.dryRun) {
633
- await mkdir(path.dirname(destination), { recursive: true });
590
+ await installFileIfAllowed(targetDir, file, options, result, async () => {
634
591
  const content = await buildInitTemplateContent(file, preset, options.personalization, designSystem);
635
- await writeFile(destination, wrapManagedBlock(file, content));
636
- }
592
+ return wrapManagedBlock(file, content);
593
+ });
637
594
  }
638
595
  if (preset) {
639
- const stackFile = "STACK.md";
640
- const destination = path.join(targetDir, stackFile);
641
- const destinationExists = await exists(destination);
642
- if (destinationExists && !options.force) {
643
- skipped.push(stackFile);
644
- }
645
- else {
646
- created.push(stackFile);
647
- if (!options.dryRun) {
648
- await writeFile(destination, wrapManagedBlock(stackFile, getStackGuidance(preset)));
649
- }
650
- }
596
+ await installFileIfAllowed(targetDir, "STACK.md", options, result, () => wrapManagedBlock("STACK.md", getStackGuidance(preset)));
651
597
  }
652
- return { targetDir, created, skipped };
598
+ return result;
653
599
  }
654
600
  function replaceManagedBlock(file, existingContent, nextContent) {
655
601
  const id = getTemplateId(file);
@@ -770,110 +716,113 @@ function printUpdateResult(result, dryRun = false) {
770
716
  console.log("All managed AgentKit files are current.");
771
717
  }
772
718
  }
773
- async function resolveInteractiveTarget(target, options) {
774
- const providedPreset = resolvePreset(options.preset);
775
- const shouldPrompt = shouldPromptForInit(options, process);
776
- if (!shouldPrompt) {
719
+ async function promptForTarget(target) {
720
+ if (target && target !== ".") {
777
721
  return target;
778
722
  }
779
- intro("Welcome to AgentKit");
780
- let resolvedTarget = target;
781
- if (!target || target === ".") {
782
- const targetResponse = await text({
783
- message: "Where should AgentKit install files?",
784
- placeholder: ".",
785
- defaultValue: ".",
786
- });
787
- if (isCancel(targetResponse)) {
788
- process.exit(130);
789
- }
790
- resolvedTarget = targetResponse || ".";
791
- }
792
- if (!providedPreset) {
793
- const projectTypeResponse = await select({
794
- message: "What type of project is this?",
795
- initialValue: "generic",
796
- options: [
797
- { label: "Generic TypeScript project", value: "generic" },
798
- { label: "Next.js app", value: "next" },
799
- { label: "SvelteKit app", value: "sveltekit" },
800
- { label: "Express API", value: "express" },
801
- { label: "Convex app", value: "convex" },
802
- { label: "Fullstack app", value: "fullstack" },
803
- ],
804
- });
805
- if (isCancel(projectTypeResponse)) {
806
- process.exit(130);
807
- }
808
- options.preset = resolveProjectPreset(projectTypeResponse);
809
- }
810
- const aiToolResponse = await multiselect({
723
+ return (resolvePrompt(await text({
724
+ message: "Where should AgentKit install files?",
725
+ placeholder: ".",
726
+ defaultValue: ".",
727
+ })) || ".");
728
+ }
729
+ async function promptForProjectPreset() {
730
+ const projectType = resolvePrompt(await select({
731
+ message: "What type of project is this?",
732
+ initialValue: "generic",
733
+ options: [
734
+ { label: "Generic TypeScript project", value: "generic" },
735
+ { label: "Next.js app", value: "next" },
736
+ { label: "SvelteKit app", value: "sveltekit" },
737
+ { label: "Express API", value: "express" },
738
+ { label: "Convex app", value: "convex" },
739
+ { label: "Fullstack app", value: "fullstack" },
740
+ ],
741
+ }));
742
+ return resolveProjectPreset(projectType);
743
+ }
744
+ async function promptForAiTools(defaults) {
745
+ return resolvePrompt(await multiselect({
811
746
  message: "Which AI tools do you use?",
812
- initialValues: options.aiTools ?? ["codex", "cursor", "claude"],
747
+ initialValues: defaults ?? ["codex", "cursor", "claude"],
813
748
  options: [
814
749
  { label: "Codex", value: "codex" },
815
750
  { label: "Cursor", value: "cursor" },
816
751
  { label: "Claude Code", value: "claude" },
817
752
  { label: "GitHub Copilot", value: "copilot" },
818
753
  ],
819
- });
820
- if (isCancel(aiToolResponse)) {
821
- process.exit(130);
822
- }
823
- const templateSetResponse = await select({
754
+ }));
755
+ }
756
+ async function promptForTemplateSet(defaultValue) {
757
+ return resolvePrompt(await select({
824
758
  message: "Which template set do you want?",
825
- initialValue: options.templateSet ?? "standard",
759
+ initialValue: defaultValue ?? "standard",
826
760
  options: [
827
761
  { label: "Minimal", value: "minimal" },
828
762
  { label: "Standard", value: "standard" },
829
763
  { label: "Full", value: "full" },
830
764
  ],
831
- });
832
- if (isCancel(templateSetResponse)) {
833
- process.exit(130);
765
+ }));
766
+ }
767
+ async function promptForDesignSystem(defaultValue) {
768
+ return resolvePrompt(await select({
769
+ message: "Which design system guidance?",
770
+ initialValue: effectiveDesignSystem(defaultValue),
771
+ options: validDesignSystems.map((value) => ({ label: designSystemLabels[value], value })),
772
+ }));
773
+ }
774
+ async function findExistingInstallFiles(target, files) {
775
+ const targetDir = path.resolve(process.cwd(), target || ".");
776
+ const existingFiles = [];
777
+ for (const file of files) {
778
+ if (await exists(path.join(targetDir, file))) {
779
+ existingFiles.push(file);
780
+ }
834
781
  }
782
+ return existingFiles;
783
+ }
784
+ async function promptForConflictStrategy(existingFiles) {
785
+ return resolvePrompt(await select({
786
+ message: `Existing files found: ${existingFiles.join(", ")}. How should AgentKit handle conflicts?`,
787
+ initialValue: "skip",
788
+ options: [
789
+ { label: "Skip existing files", value: "skip" },
790
+ { label: "Overwrite existing files", value: "overwrite" },
791
+ ],
792
+ }));
793
+ }
794
+ async function applyInteractiveSelections(resolvedTarget, providedPreset, options) {
795
+ if (!providedPreset) {
796
+ options.preset = await promptForProjectPreset();
797
+ }
798
+ const aiTools = await promptForAiTools(options.aiTools);
799
+ const templateSet = await promptForTemplateSet(options.templateSet);
835
800
  const templateFiles = await getTemplateFiles();
836
- options.templateSet = templateSetResponse;
837
- options.aiTools = aiToolResponse;
838
- options.files = getSelectedTemplateFiles(templateSetResponse, aiToolResponse, templateFiles);
839
- if (templateSetResponse === "standard" || templateSetResponse === "full") {
840
- const designSystemResponse = await select({
841
- message: "Which design system guidance?",
842
- initialValue: effectiveDesignSystem(options.designSystem),
843
- options: validDesignSystems.map((value) => ({ label: designSystemLabels[value], value })),
844
- });
845
- if (isCancel(designSystemResponse)) {
846
- process.exit(130);
847
- }
848
- options.designSystem = designSystemResponse;
801
+ options.templateSet = templateSet;
802
+ options.aiTools = aiTools;
803
+ options.files = getSelectedTemplateFiles(templateSet, aiTools, templateFiles);
804
+ if (templateSet === "standard" || templateSet === "full") {
805
+ options.designSystem = await promptForDesignSystem(options.designSystem);
849
806
  }
850
807
  if (!options.force) {
851
808
  const preset = resolvePreset(options.preset);
852
809
  const installFiles = preset ? [...options.files, "STACK.md"] : options.files;
853
- const targetDir = path.resolve(process.cwd(), resolvedTarget || ".");
854
- const existingFiles = [];
855
- for (const file of installFiles) {
856
- if (await exists(path.join(targetDir, file))) {
857
- existingFiles.push(file);
858
- }
859
- }
810
+ const existingFiles = await findExistingInstallFiles(resolvedTarget, installFiles);
860
811
  if (existingFiles.length > 0) {
861
- const conflictResponse = await select({
862
- message: `Existing files found: ${existingFiles.join(", ")}. How should AgentKit handle conflicts?`,
863
- initialValue: "skip",
864
- options: [
865
- { label: "Skip existing files", value: "skip" },
866
- { label: "Overwrite existing files", value: "overwrite" },
867
- ],
868
- });
869
- if (isCancel(conflictResponse)) {
870
- process.exit(130);
871
- }
872
- options.force = conflictResponse === "overwrite";
812
+ options.force = (await promptForConflictStrategy(existingFiles)) === "overwrite";
873
813
  }
874
814
  }
815
+ }
816
+ async function resolveInteractiveTarget(target, options) {
817
+ const providedPreset = resolvePreset(options.preset);
818
+ if (!shouldPromptForInit(options, process)) {
819
+ return target;
820
+ }
821
+ intro("Welcome to AgentKit");
822
+ const resolvedTarget = await promptForTarget(target);
823
+ await applyInteractiveSelections(resolvedTarget, providedPreset, options);
875
824
  options.personalization = await promptForPersonalization(options.personalization);
876
- return resolvedTarget || ".";
825
+ return resolvedTarget;
877
826
  }
878
827
  async function main() {
879
828
  if (process.argv.slice(2).includes("--list")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thomas-agentkit",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Install AI-agent-ready development templates into a project.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,26 +1,26 @@
1
1
  # Cursor AgentKit Rules
2
2
 
3
- Use these rules with Cursor agents and composer workflows.
3
+ Use this rule as a repository guidance router for Cursor agents and composer workflows.
4
4
 
5
- ## Default Behavior
5
+ ## Primary Guidance
6
6
 
7
- - Read `AGENTS.md` before implementation.
8
- - Read `DESIGN-SYSTEM.md` before UI, styling, layout, navigation, or component work.
9
- - Read `CODE-QUALITY.md` before review or refactor work.
10
- - Create or update an implementation brief for complex work.
7
+ - Follow `AGENTS.md` first.
8
+ - Treat `AGENTS.md` as the source of truth for repository-wide agent behavior.
9
+ - Keep context focused. Read companion files only when relevant to the task.
11
10
 
12
- ## Implementation Rules
11
+ ## Read When Relevant
13
12
 
14
- - Prefer the smallest complete solution.
15
- - Reuse existing components, utilities, and patterns.
16
- - Keep diffs focused and reviewable.
17
- - Avoid speculative abstractions and dependency bloat.
18
- - Do not change foundational theme, schema, or architecture without explicit approval.
13
+ - `CODE-QUALITY.md`: code quality, review, refactors, dependencies.
14
+ - `WORKFLOWS.md`: planning, implementation, review, release.
15
+ - `CHANGE-EXPLANATION.md`: final handoff and developer-facing explanation.
16
+ - `DESIGN-SYSTEM.md`: UI, styling, layout, navigation, components.
17
+ - `TESTING.md`: tests, fixtures, mocks, QA strategy.
18
+ - `SECURITY-CHECKLIST.md`: auth, permissions, secrets, PII, data handling.
19
+ - `STACK.md`: stack-specific rules when present.
19
20
 
20
- ## Handoff
21
+ ## Cursor Behavior
21
22
 
22
- Always summarize:
23
-
24
- - Files or areas changed.
25
- - Checks run.
26
- - Known risks or follow-up work.
23
+ - Prefer existing repository patterns over generic generated patterns.
24
+ - Keep changes scoped and reviewable.
25
+ - Do not change foundational architecture, schema, dependencies, or theme primitives without explicit approval.
26
+ - Summarize changed files, checks run, risks, and review focus before handoff.