@curenorway/kode-cli 1.9.0 → 1.10.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/dist/cli.js CHANGED
@@ -15,41 +15,101 @@ import {
15
15
  readPageContext,
16
16
  statusCommand,
17
17
  watchCommand
18
- } from "./chunk-HKPZVOMY.js";
18
+ } from "./chunk-CUZJE4JZ.js";
19
19
 
20
20
  // src/cli.ts
21
21
  import { Command } from "commander";
22
- import chalk5 from "chalk";
22
+ import chalk6 from "chalk";
23
23
  import { createRequire } from "module";
24
24
 
25
- // src/commands/pages.ts
25
+ // src/commands/rollback.ts
26
26
  import chalk from "chalk";
27
+ import ora from "ora";
28
+ async function rollbackCommand(environment = "staging") {
29
+ const projectRoot = findProjectRoot();
30
+ if (!projectRoot) {
31
+ console.log(chalk.red("\u274C Not in a Cure Kode project."));
32
+ console.log(chalk.dim(' Run "kode init" first.'));
33
+ return;
34
+ }
35
+ const config = getProjectConfig(projectRoot);
36
+ if (!config) {
37
+ console.log(chalk.red("\u274C Could not read project configuration."));
38
+ return;
39
+ }
40
+ if (!["staging", "production"].includes(environment)) {
41
+ console.log(chalk.red(`\u274C Invalid environment: ${environment}`));
42
+ console.log(chalk.dim(' Use "staging" or "production"'));
43
+ return;
44
+ }
45
+ const client = createApiClient(config);
46
+ const spinner = ora(`Ruller tilbake ${environment}...`).start();
47
+ try {
48
+ if (environment === "production") {
49
+ const status = await client.getDeploymentStatus(config.siteId);
50
+ if (!status.productionEnabled) {
51
+ spinner.fail("Produksjon er ikke aktivert");
52
+ console.log();
53
+ console.log(chalk.yellow("\u26A0\uFE0F Produksjon er deaktivert for dette prosjektet."));
54
+ console.log(chalk.dim(" Kan ikke rulle tilbake n\xE5r produksjon er deaktivert."));
55
+ return;
56
+ }
57
+ }
58
+ const result = await client.rollback(config.siteId, environment);
59
+ spinner.succeed(`Tilbakerulling fullf\xF8rt (${result.duration_ms}ms)`);
60
+ console.log();
61
+ console.log(chalk.dim("Tilbakerulling detaljer:"));
62
+ console.log(chalk.dim(` Fra: ${result.rolledBackFrom.version}`));
63
+ console.log(chalk.dim(` Til: ${result.rolledBackTo.version}`));
64
+ console.log();
65
+ console.log(chalk.bold("CDN URL:"));
66
+ console.log(chalk.cyan(` ${result.cdn_url}`));
67
+ console.log();
68
+ console.log(chalk.green(`\u2705 ${environment.charAt(0).toUpperCase() + environment.slice(1)} er n\xE5 tilbake til forrige versjon!`));
69
+ } catch (error) {
70
+ spinner.fail("Tilbakerulling feilet");
71
+ if (error.statusCode === 404) {
72
+ console.log();
73
+ console.log(chalk.yellow("\u26A0\uFE0F Ingen tidligere versjon \xE5 rulle tilbake til."));
74
+ console.log(chalk.dim(" Det m\xE5 v\xE6re minst 2 deployments for \xE5 kunne rulle tilbake."));
75
+ } else if (error.statusCode === 409) {
76
+ console.log();
77
+ console.log(chalk.yellow("\u26A0\uFE0F En annen deployment kj\xF8rer."));
78
+ console.log(chalk.dim(" Vent til den er ferdig og pr\xF8v igjen."));
79
+ } else {
80
+ console.error(chalk.red("\nError:"), error.message || error);
81
+ }
82
+ }
83
+ }
84
+
85
+ // src/commands/pages.ts
86
+ import chalk2 from "chalk";
27
87
  async function pagesCommand(options) {
28
88
  const projectRoot = findProjectRoot();
29
89
  if (!projectRoot) {
30
- console.log(chalk.red("Error: Not in a Cure Kode project."));
31
- console.log(chalk.dim('Run "kode init" first.'));
90
+ console.log(chalk2.red("Error: Not in a Cure Kode project."));
91
+ console.log(chalk2.dim('Run "kode init" first.'));
32
92
  return;
33
93
  }
34
94
  const config = getProjectConfig(projectRoot);
35
95
  if (!config) {
36
- console.log(chalk.red("Error: Invalid project configuration."));
96
+ console.log(chalk2.red("Error: Invalid project configuration."));
37
97
  return;
38
98
  }
39
99
  if (options.delete && options.page) {
40
100
  const deleted = deletePageContext(projectRoot, options.page);
41
101
  if (deleted) {
42
- console.log(chalk.green(`Deleted: ${options.page}`));
102
+ console.log(chalk2.green(`Deleted: ${options.page}`));
43
103
  } else {
44
- console.log(chalk.red(`Not found: ${options.page}`));
104
+ console.log(chalk2.red(`Not found: ${options.page}`));
45
105
  }
46
106
  return;
47
107
  }
48
108
  if (options.page) {
49
109
  const context = readPageContext(projectRoot, options.page);
50
110
  if (!context) {
51
- console.log(chalk.red(`Page not found: ${options.page}`));
52
- console.log(chalk.dim('Use "kode pages" to list cached pages'));
111
+ console.log(chalk2.red(`Page not found: ${options.page}`));
112
+ console.log(chalk2.dim('Use "kode pages" to list cached pages'));
53
113
  return;
54
114
  }
55
115
  if (options.json) {
@@ -61,80 +121,80 @@ async function pagesCommand(options) {
61
121
  }
62
122
  const pages = listCachedPages(projectRoot);
63
123
  if (pages.length === 0) {
64
- console.log(chalk.yellow("No cached pages."));
65
- console.log(chalk.dim('Use "kode html <url> --save" to cache page structures.'));
124
+ console.log(chalk2.yellow("No cached pages."));
125
+ console.log(chalk2.dim('Use "kode html <url> --save" to cache page structures.'));
66
126
  return;
67
127
  }
68
128
  if (options.json) {
69
129
  console.log(JSON.stringify(pages, null, 2));
70
130
  return;
71
131
  }
72
- console.log(chalk.bold(`Cached Pages (${pages.length})`));
132
+ console.log(chalk2.bold(`Cached Pages (${pages.length})`));
73
133
  console.log();
74
134
  for (const page of pages) {
75
135
  const path = new URL(page.url).pathname;
76
136
  const date = new Date(page.extractedAt).toLocaleDateString();
77
137
  const badges = [];
78
138
  if (page.sectionCount > 0) badges.push(`${page.sectionCount} sections`);
79
- if (page.cmsCollectionCount > 0) badges.push(chalk.cyan(`${page.cmsCollectionCount} CMS`));
80
- console.log(` ${chalk.bold(path)} ${chalk.dim(`[${page.slug}]`)}`);
139
+ if (page.cmsCollectionCount > 0) badges.push(chalk2.cyan(`${page.cmsCollectionCount} CMS`));
140
+ console.log(` ${chalk2.bold(path)} ${chalk2.dim(`[${page.slug}]`)}`);
81
141
  if (page.title) {
82
- console.log(chalk.dim(` "${page.title}"`));
142
+ console.log(chalk2.dim(` "${page.title}"`));
83
143
  }
84
- console.log(chalk.dim(` ${badges.join(", ")} \u2022 ${date}`));
144
+ console.log(chalk2.dim(` ${badges.join(", ")} \u2022 ${date}`));
85
145
  console.log();
86
146
  }
87
- console.log(chalk.dim(`Use "kode pages <slug>" to see details`));
88
- console.log(chalk.dim(`Use "kode html <url> --save --force" to refresh`));
147
+ console.log(chalk2.dim(`Use "kode pages <slug>" to see details`));
148
+ console.log(chalk2.dim(`Use "kode html <url> --save --force" to refresh`));
89
149
  }
90
150
  function printPageDetails(context) {
91
- console.log(chalk.bold(context.title || context.url));
92
- console.log(chalk.dim(context.url));
93
- console.log(chalk.dim(`Extracted: ${context.extractedAt}`));
151
+ console.log(chalk2.bold(context.title || context.url));
152
+ console.log(chalk2.dim(context.url));
153
+ console.log(chalk2.dim(`Extracted: ${context.extractedAt}`));
94
154
  console.log();
95
155
  if (context.sections.length > 0) {
96
- console.log(chalk.bold("Sections"));
156
+ console.log(chalk2.bold("Sections"));
97
157
  for (const section of context.sections) {
98
158
  const name = section.heading || section.id || section.className?.split(" ")[0] || "section";
99
159
  const badges = [];
100
- if (section.hasCms) badges.push(chalk.cyan("CMS"));
101
- if (section.hasForm) badges.push(chalk.yellow("Form"));
160
+ if (section.hasCms) badges.push(chalk2.cyan("CMS"));
161
+ if (section.hasForm) badges.push(chalk2.yellow("Form"));
102
162
  const badgeStr = badges.length > 0 ? ` [${badges.join(", ")}]` : "";
103
163
  console.log(` \u2022 ${name}${badgeStr}`);
104
164
  if (section.textSample) {
105
- console.log(chalk.dim(` "${section.textSample.slice(0, 80)}..."`));
165
+ console.log(chalk2.dim(` "${section.textSample.slice(0, 80)}..."`));
106
166
  }
107
167
  }
108
168
  console.log();
109
169
  }
110
170
  if (context.headings.length > 0) {
111
- console.log(chalk.bold("Headings"));
171
+ console.log(chalk2.bold("Headings"));
112
172
  for (const h of context.headings.slice(0, 10)) {
113
173
  const level = "H" + h.level;
114
- console.log(` ${chalk.dim(level)} ${h.text}`);
174
+ console.log(` ${chalk2.dim(level)} ${h.text}`);
115
175
  }
116
176
  if (context.headings.length > 10) {
117
- console.log(chalk.dim(` ... and ${context.headings.length - 10} more`));
177
+ console.log(chalk2.dim(` ... and ${context.headings.length - 10} more`));
118
178
  }
119
179
  console.log();
120
180
  }
121
181
  if (context.ctas.length > 0) {
122
- console.log(chalk.bold("CTAs"));
182
+ console.log(chalk2.bold("CTAs"));
123
183
  for (const cta of context.ctas) {
124
- const href = cta.href ? chalk.dim(` \u2192 ${cta.href}`) : "";
184
+ const href = cta.href ? chalk2.dim(` \u2192 ${cta.href}`) : "";
125
185
  console.log(` \u2022 "${cta.text}"${href}`);
126
- console.log(chalk.dim(` in ${cta.location}`));
186
+ console.log(chalk2.dim(` in ${cta.location}`));
127
187
  }
128
188
  console.log();
129
189
  }
130
190
  if (context.forms.length > 0) {
131
- console.log(chalk.bold("Forms"));
191
+ console.log(chalk2.bold("Forms"));
132
192
  for (const form of context.forms) {
133
- console.log(` ${chalk.bold(form.name || "form")}`);
193
+ console.log(` ${chalk2.bold(form.name || "form")}`);
134
194
  for (const field of form.fields) {
135
- const required = field.required ? chalk.red("*") : "";
195
+ const required = field.required ? chalk2.red("*") : "";
136
196
  const label = field.label || field.type;
137
- console.log(` \u2022 ${label}${required} ${chalk.dim(`(${field.type})`)}`);
197
+ console.log(` \u2022 ${label}${required} ${chalk2.dim(`(${field.type})`)}`);
138
198
  }
139
199
  if (form.submitText) {
140
200
  console.log(` \u2192 Submit: "${form.submitText}"`);
@@ -143,31 +203,31 @@ function printPageDetails(context) {
143
203
  console.log();
144
204
  }
145
205
  if (context.cmsPatterns.length > 0) {
146
- console.log(chalk.bold("CMS Collections"));
206
+ console.log(chalk2.bold("CMS Collections"));
147
207
  for (const cms of context.cmsPatterns) {
148
- console.log(` ${chalk.bold(cms.containerClass)}: ${chalk.cyan(`${cms.itemCount} items`)}`);
208
+ console.log(` ${chalk2.bold(cms.containerClass)}: ${chalk2.cyan(`${cms.itemCount} items`)}`);
149
209
  if (cms.templateFields.length > 0) {
150
- console.log(chalk.dim(` Template fields: ${cms.templateFields.join(", ")}`));
210
+ console.log(chalk2.dim(` Template fields: ${cms.templateFields.join(", ")}`));
151
211
  }
152
212
  }
153
213
  console.log();
154
214
  }
155
215
  if (context.navigation.length > 0) {
156
- console.log(chalk.bold("Navigation"));
216
+ console.log(chalk2.bold("Navigation"));
157
217
  for (const nav of context.navigation) {
158
- console.log(` ${chalk.bold(nav.type)}:`);
218
+ console.log(` ${chalk2.bold(nav.type)}:`);
159
219
  for (const item of nav.items.slice(0, 8)) {
160
- const href = item.href ? chalk.dim(` \u2192 ${item.href}`) : "";
220
+ const href = item.href ? chalk2.dim(` \u2192 ${item.href}`) : "";
161
221
  console.log(` \u2022 ${item.text}${href}`);
162
222
  }
163
223
  if (nav.items.length > 8) {
164
- console.log(chalk.dim(` ... and ${nav.items.length - 8} more`));
224
+ console.log(chalk2.dim(` ... and ${nav.items.length - 8} more`));
165
225
  }
166
226
  }
167
227
  console.log();
168
228
  }
169
229
  if (context.notes && context.notes.length > 0) {
170
- console.log(chalk.bold("Notes"));
230
+ console.log(chalk2.bold("Notes"));
171
231
  for (const note of context.notes) {
172
232
  console.log(` \u2022 ${note}`);
173
233
  }
@@ -176,40 +236,40 @@ function printPageDetails(context) {
176
236
  }
177
237
 
178
238
  // src/commands/set.ts
179
- import chalk2 from "chalk";
180
- import ora from "ora";
239
+ import chalk3 from "chalk";
240
+ import ora2 from "ora";
181
241
  async function setCommand(script, options) {
182
242
  const projectRoot = findProjectRoot();
183
243
  if (!projectRoot) {
184
- console.log(chalk2.red("\u274C Not in a Cure Kode project."));
185
- console.log(chalk2.dim(' Run "kode init" first.'));
244
+ console.log(chalk3.red("\u274C Not in a Cure Kode project."));
245
+ console.log(chalk3.dim(' Run "kode init" first.'));
186
246
  return;
187
247
  }
188
248
  const config = getProjectConfig(projectRoot);
189
249
  if (!config) {
190
- console.log(chalk2.red("\u274C Could not read project configuration."));
250
+ console.log(chalk3.red("\u274C Could not read project configuration."));
191
251
  return;
192
252
  }
193
253
  if (!options.scope && options.autoLoad === void 0) {
194
- console.log(chalk2.yellow("\u26A0\uFE0F No changes specified."));
195
- console.log(chalk2.dim(" Use --scope or --auto-load/--no-auto-load"));
254
+ console.log(chalk3.yellow("\u26A0\uFE0F No changes specified."));
255
+ console.log(chalk3.dim(" Use --scope or --auto-load/--no-auto-load"));
196
256
  console.log();
197
- console.log(chalk2.dim("Examples:"));
198
- console.log(chalk2.dim(" kode set my-script --scope page-specific"));
199
- console.log(chalk2.dim(" kode set my-script --scope global --auto-load"));
200
- console.log(chalk2.dim(" kode set my-script --no-auto-load"));
257
+ console.log(chalk3.dim("Examples:"));
258
+ console.log(chalk3.dim(" kode set my-script --scope page-specific"));
259
+ console.log(chalk3.dim(" kode set my-script --scope global --auto-load"));
260
+ console.log(chalk3.dim(" kode set my-script --no-auto-load"));
201
261
  return;
202
262
  }
203
- const spinner = ora(`Updating ${script}...`).start();
263
+ const spinner = ora2(`Updating ${script}...`).start();
204
264
  try {
205
265
  const client = createApiClient(config);
206
266
  const scripts = await client.listScripts(config.siteId);
207
267
  const targetScript = scripts.find((s) => s.slug === script || s.name === script);
208
268
  if (!targetScript) {
209
269
  spinner.fail(`Script "${script}" not found`);
210
- console.log(chalk2.dim("\nAvailable scripts:"));
270
+ console.log(chalk3.dim("\nAvailable scripts:"));
211
271
  scripts.forEach((s) => {
212
- console.log(chalk2.dim(` - ${s.slug}`));
272
+ console.log(chalk3.dim(` - ${s.slug}`));
213
273
  });
214
274
  return;
215
275
  }
@@ -229,80 +289,80 @@ async function setCommand(script, options) {
229
289
  }
230
290
  updates.changeSummary = `Updated settings: ${changes.join(", ")}`;
231
291
  const updated = await client.updateScript(targetScript.id, updates);
232
- spinner.succeed(chalk2.green(`Updated ${script}`));
292
+ spinner.succeed(chalk3.green(`Updated ${script}`));
233
293
  console.log();
234
294
  for (const change of changes) {
235
- console.log(chalk2.dim(` ${change}`));
295
+ console.log(chalk3.dim(` ${change}`));
236
296
  }
237
297
  console.log();
238
298
  if (options.scope === "page-specific") {
239
- console.log(chalk2.yellow("\u26A0\uFE0F Page-specific scripts need page assignments to load."));
240
- console.log(chalk2.dim(" Use app.cure.no \u2192 Kode to assign pages, or use MCP:"));
241
- console.log(chalk2.dim(" kode_assign_script_to_page(scriptSlug, pageSlug)"));
299
+ console.log(chalk3.yellow("\u26A0\uFE0F Page-specific scripts need page assignments to load."));
300
+ console.log(chalk3.dim(" Use app.cure.no \u2192 Kode to assign pages, or use MCP:"));
301
+ console.log(chalk3.dim(" kode_assign_script_to_page(scriptSlug, pageSlug)"));
242
302
  console.log();
243
303
  }
244
- console.log(chalk2.dim('Run "kode deploy" to make changes live.'));
304
+ console.log(chalk3.dim('Run "kode deploy" to make changes live.'));
245
305
  } catch (error) {
246
306
  spinner.fail("Failed to update script");
247
- console.error(chalk2.red("\nError:"), error);
307
+ console.error(chalk3.red("\nError:"), error);
248
308
  }
249
309
  }
250
310
 
251
311
  // src/commands/production.ts
252
- import chalk3 from "chalk";
253
- import ora2 from "ora";
312
+ import chalk4 from "chalk";
313
+ import ora3 from "ora";
254
314
  async function productionCommand(action, options) {
255
315
  if (!["enable", "disable", "status"].includes(action)) {
256
- console.log(chalk3.red("\u274C Invalid action. Use: enable, disable, or status"));
257
- console.log(chalk3.dim(" kode production enable [--domain <domain>]"));
258
- console.log(chalk3.dim(" kode production disable"));
259
- console.log(chalk3.dim(" kode production status"));
316
+ console.log(chalk4.red("\u274C Invalid action. Use: enable, disable, or status"));
317
+ console.log(chalk4.dim(" kode production enable [--domain <domain>]"));
318
+ console.log(chalk4.dim(" kode production disable"));
319
+ console.log(chalk4.dim(" kode production status"));
260
320
  return;
261
321
  }
262
322
  const projectRoot = findProjectRoot();
263
323
  if (!projectRoot) {
264
- console.log(chalk3.red("\u274C Not in a Cure Kode project."));
265
- console.log(chalk3.dim(' Run "kode init" first.'));
324
+ console.log(chalk4.red("\u274C Not in a Cure Kode project."));
325
+ console.log(chalk4.dim(' Run "kode init" first.'));
266
326
  return;
267
327
  }
268
328
  const config = getProjectConfig(projectRoot);
269
329
  if (!config) {
270
- console.log(chalk3.red("\u274C Could not read project configuration."));
330
+ console.log(chalk4.red("\u274C Could not read project configuration."));
271
331
  return;
272
332
  }
273
333
  const client = createApiClient(config);
274
334
  if (action === "status") {
275
- const spinner2 = ora2("Fetching production status...").start();
335
+ const spinner2 = ora3("Fetching production status...").start();
276
336
  try {
277
337
  const status = await client.getDeploymentStatus(config.siteId);
278
338
  spinner2.stop();
279
339
  console.log();
280
- console.log(chalk3.bold("Production Status"));
340
+ console.log(chalk4.bold("Production Status"));
281
341
  console.log();
282
342
  if (status.productionEnabled) {
283
- console.log(chalk3.green(" \u25CF Produksjon er aktivert"));
343
+ console.log(chalk4.green(" \u25CF Produksjon er aktivert"));
284
344
  if (status.productionDomain) {
285
- console.log(chalk3.dim(` Domain: ${status.productionDomain}`));
345
+ console.log(chalk4.dim(` Domain: ${status.productionDomain}`));
286
346
  }
287
347
  if (status.production.lastSuccessful) {
288
348
  console.log(
289
- chalk3.dim(
349
+ chalk4.dim(
290
350
  ` Siste deploy: v${status.production.lastSuccessful.version}`
291
351
  )
292
352
  );
293
353
  }
294
354
  } else {
295
- console.log(chalk3.gray(" \u25CB Produksjon er deaktivert"));
296
- console.log(chalk3.dim(" Kun staging er aktiv"));
355
+ console.log(chalk4.gray(" \u25CB Produksjon er deaktivert"));
356
+ console.log(chalk4.dim(" Kun staging er aktiv"));
297
357
  }
298
358
  console.log();
299
359
  } catch (error) {
300
360
  spinner2.fail("Failed to fetch status");
301
- console.error(chalk3.red("\nError:"), error.message || error);
361
+ console.error(chalk4.red("\nError:"), error.message || error);
302
362
  }
303
363
  return;
304
364
  }
305
- const spinner = ora2(
365
+ const spinner = ora3(
306
366
  action === "enable" ? "Aktiverer produksjon..." : "Deaktiverer produksjon..."
307
367
  ).start();
308
368
  try {
@@ -314,49 +374,49 @@ async function productionCommand(action, options) {
314
374
  spinner.stop();
315
375
  console.log();
316
376
  if (action === "enable") {
317
- console.log(chalk3.green("\u2713 Produksjon er n\xE5 aktivert"));
377
+ console.log(chalk4.green("\u2713 Produksjon er n\xE5 aktivert"));
318
378
  if (result.productionDomain) {
319
- console.log(chalk3.dim(` Domain: ${result.productionDomain}`));
379
+ console.log(chalk4.dim(` Domain: ${result.productionDomain}`));
320
380
  }
321
381
  console.log();
322
- console.log(chalk3.dim(" Neste steg:"));
323
- console.log(chalk3.dim(" 1. Deploy til staging: kode deploy"));
324
- console.log(chalk3.dim(" 2. Promoter til produksjon: kode deploy --promote"));
382
+ console.log(chalk4.dim(" Neste steg:"));
383
+ console.log(chalk4.dim(" 1. Deploy til staging: kode deploy"));
384
+ console.log(chalk4.dim(" 2. Promoter til produksjon: kode deploy --promote"));
325
385
  } else {
326
- console.log(chalk3.yellow("\u2713 Produksjon er n\xE5 deaktivert"));
327
- console.log(chalk3.dim(" Kun staging-milj\xF8et er aktivt."));
386
+ console.log(chalk4.yellow("\u2713 Produksjon er n\xE5 deaktivert"));
387
+ console.log(chalk4.dim(" Kun staging-milj\xF8et er aktivt."));
328
388
  console.log(
329
- chalk3.dim(" Produksjonsdomenet vil f\xE5 en tom script-respons.")
389
+ chalk4.dim(" Produksjonsdomenet vil f\xE5 en tom script-respons.")
330
390
  );
331
391
  }
332
392
  console.log();
333
393
  } catch (error) {
334
394
  spinner.fail(action === "enable" ? "Kunne ikke aktivere produksjon" : "Kunne ikke deaktivere produksjon");
335
- console.error(chalk3.red("\nError:"), error.message || error);
395
+ console.error(chalk4.red("\nError:"), error.message || error);
336
396
  }
337
397
  }
338
398
 
339
399
  // src/commands/update-claude-md.ts
340
- import chalk4 from "chalk";
400
+ import chalk5 from "chalk";
341
401
  import { existsSync, readFileSync, writeFileSync } from "fs";
342
402
  import { join } from "path";
343
403
  async function updateClaudeMdCommand() {
344
404
  const projectRoot = findProjectRoot();
345
405
  if (!projectRoot) {
346
- console.log(chalk4.red("\u274C Not in a Cure Kode project."));
347
- console.log(chalk4.dim(' Run "kode init" first.'));
406
+ console.log(chalk5.red("\u274C Not in a Cure Kode project."));
407
+ console.log(chalk5.dim(' Run "kode init" first.'));
348
408
  return;
349
409
  }
350
410
  const config = getProjectConfig(projectRoot);
351
411
  if (!config) {
352
- console.log(chalk4.red("\u274C Could not read project configuration."));
412
+ console.log(chalk5.red("\u274C Could not read project configuration."));
353
413
  return;
354
414
  }
355
415
  const claudeMdPath = join(projectRoot, "CLAUDE.md");
356
416
  const newKodeSection = generateClaudeMdMinimal(config.siteName, config.siteSlug);
357
417
  if (!existsSync(claudeMdPath)) {
358
418
  writeFileSync(claudeMdPath, newKodeSection);
359
- console.log(chalk4.green("\u2705 Created CLAUDE.md with Cure Kode section"));
419
+ console.log(chalk5.green("\u2705 Created CLAUDE.md with Cure Kode section"));
360
420
  return;
361
421
  }
362
422
  let content = readFileSync(claudeMdPath, "utf-8");
@@ -375,18 +435,18 @@ async function updateClaudeMdCommand() {
375
435
  content = newKodeSection + "---\n\n" + content;
376
436
  writeFileSync(claudeMdPath, content);
377
437
  if (removedCount > 1) {
378
- console.log(chalk4.green(`\u2705 Cleaned up ${removedCount} duplicate Kode sections and added fresh one`));
438
+ console.log(chalk5.green(`\u2705 Cleaned up ${removedCount} duplicate Kode sections and added fresh one`));
379
439
  } else if (removedCount === 1) {
380
- console.log(chalk4.green("\u2705 Updated Cure Kode section in CLAUDE.md"));
440
+ console.log(chalk5.green("\u2705 Updated Cure Kode section in CLAUDE.md"));
381
441
  } else {
382
- console.log(chalk4.green("\u2705 Added Cure Kode section to CLAUDE.md"));
442
+ console.log(chalk5.green("\u2705 Added Cure Kode section to CLAUDE.md"));
383
443
  }
384
444
  console.log();
385
- console.log(chalk4.dim("The Cure Kode section now includes:"));
386
- console.log(chalk4.dim(" \u2022 What is Cure Kode (internal tool explanation)"));
387
- console.log(chalk4.dim(" \u2022 CDN URL with script tag for Webflow"));
388
- console.log(chalk4.dim(" \u2022 Workflow steps"));
389
- console.log(chalk4.dim(" \u2022 Command reference"));
445
+ console.log(chalk5.dim("The Cure Kode section now includes:"));
446
+ console.log(chalk5.dim(" \u2022 What is Cure Kode (internal tool explanation)"));
447
+ console.log(chalk5.dim(" \u2022 CDN URL with script tag for Webflow"));
448
+ console.log(chalk5.dim(" \u2022 Workflow steps"));
449
+ console.log(chalk5.dim(" \u2022 Command reference"));
390
450
  }
391
451
 
392
452
  // src/cli.ts
@@ -406,9 +466,12 @@ program.command("push").description("Upload local scripts to Cure").argument("[s
406
466
  program.command("watch").description("Watch for changes and auto-push").option("-d, --deploy", "Auto-deploy after each push").action((options) => {
407
467
  watchCommand(options);
408
468
  });
409
- program.command("deploy [environment]").description("Deploy to staging or production").option("-p, --promote", "Promote staging to production").action((environment, options) => {
469
+ program.command("deploy [environment]").description("Deploy to staging or production").option("-p, --promote", "Promote staging to production").option("-f, --force", "Force release stale deploy lock before deploying").action((environment, options) => {
410
470
  deployCommand(environment, options);
411
471
  });
472
+ program.command("rollback [environment]").description("Rollback to previous deployment").action((environment = "staging") => {
473
+ rollbackCommand(environment);
474
+ });
412
475
  program.command("html <url>").description("Fetch and analyze HTML from a URL").option("-j, --json", "Output as JSON").option("--scripts", "Show only scripts").option("--styles", "Show only styles").option("-s, --save", "Save page structure to context").option("-f, --force", "Force refresh when using --save").action((url, options) => {
413
476
  htmlCommand(url, options);
414
477
  });
@@ -432,7 +495,7 @@ program.command("update-claude-md").alias("ucm").description("Add or update Cure
432
495
  });
433
496
  program.showHelpAfterError();
434
497
  console.log();
435
- console.log(chalk5.bold(" Cure Kode CLI"));
436
- console.log(chalk5.dim(" Manage JS/CSS for Webflow sites"));
498
+ console.log(chalk6.bold(" Cure Kode CLI"));
499
+ console.log(chalk6.dim(" Manage JS/CSS for Webflow sites"));
437
500
  console.log();
438
501
  program.parse();
package/dist/index.d.ts CHANGED
@@ -41,6 +41,9 @@ declare function getScriptsDir(projectRoot: string, projectConfig?: ProjectConfi
41
41
 
42
42
  /**
43
43
  * Cure Kode API Client
44
+ *
45
+ * All requests automatically retry on network/transient errors
46
+ * with exponential backoff (500ms → 1s → 2s).
44
47
  */
45
48
  interface CdnSite {
46
49
  id: string;
@@ -158,12 +161,42 @@ declare class KodeApiClient {
158
161
  };
159
162
  canPromote: boolean;
160
163
  }>;
164
+ rollback(siteId: string, environment?: 'staging' | 'production'): Promise<{
165
+ id: string;
166
+ status: 'rolled_back';
167
+ environment: 'staging' | 'production';
168
+ rolledBackFrom: {
169
+ version: string;
170
+ deploymentId: string;
171
+ };
172
+ rolledBackTo: {
173
+ version: string;
174
+ deploymentId: string;
175
+ };
176
+ cdn_url: string;
177
+ duration_ms: number;
178
+ }>;
161
179
  setProductionEnabled(siteId: string, enabled: boolean, productionDomain?: string): Promise<{
162
180
  success: boolean;
163
181
  productionEnabled: boolean;
164
182
  productionDomain: string | null;
165
183
  }>;
166
184
  fetchHtml(url: string): Promise<ParsedHtmlResult>;
185
+ getLockStatus(siteId: string): Promise<{
186
+ isLocked: boolean;
187
+ isStale: boolean;
188
+ lockHolder: string | null;
189
+ acquiredAt: string | null;
190
+ }>;
191
+ forceReleaseLock(siteId: string): Promise<{
192
+ success: boolean;
193
+ message: string;
194
+ wasLocked: boolean;
195
+ wasStale?: boolean;
196
+ previousLockHolder?: string;
197
+ acquiredAt?: string;
198
+ duration_ms?: number;
199
+ }>;
167
200
  }
168
201
  /**
169
202
  * Create API client from project config
@@ -203,6 +236,11 @@ declare function pushCommand(options: {
203
236
 
204
237
  /**
205
238
  * Watch for local changes and auto-push
239
+ *
240
+ * Features:
241
+ * - Error tracking with retry queue
242
+ * - Status summary on error/success
243
+ * - Automatic retry after 30 seconds
206
244
  */
207
245
  declare function watchCommand(options: {
208
246
  deploy?: boolean;
@@ -214,9 +252,11 @@ declare function watchCommand(options: {
214
252
  * Workflow:
215
253
  * - `kode deploy` or `kode deploy staging` → deploys to staging
216
254
  * - `kode deploy production` or `kode deploy --promote` → promotes staging to production
255
+ * - `kode deploy --force` → force release stale lock before deploying
217
256
  */
218
257
  declare function deployCommand(environment?: 'staging' | 'production', options?: {
219
258
  promote?: boolean;
259
+ force?: boolean;
220
260
  }): Promise<void>;
221
261
 
222
262
  /**
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  updateScriptPurpose,
28
28
  watchCommand,
29
29
  writeContext
30
- } from "./chunk-HKPZVOMY.js";
30
+ } from "./chunk-CUZJE4JZ.js";
31
31
  export {
32
32
  KodeApiClient,
33
33
  KodeApiError,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@curenorway/kode-cli",
3
- "version": "1.9.0",
4
- "description": "CLI tool for Cure Kode - manage JS/CSS scripts for Webflow sites",
3
+ "version": "1.10.0",
4
+ "description": "CLI for Cure Kode CDN - manage, deploy, and sync JS/CSS scripts for Webflow sites with AI agent support",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "cure-kode": "./dist/cli.js",