@curenorway/kode-cli 1.7.1 → 1.9.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.
@@ -426,6 +426,35 @@ Scripts have two properties: **scope** and **autoLoad**.
426
426
 
427
427
  **CLI flags**: \`kode push --auto-load\` or \`kode push --no-auto-load\`
428
428
 
429
+ ### Script Metadata (Documentation)
430
+
431
+ **ALWAYS provide metadata when creating or updating scripts.** This helps other AI agents and the Chrome extension understand what scripts do.
432
+
433
+ | Field | Description | Example |
434
+ |-------|-------------|---------|
435
+ | \`purpose\` | What the script does (1-2 sentences) | "Adds smooth scroll behavior to anchor links" |
436
+ | \`triggers\` | When the script runs | \`[{ event: 'domReady' }, { event: 'click', selector: '.nav-link' }]\` |
437
+ | \`dependencies\` | External libraries needed | \`[{ name: 'gsap', version: '3.12' }]\` |
438
+ | \`domTargets\` | CSS selectors the script affects | \`[{ selector: '.hero-title' }, { selector: '#contact-form' }]\` |
439
+
440
+ **MCP Tools**:
441
+ - \`kode_create_script\` / \`kode_update_script\` \u2192 include \`metadata: { purpose: "..." }\`
442
+ - \`kode_analyze_script\` \u2192 auto-detect triggers, dependencies, and DOM targets from code
443
+ - \`kode_get_script_metadata\` \u2192 get existing metadata and AI summary
444
+
445
+ **Example**:
446
+ \`\`\`json
447
+ {
448
+ "name": "smooth-scroll",
449
+ "content": "...",
450
+ "metadata": {
451
+ "purpose": "Adds smooth scroll animation to all anchor links",
452
+ "triggers": [{ "event": "click", "selector": "a[href^='#']" }],
453
+ "domTargets": [{ "selector": "a[href^='#']" }]
454
+ }
455
+ }
456
+ \`\`\`
457
+
429
458
  ### Context
430
459
 
431
460
  **Read \`.cure-kode/context.md\` before working on scripts** - it contains:
@@ -597,13 +626,119 @@ if (CK.isLoaded('my-modal-script')) {
597
626
  }
598
627
  \`\`\`
599
628
 
629
+ ## Script Metadata (Documentation)
630
+
631
+ **IMPORTANT: Always provide metadata when creating or updating scripts!**
632
+
633
+ Metadata helps:
634
+ - Other AI agents understand what scripts do
635
+ - The Chrome extension display script info
636
+ - Future debugging and maintenance
637
+
638
+ ### Metadata Fields
639
+
640
+ | Field | Type | Description |
641
+ |-------|------|-------------|
642
+ | \`purpose\` | string | 1-2 sentence description of what the script does |
643
+ | \`triggers\` | array | Events that activate the script |
644
+ | \`dependencies\` | array | External libraries the script needs |
645
+ | \`domTargets\` | array | CSS selectors the script manipulates |
646
+
647
+ ### Trigger Events
648
+
649
+ \`\`\`typescript
650
+ type TriggerEvent =
651
+ | 'domReady' // DOMContentLoaded
652
+ | 'load' // window.onload
653
+ | 'scroll' // scroll events
654
+ | 'click' // click events
655
+ | 'hover' // mouseenter/mouseleave
656
+ | 'resize' // window resize
657
+ | 'mutation' // MutationObserver
658
+ | 'interval' // setInterval
659
+ | 'timeout' // setTimeout
660
+ | 'formSubmit' // form submission
661
+ | 'visibility' // IntersectionObserver
662
+ | 'immediate' // runs immediately
663
+ | 'custom' // custom event
664
+
665
+ // Each trigger can have optional selector/target
666
+ { event: 'click', selector: '.btn-primary' }
667
+ { event: 'scroll', target: 'window' }
668
+ \`\`\`
669
+
670
+ ### Providing Metadata via MCP
671
+
672
+ When creating scripts:
673
+ \`\`\`javascript
674
+ kode_create_script({
675
+ name: 'accordion',
676
+ slug: 'accordion',
677
+ type: 'javascript',
678
+ content: '// accordion code...',
679
+ metadata: {
680
+ purpose: 'Expands and collapses FAQ accordion items on click',
681
+ triggers: [
682
+ { event: 'domReady' },
683
+ { event: 'click', selector: '.accordion-trigger' }
684
+ ],
685
+ domTargets: [
686
+ { selector: '.accordion-trigger' },
687
+ { selector: '.accordion-content' }
688
+ ]
689
+ }
690
+ })
691
+ \`\`\`
692
+
693
+ When updating scripts:
694
+ \`\`\`javascript
695
+ kode_update_script({
696
+ scriptId: 'xxx',
697
+ content: '// updated code...',
698
+ metadata: {
699
+ purpose: 'Updated description...',
700
+ // ... other fields
701
+ }
702
+ })
703
+ \`\`\`
704
+
705
+ ### Auto-Analyze Scripts
706
+
707
+ Use \`kode_analyze_script\` to automatically detect metadata from code:
708
+ \`\`\`javascript
709
+ kode_analyze_script({ scriptId: 'xxx' })
710
+ // Returns detected: triggers, dependencies, domTargets
711
+ \`\`\`
712
+
713
+ The analyzer detects:
714
+ - **DOM selectors**: querySelector, getElementById, getElementsByClassName
715
+ - **Event listeners**: addEventListener, on events
716
+ - **Dependencies**: Library globals (gsap, Swiper, jQuery, etc.)
717
+ - **Timing patterns**: DOMContentLoaded, load, intervals
718
+
719
+ ### Get Script Metadata
720
+
721
+ \`\`\`javascript
722
+ kode_get_script_metadata({ scriptId: 'xxx' })
723
+ // Returns: purpose, triggers, dependencies, domTargets, aiSummary
724
+ \`\`\`
725
+
726
+ ### Best Practice Workflow
727
+
728
+ 1. **Create script** with at least \`purpose\` in metadata
729
+ 2. **Run analyze** to auto-detect triggers and targets
730
+ 3. **Update metadata** with any corrections or additions
731
+ 4. **Deploy** - metadata is included in init.js for Chrome extension
732
+
600
733
  ## Best Practices
601
734
 
602
735
  1. **Always deploy to staging first** - Test before production
603
- 2. **Use page-specific scripts** for page-only functionality
604
- 3. **Use \`autoLoad: false\`** for lazy-loaded features (modals, etc.)
605
- 4. **Document your changes** - Update context.md
606
- 5. **Cache page HTML** - Use \`kode html <url> --save\` to understand structure
736
+ 2. **Always provide metadata** - At minimum, include \`purpose\` when creating scripts
737
+ 3. **Use page-specific scripts** for page-only functionality
738
+ 4. **Use \`autoLoad: false\`** for lazy-loaded features (modals, etc.)
739
+ 5. **Document your changes** - Update context.md
740
+ 6. **Cache page HTML** - Use \`kode html <url> --save\` to understand structure
741
+ 7. **Run analyze after creating** - Use \`kode_analyze_script\` to auto-detect metadata
607
742
 
608
743
  ## MCP Tools
609
744
 
@@ -611,10 +746,12 @@ If using the Kode MCP server, these tools are available:
611
746
 
612
747
  ### Script Management
613
748
  - \`kode_list_scripts\` - List all scripts with scope and autoLoad status
614
- - \`kode_get_script\` - Get script content
615
- - \`kode_create_script\` - Create new script (accepts \`scope\` and \`autoLoad\`)
616
- - \`kode_update_script\` - Update script content, scope, or autoLoad
749
+ - \`kode_get_script\` - Get script content (use \`includeContent: false\` to save tokens)
750
+ - \`kode_create_script\` - Create new script (accepts \`scope\`, \`autoLoad\`, \`metadata\`)
751
+ - \`kode_update_script\` - Update script content, scope, autoLoad, or metadata
617
752
  - \`kode_delete_script\` - Delete a script
753
+ - \`kode_analyze_script\` - Auto-detect metadata (triggers, dependencies, DOM targets) from code
754
+ - \`kode_get_script_metadata\` - Get script metadata and AI summary
618
755
 
619
756
  ### Page Assignment (for page-specific scripts)
620
757
  - \`kode_list_pages\` - List page definitions with URL patterns
@@ -1266,9 +1403,9 @@ config.json
1266
1403
  }
1267
1404
  mcpConfig.mcpServers = mcpConfig.mcpServers || {};
1268
1405
  mcpConfig.mcpServers["cure-kode"] = {
1406
+ type: "stdio",
1269
1407
  command: "npx",
1270
- args: ["-y", "@curenorway/kode-mcp"],
1271
- env: {}
1408
+ args: ["-y", "@curenorway/kode-mcp"]
1272
1409
  };
1273
1410
  let webflowToken = site.webflow_token;
1274
1411
  let webflowMcpMethod = null;
@@ -1317,6 +1454,7 @@ config.json
1317
1454
  }
1318
1455
  if (webflowMcpMethod === "token" && webflowToken) {
1319
1456
  mcpConfig.mcpServers["webflow"] = {
1457
+ type: "stdio",
1320
1458
  command: "npx",
1321
1459
  args: ["-y", "webflow-mcp-server@latest"],
1322
1460
  env: {
@@ -1325,10 +1463,12 @@ config.json
1325
1463
  };
1326
1464
  } else if (webflowMcpMethod === "oauth") {
1327
1465
  mcpConfig.mcpServers["webflow"] = {
1466
+ type: "http",
1328
1467
  url: "https://mcp.webflow.com/sse"
1329
1468
  };
1330
1469
  }
1331
1470
  mcpConfig.mcpServers["playwright"] = {
1471
+ type: "stdio",
1332
1472
  command: "npx",
1333
1473
  args: ["-y", "@playwright/mcp@latest"]
1334
1474
  };
@@ -1610,6 +1750,16 @@ var KodeApiClient = class {
1610
1750
  async getDeploymentStatus(siteId) {
1611
1751
  return this.request(`/api/cdn/sites/${siteId}/deployments/status`);
1612
1752
  }
1753
+ // Production enabled toggle (v2.3)
1754
+ async setProductionEnabled(siteId, enabled, productionDomain) {
1755
+ return this.request(`/api/cdn/sites/${siteId}/production`, {
1756
+ method: "POST",
1757
+ body: JSON.stringify({
1758
+ enabled,
1759
+ productionDomain
1760
+ })
1761
+ });
1762
+ }
1613
1763
  // HTML Fetch
1614
1764
  async fetchHtml(url) {
1615
1765
  return this.request("/api/cdn/fetch-html", {
@@ -2005,9 +2155,20 @@ async function deployCommand(environment, options) {
2005
2155
  }
2006
2156
  const shouldPromote = options?.promote || environment === "production";
2007
2157
  if (shouldPromote) {
2008
- const spinner2 = ora4("Promoting staging to production...").start();
2158
+ const client = createApiClient(config);
2159
+ const spinner2 = ora4("Sjekker produksjonsstatus...").start();
2009
2160
  try {
2010
- const client = createApiClient(config);
2161
+ const status = await client.getDeploymentStatus(config.siteId);
2162
+ if (!status.productionEnabled) {
2163
+ spinner2.fail("Produksjon er ikke aktivert");
2164
+ console.log();
2165
+ console.log(chalk5.yellow("\u26A0\uFE0F Produksjon er deaktivert for dette prosjektet."));
2166
+ console.log(chalk5.dim(" Aktiver produksjon f\xF8rst:"));
2167
+ console.log(chalk5.dim(" kode production enable [--domain <domain>]"));
2168
+ console.log();
2169
+ return;
2170
+ }
2171
+ spinner2.text = "Promoterer staging til produksjon...";
2011
2172
  const deployment = await client.promoteToProduction(config.siteId);
2012
2173
  spinner2.succeed("Promoted to production");
2013
2174
  console.log();
@@ -2434,17 +2595,29 @@ async function statusCommand(options) {
2434
2595
  } else {
2435
2596
  console.log(chalk7.blue(" Staging: ") + chalk7.yellow("\u25CB") + chalk7.dim(" No deployments"));
2436
2597
  }
2598
+ const productionEnabled = deployStatus.productionEnabled ?? false;
2437
2599
  const prodStatus = deployStatus.production.lastSuccessful;
2438
- if (prodStatus) {
2600
+ if (!productionEnabled) {
2601
+ console.log(
2602
+ chalk7.gray(" Production: ") + chalk7.gray("\u25CB") + chalk7.gray(" Deaktivert") + chalk7.dim(" (kun staging)")
2603
+ );
2604
+ console.log();
2605
+ console.log(
2606
+ chalk7.dim(' \u{1F4A1} Run "kode production enable" to activate production environment.')
2607
+ );
2608
+ } else if (prodStatus) {
2439
2609
  console.log(
2440
2610
  chalk7.green(" Production: ") + chalk7.green("\u25CF") + chalk7.dim(` v${prodStatus.version}`) + chalk7.dim(` (${formatDate(prodStatus.completedAt)})`)
2441
2611
  );
2612
+ if (deployStatus.productionDomain) {
2613
+ console.log(chalk7.dim(` Domain: ${deployStatus.productionDomain}`));
2614
+ }
2442
2615
  } else {
2443
2616
  console.log(
2444
- chalk7.green(" Production: ") + chalk7.yellow("\u25CB") + chalk7.dim(" No deployments")
2617
+ chalk7.green(" Production: ") + chalk7.yellow("\u25CB") + chalk7.dim(" Aktivert, ingen deployments enda")
2445
2618
  );
2446
2619
  }
2447
- if (deployStatus.canPromote) {
2620
+ if (deployStatus.canPromote && productionEnabled) {
2448
2621
  console.log();
2449
2622
  console.log(
2450
2623
  chalk7.cyan(' \u{1F4A1} Staging is ahead of production. Run "kode deploy --promote" to update.')
package/dist/cli.js CHANGED
@@ -15,11 +15,11 @@ import {
15
15
  readPageContext,
16
16
  statusCommand,
17
17
  watchCommand
18
- } from "./chunk-KAYIHS2T.js";
18
+ } from "./chunk-HKPZVOMY.js";
19
19
 
20
20
  // src/cli.ts
21
21
  import { Command } from "commander";
22
- import chalk4 from "chalk";
22
+ import chalk5 from "chalk";
23
23
  import { createRequire } from "module";
24
24
 
25
25
  // src/commands/pages.ts
@@ -248,11 +248,17 @@ async function setCommand(script, options) {
248
248
  }
249
249
  }
250
250
 
251
- // src/commands/update-claude-md.ts
251
+ // src/commands/production.ts
252
252
  import chalk3 from "chalk";
253
- import { existsSync, readFileSync, writeFileSync } from "fs";
254
- import { join } from "path";
255
- async function updateClaudeMdCommand() {
253
+ import ora2 from "ora";
254
+ async function productionCommand(action, options) {
255
+ 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"));
260
+ return;
261
+ }
256
262
  const projectRoot = findProjectRoot();
257
263
  if (!projectRoot) {
258
264
  console.log(chalk3.red("\u274C Not in a Cure Kode project."));
@@ -264,11 +270,93 @@ async function updateClaudeMdCommand() {
264
270
  console.log(chalk3.red("\u274C Could not read project configuration."));
265
271
  return;
266
272
  }
273
+ const client = createApiClient(config);
274
+ if (action === "status") {
275
+ const spinner2 = ora2("Fetching production status...").start();
276
+ try {
277
+ const status = await client.getDeploymentStatus(config.siteId);
278
+ spinner2.stop();
279
+ console.log();
280
+ console.log(chalk3.bold("Production Status"));
281
+ console.log();
282
+ if (status.productionEnabled) {
283
+ console.log(chalk3.green(" \u25CF Produksjon er aktivert"));
284
+ if (status.productionDomain) {
285
+ console.log(chalk3.dim(` Domain: ${status.productionDomain}`));
286
+ }
287
+ if (status.production.lastSuccessful) {
288
+ console.log(
289
+ chalk3.dim(
290
+ ` Siste deploy: v${status.production.lastSuccessful.version}`
291
+ )
292
+ );
293
+ }
294
+ } else {
295
+ console.log(chalk3.gray(" \u25CB Produksjon er deaktivert"));
296
+ console.log(chalk3.dim(" Kun staging er aktiv"));
297
+ }
298
+ console.log();
299
+ } catch (error) {
300
+ spinner2.fail("Failed to fetch status");
301
+ console.error(chalk3.red("\nError:"), error.message || error);
302
+ }
303
+ return;
304
+ }
305
+ const spinner = ora2(
306
+ action === "enable" ? "Aktiverer produksjon..." : "Deaktiverer produksjon..."
307
+ ).start();
308
+ try {
309
+ const result = await client.setProductionEnabled(
310
+ config.siteId,
311
+ action === "enable",
312
+ options?.domain
313
+ );
314
+ spinner.stop();
315
+ console.log();
316
+ if (action === "enable") {
317
+ console.log(chalk3.green("\u2713 Produksjon er n\xE5 aktivert"));
318
+ if (result.productionDomain) {
319
+ console.log(chalk3.dim(` Domain: ${result.productionDomain}`));
320
+ }
321
+ 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"));
325
+ } else {
326
+ console.log(chalk3.yellow("\u2713 Produksjon er n\xE5 deaktivert"));
327
+ console.log(chalk3.dim(" Kun staging-milj\xF8et er aktivt."));
328
+ console.log(
329
+ chalk3.dim(" Produksjonsdomenet vil f\xE5 en tom script-respons.")
330
+ );
331
+ }
332
+ console.log();
333
+ } catch (error) {
334
+ spinner.fail(action === "enable" ? "Kunne ikke aktivere produksjon" : "Kunne ikke deaktivere produksjon");
335
+ console.error(chalk3.red("\nError:"), error.message || error);
336
+ }
337
+ }
338
+
339
+ // src/commands/update-claude-md.ts
340
+ import chalk4 from "chalk";
341
+ import { existsSync, readFileSync, writeFileSync } from "fs";
342
+ import { join } from "path";
343
+ async function updateClaudeMdCommand() {
344
+ const projectRoot = findProjectRoot();
345
+ if (!projectRoot) {
346
+ console.log(chalk4.red("\u274C Not in a Cure Kode project."));
347
+ console.log(chalk4.dim(' Run "kode init" first.'));
348
+ return;
349
+ }
350
+ const config = getProjectConfig(projectRoot);
351
+ if (!config) {
352
+ console.log(chalk4.red("\u274C Could not read project configuration."));
353
+ return;
354
+ }
267
355
  const claudeMdPath = join(projectRoot, "CLAUDE.md");
268
356
  const newKodeSection = generateClaudeMdMinimal(config.siteName, config.siteSlug);
269
357
  if (!existsSync(claudeMdPath)) {
270
358
  writeFileSync(claudeMdPath, newKodeSection);
271
- console.log(chalk3.green("\u2705 Created CLAUDE.md with Cure Kode section"));
359
+ console.log(chalk4.green("\u2705 Created CLAUDE.md with Cure Kode section"));
272
360
  return;
273
361
  }
274
362
  let content = readFileSync(claudeMdPath, "utf-8");
@@ -287,18 +375,18 @@ async function updateClaudeMdCommand() {
287
375
  content = newKodeSection + "---\n\n" + content;
288
376
  writeFileSync(claudeMdPath, content);
289
377
  if (removedCount > 1) {
290
- console.log(chalk3.green(`\u2705 Cleaned up ${removedCount} duplicate Kode sections and added fresh one`));
378
+ console.log(chalk4.green(`\u2705 Cleaned up ${removedCount} duplicate Kode sections and added fresh one`));
291
379
  } else if (removedCount === 1) {
292
- console.log(chalk3.green("\u2705 Updated Cure Kode section in CLAUDE.md"));
380
+ console.log(chalk4.green("\u2705 Updated Cure Kode section in CLAUDE.md"));
293
381
  } else {
294
- console.log(chalk3.green("\u2705 Added Cure Kode section to CLAUDE.md"));
382
+ console.log(chalk4.green("\u2705 Added Cure Kode section to CLAUDE.md"));
295
383
  }
296
384
  console.log();
297
- console.log(chalk3.dim("The Cure Kode section now includes:"));
298
- console.log(chalk3.dim(" \u2022 What is Cure Kode (internal tool explanation)"));
299
- console.log(chalk3.dim(" \u2022 CDN URL with script tag for Webflow"));
300
- console.log(chalk3.dim(" \u2022 Workflow steps"));
301
- console.log(chalk3.dim(" \u2022 Command reference"));
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"));
302
390
  }
303
391
 
304
392
  // src/cli.ts
@@ -336,12 +424,15 @@ program.command("context").description("View or edit project context for AI agen
336
424
  program.command("set <script>").description("Update script settings (scope, autoLoad)").option("--scope <scope>", "Set scope: global or page-specific").option("--auto-load", "Enable auto-loading").option("--no-auto-load", "Disable auto-loading").action((script, options) => {
337
425
  setCommand(script, options);
338
426
  });
427
+ program.command("production <action>").description("Enable or disable production environment (v2.3)").option("-d, --domain <domain>", "Set production domain when enabling").action((action, options) => {
428
+ productionCommand(action, options);
429
+ });
339
430
  program.command("update-claude-md").alias("ucm").description("Add or update Cure Kode section in CLAUDE.md").action(() => {
340
431
  updateClaudeMdCommand();
341
432
  });
342
433
  program.showHelpAfterError();
343
434
  console.log();
344
- console.log(chalk4.bold(" Cure Kode CLI"));
345
- console.log(chalk4.dim(" Manage JS/CSS for Webflow sites"));
435
+ console.log(chalk5.bold(" Cure Kode CLI"));
436
+ console.log(chalk5.dim(" Manage JS/CSS for Webflow sites"));
346
437
  console.log();
347
438
  program.parse();
package/dist/index.d.ts CHANGED
@@ -146,6 +146,8 @@ declare class KodeApiClient {
146
146
  deploy(siteId: string, environment?: 'staging' | 'production'): Promise<CdnDeployment>;
147
147
  promoteToProduction(siteId: string, stagingDeploymentId?: string): Promise<CdnDeployment>;
148
148
  getDeploymentStatus(siteId: string): Promise<{
149
+ productionEnabled: boolean;
150
+ productionDomain: string | null;
149
151
  staging: {
150
152
  latest: CdnDeployment | null;
151
153
  lastSuccessful: any;
@@ -156,6 +158,11 @@ declare class KodeApiClient {
156
158
  };
157
159
  canPromote: boolean;
158
160
  }>;
161
+ setProductionEnabled(siteId: string, enabled: boolean, productionDomain?: string): Promise<{
162
+ success: boolean;
163
+ productionEnabled: boolean;
164
+ productionDomain: string | null;
165
+ }>;
159
166
  fetchHtml(url: string): Promise<ParsedHtmlResult>;
160
167
  }
161
168
  /**
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  updateScriptPurpose,
28
28
  watchCommand,
29
29
  writeContext
30
- } from "./chunk-KAYIHS2T.js";
30
+ } from "./chunk-HKPZVOMY.js";
31
31
  export {
32
32
  KodeApiClient,
33
33
  KodeApiError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curenorway/kode-cli",
3
- "version": "1.7.1",
3
+ "version": "1.9.0",
4
4
  "description": "CLI tool for Cure Kode - manage JS/CSS scripts for Webflow sites",
5
5
  "type": "module",
6
6
  "bin": {