thomas-agentkit 0.4.0 → 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 +49 -3
- package/dist/cli.js +278 -25
- package/package.json +1 -1
- /package/templates/{DESIGN-SYSTEM.md → design-systems/linear.md} +0 -0
package/README.md
CHANGED
|
@@ -66,6 +66,38 @@ Install stack-specific agent guidance:
|
|
|
66
66
|
npx thomas-agentkit init --preset next
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
Standardize install defaults with `agentkit.config.json`:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"preset": "next",
|
|
74
|
+
"templateSet": "standard",
|
|
75
|
+
"designSystem": "linear",
|
|
76
|
+
"aiTools": ["codex", "cursor", "claude"],
|
|
77
|
+
"personalization": {
|
|
78
|
+
"projectName": "Acme CRM",
|
|
79
|
+
"projectDescription": "a customer operations dashboard",
|
|
80
|
+
"issueTracker": "Linear",
|
|
81
|
+
"designSystemPath": "docs/design-system.md",
|
|
82
|
+
"briefsPath": "docs/briefs",
|
|
83
|
+
"testCommand": "pnpm test",
|
|
84
|
+
"lintCommand": "pnpm lint",
|
|
85
|
+
"buildCommand": "pnpm build",
|
|
86
|
+
"stackSummary": "Next.js, TypeScript, PostgreSQL"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
AgentKit reads `agentkit.config.json` from the target directory first. When installing into another target that does not have a config file, it falls back to the current working directory. Config values are defaults: explicit CLI flags override them.
|
|
92
|
+
|
|
93
|
+
Create a config file from resolved install choices:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx thomas-agentkit init --write-config
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`--write-config` writes `agentkit.config.json` in the target directory. Existing config files are skipped by default; use `--force` to overwrite one intentionally.
|
|
100
|
+
|
|
69
101
|
List bundled templates:
|
|
70
102
|
|
|
71
103
|
```bash
|
|
@@ -78,6 +110,12 @@ List available presets:
|
|
|
78
110
|
npx thomas-agentkit --list-presets
|
|
79
111
|
```
|
|
80
112
|
|
|
113
|
+
List bundled design systems (source variants for `DESIGN-SYSTEM.md`):
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx thomas-agentkit --list-design-systems
|
|
117
|
+
```
|
|
118
|
+
|
|
81
119
|
## Installed Files
|
|
82
120
|
|
|
83
121
|
AgentKit copies these bundled files into the target project:
|
|
@@ -95,6 +133,8 @@ AgentKit copies these bundled files into the target project:
|
|
|
95
133
|
|
|
96
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.
|
|
97
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
|
+
|
|
98
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.
|
|
99
139
|
|
|
100
140
|
New installs wrap generated template content in AgentKit managed block markers:
|
|
@@ -109,6 +149,8 @@ Generated content
|
|
|
109
149
|
|
|
110
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.
|
|
111
151
|
|
|
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.
|
|
153
|
+
|
|
112
154
|
## Presets
|
|
113
155
|
|
|
114
156
|
Presets add stack-specific guidance without scaffolding framework app files.
|
|
@@ -122,10 +164,11 @@ Presets add stack-specific guidance without scaffolding framework app files.
|
|
|
122
164
|
## CLI Reference
|
|
123
165
|
|
|
124
166
|
```text
|
|
125
|
-
agentkit init [target] [--force] [--dry-run] [--interactive] [--yes] [--preset <name>]
|
|
126
|
-
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>]
|
|
127
169
|
agentkit --list
|
|
128
170
|
agentkit --list-presets
|
|
171
|
+
agentkit --list-design-systems
|
|
129
172
|
agentkit --help
|
|
130
173
|
agentkit --version
|
|
131
174
|
```
|
|
@@ -136,13 +179,16 @@ Options:
|
|
|
136
179
|
- `--dry-run`: print planned changes without writing files
|
|
137
180
|
- `-i, --interactive`: explicitly prompt for install options
|
|
138
181
|
- `-y, --yes`: accept defaults without prompts
|
|
182
|
+
- `--write-config`: write resolved install defaults to `agentkit.config.json`
|
|
139
183
|
- `--preset <name>`: install stack-specific guidance (`next`, `sveltekit`, `express`, `convex`, `fullstack`)
|
|
184
|
+
- `--design-system <name>`: design system variant for `DESIGN-SYSTEM.md` (`linear`)
|
|
140
185
|
- `--list`: list bundled template files
|
|
141
186
|
- `--list-presets`: list available presets
|
|
187
|
+
- `--list-design-systems`: list available design systems
|
|
142
188
|
- `-h, --help`: show help
|
|
143
189
|
- `-v, --version`: show package version
|
|
144
190
|
|
|
145
|
-
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.
|
|
146
192
|
|
|
147
193
|
## Local Development
|
|
148
194
|
|
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,24 @@ const packageJsonPath = path.join(packageRoot, "package.json");
|
|
|
13
13
|
const validPresets = ["next", "sveltekit", "express", "convex", "fullstack"];
|
|
14
14
|
const validProjectTypes = ["generic", ...validPresets];
|
|
15
15
|
const validAiTools = ["codex", "cursor", "claude", "copilot"];
|
|
16
|
+
const validTemplateSets = ["minimal", "standard", "full"];
|
|
17
|
+
const validDesignSystems = ["linear"];
|
|
18
|
+
const designSystemLabels = {
|
|
19
|
+
linear: "Linear-inspired",
|
|
20
|
+
};
|
|
21
|
+
const configFileName = "agentkit.config.json";
|
|
22
|
+
const configKeys = ["preset", "templateSet", "aiTools", "designSystem", "personalization"];
|
|
23
|
+
const personalizationKeys = [
|
|
24
|
+
"projectName",
|
|
25
|
+
"projectDescription",
|
|
26
|
+
"issueTracker",
|
|
27
|
+
"designSystemPath",
|
|
28
|
+
"briefsPath",
|
|
29
|
+
"testCommand",
|
|
30
|
+
"lintCommand",
|
|
31
|
+
"buildCommand",
|
|
32
|
+
"stackSummary",
|
|
33
|
+
];
|
|
16
34
|
const presetLabels = {
|
|
17
35
|
next: "Next.js",
|
|
18
36
|
sveltekit: "SvelteKit",
|
|
@@ -93,23 +111,35 @@ async function readPackageVersion() {
|
|
|
93
111
|
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
94
112
|
return packageJson.version ?? "0.0.0";
|
|
95
113
|
}
|
|
96
|
-
async function
|
|
114
|
+
async function collectInstallableTemplatePaths(dir, base) {
|
|
97
115
|
const dirStat = await stat(dir);
|
|
98
116
|
if (!dirStat.isDirectory()) {
|
|
99
117
|
throw new Error(`Bundled templates directory not found: ${templatesDir}`);
|
|
100
118
|
}
|
|
101
119
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
102
|
-
const
|
|
120
|
+
const discovered = [];
|
|
121
|
+
for (const entry of entries) {
|
|
103
122
|
const absolutePath = path.join(dir, entry.name);
|
|
104
123
|
if (entry.isDirectory()) {
|
|
105
|
-
|
|
124
|
+
if (dir === templatesDir && entry.name === "design-systems") {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
discovered.push(...(await collectInstallableTemplatePaths(absolutePath, base)));
|
|
128
|
+
continue;
|
|
106
129
|
}
|
|
107
130
|
if (!entry.isFile()) {
|
|
108
|
-
|
|
131
|
+
continue;
|
|
109
132
|
}
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
return
|
|
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();
|
|
113
143
|
}
|
|
114
144
|
function isPresetName(value) {
|
|
115
145
|
return validPresets.includes(value);
|
|
@@ -120,9 +150,35 @@ function isProjectTypeName(value) {
|
|
|
120
150
|
function isAiToolName(value) {
|
|
121
151
|
return validAiTools.includes(value);
|
|
122
152
|
}
|
|
153
|
+
function isTemplateSetName(value) {
|
|
154
|
+
return validTemplateSets.includes(value);
|
|
155
|
+
}
|
|
156
|
+
function isDesignSystemName(value) {
|
|
157
|
+
return validDesignSystems.includes(value);
|
|
158
|
+
}
|
|
123
159
|
function formatPresetList() {
|
|
124
160
|
return validPresets.join(", ");
|
|
125
161
|
}
|
|
162
|
+
function formatTemplateSetList() {
|
|
163
|
+
return validTemplateSets.join(", ");
|
|
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
|
+
}
|
|
126
182
|
function resolvePreset(preset) {
|
|
127
183
|
if (!preset) {
|
|
128
184
|
return undefined;
|
|
@@ -133,6 +189,123 @@ function resolvePreset(preset) {
|
|
|
133
189
|
}
|
|
134
190
|
return normalizedPreset;
|
|
135
191
|
}
|
|
192
|
+
function resolveTemplateSet(templateSet) {
|
|
193
|
+
if (!templateSet) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
const normalizedTemplateSet = templateSet.toLowerCase();
|
|
197
|
+
if (!isTemplateSetName(normalizedTemplateSet)) {
|
|
198
|
+
throw new Error(`Unknown template set "${templateSet}". Valid template sets: ${formatTemplateSetList()}.`);
|
|
199
|
+
}
|
|
200
|
+
return normalizedTemplateSet;
|
|
201
|
+
}
|
|
202
|
+
function assertPlainObject(value, name) {
|
|
203
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
204
|
+
throw new Error(`${name} must be an object.`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function assertKnownKeys(value, validKeys, name) {
|
|
208
|
+
for (const key of Object.keys(value)) {
|
|
209
|
+
if (!validKeys.includes(key)) {
|
|
210
|
+
throw new Error(`Unknown ${name} key "${key}". Valid keys: ${validKeys.join(", ")}.`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function parseConfig(rawConfig, configPath) {
|
|
215
|
+
assertPlainObject(rawConfig, configFileName);
|
|
216
|
+
assertKnownKeys(rawConfig, configKeys, configFileName);
|
|
217
|
+
const config = {};
|
|
218
|
+
if (rawConfig.preset !== undefined) {
|
|
219
|
+
if (typeof rawConfig.preset !== "string") {
|
|
220
|
+
throw new Error(`${configFileName} preset must be a string.`);
|
|
221
|
+
}
|
|
222
|
+
config.preset = resolvePreset(rawConfig.preset);
|
|
223
|
+
}
|
|
224
|
+
if (rawConfig.templateSet !== undefined) {
|
|
225
|
+
if (typeof rawConfig.templateSet !== "string") {
|
|
226
|
+
throw new Error(`${configFileName} templateSet must be a string.`);
|
|
227
|
+
}
|
|
228
|
+
config.templateSet = resolveTemplateSet(rawConfig.templateSet);
|
|
229
|
+
}
|
|
230
|
+
if (rawConfig.aiTools !== undefined) {
|
|
231
|
+
if (!Array.isArray(rawConfig.aiTools)) {
|
|
232
|
+
throw new Error(`${configFileName} aiTools must be an array.`);
|
|
233
|
+
}
|
|
234
|
+
config.aiTools = rawConfig.aiTools.map((aiTool) => {
|
|
235
|
+
if (typeof aiTool !== "string" || !isAiToolName(aiTool)) {
|
|
236
|
+
throw new Error(`Unknown AI tool "${String(aiTool)}". Valid AI tools: ${validAiTools.join(", ")}.`);
|
|
237
|
+
}
|
|
238
|
+
return aiTool;
|
|
239
|
+
});
|
|
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
|
+
}
|
|
247
|
+
if (rawConfig.personalization !== undefined) {
|
|
248
|
+
assertPlainObject(rawConfig.personalization, `${configFileName} personalization`);
|
|
249
|
+
assertKnownKeys(rawConfig.personalization, personalizationKeys, `${configFileName} personalization`);
|
|
250
|
+
config.personalization = {};
|
|
251
|
+
for (const [key, value] of Object.entries(rawConfig.personalization)) {
|
|
252
|
+
if (typeof value !== "string") {
|
|
253
|
+
throw new Error(`${configFileName} personalization.${key} must be a string.`);
|
|
254
|
+
}
|
|
255
|
+
config.personalization[key] = value;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (config.preset === undefined && rawConfig.preset !== undefined) {
|
|
259
|
+
throw new Error(`Invalid preset in ${configPath}.`);
|
|
260
|
+
}
|
|
261
|
+
return config;
|
|
262
|
+
}
|
|
263
|
+
async function readConfig(configPath) {
|
|
264
|
+
let rawContent;
|
|
265
|
+
try {
|
|
266
|
+
rawContent = await readFile(configPath, "utf8");
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
throw new Error(`Unable to read ${configPath}.`);
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
return parseConfig(JSON.parse(rawContent), configPath);
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
276
|
+
throw new Error(`Invalid ${configPath}: ${message}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function loadConfigForTarget(targetArg) {
|
|
280
|
+
const cwd = process.cwd();
|
|
281
|
+
const targetDir = path.resolve(cwd, targetArg || ".");
|
|
282
|
+
const targetConfigPath = path.join(targetDir, configFileName);
|
|
283
|
+
if (await exists(targetConfigPath)) {
|
|
284
|
+
return readConfig(targetConfigPath);
|
|
285
|
+
}
|
|
286
|
+
const cwdConfigPath = path.join(cwd, configFileName);
|
|
287
|
+
if (targetDir !== cwd && (await exists(cwdConfigPath))) {
|
|
288
|
+
return readConfig(cwdConfigPath);
|
|
289
|
+
}
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
async function applyInitConfig(options, config) {
|
|
293
|
+
if (!config) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
options.preset ??= config.preset;
|
|
297
|
+
options.designSystem ??= config.designSystem;
|
|
298
|
+
options.personalization ??= config.personalization;
|
|
299
|
+
options.templateSet ??= config.templateSet;
|
|
300
|
+
options.aiTools ??= config.aiTools;
|
|
301
|
+
}
|
|
302
|
+
function applyUpdateConfig(options, config) {
|
|
303
|
+
if (!config) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
options.preset ??= config.preset;
|
|
307
|
+
options.designSystem ??= config.designSystem;
|
|
308
|
+
}
|
|
136
309
|
export function resolveProjectPreset(projectType) {
|
|
137
310
|
if (!projectType) {
|
|
138
311
|
return undefined;
|
|
@@ -195,6 +368,33 @@ function cleanPersonalizationValue(value) {
|
|
|
195
368
|
const trimmed = value?.trim();
|
|
196
369
|
return trimmed ? trimmed : undefined;
|
|
197
370
|
}
|
|
371
|
+
function getResolvedConfig(options) {
|
|
372
|
+
const config = {
|
|
373
|
+
templateSet: options.templateSet ?? "full",
|
|
374
|
+
aiTools: options.aiTools ?? [],
|
|
375
|
+
designSystem: effectiveDesignSystem(options.designSystem),
|
|
376
|
+
};
|
|
377
|
+
const preset = resolvePreset(options.preset);
|
|
378
|
+
if (preset) {
|
|
379
|
+
config.preset = preset;
|
|
380
|
+
}
|
|
381
|
+
if (options.personalization) {
|
|
382
|
+
const personalization = {};
|
|
383
|
+
for (const key of personalizationKeys) {
|
|
384
|
+
const value = cleanPersonalizationValue(options.personalization[key]);
|
|
385
|
+
if (value) {
|
|
386
|
+
personalization[key] = value;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (Object.keys(personalization).length > 0) {
|
|
390
|
+
config.personalization = personalization;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return config;
|
|
394
|
+
}
|
|
395
|
+
function serializeConfig(config) {
|
|
396
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
397
|
+
}
|
|
198
398
|
function replaceIfProvided(content, placeholder, value) {
|
|
199
399
|
const replacement = cleanPersonalizationValue(value);
|
|
200
400
|
return replacement ? content.replaceAll(placeholder, replacement) : content;
|
|
@@ -282,10 +482,10 @@ export function personalizeTemplateContent(file, content, values) {
|
|
|
282
482
|
}
|
|
283
483
|
return personalized;
|
|
284
484
|
}
|
|
285
|
-
async function promptForPersonalization() {
|
|
485
|
+
async function promptForPersonalization(defaults) {
|
|
286
486
|
const shouldPersonalize = await confirm({
|
|
287
487
|
message: "Personalize template placeholders?",
|
|
288
|
-
initialValue:
|
|
488
|
+
initialValue: Boolean(defaults),
|
|
289
489
|
});
|
|
290
490
|
if (isCancel(shouldPersonalize)) {
|
|
291
491
|
process.exit(130);
|
|
@@ -296,6 +496,7 @@ async function promptForPersonalization() {
|
|
|
296
496
|
const projectName = await text({
|
|
297
497
|
message: "Project name",
|
|
298
498
|
placeholder: "[Project Name]",
|
|
499
|
+
defaultValue: defaults?.projectName,
|
|
299
500
|
});
|
|
300
501
|
if (isCancel(projectName)) {
|
|
301
502
|
process.exit(130);
|
|
@@ -303,6 +504,7 @@ async function promptForPersonalization() {
|
|
|
303
504
|
const projectDescription = await text({
|
|
304
505
|
message: "Short project description",
|
|
305
506
|
placeholder: "[short project description]",
|
|
507
|
+
defaultValue: defaults?.projectDescription,
|
|
306
508
|
});
|
|
307
509
|
if (isCancel(projectDescription)) {
|
|
308
510
|
process.exit(130);
|
|
@@ -310,6 +512,7 @@ async function promptForPersonalization() {
|
|
|
310
512
|
const issueTracker = await text({
|
|
311
513
|
message: "Issue tracker name",
|
|
312
514
|
placeholder: "Linear or GitHub Issues",
|
|
515
|
+
defaultValue: defaults?.issueTracker,
|
|
313
516
|
});
|
|
314
517
|
if (isCancel(issueTracker)) {
|
|
315
518
|
process.exit(130);
|
|
@@ -317,6 +520,7 @@ async function promptForPersonalization() {
|
|
|
317
520
|
const designSystemPath = await text({
|
|
318
521
|
message: "Design system path",
|
|
319
522
|
placeholder: "docs/design-system.md",
|
|
523
|
+
defaultValue: defaults?.designSystemPath,
|
|
320
524
|
});
|
|
321
525
|
if (isCancel(designSystemPath)) {
|
|
322
526
|
process.exit(130);
|
|
@@ -324,6 +528,7 @@ async function promptForPersonalization() {
|
|
|
324
528
|
const briefsPath = await text({
|
|
325
529
|
message: "Briefs path",
|
|
326
530
|
placeholder: "docs/briefs",
|
|
531
|
+
defaultValue: defaults?.briefsPath,
|
|
327
532
|
});
|
|
328
533
|
if (isCancel(briefsPath)) {
|
|
329
534
|
process.exit(130);
|
|
@@ -331,6 +536,7 @@ async function promptForPersonalization() {
|
|
|
331
536
|
const testCommand = await text({
|
|
332
537
|
message: "Test command",
|
|
333
538
|
placeholder: "npm test",
|
|
539
|
+
defaultValue: defaults?.testCommand,
|
|
334
540
|
});
|
|
335
541
|
if (isCancel(testCommand)) {
|
|
336
542
|
process.exit(130);
|
|
@@ -338,6 +544,7 @@ async function promptForPersonalization() {
|
|
|
338
544
|
const lintCommand = await text({
|
|
339
545
|
message: "Lint command",
|
|
340
546
|
placeholder: "npm run lint",
|
|
547
|
+
defaultValue: defaults?.lintCommand,
|
|
341
548
|
});
|
|
342
549
|
if (isCancel(lintCommand)) {
|
|
343
550
|
process.exit(130);
|
|
@@ -345,6 +552,7 @@ async function promptForPersonalization() {
|
|
|
345
552
|
const buildCommand = await text({
|
|
346
553
|
message: "Build/check command",
|
|
347
554
|
placeholder: "npm run build",
|
|
555
|
+
defaultValue: defaults?.buildCommand,
|
|
348
556
|
});
|
|
349
557
|
if (isCancel(buildCommand)) {
|
|
350
558
|
process.exit(130);
|
|
@@ -352,6 +560,7 @@ async function promptForPersonalization() {
|
|
|
352
560
|
const stackSummary = await text({
|
|
353
561
|
message: "Stack summary",
|
|
354
562
|
placeholder: "Next.js, TypeScript, Tailwind CSS, Vitest",
|
|
563
|
+
defaultValue: defaults?.stackSummary,
|
|
355
564
|
});
|
|
356
565
|
if (isCancel(stackSummary)) {
|
|
357
566
|
process.exit(130);
|
|
@@ -368,19 +577,37 @@ async function promptForPersonalization() {
|
|
|
368
577
|
stackSummary,
|
|
369
578
|
};
|
|
370
579
|
}
|
|
371
|
-
async function buildInitTemplateContent(file, preset, personalization) {
|
|
372
|
-
const content = await buildTemplateContent(file, preset);
|
|
580
|
+
async function buildInitTemplateContent(file, preset, personalization, designSystem) {
|
|
581
|
+
const content = await buildTemplateContent(file, preset, designSystem);
|
|
373
582
|
return personalizeTemplateContent(file, content, personalization);
|
|
374
583
|
}
|
|
375
584
|
async function installTemplates(targetArg, options) {
|
|
376
585
|
const targetDir = path.resolve(process.cwd(), targetArg || ".");
|
|
377
|
-
const
|
|
586
|
+
const allTemplateFiles = await getTemplateFiles();
|
|
587
|
+
const files = options.files ??
|
|
588
|
+
(options.templateSet || options.aiTools
|
|
589
|
+
? getSelectedTemplateFiles(options.templateSet ?? "full", options.aiTools ?? [], allTemplateFiles)
|
|
590
|
+
: allTemplateFiles);
|
|
378
591
|
const preset = resolvePreset(options.preset);
|
|
592
|
+
const designSystem = effectiveDesignSystem(options.designSystem);
|
|
379
593
|
const created = [];
|
|
380
594
|
const skipped = [];
|
|
381
595
|
if (!options.dryRun) {
|
|
382
596
|
await mkdir(targetDir, { recursive: true });
|
|
383
597
|
}
|
|
598
|
+
if (options.writeConfig) {
|
|
599
|
+
const destination = path.join(targetDir, configFileName);
|
|
600
|
+
const destinationExists = await exists(destination);
|
|
601
|
+
if (destinationExists && !options.force) {
|
|
602
|
+
skipped.push(configFileName);
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
created.push(configFileName);
|
|
606
|
+
if (!options.dryRun) {
|
|
607
|
+
await writeFile(destination, serializeConfig(getResolvedConfig(options)));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
384
611
|
for (const file of files) {
|
|
385
612
|
const destination = path.join(targetDir, file);
|
|
386
613
|
const destinationExists = await exists(destination);
|
|
@@ -391,14 +618,8 @@ async function installTemplates(targetArg, options) {
|
|
|
391
618
|
created.push(file);
|
|
392
619
|
if (!options.dryRun) {
|
|
393
620
|
await mkdir(path.dirname(destination), { recursive: true });
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
await writeFile(destination, wrapManagedBlock(file, content));
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
const content = await buildInitTemplateContent(file, preset, options.personalization);
|
|
400
|
-
await writeFile(destination, wrapManagedBlock(file, content));
|
|
401
|
-
}
|
|
621
|
+
const content = await buildInitTemplateContent(file, preset, options.personalization, designSystem);
|
|
622
|
+
await writeFile(destination, wrapManagedBlock(file, content));
|
|
402
623
|
}
|
|
403
624
|
}
|
|
404
625
|
if (preset) {
|
|
@@ -433,13 +654,18 @@ function replaceManagedBlock(file, existingContent, nextContent) {
|
|
|
433
654
|
const replacement = wrapManagedBlock(file, nextContent).trimEnd();
|
|
434
655
|
return `${existingContent.slice(0, startIndex)}${replacement}${existingContent.slice(afterEndIndex)}`;
|
|
435
656
|
}
|
|
436
|
-
async function buildTemplateContent(file, preset) {
|
|
657
|
+
async function buildTemplateContent(file, preset, designSystem) {
|
|
437
658
|
if (file === "STACK.md") {
|
|
438
659
|
if (!preset) {
|
|
439
660
|
throw new Error("STACK.md requires a preset.");
|
|
440
661
|
}
|
|
441
662
|
return getStackGuidance(preset);
|
|
442
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
|
+
}
|
|
443
669
|
const source = path.join(templatesDir, file);
|
|
444
670
|
const content = await readFile(source, "utf8");
|
|
445
671
|
return addStackReference(file, content, preset);
|
|
@@ -447,6 +673,7 @@ async function buildTemplateContent(file, preset) {
|
|
|
447
673
|
async function updateTemplates(targetArg, options) {
|
|
448
674
|
const targetDir = path.resolve(process.cwd(), targetArg || ".");
|
|
449
675
|
const preset = resolvePreset(options.preset);
|
|
676
|
+
const designSystem = effectiveDesignSystem(options.designSystem);
|
|
450
677
|
const files = preset ? [...(await getTemplateFiles()), "STACK.md"] : await getTemplateFiles();
|
|
451
678
|
const created = [];
|
|
452
679
|
const updated = [];
|
|
@@ -455,7 +682,7 @@ async function updateTemplates(targetArg, options) {
|
|
|
455
682
|
const unchanged = [];
|
|
456
683
|
for (const file of files) {
|
|
457
684
|
const destination = path.join(targetDir, file);
|
|
458
|
-
const nextContent = await buildTemplateContent(file, preset);
|
|
685
|
+
const nextContent = await buildTemplateContent(file, preset, designSystem);
|
|
459
686
|
const destinationExists = await exists(destination);
|
|
460
687
|
if (!destinationExists) {
|
|
461
688
|
created.push(file);
|
|
@@ -569,7 +796,7 @@ async function resolveInteractiveTarget(target, options) {
|
|
|
569
796
|
}
|
|
570
797
|
const aiToolResponse = await multiselect({
|
|
571
798
|
message: "Which AI tools do you use?",
|
|
572
|
-
initialValues: ["codex", "cursor", "claude"],
|
|
799
|
+
initialValues: options.aiTools ?? ["codex", "cursor", "claude"],
|
|
573
800
|
options: [
|
|
574
801
|
{ label: "Codex", value: "codex" },
|
|
575
802
|
{ label: "Cursor", value: "cursor" },
|
|
@@ -582,7 +809,7 @@ async function resolveInteractiveTarget(target, options) {
|
|
|
582
809
|
}
|
|
583
810
|
const templateSetResponse = await select({
|
|
584
811
|
message: "Which template set do you want?",
|
|
585
|
-
initialValue: "standard",
|
|
812
|
+
initialValue: options.templateSet ?? "standard",
|
|
586
813
|
options: [
|
|
587
814
|
{ label: "Minimal", value: "minimal" },
|
|
588
815
|
{ label: "Standard", value: "standard" },
|
|
@@ -593,7 +820,20 @@ async function resolveInteractiveTarget(target, options) {
|
|
|
593
820
|
process.exit(130);
|
|
594
821
|
}
|
|
595
822
|
const templateFiles = await getTemplateFiles();
|
|
823
|
+
options.templateSet = templateSetResponse;
|
|
824
|
+
options.aiTools = aiToolResponse;
|
|
596
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
|
+
}
|
|
597
837
|
if (!options.force) {
|
|
598
838
|
const preset = resolvePreset(options.preset);
|
|
599
839
|
const installFiles = preset ? [...options.files, "STACK.md"] : options.files;
|
|
@@ -619,7 +859,7 @@ async function resolveInteractiveTarget(target, options) {
|
|
|
619
859
|
options.force = conflictResponse === "overwrite";
|
|
620
860
|
}
|
|
621
861
|
}
|
|
622
|
-
options.personalization = await promptForPersonalization();
|
|
862
|
+
options.personalization = await promptForPersonalization(options.personalization);
|
|
623
863
|
return resolvedTarget || ".";
|
|
624
864
|
}
|
|
625
865
|
async function main() {
|
|
@@ -636,6 +876,12 @@ async function main() {
|
|
|
636
876
|
}
|
|
637
877
|
return;
|
|
638
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
|
+
}
|
|
639
885
|
const program = new Command();
|
|
640
886
|
program
|
|
641
887
|
.name("agentkit")
|
|
@@ -643,6 +889,7 @@ async function main() {
|
|
|
643
889
|
.version(await readPackageVersion(), "-v, --version")
|
|
644
890
|
.option("--list", "list bundled template files")
|
|
645
891
|
.option("--list-presets", "list available presets")
|
|
892
|
+
.option("--list-design-systems", "list available design systems")
|
|
646
893
|
.addHelpText("after", `
|
|
647
894
|
|
|
648
895
|
Examples:
|
|
@@ -651,6 +898,7 @@ Examples:
|
|
|
651
898
|
agentkit init --preset next
|
|
652
899
|
agentkit init ./my-project --yes --dry-run
|
|
653
900
|
agentkit --list-presets
|
|
901
|
+
agentkit --list-design-systems
|
|
654
902
|
agentkit --list`);
|
|
655
903
|
program
|
|
656
904
|
.command("init")
|
|
@@ -660,8 +908,11 @@ Examples:
|
|
|
660
908
|
.option("--dry-run", "print planned changes without writing files")
|
|
661
909
|
.option("-i, --interactive", "prompt for install options")
|
|
662
910
|
.option("-y, --yes", "accept defaults for non-interactive runs")
|
|
911
|
+
.option("--write-config", "write resolved install defaults to agentkit.config.json")
|
|
663
912
|
.option("--preset <name>", `install stack-specific guidance (${formatPresetList()})`)
|
|
913
|
+
.option("--design-system <name>", `design system guidance for DESIGN-SYSTEM.md (${formatDesignSystemList()})`)
|
|
664
914
|
.action(async (target, options) => {
|
|
915
|
+
await applyInitConfig(options, await loadConfigForTarget(target));
|
|
665
916
|
const resolvedTarget = await resolveInteractiveTarget(target, options);
|
|
666
917
|
const result = await installTemplates(resolvedTarget, options);
|
|
667
918
|
printInstallResult(result, Boolean(options.dryRun));
|
|
@@ -672,7 +923,9 @@ Examples:
|
|
|
672
923
|
.argument("[target]", "target project directory", ".")
|
|
673
924
|
.option("--dry-run", "print planned changes without writing files")
|
|
674
925
|
.option("--preset <name>", `update stack-specific guidance (${formatPresetList()})`)
|
|
926
|
+
.option("--design-system <name>", `design system guidance for DESIGN-SYSTEM.md (${formatDesignSystemList()})`)
|
|
675
927
|
.action(async (target, options) => {
|
|
928
|
+
applyUpdateConfig(options, await loadConfigForTarget(target));
|
|
676
929
|
const result = await updateTemplates(target, options);
|
|
677
930
|
printUpdateResult(result, Boolean(options.dryRun));
|
|
678
931
|
});
|
package/package.json
CHANGED
|
File without changes
|