@curenorway/kode-cli 1.9.2 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -67
- package/dist/{chunk-CUZJE4JZ.js → chunk-XU4YS3JQ.js} +893 -480
- package/dist/cli.js +1342 -168
- package/dist/index.d.ts +45 -11
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,115 +1,537 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
CLI_VERSION,
|
|
4
|
+
KodeApiError,
|
|
3
5
|
contextCommand,
|
|
4
6
|
createApiClient,
|
|
5
7
|
deletePageContext,
|
|
6
8
|
deployCommand,
|
|
7
9
|
findProjectRoot,
|
|
8
|
-
generateClaudeMdMinimal,
|
|
9
10
|
getProjectConfig,
|
|
11
|
+
getScriptsDir,
|
|
10
12
|
htmlCommand,
|
|
11
13
|
initCommand,
|
|
12
14
|
listCachedPages,
|
|
15
|
+
pagesToInfoFormat,
|
|
13
16
|
pullCommand,
|
|
14
17
|
pushCommand,
|
|
15
18
|
readPageContext,
|
|
19
|
+
scriptsToDocsFormat,
|
|
16
20
|
statusCommand,
|
|
21
|
+
updateClaudeMd,
|
|
22
|
+
updateKodeDocs,
|
|
17
23
|
watchCommand
|
|
18
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-XU4YS3JQ.js";
|
|
19
25
|
|
|
20
26
|
// src/cli.ts
|
|
21
27
|
import { Command } from "commander";
|
|
22
|
-
import chalk6 from "chalk";
|
|
23
|
-
import { createRequire } from "module";
|
|
24
28
|
|
|
25
|
-
// src/commands/
|
|
29
|
+
// src/commands/upgrade.ts
|
|
26
30
|
import chalk from "chalk";
|
|
27
31
|
import ora from "ora";
|
|
32
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
33
|
+
import { join } from "path";
|
|
34
|
+
async function upgradeCommand() {
|
|
35
|
+
const projectRoot = findProjectRoot();
|
|
36
|
+
if (!projectRoot) {
|
|
37
|
+
console.log();
|
|
38
|
+
console.log(chalk.red(" Feil: Ikke i et Cure Kode-prosjekt."));
|
|
39
|
+
console.log();
|
|
40
|
+
console.log(chalk.dim(" Kj\xF8r ") + chalk.cyan("kode init") + chalk.dim(" for \xE5 initialisere et nytt prosjekt."));
|
|
41
|
+
console.log();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const config = getProjectConfig(projectRoot);
|
|
45
|
+
if (!config) {
|
|
46
|
+
console.log();
|
|
47
|
+
console.log(chalk.red(" Feil: Kunne ikke lese prosjektkonfigurasjon."));
|
|
48
|
+
console.log();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log();
|
|
52
|
+
console.log(chalk.bold(" Cure Kode Upgrade") + chalk.dim(` \u2192 v${CLI_VERSION}`));
|
|
53
|
+
console.log();
|
|
54
|
+
const spinner = ora("Sjekker prosjekt...").start();
|
|
55
|
+
try {
|
|
56
|
+
const client = createApiClient(config);
|
|
57
|
+
spinner.text = "Henter skript og sider fra server...";
|
|
58
|
+
const [scripts, pages] = await Promise.all([
|
|
59
|
+
client.listScripts(config.siteId),
|
|
60
|
+
client.listPages(config.siteId)
|
|
61
|
+
]);
|
|
62
|
+
spinner.succeed(`Fant ${scripts.length} skript og ${pages.length} sider`);
|
|
63
|
+
spinner.start("Oppdaterer dokumentasjon...");
|
|
64
|
+
const result = updateClaudeMd(
|
|
65
|
+
projectRoot,
|
|
66
|
+
config.siteName,
|
|
67
|
+
config.siteSlug,
|
|
68
|
+
scriptsToDocsFormat(scripts),
|
|
69
|
+
pagesToInfoFormat(pages)
|
|
70
|
+
);
|
|
71
|
+
if (result.kodeMd.created) {
|
|
72
|
+
spinner.succeed("Opprettet .cure-kode/KODE.md");
|
|
73
|
+
} else {
|
|
74
|
+
spinner.succeed("Oppdatert .cure-kode/KODE.md");
|
|
75
|
+
}
|
|
76
|
+
if (result.claudeMd.updated) {
|
|
77
|
+
console.log(chalk.dim(" La til Kode-referanse i CLAUDE.md"));
|
|
78
|
+
}
|
|
79
|
+
spinner.start("Oppdaterer MCP-servere...");
|
|
80
|
+
const mcpConfigPath = join(projectRoot, ".mcp.json");
|
|
81
|
+
let mcpConfig = {};
|
|
82
|
+
if (existsSync(mcpConfigPath)) {
|
|
83
|
+
try {
|
|
84
|
+
mcpConfig = JSON.parse(readFileSync(mcpConfigPath, "utf-8"));
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
mcpConfig.mcpServers = mcpConfig.mcpServers || {};
|
|
89
|
+
mcpConfig.mcpServers["cure-kode"] = {
|
|
90
|
+
type: "stdio",
|
|
91
|
+
command: "npx",
|
|
92
|
+
args: ["-y", "@curenorway/kode-mcp@^1.5.0"]
|
|
93
|
+
};
|
|
94
|
+
const existingWebflow = mcpConfig.mcpServers["webflow"];
|
|
95
|
+
if (existingWebflow && existingWebflow.env) {
|
|
96
|
+
mcpConfig.mcpServers["webflow"] = {
|
|
97
|
+
type: "stdio",
|
|
98
|
+
command: "npx",
|
|
99
|
+
args: ["-y", "webflow-mcp-server@^0.6.0"],
|
|
100
|
+
env: existingWebflow.env
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
mcpConfig.mcpServers["playwright"] = {
|
|
104
|
+
type: "stdio",
|
|
105
|
+
command: "npx",
|
|
106
|
+
args: ["-y", "@playwright/mcp@^0.0.21"]
|
|
107
|
+
};
|
|
108
|
+
writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
109
|
+
spinner.succeed("MCP-servere oppdatert");
|
|
110
|
+
spinner.start("Oppdaterer context.md...");
|
|
111
|
+
const contextPath = join(projectRoot, ".cure-kode", "context.md");
|
|
112
|
+
if (existsSync(contextPath)) {
|
|
113
|
+
const existingContext = readFileSync(contextPath, "utf-8");
|
|
114
|
+
const scriptsSection = scripts.length > 0 ? `## Scripts
|
|
115
|
+
|
|
116
|
+
${scripts.map((s) => {
|
|
117
|
+
const ext = s.type === "javascript" ? "js" : "css";
|
|
118
|
+
const scope = s.scope === "global" ? "[G]" : "[P]";
|
|
119
|
+
const auto = s.auto_load ? "\u26A1" : "\u25CB";
|
|
120
|
+
return `- \`${s.slug}.${ext}\` ${scope} ${auto}`;
|
|
121
|
+
}).join("\n")}
|
|
122
|
+
|
|
123
|
+
[G]=Global [P]=Page-specific \u26A1=Auto-load \u25CB=Manual
|
|
124
|
+
` : "## Scripts\n\nNo scripts yet. Create one with `kode push script-name.js`\n";
|
|
125
|
+
const scriptsPattern = /## Scripts[\s\S]*?(?=\n## |\n---\n|$)/;
|
|
126
|
+
let updatedContext;
|
|
127
|
+
if (scriptsPattern.test(existingContext)) {
|
|
128
|
+
updatedContext = existingContext.replace(scriptsPattern, scriptsSection);
|
|
129
|
+
} else {
|
|
130
|
+
updatedContext = existingContext + "\n" + scriptsSection;
|
|
131
|
+
}
|
|
132
|
+
writeFileSync(contextPath, updatedContext);
|
|
133
|
+
}
|
|
134
|
+
spinner.succeed("context.md oppdatert");
|
|
135
|
+
console.log();
|
|
136
|
+
console.log(chalk.green(" \u2705 Oppgradering fullf\xF8rt!"));
|
|
137
|
+
console.log();
|
|
138
|
+
console.log(chalk.dim(" Oppdatert:"));
|
|
139
|
+
console.log(chalk.dim(" \u2022 .cure-kode/KODE.md - Kode-dokumentasjon"));
|
|
140
|
+
console.log(chalk.dim(" \u2022 .mcp.json - MCP-servere til nyeste versjoner"));
|
|
141
|
+
console.log(chalk.dim(" \u2022 context.md - Dynamisk AI-kontekst"));
|
|
142
|
+
console.log();
|
|
143
|
+
console.log(chalk.dim(" Neste steg:"));
|
|
144
|
+
console.log(chalk.cyan(" kode pull") + chalk.dim(" - Synkroniser lokale skriptfiler"));
|
|
145
|
+
console.log();
|
|
146
|
+
} catch (error) {
|
|
147
|
+
spinner.fail("Oppgradering feilet");
|
|
148
|
+
console.log();
|
|
149
|
+
console.error(chalk.red(" Feil:"), error);
|
|
150
|
+
console.log();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/commands/rollback.ts
|
|
155
|
+
import chalk2 from "chalk";
|
|
156
|
+
import ora2 from "ora";
|
|
28
157
|
async function rollbackCommand(environment = "staging") {
|
|
29
158
|
const projectRoot = findProjectRoot();
|
|
30
159
|
if (!projectRoot) {
|
|
31
|
-
console.log(
|
|
32
|
-
console.log(
|
|
160
|
+
console.log(chalk2.red("\u274C Not in a Cure Kode project."));
|
|
161
|
+
console.log(chalk2.dim(' Run "kode init" first.'));
|
|
33
162
|
return;
|
|
34
163
|
}
|
|
35
164
|
const config = getProjectConfig(projectRoot);
|
|
36
165
|
if (!config) {
|
|
37
|
-
console.log(
|
|
166
|
+
console.log(chalk2.red("\u274C Could not read project configuration."));
|
|
38
167
|
return;
|
|
39
168
|
}
|
|
40
169
|
if (!["staging", "production"].includes(environment)) {
|
|
41
|
-
console.log(
|
|
42
|
-
console.log(
|
|
170
|
+
console.log(chalk2.red(`\u274C Invalid environment: ${environment}`));
|
|
171
|
+
console.log(chalk2.dim(' Use "staging" or "production"'));
|
|
43
172
|
return;
|
|
44
173
|
}
|
|
45
174
|
const client = createApiClient(config);
|
|
46
|
-
const spinner =
|
|
175
|
+
const spinner = ora2(`Ruller tilbake ${environment}...`).start();
|
|
47
176
|
try {
|
|
48
177
|
if (environment === "production") {
|
|
49
178
|
const status = await client.getDeploymentStatus(config.siteId);
|
|
50
179
|
if (!status.productionEnabled) {
|
|
51
180
|
spinner.fail("Produksjon er ikke aktivert");
|
|
52
181
|
console.log();
|
|
53
|
-
console.log(
|
|
54
|
-
console.log(
|
|
182
|
+
console.log(chalk2.yellow("\u26A0\uFE0F Produksjon er deaktivert for dette prosjektet."));
|
|
183
|
+
console.log(chalk2.dim(" Kan ikke rulle tilbake n\xE5r produksjon er deaktivert."));
|
|
55
184
|
return;
|
|
56
185
|
}
|
|
57
186
|
}
|
|
58
187
|
const result = await client.rollback(config.siteId, environment);
|
|
59
188
|
spinner.succeed(`Tilbakerulling fullf\xF8rt (${result.duration_ms}ms)`);
|
|
60
189
|
console.log();
|
|
61
|
-
console.log(
|
|
62
|
-
console.log(
|
|
63
|
-
console.log(
|
|
190
|
+
console.log(chalk2.dim("Tilbakerulling detaljer:"));
|
|
191
|
+
console.log(chalk2.dim(` Fra: ${result.rolledBackFrom.version}`));
|
|
192
|
+
console.log(chalk2.dim(` Til: ${result.rolledBackTo.version}`));
|
|
64
193
|
console.log();
|
|
65
|
-
console.log(
|
|
66
|
-
console.log(
|
|
194
|
+
console.log(chalk2.bold("CDN URL:"));
|
|
195
|
+
console.log(chalk2.cyan(` ${result.cdn_url}`));
|
|
67
196
|
console.log();
|
|
68
|
-
console.log(
|
|
197
|
+
console.log(chalk2.green(`\u2705 ${environment.charAt(0).toUpperCase() + environment.slice(1)} er n\xE5 tilbake til forrige versjon!`));
|
|
69
198
|
} catch (error) {
|
|
70
199
|
spinner.fail("Tilbakerulling feilet");
|
|
71
200
|
if (error.statusCode === 404) {
|
|
72
201
|
console.log();
|
|
73
|
-
console.log(
|
|
74
|
-
console.log(
|
|
202
|
+
console.log(chalk2.yellow("\u26A0\uFE0F Ingen tidligere versjon \xE5 rulle tilbake til."));
|
|
203
|
+
console.log(chalk2.dim(" Det m\xE5 v\xE6re minst 2 deployments for \xE5 kunne rulle tilbake."));
|
|
75
204
|
} else if (error.statusCode === 409) {
|
|
76
205
|
console.log();
|
|
77
|
-
console.log(
|
|
78
|
-
console.log(
|
|
206
|
+
console.log(chalk2.yellow("\u26A0\uFE0F En annen deployment kj\xF8rer."));
|
|
207
|
+
console.log(chalk2.dim(" Vent til den er ferdig og pr\xF8v igjen."));
|
|
79
208
|
} else {
|
|
80
|
-
console.error(
|
|
209
|
+
console.error(chalk2.red("\nError:"), error.message || error);
|
|
81
210
|
}
|
|
82
211
|
}
|
|
83
212
|
}
|
|
84
213
|
|
|
85
214
|
// src/commands/pages.ts
|
|
86
|
-
import
|
|
87
|
-
async function
|
|
215
|
+
import chalk3 from "chalk";
|
|
216
|
+
async function pagesListCommand(options) {
|
|
217
|
+
const projectRoot = findProjectRoot();
|
|
218
|
+
if (!projectRoot) {
|
|
219
|
+
console.log(chalk3.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
220
|
+
console.log(chalk3.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const config = getProjectConfig(projectRoot);
|
|
224
|
+
if (!config) {
|
|
225
|
+
console.log(chalk3.red("Feil: Ugyldig prosjektkonfigurasjon."));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const api = createApiClient(config);
|
|
230
|
+
const pages = await api.listPages(config.siteId);
|
|
231
|
+
if (options.json) {
|
|
232
|
+
console.log(JSON.stringify(pages, null, 2));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (pages.length === 0) {
|
|
236
|
+
console.log(chalk3.yellow("Ingen sider funnet."));
|
|
237
|
+
console.log(chalk3.dim('Bruk "kode pages create <name>" for \xE5 opprette en side.'));
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
console.log(chalk3.bold(`CDN-sider (${pages.length})`));
|
|
241
|
+
console.log();
|
|
242
|
+
const rootPages = pages.filter((p) => !p.parent_id);
|
|
243
|
+
const childPages = pages.filter((p) => p.parent_id);
|
|
244
|
+
const printPage = (page, indent = 0) => {
|
|
245
|
+
const prefix = " ".repeat(indent);
|
|
246
|
+
const patterns = page.url_patterns.join(", ");
|
|
247
|
+
const patternType = chalk3.dim(`[${page.pattern_type}]`);
|
|
248
|
+
console.log(`${prefix}${chalk3.bold(page.name)} ${chalk3.dim(`(${page.slug})`)}`);
|
|
249
|
+
console.log(`${prefix} ${patterns} ${patternType}`);
|
|
250
|
+
const children = childPages.filter((c) => c.parent_id === page.id);
|
|
251
|
+
for (const child of children) {
|
|
252
|
+
printPage(child, indent + 1);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
for (const page of rootPages) {
|
|
256
|
+
printPage(page);
|
|
257
|
+
console.log();
|
|
258
|
+
}
|
|
259
|
+
console.log(chalk3.dim('Bruk "kode pages create <name>" for \xE5 legge til sider'));
|
|
260
|
+
console.log(chalk3.dim('Bruk "kode pages assign <page> <script>" for \xE5 koble skript'));
|
|
261
|
+
} catch (error) {
|
|
262
|
+
const apiError = error;
|
|
263
|
+
console.log(chalk3.red(`Feil: ${apiError.message}`));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async function pagesCreateCommand(name, options) {
|
|
88
267
|
const projectRoot = findProjectRoot();
|
|
89
268
|
if (!projectRoot) {
|
|
90
|
-
console.log(
|
|
91
|
-
console.log(
|
|
269
|
+
console.log(chalk3.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
270
|
+
console.log(chalk3.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
92
271
|
return;
|
|
93
272
|
}
|
|
94
273
|
const config = getProjectConfig(projectRoot);
|
|
95
274
|
if (!config) {
|
|
96
|
-
console.log(
|
|
275
|
+
console.log(chalk3.red("Feil: Ugyldig prosjektkonfigurasjon."));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
let patterns = options.patterns;
|
|
279
|
+
if (!patterns || patterns.length === 0) {
|
|
280
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
281
|
+
patterns = [`/${slug}`, `/${slug}/*`];
|
|
282
|
+
console.log(chalk3.dim(`Bruker standard URL-m\xF8nstre: ${patterns.join(", ")}`));
|
|
283
|
+
}
|
|
284
|
+
const patternType = options.patternType || "prefix";
|
|
285
|
+
const priority = options.priority ? parseInt(options.priority, 10) : 0;
|
|
286
|
+
try {
|
|
287
|
+
const api = createApiClient(config);
|
|
288
|
+
let parentId;
|
|
289
|
+
if (options.parent) {
|
|
290
|
+
const pages = await api.listPages(config.siteId);
|
|
291
|
+
const parentPage = pages.find(
|
|
292
|
+
(p) => p.slug === options.parent || p.name === options.parent
|
|
293
|
+
);
|
|
294
|
+
if (!parentPage) {
|
|
295
|
+
console.log(chalk3.red(`Feil: Finner ikke foreldreside "${options.parent}"`));
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
parentId = parentPage.id;
|
|
299
|
+
}
|
|
300
|
+
const page = await api.createPage(config.siteId, {
|
|
301
|
+
name,
|
|
302
|
+
urlPatterns: patterns,
|
|
303
|
+
patternType,
|
|
304
|
+
priority,
|
|
305
|
+
parentId
|
|
306
|
+
});
|
|
307
|
+
console.log(chalk3.green(`\u2713 Side opprettet: ${page.name} (${page.slug})`));
|
|
308
|
+
console.log(chalk3.dim(` URL-m\xF8nstre: ${page.url_patterns.join(", ")}`));
|
|
309
|
+
console.log(chalk3.dim(` M\xF8nstertype: ${page.pattern_type}`));
|
|
310
|
+
if (parentId) {
|
|
311
|
+
console.log(chalk3.dim(` Forelder: ${options.parent}`));
|
|
312
|
+
}
|
|
313
|
+
} catch (error) {
|
|
314
|
+
const apiError = error;
|
|
315
|
+
console.log(chalk3.red(`Feil: ${apiError.message}`));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function pagesDeleteCommand(pageSlug, options) {
|
|
319
|
+
const projectRoot = findProjectRoot();
|
|
320
|
+
if (!projectRoot) {
|
|
321
|
+
console.log(chalk3.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
322
|
+
console.log(chalk3.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const config = getProjectConfig(projectRoot);
|
|
326
|
+
if (!config) {
|
|
327
|
+
console.log(chalk3.red("Feil: Ugyldig prosjektkonfigurasjon."));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
const api = createApiClient(config);
|
|
332
|
+
const pages = await api.listPages(config.siteId);
|
|
333
|
+
const page = pages.find((p) => p.slug === pageSlug || p.name === pageSlug);
|
|
334
|
+
if (!page) {
|
|
335
|
+
console.log(chalk3.red(`Feil: Finner ikke side "${pageSlug}"`));
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (!options.force) {
|
|
339
|
+
console.log(chalk3.yellow(`Vil du slette siden "${page.name}"? (--force for \xE5 bekrefte)`));
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
await api.deletePage(page.id);
|
|
343
|
+
console.log(chalk3.green(`\u2713 Side slettet: ${page.name}`));
|
|
344
|
+
} catch (error) {
|
|
345
|
+
const apiError = error;
|
|
346
|
+
console.log(chalk3.red(`Feil: ${apiError.message}`));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function pagesUpdateCommand(pageSlug, options) {
|
|
350
|
+
const projectRoot = findProjectRoot();
|
|
351
|
+
if (!projectRoot) {
|
|
352
|
+
console.log(chalk3.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
353
|
+
console.log(chalk3.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const config = getProjectConfig(projectRoot);
|
|
357
|
+
if (!config) {
|
|
358
|
+
console.log(chalk3.red("Feil: Ugyldig prosjektkonfigurasjon."));
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const api = createApiClient(config);
|
|
363
|
+
const pages = await api.listPages(config.siteId);
|
|
364
|
+
const page = pages.find((p) => p.slug === pageSlug || p.name === pageSlug);
|
|
365
|
+
if (!page) {
|
|
366
|
+
console.log(chalk3.red(`Feil: Finner ikke side "${pageSlug}"`));
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const updates = {};
|
|
370
|
+
if (options.name) updates.name = options.name;
|
|
371
|
+
if (options.patterns && options.patterns.length > 0) updates.urlPatterns = options.patterns;
|
|
372
|
+
if (options.patternType)
|
|
373
|
+
updates.patternType = options.patternType;
|
|
374
|
+
if (options.priority) updates.priority = parseInt(options.priority, 10);
|
|
375
|
+
if (options.removeParent) {
|
|
376
|
+
updates.parentId = null;
|
|
377
|
+
} else if (options.parent) {
|
|
378
|
+
const parentPage = pages.find(
|
|
379
|
+
(p) => p.slug === options.parent || p.name === options.parent
|
|
380
|
+
);
|
|
381
|
+
if (!parentPage) {
|
|
382
|
+
console.log(chalk3.red(`Feil: Finner ikke foreldreside "${options.parent}"`));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
updates.parentId = parentPage.id;
|
|
386
|
+
}
|
|
387
|
+
if (Object.keys(updates).length === 0) {
|
|
388
|
+
console.log(chalk3.yellow("Ingen endringer angitt."));
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const updatedPage = await api.updatePage(page.id, updates);
|
|
392
|
+
console.log(chalk3.green(`\u2713 Side oppdatert: ${updatedPage.name}`));
|
|
393
|
+
} catch (error) {
|
|
394
|
+
const apiError = error;
|
|
395
|
+
console.log(chalk3.red(`Feil: ${apiError.message}`));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async function pagesAssignCommand(pageSlug, scriptSlug, options) {
|
|
399
|
+
const projectRoot = findProjectRoot();
|
|
400
|
+
if (!projectRoot) {
|
|
401
|
+
console.log(chalk3.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
402
|
+
console.log(chalk3.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const config = getProjectConfig(projectRoot);
|
|
406
|
+
if (!config) {
|
|
407
|
+
console.log(chalk3.red("Feil: Ugyldig prosjektkonfigurasjon."));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
const api = createApiClient(config);
|
|
412
|
+
const pages = await api.listPages(config.siteId);
|
|
413
|
+
const page = pages.find((p) => p.slug === pageSlug || p.name === pageSlug);
|
|
414
|
+
if (!page) {
|
|
415
|
+
console.log(chalk3.red(`Feil: Finner ikke side "${pageSlug}"`));
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const scripts = await api.listScripts(config.siteId);
|
|
419
|
+
const script = scripts.find((s) => s.slug === scriptSlug || s.name === scriptSlug);
|
|
420
|
+
if (!script) {
|
|
421
|
+
console.log(chalk3.red(`Feil: Finner ikke skript "${scriptSlug}"`));
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const loadOrder = options.loadOrder ? parseInt(options.loadOrder, 10) : void 0;
|
|
425
|
+
await api.assignScriptToPage(page.id, script.id, loadOrder);
|
|
426
|
+
console.log(chalk3.green(`\u2713 Skript "${script.name}" tilordnet til side "${page.name}"`));
|
|
427
|
+
} catch (error) {
|
|
428
|
+
const apiError = error;
|
|
429
|
+
if (apiError.statusCode === 409) {
|
|
430
|
+
console.log(chalk3.yellow("Skriptet er allerede tilordnet denne siden."));
|
|
431
|
+
} else {
|
|
432
|
+
console.log(chalk3.red(`Feil: ${apiError.message}`));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
async function pagesUnassignCommand(pageSlug, scriptSlug) {
|
|
437
|
+
const projectRoot = findProjectRoot();
|
|
438
|
+
if (!projectRoot) {
|
|
439
|
+
console.log(chalk3.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
440
|
+
console.log(chalk3.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const config = getProjectConfig(projectRoot);
|
|
444
|
+
if (!config) {
|
|
445
|
+
console.log(chalk3.red("Feil: Ugyldig prosjektkonfigurasjon."));
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
try {
|
|
449
|
+
const api = createApiClient(config);
|
|
450
|
+
const pages = await api.listPages(config.siteId);
|
|
451
|
+
const page = pages.find((p) => p.slug === pageSlug || p.name === pageSlug);
|
|
452
|
+
if (!page) {
|
|
453
|
+
console.log(chalk3.red(`Feil: Finner ikke side "${pageSlug}"`));
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const scripts = await api.listScripts(config.siteId);
|
|
457
|
+
const script = scripts.find((s) => s.slug === scriptSlug || s.name === scriptSlug);
|
|
458
|
+
if (!script) {
|
|
459
|
+
console.log(chalk3.red(`Feil: Finner ikke skript "${scriptSlug}"`));
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
await api.removeScriptFromPage(page.id, script.id);
|
|
463
|
+
console.log(chalk3.green(`\u2713 Skript "${script.name}" fjernet fra side "${page.name}"`));
|
|
464
|
+
} catch (error) {
|
|
465
|
+
const apiError = error;
|
|
466
|
+
console.log(chalk3.red(`Feil: ${apiError.message}`));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
async function pagesScriptsCommand(pageSlug) {
|
|
470
|
+
const projectRoot = findProjectRoot();
|
|
471
|
+
if (!projectRoot) {
|
|
472
|
+
console.log(chalk3.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
473
|
+
console.log(chalk3.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const config = getProjectConfig(projectRoot);
|
|
477
|
+
if (!config) {
|
|
478
|
+
console.log(chalk3.red("Feil: Ugyldig prosjektkonfigurasjon."));
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
const api = createApiClient(config);
|
|
483
|
+
const pages = await api.listPages(config.siteId);
|
|
484
|
+
const page = pages.find((p) => p.slug === pageSlug || p.name === pageSlug);
|
|
485
|
+
if (!page) {
|
|
486
|
+
console.log(chalk3.red(`Feil: Finner ikke side "${pageSlug}"`));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const assignments = await api.getPageScripts(page.id);
|
|
490
|
+
console.log(chalk3.bold(`Skript p\xE5 "${page.name}"`));
|
|
491
|
+
console.log();
|
|
492
|
+
if (assignments.length === 0) {
|
|
493
|
+
console.log(chalk3.yellow("Ingen skript tilordnet denne siden."));
|
|
494
|
+
console.log(chalk3.dim('Bruk "kode pages assign <page> <script>" for \xE5 tilordne skript.'));
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
for (const assignment of assignments) {
|
|
498
|
+
const script = assignment.script;
|
|
499
|
+
const type = script.type === "javascript" ? chalk3.yellow("JS") : chalk3.blue("CSS");
|
|
500
|
+
const loadOrder = assignment.load_order_override !== null ? chalk3.dim(`[ordre: ${assignment.load_order_override}]`) : "";
|
|
501
|
+
const enabled = assignment.is_enabled ? "" : chalk3.red(" (deaktivert)");
|
|
502
|
+
console.log(` ${type} ${chalk3.bold(script.name)} ${loadOrder}${enabled}`);
|
|
503
|
+
}
|
|
504
|
+
} catch (error) {
|
|
505
|
+
const apiError = error;
|
|
506
|
+
console.log(chalk3.red(`Feil: ${apiError.message}`));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function pagesContextCommand(options) {
|
|
510
|
+
const projectRoot = findProjectRoot();
|
|
511
|
+
if (!projectRoot) {
|
|
512
|
+
console.log(chalk3.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
513
|
+
console.log(chalk3.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const config = getProjectConfig(projectRoot);
|
|
517
|
+
if (!config) {
|
|
518
|
+
console.log(chalk3.red("Feil: Ugyldig prosjektkonfigurasjon."));
|
|
97
519
|
return;
|
|
98
520
|
}
|
|
99
521
|
if (options.delete && options.page) {
|
|
100
522
|
const deleted = deletePageContext(projectRoot, options.page);
|
|
101
523
|
if (deleted) {
|
|
102
|
-
console.log(
|
|
524
|
+
console.log(chalk3.green(`Slettet: ${options.page}`));
|
|
103
525
|
} else {
|
|
104
|
-
console.log(
|
|
526
|
+
console.log(chalk3.red(`Ikke funnet: ${options.page}`));
|
|
105
527
|
}
|
|
106
528
|
return;
|
|
107
529
|
}
|
|
108
530
|
if (options.page) {
|
|
109
531
|
const context = readPageContext(projectRoot, options.page);
|
|
110
532
|
if (!context) {
|
|
111
|
-
console.log(
|
|
112
|
-
console.log(
|
|
533
|
+
console.log(chalk3.red(`Side ikke funnet: ${options.page}`));
|
|
534
|
+
console.log(chalk3.dim('Bruk "kode pages context" for \xE5 liste bufrede sider'));
|
|
113
535
|
return;
|
|
114
536
|
}
|
|
115
537
|
if (options.json) {
|
|
@@ -121,113 +543,113 @@ async function pagesCommand(options) {
|
|
|
121
543
|
}
|
|
122
544
|
const pages = listCachedPages(projectRoot);
|
|
123
545
|
if (pages.length === 0) {
|
|
124
|
-
console.log(
|
|
125
|
-
console.log(
|
|
546
|
+
console.log(chalk3.yellow("Ingen bufrede sidekontekster."));
|
|
547
|
+
console.log(chalk3.dim('Bruk "kode html <url> --save" for \xE5 lagre sidestrukturer.'));
|
|
126
548
|
return;
|
|
127
549
|
}
|
|
128
550
|
if (options.json) {
|
|
129
551
|
console.log(JSON.stringify(pages, null, 2));
|
|
130
552
|
return;
|
|
131
553
|
}
|
|
132
|
-
console.log(
|
|
554
|
+
console.log(chalk3.bold(`Bufrede sidekontekster (${pages.length})`));
|
|
133
555
|
console.log();
|
|
134
556
|
for (const page of pages) {
|
|
135
557
|
const path = new URL(page.url).pathname;
|
|
136
|
-
const date = new Date(page.extractedAt).toLocaleDateString();
|
|
558
|
+
const date = new Date(page.extractedAt).toLocaleDateString("nb-NO");
|
|
137
559
|
const badges = [];
|
|
138
|
-
if (page.sectionCount > 0) badges.push(`${page.sectionCount}
|
|
139
|
-
if (page.cmsCollectionCount > 0) badges.push(
|
|
140
|
-
console.log(` ${
|
|
560
|
+
if (page.sectionCount > 0) badges.push(`${page.sectionCount} seksjoner`);
|
|
561
|
+
if (page.cmsCollectionCount > 0) badges.push(chalk3.cyan(`${page.cmsCollectionCount} CMS`));
|
|
562
|
+
console.log(` ${chalk3.bold(path)} ${chalk3.dim(`[${page.slug}]`)}`);
|
|
141
563
|
if (page.title) {
|
|
142
|
-
console.log(
|
|
564
|
+
console.log(chalk3.dim(` "${page.title}"`));
|
|
143
565
|
}
|
|
144
|
-
console.log(
|
|
566
|
+
console.log(chalk3.dim(` ${badges.join(", ")} \u2022 ${date}`));
|
|
145
567
|
console.log();
|
|
146
568
|
}
|
|
147
|
-
console.log(
|
|
148
|
-
console.log(
|
|
569
|
+
console.log(chalk3.dim('Bruk "kode pages context <slug>" for \xE5 se detaljer'));
|
|
570
|
+
console.log(chalk3.dim('Bruk "kode html <url> --save --force" for \xE5 oppdatere'));
|
|
149
571
|
}
|
|
150
572
|
function printPageDetails(context) {
|
|
151
|
-
console.log(
|
|
152
|
-
console.log(
|
|
153
|
-
console.log(
|
|
573
|
+
console.log(chalk3.bold(context.title || context.url));
|
|
574
|
+
console.log(chalk3.dim(context.url));
|
|
575
|
+
console.log(chalk3.dim(`Hentet: ${context.extractedAt}`));
|
|
154
576
|
console.log();
|
|
155
577
|
if (context.sections.length > 0) {
|
|
156
|
-
console.log(
|
|
578
|
+
console.log(chalk3.bold("Seksjoner"));
|
|
157
579
|
for (const section of context.sections) {
|
|
158
580
|
const name = section.heading || section.id || section.className?.split(" ")[0] || "section";
|
|
159
581
|
const badges = [];
|
|
160
|
-
if (section.hasCms) badges.push(
|
|
161
|
-
if (section.hasForm) badges.push(
|
|
582
|
+
if (section.hasCms) badges.push(chalk3.cyan("CMS"));
|
|
583
|
+
if (section.hasForm) badges.push(chalk3.yellow("Skjema"));
|
|
162
584
|
const badgeStr = badges.length > 0 ? ` [${badges.join(", ")}]` : "";
|
|
163
585
|
console.log(` \u2022 ${name}${badgeStr}`);
|
|
164
586
|
if (section.textSample) {
|
|
165
|
-
console.log(
|
|
587
|
+
console.log(chalk3.dim(` "${section.textSample.slice(0, 80)}..."`));
|
|
166
588
|
}
|
|
167
589
|
}
|
|
168
590
|
console.log();
|
|
169
591
|
}
|
|
170
592
|
if (context.headings.length > 0) {
|
|
171
|
-
console.log(
|
|
593
|
+
console.log(chalk3.bold("Overskrifter"));
|
|
172
594
|
for (const h of context.headings.slice(0, 10)) {
|
|
173
595
|
const level = "H" + h.level;
|
|
174
|
-
console.log(` ${
|
|
596
|
+
console.log(` ${chalk3.dim(level)} ${h.text}`);
|
|
175
597
|
}
|
|
176
598
|
if (context.headings.length > 10) {
|
|
177
|
-
console.log(
|
|
599
|
+
console.log(chalk3.dim(` ... og ${context.headings.length - 10} flere`));
|
|
178
600
|
}
|
|
179
601
|
console.log();
|
|
180
602
|
}
|
|
181
603
|
if (context.ctas.length > 0) {
|
|
182
|
-
console.log(
|
|
604
|
+
console.log(chalk3.bold("CTAs"));
|
|
183
605
|
for (const cta of context.ctas) {
|
|
184
|
-
const href = cta.href ?
|
|
606
|
+
const href = cta.href ? chalk3.dim(` \u2192 ${cta.href}`) : "";
|
|
185
607
|
console.log(` \u2022 "${cta.text}"${href}`);
|
|
186
|
-
console.log(
|
|
608
|
+
console.log(chalk3.dim(` i ${cta.location}`));
|
|
187
609
|
}
|
|
188
610
|
console.log();
|
|
189
611
|
}
|
|
190
612
|
if (context.forms.length > 0) {
|
|
191
|
-
console.log(
|
|
613
|
+
console.log(chalk3.bold("Skjemaer"));
|
|
192
614
|
for (const form of context.forms) {
|
|
193
|
-
console.log(` ${
|
|
615
|
+
console.log(` ${chalk3.bold(form.name || "skjema")}`);
|
|
194
616
|
for (const field of form.fields) {
|
|
195
|
-
const required = field.required ?
|
|
617
|
+
const required = field.required ? chalk3.red("*") : "";
|
|
196
618
|
const label = field.label || field.type;
|
|
197
|
-
console.log(` \u2022 ${label}${required} ${
|
|
619
|
+
console.log(` \u2022 ${label}${required} ${chalk3.dim(`(${field.type})`)}`);
|
|
198
620
|
}
|
|
199
621
|
if (form.submitText) {
|
|
200
|
-
console.log(` \u2192
|
|
622
|
+
console.log(` \u2192 Send: "${form.submitText}"`);
|
|
201
623
|
}
|
|
202
624
|
}
|
|
203
625
|
console.log();
|
|
204
626
|
}
|
|
205
627
|
if (context.cmsPatterns.length > 0) {
|
|
206
|
-
console.log(
|
|
628
|
+
console.log(chalk3.bold("CMS-samlinger"));
|
|
207
629
|
for (const cms of context.cmsPatterns) {
|
|
208
|
-
console.log(` ${
|
|
630
|
+
console.log(` ${chalk3.bold(cms.containerClass)}: ${chalk3.cyan(`${cms.itemCount} elementer`)}`);
|
|
209
631
|
if (cms.templateFields.length > 0) {
|
|
210
|
-
console.log(
|
|
632
|
+
console.log(chalk3.dim(` Malfelt: ${cms.templateFields.join(", ")}`));
|
|
211
633
|
}
|
|
212
634
|
}
|
|
213
635
|
console.log();
|
|
214
636
|
}
|
|
215
637
|
if (context.navigation.length > 0) {
|
|
216
|
-
console.log(
|
|
638
|
+
console.log(chalk3.bold("Navigasjon"));
|
|
217
639
|
for (const nav of context.navigation) {
|
|
218
|
-
console.log(` ${
|
|
640
|
+
console.log(` ${chalk3.bold(nav.type)}:`);
|
|
219
641
|
for (const item of nav.items.slice(0, 8)) {
|
|
220
|
-
const href = item.href ?
|
|
642
|
+
const href = item.href ? chalk3.dim(` \u2192 ${item.href}`) : "";
|
|
221
643
|
console.log(` \u2022 ${item.text}${href}`);
|
|
222
644
|
}
|
|
223
645
|
if (nav.items.length > 8) {
|
|
224
|
-
console.log(
|
|
646
|
+
console.log(chalk3.dim(` ... og ${nav.items.length - 8} flere`));
|
|
225
647
|
}
|
|
226
648
|
}
|
|
227
649
|
console.log();
|
|
228
650
|
}
|
|
229
651
|
if (context.notes && context.notes.length > 0) {
|
|
230
|
-
console.log(
|
|
652
|
+
console.log(chalk3.bold("Notater"));
|
|
231
653
|
for (const note of context.notes) {
|
|
232
654
|
console.log(` \u2022 ${note}`);
|
|
233
655
|
}
|
|
@@ -236,40 +658,40 @@ function printPageDetails(context) {
|
|
|
236
658
|
}
|
|
237
659
|
|
|
238
660
|
// src/commands/set.ts
|
|
239
|
-
import
|
|
240
|
-
import
|
|
661
|
+
import chalk4 from "chalk";
|
|
662
|
+
import ora3 from "ora";
|
|
241
663
|
async function setCommand(script, options) {
|
|
242
664
|
const projectRoot = findProjectRoot();
|
|
243
665
|
if (!projectRoot) {
|
|
244
|
-
console.log(
|
|
245
|
-
console.log(
|
|
666
|
+
console.log(chalk4.red("\u274C Not in a Cure Kode project."));
|
|
667
|
+
console.log(chalk4.dim(' Run "kode init" first.'));
|
|
246
668
|
return;
|
|
247
669
|
}
|
|
248
670
|
const config = getProjectConfig(projectRoot);
|
|
249
671
|
if (!config) {
|
|
250
|
-
console.log(
|
|
672
|
+
console.log(chalk4.red("\u274C Could not read project configuration."));
|
|
251
673
|
return;
|
|
252
674
|
}
|
|
253
675
|
if (!options.scope && options.autoLoad === void 0) {
|
|
254
|
-
console.log(
|
|
255
|
-
console.log(
|
|
676
|
+
console.log(chalk4.yellow("\u26A0\uFE0F No changes specified."));
|
|
677
|
+
console.log(chalk4.dim(" Use --scope or --auto-load/--no-auto-load"));
|
|
256
678
|
console.log();
|
|
257
|
-
console.log(
|
|
258
|
-
console.log(
|
|
259
|
-
console.log(
|
|
260
|
-
console.log(
|
|
679
|
+
console.log(chalk4.dim("Examples:"));
|
|
680
|
+
console.log(chalk4.dim(" kode set my-script --scope page-specific"));
|
|
681
|
+
console.log(chalk4.dim(" kode set my-script --scope global --auto-load"));
|
|
682
|
+
console.log(chalk4.dim(" kode set my-script --no-auto-load"));
|
|
261
683
|
return;
|
|
262
684
|
}
|
|
263
|
-
const spinner =
|
|
685
|
+
const spinner = ora3(`Updating ${script}...`).start();
|
|
264
686
|
try {
|
|
265
687
|
const client = createApiClient(config);
|
|
266
688
|
const scripts = await client.listScripts(config.siteId);
|
|
267
689
|
const targetScript = scripts.find((s) => s.slug === script || s.name === script);
|
|
268
690
|
if (!targetScript) {
|
|
269
691
|
spinner.fail(`Script "${script}" not found`);
|
|
270
|
-
console.log(
|
|
692
|
+
console.log(chalk4.dim("\nAvailable scripts:"));
|
|
271
693
|
scripts.forEach((s) => {
|
|
272
|
-
console.log(
|
|
694
|
+
console.log(chalk4.dim(` - ${s.slug}`));
|
|
273
695
|
});
|
|
274
696
|
return;
|
|
275
697
|
}
|
|
@@ -289,80 +711,80 @@ async function setCommand(script, options) {
|
|
|
289
711
|
}
|
|
290
712
|
updates.changeSummary = `Updated settings: ${changes.join(", ")}`;
|
|
291
713
|
const updated = await client.updateScript(targetScript.id, updates);
|
|
292
|
-
spinner.succeed(
|
|
714
|
+
spinner.succeed(chalk4.green(`Updated ${script}`));
|
|
293
715
|
console.log();
|
|
294
716
|
for (const change of changes) {
|
|
295
|
-
console.log(
|
|
717
|
+
console.log(chalk4.dim(` ${change}`));
|
|
296
718
|
}
|
|
297
719
|
console.log();
|
|
298
720
|
if (options.scope === "page-specific") {
|
|
299
|
-
console.log(
|
|
300
|
-
console.log(
|
|
301
|
-
console.log(
|
|
721
|
+
console.log(chalk4.yellow("\u26A0\uFE0F Page-specific scripts need page assignments to load."));
|
|
722
|
+
console.log(chalk4.dim(" Use app.cure.no \u2192 Kode to assign pages, or use MCP:"));
|
|
723
|
+
console.log(chalk4.dim(" kode_assign_script_to_page(scriptSlug, pageSlug)"));
|
|
302
724
|
console.log();
|
|
303
725
|
}
|
|
304
|
-
console.log(
|
|
726
|
+
console.log(chalk4.dim('Run "kode deploy" to make changes live.'));
|
|
305
727
|
} catch (error) {
|
|
306
728
|
spinner.fail("Failed to update script");
|
|
307
|
-
console.error(
|
|
729
|
+
console.error(chalk4.red("\nError:"), error);
|
|
308
730
|
}
|
|
309
731
|
}
|
|
310
732
|
|
|
311
733
|
// src/commands/production.ts
|
|
312
|
-
import
|
|
313
|
-
import
|
|
734
|
+
import chalk5 from "chalk";
|
|
735
|
+
import ora4 from "ora";
|
|
314
736
|
async function productionCommand(action, options) {
|
|
315
737
|
if (!["enable", "disable", "status"].includes(action)) {
|
|
316
|
-
console.log(
|
|
317
|
-
console.log(
|
|
318
|
-
console.log(
|
|
319
|
-
console.log(
|
|
738
|
+
console.log(chalk5.red("\u274C Invalid action. Use: enable, disable, or status"));
|
|
739
|
+
console.log(chalk5.dim(" kode production enable [--domain <domain>]"));
|
|
740
|
+
console.log(chalk5.dim(" kode production disable"));
|
|
741
|
+
console.log(chalk5.dim(" kode production status"));
|
|
320
742
|
return;
|
|
321
743
|
}
|
|
322
744
|
const projectRoot = findProjectRoot();
|
|
323
745
|
if (!projectRoot) {
|
|
324
|
-
console.log(
|
|
325
|
-
console.log(
|
|
746
|
+
console.log(chalk5.red("\u274C Not in a Cure Kode project."));
|
|
747
|
+
console.log(chalk5.dim(' Run "kode init" first.'));
|
|
326
748
|
return;
|
|
327
749
|
}
|
|
328
750
|
const config = getProjectConfig(projectRoot);
|
|
329
751
|
if (!config) {
|
|
330
|
-
console.log(
|
|
752
|
+
console.log(chalk5.red("\u274C Could not read project configuration."));
|
|
331
753
|
return;
|
|
332
754
|
}
|
|
333
755
|
const client = createApiClient(config);
|
|
334
756
|
if (action === "status") {
|
|
335
|
-
const spinner2 =
|
|
757
|
+
const spinner2 = ora4("Fetching production status...").start();
|
|
336
758
|
try {
|
|
337
759
|
const status = await client.getDeploymentStatus(config.siteId);
|
|
338
760
|
spinner2.stop();
|
|
339
761
|
console.log();
|
|
340
|
-
console.log(
|
|
762
|
+
console.log(chalk5.bold("Production Status"));
|
|
341
763
|
console.log();
|
|
342
764
|
if (status.productionEnabled) {
|
|
343
|
-
console.log(
|
|
765
|
+
console.log(chalk5.green(" \u25CF Produksjon er aktivert"));
|
|
344
766
|
if (status.productionDomain) {
|
|
345
|
-
console.log(
|
|
767
|
+
console.log(chalk5.dim(` Domain: ${status.productionDomain}`));
|
|
346
768
|
}
|
|
347
769
|
if (status.production.lastSuccessful) {
|
|
348
770
|
console.log(
|
|
349
|
-
|
|
771
|
+
chalk5.dim(
|
|
350
772
|
` Siste deploy: v${status.production.lastSuccessful.version}`
|
|
351
773
|
)
|
|
352
774
|
);
|
|
353
775
|
}
|
|
354
776
|
} else {
|
|
355
|
-
console.log(
|
|
356
|
-
console.log(
|
|
777
|
+
console.log(chalk5.gray(" \u25CB Produksjon er deaktivert"));
|
|
778
|
+
console.log(chalk5.dim(" Kun staging er aktiv"));
|
|
357
779
|
}
|
|
358
780
|
console.log();
|
|
359
781
|
} catch (error) {
|
|
360
782
|
spinner2.fail("Failed to fetch status");
|
|
361
|
-
console.error(
|
|
783
|
+
console.error(chalk5.red("\nError:"), error.message || error);
|
|
362
784
|
}
|
|
363
785
|
return;
|
|
364
786
|
}
|
|
365
|
-
const spinner =
|
|
787
|
+
const spinner = ora4(
|
|
366
788
|
action === "enable" ? "Aktiverer produksjon..." : "Deaktiverer produksjon..."
|
|
367
789
|
).start();
|
|
368
790
|
try {
|
|
@@ -374,99 +796,830 @@ async function productionCommand(action, options) {
|
|
|
374
796
|
spinner.stop();
|
|
375
797
|
console.log();
|
|
376
798
|
if (action === "enable") {
|
|
377
|
-
console.log(
|
|
799
|
+
console.log(chalk5.green("\u2713 Produksjon er n\xE5 aktivert"));
|
|
378
800
|
if (result.productionDomain) {
|
|
379
|
-
console.log(
|
|
801
|
+
console.log(chalk5.dim(` Domain: ${result.productionDomain}`));
|
|
380
802
|
}
|
|
381
803
|
console.log();
|
|
382
|
-
console.log(
|
|
383
|
-
console.log(
|
|
384
|
-
console.log(
|
|
804
|
+
console.log(chalk5.dim(" Neste steg:"));
|
|
805
|
+
console.log(chalk5.dim(" 1. Deploy til staging: kode deploy"));
|
|
806
|
+
console.log(chalk5.dim(" 2. Promoter til produksjon: kode deploy --promote"));
|
|
385
807
|
} else {
|
|
386
|
-
console.log(
|
|
387
|
-
console.log(
|
|
808
|
+
console.log(chalk5.yellow("\u2713 Produksjon er n\xE5 deaktivert"));
|
|
809
|
+
console.log(chalk5.dim(" Kun staging-milj\xF8et er aktivt."));
|
|
388
810
|
console.log(
|
|
389
|
-
|
|
811
|
+
chalk5.dim(" Produksjonsdomenet vil f\xE5 en tom script-respons.")
|
|
390
812
|
);
|
|
391
813
|
}
|
|
392
814
|
console.log();
|
|
393
815
|
} catch (error) {
|
|
394
816
|
spinner.fail(action === "enable" ? "Kunne ikke aktivere produksjon" : "Kunne ikke deaktivere produksjon");
|
|
395
|
-
console.error(
|
|
817
|
+
console.error(chalk5.red("\nError:"), error.message || error);
|
|
396
818
|
}
|
|
397
819
|
}
|
|
398
820
|
|
|
399
821
|
// src/commands/update-claude-md.ts
|
|
400
|
-
import
|
|
401
|
-
import
|
|
402
|
-
import { join } from "path";
|
|
822
|
+
import chalk6 from "chalk";
|
|
823
|
+
import ora5 from "ora";
|
|
403
824
|
async function updateClaudeMdCommand() {
|
|
404
825
|
const projectRoot = findProjectRoot();
|
|
405
826
|
if (!projectRoot) {
|
|
406
|
-
console.log(
|
|
407
|
-
console.log(
|
|
827
|
+
console.log(chalk6.red("\u274C Ikke i et Cure Kode-prosjekt."));
|
|
828
|
+
console.log(chalk6.dim(' Kj\xF8r "kode init" f\xF8rst.'));
|
|
408
829
|
return;
|
|
409
830
|
}
|
|
410
831
|
const config = getProjectConfig(projectRoot);
|
|
411
832
|
if (!config) {
|
|
412
|
-
console.log(
|
|
833
|
+
console.log(chalk6.red("\u274C Kunne ikke lese prosjektkonfigurasjon."));
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const spinner = ora5("Henter skript og sider...").start();
|
|
837
|
+
try {
|
|
838
|
+
const client = createApiClient(config);
|
|
839
|
+
const [scripts, pages] = await Promise.all([
|
|
840
|
+
client.listScripts(config.siteId),
|
|
841
|
+
client.listPages(config.siteId)
|
|
842
|
+
]);
|
|
843
|
+
spinner.succeed(`Fant ${scripts.length} skript og ${pages.length} sider`);
|
|
844
|
+
const result = updateKodeDocs(
|
|
845
|
+
projectRoot,
|
|
846
|
+
config.siteName,
|
|
847
|
+
config.siteSlug,
|
|
848
|
+
scriptsToDocsFormat(scripts),
|
|
849
|
+
pagesToInfoFormat(pages)
|
|
850
|
+
);
|
|
851
|
+
if (result.kodeMd.created) {
|
|
852
|
+
console.log(chalk6.green("\u2705 Opprettet .cure-kode/KODE.md"));
|
|
853
|
+
} else {
|
|
854
|
+
console.log(chalk6.green("\u2705 Oppdatert .cure-kode/KODE.md"));
|
|
855
|
+
}
|
|
856
|
+
if (result.claudeMd.created) {
|
|
857
|
+
console.log(chalk6.green("\u2705 Opprettet CLAUDE.md med referanse"));
|
|
858
|
+
} else if (result.claudeMd.updated) {
|
|
859
|
+
console.log(chalk6.green("\u2705 La til Kode-referanse i CLAUDE.md"));
|
|
860
|
+
} else {
|
|
861
|
+
console.log(chalk6.dim(" CLAUDE.md har allerede referanse"));
|
|
862
|
+
}
|
|
863
|
+
console.log();
|
|
864
|
+
console.log(chalk6.dim("KODE.md inneholder n\xE5:"));
|
|
865
|
+
console.log(chalk6.dim(" \u2022 CDN URL og Webflow-oppsett"));
|
|
866
|
+
console.log(chalk6.dim(" \u2022 Liste over alle skript"));
|
|
867
|
+
if (pages.length > 0) {
|
|
868
|
+
console.log(chalk6.dim(" \u2022 Liste over sider og URL-m\xF8nstre"));
|
|
869
|
+
}
|
|
870
|
+
console.log(chalk6.dim(" \u2022 CLI-kommandoer og MCP-verkt\xF8y"));
|
|
871
|
+
} catch (error) {
|
|
872
|
+
spinner.fail("Kunne ikke oppdatere dokumentasjon");
|
|
873
|
+
console.error(chalk6.red("\nFeil:"), error.message || error);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// src/commands/sync.ts
|
|
878
|
+
import chalk7 from "chalk";
|
|
879
|
+
import ora6 from "ora";
|
|
880
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, readdirSync, mkdirSync } from "fs";
|
|
881
|
+
import { join as join2, basename, extname } from "path";
|
|
882
|
+
import { createHash } from "crypto";
|
|
883
|
+
function hashContent(content) {
|
|
884
|
+
return createHash("sha256").update(content).digest("hex").substring(0, 16);
|
|
885
|
+
}
|
|
886
|
+
async function syncCommand(options) {
|
|
887
|
+
const projectRoot = findProjectRoot();
|
|
888
|
+
if (!projectRoot) {
|
|
889
|
+
console.log(chalk7.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
890
|
+
console.log(chalk7.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const config = getProjectConfig(projectRoot);
|
|
894
|
+
if (!config) {
|
|
895
|
+
console.log(chalk7.red("Feil: Kunne ikke lese prosjektkonfigurasjon."));
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
const scriptsDir = getScriptsDir(projectRoot, config);
|
|
899
|
+
if (!existsSync2(scriptsDir)) {
|
|
900
|
+
mkdirSync(scriptsDir, { recursive: true });
|
|
901
|
+
}
|
|
902
|
+
const metadataPath = join2(projectRoot, ".cure-kode", "scripts.json");
|
|
903
|
+
let metadata = [];
|
|
904
|
+
if (existsSync2(metadataPath)) {
|
|
905
|
+
try {
|
|
906
|
+
metadata = JSON.parse(readFileSync2(metadataPath, "utf-8"));
|
|
907
|
+
} catch {
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
const spinner = ora6("Analyserer synkroniseringsstatus...").start();
|
|
911
|
+
try {
|
|
912
|
+
const client = createApiClient(config);
|
|
913
|
+
const remoteScripts = await client.listScripts(config.siteId);
|
|
914
|
+
const localFiles = existsSync2(scriptsDir) ? readdirSync(scriptsDir).filter((f) => f.endsWith(".js") || f.endsWith(".css")) : [];
|
|
915
|
+
const syncStatuses = [];
|
|
916
|
+
for (const remote of remoteScripts) {
|
|
917
|
+
const ext = remote.type === "javascript" ? "js" : "css";
|
|
918
|
+
const fileName = `${remote.slug}.${ext}`;
|
|
919
|
+
const filePath = join2(scriptsDir, fileName);
|
|
920
|
+
const localMeta = metadata.find((m) => m.slug === remote.slug);
|
|
921
|
+
const lastPulledVersion = localMeta?.lastPulledVersion || 0;
|
|
922
|
+
if (!existsSync2(filePath)) {
|
|
923
|
+
syncStatuses.push({
|
|
924
|
+
slug: remote.slug,
|
|
925
|
+
fileName,
|
|
926
|
+
status: "remote-only",
|
|
927
|
+
remoteVersion: remote.current_version,
|
|
928
|
+
remoteContent: remote.content
|
|
929
|
+
});
|
|
930
|
+
} else {
|
|
931
|
+
const localContent = readFileSync2(filePath, "utf-8");
|
|
932
|
+
const localHash = hashContent(localContent);
|
|
933
|
+
const hasLocalChanges = localMeta && localHash !== localMeta.contentHash;
|
|
934
|
+
const hasRemoteChanges = remote.current_version > lastPulledVersion;
|
|
935
|
+
if (hasLocalChanges && hasRemoteChanges) {
|
|
936
|
+
syncStatuses.push({
|
|
937
|
+
slug: remote.slug,
|
|
938
|
+
fileName,
|
|
939
|
+
status: "conflict",
|
|
940
|
+
localVersion: lastPulledVersion,
|
|
941
|
+
remoteVersion: remote.current_version,
|
|
942
|
+
localContent,
|
|
943
|
+
remoteContent: remote.content
|
|
944
|
+
});
|
|
945
|
+
} else if (hasLocalChanges) {
|
|
946
|
+
syncStatuses.push({
|
|
947
|
+
slug: remote.slug,
|
|
948
|
+
fileName,
|
|
949
|
+
status: "local-changed",
|
|
950
|
+
localVersion: lastPulledVersion,
|
|
951
|
+
remoteVersion: remote.current_version,
|
|
952
|
+
localContent
|
|
953
|
+
});
|
|
954
|
+
} else if (hasRemoteChanges) {
|
|
955
|
+
syncStatuses.push({
|
|
956
|
+
slug: remote.slug,
|
|
957
|
+
fileName,
|
|
958
|
+
status: "remote-changed",
|
|
959
|
+
localVersion: lastPulledVersion,
|
|
960
|
+
remoteVersion: remote.current_version,
|
|
961
|
+
remoteContent: remote.content
|
|
962
|
+
});
|
|
963
|
+
} else if (localContent !== remote.content) {
|
|
964
|
+
syncStatuses.push({
|
|
965
|
+
slug: remote.slug,
|
|
966
|
+
fileName,
|
|
967
|
+
status: "remote-changed",
|
|
968
|
+
remoteVersion: remote.current_version,
|
|
969
|
+
remoteContent: remote.content
|
|
970
|
+
});
|
|
971
|
+
} else {
|
|
972
|
+
syncStatuses.push({
|
|
973
|
+
slug: remote.slug,
|
|
974
|
+
fileName,
|
|
975
|
+
status: "up-to-date",
|
|
976
|
+
localVersion: remote.current_version,
|
|
977
|
+
remoteVersion: remote.current_version
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
for (const file of localFiles) {
|
|
983
|
+
const slug = basename(file, extname(file));
|
|
984
|
+
if (!remoteScripts.find((r) => r.slug === slug)) {
|
|
985
|
+
syncStatuses.push({
|
|
986
|
+
slug,
|
|
987
|
+
fileName: file,
|
|
988
|
+
status: "local-only",
|
|
989
|
+
localContent: readFileSync2(join2(scriptsDir, file), "utf-8")
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
spinner.stop();
|
|
994
|
+
const upToDate = syncStatuses.filter((s) => s.status === "up-to-date");
|
|
995
|
+
const toPull = syncStatuses.filter((s) => s.status === "remote-only" || s.status === "remote-changed");
|
|
996
|
+
const toPush = syncStatuses.filter((s) => s.status === "local-only" || s.status === "local-changed");
|
|
997
|
+
const conflicts = syncStatuses.filter((s) => s.status === "conflict");
|
|
998
|
+
console.log();
|
|
999
|
+
console.log(chalk7.bold("Synkroniseringsstatus:"));
|
|
1000
|
+
console.log();
|
|
1001
|
+
if (upToDate.length > 0) {
|
|
1002
|
+
console.log(chalk7.green(` \u2713 ${upToDate.length} skript er oppdatert`));
|
|
1003
|
+
}
|
|
1004
|
+
if (toPull.length > 0) {
|
|
1005
|
+
console.log(chalk7.cyan(` \u2193 ${toPull.length} skript \xE5 hente fra server`));
|
|
1006
|
+
for (const s of toPull) {
|
|
1007
|
+
const versionInfo = s.status === "remote-changed" ? chalk7.dim(` (v${s.localVersion || "?"} \u2192 v${s.remoteVersion})`) : chalk7.dim(" (ny)");
|
|
1008
|
+
console.log(` ${s.fileName}${versionInfo}`);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (toPush.length > 0) {
|
|
1012
|
+
console.log(chalk7.yellow(` \u2191 ${toPush.length} skript \xE5 laste opp`));
|
|
1013
|
+
for (const s of toPush) {
|
|
1014
|
+
const versionInfo = s.status === "local-changed" ? chalk7.dim(` (lokale endringer)`) : chalk7.dim(" (ny)");
|
|
1015
|
+
console.log(` ${s.fileName}${versionInfo}`);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (conflicts.length > 0) {
|
|
1019
|
+
console.log(chalk7.red(` \u26A0 ${conflicts.length} konflikter`));
|
|
1020
|
+
for (const s of conflicts) {
|
|
1021
|
+
console.log(` ${s.fileName} ${chalk7.dim(`(lokal v${s.localVersion}, server v${s.remoteVersion})`)}`);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
if (options.dryRun) {
|
|
1025
|
+
console.log();
|
|
1026
|
+
console.log(chalk7.dim("T\xF8rrkj\xF8ring - ingen endringer gjort"));
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
if (toPull.length === 0 && toPush.length === 0 && conflicts.length === 0) {
|
|
1030
|
+
console.log();
|
|
1031
|
+
console.log(chalk7.green("Alt er synkronisert!"));
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
console.log();
|
|
1035
|
+
let pulled = 0;
|
|
1036
|
+
for (const s of toPull) {
|
|
1037
|
+
const filePath = join2(scriptsDir, s.fileName);
|
|
1038
|
+
writeFileSync2(filePath, s.remoteContent);
|
|
1039
|
+
console.log(chalk7.cyan(` \u2193 ${s.fileName}`));
|
|
1040
|
+
pulled++;
|
|
1041
|
+
}
|
|
1042
|
+
let pushed = 0;
|
|
1043
|
+
for (const s of toPush) {
|
|
1044
|
+
const filePath = join2(scriptsDir, s.fileName);
|
|
1045
|
+
const content = s.localContent;
|
|
1046
|
+
const type = extname(s.fileName) === ".js" ? "javascript" : "css";
|
|
1047
|
+
const remote = remoteScripts.find((r) => r.slug === s.slug);
|
|
1048
|
+
if (remote) {
|
|
1049
|
+
await client.updateScript(remote.id, {
|
|
1050
|
+
content,
|
|
1051
|
+
changeSummary: "Synkronisert via CLI"
|
|
1052
|
+
});
|
|
1053
|
+
} else {
|
|
1054
|
+
await client.createScript(config.siteId, {
|
|
1055
|
+
name: s.slug.charAt(0).toUpperCase() + s.slug.slice(1).replace(/-/g, " "),
|
|
1056
|
+
slug: s.slug,
|
|
1057
|
+
type,
|
|
1058
|
+
scope: "global",
|
|
1059
|
+
content
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
console.log(chalk7.yellow(` \u2191 ${s.fileName}`));
|
|
1063
|
+
pushed++;
|
|
1064
|
+
}
|
|
1065
|
+
console.log();
|
|
1066
|
+
if (pulled > 0) {
|
|
1067
|
+
console.log(chalk7.cyan(`Hentet ${pulled} skript`));
|
|
1068
|
+
}
|
|
1069
|
+
if (pushed > 0) {
|
|
1070
|
+
console.log(chalk7.yellow(`Lastet opp ${pushed} skript`));
|
|
1071
|
+
}
|
|
1072
|
+
if (conflicts.length > 0) {
|
|
1073
|
+
console.log();
|
|
1074
|
+
console.log(chalk7.red(`${conflicts.length} konflikter krever manuell h\xE5ndtering:`));
|
|
1075
|
+
console.log(chalk7.dim(' Bruk "kode diff <script>" for \xE5 se forskjeller'));
|
|
1076
|
+
console.log(chalk7.dim(' Bruk "kode push --force <script>" for \xE5 overskrive server'));
|
|
1077
|
+
console.log(chalk7.dim(' Bruk "kode pull --force <script>" for \xE5 overskrive lokalt'));
|
|
1078
|
+
}
|
|
1079
|
+
const updatedScripts = await client.listScripts(config.siteId);
|
|
1080
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1081
|
+
const updatedMetadata = updatedScripts.map((s) => {
|
|
1082
|
+
const ext = s.type === "javascript" ? "js" : "css";
|
|
1083
|
+
const filePath = join2(scriptsDir, `${s.slug}.${ext}`);
|
|
1084
|
+
const localContent = existsSync2(filePath) ? readFileSync2(filePath, "utf-8") : s.content;
|
|
1085
|
+
return {
|
|
1086
|
+
id: s.id,
|
|
1087
|
+
slug: s.slug,
|
|
1088
|
+
name: s.name,
|
|
1089
|
+
type: s.type,
|
|
1090
|
+
scope: s.scope,
|
|
1091
|
+
autoLoad: s.auto_load,
|
|
1092
|
+
version: s.current_version,
|
|
1093
|
+
loadOrder: s.load_order,
|
|
1094
|
+
lastPulledVersion: s.current_version,
|
|
1095
|
+
lastPulledAt: now,
|
|
1096
|
+
contentHash: hashContent(localContent)
|
|
1097
|
+
};
|
|
1098
|
+
});
|
|
1099
|
+
writeFileSync2(metadataPath, JSON.stringify(updatedMetadata, null, 2));
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
spinner.fail("Synkronisering feilet");
|
|
1102
|
+
console.error(chalk7.red("\nFeil:"), error);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// src/commands/diff.ts
|
|
1107
|
+
import chalk8 from "chalk";
|
|
1108
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
|
|
1109
|
+
import { join as join3 } from "path";
|
|
1110
|
+
function showDiff(local, remote) {
|
|
1111
|
+
const localLines = local.split("\n");
|
|
1112
|
+
const remoteLines = remote.split("\n");
|
|
1113
|
+
let commonPrefix = 0;
|
|
1114
|
+
while (commonPrefix < localLines.length && commonPrefix < remoteLines.length && localLines[commonPrefix] === remoteLines[commonPrefix]) {
|
|
1115
|
+
commonPrefix++;
|
|
1116
|
+
}
|
|
1117
|
+
let commonSuffix = 0;
|
|
1118
|
+
while (commonSuffix < localLines.length - commonPrefix && commonSuffix < remoteLines.length - commonPrefix && localLines[localLines.length - 1 - commonSuffix] === remoteLines[remoteLines.length - 1 - commonSuffix]) {
|
|
1119
|
+
commonSuffix++;
|
|
1120
|
+
}
|
|
1121
|
+
const contextLines = 3;
|
|
1122
|
+
const startLine = Math.max(0, commonPrefix - contextLines);
|
|
1123
|
+
const localEndLine = localLines.length - commonSuffix;
|
|
1124
|
+
const remoteEndLine = remoteLines.length - commonSuffix;
|
|
1125
|
+
console.log();
|
|
1126
|
+
console.log(chalk8.bold("Forskjeller:"));
|
|
1127
|
+
console.log(chalk8.dim("\u2500".repeat(60)));
|
|
1128
|
+
if (startLine < commonPrefix) {
|
|
1129
|
+
for (let i = startLine; i < commonPrefix; i++) {
|
|
1130
|
+
console.log(chalk8.dim(` ${(i + 1).toString().padStart(4)} \u2502 ${localLines[i]}`));
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (localEndLine > commonPrefix) {
|
|
1134
|
+
console.log(chalk8.red.bold(`
|
|
1135
|
+
--- Lokal (linje ${commonPrefix + 1}-${localEndLine}) ---`));
|
|
1136
|
+
for (let i = commonPrefix; i < localEndLine; i++) {
|
|
1137
|
+
console.log(chalk8.red(`- ${(i + 1).toString().padStart(4)} \u2502 ${localLines[i]}`));
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if (remoteEndLine > commonPrefix) {
|
|
1141
|
+
console.log(chalk8.green.bold(`
|
|
1142
|
+
+++ Server (linje ${commonPrefix + 1}-${remoteEndLine}) ---`));
|
|
1143
|
+
for (let i = commonPrefix; i < remoteEndLine; i++) {
|
|
1144
|
+
console.log(chalk8.green(`+ ${(i + 1).toString().padStart(4)} \u2502 ${remoteLines[i]}`));
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
const suffixStart = localLines.length - commonSuffix;
|
|
1148
|
+
const suffixEnd = Math.min(localLines.length, suffixStart + contextLines);
|
|
1149
|
+
if (commonSuffix > 0 && suffixStart < suffixEnd) {
|
|
1150
|
+
console.log();
|
|
1151
|
+
for (let i = suffixStart; i < suffixEnd; i++) {
|
|
1152
|
+
console.log(chalk8.dim(` ${(i + 1).toString().padStart(4)} \u2502 ${localLines[i]}`));
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
console.log(chalk8.dim("\u2500".repeat(60)));
|
|
1156
|
+
}
|
|
1157
|
+
async function diffCommand(scriptSlug) {
|
|
1158
|
+
const projectRoot = findProjectRoot();
|
|
1159
|
+
if (!projectRoot) {
|
|
1160
|
+
console.log(chalk8.red("Feil: Ikke i et Cure Kode-prosjekt."));
|
|
1161
|
+
console.log(chalk8.dim('Kj\xF8r "kode init" f\xF8rst.'));
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const config = getProjectConfig(projectRoot);
|
|
1165
|
+
if (!config) {
|
|
1166
|
+
console.log(chalk8.red("Feil: Kunne ikke lese prosjektkonfigurasjon."));
|
|
413
1167
|
return;
|
|
414
1168
|
}
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
1169
|
+
const scriptsDir = getScriptsDir(projectRoot, config);
|
|
1170
|
+
try {
|
|
1171
|
+
const client = createApiClient(config);
|
|
1172
|
+
const remoteScripts = await client.listScripts(config.siteId);
|
|
1173
|
+
const remote = remoteScripts.find(
|
|
1174
|
+
(s) => s.slug === scriptSlug || s.name === scriptSlug
|
|
1175
|
+
);
|
|
1176
|
+
let localPath = null;
|
|
1177
|
+
let localContent = null;
|
|
1178
|
+
const jsPath = join3(scriptsDir, `${scriptSlug}.js`);
|
|
1179
|
+
const cssPath = join3(scriptsDir, `${scriptSlug}.css`);
|
|
1180
|
+
if (existsSync3(jsPath)) {
|
|
1181
|
+
localPath = jsPath;
|
|
1182
|
+
localContent = readFileSync3(jsPath, "utf-8");
|
|
1183
|
+
} else if (existsSync3(cssPath)) {
|
|
1184
|
+
localPath = cssPath;
|
|
1185
|
+
localContent = readFileSync3(cssPath, "utf-8");
|
|
1186
|
+
}
|
|
1187
|
+
if (!remote && !localContent) {
|
|
1188
|
+
console.log(chalk8.red(`Fant ikke skript "${scriptSlug}".`));
|
|
1189
|
+
console.log(chalk8.dim("Tilgjengelige skript:"));
|
|
1190
|
+
remoteScripts.forEach((s) => {
|
|
1191
|
+
console.log(chalk8.dim(` - ${s.slug}`));
|
|
1192
|
+
});
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
if (!remote && localContent) {
|
|
1196
|
+
console.log(chalk8.yellow(`Skript "${scriptSlug}" finnes bare lokalt (ikke p\xE5 server).`));
|
|
1197
|
+
console.log(chalk8.dim(`
|
|
1198
|
+
Filsti: ${localPath}`));
|
|
1199
|
+
console.log(chalk8.dim(`Linjer: ${localContent.split("\n").length}`));
|
|
1200
|
+
console.log(chalk8.dim(`Tegn: ${localContent.length}`));
|
|
1201
|
+
console.log();
|
|
1202
|
+
console.log(chalk8.dim('Bruk "kode push" for \xE5 laste opp til server.'));
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
if (remote && !localContent) {
|
|
1206
|
+
console.log(chalk8.yellow(`Skript "${scriptSlug}" finnes bare p\xE5 server (ikke lokalt).`));
|
|
1207
|
+
console.log(chalk8.dim(`
|
|
1208
|
+
Versjon: v${remote.current_version}`));
|
|
1209
|
+
console.log(chalk8.dim(`Type: ${remote.type}`));
|
|
1210
|
+
console.log(chalk8.dim(`Linjer: ${remote.content.split("\n").length}`));
|
|
1211
|
+
console.log(chalk8.dim(`Tegn: ${remote.content.length}`));
|
|
1212
|
+
console.log();
|
|
1213
|
+
console.log(chalk8.dim('Bruk "kode pull" for \xE5 hente fra server.'));
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
console.log(chalk8.bold(`Sammenligner "${scriptSlug}":`));
|
|
1217
|
+
console.log();
|
|
1218
|
+
console.log(chalk8.cyan("Lokal:"));
|
|
1219
|
+
console.log(chalk8.dim(` Filsti: ${localPath}`));
|
|
1220
|
+
console.log(chalk8.dim(` Linjer: ${localContent.split("\n").length}`));
|
|
1221
|
+
console.log(chalk8.dim(` Tegn: ${localContent.length}`));
|
|
1222
|
+
console.log();
|
|
1223
|
+
console.log(chalk8.green("Server:"));
|
|
1224
|
+
console.log(chalk8.dim(` Versjon: v${remote.current_version}`));
|
|
1225
|
+
console.log(chalk8.dim(` Type: ${remote.type}`));
|
|
1226
|
+
console.log(chalk8.dim(` Linjer: ${remote.content.split("\n").length}`));
|
|
1227
|
+
console.log(chalk8.dim(` Tegn: ${remote.content.length}`));
|
|
1228
|
+
if (localContent === remote.content) {
|
|
1229
|
+
console.log();
|
|
1230
|
+
console.log(chalk8.green("\u2713 Innholdet er identisk"));
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
showDiff(localContent, remote.content);
|
|
1234
|
+
console.log();
|
|
1235
|
+
console.log(chalk8.dim("Handlinger:"));
|
|
1236
|
+
console.log(chalk8.dim(" kode push --force " + scriptSlug + " \u2192 Bruk lokal versjon"));
|
|
1237
|
+
console.log(chalk8.dim(" kode pull --force " + scriptSlug + " \u2192 Bruk server-versjon"));
|
|
1238
|
+
} catch (error) {
|
|
1239
|
+
console.error(chalk8.red("\nFeil:"), error);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// src/commands/doctor.ts
|
|
1244
|
+
import chalk9 from "chalk";
|
|
1245
|
+
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
1246
|
+
import { join as join4 } from "path";
|
|
1247
|
+
function isNewerVersion(v1, v2) {
|
|
1248
|
+
const parts1 = v1.split(".").map(Number);
|
|
1249
|
+
const parts2 = v2.split(".").map(Number);
|
|
1250
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
1251
|
+
const p1 = parts1[i] || 0;
|
|
1252
|
+
const p2 = parts2[i] || 0;
|
|
1253
|
+
if (p2 > p1) return true;
|
|
1254
|
+
if (p2 < p1) return false;
|
|
1255
|
+
}
|
|
1256
|
+
return false;
|
|
1257
|
+
}
|
|
1258
|
+
async function doctorCommand() {
|
|
1259
|
+
console.log();
|
|
1260
|
+
console.log(chalk9.bold("\u{1F50D} Cure Kode Doctor"));
|
|
1261
|
+
console.log();
|
|
1262
|
+
const results = [];
|
|
1263
|
+
const configChecks = [];
|
|
1264
|
+
const projectRoot = findProjectRoot();
|
|
1265
|
+
if (!projectRoot) {
|
|
1266
|
+
configChecks.push({
|
|
1267
|
+
status: "fail",
|
|
1268
|
+
message: "Konfigurasjonsfil ikke funnet",
|
|
1269
|
+
detail: 'Kj\xF8r "kode init" for \xE5 sette opp prosjektet'
|
|
1270
|
+
});
|
|
1271
|
+
results.push({ category: "Prosjektkonfigurasjon", checks: configChecks });
|
|
1272
|
+
printResults(results);
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
configChecks.push({
|
|
1276
|
+
status: "pass",
|
|
1277
|
+
message: "Konfigurasjonsfil funnet"
|
|
1278
|
+
});
|
|
1279
|
+
const config = getProjectConfig(projectRoot);
|
|
1280
|
+
if (!config) {
|
|
1281
|
+
configChecks.push({
|
|
1282
|
+
status: "fail",
|
|
1283
|
+
message: "Kunne ikke lese konfigurasjon",
|
|
1284
|
+
detail: "config.json kan v\xE6re korrupt"
|
|
1285
|
+
});
|
|
1286
|
+
results.push({ category: "Prosjektkonfigurasjon", checks: configChecks });
|
|
1287
|
+
printResults(results);
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
if (config.apiKey && config.apiKey.startsWith("ck_")) {
|
|
1291
|
+
const maskedKey = `${config.apiKey.substring(0, 6)}***${config.apiKey.substring(config.apiKey.length - 3)}`;
|
|
1292
|
+
configChecks.push({
|
|
1293
|
+
status: "pass",
|
|
1294
|
+
message: `API-n\xF8kkel satt (${maskedKey})`
|
|
1295
|
+
});
|
|
441
1296
|
} else {
|
|
442
|
-
|
|
1297
|
+
configChecks.push({
|
|
1298
|
+
status: "fail",
|
|
1299
|
+
message: "API-n\xF8kkel mangler eller ugyldig format",
|
|
1300
|
+
detail: 'Kj\xF8r "kode init" p\xE5 nytt'
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
configChecks.push({
|
|
1304
|
+
status: "pass",
|
|
1305
|
+
message: `Site: ${config.siteName}`,
|
|
1306
|
+
detail: config.siteSlug
|
|
1307
|
+
});
|
|
1308
|
+
results.push({ category: "Prosjektkonfigurasjon", checks: configChecks });
|
|
1309
|
+
const networkChecks = [];
|
|
1310
|
+
try {
|
|
1311
|
+
const client = createApiClient(config);
|
|
1312
|
+
const scripts = await client.listScripts(config.siteId);
|
|
1313
|
+
networkChecks.push({
|
|
1314
|
+
status: "pass",
|
|
1315
|
+
message: "API tilgjengelig"
|
|
1316
|
+
});
|
|
1317
|
+
networkChecks.push({
|
|
1318
|
+
status: "pass",
|
|
1319
|
+
message: `${scripts.length} skript p\xE5 server`
|
|
1320
|
+
});
|
|
1321
|
+
} catch (error) {
|
|
1322
|
+
if (error instanceof KodeApiError) {
|
|
1323
|
+
if (error.statusCode === 401) {
|
|
1324
|
+
networkChecks.push({
|
|
1325
|
+
status: "fail",
|
|
1326
|
+
message: "API-n\xF8kkel ugyldig",
|
|
1327
|
+
detail: "Sjekk at n\xF8kkelen er korrekt i config.json"
|
|
1328
|
+
});
|
|
1329
|
+
} else if (error.statusCode === 404) {
|
|
1330
|
+
networkChecks.push({
|
|
1331
|
+
status: "fail",
|
|
1332
|
+
message: "Site ikke funnet",
|
|
1333
|
+
detail: `Site ID "${config.siteId}" finnes ikke`
|
|
1334
|
+
});
|
|
1335
|
+
} else {
|
|
1336
|
+
networkChecks.push({
|
|
1337
|
+
status: "fail",
|
|
1338
|
+
message: `API-feil: ${error.statusCode}`,
|
|
1339
|
+
detail: error.message
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
} else if (error.code === "ENOTFOUND" || error.code === "ECONNREFUSED") {
|
|
1343
|
+
networkChecks.push({
|
|
1344
|
+
status: "fail",
|
|
1345
|
+
message: "Kunne ikke n\xE5 API",
|
|
1346
|
+
detail: "Sjekk internettforbindelse"
|
|
1347
|
+
});
|
|
1348
|
+
} else {
|
|
1349
|
+
networkChecks.push({
|
|
1350
|
+
status: "fail",
|
|
1351
|
+
message: "Nettverksfeil",
|
|
1352
|
+
detail: error.message || "Ukjent feil"
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
443
1355
|
}
|
|
1356
|
+
results.push({ category: "Nettverk & API", checks: networkChecks });
|
|
1357
|
+
const mcpChecks = [];
|
|
1358
|
+
const claudeConfigPaths = [
|
|
1359
|
+
join4(projectRoot, ".mcp.json"),
|
|
1360
|
+
join4(projectRoot, "mcp.json"),
|
|
1361
|
+
join4(projectRoot, ".claude", "mcp.json")
|
|
1362
|
+
];
|
|
1363
|
+
let mcpConfigFound = false;
|
|
1364
|
+
let mcpConfig = null;
|
|
1365
|
+
let mcpConfigPath = "";
|
|
1366
|
+
for (const configPath of claudeConfigPaths) {
|
|
1367
|
+
if (existsSync4(configPath)) {
|
|
1368
|
+
try {
|
|
1369
|
+
mcpConfig = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
1370
|
+
mcpConfigPath = configPath;
|
|
1371
|
+
mcpConfigFound = true;
|
|
1372
|
+
break;
|
|
1373
|
+
} catch {
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
1378
|
+
const homeMcpPath = join4(homeDir, ".claude", "mcp.json");
|
|
1379
|
+
if (!mcpConfigFound && existsSync4(homeMcpPath)) {
|
|
1380
|
+
try {
|
|
1381
|
+
mcpConfig = JSON.parse(readFileSync4(homeMcpPath, "utf-8"));
|
|
1382
|
+
mcpConfigPath = homeMcpPath;
|
|
1383
|
+
mcpConfigFound = true;
|
|
1384
|
+
} catch {
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
if (mcpConfigFound) {
|
|
1388
|
+
mcpChecks.push({
|
|
1389
|
+
status: "pass",
|
|
1390
|
+
message: "MCP-konfigurasjon funnet",
|
|
1391
|
+
detail: mcpConfigPath.replace(homeDir, "~")
|
|
1392
|
+
});
|
|
1393
|
+
const servers = mcpConfig?.mcpServers || {};
|
|
1394
|
+
if (servers["cure-kode"]) {
|
|
1395
|
+
mcpChecks.push({
|
|
1396
|
+
status: "pass",
|
|
1397
|
+
message: "cure-kode server \u2713"
|
|
1398
|
+
});
|
|
1399
|
+
} else {
|
|
1400
|
+
mcpChecks.push({
|
|
1401
|
+
status: "warn",
|
|
1402
|
+
message: "cure-kode server mangler",
|
|
1403
|
+
detail: 'Kj\xF8r "kode init" for \xE5 legge til'
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
if (servers["webflow"] || servers["Webflow"]) {
|
|
1407
|
+
mcpChecks.push({
|
|
1408
|
+
status: "pass",
|
|
1409
|
+
message: "webflow server \u2713"
|
|
1410
|
+
});
|
|
1411
|
+
} else {
|
|
1412
|
+
mcpChecks.push({
|
|
1413
|
+
status: "warn",
|
|
1414
|
+
message: "webflow server mangler (valgfri)",
|
|
1415
|
+
detail: "For AI-drevet Webflow-redigering"
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
if (servers["playwright"]) {
|
|
1419
|
+
mcpChecks.push({
|
|
1420
|
+
status: "pass",
|
|
1421
|
+
message: "playwright server \u2713"
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
} else {
|
|
1425
|
+
mcpChecks.push({
|
|
1426
|
+
status: "warn",
|
|
1427
|
+
message: "MCP-konfigurasjon ikke funnet",
|
|
1428
|
+
detail: 'Kj\xF8r "kode init" for \xE5 sette opp'
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
const kodeMdPath = join4(projectRoot, ".cure-kode", "KODE.md");
|
|
1432
|
+
if (existsSync4(kodeMdPath)) {
|
|
1433
|
+
mcpChecks.push({
|
|
1434
|
+
status: "pass",
|
|
1435
|
+
message: "KODE.md dokumentasjon \u2713"
|
|
1436
|
+
});
|
|
1437
|
+
} else {
|
|
1438
|
+
mcpChecks.push({
|
|
1439
|
+
status: "warn",
|
|
1440
|
+
message: "KODE.md mangler",
|
|
1441
|
+
detail: 'Kj\xF8r "kode pull" eller "kode update-claude-md"'
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
const claudeMdPath = join4(projectRoot, "CLAUDE.md");
|
|
1445
|
+
if (existsSync4(claudeMdPath)) {
|
|
1446
|
+
const claudeMd = readFileSync4(claudeMdPath, "utf-8");
|
|
1447
|
+
if (claudeMd.includes("KODE.md") || claudeMd.includes(".cure-kode/KODE.md")) {
|
|
1448
|
+
mcpChecks.push({
|
|
1449
|
+
status: "pass",
|
|
1450
|
+
message: "CLAUDE.md har Kode-referanse"
|
|
1451
|
+
});
|
|
1452
|
+
} else {
|
|
1453
|
+
mcpChecks.push({
|
|
1454
|
+
status: "warn",
|
|
1455
|
+
message: "CLAUDE.md mangler Kode-referanse",
|
|
1456
|
+
detail: 'Kj\xF8r "kode update-claude-md"'
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
} else {
|
|
1460
|
+
mcpChecks.push({
|
|
1461
|
+
status: "warn",
|
|
1462
|
+
message: "CLAUDE.md ikke funnet",
|
|
1463
|
+
detail: 'Kj\xF8r "kode update-claude-md" for \xE5 opprette'
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
results.push({ category: "AI Agent Setup", checks: mcpChecks });
|
|
1467
|
+
const securityChecks = [];
|
|
1468
|
+
const gitignorePath = join4(projectRoot, ".gitignore");
|
|
1469
|
+
if (existsSync4(gitignorePath)) {
|
|
1470
|
+
const gitignore = readFileSync4(gitignorePath, "utf-8");
|
|
1471
|
+
if (gitignore.includes(".cure-kode") || gitignore.includes(".cure-kode/")) {
|
|
1472
|
+
securityChecks.push({
|
|
1473
|
+
status: "pass",
|
|
1474
|
+
message: ".cure-kode er gitignore'd"
|
|
1475
|
+
});
|
|
1476
|
+
} else {
|
|
1477
|
+
securityChecks.push({
|
|
1478
|
+
status: "warn",
|
|
1479
|
+
message: ".cure-kode b\xF8r gitignores",
|
|
1480
|
+
detail: "API-n\xF8kkel kan lekke i versjonskontroll"
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
} else {
|
|
1484
|
+
securityChecks.push({
|
|
1485
|
+
status: "warn",
|
|
1486
|
+
message: "Ingen .gitignore funnet",
|
|
1487
|
+
detail: "Legg til .cure-kode/ i .gitignore"
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
results.push({ category: "Sikkerhet", checks: securityChecks });
|
|
1491
|
+
const syncChecks = [];
|
|
1492
|
+
const metadataPath = join4(projectRoot, ".cure-kode", "scripts.json");
|
|
1493
|
+
if (existsSync4(metadataPath)) {
|
|
1494
|
+
try {
|
|
1495
|
+
const metadata = JSON.parse(readFileSync4(metadataPath, "utf-8"));
|
|
1496
|
+
if (Array.isArray(metadata) && metadata.length > 0) {
|
|
1497
|
+
const latestSync = metadata.reduce((latest, m) => {
|
|
1498
|
+
const date = new Date(m.lastPulledAt);
|
|
1499
|
+
return date > latest ? date : latest;
|
|
1500
|
+
}, /* @__PURE__ */ new Date(0));
|
|
1501
|
+
const ageHours = (Date.now() - latestSync.getTime()) / (1e3 * 60 * 60);
|
|
1502
|
+
const ageDays = Math.floor(ageHours / 24);
|
|
1503
|
+
if (ageHours < 1) {
|
|
1504
|
+
syncChecks.push({
|
|
1505
|
+
status: "pass",
|
|
1506
|
+
message: "Sist synkronisert: nylig"
|
|
1507
|
+
});
|
|
1508
|
+
} else if (ageDays >= 1) {
|
|
1509
|
+
syncChecks.push({
|
|
1510
|
+
status: ageDays > 3 ? "warn" : "pass",
|
|
1511
|
+
message: `Sist synkronisert: ${ageDays} dag${ageDays > 1 ? "er" : ""} siden`,
|
|
1512
|
+
detail: ageDays > 3 ? 'Vurder \xE5 kj\xF8re "kode sync"' : void 0
|
|
1513
|
+
});
|
|
1514
|
+
} else {
|
|
1515
|
+
syncChecks.push({
|
|
1516
|
+
status: "pass",
|
|
1517
|
+
message: `Sist synkronisert: ${Math.floor(ageHours)} time${ageHours >= 2 ? "r" : ""} siden`
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
syncChecks.push({
|
|
1521
|
+
status: "pass",
|
|
1522
|
+
message: `${metadata.length} skript sporet`
|
|
1523
|
+
});
|
|
1524
|
+
} else {
|
|
1525
|
+
syncChecks.push({
|
|
1526
|
+
status: "warn",
|
|
1527
|
+
message: "Ingen skript sporet",
|
|
1528
|
+
detail: 'Kj\xF8r "kode pull" for \xE5 synkronisere'
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
} catch {
|
|
1532
|
+
syncChecks.push({
|
|
1533
|
+
status: "fail",
|
|
1534
|
+
message: "Korrupt synkroniseringsdata",
|
|
1535
|
+
detail: 'Slett .cure-kode/scripts.json og kj\xF8r "kode pull"'
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
} else {
|
|
1539
|
+
syncChecks.push({
|
|
1540
|
+
status: "warn",
|
|
1541
|
+
message: "Aldri synkronisert",
|
|
1542
|
+
detail: 'Kj\xF8r "kode pull" for \xE5 hente skript'
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
results.push({ category: "Synkronisering", checks: syncChecks });
|
|
1546
|
+
const versionChecks = [];
|
|
1547
|
+
versionChecks.push({
|
|
1548
|
+
status: "pass",
|
|
1549
|
+
message: `CLI-versjon: ${CLI_VERSION}`
|
|
1550
|
+
});
|
|
1551
|
+
try {
|
|
1552
|
+
const response = await fetch("https://registry.npmjs.org/@curenorway/kode-cli/latest", {
|
|
1553
|
+
signal: AbortSignal.timeout(3e3)
|
|
1554
|
+
});
|
|
1555
|
+
if (response.ok) {
|
|
1556
|
+
const data = await response.json();
|
|
1557
|
+
if (data.version && isNewerVersion(CLI_VERSION, data.version)) {
|
|
1558
|
+
versionChecks.push({
|
|
1559
|
+
status: "warn",
|
|
1560
|
+
message: `Oppdatering tilgjengelig: v${data.version}`,
|
|
1561
|
+
detail: 'Kj\xF8r "npm install -g @curenorway/kode-cli@latest"'
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
} catch {
|
|
1566
|
+
}
|
|
1567
|
+
results.push({ category: "Versjon", checks: versionChecks });
|
|
1568
|
+
printResults(results);
|
|
1569
|
+
}
|
|
1570
|
+
function printResults(results) {
|
|
1571
|
+
let passCount = 0;
|
|
1572
|
+
let warnCount = 0;
|
|
1573
|
+
let failCount = 0;
|
|
1574
|
+
for (const { category, checks } of results) {
|
|
1575
|
+
console.log(chalk9.bold(category));
|
|
1576
|
+
for (const check of checks) {
|
|
1577
|
+
const icon = check.status === "pass" ? chalk9.green("\u2713") : check.status === "warn" ? chalk9.yellow("\u26A0") : chalk9.red("\u2717");
|
|
1578
|
+
const color = check.status === "pass" ? chalk9.dim : check.status === "warn" ? chalk9.yellow : chalk9.red;
|
|
1579
|
+
console.log(` ${icon} ${color(check.message)}`);
|
|
1580
|
+
if (check.detail) {
|
|
1581
|
+
console.log(chalk9.dim(` ${check.detail}`));
|
|
1582
|
+
}
|
|
1583
|
+
if (check.status === "pass") passCount++;
|
|
1584
|
+
else if (check.status === "warn") warnCount++;
|
|
1585
|
+
else failCount++;
|
|
1586
|
+
}
|
|
1587
|
+
console.log();
|
|
1588
|
+
}
|
|
1589
|
+
console.log(chalk9.dim("\u2500".repeat(40)));
|
|
1590
|
+
const parts = [];
|
|
1591
|
+
if (passCount > 0) parts.push(chalk9.green(`${passCount} \u2713`));
|
|
1592
|
+
if (warnCount > 0) parts.push(chalk9.yellow(`${warnCount} \u26A0`));
|
|
1593
|
+
if (failCount > 0) parts.push(chalk9.red(`${failCount} \u2717`));
|
|
1594
|
+
console.log(`Resultat: ${parts.join(", ")}`);
|
|
444
1595
|
console.log();
|
|
445
|
-
console.log(chalk5.dim("The Cure Kode section now includes:"));
|
|
446
|
-
console.log(chalk5.dim(" \u2022 What is Cure Kode (internal tool explanation)"));
|
|
447
|
-
console.log(chalk5.dim(" \u2022 CDN URL with script tag for Webflow"));
|
|
448
|
-
console.log(chalk5.dim(" \u2022 Workflow steps"));
|
|
449
|
-
console.log(chalk5.dim(" \u2022 Command reference"));
|
|
450
1596
|
}
|
|
451
1597
|
|
|
452
1598
|
// src/cli.ts
|
|
453
|
-
var require2 = createRequire(import.meta.url);
|
|
454
|
-
var pkg = require2("../package.json");
|
|
455
1599
|
var program = new Command();
|
|
456
|
-
program.name("kode").description("CLI for Cure Kode - manage JS/CSS scripts for Webflow sites").version(
|
|
457
|
-
program.command("init").description("Initialize Cure Kode in current directory").option("-k, --api-key <key>", "API key
|
|
1600
|
+
program.name("kode").description("CLI for Cure Kode - manage JS/CSS scripts for Webflow sites").version(CLI_VERSION);
|
|
1601
|
+
program.command("init").description("Initialize Cure Kode in current directory").option("-k, --api-key <key>", "API key (from Cure App)").option("-f, --force", "Reinitialize even if already configured").action((options) => {
|
|
458
1602
|
initCommand(options);
|
|
459
1603
|
});
|
|
1604
|
+
program.command("upgrade").description("Upgrade existing project to latest CLI version").action(() => {
|
|
1605
|
+
upgradeCommand();
|
|
1606
|
+
});
|
|
460
1607
|
program.command("pull").description("Download scripts from Cure to local files").argument("[script]", "Specific script slug to pull").option("-f, --force", "Overwrite local changes").action((script, options) => {
|
|
461
1608
|
pullCommand({ script, ...options });
|
|
462
1609
|
});
|
|
463
|
-
program.command("push").description("Upload local scripts to Cure").argument("[script]", "Specific script file or slug to push").option("-m, --message <message>", "Change summary").option("-a, --all", "Push all scripts even if unchanged").option("--auto-load", "Enable auto-loading for new scripts (default for global)").option("--no-auto-load", "Disable auto-loading for new scripts").action((script, options) => {
|
|
1610
|
+
program.command("push").description("Upload local scripts to Cure").argument("[script]", "Specific script file or slug to push").option("-m, --message <message>", "Change summary").option("-a, --all", "Push all scripts even if unchanged").option("-f, --force", "Force push even if remote has newer version").option("--auto-load", "Enable auto-loading for new scripts (default for global)").option("--no-auto-load", "Disable auto-loading for new scripts").action((script, options) => {
|
|
464
1611
|
pushCommand({ script, ...options });
|
|
465
1612
|
});
|
|
1613
|
+
program.command("sync").description("Bidirectional sync - pull and push changes intelligently").option("-n, --dry-run", "Show what would change without making changes").action((options) => {
|
|
1614
|
+
syncCommand(options);
|
|
1615
|
+
});
|
|
1616
|
+
program.command("diff <script>").description("Show differences between local and remote script").action((script) => {
|
|
1617
|
+
diffCommand(script);
|
|
1618
|
+
});
|
|
466
1619
|
program.command("watch").description("Watch for changes and auto-push").option("-d, --deploy", "Auto-deploy after each push").action((options) => {
|
|
467
1620
|
watchCommand(options);
|
|
468
1621
|
});
|
|
469
|
-
program.command("deploy [environment]").description("Deploy to staging or production").option("-p, --promote", "Promote staging to production").option("-f, --force", "Force release stale deploy lock before deploying").action((environment, options) => {
|
|
1622
|
+
program.command("deploy [environment]").description("Deploy to staging or production").option("-p, --promote", "Promote staging to production").option("-f, --force", "Force release stale deploy lock before deploying").option("-n, --dry-run", "Preview deployment without executing").action((environment, options) => {
|
|
470
1623
|
deployCommand(environment, options);
|
|
471
1624
|
});
|
|
472
1625
|
program.command("rollback [environment]").description("Rollback to previous deployment").action((environment = "staging") => {
|
|
@@ -475,8 +1628,30 @@ program.command("rollback [environment]").description("Rollback to previous depl
|
|
|
475
1628
|
program.command("html <url>").description("Fetch and analyze HTML from a URL").option("-j, --json", "Output as JSON").option("--scripts", "Show only scripts").option("--styles", "Show only styles").option("-s, --save", "Save page structure to context").option("-f, --force", "Force refresh when using --save").action((url, options) => {
|
|
476
1629
|
htmlCommand(url, options);
|
|
477
1630
|
});
|
|
478
|
-
program.command("pages").description("
|
|
479
|
-
|
|
1631
|
+
var pagesCmd = program.command("pages").description("Manage CDN pages and page-specific scripts");
|
|
1632
|
+
pagesCmd.command("list", { isDefault: true }).description("List all CDN pages (remote)").option("-j, --json", "Output as JSON").action((options) => {
|
|
1633
|
+
pagesListCommand(options);
|
|
1634
|
+
});
|
|
1635
|
+
pagesCmd.command("create <name>").description("Create a new CDN page").option("-p, --patterns <patterns...>", "URL patterns (e.g., /about, /about/*)").option("-t, --pattern-type <type>", "Pattern type: exact, prefix, wildcard, regex", "prefix").option("--priority <n>", "Priority (higher = checked first)", "0").option("--parent <slug>", "Parent page slug for nesting").action((name, options) => {
|
|
1636
|
+
pagesCreateCommand(name, options);
|
|
1637
|
+
});
|
|
1638
|
+
pagesCmd.command("delete <slug>").description("Delete a CDN page").option("-f, --force", "Confirm deletion").action((slug, options) => {
|
|
1639
|
+
pagesDeleteCommand(slug, options);
|
|
1640
|
+
});
|
|
1641
|
+
pagesCmd.command("update <slug>").description("Update a CDN page").option("-n, --name <name>", "New name").option("-p, --patterns <patterns...>", "New URL patterns").option("-t, --pattern-type <type>", "Pattern type: exact, prefix, wildcard, regex").option("--priority <n>", "Priority").option("--parent <slug>", "Parent page slug").option("--remove-parent", "Remove parent (make root-level)").action((slug, options) => {
|
|
1642
|
+
pagesUpdateCommand(slug, options);
|
|
1643
|
+
});
|
|
1644
|
+
pagesCmd.command("assign <page> <script>").description("Assign a script to a page").option("-o, --load-order <n>", "Load order override").action((page, script, options) => {
|
|
1645
|
+
pagesAssignCommand(page, script, options);
|
|
1646
|
+
});
|
|
1647
|
+
pagesCmd.command("unassign <page> <script>").description("Remove a script from a page").action((page, script) => {
|
|
1648
|
+
pagesUnassignCommand(page, script);
|
|
1649
|
+
});
|
|
1650
|
+
pagesCmd.command("scripts <page>").description("Show scripts assigned to a page").action((page) => {
|
|
1651
|
+
pagesScriptsCommand(page);
|
|
1652
|
+
});
|
|
1653
|
+
pagesCmd.command("context").description("Manage local HTML page cache (for AI context)").option("-j, --json", "Output as JSON").option("-d, --delete", "Delete the specified page context").argument("[page]", "Page slug to view or delete").action((page, options) => {
|
|
1654
|
+
pagesContextCommand({ page, ...options });
|
|
480
1655
|
});
|
|
481
1656
|
program.command("status").description("Show current status of scripts and deployments").option("-v, --verbose", "Show more details").action((options) => {
|
|
482
1657
|
statusCommand(options);
|
|
@@ -493,9 +1668,8 @@ program.command("production <action>").description("Enable or disable production
|
|
|
493
1668
|
program.command("update-claude-md").alias("ucm").description("Add or update Cure Kode section in CLAUDE.md").action(() => {
|
|
494
1669
|
updateClaudeMdCommand();
|
|
495
1670
|
});
|
|
1671
|
+
program.command("doctor").description("Diagnose configuration and environment issues").action(() => {
|
|
1672
|
+
doctorCommand();
|
|
1673
|
+
});
|
|
496
1674
|
program.showHelpAfterError();
|
|
497
|
-
console.log();
|
|
498
|
-
console.log(chalk6.bold(" Cure Kode CLI"));
|
|
499
|
-
console.log(chalk6.dim(" Manage JS/CSS for Webflow sites"));
|
|
500
|
-
console.log();
|
|
501
1675
|
program.parse();
|