@curenorway/kode-cli 1.3.8 → 1.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.
@@ -10,6 +10,7 @@ var globalConfig = new Conf({
10
10
  });
11
11
  var PROJECT_CONFIG_DIR = ".cure-kode";
12
12
  var PROJECT_CONFIG_FILE = "config.json";
13
+ var DEFAULT_SCRIPTS_DIR = ".cure-kode-scripts";
13
14
  function findProjectRoot(startDir = process.cwd()) {
14
15
  let currentDir = startDir;
15
16
  while (currentDir !== dirname(currentDir)) {
@@ -51,7 +52,7 @@ function setGlobalConfig(key, value) {
51
52
  globalConfig.set(key, value);
52
53
  }
53
54
  function getScriptsDir(projectRoot, projectConfig) {
54
- return join(projectRoot, projectConfig?.scriptsDir || "scripts");
55
+ return join(projectRoot, projectConfig?.scriptsDir || DEFAULT_SCRIPTS_DIR);
55
56
  }
56
57
 
57
58
  // src/lib/context.ts
@@ -377,7 +378,7 @@ Cure Kode is an **internal CDN tool by Cure Norway** for managing JavaScript/CSS
377
378
  **IMPORTANT**: This is NOT a public product. Do NOT search the web for documentation - all info is here.
378
379
 
379
380
  **How it works**:
380
- 1. Scripts live locally in the \`scripts/\` folder (or configured folder)
381
+ 1. Scripts live locally in the \`.cure-kode-scripts/\` folder
381
382
  2. \`kode push\` uploads scripts to Cure's CDN at \`app.cure.no\`
382
383
  3. \`kode deploy\` makes them live on staging or production
383
384
  4. A single \`init.js\` script tag in Webflow loads all your scripts
@@ -436,12 +437,20 @@ Update context.md after your session with discoveries and changes.
436
437
 
437
438
  ### MCP Tools
438
439
 
439
- The \`.mcp.json\` file enables AI tools for page assignment, HTML caching, and context updates.
440
- Restart Claude Code after \`kode init\` to load the MCP server.
440
+ The \`.mcp.json\` file enables AI tools:
441
+
442
+ **cure-kode MCP**: Script management, HTML analysis, deployment
443
+ - \`kode_update_script\`, \`kode_deploy\`, \`kode_fetch_html_smart\`, etc.
444
+
445
+ **webflow MCP** (if configured): Webflow Designer API access
446
+ - Modify site structure, CMS collections, pages, and more
447
+ - Requires the Webflow MCP Bridge App running in Designer (press E \u2192 Apps)
448
+
449
+ Restart Claude Code after \`kode init\` to load MCP servers.
441
450
 
442
451
  `;
443
452
  }
444
- function generateClaudeMd(siteName, scriptsDir = "scripts", siteSlug) {
453
+ function generateClaudeMd(siteName, scriptsDir = ".cure-kode-scripts", siteSlug) {
445
454
  const slug = siteSlug || "your-site-slug";
446
455
  return `# Cure Kode Project: ${siteName}
447
456
 
@@ -628,9 +637,41 @@ If using the Kode MCP server, these tools are available:
628
637
  ### MCP Setup
629
638
 
630
639
  The \`.mcp.json\` file (created by \`kode init\`) enables MCP tools automatically.
631
- **Restart Claude Code** after init to load the MCP server.
640
+ **Restart Claude Code** after init to load the MCP servers.
641
+
642
+ ## Webflow MCP (Optional)
643
+
644
+ If configured during \`kode init\`, you also have access to the official Webflow MCP server.
645
+ This enables AI agents to interact directly with the Webflow Designer API.
646
+
647
+ ### Webflow MCP Tools
648
+
649
+ - **Site & Page Management**: Create, read, update pages and site settings
650
+ - **CMS Collections**: Manage collection schemas and items
651
+ - **Components**: Work with reusable components
652
+ - **Assets**: Upload and manage media assets
653
+ - **Forms**: Configure form settings
654
+
655
+ ### Using Webflow MCP
656
+
657
+ 1. **Start the Bridge App**: Open your site in Webflow Designer, press E, launch "Webflow MCP Bridge App"
658
+ 2. **OAuth Mode**: If using OAuth, you'll be prompted to authorize on first use
659
+ 3. **Token Mode**: Works immediately if a token was configured
660
+
661
+ ### Example Prompts with Webflow MCP
662
+
663
+ - "Add a new blog post to the CMS collection"
664
+ - "Create a new page at /about with a hero section"
665
+ - "Update the navigation menu to include a new link"
666
+ - "Show me all published pages on this site"
667
+
668
+ ### Combined Workflows
669
+
670
+ Use both MCPs together for powerful workflows:
671
+ 1. Use **webflow MCP** to understand site structure and create pages
672
+ 2. Use **cure-kode MCP** to add custom JavaScript behavior to those pages
673
+ 3. Deploy scripts with \`kode_deploy\`, then verify in Webflow Designer
632
674
 
633
- MCP tools provide richer functionality than CLI alone (page assignment, context updates, HTML caching).
634
675
  `;
635
676
  }
636
677
 
@@ -653,77 +694,56 @@ async function initCommand(options) {
653
694
  return;
654
695
  }
655
696
  console.log(chalk.bold("\n\u{1F680} Cure Kode Setup\n"));
656
- const answers = await prompt([
697
+ const { apiKey } = await prompt([
657
698
  {
658
699
  type: "password",
659
700
  name: "apiKey",
660
701
  message: "API Key (from Cure App \u2192 Tools \u2192 Kode \u2192 API Keys):",
661
702
  initial: options.apiKey,
662
703
  validate: (value) => value.length > 0 ? true : "API key is required"
663
- },
664
- {
665
- type: "input",
666
- name: "siteSlug",
667
- message: 'Site slug (e.g., "swipp"):',
668
- initial: options.siteSlug,
669
- validate: (value) => /^[a-z0-9-]+$/.test(value) ? true : "Slug must be lowercase letters, numbers, and hyphens only"
670
- },
671
- {
672
- type: "input",
673
- name: "siteName",
674
- message: "Site name (for display):",
675
- initial: (answers2) => answers2.siteSlug ? answers2.siteSlug.charAt(0).toUpperCase() + answers2.siteSlug.slice(1) : ""
676
- },
677
- {
678
- type: "input",
679
- name: "siteId",
680
- message: "Site ID (UUID from Cure App):",
681
- validate: (value) => /^[0-9a-f-]{36}$/.test(value) ? true : "Must be a valid UUID"
682
- },
683
- {
684
- type: "input",
685
- name: "scriptsDir",
686
- message: "Scripts directory:",
687
- initial: "scripts"
688
- },
689
- {
690
- type: "select",
691
- name: "environment",
692
- message: "Default environment:",
693
- choices: [
694
- { name: "staging", message: "Staging (recommended for development)" },
695
- { name: "production", message: "Production" }
696
- ],
697
- initial: 0
698
704
  }
699
705
  ]);
700
- const spinner = ora("Validating configuration...").start();
706
+ const spinner = ora("Validating API key...").start();
701
707
  try {
702
- const response = await fetch(
703
- `https://app.cure.no/api/cdn/sites/${answers.siteId}`,
704
- {
705
- headers: {
706
- "X-API-Key": answers.apiKey
707
- }
708
+ const response = await fetch("https://app.cure.no/api/cdn/sites", {
709
+ headers: {
710
+ "X-API-Key": apiKey
708
711
  }
709
- );
712
+ });
710
713
  if (!response.ok) {
711
- spinner.fail("Invalid API key or site ID");
712
- console.log(chalk.red("\nCould not validate your credentials."));
713
- console.log(chalk.dim("Make sure your API key has access to this site."));
714
+ spinner.fail("Invalid API key");
715
+ console.log(chalk.red("\nCould not validate your API key."));
716
+ console.log(chalk.dim("Make sure you copied the full key from Cure App \u2192 Tools \u2192 Kode \u2192 API Keys."));
714
717
  return;
715
718
  }
716
- spinner.succeed("Configuration validated");
719
+ const sites = await response.json();
720
+ if (!sites || sites.length === 0) {
721
+ spinner.fail("No site found for this API key");
722
+ console.log(chalk.red("\nThis API key is not associated with any site."));
723
+ return;
724
+ }
725
+ const site = sites[0];
726
+ spinner.succeed("API key validated");
727
+ console.log();
728
+ console.log(chalk.bold(" Connected to: ") + chalk.cyan(site.name) + chalk.dim(` (${site.slug})`));
729
+ if (site.production_domain || site.domain) {
730
+ console.log(chalk.dim(" Production: ") + (site.production_domain || site.domain));
731
+ }
732
+ if (site.staging_domain) {
733
+ console.log(chalk.dim(" Staging: ") + site.staging_domain);
734
+ }
735
+ console.log();
717
736
  const config = {
718
- siteId: answers.siteId,
719
- siteSlug: answers.siteSlug,
720
- siteName: answers.siteName,
721
- apiKey: answers.apiKey,
722
- scriptsDir: answers.scriptsDir,
723
- environment: answers.environment
737
+ siteId: site.id,
738
+ siteSlug: site.slug,
739
+ siteName: site.name,
740
+ apiKey,
741
+ scriptsDir: DEFAULT_SCRIPTS_DIR,
742
+ environment: "staging"
743
+ // Always default to staging
724
744
  };
725
745
  saveProjectConfig(config, cwd);
726
- const scriptsPath = join3(cwd, answers.scriptsDir);
746
+ const scriptsPath = join3(cwd, DEFAULT_SCRIPTS_DIR);
727
747
  if (!existsSync3(scriptsPath)) {
728
748
  mkdirSync2(scriptsPath, { recursive: true });
729
749
  }
@@ -746,25 +766,74 @@ config.json
746
766
  args: ["-y", "@curenorway/kode-mcp"],
747
767
  env: {}
748
768
  };
769
+ let webflowToken = site.webflow_token;
770
+ let webflowMcpMethod = null;
771
+ spinner.stop();
772
+ if (webflowToken) {
773
+ console.log(chalk.green("\u2713 Webflow API token found in site settings"));
774
+ webflowMcpMethod = "token";
775
+ } else {
776
+ console.log(chalk.yellow("\n\u{1F4E6} Webflow MCP Setup"));
777
+ console.log(chalk.dim(" The Webflow MCP enables AI agents to interact with the Webflow Designer."));
778
+ console.log();
779
+ const { webflowSetup } = await prompt([
780
+ {
781
+ type: "select",
782
+ name: "webflowSetup",
783
+ message: "How would you like to set up Webflow MCP?",
784
+ choices: [
785
+ {
786
+ name: "oauth",
787
+ message: "Use OAuth (recommended) - authorize via browser when needed"
788
+ },
789
+ {
790
+ name: "token",
791
+ message: "Provide API token - use a Webflow API token"
792
+ },
793
+ {
794
+ name: "skip",
795
+ message: "Skip - set up Webflow MCP later"
796
+ }
797
+ ],
798
+ initial: 0
799
+ }
800
+ ]);
801
+ webflowMcpMethod = webflowSetup;
802
+ if (webflowSetup === "token") {
803
+ const { webflowToken: providedToken } = await prompt([
804
+ {
805
+ type: "password",
806
+ name: "webflowToken",
807
+ message: "Webflow API Token (from webflow.com/dashboard/account/integrations):",
808
+ validate: (value) => value.length > 0 ? true : "Token is required"
809
+ }
810
+ ]);
811
+ webflowToken = providedToken;
812
+ }
813
+ }
814
+ if (webflowMcpMethod === "token" && webflowToken) {
815
+ mcpConfig.mcpServers["webflow"] = {
816
+ command: "npx",
817
+ args: ["-y", "webflow-mcp-server@latest"],
818
+ env: {
819
+ WEBFLOW_TOKEN: webflowToken
820
+ }
821
+ };
822
+ } else if (webflowMcpMethod === "oauth") {
823
+ mcpConfig.mcpServers["webflow"] = {
824
+ url: "https://mcp.webflow.com/sse"
825
+ };
826
+ }
827
+ spinner.start("Generating AI context files...");
749
828
  writeFileSync3(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
750
829
  spinner.text = "Generating AI context files...";
751
830
  spinner.start();
752
831
  let scripts = [];
753
- let siteInfo;
754
832
  try {
755
- const siteResponse = await fetch(
756
- `https://app.cure.no/api/cdn/sites/${answers.siteId}`,
757
- {
758
- headers: { "X-API-Key": answers.apiKey }
759
- }
760
- );
761
- if (siteResponse.ok) {
762
- siteInfo = await siteResponse.json();
763
- }
764
833
  const scriptsResponse = await fetch(
765
- `https://app.cure.no/api/cdn/sites/${answers.siteId}/scripts`,
834
+ `https://app.cure.no/api/cdn/sites/${site.id}/scripts`,
766
835
  {
767
- headers: { "X-API-Key": answers.apiKey }
836
+ headers: { "X-API-Key": apiKey }
768
837
  }
769
838
  );
770
839
  if (scriptsResponse.ok) {
@@ -773,7 +842,7 @@ config.json
773
842
  } catch {
774
843
  }
775
844
  const claudeMdPath = join3(cwd, "CLAUDE.md");
776
- const claudeMdContentFull = generateClaudeMd(config.siteName, config.scriptsDir || "scripts", config.siteSlug);
845
+ const claudeMdContentFull = generateClaudeMd(config.siteName, DEFAULT_SCRIPTS_DIR, config.siteSlug);
777
846
  const claudeMdContentMinimal = generateClaudeMdMinimal(config.siteName, config.siteSlug);
778
847
  let claudeMdAction = "created";
779
848
  if (existsSync3(claudeMdPath)) {
@@ -848,6 +917,11 @@ config.json
848
917
  } else {
849
918
  writeFileSync3(claudeMdPath, claudeMdContentFull);
850
919
  }
920
+ const siteInfo = {
921
+ domain: site.domain,
922
+ staging_domain: site.staging_domain,
923
+ production_domain: site.production_domain
924
+ };
851
925
  const contextMdContent = generateInitialContext(config, scripts, siteInfo);
852
926
  writeFileSync3(join3(cwd, ".cure-kode", "context.md"), contextMdContent);
853
927
  spinner.succeed("AI context files generated");
@@ -855,35 +929,61 @@ config.json
855
929
  console.log(chalk.dim("Project structure:"));
856
930
  console.log(chalk.dim(` ${cwd}/`));
857
931
  if (claudeMdAction === "created") {
858
- console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (AI agent instructions)`));
932
+ console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (AI agent instructions)`));
859
933
  } else if (claudeMdAction === "prepended") {
860
- console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (Kode section prepended)`));
934
+ console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (Kode section prepended)`));
861
935
  } else if (claudeMdAction === "updated") {
862
- console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (Kode section updated)`));
936
+ console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (Kode section updated)`));
863
937
  } else if (claudeMdAction === "separate") {
864
- console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (existing - unchanged)`));
865
- console.log(chalk.dim(` \u251C\u2500\u2500 KODE.md (Kode AI instructions)`));
938
+ console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (existing - unchanged)`));
939
+ console.log(chalk.dim(` \u251C\u2500\u2500 KODE.md (Kode AI instructions)`));
866
940
  } else {
867
- console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (existing - unchanged)`));
941
+ console.log(chalk.dim(` \u251C\u2500\u2500 CLAUDE.md (existing - unchanged)`));
868
942
  }
869
- console.log(chalk.dim(` \u251C\u2500\u2500 .mcp.json (MCP server config)`));
943
+ console.log(chalk.dim(` \u251C\u2500\u2500 .mcp.json (MCP server config)`));
870
944
  console.log(chalk.dim(` \u251C\u2500\u2500 .cure-kode/`));
871
- console.log(chalk.dim(` \u2502 \u251C\u2500\u2500 config.json (your configuration)`));
872
- console.log(chalk.dim(` \u2502 \u251C\u2500\u2500 context.md (dynamic AI context)`));
873
- console.log(chalk.dim(` \u2502 \u2514\u2500\u2500 .gitignore (protects API key)`));
874
- console.log(chalk.dim(` \u2514\u2500\u2500 ${answers.scriptsDir}/`));
875
- console.log(chalk.dim(` \u2514\u2500\u2500 (your scripts will be here)`));
945
+ console.log(chalk.dim(` \u2502 \u251C\u2500\u2500 config.json (your configuration)`));
946
+ console.log(chalk.dim(` \u2502 \u251C\u2500\u2500 context.md (dynamic AI context)`));
947
+ console.log(chalk.dim(` \u2502 \u2514\u2500\u2500 .gitignore (protects API key)`));
948
+ console.log(chalk.dim(` \u2514\u2500\u2500 ${DEFAULT_SCRIPTS_DIR}/`));
949
+ console.log(chalk.dim(` \u2514\u2500\u2500 (your scripts here)`));
950
+ console.log(chalk.bold("\nWebflow Setup:"));
951
+ console.log(chalk.dim(" Add this to your Webflow <head> code:"));
952
+ console.log(chalk.cyan(` <script defer src="https://app.cure.no/api/cdn/${site.slug}/init.js"></script>`));
876
953
  console.log(chalk.bold("\nNext steps:"));
877
954
  console.log(chalk.cyan(" 1. kode pull ") + chalk.dim("Download existing scripts"));
878
955
  console.log(chalk.cyan(" 2. kode watch ") + chalk.dim("Watch for changes and auto-push"));
879
- console.log(chalk.cyan(" 3. kode deploy ") + chalk.dim("Deploy to staging/production"));
956
+ console.log(chalk.cyan(" 3. kode deploy ") + chalk.dim("Deploy to staging"));
880
957
  console.log(chalk.bold("\n\u{1F4A1} MCP Tools:"));
881
- console.log(chalk.dim(" Restart Claude Code, then use /mcp to approve the cure-kode server."));
882
- console.log(chalk.dim(" This enables AI tools like kode_update_script, kode_assign_script_to_page, etc."));
958
+ console.log(chalk.dim(" Restart Claude Code, then use /mcp to approve the MCP servers."));
959
+ console.log();
960
+ console.log(chalk.dim(" cure-kode:"));
961
+ console.log(chalk.dim(" kode_update_script, kode_fetch_html_smart, kode_deploy, etc."));
962
+ if (webflowMcpMethod === "token") {
963
+ console.log();
964
+ console.log(chalk.dim(" webflow (token mode):"));
965
+ console.log(chalk.dim(" Interact with Webflow Designer API, modify site structure, CMS, etc."));
966
+ } else if (webflowMcpMethod === "oauth") {
967
+ console.log();
968
+ console.log(chalk.dim(" webflow (OAuth mode):"));
969
+ console.log(chalk.dim(" You'll be prompted to authorize via browser on first use."));
970
+ console.log(chalk.dim(" Requires Node.js 22.3.0+ for remote MCP."));
971
+ } else {
972
+ console.log();
973
+ console.log(chalk.dim(" webflow: ") + chalk.yellow("Not configured"));
974
+ console.log(chalk.dim(" Run `kode init --force` to set up Webflow MCP later."));
975
+ }
883
976
  if (claudeMdAction === "separate") {
884
977
  console.log(chalk.yellow("\n\u{1F4A1} Tip: Add this line to your CLAUDE.md to reference Kode instructions:"));
885
978
  console.log(chalk.dim(" See KODE.md for Cure Kode CDN management instructions."));
886
979
  }
980
+ if (webflowMcpMethod === "token" || webflowMcpMethod === "oauth") {
981
+ console.log(chalk.bold("\n\u{1F517} Webflow Designer Integration:"));
982
+ console.log(chalk.dim(" 1. Open your site in Webflow Designer"));
983
+ console.log(chalk.dim(" 2. Press E to open Apps panel"));
984
+ console.log(chalk.dim(' 3. Launch the "Webflow MCP Bridge App"'));
985
+ console.log(chalk.dim(" 4. AI agents can now interact with the Designer"));
986
+ }
887
987
  } catch (error) {
888
988
  spinner.fail("Initialization failed");
889
989
  console.error(chalk.red("\nError:"), error);
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  readPageContext,
16
16
  statusCommand,
17
17
  watchCommand
18
- } from "./chunk-EBZNCW34.js";
18
+ } from "./chunk-XBDIENFY.js";
19
19
 
20
20
  // src/cli.ts
21
21
  import { Command } from "commander";
package/dist/index.d.ts CHANGED
@@ -165,10 +165,14 @@ declare function createApiClient(config: ProjectConfig): KodeApiClient;
165
165
 
166
166
  /**
167
167
  * Initialize Cure Kode in current directory
168
+ *
169
+ * Simplified flow:
170
+ * 1. Ask for API key
171
+ * 2. Validate key and fetch site info from API
172
+ * 3. Auto-configure everything else
168
173
  */
169
174
  declare function initCommand(options: {
170
175
  apiKey?: string;
171
- siteSlug?: string;
172
176
  force?: boolean;
173
177
  }): Promise<void>;
174
178
 
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  updateScriptPurpose,
28
28
  watchCommand,
29
29
  writeContext
30
- } from "./chunk-EBZNCW34.js";
30
+ } from "./chunk-XBDIENFY.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.3.8",
3
+ "version": "1.5.0",
4
4
  "description": "CLI tool for Cure Kode - manage JS/CSS scripts for Webflow sites",
5
5
  "type": "module",
6
6
  "bin": {