@curenorway/kode-cli 1.14.0 → 1.15.1

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 CHANGED
@@ -4,6 +4,7 @@ Command-line tool for managing JavaScript and CSS scripts for Webflow sites via
4
4
 
5
5
  ## Features
6
6
 
7
+ - **Webflow Auto-Inject** - Automatically injects init.js into Webflow `<head>` via Custom Code API during init
7
8
  - **Pull/Push scripts** - Sync scripts between local files and remote CDN with conflict detection
8
9
  - **Sync awareness** - Staleness indicators show when local state is out of date
9
10
  - **Deploy dry-run** - Preview what will be deployed before executing
@@ -61,6 +62,8 @@ Creates:
61
62
  - `CLAUDE.md` - Reference to KODE.md (prepended, never overwrites existing content)
62
63
  - `.mcp.json` - MCP server configuration (cure-kode, webflow, playwright)
63
64
 
65
+ Automatically registers and injects init.js into the Webflow site's `<head>` via the Custom Code API. If injection fails, it falls back to showing the manual embed code.
66
+
64
67
  ### `kode pull`
65
68
 
66
69
  Download scripts from remote to your local scripts directory.
@@ -215,6 +218,25 @@ kode sync # Sync both directions
215
218
  kode sync --dry-run # Preview what would change
216
219
  ```
217
220
 
221
+ ### `kode webflow`
222
+
223
+ Manage the Webflow Custom Code integration. During `kode init`, the init.js script is automatically injected into Webflow's `<head>` via the Custom Code API. These commands let you manage it afterwards.
224
+
225
+ ```bash
226
+ kode webflow status # Check if init.js is injected (default)
227
+ kode webflow inject # Inject init.js into Webflow <head>
228
+ kode webflow inject --location footer # Inject into footer instead
229
+ kode webflow remove # Remove init.js from Webflow
230
+ ```
231
+
232
+ **How it works:**
233
+ 1. Registers init.js as a hosted script with Webflow
234
+ 2. Applies it to the site's `<head>` via the Custom Code API
235
+ 3. Preserves any other scripts already managed by the API
236
+ 4. Does not affect manually-added code in the Webflow Designer
237
+
238
+ **Note:** After inject/remove, you must **publish** the Webflow site for changes to take effect.
239
+
218
240
  ## Configuration
219
241
 
220
242
  ### Project Config (`.cure-kode/config.json`)
@@ -1201,7 +1201,7 @@ Option C: Full rollback
1201
1201
  }
1202
1202
 
1203
1203
  // src/version.ts
1204
- var CLI_VERSION = "1.12.1";
1204
+ var CLI_VERSION = "1.13.0";
1205
1205
 
1206
1206
  // src/commands/init.ts
1207
1207
  import chalk from "chalk";
@@ -1451,6 +1451,12 @@ function showBanner() {
1451
1451
  console.log(chalk.dim(" JS/CSS delivery for Webflow sites"));
1452
1452
  console.log();
1453
1453
  }
1454
+ function showCompactBanner() {
1455
+ console.log();
1456
+ console.log(chalk.bold(" Cure Kode CLI") + chalk.dim(` v${CLI_VERSION}`));
1457
+ console.log(chalk.dim(" Manage JS/CSS for Webflow sites"));
1458
+ console.log();
1459
+ }
1454
1460
  async function initCommand(options) {
1455
1461
  const cwd = process.cwd();
1456
1462
  showBanner();
@@ -1543,13 +1549,34 @@ async function initCommand(options) {
1543
1549
  }
1544
1550
  console.log(chalk.dim(" Webflow: ") + chalk.green("\u2713 Konfigurert"));
1545
1551
  console.log();
1552
+ let projectCtx = null;
1553
+ try {
1554
+ const projResponse = await fetch(`https://app.cure.no/api/cdn/sites/${site.id}/project`, {
1555
+ headers: { "X-API-Key": apiKey }
1556
+ });
1557
+ if (projResponse.ok) {
1558
+ projectCtx = await projResponse.json();
1559
+ }
1560
+ } catch {
1561
+ }
1562
+ if (projectCtx?.project) {
1563
+ console.log(chalk.dim(" Prosjekt: ") + chalk.cyan(projectCtx.project.name));
1564
+ if (projectCtx.github_repo) {
1565
+ console.log(chalk.dim(" GitHub: ") + chalk.cyan(projectCtx.github_repo) + chalk.dim(` (${projectCtx.github_branch})`));
1566
+ }
1567
+ if (projectCtx.mcp_servers.length > 0) {
1568
+ console.log(chalk.dim(" MCP-servere: ") + chalk.cyan(`${projectCtx.mcp_servers.length} fra prosjekt`));
1569
+ }
1570
+ console.log();
1571
+ }
1546
1572
  const config = {
1547
1573
  siteId: site.id,
1548
1574
  siteSlug: site.slug,
1549
1575
  siteName: site.name,
1550
1576
  apiKey,
1551
1577
  scriptsDir: DEFAULT_SCRIPTS_DIR,
1552
- environment: "staging"
1578
+ environment: "staging",
1579
+ ...projectCtx?.project && { projectId: projectCtx.project.id }
1553
1580
  };
