claude-toolkit 0.1.24 → 0.1.25

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.25 (2026-06-01)
4
+
5
+ - feat: add update command to sync detected stacks into existing config
6
+
3
7
  ## 0.1.24 (2026-06-01)
4
8
 
5
9
  - feat: add capacitor stack with Capgo OTA live updates, channels, and encryption
package/README.md CHANGED
@@ -75,9 +75,25 @@ Stack drift detected:
75
75
 
76
76
  Suggested update in claude-toolkit.config.ts:
77
77
  stacks: ["solidjs", "vite", "cloudflare", "playwright"]
78
+
79
+ Run "claude-toolkit update" to add detected stacks automatically.
78
80
  ```
79
81
 
80
- This keeps your config aligned as your project evolves stacks you add or remove are surfaced automatically. The suggestion is informational; your config is not modified unless you update it yourself.
82
+ `sync` is non-destructiveit reports drift but never edits your config.
83
+
84
+ **On `update`** (existing config), the toolkit adds any newly detected stacks to your config and regenerates `.claude/` in one step — use this when you add a new stack (e.g. Capacitor) to an existing project:
85
+
86
+ ```text
87
+ Adding newly detected stacks to config:
88
+ + capacitor — found @capacitor/core in dependencies
89
+ Updated claude-toolkit.config.ts
90
+ Generated .claude/ with 3 stack(s) and 4 core skills
91
+ Update complete.
92
+ ```
93
+
94
+ Stacks already in your config that are no longer detected are reported but left unchanged (remove them manually if intended). If no config exists yet, `update` tells you to run `init` first.
95
+
96
+ This keeps your config aligned as your project evolves — `init` for first-time setup, `update` to pull in new stacks, `sync` to regenerate from the current config.
81
97
 
82
98
  ## Available Stacks
83
99
 
package/bin/cli.ts CHANGED
@@ -4,8 +4,9 @@
4
4
  * claude-toolkit CLI
5
5
  *
6
6
  * Commands:
7
- * init — Scaffold config file and generate .claude/
8
- * syncRegenerate .claude/ from existing config
7
+ * init — Scaffold config file and generate .claude/ (first-time setup)
8
+ * updateAdd newly detected stacks to an existing config, then regenerate
9
+ * sync — Regenerate .claude/ from existing config
9
10
  */
10
11
 
11
12
  import { existsSync } from "node:fs";
@@ -17,6 +18,24 @@ import type { ClaudeToolkitConfig } from "../src/types.js";
17
18
 
18
19
  const CONFIG_FILENAME = "claude-toolkit.config.ts";
19
20
 
21
+ /** Build a `stacks: [...]` literal for injection into the config file */
22
+ function buildStacksLiteral(stacks: string[]): string {
23
+ return stacks.length > 0 ? `stacks: [${stacks.map((s) => `"${s}"`).join(", ")}]` : "stacks: []";
24
+ }
25
+
26
+ /** Rewrite the `stacks: [...]` array in an existing config file in place */
27
+ async function updateConfigStacks(configPath: string, stacks: string[]): Promise<void> {
28
+ const content = await readFile(configPath, "utf-8");
29
+ const stacksArray = /stacks:\s*\[[^\]]*\]/;
30
+ if (!stacksArray.test(content)) {
31
+ throw new Error(
32
+ `Could not find a "stacks: [...]" array in ${configPath} to update.\n` +
33
+ `Add it manually: ${buildStacksLiteral(stacks)}`,
34
+ );
35
+ }
36
+ await writeFile(configPath, content.replace(stacksArray, buildStacksLiteral(stacks)), "utf-8");
37
+ }
38
+
20
39
  async function loadConfig(projectDir: string): Promise<ClaudeToolkitConfig> {
21
40
  const configPath = join(projectDir, CONFIG_FILENAME);
22
41
  if (!existsSync(configPath)) {
@@ -31,8 +50,12 @@ async function init(projectDir: string): Promise<void> {
31
50
 
32
51
  if (existsSync(configPath)) {
33
52
  console.log(`Config already exists: ${configPath}`);
34
- console.log("Running sync instead...");
35
- return sync(projectDir);
53
+ console.log("\nThis project is already initialized. Did you mean to:");
54
+ console.log(
55
+ " claude-toolkit update # detect & add new stacks to your config, then regenerate",
56
+ );
57
+ console.log(" claude-toolkit sync # regenerate .claude/ from the current config");
58
+ return;
36
59
  }
37
60
 
38
61
  // Detect stacks
@@ -48,10 +71,7 @@ async function init(projectDir: string): Promise<void> {
48
71
  }
49
72
 
50
73
  // Build stacks literal for config injection
51
- const stacksLiteral =
52
- detected.length > 0
53
- ? `stacks: [${detected.map((d) => `"${d.name}"`).join(", ")}]`
54
- : "stacks: []";
74
+ const stacksLiteral = buildStacksLiteral(detected.map((d) => d.name));
55
75
 
56
76
  // Copy starter config with detected stacks injected
57
77
  const templatePath = join(import.meta.dirname, "..", "templates", "claude-toolkit.config.ts");
@@ -117,13 +137,63 @@ async function sync(projectDir: string): Promise<void> {
117
137
  ]),
118
138
  ];
119
139
  console.log(`\nSuggested update in ${CONFIG_FILENAME}:`);
120
- console.log(` stacks: [${suggested.map((s) => `"${s}"`).join(", ")}]\n`);
140
+ console.log(` ${buildStacksLiteral(suggested)}`);
141
+ if (missing.length > 0) {
142
+ console.log('\nRun "claude-toolkit update" to add detected stacks automatically.\n');
143
+ }
121
144
  }
122
145
 
123
146
  await generate(projectDir, config);
124
147
  console.log("Sync complete.");
125
148
  }
126
149
 
150
+ /**
151
+ * Update an existing config by adding newly detected stacks, then regenerate.
152
+ * Detected-but-missing stacks are added; stacks in config that are no longer
153
+ * detected are reported but left unchanged. Errors out if no config exists.
154
+ */
155
+ async function update(projectDir: string): Promise<void> {
156
+ const configPath = join(projectDir, CONFIG_FILENAME);
157
+ if (!existsSync(configPath)) {
158
+ console.error(`No ${CONFIG_FILENAME} found in ${projectDir}.`);
159
+ console.error(`Run "claude-toolkit init" to create one first.`);
160
+ process.exit(1);
161
+ }
162
+
163
+ const config = await loadConfig(projectDir);
164
+ const detected = detectStacks(projectDir);
165
+ const configuredNames = new Set(config.stacks);
166
+ const detectedNames = new Set(detected.map((d) => d.name));
167
+
168
+ const missing = detected.filter((d) => !configuredNames.has(d.name));
169
+ const stale = config.stacks.filter((s) => !detectedNames.has(s));
170
+
171
+ if (missing.length === 0) {
172
+ console.log("Config is already up to date — all detected stacks are present.");
173
+ } else {
174
+ console.log("Adding newly detected stacks to config:");
175
+ const maxLen = Math.max(...missing.map((d) => d.name.length));
176
+ for (const d of missing) {
177
+ console.log(` + ${d.name.padEnd(maxLen)} — ${d.reason}`);
178
+ }
179
+ const nextStacks = [...config.stacks, ...missing.map((d) => d.name)];
180
+ await updateConfigStacks(configPath, nextStacks);
181
+ config.stacks = nextStacks;
182
+ console.log(`Updated ${CONFIG_FILENAME}`);
183
+ }
184
+
185
+ if (stale.length > 0) {
186
+ console.log("\nIn config but not detected (left unchanged):");
187
+ for (const s of stale) {
188
+ console.log(` - ${s}`);
189
+ }
190
+ console.log("Remove them from the config manually if they no longer apply.");
191
+ }
192
+
193
+ await generate(projectDir, config);
194
+ console.log("Update complete.");
195
+ }
196
+
127
197
  // CLI entry
128
198
  const args = process.argv.slice(2);
129
199
  const command = args[0];
@@ -136,18 +206,23 @@ switch (command) {
136
206
  case "sync":
137
207
  await sync(projectDir);
138
208
  break;
209
+ case "update":
210
+ await update(projectDir);
211
+ break;
139
212
  case undefined:
140
213
  case "help":
141
214
  console.log(`
142
215
  claude-toolkit — Reusable Claude Code configuration
143
216
 
144
217
  Commands:
145
- init Scaffold config file and generate .claude/
146
- sync Regenerate .claude/ from config
218
+ init Scaffold config file and generate .claude/ (first-time setup)
219
+ update Add newly detected stacks to an existing config, then regenerate
220
+ sync Regenerate .claude/ from the current config
147
221
  help Show this message
148
222
 
149
223
  Usage:
150
224
  bunx claude-toolkit init [project-dir]
225
+ bunx claude-toolkit update [project-dir]
151
226
  bunx claude-toolkit sync [project-dir]
152
227
  `);
153
228
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-toolkit",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Reusable Claude Code configuration toolkit with stack-specific connectors",
5
5
  "type": "module",
6
6
  "bin": {