thomas-agentkit 0.4.2 → 0.5.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
@@ -72,6 +72,7 @@ Standardize install defaults with `agentkit.config.json`:
72
72
  {
73
73
  "preset": "next",
74
74
  "templateSet": "standard",
75
+ "designSystem": "linear",
75
76
  "aiTools": ["codex", "cursor", "claude"],
76
77
  "personalization": {
77
78
  "projectName": "Acme CRM",
@@ -109,6 +110,12 @@ List available presets:
109
110
  npx thomas-agentkit --list-presets
110
111
  ```
111
112
 
113
+ List bundled design systems (source variants for `DESIGN-SYSTEM.md`):
114
+
115
+ ```bash
116
+ npx thomas-agentkit --list-design-systems
117
+ ```
118
+
112
119
  ## Installed Files
113
120
 
114
121
  AgentKit copies these bundled files into the target project:
@@ -126,6 +133,8 @@ AgentKit copies these bundled files into the target project:
126
133
 
127
134
  When a preset is selected, AgentKit also installs `STACK.md` and adds a note in `AGENTS.md` telling agents to read it before changing stack-specific code.
128
135
 
136
+ Bundled design system guidance is stored under `templates/design-systems/` in this repository (for example `linear.md`). The CLI installs the chosen variant into `DESIGN-SYSTEM.md` in the target project; variant paths are not separate install targets.
137
+
129
138
  Existing files are skipped by default so local edits are preserved. Use `--force` when you intentionally want to refresh files from the bundled package version.
130
139
 
131
140
  New installs wrap generated template content in AgentKit managed block markers:
@@ -140,7 +149,7 @@ Generated content
140
149
 
141
150
  Interactive personalization only applies during `agentkit init` when files are created or overwritten. It does not write a config file, and `agentkit update` does not reapply personalized values.
142
151
 
143
- `agentkit.config.json` can set `preset`, `templateSet`, `aiTools`, and `personalization` defaults. `templateSet` may be `minimal`, `standard`, or `full`; `aiTools` may include `codex`, `cursor`, `claude`, and `copilot`. `agentkit update` only uses config `preset` and continues to update all managed bundled templates.
152
+ `agentkit.config.json` can set `preset`, `templateSet`, `designSystem`, `aiTools`, and `personalization` defaults. `templateSet` may be `minimal`, `standard`, or `full`; `designSystem` selects which bundled design-system variant fills `DESIGN-SYSTEM.md` (currently `linear`); `aiTools` may include `codex`, `cursor`, `claude`, and `copilot`. `agentkit update` uses config `preset` and `designSystem` and continues to update all managed bundled templates.
144
153
 
145
154
  ## Presets
146
155
 
@@ -155,10 +164,11 @@ Presets add stack-specific guidance without scaffolding framework app files.
155
164
  ## CLI Reference
156
165
 
157
166
  ```text
158
- agentkit init [target] [--force] [--dry-run] [--interactive] [--yes] [--write-config] [--preset <name>]
159
- agentkit update [target] [--dry-run] [--preset <name>]
167
+ agentkit init [target] [--force] [--dry-run] [--interactive] [--yes] [--write-config] [--preset <name>] [--design-system <name>]
168
+ agentkit update [target] [--dry-run] [--preset <name>] [--design-system <name>]
160
169
  agentkit --list
161
170
  agentkit --list-presets
171
+ agentkit --list-design-systems
162
172
  agentkit --help
163
173
  agentkit --version
164
174
  ```
@@ -171,12 +181,14 @@ Options:
171
181
  - `-y, --yes`: accept defaults without prompts
172
182
  - `--write-config`: write resolved install defaults to `agentkit.config.json`
173
183
  - `--preset <name>`: install stack-specific guidance (`next`, `sveltekit`, `express`, `convex`, `fullstack`)
184
+ - `--design-system <name>`: design system variant for `DESIGN-SYSTEM.md` (`linear`)
174
185
  - `--list`: list bundled template files
175
186
  - `--list-presets`: list available presets
187
+ - `--list-design-systems`: list available design systems
176
188
  - `-h, --help`: show help
177
189
  - `-v, --version`: show package version
178
190
 
179
- For `agentkit update`, `--preset <name>` refreshes preset-specific managed content, including `STACK.md`.
191
+ For `agentkit update`, `--preset <name>` refreshes preset-specific managed content, including `STACK.md`. `--design-system <name>` refreshes the managed `DESIGN-SYSTEM.md` body from the matching bundled variant.
180
192
 
181
193
  ## Local Development
182
194
 
package/dist/cli.js CHANGED
@@ -14,8 +14,12 @@ const validPresets = ["next", "sveltekit", "express", "convex", "fullstack"];
14
14
  const validProjectTypes = ["generic", ...validPresets];
15
15
  const validAiTools = ["codex", "cursor", "claude", "copilot"];
16
16
  const validTemplateSets = ["minimal", "standard", "full"];
17
+ const validDesignSystems = ["linear"];
18
+ const designSystemLabels = {
19
+ linear: "Linear-inspired",
20
+ };
17
21
  const configFileName = "agentkit.config.json";
18
- const configKeys = ["preset", "templateSet", "aiTools", "personalization"];
22
+ const configKeys = ["preset", "templateSet", "aiTools", "designSystem", "personalization"];
19
23
  const personalizationKeys = [
20
24
  "projectName",
21
25
  "projectDescription",
@@ -107,23 +111,35 @@ async function readPackageVersion() {
107
111
  const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
108
112
  return packageJson.version ?? "0.0.0";
109
113
  }
110
- async function getTemplateFiles(dir = templatesDir, base = templatesDir) {
114
+ async function collectInstallableTemplatePaths(dir, base) {
111
115
  const dirStat = await stat(dir);
112
116
  if (!dirStat.isDirectory()) {
113
117
  throw new Error(`Bundled templates directory not found: ${templatesDir}`);
114
118
  }
115
119
  const entries = await readdir(dir, { withFileTypes: true });
116
- const files = await Promise.all(entries.map(async (entry) => {
120
+ const discovered = [];
121
+ for (const entry of entries) {
117
122
  const absolutePath = path.join(dir, entry.name);
118
123
  if (entry.isDirectory()) {
119
- return getTemplateFiles(absolutePath, base);
124
+ if (dir === templatesDir && entry.name === "design-systems") {
125
+ continue;
126
+ }
127
+ discovered.push(...(await collectInstallableTemplatePaths(absolutePath, base)));
128
+ continue;
120
129
  }
121
130
  if (!entry.isFile()) {
122
- return [];
131
+ continue;
123
132
  }
124
- return [path.relative(base, absolutePath).split(path.sep).join("/")];
125
- }));
126
- return files.flat().sort();
133
+ discovered.push(path.relative(base, absolutePath).split(path.sep).join("/"));
134
+ }
135
+ return discovered;
136
+ }
137
+ async function getTemplateFiles() {
138
+ const discovered = await collectInstallableTemplatePaths(templatesDir, templatesDir);
139
+ const withDesignSystem = discovered.includes("DESIGN-SYSTEM.md")
140
+ ? discovered
141
+ : [...discovered, "DESIGN-SYSTEM.md"];
142
+ return withDesignSystem.sort();
127
143
  }
128
144
  function isPresetName(value) {
129
145
  return validPresets.includes(value);
@@ -137,12 +153,32 @@ function isAiToolName(value) {
137
153
  function isTemplateSetName(value) {
138
154
  return validTemplateSets.includes(value);
139
155
  }
156
+ function isDesignSystemName(value) {
157
+ return validDesignSystems.includes(value);
158
+ }
140
159
  function formatPresetList() {
141
160
  return validPresets.join(", ");
142
161
  }
143
162
  function formatTemplateSetList() {
144
163
  return validTemplateSets.join(", ");
145
164
  }
165
+ function formatDesignSystemList() {
166
+ return validDesignSystems.join(", ");
167
+ }
168
+ function resolveDesignSystem(name) {
169
+ const normalized = name.toLowerCase();
170
+ if (!isDesignSystemName(normalized)) {
171
+ throw new Error(`Unknown design system "${name}". Valid design systems: ${formatDesignSystemList()}.`);
172
+ }
173
+ return normalized;
174
+ }
175
+ function effectiveDesignSystem(name) {
176
+ const trimmed = name?.trim();
177
+ if (!trimmed) {
178
+ return "linear";
179
+ }
180
+ return resolveDesignSystem(trimmed);
181
+ }
146
182
  function resolvePreset(preset) {
147
183
  if (!preset) {
148
184
  return undefined;
@@ -202,6 +238,12 @@ function parseConfig(rawConfig, configPath) {
202
238
  return aiTool;
203
239
  });
204
240
  }
241
+ if (rawConfig.designSystem !== undefined) {
242
+ if (typeof rawConfig.designSystem !== "string") {
243
+ throw new Error(`${configFileName} designSystem must be a string.`);
244
+ }
245
+ config.designSystem = resolveDesignSystem(rawConfig.designSystem);
246
+ }
205
247
  if (rawConfig.personalization !== undefined) {
206
248
  assertPlainObject(rawConfig.personalization, `${configFileName} personalization`);
207
249
  assertKnownKeys(rawConfig.personalization, personalizationKeys, `${configFileName} personalization`);
@@ -252,6 +294,7 @@ async function applyInitConfig(options, config) {
252
294
  return;
253
295
  }
254
296
  options.preset ??= config.preset;
297
+ options.designSystem ??= config.designSystem;
255
298
  options.personalization ??= config.personalization;
256
299
  options.templateSet ??= config.templateSet;
257
300
  options.aiTools ??= config.aiTools;
@@ -261,6 +304,7 @@ function applyUpdateConfig(options, config) {
261
304
  return;
262
305
  }
263
306
  options.preset ??= config.preset;
307
+ options.designSystem ??= config.designSystem;
264
308
  }
265
309
  export function resolveProjectPreset(projectType) {
266
310
  if (!projectType) {
@@ -328,6 +372,7 @@ function getResolvedConfig(options) {
328
372
  const config = {
329
373
  templateSet: options.templateSet ?? "full",
330
374
  aiTools: options.aiTools ?? [],
375
+ designSystem: effectiveDesignSystem(options.designSystem),
331
376
  };
332
377
  const preset = resolvePreset(options.preset);
333
378
  if (preset) {
@@ -532,8 +577,8 @@ async function promptForPersonalization(defaults) {
532
577
  stackSummary,
533
578
  };
534
579
  }
535
- async function buildInitTemplateContent(file, preset, personalization) {
536
- const content = await buildTemplateContent(file, preset);
580
+ async function buildInitTemplateContent(file, preset, personalization, designSystem) {
581
+ const content = await buildTemplateContent(file, preset, designSystem);
537
582
  return personalizeTemplateContent(file, content, personalization);
538
583
  }
539
584
  async function installTemplates(targetArg, options) {
@@ -544,6 +589,7 @@ async function installTemplates(targetArg, options) {
544
589
  ? getSelectedTemplateFiles(options.templateSet ?? "full", options.aiTools ?? [], allTemplateFiles)
545
590
  : allTemplateFiles);
546
591
  const preset = resolvePreset(options.preset);
592
+ const designSystem = effectiveDesignSystem(options.designSystem);
547
593
  const created = [];
548
594
  const skipped = [];
549
595
  if (!options.dryRun) {
@@ -572,14 +618,8 @@ async function installTemplates(targetArg, options) {
572
618
  created.push(file);
573
619
  if (!options.dryRun) {
574
620
  await mkdir(path.dirname(destination), { recursive: true });
575
- if (preset && file === "AGENTS.md") {
576
- const content = await buildInitTemplateContent(file, preset, options.personalization);
577
- await writeFile(destination, wrapManagedBlock(file, content));
578
- }
579
- else {
580
- const content = await buildInitTemplateContent(file, preset, options.personalization);
581
- await writeFile(destination, wrapManagedBlock(file, content));
582
- }
621
+ const content = await buildInitTemplateContent(file, preset, options.personalization, designSystem);
622
+ await writeFile(destination, wrapManagedBlock(file, content));
583
623
  }
584
624
  }
585
625
  if (preset) {
@@ -614,13 +654,18 @@ function replaceManagedBlock(file, existingContent, nextContent) {
614
654
  const replacement = wrapManagedBlock(file, nextContent).trimEnd();
615
655
  return `${existingContent.slice(0, startIndex)}${replacement}${existingContent.slice(afterEndIndex)}`;
616
656
  }
617
- async function buildTemplateContent(file, preset) {
657
+ async function buildTemplateContent(file, preset, designSystem) {
618
658
  if (file === "STACK.md") {
619
659
  if (!preset) {
620
660
  throw new Error("STACK.md requires a preset.");
621
661
  }
622
662
  return getStackGuidance(preset);
623
663
  }
664
+ if (file === "DESIGN-SYSTEM.md") {
665
+ const source = path.join(templatesDir, "design-systems", `${designSystem}.md`);
666
+ const content = await readFile(source, "utf8");
667
+ return addStackReference(file, content, preset);
668
+ }
624
669
  const source = path.join(templatesDir, file);
625
670
  const content = await readFile(source, "utf8");
626
671
  return addStackReference(file, content, preset);
@@ -628,6 +673,7 @@ async function buildTemplateContent(file, preset) {
628
673
  async function updateTemplates(targetArg, options) {
629
674
  const targetDir = path.resolve(process.cwd(), targetArg || ".");
630
675
  const preset = resolvePreset(options.preset);
676
+ const designSystem = effectiveDesignSystem(options.designSystem);
631
677
  const files = preset ? [...(await getTemplateFiles()), "STACK.md"] : await getTemplateFiles();
632
678
  const created = [];
633
679
  const updated = [];
@@ -636,7 +682,7 @@ async function updateTemplates(targetArg, options) {
636
682
  const unchanged = [];
637
683
  for (const file of files) {
638
684
  const destination = path.join(targetDir, file);
639
- const nextContent = await buildTemplateContent(file, preset);
685
+ const nextContent = await buildTemplateContent(file, preset, designSystem);
640
686
  const destinationExists = await exists(destination);
641
687
  if (!destinationExists) {
642
688
  created.push(file);
@@ -777,6 +823,17 @@ async function resolveInteractiveTarget(target, options) {
777
823
  options.templateSet = templateSetResponse;
778
824
  options.aiTools = aiToolResponse;
779
825
  options.files = getSelectedTemplateFiles(templateSetResponse, aiToolResponse, templateFiles);
826
+ if (templateSetResponse === "standard" || templateSetResponse === "full") {
827
+ const designSystemResponse = await select({
828
+ message: "Which design system guidance?",
829
+ initialValue: effectiveDesignSystem(options.designSystem),
830
+ options: validDesignSystems.map((value) => ({ label: designSystemLabels[value], value })),
831
+ });
832
+ if (isCancel(designSystemResponse)) {
833
+ process.exit(130);
834
+ }
835
+ options.designSystem = designSystemResponse;
836
+ }
780
837
  if (!options.force) {
781
838
  const preset = resolvePreset(options.preset);
782
839
  const installFiles = preset ? [...options.files, "STACK.md"] : options.files;
@@ -819,6 +876,12 @@ async function main() {
819
876
  }
820
877
  return;
821
878
  }
879
+ if (process.argv.slice(2).includes("--list-design-systems")) {
880
+ for (const designSystem of validDesignSystems) {
881
+ console.log(designSystem);
882
+ }
883
+ return;
884
+ }
822
885
  const program = new Command();
823
886
  program
824
887
  .name("agentkit")
@@ -826,6 +889,7 @@ async function main() {
826
889
  .version(await readPackageVersion(), "-v, --version")
827
890
  .option("--list", "list bundled template files")
828
891
  .option("--list-presets", "list available presets")
892
+ .option("--list-design-systems", "list available design systems")
829
893
  .addHelpText("after", `
830
894
 
831
895
  Examples:
@@ -834,6 +898,7 @@ Examples:
834
898
  agentkit init --preset next
835
899
  agentkit init ./my-project --yes --dry-run
836
900
  agentkit --list-presets
901
+ agentkit --list-design-systems
837
902
  agentkit --list`);
838
903
  program
839
904
  .command("init")
@@ -845,6 +910,7 @@ Examples:
845
910
  .option("-y, --yes", "accept defaults for non-interactive runs")
846
911
  .option("--write-config", "write resolved install defaults to agentkit.config.json")
847
912
  .option("--preset <name>", `install stack-specific guidance (${formatPresetList()})`)
913
+ .option("--design-system <name>", `design system guidance for DESIGN-SYSTEM.md (${formatDesignSystemList()})`)
848
914
  .action(async (target, options) => {
849
915
  await applyInitConfig(options, await loadConfigForTarget(target));
850
916
  const resolvedTarget = await resolveInteractiveTarget(target, options);
@@ -857,6 +923,7 @@ Examples:
857
923
  .argument("[target]", "target project directory", ".")
858
924
  .option("--dry-run", "print planned changes without writing files")
859
925
  .option("--preset <name>", `update stack-specific guidance (${formatPresetList()})`)
926
+ .option("--design-system <name>", `design system guidance for DESIGN-SYSTEM.md (${formatDesignSystemList()})`)
860
927
  .action(async (target, options) => {
861
928
  applyUpdateConfig(options, await loadConfigForTarget(target));
862
929
  const result = await updateTemplates(target, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thomas-agentkit",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Install AI-agent-ready development templates into a project.",
5
5
  "license": "MIT",
6
6
  "repository": {