1554
1581
  spinner.start("Oppretter prosjektstruktur...");
1555
1582
  saveProjectConfig(config, cwd);
@@ -1591,8 +1618,42 @@ config.json
1591
1618
  command: "npx",
1592
1619
  args: ["-y", "@playwright/mcp@^0.0.21"]
1593
1620
  };
1621
+ const secretWarnings = [];
1622
+ if (projectCtx?.mcp_servers && projectCtx.mcp_servers.length > 0) {
1623
+ for (const server of projectCtx.mcp_servers) {
1624
+ const serverConfig = { type: server.transport };
1625
+ if (server.transport === "sse" && server.url) {
1626
+ serverConfig.url = server.url;
1627
+ } else if (server.transport === "stdio") {
1628
+ if (server.command) serverConfig.command = server.command;
1629
+ if (server.args) serverConfig.args = server.args;
1630
+ }
1631
+ if (server.env_vars && Object.keys(server.env_vars).length > 0) {
1632
+ const env = {};
1633
+ for (const [key, value] of Object.entries(server.env_vars)) {
1634
+ if (value.startsWith("$SECRET:")) {
1635
+ const secretKey = value.replace("$SECRET:", "");
1636
+ env[key] = `<SET_ME:${secretKey}>`;
1637
+ secretWarnings.push(`${server.name}: ${key} \u2192 hent fra hemmelighetsvelvet (${secretKey})`);
1638
+ } else {
1639
+ env[key] = value;
1640
+ }
1641
+ }
1642
+ serverConfig.env = env;
1643
+ }
1644
+ mcpConfig.mcpServers[server.name] = serverConfig;
1645
+ }
1646
+ }
1594
1647
  writeFileSync4(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
1595
1648
  spinner.succeed("MCP-servere konfigurert");
1649
+ if (secretWarnings.length > 0) {
1650
+ console.log();
1651
+ console.log(chalk.yellow(" \u26A0\uFE0F Noen MCP-servere krever hemmeligheter som m\xE5 settes lokalt:"));
1652
+ for (const warning of secretWarnings) {
1653
+ console.log(chalk.dim(` \u2022 ${warning}`));
1654
+ }
1655
+ console.log(chalk.dim(" Rediger .mcp.json og erstatt <SET_ME:...> med faktiske verdier."));
1656
+ }
1596
1657
  spinner.start("Genererer AI-dokumentasjon...");
1597
1658
  let scripts = [];
1598
1659
  let pages = [];
@@ -1660,11 +1721,39 @@ config.json
1660
1721
  console.log(chalk.dim(` \u2514\u2500\u2500 ${DEFAULT_SCRIPTS_DIR}/`));
1661
1722
  console.log(chalk.dim(" \u2514\u2500\u2500 (skriptene dine her)"));
1662
1723
  console.log();
1663
- console.log(chalk.bold(" Webflow-oppsett:"));
1664
- console.log(chalk.dim(" Legg til dette i Webflow <head>:"));
1665
- console.log();
1666
- console.log(chalk.cyan(` <script defer src="https://app.cure.no/api/cdn/${site.slug}/init.js"></script>`));
1667
- console.log();
1724
+ spinner.start("Legger til init.js i Webflow...");
1725
+ let webflowInjected = false;
1726
+ try {
1727
+ const injectResponse = await fetch(`https://app.cure.no/api/cdn/sites/${site.id}/webflow/custom-code`, {
1728
+ method: "POST",
1729
+ headers: {
1730
+ "Content-Type": "application/json",
1731
+ "X-API-Key": apiKey
1732
+ },
1733
+ body: JSON.stringify({ location: "header" })
1734
+ });
1735
+ if (injectResponse.ok) {
1736
+ spinner.succeed("init.js lagt til i Webflow <head>");
1737
+ webflowInjected = true;
1738
+ } else {
1739
+ const errData = await injectResponse.json().catch(() => ({}));
1740
+ spinner.warn("Kunne ikke legge til init.js automatisk");
1741
+ console.log(chalk.dim(` ${errData.error || "Ukjent feil"}`));
1742
+ }
1743
+ } catch {
1744
+ spinner.warn("Kunne ikke koble til Webflow API");
1745
+ }
1746
+ if (webflowInjected) {
1747
+ console.log(chalk.yellow(" OBS: Publiser Webflow-nettstedet for at skriptet skal vises."));
1748
+ console.log();
1749
+ } else {
1750
+ console.log(chalk.dim(" Legg til dette i Webflow <head> manuelt:"));
1751
+ console.log();
1752
+ console.log(chalk.cyan(` <script defer src="https://app.cure.no/api/cdn/${site.slug}/init.js"></script>`));
1753
+ console.log();
1754
+ console.log(chalk.dim(" Eller kjor ") + chalk.cyan("kode webflow inject") + chalk.dim(" nar du er klar."));
1755
+ console.log();
1756
+ }
1668
1757
  console.log(chalk.bold(" Neste steg:"));
1669
1758
  console.log();
1670
1759
  console.log(chalk.cyan(" 1. kode pull ") + chalk.dim("Last ned eksisterende skript"));
@@ -1959,6 +2048,21 @@ var KodeApiClient = class {
1959
2048
  body: JSON.stringify({ siteId })
1960
2049
  });
