@curenorway/kode-cli 1.13.0 → 1.15.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.
@@ -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";
@@ -1229,13 +1229,16 @@ Full documentation: [.cure-kode/KODE.md](.cure-kode/KODE.md)
1229
1229
  function hasKodeReference(content) {
1230
1230
  return content.includes(".cure-kode/KODE.md") || content.includes("cure-kode/KODE.md") || content.includes("KODE.md");
1231
1231
  }
1232
- function generateKodeMd(siteName, siteSlug, scripts, pages) {
1232
+ function generateKodeMd(siteName, siteSlug, scripts, pages, options) {
1233
+ const productionEnabled = options?.productionEnabled ?? false;
1233
1234
  let md = `# Cure Kode: ${siteName}
1234
1235
 
1235
1236
  This project uses **Cure Kode** CDN for JavaScript/CSS delivery to Webflow.
1236
1237
 
1237
1238
  > **This file is auto-generated.** Do not edit manually - changes will be overwritten by \`kode pull\`, \`kode push\`, and \`kode sync\`.
1238
1239
 
1240
+ **Produksjon:** ${productionEnabled ? "\u2713 Aktivert \u2014 deploy til staging og produksjon" : "\u25CB Deaktivert \u2014 kun staging. Ikke hent fra produksjons-URL."}
1241
+
1239
1242
  ---
1240
1243
 
1241
1244
  `;
@@ -1394,15 +1397,15 @@ function ensureClaudeMdReference(projectRoot) {
1394
1397
  writeFileSync3(claudeMdPath, newContent);
1395
1398
  return { created: false, updated: true, skipped: false };
1396
1399
  }
1397
- function updateKodeMd(projectRoot, siteName, siteSlug, scripts, pages) {
1400
+ function updateKodeMd(projectRoot, siteName, siteSlug, scripts, pages, options) {
1398
1401
  const kodeMdPath = join3(projectRoot, ".cure-kode", "KODE.md");
1399
1402
  const existed = existsSync3(kodeMdPath);
1400
- const content = generateKodeMd(siteName, siteSlug, scripts, pages);
1403
+ const content = generateKodeMd(siteName, siteSlug, scripts, pages, options);
1401
1404
  writeFileSync3(kodeMdPath, content);
1402
1405
  return { created: !existed, updated: existed };
1403
1406
  }
1404
- function updateKodeDocs(projectRoot, siteName, siteSlug, scripts, pages) {
1405
- const kodeMd = updateKodeMd(projectRoot, siteName, siteSlug, scripts, pages);
1407
+ function updateKodeDocs(projectRoot, siteName, siteSlug, scripts, pages, options) {
1408
+ const kodeMd = updateKodeMd(projectRoot, siteName, siteSlug, scripts, pages, options);
1406
1409
  const claudeMd = ensureClaudeMdReference(projectRoot);
1407
1410
  return { kodeMd, claudeMd };
1408
1411
  }
@@ -1448,6 +1451,12 @@ function showBanner() {
1448
1451
  console.log(chalk.dim(" JS/CSS delivery for Webflow sites"));
1449
1452
  console.log();
1450
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
+ }
1451
1460
  async function initCommand(options) {
1452
1461
  const cwd = process.cwd();
1453
1462
  showBanner();
@@ -1540,13 +1549,34 @@ async function initCommand(options) {
1540
1549
  }
1541
1550
  console.log(chalk.dim(" Webflow: ") + chalk.green("\u2713 Konfigurert"));
1542
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
+ }
1543
1572
  const config = {
1544
1573
  siteId: site.id,
1545
1574
  siteSlug: site.slug,
1546
1575
  siteName: site.name,
1547
1576
  apiKey,
1548
1577
  scriptsDir: DEFAULT_SCRIPTS_DIR,
1549
- environment: "staging"
1578
+ environment: "staging",
1579
+ ...projectCtx?.project && { projectId: projectCtx.project.id }
1550
1580
  };
1551
1581
  spinner.start("Oppretter prosjektstruktur...");
1552
1582
  saveProjectConfig(config, cwd);
@@ -1588,18 +1618,56 @@ config.json
1588
1618
  command: "npx",
1589
1619
  args: ["-y", "@playwright/mcp@^0.0.21"]
1590
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
+ }
1591
1647
  writeFileSync4(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
1592
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
+ }
1593
1657
  spinner.start("Genererer AI-dokumentasjon...");
1594
1658
  let scripts = [];
1595
1659
  let pages = [];
1660
+ let productionEnabled = false;
1596
1661
  try {
1597
- const [scriptsResponse, pagesResponse] = await Promise.all([
1662
+ const [scriptsResponse, pagesResponse, prodResponse] = await Promise.all([
1598
1663
  fetch(`https://app.cure.no/api/cdn/sites/${site.id}/scripts`, {
1599
1664
  headers: { "X-API-Key": apiKey }
1600
1665
  }),
1601
1666
  fetch(`https://app.cure.no/api/cdn/sites/${site.id}/pages`, {
1602
1667
  headers: { "X-API-Key": apiKey }
1668
+ }),
1669
+ fetch(`https://app.cure.no/api/cdn/sites/${site.id}/production`, {
1670
+ headers: { "X-API-Key": apiKey }
1603
1671
  })
1604
1672
  ]);
1605
1673
  if (scriptsResponse.ok) {
@@ -1608,6 +1676,10 @@ config.json
1608
1676
  if (pagesResponse.ok) {
1609
1677
  pages = await pagesResponse.json();
1610
1678
  }
1679
+ if (prodResponse.ok) {
1680
+ const prodData = await prodResponse.json();
1681
+ productionEnabled = prodData.production_enabled ?? false;
1682
+ }
1611
1683
  } catch {
1612
1684
  }
1613
1685
  const docsResult = updateKodeDocs(
@@ -1615,7 +1687,8 @@ config.json
1615
1687
  config.siteName,
1616
1688
  config.siteSlug,
1617
1689
  scriptsToDocsFormat(scripts, pages),
1618
- pagesToInfoFormat(pages)
1690
+ pagesToInfoFormat(pages),
1691
+ { productionEnabled }
1619
1692
  );
1620
1693
  const siteInfo = {
1621
1694
  domain: site.domain,
@@ -1648,11 +1721,39 @@ config.json
1648
1721
  console.log(chalk.dim(` \u2514\u2500\u2500 ${DEFAULT_SCRIPTS_DIR}/`));
1649
1722
  console.log(chalk.dim(" \u2514\u2500\u2500 (skriptene dine her)"));
1650
1723
  console.log();
1651
- console.log(chalk.bold(" Webflow-oppsett:"));
1652
- console.log(chalk.dim(" Legg til dette i Webflow <head>:"));
1653
- console.log();
1654
- console.log(chalk.cyan(` <script defer src="https://app.cure.no/api/cdn/${site.slug}/init.js"></script>`));
1655
- 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
+ }
1656
1757
  console.log(chalk.bold(" Neste steg:"));
1657
1758
  console.log();
1658
1759
  console.log(chalk.cyan(" 1. kode pull ") + chalk.dim("Last ned eksisterende skript"));
@@ -1947,6 +2048,21 @@ var KodeApiClient = class {
1947
2048
  body: JSON.stringify({ siteId })
1948
2049
  });
1949
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
+ }
1950
2066
  };
1951
2067
  function createApiClient(config) {
1952
2068
  return new KodeApiClient(config);
@@ -3619,6 +3735,154 @@ async function contextCommand(options) {
3619
3735
  console.log(chalk8.dim("Use --edit to open in editor, --refresh to update from server"));
3620
3736
  }
3621
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
+
3622
3886
  export {
3623
3887
  findProjectRoot,
3624
3888
  getProjectConfig,
@@ -3642,6 +3906,7 @@ export {
3642
3906
  pagesToInfoFormat,
3643
3907
  updateClaudeMd,
3644
3908
  CLI_VERSION,
3909
+ showCompactBanner,
3645
3910
  initCommand,
3646
3911
  KodeApiError,
3647
3912
  KodeApiClient,
@@ -3655,5 +3920,6 @@ export {
3655
3920
  listCachedPages,
3656
3921
  htmlCommand,
3657
3922
  statusCommand,
3658
- contextCommand
3923
+ contextCommand,
3924
+ syncConfigCommand
3659
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-C2BM7IJ6.js";
26
+ } from "./chunk-E7DFDYOS.js";
25
27
 
26
28
  // src/cli.ts
27
29
  import { Command } from "commander";
@@ -836,9 +838,10 @@ async function updateClaudeMdCommand() {
836
838
  const spinner = ora5("Henter skript og sider...").start();
837
839
  try {
838
840
  const client = createApiClient(config);
839
- const [scripts, pages] = await Promise.all([
841
+ const [scripts, pages, deployStatus] = await Promise.all([
840
842
  client.listScripts(config.siteId),
841
- client.listPages(config.siteId)
843
+ client.listPages(config.siteId),
844
+ client.getDeploymentStatus(config.siteId).catch(() => null)
842
845
  ]);
843
846
  spinner.succeed(`Fant ${scripts.length} skript og ${pages.length} sider`);
844
847
  const result = updateKodeDocs(
@@ -846,7 +849,8 @@ async function updateClaudeMdCommand() {
846
849
  config.siteName,
847
850
  config.siteSlug,
848
851
  scriptsToDocsFormat(scripts, pages),
849
- pagesToInfoFormat(pages)
852
+ pagesToInfoFormat(pages),
853
+ { productionEnabled: deployStatus?.productionEnabled ?? false }
850
854
  );
851
855
  if (result.kodeMd.created) {
852
856
  console.log(chalk6.green("\u2705 Opprettet .cure-kode/KODE.md"));
@@ -1605,6 +1609,184 @@ function printResults(results) {
1605
1609
  console.log();
1606
1610
  }
1607
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
+
1608
1790
  // src/cli.ts
1609
1791
  var program = new Command();
1610
1792
  program.name("kode").description("CLI for Cure Kode - manage JS/CSS scripts for Webflow sites").version(CLI_VERSION);
@@ -1626,6 +1808,9 @@ program.command("sync").description("Bidirectional sync - pull and push changes
1626
1808
  program.command("diff <script>").description("Show differences between local and remote script").action((script) => {
1627
1809
  diffCommand(script);
1628
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
+ });
1629
1814
  program.command("watch").description("Watch for changes and auto-push").option("-d, --deploy", "Auto-deploy after each push").action((options) => {
1630
1815
  watchCommand(options);
1631
1816
  });
@@ -1678,6 +1863,19 @@ program.command("production <action>").description("Enable or disable production
1678
1863
  program.command("update-claude-md").alias("ucm").description("Add or update Cure Kode section in CLAUDE.md").action(() => {
1679
1864
  updateClaudeMdCommand();
1680
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
+ });
1681
1879
  program.command("doctor").description("Diagnose configuration and environment issues").action(() => {
1682
1880
  doctorCommand();
1683
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-C2BM7IJ6.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.13.0",
3
+ "version": "1.15.0",
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": {