1961
2050
  }
2051
+ // Webflow Custom Code injection
2052
+ async getWebflowCustomCodeStatus(siteId) {
2053
+ return this.request(`/api/cdn/sites/${siteId}/webflow/custom-code`);
2054
+ }
2055
+ async injectWebflowCustomCode(siteId, location = "header") {
2056
+ return this.request(`/api/cdn/sites/${siteId}/webflow/custom-code`, {
2057
+ method: "POST",
2058
+ body: JSON.stringify({ location })
2059
+ });
2060
+ }
2061
+ async removeWebflowCustomCode(siteId) {
2062
+ return this.request(`/api/cdn/sites/${siteId}/webflow/custom-code`, {
2063
+ method: "DELETE"
2064
+ });
2065
+ }
1962
2066
  };
1963
2067
  function createApiClient(config) {
1964
2068
  return new KodeApiClient(config);
@@ -3631,6 +3735,154 @@ async function contextCommand(options) {
3631
3735
  console.log(chalk8.dim("Use --edit to open in editor, --refresh to update from server"));
3632
3736
  }
3633
3737
 
3738
+ // src/commands/sync-config.ts
3739
+ import chalk9 from "chalk";
3740
+ import ora8 from "ora";
3741
+ import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
3742
+ import { join as join11 } from "path";
3743
+ async function syncConfigCommand() {
3744
+ const cwd = process.cwd();
3745
+ const projectRoot = findProjectRoot(cwd);
3746
+ if (!projectRoot) {
3747
+ console.log(chalk9.red(" Feil: Cure Kode er ikke initialisert i denne mappen."));
3748
+ console.log(chalk9.dim(" Kj\xF8r ") + chalk9.cyan("kode init") + chalk9.dim(" f\xF8rst."));
3749
+ return;
3750
+ }
3751
+ const config = getProjectConfig(projectRoot);
3752
+ if (!config) {
3753
+ console.log(chalk9.red(" Feil: Kunne ikke lese prosjektkonfigurasjon."));
3754
+ return;
3755
+ }
3756
+ if (!config.projectId) {
3757
+ console.log(chalk9.yellow(" Nettstedet er ikke koblet til et prosjekt."));
3758
+ console.log(chalk9.dim(' Koble til via Curie: "Koble denne Kode-siten til prosjekt X"'));
3759
+ return;
3760
+ }
3761
+ const spinner = ora8("Henter prosjektkonfigurasjon...").start();
3762
+ try {
3763
+ const response = await fetch(`https://app.cure.no/api/cdn/sites/${config.siteId}/project`, {
3764
+ headers: { "X-API-Key": config.apiKey }
3765
+ });
3766
+ if (!response.ok) {
3767
+ spinner.fail("Kunne ikke hente prosjektkonfigurasjon");
3768
+ console.log(chalk9.dim(` HTTP ${response.status}: ${response.statusText}`));
3769
+ return;
3770
+ }
3771
+ const projectCtx = await response.json();
3772
+ spinner.succeed("Prosjektkonfigurasjon hentet");
3773
+ const mcpConfigPath = join11(projectRoot, ".mcp.json");
3774
+ let mcpConfig = {};
3775
+ if (existsSync11(mcpConfigPath)) {
3776
+ try {
3777
+ mcpConfig = JSON.parse(readFileSync11(mcpConfigPath, "utf-8"));
3778
+ } catch {
3779
+ }
3780
+ }
3781
+ mcpConfig.mcpServers = mcpConfig.mcpServers || {};
3782
+ const coreServers = /* @__PURE__ */ new Set(["cure-kode", "webflow", "playwright"]);
3783
+ const projectServerNames = new Set(projectCtx.mcp_servers.map((s) => s.name));
3784
+ const removed = [];
3785
+ for (const name of Object.keys(mcpConfig.mcpServers)) {
3786
+ if (!coreServers.has(name) && !projectServerNames.has(name)) {
3787
+ }
3788
+ }
3789
+ const added = [];
3790
+ const updated = [];
3791
+ const secretWarnings = [];
3792
+ for (const server of projectCtx.mcp_servers) {
3793
+ const existed = mcpConfig.mcpServers[server.name] !== void 0;
3794
+ const serverConfig = { type: server.transport };
3795
+ if (server.transport === "sse" && server.url) {
3796
+ serverConfig.url = server.url;
3797
+ } else if (server.transport === "stdio") {
3798
+ if (server.command) serverConfig.command = server.command;
3799
+ if (server.args) serverConfig.args = server.args;
3800
+ }
3801
+ if (server.env_vars && Object.keys(server.env_vars).length > 0) {
3802
+ const env = {};
3803
+ for (const [key, value] of Object.entries(server.env_vars)) {
3804
+ if (value.startsWith("$SECRET:")) {
3805
+ const secretKey = value.replace("$SECRET:", "");
3806
+ env[key] = `<SET_ME:${secretKey}>`;
3807
+ secretWarnings.push(`${server.name}: ${key} \u2192 ${secretKey}`);
3808
+ } else {
3809
+ env[key] = value;
3810
+ }
3811
+ }
3812
+ serverConfig.env = env;
3813
+ }
3814
+ mcpConfig.mcpServers[server.name] = serverConfig;
3815
+ if (existed) {
3816
+ updated.push(server.name);
3817
+ } else {
3818
+ added.push(server.name);
3819
+ }
3820
+ }
3821
+ writeFileSync8(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
3822
+ spinner.start("Oppdaterer dokumentasjon...");
3823
+ let scripts = [];
3824
+ let pages = [];
3825
+ let productionEnabled = false;
3826
+ try {
3827
+ const [scriptsRes, pagesRes, prodRes] = await Promise.all([
3828
+ fetch(`https://app.cure.no/api/cdn/sites/${config.siteId}/scripts`, {
3829
+ headers: { "X-API-Key": config.apiKey }
3830
+ }),
3831
+ fetch(`https://app.cure.no/api/cdn/sites/${config.siteId}/pages`, {
3832
+ headers: { "X-API-Key": config.apiKey }
3833
+ }),
3834
+ fetch(`https://app.cure.no/api/cdn/sites/${config.siteId}/production`, {
3835
+ headers: { "X-API-Key": config.apiKey }
3836
+ })
3837
+ ]);
3838
+ if (scriptsRes.ok) scripts = await scriptsRes.json();
3839
+ if (pagesRes.ok) pages = await pagesRes.json();
3840
+ if (prodRes.ok) {
3841
+ const prodData = await prodRes.json();
3842
+ productionEnabled = prodData.production_enabled ?? false;
3843
+ }
3844
+ } catch {
3845
+ }
3846
+ updateKodeDocs(
3847
+ projectRoot,
3848
+ config.siteName,
3849
+ config.siteSlug,
3850
+ scriptsToDocsFormat(scripts, pages),
3851
+ pagesToInfoFormat(pages),
3852
+ { productionEnabled }
3853
+ );
3854
+ spinner.succeed("Dokumentasjon oppdatert");
3855
+ console.log();
3856
+ if (projectCtx.project) {
3857
+ console.log(chalk9.bold(" Prosjekt: ") + chalk9.cyan(projectCtx.project.name));
3858
+ }
3859
+ if (added.length > 0 || updated.length > 0) {
3860
+ console.log(chalk9.bold(" MCP-servere:"));
3861
+ for (const name of added) {
3862
+ console.log(chalk9.green(` + ${name}`) + chalk9.dim(" (ny)"));
3863
+ }
3864
+ for (const name of updated) {
3865
+ console.log(chalk9.blue(` ~ ${name}`) + chalk9.dim(" (oppdatert)"));
3866
+ }
3867
+ } else if (projectCtx.mcp_servers.length === 0) {
3868
+ console.log(chalk9.dim(" Ingen prosjekt-MCP-servere konfigurert."));
3869
+ } else {
3870
+ console.log(chalk9.dim(" Ingen endringer i MCP-servere."));
3871
+ }
3872
+ if (secretWarnings.length > 0) {
3873
+ console.log();
3874
+ console.log(chalk9.yellow(" \u26A0\uFE0F Hemmeligheter som m\xE5 settes i .mcp.json:"));
3875
+ for (const warning of secretWarnings) {
3876
+ console.log(chalk9.dim(` \u2022 ${warning}`));
3877
+ }
3878
+ }
3879
+ console.log();
3880
+ } catch (error) {
3881
+ spinner.fail("Sync feilet");
3882
+ console.log(chalk9.red(` Feil: ${error.message}`));
3883
+ }
3884
+ }
3885
+
3634
3886
  export {
3635
3887
  findProjectRoot,
3636
3888
  getProjectConfig,
@@ -3654,6 +3906,7 @@ export {
3654
3906
  pagesToInfoFormat,
3655
3907
  updateClaudeMd,
3656
3908
  CLI_VERSION,
3909
+ showCompactBanner,
3657
3910
  initCommand,
3658
3911
  KodeApiError,
3659
3912
  KodeApiClient,
@@ -3667,5 +3920,6 @@ export {
3667
3920
  listCachedPages,
3668
3921
  htmlCommand,
3669
3922
  statusCommand,
3670
- contextCommand
3923
+ contextCommand,
3924
+ syncConfigCommand
3671
3925
  };
package/dist/cli.js CHANGED
@@ -17,11 +17,13 @@ import {
17
17
  pushCommand,
18
18
  readPageContext,
19
19
  scriptsToDocsFormat,
20
+ showCompactBanner,
20
21
  statusCommand,
22
+ syncConfigCommand,
21
23
  updateClaudeMd,
22
24
  updateKodeDocs,
23
25
  watchCommand
24
- } from "./chunk-I7TVOZJR.js";
26
+ } from "./chunk-E7DFDYOS.js";
25
27
 
26
28
  // src/cli.ts
27
29
  import { Command } from "commander";
@@ -1607,6 +1609,184 @@ function printResults(results) {
1607
1609
  console.log();
1608
1610
  }
1609
1611
 
1612
+ // src/commands/delete.ts
1613
+ import chalk10 from "chalk";
1614
+ async function deleteCommand(scriptSlug, options) {
1615
+ const projectRoot = findProjectRoot();
1616
+ if (!projectRoot) {
1617
+ console.log(chalk10.red("Feil: Ikke i et Cure Kode-prosjekt."));
1618
+ console.log(chalk10.dim('Kj\xF8r "kode init" f\xF8rst.'));
1619
+ return;
1620
+ }
1621
+ const config = getProjectConfig(projectRoot);
1622
+ if (!config) {
1623
+ console.log(chalk10.red("Feil: Ugyldig prosjektkonfigurasjon."));
1624
+ return;
1625
+ }
1626
+ try {
1627
+ const api = createApiClient(config);
1628
+ const scripts = await api.listScripts(config.siteId);
1629
+ const script = scripts.find(
1630
+ (s) => s.slug === scriptSlug || s.name === scriptSlug || s.id === scriptSlug
1631
+ );
1632
+ if (!script) {
1633
+ console.log(chalk10.red(`Feil: Finner ikke skript "${scriptSlug}"`));
1634
+ console.log(chalk10.dim("Tilgjengelige skript:"));
1635
+ for (const s of scripts) {
1636
+ console.log(chalk10.dim(` - ${s.slug} (${s.name})`));
1637
+ }
1638
+ return;
1639
+ }
1640
+ if (!options.force) {
1641
+ console.log(chalk10.yellow(`
1642
+ Vil du slette skriptet "${script.name}" (${script.slug})?`));
1643
+ console.log(chalk10.dim(` Type: ${script.type}`));
1644
+ console.log(chalk10.dim(` Scope: ${script.scope}`));
1645
+ console.log(chalk10.dim(` Versjon: v${script.current_version}`));
1646
+ console.log();
1647
+ console.log(chalk10.yellow("Bruk --force for \xE5 bekrefte sletting."));
1648
+ return;
1649
+ }
1650
+ await api.deleteScript(script.id);
1651
+ console.log(chalk10.green(`\u2713 Skript slettet: ${script.name} (${script.slug})`));
1652
+ console.log(chalk10.dim(' Kj\xF8r "kode deploy" for \xE5 oppdatere staging.'));
1653
+ } catch (error) {
1654
+ const apiError = error;
1655
+ console.log(chalk10.red(`Feil: ${apiError.message}`));
1656
+ }
1657
+ }
1658
+
1659
+ // src/commands/webflow.ts
1660
+ import chalk11 from "chalk";
1661
+ import ora7 from "ora";
1662
+ import enquirer from "enquirer";
1663
+ var { prompt } = enquirer;
1664
+ function loadConfig() {
1665
+ const projectRoot = findProjectRoot(process.cwd());
1666
+ if (!projectRoot) {
1667
+ console.log(chalk11.red(" Ikke et Cure Kode-prosjekt. Kjor kode init forst."));
1668
+ console.log();
1669
+ return null;
1670
+ }
1671
+ const config = getProjectConfig(projectRoot);
1672
+ if (!config) {
1673
+ console.log(chalk11.red(" Kunne ikke lese prosjektkonfigurasjon."));
1674
+ console.log();
1675
+ return null;
1676
+ }
1677
+ return config;
1678
+ }
1679
+ async function webflowInjectCommand(options) {
1680
+ showCompactBanner();
1681
+ const config = loadConfig();
1682
+ if (!config) return;
1683
+ const api = createApiClient(config);
1684
+ const spinner = ora7("Sjekker Webflow-status...").start();
1685
+ try {
1686
+ const status = await api.getWebflowCustomCodeStatus(config.siteId);
1687
+ if (status.injected) {
1688
+ spinner.info("Cure Kode er allerede injisert i Webflow");
1689
+ console.log();
1690
+ console.log(chalk11.dim(" Skript-URL: ") + chalk11.cyan(status.scriptUrl));
1691
+ console.log(chalk11.dim(" Plassering: ") + (status.location === "header" ? "<head>" : "<body>"));
1692
+ console.log(chalk11.dim(" Injisert: ") + new Date(status.injectedAt).toLocaleString("nb-NO"));
1693
+ console.log();
1694
+ console.log(chalk11.dim(" Bruk ") + chalk11.cyan("kode webflow remove") + chalk11.dim(" for a fjerne den."));
1695
+ console.log();
1696
+ return;
1697
+ }
1698
+ spinner.text = "Registrerer skript i Webflow...";
1699
+ const location = options.location === "footer" ? "footer" : "header";
1700
+ const result = await api.injectWebflowCustomCode(config.siteId, location);
1701
+ spinner.succeed("Cure Kode injisert i Webflow!");
1702
+ console.log();
1703
+ console.log(chalk11.green(" Skriptet er lagt til i Webflow Custom Code."));
1704
+ console.log();
1705
+ console.log(chalk11.dim(" Skript-URL: ") + chalk11.cyan(result.scriptUrl));
1706
+ console.log(chalk11.dim(" Plassering: ") + (result.location === "header" ? "<head>" : "<body>"));
1707
+ console.log();
1708
+ console.log(chalk11.yellow(" OBS: Du ma publisere Webflow-nettstedet for at endringene skal vises."));
1709
+ console.log();
1710
+ } catch (error) {
1711
+ spinner.fail("Kunne ikke injisere i Webflow");
1712
+ console.log();
1713
+ if (error.statusCode === 400) {
1714
+ console.log(chalk11.red(" Webflow er ikke konfigurert for dette nettstedet."));
1715
+ console.log(chalk11.dim(" Sett opp Webflow-tilkobling i Cure App forst."));
1716
+ } else {
1717
+ console.log(chalk11.red(` Feil: ${error.message}`));
1718
+ }
1719
+ console.log();
1720
+ }
1721
+ }
1722
+ async function webflowRemoveCommand() {
1723
+ showCompactBanner();
1724
+ const config = loadConfig();
1725
+ if (!config) return;
1726
+ const api = createApiClient(config);
1727
+ const { confirm } = await prompt([
1728
+ {
1729
+ type: "confirm",
1730
+ name: "confirm",
1731
+ message: "Fjerne Cure Kode-skriptet fra Webflow?",
1732
+ initial: false
1733
+ }
1734
+ ]);
1735
+ if (!confirm) {
1736
+ console.log(chalk11.dim(" Avbrutt."));
1737
+ console.log();
1738
+ return;
1739
+ }
1740
+ const spinner = ora7("Fjerner skript fra Webflow...").start();
1741
+ try {
1742
+ const result = await api.removeWebflowCustomCode(config.siteId);
1743
+ spinner.succeed("Skript fjernet fra Webflow");
1744
+ console.log();
1745
+ console.log(chalk11.dim(` ${result.message}`));
1746
+ console.log();
1747
+ console.log(chalk11.yellow(" OBS: Du ma publisere Webflow-nettstedet for at endringene skal forsvinne."));
1748
+ console.log();
1749
+ } catch (error) {
1750
+ spinner.fail("Kunne ikke fjerne skript");
1751
+ console.log();
1752
+ console.log(chalk11.red(` Feil: ${error.message}`));
1753
+ console.log();
1754
+ }
1755
+ }
1756
+ async function webflowStatusCommand() {
1757
+ showCompactBanner();
1758
+ const config = loadConfig();
1759
+ if (!config) return;
1760
+ const api = createApiClient(config);
1761
+ const spinner = ora7("Henter Webflow-status...").start();
1762
+ try {
1763
+ const status = await api.getWebflowCustomCodeStatus(config.siteId);
1764
+ if (status.injected) {
1765
+ spinner.succeed("Cure Kode er aktiv i Webflow");
1766
+ console.log();
1767
+ console.log(chalk11.dim(" Skript-URL: ") + chalk11.cyan(status.scriptUrl));
1768
+ console.log(chalk11.dim(" Plassering: ") + (status.location === "header" ? "<head>" : "<body>"));
1769
+ console.log(chalk11.dim(" Injisert: ") + new Date(status.injectedAt).toLocaleString("nb-NO"));
1770
+ console.log(chalk11.dim(" Andre skript: ") + `${status.totalScripts - 1} til`);
1771
+ } else {
1772
+ spinner.info("Cure Kode er ikke injisert i Webflow");
1773
+ console.log();
1774
+ console.log(chalk11.dim(" Skript-URL: ") + chalk11.cyan(status.scriptUrl));
1775
+ console.log(chalk11.dim(" Bruk ") + chalk11.cyan("kode webflow inject") + chalk11.dim(" for a legge den til."));
1776
+ }
1777
+ console.log();
1778
+ } catch (error) {
1779
+ spinner.fail("Kunne ikke hente status");
1780
+ console.log();
1781
+ if (error.statusCode === 400) {
1782
+ console.log(chalk11.red(" Webflow er ikke konfigurert for dette nettstedet."));
1783
+ } else {
1784
+ console.log(chalk11.red(` Feil: ${error.message}`));
1785
+ }
1786
+ console.log();
1787
+ }
1788
+ }
1789
+
1610
1790
  // src/cli.ts
1611
1791
  var program = new Command();
1612
1792
  program.name("kode").description("CLI for Cure Kode - manage JS/CSS scripts for Webflow sites").version(CLI_VERSION);
@@ -1628,6 +1808,9 @@ program.command("sync").description("Bidirectional sync - pull and push changes
1628
1808
  program.command("diff <script>").description("Show differences between local and remote script").action((script) => {
1629
1809
  diffCommand(script);
1630
1810
  });
1811
+ program.command("delete <script>").description("Delete a script from Cure Kode CDN").option("-f, --force", "Confirm deletion").action((script, options) => {
1812
+ deleteCommand(script, options);
1813
+ });
1631
1814
  program.command("watch").description("Watch for changes and auto-push").option("-d, --deploy", "Auto-deploy after each push").action((options) => {
1632
1815
  watchCommand(options);
1633
1816
  });
@@ -1680,6 +1863,19 @@ program.command("production <action>").description("Enable or disable production
1680
1863
  program.command("update-claude-md").alias("ucm").description("Add or update Cure Kode section in CLAUDE.md").action(() => {
1681
1864
  updateClaudeMdCommand();
1682
1865
  });
1866
+ program.command("sync-config").description("Sync project MCP servers and regenerate documentation").action(() => {
1867
+ syncConfigCommand();
1868
+ });
1869
+ var webflowCmd = program.command("webflow").description("Manage Webflow Custom Code integration");
1870
+ webflowCmd.command("inject").description("Inject Cure Kode init.js into Webflow <head>").option("-l, --location <location>", "Script location: header or footer", "header").action((options) => {
1871
+ webflowInjectCommand(options);
1872
+ });
1873
+ webflowCmd.command("remove").description("Remove Cure Kode init.js from Webflow").action(() => {
1874
+ webflowRemoveCommand();
1875
+ });
1876
+ webflowCmd.command("status", { isDefault: true }).description("Show current Webflow injection status").action(() => {
1877
+ webflowStatusCommand();
1878
+ });
1683
1879
  program.command("doctor").description("Diagnose configuration and environment issues").action(() => {
1684
1880
  doctorCommand();
1685
1881
  });
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ interface ProjectConfig {
9
9
  apiUrl?: string;
10
10
  scriptsDir?: string;
11
11
  environment?: 'staging' | 'production';
12
+ projectId?: string;
12
13
  }
13
14
  /**
14
15
  * Find project config by walking up directory tree
@@ -239,6 +240,26 @@ declare class KodeApiClient {
239
240
  acquiredAt?: string;
240
241
  duration_ms?: number;
241
242
  }>;
243
+ getWebflowCustomCodeStatus(siteId: string): Promise<{
244
+ injected: boolean;
245
+ scriptId: string | null;
246
+ scriptUrl: string;
247
+ location: string | null;
248
+ version: string | null;
249
+ totalScripts: number;
250
+ injectedAt: string | null;
251
+ }>;
252
+ injectWebflowCustomCode(siteId: string, location?: 'header' | 'footer'): Promise<{
253
+ success: boolean;
254
+ scriptId: string;
255
+ scriptUrl: string;
256
+ location: string;
257
+ message: string;
258
+ }>;
259
+ removeWebflowCustomCode(siteId: string): Promise<{
260
+ success: boolean;
261
+ message: string;
262
+ }>;
242
263
  }
243
264
  /**
244
265
  * Create API client from project config
@@ -325,6 +346,15 @@ interface ContextOptions {
325
346
  */
326
347
  declare function contextCommand(options: ContextOptions): Promise<void>;
327
348
 
349
+ /**
350
+ * Sync project MCP servers and regenerate documentation.
351
+ *
352
+ * Re-fetches project MCP servers from the Cure app and merges them into
353
+ * the local .mcp.json. Preserves non-project servers (cure-kode, webflow,
354
+ * playwright) and updates project ones.
355
+ */
356
+ declare function syncConfigCommand(): Promise<void>;
357
+
328
358
  /**
329
359
  * Kode Context - Dynamic project state for AI agents
330
360
  */
@@ -409,4 +439,4 @@ declare function generateInitialContext(config: ProjectConfig, scripts: CdnScrip
409
439
  */
410
440
  declare function generateClaudeMd(siteName: string, scriptsDir?: string, siteSlug?: string): string;
411
441
 
412
- export { type CdnDeployment, type CdnPage, type CdnScript, type CdnSite, KodeApiClient, KodeApiError, type KodeContext, type KodeScriptContext, type KodeSession, type ParsedHtmlResult, type ProjectConfig, addSession, appendNote, contextCommand, createApiClient, deployCommand, findProjectRoot, generateClaudeMd, generateInitialContext, getApiKey, getApiUrl, getContextPath, getProjectConfig, getScriptsDir, htmlCommand, initCommand, parseContext, pullCommand, pushCommand, readContext, saveProjectConfig, serializeContext, setGlobalConfig, statusCommand, updateScriptPurpose, watchCommand, writeContext };
442
+ export { type CdnDeployment, type CdnPage, type CdnScript, type CdnSite, KodeApiClient, KodeApiError, type KodeContext, type KodeScriptContext, type KodeSession, type ParsedHtmlResult, type ProjectConfig, addSession, appendNote, contextCommand, createApiClient, deployCommand, findProjectRoot, generateClaudeMd, generateInitialContext, getApiKey, getApiUrl, getContextPath, getProjectConfig, getScriptsDir, htmlCommand, initCommand, parseContext, pullCommand, pushCommand, readContext, saveProjectConfig, serializeContext, setGlobalConfig, statusCommand, syncConfigCommand, updateScriptPurpose, watchCommand, writeContext };
package/dist/index.js CHANGED
@@ -24,10 +24,11 @@ import {
24
24
  serializeContext,
25
25
  setGlobalConfig,
26
26
  statusCommand,
27
+ syncConfigCommand,
27
28
  updateScriptPurpose,
28
29
  watchCommand,
29
30
  writeContext
30
- } from "./chunk-I7TVOZJR.js";
31
+ } from "./chunk-E7DFDYOS.js";
31
32
  export {
32
33
  KodeApiClient,
33
34
  KodeApiError,
@@ -54,6 +55,7 @@ export {
54
55
  serializeContext,
55
56
  setGlobalConfig,
56
57
  statusCommand,
58
+ syncConfigCommand,
57
59
  updateScriptPurpose,
58
60
  watchCommand,
59
61
  writeContext
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curenorway/kode-cli",
3
- "version": "1.14.0",
3
+ "version": "1.15.1",
4
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": {