@thuanha-groove/gck 0.0.1 → 0.0.2
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 +3 -3
- package/cli-manifest.json +4 -4
- package/dist/index.js +1603 -1602
- package/dist/ui/apple-touch-icon.png +0 -0
- package/dist/ui/assets/{index-aVRtIQEq.js → index-T1xs-ZZP.js} +6 -6
- package/dist/ui/favicon.ico +0 -0
- package/dist/ui/images/apple-touch-icon.png +0 -0
- package/dist/ui/images/favicon-16.png +0 -0
- package/dist/ui/images/favicon-32.png +0 -0
- package/dist/ui/images/logo-192.png +0 -0
- package/dist/ui/images/logo-512.png +0 -0
- package/dist/ui/images/logo-dark-32.png +0 -0
- package/dist/ui/images/logo-light-32.png +0 -0
- package/dist/ui/images/logo-transparent-128.png +0 -0
- package/dist/ui/images/logo-transparent-32.png +0 -0
- package/dist/ui/images/logo-transparent-64.png +0 -0
- package/dist/ui/index.html +3 -3
- package/dist/ui/manifest.json +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-jFAdUzwZ.js +0 -334
package/dist/index.js
CHANGED
|
@@ -15516,7 +15516,7 @@ var init_metadata = __esm(() => {
|
|
|
15516
15516
|
TrackedFileSchema = objectType({
|
|
15517
15517
|
path: stringType(),
|
|
15518
15518
|
checksum: stringType().regex(/^[a-f0-9]{64}$/, "Invalid SHA-256 checksum"),
|
|
15519
|
-
ownership: enumType(["
|
|
15519
|
+
ownership: enumType(["gck", "user", "gck-modified"]),
|
|
15520
15520
|
installedVersion: stringType(),
|
|
15521
15521
|
baseChecksum: stringType().regex(/^[a-f0-9]{64}$/, "Invalid SHA-256 checksum").optional(),
|
|
15522
15522
|
sourceTimestamp: stringType().datetime({ offset: true }).optional(),
|
|
@@ -15733,7 +15733,7 @@ var init_claudekit_api = __esm(() => {
|
|
|
15733
15733
|
});
|
|
15734
15734
|
});
|
|
15735
15735
|
|
|
15736
|
-
// src/types/
|
|
15736
|
+
// src/types/gck-config.ts
|
|
15737
15737
|
function normalizeMigrateProviderToken(token) {
|
|
15738
15738
|
const trimmed = token.trim();
|
|
15739
15739
|
const unwrapped = trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'") ? trimmed.slice(1, -1) : trimmed;
|
|
@@ -15804,7 +15804,7 @@ function normalizeCkConfigInput(value) {
|
|
|
15804
15804
|
return normalized;
|
|
15805
15805
|
}
|
|
15806
15806
|
var PlanValidationModeSchema, PlanFocusAreaSchema, PlanResolutionOrderSchema, ProjectTypeSchema, PackageManagerSchema, FrameworkSchema, LEGACY_GEMINI_MODEL_ALIASES, GeminiModelSchema, StatuslineModeSchema, StatuslineSectionIdSchema, StatuslineSectionConfigSchema, StatuslineThemeSchema, StatuslineLayoutSchema, CodingLevelSchema, PlanResolutionSchema, PlanValidationSchema, CkPlanConfigSchema, CkDocsConfigSchema, CkPathsConfigSchema, CkLocaleConfigSchema, CkTrustConfigSchema, CkProjectConfigSchema, CkGeminiConfigSchema, CkSkillsConfigSchema, MigrateScopeConfigSchema, UpdatePipelineSchema, ResolvedModelConfigSchema, ModelTierMapSchema, CkModelTaxonomySchema, CkAssertionSchema, CkHooksConfigSchema, CkSimplifyConfigSchema, CkConfigSchema, DEFAULT_CK_CONFIG;
|
|
15807
|
-
var
|
|
15807
|
+
var init_gck_config = __esm(() => {
|
|
15808
15808
|
init_zod();
|
|
15809
15809
|
PlanValidationModeSchema = enumType(["prompt", "auto", "strict", "none"]);
|
|
15810
15810
|
PlanFocusAreaSchema = enumType([
|
|
@@ -16110,7 +16110,7 @@ var init_types3 = __esm(() => {
|
|
|
16110
16110
|
init_errors2();
|
|
16111
16111
|
init_claudekit_data();
|
|
16112
16112
|
init_claudekit_api();
|
|
16113
|
-
|
|
16113
|
+
init_gck_config();
|
|
16114
16114
|
init_statusline_section_defaults();
|
|
16115
16115
|
});
|
|
16116
16116
|
|
|
@@ -45076,7 +45076,7 @@ var init_config_manager = __esm(() => {
|
|
|
45076
45076
|
init_types3();
|
|
45077
45077
|
});
|
|
45078
45078
|
|
|
45079
|
-
// src/domains/config/
|
|
45079
|
+
// src/domains/config/gck-config-manager.ts
|
|
45080
45080
|
import { existsSync as existsSync11 } from "node:fs";
|
|
45081
45081
|
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile6 } from "node:fs/promises";
|
|
45082
45082
|
import { homedir as homedir10 } from "node:os";
|
|
@@ -45281,7 +45281,7 @@ class CkConfigManager {
|
|
|
45281
45281
|
}
|
|
45282
45282
|
}
|
|
45283
45283
|
var CK_CONFIG_FILE = ".ck.json", DANGEROUS_KEYS;
|
|
45284
|
-
var
|
|
45284
|
+
var init_gck_config_manager = __esm(() => {
|
|
45285
45285
|
init_logger();
|
|
45286
45286
|
init_types3();
|
|
45287
45287
|
DANGEROUS_KEYS = ["__proto__", "constructor", "prototype"];
|
|
@@ -45354,7 +45354,7 @@ function getCliVersion2() {
|
|
|
45354
45354
|
function getCliUserAgent() {
|
|
45355
45355
|
return `${CLAUDEKIT_CLI_NPM_PACKAGE_NAME}/${getCliVersion2()}`;
|
|
45356
45356
|
}
|
|
45357
|
-
var CLAUDEKIT_CLI_NPM_PACKAGE_NAME = "gck", CLAUDEKIT_CLI_NPM_PACKAGE_URL, CLAUDEKIT_CLI_GLOBAL_INSTALL_COMMAND, CLAUDEKIT_CLI_INSTALL_COMMANDS, DEFAULT_NETWORK_TIMEOUT_MS = 3000;
|
|
45357
|
+
var CLAUDEKIT_CLI_NPM_PACKAGE_NAME = "@thuanha-groove/gck", CLAUDEKIT_CLI_NPM_PACKAGE_URL, CLAUDEKIT_CLI_GLOBAL_INSTALL_COMMAND, CLAUDEKIT_CLI_INSTALL_COMMANDS, DEFAULT_NETWORK_TIMEOUT_MS = 3000;
|
|
45358
45358
|
var init_claudekit_constants = __esm(() => {
|
|
45359
45359
|
CLAUDEKIT_CLI_NPM_PACKAGE_URL = `https://www.npmjs.com/package/${CLAUDEKIT_CLI_NPM_PACKAGE_NAME}`;
|
|
45360
45360
|
CLAUDEKIT_CLI_GLOBAL_INSTALL_COMMAND = `npm install -g ${CLAUDEKIT_CLI_NPM_PACKAGE_NAME}`;
|
|
@@ -46179,7 +46179,7 @@ var init_types4 = __esm(() => {
|
|
|
46179
46179
|
// src/domains/config/index.ts
|
|
46180
46180
|
var init_config = __esm(() => {
|
|
46181
46181
|
init_config_manager();
|
|
46182
|
-
|
|
46182
|
+
init_gck_config_manager();
|
|
46183
46183
|
init_settings_merger();
|
|
46184
46184
|
init_config_generator();
|
|
46185
46185
|
init_config_validator();
|
|
@@ -49360,1154 +49360,311 @@ var init_agents_routes = __esm(() => {
|
|
|
49360
49360
|
home2 = homedir14();
|
|
49361
49361
|
});
|
|
49362
49362
|
|
|
49363
|
-
// src/
|
|
49364
|
-
|
|
49365
|
-
|
|
49366
|
-
|
|
49367
|
-
|
|
49368
|
-
|
|
49369
|
-
|
|
49370
|
-
|
|
49371
|
-
|
|
49372
|
-
|
|
49373
|
-
|
|
49374
|
-
|
|
49375
|
-
|
|
49376
|
-
|
|
49377
|
-
|
|
49378
|
-
|
|
49379
|
-
|
|
49380
|
-
|
|
49381
|
-
|
|
49382
|
-
|
|
49383
|
-
|
|
49384
|
-
|
|
49385
|
-
|
|
49386
|
-
|
|
49387
|
-
|
|
49388
|
-
|
|
49389
|
-
|
|
49390
|
-
|
|
49391
|
-
|
|
49392
|
-
|
|
49393
|
-
|
|
49394
|
-
|
|
49395
|
-
|
|
49396
|
-
|
|
49397
|
-
|
|
49398
|
-
|
|
49399
|
-
|
|
49400
|
-
|
|
49401
|
-
|
|
49402
|
-
|
|
49403
|
-
|
|
49404
|
-
|
|
49405
|
-
|
|
49406
|
-
|
|
49407
|
-
|
|
49408
|
-
|
|
49409
|
-
|
|
49410
|
-
|
|
49411
|
-
|
|
49412
|
-
|
|
49413
|
-
|
|
49414
|
-
|
|
49415
|
-
|
|
49416
|
-
|
|
49417
|
-
|
|
49418
|
-
|
|
49419
|
-
|
|
49420
|
-
|
|
49421
|
-
|
|
49422
|
-
|
|
49423
|
-
|
|
49424
|
-
|
|
49425
|
-
|
|
49426
|
-
|
|
49427
|
-
|
|
49428
|
-
|
|
49429
|
-
|
|
49430
|
-
|
|
49431
|
-
|
|
49432
|
-
|
|
49433
|
-
|
|
49434
|
-
|
|
49435
|
-
|
|
49436
|
-
|
|
49437
|
-
|
|
49438
|
-
|
|
49439
|
-
|
|
49440
|
-
|
|
49441
|
-
|
|
49442
|
-
|
|
49443
|
-
|
|
49444
|
-
|
|
49445
|
-
|
|
49446
|
-
|
|
49447
|
-
|
|
49448
|
-
|
|
49449
|
-
|
|
49450
|
-
|
|
49451
|
-
|
|
49452
|
-
|
|
49453
|
-
|
|
49454
|
-
|
|
49455
|
-
|
|
49456
|
-
|
|
49457
|
-
|
|
49458
|
-
|
|
49459
|
-
|
|
49460
|
-
|
|
49461
|
-
|
|
49462
|
-
|
|
49463
|
-
|
|
49464
|
-
|
|
49465
|
-
|
|
49466
|
-
|
|
49467
|
-
|
|
49468
|
-
|
|
49469
|
-
|
|
49470
|
-
|
|
49471
|
-
|
|
49472
|
-
|
|
49473
|
-
|
|
49474
|
-
|
|
49475
|
-
|
|
49476
|
-
|
|
49477
|
-
|
|
49478
|
-
|
|
49479
|
-
|
|
49480
|
-
|
|
49481
|
-
|
|
49482
|
-
|
|
49483
|
-
|
|
49484
|
-
|
|
49485
|
-
|
|
49486
|
-
|
|
49487
|
-
|
|
49488
|
-
|
|
49489
|
-
|
|
49490
|
-
|
|
49491
|
-
|
|
49492
|
-
|
|
49493
|
-
|
|
49494
|
-
|
|
49495
|
-
|
|
49496
|
-
|
|
49497
|
-
|
|
49498
|
-
|
|
49499
|
-
|
|
49500
|
-
|
|
49501
|
-
|
|
49502
|
-
|
|
49503
|
-
|
|
49504
|
-
|
|
49505
|
-
|
|
49506
|
-
|
|
49507
|
-
|
|
49508
|
-
|
|
49509
|
-
|
|
49510
|
-
|
|
49511
|
-
|
|
49512
|
-
|
|
49513
|
-
|
|
49514
|
-
|
|
49515
|
-
|
|
49516
|
-
|
|
49517
|
-
|
|
49518
|
-
|
|
49519
|
-
|
|
49520
|
-
|
|
49521
|
-
|
|
49522
|
-
|
|
49523
|
-
|
|
49524
|
-
|
|
49525
|
-
|
|
49526
|
-
|
|
49527
|
-
|
|
49528
|
-
|
|
49529
|
-
|
|
49530
|
-
|
|
49531
|
-
|
|
49532
|
-
|
|
49533
|
-
|
|
49534
|
-
|
|
49535
|
-
|
|
49536
|
-
|
|
49537
|
-
|
|
49538
|
-
|
|
49539
|
-
|
|
49540
|
-
|
|
49541
|
-
|
|
49542
|
-
|
|
49543
|
-
|
|
49544
|
-
|
|
49545
|
-
|
|
49546
|
-
|
|
49547
|
-
|
|
49548
|
-
|
|
49549
|
-
|
|
49550
|
-
|
|
49551
|
-
|
|
49552
|
-
|
|
49553
|
-
type: "object",
|
|
49554
|
-
description: "Project type detection settings",
|
|
49555
|
-
properties: {
|
|
49556
|
-
type: {
|
|
49557
|
-
type: "string",
|
|
49558
|
-
enum: ["auto", "library", "application", "monorepo", "cli", "api", "web", "mobile"],
|
|
49559
|
-
default: "auto",
|
|
49560
|
-
description: "Project type (auto=detect from package.json)"
|
|
49561
|
-
},
|
|
49562
|
-
packageManager: {
|
|
49563
|
-
type: "string",
|
|
49564
|
-
enum: ["auto", "npm", "yarn", "pnpm", "bun"],
|
|
49565
|
-
default: "auto",
|
|
49566
|
-
description: "Package manager (auto=detect from lockfile)"
|
|
49567
|
-
},
|
|
49568
|
-
framework: {
|
|
49569
|
-
type: "string",
|
|
49570
|
-
enum: [
|
|
49571
|
-
"auto",
|
|
49572
|
-
"react",
|
|
49573
|
-
"vue",
|
|
49574
|
-
"angular",
|
|
49575
|
-
"svelte",
|
|
49576
|
-
"nextjs",
|
|
49577
|
-
"nuxt",
|
|
49578
|
-
"express",
|
|
49579
|
-
"nestjs",
|
|
49580
|
-
"fastify",
|
|
49581
|
-
"none"
|
|
49582
|
-
],
|
|
49583
|
-
default: "auto",
|
|
49584
|
-
description: "Framework (auto=detect from dependencies)"
|
|
49585
|
-
}
|
|
49586
|
-
},
|
|
49587
|
-
additionalProperties: false
|
|
49588
|
-
},
|
|
49589
|
-
gemini: {
|
|
49590
|
-
type: "object",
|
|
49591
|
-
description: "Gemini CLI configuration",
|
|
49592
|
-
properties: {
|
|
49593
|
-
model: {
|
|
49594
|
-
type: "string",
|
|
49595
|
-
enum: [
|
|
49596
|
-
"gemini-2.5-flash",
|
|
49597
|
-
"gemini-2.5-pro",
|
|
49598
|
-
"gemini-3-pro-preview",
|
|
49599
|
-
"gemini-3-flash-preview"
|
|
49600
|
-
],
|
|
49601
|
-
default: "gemini-3-flash-preview",
|
|
49602
|
-
description: "Gemini model to use for CLI commands. Legacy gemini-3.0-* ids are auto-mapped to the current preview aliases when loading config."
|
|
49603
|
-
}
|
|
49604
|
-
},
|
|
49605
|
-
additionalProperties: false
|
|
49606
|
-
},
|
|
49607
|
-
skills: {
|
|
49608
|
-
type: "object",
|
|
49609
|
-
description: "Per-skill configuration",
|
|
49610
|
-
properties: {
|
|
49611
|
-
research: {
|
|
49612
|
-
type: "object",
|
|
49613
|
-
properties: {
|
|
49614
|
-
useGemini: {
|
|
49615
|
-
type: "boolean",
|
|
49616
|
-
default: true,
|
|
49617
|
-
description: "Use Gemini CLI for research (false=use WebSearch)"
|
|
49363
|
+
// src/domains/web-server/routes/command-routes.ts
|
|
49364
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
49365
|
+
import { readFile as readFile11, readdir as readdir8 } from "node:fs/promises";
|
|
49366
|
+
import { homedir as homedir15 } from "node:os";
|
|
49367
|
+
import { basename as basename9, isAbsolute as isAbsolute5, join as join29, relative as relative8, resolve as resolve13 } from "node:path";
|
|
49368
|
+
async function buildCommandTree(dir, baseDir) {
|
|
49369
|
+
let dirents;
|
|
49370
|
+
try {
|
|
49371
|
+
dirents = await readdir8(dir, { withFileTypes: true });
|
|
49372
|
+
} catch {
|
|
49373
|
+
return [];
|
|
49374
|
+
}
|
|
49375
|
+
const nodes = [];
|
|
49376
|
+
const sorted = [...dirents].sort((a, b) => {
|
|
49377
|
+
const aDir = a.isDirectory();
|
|
49378
|
+
const bDir = b.isDirectory();
|
|
49379
|
+
if (aDir !== bDir)
|
|
49380
|
+
return aDir ? -1 : 1;
|
|
49381
|
+
return a.name.localeCompare(b.name);
|
|
49382
|
+
});
|
|
49383
|
+
for (const entry of sorted) {
|
|
49384
|
+
const name = entry.name;
|
|
49385
|
+
const isDir = entry.isDirectory();
|
|
49386
|
+
const fullPath = join29(dir, name);
|
|
49387
|
+
const relPath = relative8(baseDir, fullPath);
|
|
49388
|
+
if (isDir) {
|
|
49389
|
+
const children = await buildCommandTree(fullPath, baseDir);
|
|
49390
|
+
if (children.length > 0) {
|
|
49391
|
+
nodes.push({ name, path: relPath, children });
|
|
49392
|
+
}
|
|
49393
|
+
} else if (name.endsWith(".md")) {
|
|
49394
|
+
let description;
|
|
49395
|
+
try {
|
|
49396
|
+
const content = await readFile11(fullPath, "utf-8");
|
|
49397
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
49398
|
+
description = frontmatter.description;
|
|
49399
|
+
} catch {}
|
|
49400
|
+
const commandName = basename9(name, ".md");
|
|
49401
|
+
nodes.push({ name: commandName, path: relPath, description });
|
|
49402
|
+
}
|
|
49403
|
+
}
|
|
49404
|
+
return nodes;
|
|
49405
|
+
}
|
|
49406
|
+
function registerCommandRoutes(app) {
|
|
49407
|
+
app.get("/api/commands", async (_req, res) => {
|
|
49408
|
+
const commandsDir = join29(homedir15(), ".claude", "commands");
|
|
49409
|
+
if (!existsSync15(commandsDir)) {
|
|
49410
|
+
res.json({ tree: [] });
|
|
49411
|
+
return;
|
|
49412
|
+
}
|
|
49413
|
+
try {
|
|
49414
|
+
const tree = await buildCommandTree(commandsDir, commandsDir);
|
|
49415
|
+
res.json({ tree });
|
|
49416
|
+
} catch {
|
|
49417
|
+
res.status(500).json({ error: "Failed to scan commands directory" });
|
|
49418
|
+
}
|
|
49419
|
+
});
|
|
49420
|
+
app.get("/api/commands/detail/:slug", async (req, res) => {
|
|
49421
|
+
const slug = String(req.params.slug ?? "");
|
|
49422
|
+
const rawPath = slug.replace(/--/g, "/");
|
|
49423
|
+
if (!rawPath) {
|
|
49424
|
+
res.status(400).json({ error: "Missing command path" });
|
|
49425
|
+
return;
|
|
49426
|
+
}
|
|
49427
|
+
if (rawPath.includes("..") || rawPath.includes("\x00")) {
|
|
49428
|
+
res.status(400).json({ error: "Invalid path" });
|
|
49429
|
+
return;
|
|
49430
|
+
}
|
|
49431
|
+
const commandsDir = join29(homedir15(), ".claude", "commands");
|
|
49432
|
+
const safePath = resolve13(commandsDir, rawPath);
|
|
49433
|
+
const rel = relative8(commandsDir, safePath);
|
|
49434
|
+
if (rel.startsWith("..") || isAbsolute5(rel)) {
|
|
49435
|
+
res.status(403).json({ error: "Access denied" });
|
|
49436
|
+
return;
|
|
49437
|
+
}
|
|
49438
|
+
const filePath = safePath.endsWith(".md") ? safePath : `${safePath}.md`;
|
|
49439
|
+
if (!existsSync15(filePath)) {
|
|
49440
|
+
res.status(404).json({ error: "Command not found" });
|
|
49441
|
+
return;
|
|
49442
|
+
}
|
|
49443
|
+
try {
|
|
49444
|
+
const content = await readFile11(filePath, "utf-8");
|
|
49445
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
49446
|
+
const commandName = basename9(filePath, ".md");
|
|
49447
|
+
res.json({
|
|
49448
|
+
name: commandName,
|
|
49449
|
+
path: rawPath,
|
|
49450
|
+
content,
|
|
49451
|
+
description: frontmatter.description
|
|
49452
|
+
});
|
|
49453
|
+
} catch {
|
|
49454
|
+
res.status(500).json({ error: "Failed to read command" });
|
|
49455
|
+
}
|
|
49456
|
+
});
|
|
49457
|
+
}
|
|
49458
|
+
var init_command_routes = __esm(() => {
|
|
49459
|
+
init_frontmatter_parser();
|
|
49460
|
+
});
|
|
49461
|
+
|
|
49462
|
+
// src/services/claude-data/types.ts
|
|
49463
|
+
var HistoryEntrySchema, UserPreferencesSchema, TipsHistorySchema, FeatureFlagsSchema, HookLogEntrySchema;
|
|
49464
|
+
var init_types5 = __esm(() => {
|
|
49465
|
+
init_zod();
|
|
49466
|
+
HistoryEntrySchema = objectType({
|
|
49467
|
+
display: stringType(),
|
|
49468
|
+
pastedContents: recordType(unknownType()).optional(),
|
|
49469
|
+
timestamp: numberType(),
|
|
49470
|
+
project: stringType()
|
|
49471
|
+
});
|
|
49472
|
+
UserPreferencesSchema = objectType({
|
|
49473
|
+
numStartups: numberType().optional(),
|
|
49474
|
+
theme: enumType(["dark", "light", "dark-daltonized"]).optional(),
|
|
49475
|
+
installMethod: stringType().optional(),
|
|
49476
|
+
autoUpdates: booleanType().optional(),
|
|
49477
|
+
firstStartTime: stringType().optional(),
|
|
49478
|
+
hasSeenTasksHint: booleanType().optional(),
|
|
49479
|
+
hasSeenStashHint: booleanType().optional(),
|
|
49480
|
+
promptQueueUseCount: numberType().optional()
|
|
49481
|
+
});
|
|
49482
|
+
TipsHistorySchema = recordType(stringType(), numberType());
|
|
49483
|
+
FeatureFlagsSchema = objectType({
|
|
49484
|
+
statsigGates: recordType(stringType(), booleanType()).optional(),
|
|
49485
|
+
growthBookFeatures: recordType(stringType(), unknownType()).optional()
|
|
49486
|
+
});
|
|
49487
|
+
HookLogEntrySchema = objectType({
|
|
49488
|
+
ts: stringType().min(1).refine((value) => Number.isFinite(Date.parse(value)), {
|
|
49489
|
+
message: "Invalid timestamp"
|
|
49490
|
+
}),
|
|
49491
|
+
hook: stringType().min(1),
|
|
49492
|
+
event: stringType().min(1).optional(),
|
|
49493
|
+
tool: stringType().min(1).optional(),
|
|
49494
|
+
target: stringType().min(1).optional(),
|
|
49495
|
+
note: stringType().min(1).optional(),
|
|
49496
|
+
dur: numberType().finite().nonnegative().optional(),
|
|
49497
|
+
status: stringType().min(1),
|
|
49498
|
+
exit: numberType().int().optional(),
|
|
49499
|
+
error: stringType().min(1).optional()
|
|
49500
|
+
}).passthrough();
|
|
49501
|
+
});
|
|
49502
|
+
|
|
49503
|
+
// src/services/claude-data/history-parser.ts
|
|
49504
|
+
import { createReadStream, statSync as statSync4 } from "node:fs";
|
|
49505
|
+
import { homedir as homedir16 } from "node:os";
|
|
49506
|
+
import { join as join30 } from "node:path";
|
|
49507
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
49508
|
+
function getHistoryPath() {
|
|
49509
|
+
return process.env.CLAUDE_HISTORY_PATH ?? join30(homedir16(), ".claude", "history.jsonl");
|
|
49510
|
+
}
|
|
49511
|
+
function emptyResult(parseTimeMs, error) {
|
|
49512
|
+
return { projects: [], totalEntries: 0, errorCount: 0, parseTimeMs, error };
|
|
49513
|
+
}
|
|
49514
|
+
async function parseHistoryFile(filePath) {
|
|
49515
|
+
const path = filePath ?? getHistoryPath();
|
|
49516
|
+
const existingLock = locks.get(path);
|
|
49517
|
+
if (existingLock)
|
|
49518
|
+
return existingLock;
|
|
49519
|
+
const start = Date.now();
|
|
49520
|
+
let mtime;
|
|
49521
|
+
try {
|
|
49522
|
+
const stats = statSync4(path);
|
|
49523
|
+
mtime = stats.mtimeMs;
|
|
49524
|
+
const cached = cache.get(path);
|
|
49525
|
+
if (cached && cached.mtime === mtime) {
|
|
49526
|
+
return cached.result;
|
|
49527
|
+
}
|
|
49528
|
+
} catch (err) {
|
|
49529
|
+
const msg = err instanceof Error ? err.message : "File not found";
|
|
49530
|
+
return emptyResult(Date.now() - start, `Cannot read history.jsonl: ${msg}`);
|
|
49531
|
+
}
|
|
49532
|
+
const parsePromise = (async () => {
|
|
49533
|
+
try {
|
|
49534
|
+
const projectMap = new Map;
|
|
49535
|
+
let totalEntries = 0;
|
|
49536
|
+
let errorCount = 0;
|
|
49537
|
+
try {
|
|
49538
|
+
const fileStream = createReadStream(path);
|
|
49539
|
+
const rl = createInterface2({ input: fileStream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
49540
|
+
for await (const line of rl) {
|
|
49541
|
+
if (!line.trim())
|
|
49542
|
+
continue;
|
|
49543
|
+
totalEntries++;
|
|
49544
|
+
try {
|
|
49545
|
+
const entry = JSON.parse(line);
|
|
49546
|
+
if (!entry.project)
|
|
49547
|
+
continue;
|
|
49548
|
+
const existing = projectMap.get(entry.project);
|
|
49549
|
+
if (existing) {
|
|
49550
|
+
existing.interactionCount++;
|
|
49551
|
+
if (entry.timestamp > existing.lastUsed) {
|
|
49552
|
+
existing.lastUsed = entry.timestamp;
|
|
49618
49553
|
}
|
|
49619
|
-
}
|
|
49620
|
-
|
|
49621
|
-
|
|
49622
|
-
|
|
49623
|
-
|
|
49624
|
-
|
|
49625
|
-
description: "Custom skill configuration"
|
|
49626
|
-
}
|
|
49627
|
-
},
|
|
49628
|
-
assertions: {
|
|
49629
|
-
type: "array",
|
|
49630
|
-
items: {
|
|
49631
|
-
type: "object",
|
|
49632
|
-
properties: {
|
|
49633
|
-
pattern: {
|
|
49634
|
-
type: "string",
|
|
49635
|
-
description: "Glob pattern or regex for files to match"
|
|
49636
|
-
},
|
|
49637
|
-
rule: {
|
|
49638
|
-
type: "string",
|
|
49639
|
-
description: "Assertion rule to enforce"
|
|
49640
|
-
},
|
|
49641
|
-
message: {
|
|
49642
|
-
type: "string",
|
|
49643
|
-
description: "Error message when assertion fails"
|
|
49644
|
-
}
|
|
49645
|
-
}
|
|
49646
|
-
},
|
|
49647
|
-
default: [],
|
|
49648
|
-
description: "Code assertions and rules to enforce"
|
|
49649
|
-
},
|
|
49650
|
-
statusline: {
|
|
49651
|
-
type: "string",
|
|
49652
|
-
enum: ["full", "compact", "minimal", "none"],
|
|
49653
|
-
default: "full",
|
|
49654
|
-
description: "Statusline display mode: full=all features, compact=2-3 lines, minimal=single text line, none=disabled"
|
|
49655
|
-
},
|
|
49656
|
-
statuslineColors: {
|
|
49657
|
-
type: "boolean",
|
|
49658
|
-
default: true,
|
|
49659
|
-
description: "Enable ANSI colors in statusline output. Set false for plain text. Respects NO_COLOR/FORCE_COLOR env vars."
|
|
49660
|
-
},
|
|
49661
|
-
statuslineQuota: {
|
|
49662
|
-
type: "boolean",
|
|
49663
|
-
default: true,
|
|
49664
|
-
description: "Show the cosmetic 5h / wk quota chips in the statusline. Disable to keep the statusline layout without usage-window percentages."
|
|
49665
|
-
},
|
|
49666
|
-
statuslineLayout: {
|
|
49667
|
-
type: "object",
|
|
49668
|
-
description: "Advanced statusline layout config for the visual builder. When absent, uses hardcoded defaults (backward compatible). Takes precedence over the statusline field when both are set.",
|
|
49669
|
-
properties: {
|
|
49670
|
-
baseMode: {
|
|
49671
|
-
type: "string",
|
|
49672
|
-
enum: ["full", "compact", "minimal", "none"],
|
|
49673
|
-
default: "full",
|
|
49674
|
-
description: "Starting template mode for the layout"
|
|
49675
|
-
},
|
|
49676
|
-
lines: {
|
|
49677
|
-
type: "array",
|
|
49678
|
-
description: "Layout lines. Each inner array is one terminal row containing section IDs in left-to-right order. Sections not listed in any line are hidden.",
|
|
49679
|
-
items: {
|
|
49680
|
-
type: "array",
|
|
49681
|
-
items: {
|
|
49682
|
-
type: "string",
|
|
49683
|
-
enum: [
|
|
49684
|
-
"model",
|
|
49685
|
-
"context",
|
|
49686
|
-
"quota",
|
|
49687
|
-
"directory",
|
|
49688
|
-
"git",
|
|
49689
|
-
"cost",
|
|
49690
|
-
"changes",
|
|
49691
|
-
"agents",
|
|
49692
|
-
"todos"
|
|
49693
|
-
]
|
|
49694
|
-
}
|
|
49695
|
-
},
|
|
49696
|
-
maxItems: 10
|
|
49697
|
-
},
|
|
49698
|
-
sectionConfig: {
|
|
49699
|
-
type: "object",
|
|
49700
|
-
description: "Per-section visual customization keyed by section ID.",
|
|
49701
|
-
additionalProperties: {
|
|
49702
|
-
type: "object",
|
|
49703
|
-
properties: {
|
|
49704
|
-
icon: {
|
|
49705
|
-
type: "string",
|
|
49706
|
-
description: "Custom emoji/icon override",
|
|
49707
|
-
maxLength: 20
|
|
49708
|
-
},
|
|
49709
|
-
label: {
|
|
49710
|
-
type: "string",
|
|
49711
|
-
description: "Custom label override",
|
|
49712
|
-
maxLength: 50
|
|
49713
|
-
},
|
|
49714
|
-
color: {
|
|
49715
|
-
type: "string",
|
|
49716
|
-
description: "ANSI named color (e.g. red, cyan, green). Hex codes not supported.",
|
|
49717
|
-
maxLength: 30,
|
|
49718
|
-
pattern: "^[a-zA-Z]+$"
|
|
49719
|
-
},
|
|
49720
|
-
maxWidth: {
|
|
49721
|
-
type: "integer",
|
|
49722
|
-
minimum: 10,
|
|
49723
|
-
description: "Maximum character width for this section",
|
|
49724
|
-
maximum: 500
|
|
49725
|
-
}
|
|
49726
|
-
},
|
|
49727
|
-
additionalProperties: false
|
|
49728
|
-
}
|
|
49729
|
-
},
|
|
49730
|
-
theme: {
|
|
49731
|
-
type: "object",
|
|
49732
|
-
description: "Color theme overrides",
|
|
49733
|
-
properties: {
|
|
49734
|
-
name: {
|
|
49735
|
-
type: "string",
|
|
49736
|
-
description: "Theme name (informational)",
|
|
49737
|
-
maxLength: 50
|
|
49738
|
-
},
|
|
49739
|
-
contextLow: {
|
|
49740
|
-
type: "string",
|
|
49741
|
-
default: "green",
|
|
49742
|
-
description: "Context bar color when usage <50%",
|
|
49743
|
-
maxLength: 30,
|
|
49744
|
-
pattern: "^[a-zA-Z]+$"
|
|
49745
|
-
},
|
|
49746
|
-
contextMid: {
|
|
49747
|
-
type: "string",
|
|
49748
|
-
default: "yellow",
|
|
49749
|
-
description: "Context bar color when usage 50-75%",
|
|
49750
|
-
maxLength: 30,
|
|
49751
|
-
pattern: "^[a-zA-Z]+$"
|
|
49752
|
-
},
|
|
49753
|
-
contextHigh: {
|
|
49754
|
-
type: "string",
|
|
49755
|
-
default: "red",
|
|
49756
|
-
description: "Context bar color when usage >75%",
|
|
49757
|
-
maxLength: 30,
|
|
49758
|
-
pattern: "^[a-zA-Z]+$"
|
|
49759
|
-
},
|
|
49760
|
-
accent: {
|
|
49761
|
-
type: "string",
|
|
49762
|
-
default: "cyan",
|
|
49763
|
-
description: "Accent color for highlights",
|
|
49764
|
-
maxLength: 30,
|
|
49765
|
-
pattern: "^[a-zA-Z]+$"
|
|
49766
|
-
},
|
|
49767
|
-
muted: {
|
|
49768
|
-
type: "string",
|
|
49769
|
-
default: "dim",
|
|
49770
|
-
description: "Muted text color",
|
|
49771
|
-
maxLength: 30,
|
|
49772
|
-
pattern: "^[a-zA-Z]+$"
|
|
49773
|
-
},
|
|
49774
|
-
separator: {
|
|
49775
|
-
type: "string",
|
|
49776
|
-
default: "dim",
|
|
49777
|
-
description: "Separator color",
|
|
49778
|
-
maxLength: 30,
|
|
49779
|
-
pattern: "^[a-zA-Z]+$"
|
|
49780
|
-
},
|
|
49781
|
-
quotaLow: {
|
|
49782
|
-
type: "string",
|
|
49783
|
-
description: "Quota section color when all windows <85%",
|
|
49784
|
-
maxLength: 30,
|
|
49785
|
-
pattern: "^[a-zA-Z]+$"
|
|
49786
|
-
},
|
|
49787
|
-
quotaHigh: {
|
|
49788
|
-
type: "string",
|
|
49789
|
-
description: "Quota section color when any window >=85%",
|
|
49790
|
-
maxLength: 30,
|
|
49791
|
-
pattern: "^[a-zA-Z]+$"
|
|
49792
|
-
}
|
|
49793
|
-
},
|
|
49794
|
-
additionalProperties: false
|
|
49795
|
-
},
|
|
49796
|
-
responsiveBreakpoint: {
|
|
49797
|
-
type: "number",
|
|
49798
|
-
minimum: 0.5,
|
|
49799
|
-
maximum: 1,
|
|
49800
|
-
default: 0.85,
|
|
49801
|
-
description: "Terminal width fraction at which compact mode activates"
|
|
49802
|
-
},
|
|
49803
|
-
maxAgentRows: {
|
|
49804
|
-
type: "integer",
|
|
49805
|
-
minimum: 1,
|
|
49806
|
-
maximum: 10,
|
|
49807
|
-
default: 4,
|
|
49808
|
-
description: "Maximum rows to show in agents section"
|
|
49809
|
-
},
|
|
49810
|
-
todoTruncation: {
|
|
49811
|
-
type: "integer",
|
|
49812
|
-
minimum: 20,
|
|
49813
|
-
maximum: 100,
|
|
49814
|
-
default: 50,
|
|
49815
|
-
description: "Max characters before truncating todo text"
|
|
49816
|
-
}
|
|
49817
|
-
},
|
|
49818
|
-
additionalProperties: false
|
|
49819
|
-
},
|
|
49820
|
-
hooks: {
|
|
49821
|
-
type: "object",
|
|
49822
|
-
description: "Toggle individual hooks on/off. Default: all enabled.",
|
|
49823
|
-
properties: {
|
|
49824
|
-
"session-init": {
|
|
49825
|
-
type: "boolean",
|
|
49826
|
-
default: true,
|
|
49827
|
-
description: "SessionStart hook - project detection and env setup"
|
|
49828
|
-
},
|
|
49829
|
-
"subagent-init": {
|
|
49830
|
-
type: "boolean",
|
|
49831
|
-
default: true,
|
|
49832
|
-
description: "SubagentStart hook - injects context to subagents"
|
|
49833
|
-
},
|
|
49834
|
-
"session-state": {
|
|
49835
|
-
type: "boolean",
|
|
49836
|
-
default: true,
|
|
49837
|
-
description: "Stop/SubagentStop/PostToolUse hook - persists session state for handoff and statusline context"
|
|
49838
|
-
},
|
|
49839
|
-
"cook-after-plan-reminder": {
|
|
49840
|
-
type: "boolean",
|
|
49841
|
-
default: true,
|
|
49842
|
-
description: "SubagentStop hook - reminds agents to continue implementation after planning"
|
|
49843
|
-
},
|
|
49844
|
-
"descriptive-name": {
|
|
49845
|
-
type: "boolean",
|
|
49846
|
-
default: true,
|
|
49847
|
-
description: "PreToolUse hook - injects language-aware descriptive file naming guidance"
|
|
49848
|
-
},
|
|
49849
|
-
"dev-rules-reminder": {
|
|
49850
|
-
type: "boolean",
|
|
49851
|
-
default: true,
|
|
49852
|
-
description: "UserPromptSubmit hook - injects dev rules context"
|
|
49853
|
-
},
|
|
49854
|
-
"plan-format-kanban": {
|
|
49855
|
-
type: "boolean",
|
|
49856
|
-
default: true,
|
|
49857
|
-
description: "PostToolUse hook - keeps plan kanban metadata synchronized after edits"
|
|
49858
|
-
},
|
|
49859
|
-
"usage-quota-cache-refresh": {
|
|
49860
|
-
type: "boolean",
|
|
49861
|
-
default: true,
|
|
49862
|
-
description: "Lifecycle hook - refreshes cached usage quota information"
|
|
49863
|
-
},
|
|
49864
|
-
"usage-context-awareness": {
|
|
49865
|
-
type: "boolean",
|
|
49866
|
-
default: true,
|
|
49867
|
-
description: "UserPromptSubmit/PostToolUse hook - inject usage-limit awareness into prompt context"
|
|
49868
|
-
},
|
|
49869
|
-
"context-tracking": {
|
|
49870
|
-
type: "boolean",
|
|
49871
|
-
default: true,
|
|
49872
|
-
description: "Context window tracking injection (% used, token counts) in dev-rules-reminder"
|
|
49873
|
-
},
|
|
49874
|
-
"scout-block": {
|
|
49875
|
-
type: "boolean",
|
|
49876
|
-
default: true,
|
|
49877
|
-
description: "PreToolUse hook - blocks heavy directories"
|
|
49878
|
-
},
|
|
49879
|
-
"privacy-block": {
|
|
49880
|
-
type: "boolean",
|
|
49881
|
-
default: true,
|
|
49882
|
-
description: "PreToolUse hook - blocks sensitive files"
|
|
49883
|
-
},
|
|
49884
|
-
"simplify-gate": {
|
|
49885
|
-
type: "boolean",
|
|
49886
|
-
default: true,
|
|
49887
|
-
description: "UserPromptSubmit hook - hard-blocks ship/merge/pr/deploy/publish and soft-warns commit/finalize/release when live diff exceeds simplify thresholds. Bypass via CK_SIMPLIFY_DISABLED=1."
|
|
49888
|
-
}
|
|
49889
|
-
},
|
|
49890
|
-
additionalProperties: false
|
|
49891
|
-
},
|
|
49892
|
-
simplify: {
|
|
49893
|
-
type: "object",
|
|
49894
|
-
description: "Signal-driven simplifier orchestration. Live git diff thresholds + verb-based gate.",
|
|
49895
|
-
properties: {
|
|
49896
|
-
threshold: {
|
|
49897
|
-
type: "object",
|
|
49898
|
-
description: "LOC/file thresholds that trip the gate. Nested object stays open for forward-compat fields.",
|
|
49899
|
-
properties: {
|
|
49900
|
-
locDelta: {
|
|
49901
|
-
type: "integer",
|
|
49902
|
-
minimum: 0,
|
|
49903
|
-
default: 400,
|
|
49904
|
-
description: "Total LOC change (across all files) above which the gate fires."
|
|
49905
|
-
},
|
|
49906
|
-
fileCount: {
|
|
49907
|
-
type: "integer",
|
|
49908
|
-
minimum: 0,
|
|
49909
|
-
default: 8,
|
|
49910
|
-
description: "Number of changed files above which the gate fires."
|
|
49911
|
-
},
|
|
49912
|
-
singleFileLoc: {
|
|
49913
|
-
type: "integer",
|
|
49914
|
-
minimum: 0,
|
|
49915
|
-
default: 200,
|
|
49916
|
-
description: "Per-file LOC change above which the gate fires."
|
|
49917
|
-
}
|
|
49918
|
-
}
|
|
49919
|
-
},
|
|
49920
|
-
gate: {
|
|
49921
|
-
type: "object",
|
|
49922
|
-
description: "UserPromptSubmit gate behavior. Nested object stays open for forward-compat fields.",
|
|
49923
|
-
properties: {
|
|
49924
|
-
enabled: {
|
|
49925
|
-
type: "boolean",
|
|
49926
|
-
default: false,
|
|
49927
|
-
description: "Master toggle. Off by default (opt-in). Set true to activate; env CK_SIMPLIFY_DISABLED=1 always bypasses."
|
|
49928
|
-
},
|
|
49929
|
-
hardVerbs: {
|
|
49930
|
-
type: "array",
|
|
49931
|
-
items: { type: "string" },
|
|
49932
|
-
default: ["ship", "merge", "pr", "deploy", "publish"],
|
|
49933
|
-
description: "Verbs that hard-block (continue:false) when thresholds breached."
|
|
49934
|
-
},
|
|
49935
|
-
softVerbs: {
|
|
49936
|
-
type: "array",
|
|
49937
|
-
items: { type: "string" },
|
|
49938
|
-
default: ["commit", "finalize", "release"],
|
|
49939
|
-
description: "Verbs that soft-warn (additionalContext) when thresholds breached."
|
|
49940
|
-
}
|
|
49941
|
-
}
|
|
49942
|
-
}
|
|
49943
|
-
},
|
|
49944
|
-
additionalProperties: false
|
|
49945
|
-
},
|
|
49946
|
-
updatePipeline: {
|
|
49947
|
-
type: "object",
|
|
49948
|
-
description: "3-step update pipeline: CLI update -> kit init -> provider migrate (each independent)",
|
|
49949
|
-
properties: {
|
|
49950
|
-
autoInitAfterUpdate: {
|
|
49951
|
-
type: "boolean",
|
|
49952
|
-
default: false,
|
|
49953
|
-
description: "Automatically run ck init after ck update when kit has new version"
|
|
49954
|
-
},
|
|
49955
|
-
autoMigrateAfterUpdate: {
|
|
49956
|
-
type: "boolean",
|
|
49957
|
-
default: false,
|
|
49958
|
-
description: "Automatically run ck migrate after ck update for detected providers (independent of init)"
|
|
49959
|
-
},
|
|
49960
|
-
migrateProviders: {
|
|
49961
|
-
oneOf: [
|
|
49962
|
-
{
|
|
49963
|
-
type: "string",
|
|
49964
|
-
const: "auto"
|
|
49965
|
-
},
|
|
49966
|
-
{
|
|
49967
|
-
type: "array",
|
|
49968
|
-
items: {
|
|
49969
|
-
type: "string"
|
|
49970
|
-
}
|
|
49971
|
-
}
|
|
49972
|
-
],
|
|
49973
|
-
default: "auto",
|
|
49974
|
-
description: "Which providers to auto-migrate. 'auto' scans filesystem."
|
|
49975
|
-
}
|
|
49976
|
-
},
|
|
49977
|
-
additionalProperties: false
|
|
49978
|
-
},
|
|
49979
|
-
modelTaxonomy: {
|
|
49980
|
-
type: "object",
|
|
49981
|
-
description: "Custom model mappings for portable migration. Keys are provider names (e.g., 'codex', 'gemini-cli'), values map tiers to model config.",
|
|
49982
|
-
additionalProperties: {
|
|
49983
|
-
type: "object",
|
|
49984
|
-
properties: {
|
|
49985
|
-
heavy: {
|
|
49986
|
-
type: "object",
|
|
49987
|
-
properties: {
|
|
49988
|
-
model: {
|
|
49989
|
-
type: "string",
|
|
49990
|
-
description: "Target model name"
|
|
49991
|
-
},
|
|
49992
|
-
effort: {
|
|
49993
|
-
type: "string",
|
|
49994
|
-
description: "Effort/reasoning level (provider-specific)"
|
|
49995
|
-
}
|
|
49996
|
-
},
|
|
49997
|
-
required: ["model"]
|
|
49998
|
-
},
|
|
49999
|
-
balanced: {
|
|
50000
|
-
type: "object",
|
|
50001
|
-
properties: {
|
|
50002
|
-
model: {
|
|
50003
|
-
type: "string",
|
|
50004
|
-
description: "Target model name"
|
|
50005
|
-
},
|
|
50006
|
-
effort: {
|
|
50007
|
-
type: "string",
|
|
50008
|
-
description: "Effort/reasoning level (provider-specific)"
|
|
50009
|
-
}
|
|
50010
|
-
},
|
|
50011
|
-
required: ["model"]
|
|
50012
|
-
},
|
|
50013
|
-
light: {
|
|
50014
|
-
type: "object",
|
|
50015
|
-
properties: {
|
|
50016
|
-
model: {
|
|
50017
|
-
type: "string",
|
|
50018
|
-
description: "Target model name"
|
|
50019
|
-
},
|
|
50020
|
-
effort: {
|
|
50021
|
-
type: "string",
|
|
50022
|
-
description: "Effort/reasoning level (provider-specific)"
|
|
50023
|
-
}
|
|
50024
|
-
},
|
|
50025
|
-
required: ["model"]
|
|
49554
|
+
} else {
|
|
49555
|
+
projectMap.set(entry.project, {
|
|
49556
|
+
path: entry.project,
|
|
49557
|
+
lastUsed: entry.timestamp,
|
|
49558
|
+
interactionCount: 1
|
|
49559
|
+
});
|
|
50026
49560
|
}
|
|
49561
|
+
} catch {
|
|
49562
|
+
errorCount++;
|
|
50027
49563
|
}
|
|
50028
49564
|
}
|
|
49565
|
+
} catch (err) {
|
|
49566
|
+
const msg = err instanceof Error ? err.message : "Parse error";
|
|
49567
|
+
return emptyResult(Date.now() - start, `Failed to parse history.jsonl: ${msg}`);
|
|
50029
49568
|
}
|
|
50030
|
-
|
|
50031
|
-
|
|
50032
|
-
|
|
49569
|
+
const result = {
|
|
49570
|
+
projects: Array.from(projectMap.values()).sort((a, b) => b.lastUsed - a.lastUsed),
|
|
49571
|
+
totalEntries,
|
|
49572
|
+
errorCount,
|
|
49573
|
+
parseTimeMs: Date.now() - start
|
|
49574
|
+
};
|
|
49575
|
+
cache.set(path, { mtime, result });
|
|
49576
|
+
return result;
|
|
49577
|
+
} finally {
|
|
49578
|
+
locks.delete(path);
|
|
49579
|
+
}
|
|
49580
|
+
})();
|
|
49581
|
+
locks.set(path, parsePromise);
|
|
49582
|
+
return parsePromise;
|
|
49583
|
+
}
|
|
49584
|
+
var cache, locks;
|
|
49585
|
+
var init_history_parser = __esm(() => {
|
|
49586
|
+
cache = new Map;
|
|
49587
|
+
locks = new Map;
|
|
50033
49588
|
});
|
|
50034
49589
|
|
|
50035
|
-
// src/
|
|
50036
|
-
import { existsSync as
|
|
50037
|
-
import { readFile as
|
|
50038
|
-
import { join as
|
|
50039
|
-
|
|
50040
|
-
|
|
50041
|
-
|
|
50042
|
-
|
|
50043
|
-
|
|
50044
|
-
|
|
49590
|
+
// src/services/claude-data/session-parser.ts
|
|
49591
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
49592
|
+
import { readFile as readFile12, readdir as readdir9, stat as stat5 } from "node:fs/promises";
|
|
49593
|
+
import { join as join31 } from "node:path";
|
|
49594
|
+
function formatTimestamp(date) {
|
|
49595
|
+
const now = new Date;
|
|
49596
|
+
const isToday = date.toDateString() === now.toDateString();
|
|
49597
|
+
const hours = date.getHours().toString().padStart(2, "0");
|
|
49598
|
+
const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
49599
|
+
if (isToday) {
|
|
49600
|
+
return `Today ${hours}:${minutes}`;
|
|
50045
49601
|
}
|
|
50046
|
-
|
|
50047
|
-
|
|
50048
|
-
|
|
49602
|
+
const months = [
|
|
49603
|
+
"Jan",
|
|
49604
|
+
"Feb",
|
|
49605
|
+
"Mar",
|
|
49606
|
+
"Apr",
|
|
49607
|
+
"May",
|
|
49608
|
+
"Jun",
|
|
49609
|
+
"Jul",
|
|
49610
|
+
"Aug",
|
|
49611
|
+
"Sep",
|
|
49612
|
+
"Oct",
|
|
49613
|
+
"Nov",
|
|
49614
|
+
"Dec"
|
|
49615
|
+
];
|
|
49616
|
+
return `${months[date.getMonth()]} ${date.getDate()} ${hours}:${minutes}`;
|
|
50049
49617
|
}
|
|
50050
|
-
function
|
|
50051
|
-
|
|
50052
|
-
|
|
50053
|
-
|
|
50054
|
-
|
|
50055
|
-
|
|
50056
|
-
|
|
50057
|
-
|
|
50058
|
-
|
|
50059
|
-
|
|
50060
|
-
|
|
50061
|
-
|
|
50062
|
-
|
|
50063
|
-
|
|
50064
|
-
|
|
50065
|
-
|
|
50066
|
-
|
|
50067
|
-
|
|
50068
|
-
|
|
49618
|
+
function formatDuration(startMs, endMs) {
|
|
49619
|
+
const diffMs = endMs - startMs;
|
|
49620
|
+
const minutes = Math.floor(diffMs / 60000);
|
|
49621
|
+
const hours = Math.floor(minutes / 60);
|
|
49622
|
+
const remainingMinutes = minutes % 60;
|
|
49623
|
+
if (hours === 0) {
|
|
49624
|
+
return `${remainingMinutes}min`;
|
|
49625
|
+
}
|
|
49626
|
+
return `${hours}h ${remainingMinutes}min`;
|
|
49627
|
+
}
|
|
49628
|
+
function extractContent(message) {
|
|
49629
|
+
if (!message?.content)
|
|
49630
|
+
return "";
|
|
49631
|
+
if (typeof message.content === "string") {
|
|
49632
|
+
return message.content;
|
|
49633
|
+
}
|
|
49634
|
+
const textBlock = message.content.find((b) => b.type === "text");
|
|
49635
|
+
return textBlock?.text || "";
|
|
49636
|
+
}
|
|
49637
|
+
async function parseSession(filePath) {
|
|
49638
|
+
try {
|
|
49639
|
+
const content = await readFile12(filePath, "utf-8");
|
|
49640
|
+
const lines = content.split(`
|
|
49641
|
+
`).filter((l) => l.trim());
|
|
49642
|
+
if (lines.length === 0)
|
|
49643
|
+
return null;
|
|
49644
|
+
let summary = "";
|
|
49645
|
+
let sessionId = "";
|
|
49646
|
+
let firstTimestamp = null;
|
|
49647
|
+
let lastTimestamp = null;
|
|
49648
|
+
for (const line of lines) {
|
|
49649
|
+
try {
|
|
49650
|
+
const event = JSON.parse(line);
|
|
49651
|
+
if (event.type === "summary" && event.summary && !summary) {
|
|
49652
|
+
summary = event.summary;
|
|
50069
49653
|
}
|
|
50070
|
-
|
|
50071
|
-
|
|
50072
|
-
config: config || {},
|
|
50073
|
-
sources: {},
|
|
50074
|
-
globalPath: CkConfigManager.getGlobalConfigPath(),
|
|
50075
|
-
projectPath: CkConfigManager.getProjectConfigPath(projectDir)
|
|
50076
|
-
});
|
|
50077
|
-
return;
|
|
50078
|
-
}
|
|
50079
|
-
const result = await CkConfigManager.loadFull(projectDir);
|
|
50080
|
-
res.json(result);
|
|
50081
|
-
} catch (error) {
|
|
50082
|
-
logger.error(`Failed to load ck-config: ${error}`);
|
|
50083
|
-
res.status(500).json({ error: "Failed to load configuration" });
|
|
50084
|
-
}
|
|
50085
|
-
});
|
|
50086
|
-
app.put("/api/ck-config", async (req, res) => {
|
|
50087
|
-
try {
|
|
50088
|
-
const { scope, projectId, config } = req.body;
|
|
50089
|
-
if (!scope || !["global", "project"].includes(scope)) {
|
|
50090
|
-
res.status(400).json({ error: "Invalid scope. Must be 'global' or 'project'" });
|
|
50091
|
-
return;
|
|
50092
|
-
}
|
|
50093
|
-
if (!config || typeof config !== "object") {
|
|
50094
|
-
res.status(400).json({ error: "Invalid config payload" });
|
|
50095
|
-
return;
|
|
50096
|
-
}
|
|
50097
|
-
const parseResult = CkConfigSchema.safeParse(normalizeCkConfigInput(config));
|
|
50098
|
-
if (!parseResult.success) {
|
|
50099
|
-
res.status(400).json({
|
|
50100
|
-
error: "Config validation failed",
|
|
50101
|
-
details: parseResult.error.issues
|
|
50102
|
-
});
|
|
50103
|
-
return;
|
|
50104
|
-
}
|
|
50105
|
-
let projectDir = null;
|
|
50106
|
-
if (scope === "project") {
|
|
50107
|
-
if (!projectId) {
|
|
50108
|
-
res.status(400).json({ error: "projectId required for project scope" });
|
|
50109
|
-
return;
|
|
49654
|
+
if (event.sessionId && !sessionId) {
|
|
49655
|
+
sessionId = event.sessionId;
|
|
50110
49656
|
}
|
|
50111
|
-
|
|
50112
|
-
|
|
50113
|
-
|
|
50114
|
-
|
|
49657
|
+
if (event.timestamp) {
|
|
49658
|
+
const ts = new Date(event.timestamp);
|
|
49659
|
+
if (!firstTimestamp)
|
|
49660
|
+
firstTimestamp = ts;
|
|
49661
|
+
lastTimestamp = ts;
|
|
50115
49662
|
}
|
|
50116
|
-
|
|
50117
|
-
|
|
50118
|
-
|
|
50119
|
-
|
|
50120
|
-
|
|
50121
|
-
const rawContent = await readFile11(savedPath, "utf-8");
|
|
50122
|
-
const rawConfig = JSON.parse(rawContent);
|
|
50123
|
-
if (rawConfig && typeof rawConfig === "object" && !Array.isArray(rawConfig)) {
|
|
50124
|
-
savedConfig = normalizeCkConfigInput(rawConfig);
|
|
50125
|
-
}
|
|
50126
|
-
} catch {
|
|
50127
|
-
savedConfig = null;
|
|
50128
|
-
}
|
|
50129
|
-
}
|
|
50130
|
-
res.json({
|
|
50131
|
-
success: true,
|
|
50132
|
-
path: savedPath,
|
|
50133
|
-
scope,
|
|
50134
|
-
config: savedConfig || {}
|
|
50135
|
-
});
|
|
50136
|
-
} catch (error) {
|
|
50137
|
-
logger.error(`Failed to save ck-config: ${error}`);
|
|
50138
|
-
res.status(500).json({ error: "Failed to save configuration" });
|
|
50139
|
-
}
|
|
50140
|
-
});
|
|
50141
|
-
app.get("/api/ck-config/schema", (_req, res) => {
|
|
50142
|
-
res.json(ck_config_schema_default);
|
|
50143
|
-
});
|
|
50144
|
-
app.patch("/api/ck-config/field", async (req, res) => {
|
|
50145
|
-
try {
|
|
50146
|
-
const { scope, projectId, fieldPath, value } = req.body;
|
|
50147
|
-
if (!scope || !["global", "project"].includes(scope)) {
|
|
50148
|
-
res.status(400).json({ error: "Invalid scope" });
|
|
50149
|
-
return;
|
|
50150
|
-
}
|
|
50151
|
-
if (!fieldPath || typeof fieldPath !== "string") {
|
|
50152
|
-
res.status(400).json({ error: "fieldPath required" });
|
|
50153
|
-
return;
|
|
50154
|
-
}
|
|
50155
|
-
let projectDir = null;
|
|
50156
|
-
if (scope === "project") {
|
|
50157
|
-
if (!projectId) {
|
|
50158
|
-
res.status(400).json({ error: "projectId required for project scope" });
|
|
50159
|
-
return;
|
|
50160
|
-
}
|
|
50161
|
-
projectDir = await resolveProjectDir(projectId);
|
|
50162
|
-
if (!projectDir) {
|
|
50163
|
-
res.status(404).json({ error: "Project not found" });
|
|
50164
|
-
return;
|
|
50165
|
-
}
|
|
50166
|
-
}
|
|
50167
|
-
await CkConfigManager.updateField(fieldPath, value, scope, projectDir);
|
|
50168
|
-
res.json({ success: true, fieldPath, value, scope });
|
|
50169
|
-
} catch (error) {
|
|
50170
|
-
if (error instanceof ZodError) {
|
|
50171
|
-
res.status(400).json({ error: "Validation failed", issues: error.issues });
|
|
50172
|
-
return;
|
|
50173
|
-
}
|
|
50174
|
-
logger.error(`Failed to update ck-config field: ${error}`);
|
|
50175
|
-
res.status(500).json({ error: "Failed to update field" });
|
|
50176
|
-
}
|
|
50177
|
-
});
|
|
50178
|
-
app.get("/api/metadata/global", async (_req, res) => {
|
|
50179
|
-
try {
|
|
50180
|
-
const metadataPath = join29(PathResolver.getGlobalKitDir(), "metadata.json");
|
|
50181
|
-
let metadata = {};
|
|
50182
|
-
if (existsSync15(metadataPath)) {
|
|
50183
|
-
const content = await readFile11(metadataPath, "utf-8");
|
|
50184
|
-
try {
|
|
50185
|
-
metadata = JSON.parse(content);
|
|
50186
|
-
} catch (err) {
|
|
50187
|
-
logger.warning(`Invalid JSON in metadata.json: ${err}`);
|
|
50188
|
-
}
|
|
50189
|
-
}
|
|
50190
|
-
res.json(metadata);
|
|
50191
|
-
} catch (error) {
|
|
50192
|
-
logger.error(`Failed to load global metadata: ${error}`);
|
|
50193
|
-
res.status(500).json({ error: "Failed to load metadata" });
|
|
50194
|
-
}
|
|
50195
|
-
});
|
|
50196
|
-
}
|
|
50197
|
-
var init_ck_config_routes = __esm(() => {
|
|
50198
|
-
init_config();
|
|
50199
|
-
init_ck_config_schema();
|
|
50200
|
-
init_logger();
|
|
50201
|
-
init_path_resolver();
|
|
50202
|
-
init_types3();
|
|
50203
|
-
init_zod();
|
|
50204
|
-
});
|
|
50205
|
-
|
|
50206
|
-
// src/domains/web-server/routes/command-routes.ts
|
|
50207
|
-
import { existsSync as existsSync16 } from "node:fs";
|
|
50208
|
-
import { readFile as readFile12, readdir as readdir8 } from "node:fs/promises";
|
|
50209
|
-
import { homedir as homedir15 } from "node:os";
|
|
50210
|
-
import { basename as basename9, isAbsolute as isAbsolute5, join as join30, relative as relative8, resolve as resolve13 } from "node:path";
|
|
50211
|
-
async function buildCommandTree(dir, baseDir) {
|
|
50212
|
-
let dirents;
|
|
50213
|
-
try {
|
|
50214
|
-
dirents = await readdir8(dir, { withFileTypes: true });
|
|
50215
|
-
} catch {
|
|
50216
|
-
return [];
|
|
50217
|
-
}
|
|
50218
|
-
const nodes = [];
|
|
50219
|
-
const sorted = [...dirents].sort((a, b) => {
|
|
50220
|
-
const aDir = a.isDirectory();
|
|
50221
|
-
const bDir = b.isDirectory();
|
|
50222
|
-
if (aDir !== bDir)
|
|
50223
|
-
return aDir ? -1 : 1;
|
|
50224
|
-
return a.name.localeCompare(b.name);
|
|
50225
|
-
});
|
|
50226
|
-
for (const entry of sorted) {
|
|
50227
|
-
const name = entry.name;
|
|
50228
|
-
const isDir = entry.isDirectory();
|
|
50229
|
-
const fullPath = join30(dir, name);
|
|
50230
|
-
const relPath = relative8(baseDir, fullPath);
|
|
50231
|
-
if (isDir) {
|
|
50232
|
-
const children = await buildCommandTree(fullPath, baseDir);
|
|
50233
|
-
if (children.length > 0) {
|
|
50234
|
-
nodes.push({ name, path: relPath, children });
|
|
50235
|
-
}
|
|
50236
|
-
} else if (name.endsWith(".md")) {
|
|
50237
|
-
let description;
|
|
50238
|
-
try {
|
|
50239
|
-
const content = await readFile12(fullPath, "utf-8");
|
|
50240
|
-
const { frontmatter } = parseFrontmatter(content);
|
|
50241
|
-
description = frontmatter.description;
|
|
50242
|
-
} catch {}
|
|
50243
|
-
const commandName = basename9(name, ".md");
|
|
50244
|
-
nodes.push({ name: commandName, path: relPath, description });
|
|
50245
|
-
}
|
|
50246
|
-
}
|
|
50247
|
-
return nodes;
|
|
50248
|
-
}
|
|
50249
|
-
function registerCommandRoutes(app) {
|
|
50250
|
-
app.get("/api/commands", async (_req, res) => {
|
|
50251
|
-
const commandsDir = join30(homedir15(), ".claude", "commands");
|
|
50252
|
-
if (!existsSync16(commandsDir)) {
|
|
50253
|
-
res.json({ tree: [] });
|
|
50254
|
-
return;
|
|
50255
|
-
}
|
|
50256
|
-
try {
|
|
50257
|
-
const tree = await buildCommandTree(commandsDir, commandsDir);
|
|
50258
|
-
res.json({ tree });
|
|
50259
|
-
} catch {
|
|
50260
|
-
res.status(500).json({ error: "Failed to scan commands directory" });
|
|
50261
|
-
}
|
|
50262
|
-
});
|
|
50263
|
-
app.get("/api/commands/detail/:slug", async (req, res) => {
|
|
50264
|
-
const slug = String(req.params.slug ?? "");
|
|
50265
|
-
const rawPath = slug.replace(/--/g, "/");
|
|
50266
|
-
if (!rawPath) {
|
|
50267
|
-
res.status(400).json({ error: "Missing command path" });
|
|
50268
|
-
return;
|
|
50269
|
-
}
|
|
50270
|
-
if (rawPath.includes("..") || rawPath.includes("\x00")) {
|
|
50271
|
-
res.status(400).json({ error: "Invalid path" });
|
|
50272
|
-
return;
|
|
50273
|
-
}
|
|
50274
|
-
const commandsDir = join30(homedir15(), ".claude", "commands");
|
|
50275
|
-
const safePath = resolve13(commandsDir, rawPath);
|
|
50276
|
-
const rel = relative8(commandsDir, safePath);
|
|
50277
|
-
if (rel.startsWith("..") || isAbsolute5(rel)) {
|
|
50278
|
-
res.status(403).json({ error: "Access denied" });
|
|
50279
|
-
return;
|
|
50280
|
-
}
|
|
50281
|
-
const filePath = safePath.endsWith(".md") ? safePath : `${safePath}.md`;
|
|
50282
|
-
if (!existsSync16(filePath)) {
|
|
50283
|
-
res.status(404).json({ error: "Command not found" });
|
|
50284
|
-
return;
|
|
50285
|
-
}
|
|
50286
|
-
try {
|
|
50287
|
-
const content = await readFile12(filePath, "utf-8");
|
|
50288
|
-
const { frontmatter } = parseFrontmatter(content);
|
|
50289
|
-
const commandName = basename9(filePath, ".md");
|
|
50290
|
-
res.json({
|
|
50291
|
-
name: commandName,
|
|
50292
|
-
path: rawPath,
|
|
50293
|
-
content,
|
|
50294
|
-
description: frontmatter.description
|
|
50295
|
-
});
|
|
50296
|
-
} catch {
|
|
50297
|
-
res.status(500).json({ error: "Failed to read command" });
|
|
50298
|
-
}
|
|
50299
|
-
});
|
|
50300
|
-
}
|
|
50301
|
-
var init_command_routes = __esm(() => {
|
|
50302
|
-
init_frontmatter_parser();
|
|
50303
|
-
});
|
|
50304
|
-
|
|
50305
|
-
// src/services/claude-data/types.ts
|
|
50306
|
-
var HistoryEntrySchema, UserPreferencesSchema, TipsHistorySchema, FeatureFlagsSchema, HookLogEntrySchema;
|
|
50307
|
-
var init_types5 = __esm(() => {
|
|
50308
|
-
init_zod();
|
|
50309
|
-
HistoryEntrySchema = objectType({
|
|
50310
|
-
display: stringType(),
|
|
50311
|
-
pastedContents: recordType(unknownType()).optional(),
|
|
50312
|
-
timestamp: numberType(),
|
|
50313
|
-
project: stringType()
|
|
50314
|
-
});
|
|
50315
|
-
UserPreferencesSchema = objectType({
|
|
50316
|
-
numStartups: numberType().optional(),
|
|
50317
|
-
theme: enumType(["dark", "light", "dark-daltonized"]).optional(),
|
|
50318
|
-
installMethod: stringType().optional(),
|
|
50319
|
-
autoUpdates: booleanType().optional(),
|
|
50320
|
-
firstStartTime: stringType().optional(),
|
|
50321
|
-
hasSeenTasksHint: booleanType().optional(),
|
|
50322
|
-
hasSeenStashHint: booleanType().optional(),
|
|
50323
|
-
promptQueueUseCount: numberType().optional()
|
|
50324
|
-
});
|
|
50325
|
-
TipsHistorySchema = recordType(stringType(), numberType());
|
|
50326
|
-
FeatureFlagsSchema = objectType({
|
|
50327
|
-
statsigGates: recordType(stringType(), booleanType()).optional(),
|
|
50328
|
-
growthBookFeatures: recordType(stringType(), unknownType()).optional()
|
|
50329
|
-
});
|
|
50330
|
-
HookLogEntrySchema = objectType({
|
|
50331
|
-
ts: stringType().min(1).refine((value) => Number.isFinite(Date.parse(value)), {
|
|
50332
|
-
message: "Invalid timestamp"
|
|
50333
|
-
}),
|
|
50334
|
-
hook: stringType().min(1),
|
|
50335
|
-
event: stringType().min(1).optional(),
|
|
50336
|
-
tool: stringType().min(1).optional(),
|
|
50337
|
-
target: stringType().min(1).optional(),
|
|
50338
|
-
note: stringType().min(1).optional(),
|
|
50339
|
-
dur: numberType().finite().nonnegative().optional(),
|
|
50340
|
-
status: stringType().min(1),
|
|
50341
|
-
exit: numberType().int().optional(),
|
|
50342
|
-
error: stringType().min(1).optional()
|
|
50343
|
-
}).passthrough();
|
|
50344
|
-
});
|
|
50345
|
-
|
|
50346
|
-
// src/services/claude-data/history-parser.ts
|
|
50347
|
-
import { createReadStream, statSync as statSync4 } from "node:fs";
|
|
50348
|
-
import { homedir as homedir16 } from "node:os";
|
|
50349
|
-
import { join as join31 } from "node:path";
|
|
50350
|
-
import { createInterface as createInterface2 } from "node:readline";
|
|
50351
|
-
function getHistoryPath() {
|
|
50352
|
-
return process.env.CLAUDE_HISTORY_PATH ?? join31(homedir16(), ".claude", "history.jsonl");
|
|
50353
|
-
}
|
|
50354
|
-
function emptyResult(parseTimeMs, error) {
|
|
50355
|
-
return { projects: [], totalEntries: 0, errorCount: 0, parseTimeMs, error };
|
|
50356
|
-
}
|
|
50357
|
-
async function parseHistoryFile(filePath) {
|
|
50358
|
-
const path = filePath ?? getHistoryPath();
|
|
50359
|
-
const existingLock = locks.get(path);
|
|
50360
|
-
if (existingLock)
|
|
50361
|
-
return existingLock;
|
|
50362
|
-
const start = Date.now();
|
|
50363
|
-
let mtime;
|
|
50364
|
-
try {
|
|
50365
|
-
const stats = statSync4(path);
|
|
50366
|
-
mtime = stats.mtimeMs;
|
|
50367
|
-
const cached = cache.get(path);
|
|
50368
|
-
if (cached && cached.mtime === mtime) {
|
|
50369
|
-
return cached.result;
|
|
50370
|
-
}
|
|
50371
|
-
} catch (err) {
|
|
50372
|
-
const msg = err instanceof Error ? err.message : "File not found";
|
|
50373
|
-
return emptyResult(Date.now() - start, `Cannot read history.jsonl: ${msg}`);
|
|
50374
|
-
}
|
|
50375
|
-
const parsePromise = (async () => {
|
|
50376
|
-
try {
|
|
50377
|
-
const projectMap = new Map;
|
|
50378
|
-
let totalEntries = 0;
|
|
50379
|
-
let errorCount = 0;
|
|
50380
|
-
try {
|
|
50381
|
-
const fileStream = createReadStream(path);
|
|
50382
|
-
const rl = createInterface2({ input: fileStream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
50383
|
-
for await (const line of rl) {
|
|
50384
|
-
if (!line.trim())
|
|
50385
|
-
continue;
|
|
50386
|
-
totalEntries++;
|
|
50387
|
-
try {
|
|
50388
|
-
const entry = JSON.parse(line);
|
|
50389
|
-
if (!entry.project)
|
|
50390
|
-
continue;
|
|
50391
|
-
const existing = projectMap.get(entry.project);
|
|
50392
|
-
if (existing) {
|
|
50393
|
-
existing.interactionCount++;
|
|
50394
|
-
if (entry.timestamp > existing.lastUsed) {
|
|
50395
|
-
existing.lastUsed = entry.timestamp;
|
|
50396
|
-
}
|
|
50397
|
-
} else {
|
|
50398
|
-
projectMap.set(entry.project, {
|
|
50399
|
-
path: entry.project,
|
|
50400
|
-
lastUsed: entry.timestamp,
|
|
50401
|
-
interactionCount: 1
|
|
50402
|
-
});
|
|
50403
|
-
}
|
|
50404
|
-
} catch {
|
|
50405
|
-
errorCount++;
|
|
50406
|
-
}
|
|
50407
|
-
}
|
|
50408
|
-
} catch (err) {
|
|
50409
|
-
const msg = err instanceof Error ? err.message : "Parse error";
|
|
50410
|
-
return emptyResult(Date.now() - start, `Failed to parse history.jsonl: ${msg}`);
|
|
50411
|
-
}
|
|
50412
|
-
const result = {
|
|
50413
|
-
projects: Array.from(projectMap.values()).sort((a, b) => b.lastUsed - a.lastUsed),
|
|
50414
|
-
totalEntries,
|
|
50415
|
-
errorCount,
|
|
50416
|
-
parseTimeMs: Date.now() - start
|
|
50417
|
-
};
|
|
50418
|
-
cache.set(path, { mtime, result });
|
|
50419
|
-
return result;
|
|
50420
|
-
} finally {
|
|
50421
|
-
locks.delete(path);
|
|
50422
|
-
}
|
|
50423
|
-
})();
|
|
50424
|
-
locks.set(path, parsePromise);
|
|
50425
|
-
return parsePromise;
|
|
50426
|
-
}
|
|
50427
|
-
var cache, locks;
|
|
50428
|
-
var init_history_parser = __esm(() => {
|
|
50429
|
-
cache = new Map;
|
|
50430
|
-
locks = new Map;
|
|
50431
|
-
});
|
|
50432
|
-
|
|
50433
|
-
// src/services/claude-data/session-parser.ts
|
|
50434
|
-
import { existsSync as existsSync17 } from "node:fs";
|
|
50435
|
-
import { readFile as readFile13, readdir as readdir9, stat as stat5 } from "node:fs/promises";
|
|
50436
|
-
import { join as join32 } from "node:path";
|
|
50437
|
-
function formatTimestamp(date) {
|
|
50438
|
-
const now = new Date;
|
|
50439
|
-
const isToday = date.toDateString() === now.toDateString();
|
|
50440
|
-
const hours = date.getHours().toString().padStart(2, "0");
|
|
50441
|
-
const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
50442
|
-
if (isToday) {
|
|
50443
|
-
return `Today ${hours}:${minutes}`;
|
|
50444
|
-
}
|
|
50445
|
-
const months = [
|
|
50446
|
-
"Jan",
|
|
50447
|
-
"Feb",
|
|
50448
|
-
"Mar",
|
|
50449
|
-
"Apr",
|
|
50450
|
-
"May",
|
|
50451
|
-
"Jun",
|
|
50452
|
-
"Jul",
|
|
50453
|
-
"Aug",
|
|
50454
|
-
"Sep",
|
|
50455
|
-
"Oct",
|
|
50456
|
-
"Nov",
|
|
50457
|
-
"Dec"
|
|
50458
|
-
];
|
|
50459
|
-
return `${months[date.getMonth()]} ${date.getDate()} ${hours}:${minutes}`;
|
|
50460
|
-
}
|
|
50461
|
-
function formatDuration(startMs, endMs) {
|
|
50462
|
-
const diffMs = endMs - startMs;
|
|
50463
|
-
const minutes = Math.floor(diffMs / 60000);
|
|
50464
|
-
const hours = Math.floor(minutes / 60);
|
|
50465
|
-
const remainingMinutes = minutes % 60;
|
|
50466
|
-
if (hours === 0) {
|
|
50467
|
-
return `${remainingMinutes}min`;
|
|
50468
|
-
}
|
|
50469
|
-
return `${hours}h ${remainingMinutes}min`;
|
|
50470
|
-
}
|
|
50471
|
-
function extractContent(message) {
|
|
50472
|
-
if (!message?.content)
|
|
50473
|
-
return "";
|
|
50474
|
-
if (typeof message.content === "string") {
|
|
50475
|
-
return message.content;
|
|
50476
|
-
}
|
|
50477
|
-
const textBlock = message.content.find((b) => b.type === "text");
|
|
50478
|
-
return textBlock?.text || "";
|
|
50479
|
-
}
|
|
50480
|
-
async function parseSession(filePath) {
|
|
50481
|
-
try {
|
|
50482
|
-
const content = await readFile13(filePath, "utf-8");
|
|
50483
|
-
const lines = content.split(`
|
|
50484
|
-
`).filter((l) => l.trim());
|
|
50485
|
-
if (lines.length === 0)
|
|
50486
|
-
return null;
|
|
50487
|
-
let summary = "";
|
|
50488
|
-
let sessionId = "";
|
|
50489
|
-
let firstTimestamp = null;
|
|
50490
|
-
let lastTimestamp = null;
|
|
50491
|
-
for (const line of lines) {
|
|
50492
|
-
try {
|
|
50493
|
-
const event = JSON.parse(line);
|
|
50494
|
-
if (event.type === "summary" && event.summary && !summary) {
|
|
50495
|
-
summary = event.summary;
|
|
50496
|
-
}
|
|
50497
|
-
if (event.sessionId && !sessionId) {
|
|
50498
|
-
sessionId = event.sessionId;
|
|
50499
|
-
}
|
|
50500
|
-
if (event.timestamp) {
|
|
50501
|
-
const ts = new Date(event.timestamp);
|
|
50502
|
-
if (!firstTimestamp)
|
|
50503
|
-
firstTimestamp = ts;
|
|
50504
|
-
lastTimestamp = ts;
|
|
50505
|
-
}
|
|
50506
|
-
if (event.type === "user" && event.message?.role === "user" && !summary) {
|
|
50507
|
-
const text = extractContent(event.message);
|
|
50508
|
-
summary = text.replace(/<[^>]+>/g, "").slice(0, 100);
|
|
50509
|
-
if (text.length > 100)
|
|
50510
|
-
summary += "...";
|
|
49663
|
+
if (event.type === "user" && event.message?.role === "user" && !summary) {
|
|
49664
|
+
const text = extractContent(event.message);
|
|
49665
|
+
summary = text.replace(/<[^>]+>/g, "").slice(0, 100);
|
|
49666
|
+
if (text.length > 100)
|
|
49667
|
+
summary += "...";
|
|
50511
49668
|
}
|
|
50512
49669
|
} catch {}
|
|
50513
49670
|
}
|
|
@@ -50524,13 +49681,13 @@ async function parseSession(filePath) {
|
|
|
50524
49681
|
}
|
|
50525
49682
|
}
|
|
50526
49683
|
async function getProjectSessions(projectDir, limit = 10) {
|
|
50527
|
-
if (!
|
|
49684
|
+
if (!existsSync16(projectDir))
|
|
50528
49685
|
return [];
|
|
50529
49686
|
try {
|
|
50530
49687
|
const files = await readdir9(projectDir);
|
|
50531
49688
|
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
50532
49689
|
const fileStats = await Promise.all(jsonlFiles.map(async (file) => {
|
|
50533
|
-
const filePath =
|
|
49690
|
+
const filePath = join31(projectDir, file);
|
|
50534
49691
|
const fileStat = await stat5(filePath).catch(() => null);
|
|
50535
49692
|
return { file, filePath, mtime: fileStat?.mtime || new Date(0) };
|
|
50536
49693
|
}));
|
|
@@ -50545,24 +49702,24 @@ async function getProjectSessions(projectDir, limit = 10) {
|
|
|
50545
49702
|
var init_session_parser = () => {};
|
|
50546
49703
|
|
|
50547
49704
|
// src/services/claude-data/skill-scanner.ts
|
|
50548
|
-
import { existsSync as
|
|
50549
|
-
import { readFile as
|
|
49705
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
49706
|
+
import { readFile as readFile13, readdir as readdir10, stat as stat6 } from "node:fs/promises";
|
|
50550
49707
|
import { homedir as homedir17 } from "node:os";
|
|
50551
|
-
import { join as
|
|
49708
|
+
import { join as join32 } from "node:path";
|
|
50552
49709
|
async function getCkSkillMetadata(scope) {
|
|
50553
|
-
const metaPath = scope === "global" ?
|
|
50554
|
-
if (!
|
|
49710
|
+
const metaPath = scope === "global" ? join32(homedir17(), ".claude", "metadata.json") : join32(process.cwd(), ".claude", "metadata.json");
|
|
49711
|
+
if (!existsSync17(metaPath))
|
|
50555
49712
|
return null;
|
|
50556
49713
|
const result = new Map;
|
|
50557
49714
|
try {
|
|
50558
|
-
const content = await
|
|
49715
|
+
const content = await readFile13(metaPath, "utf-8");
|
|
50559
49716
|
const data = JSON.parse(content);
|
|
50560
49717
|
for (const [kitName, kitData] of Object.entries(data.kits || {})) {
|
|
50561
49718
|
const kit = kitData;
|
|
50562
49719
|
if (!Array.isArray(kit.files))
|
|
50563
49720
|
continue;
|
|
50564
49721
|
for (const file of kit.files) {
|
|
50565
|
-
if (file.ownership !== "
|
|
49722
|
+
if (file.ownership !== "gck")
|
|
50566
49723
|
continue;
|
|
50567
49724
|
const parts = file.path.split("/");
|
|
50568
49725
|
if (parts.length < 3 || parts[0] !== "skills")
|
|
@@ -50586,14 +49743,14 @@ async function getCkSkillMetadata(scope) {
|
|
|
50586
49743
|
return result.size > 0 ? result : null;
|
|
50587
49744
|
}
|
|
50588
49745
|
async function getSkillMetadata(skillPath) {
|
|
50589
|
-
const skillMdPath =
|
|
49746
|
+
const skillMdPath = join32(skillPath, "SKILL.md");
|
|
50590
49747
|
try {
|
|
50591
49748
|
await stat6(skillMdPath);
|
|
50592
49749
|
} catch {
|
|
50593
49750
|
return null;
|
|
50594
49751
|
}
|
|
50595
49752
|
try {
|
|
50596
|
-
const content = await
|
|
49753
|
+
const content = await readFile13(skillMdPath, "utf-8");
|
|
50597
49754
|
const { data } = import_gray_matter4.default(content, { engines: { javascript: { parse: () => ({}) } } });
|
|
50598
49755
|
return data;
|
|
50599
49756
|
} catch {
|
|
@@ -50619,7 +49776,7 @@ function inferCategory(name, metadata) {
|
|
|
50619
49776
|
return "other";
|
|
50620
49777
|
}
|
|
50621
49778
|
async function scanSkills() {
|
|
50622
|
-
if (!
|
|
49779
|
+
if (!existsSync17(skillsDir))
|
|
50623
49780
|
return [];
|
|
50624
49781
|
try {
|
|
50625
49782
|
const entries = await readdir10(skillsDir);
|
|
@@ -50634,9 +49791,9 @@ async function scanSkills() {
|
|
|
50634
49791
|
continue;
|
|
50635
49792
|
if (ckMeta && !ckMeta.has(entry))
|
|
50636
49793
|
continue;
|
|
50637
|
-
const entryPath =
|
|
50638
|
-
const skillMdPath =
|
|
50639
|
-
if (!
|
|
49794
|
+
const entryPath = join32(skillsDir, entry);
|
|
49795
|
+
const skillMdPath = join32(entryPath, "SKILL.md");
|
|
49796
|
+
if (!existsSync17(skillMdPath))
|
|
50640
49797
|
continue;
|
|
50641
49798
|
const frontmatter = await getSkillMetadata(entryPath);
|
|
50642
49799
|
const meta = ckMeta?.get(entry);
|
|
@@ -50680,24 +49837,24 @@ async function scanSkills() {
|
|
|
50680
49837
|
var import_gray_matter4, skillsDir, SKIP_DIRS2;
|
|
50681
49838
|
var init_skill_scanner = __esm(() => {
|
|
50682
49839
|
import_gray_matter4 = __toESM(require_gray_matter(), 1);
|
|
50683
|
-
skillsDir =
|
|
49840
|
+
skillsDir = join32(homedir17(), ".claude", "skills");
|
|
50684
49841
|
SKIP_DIRS2 = [".venv", "scripts", "__pycache__", "node_modules", ".git", "common"];
|
|
50685
49842
|
});
|
|
50686
49843
|
|
|
50687
49844
|
// src/services/claude-data/settings-reader.ts
|
|
50688
|
-
import { existsSync as
|
|
50689
|
-
import { copyFile as copyFile2, mkdir as mkdir9, readFile as
|
|
49845
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
49846
|
+
import { copyFile as copyFile2, mkdir as mkdir9, readFile as readFile14, rename as rename5, rm as rm4, writeFile as writeFile10 } from "node:fs/promises";
|
|
50690
49847
|
import { homedir as homedir18 } from "node:os";
|
|
50691
|
-
import { join as
|
|
49848
|
+
import { join as join33 } from "node:path";
|
|
50692
49849
|
function getSettingsPath() {
|
|
50693
|
-
return
|
|
49850
|
+
return join33(claudeDir, settingsFilename);
|
|
50694
49851
|
}
|
|
50695
49852
|
async function readSettings() {
|
|
50696
49853
|
const settingsPath = getSettingsPath();
|
|
50697
49854
|
try {
|
|
50698
|
-
if (!
|
|
49855
|
+
if (!existsSync18(settingsPath))
|
|
50699
49856
|
return null;
|
|
50700
|
-
const content = await
|
|
49857
|
+
const content = await readFile14(settingsPath, "utf-8");
|
|
50701
49858
|
return JSON.parse(content);
|
|
50702
49859
|
} catch {
|
|
50703
49860
|
return null;
|
|
@@ -50710,9 +49867,9 @@ async function backupAndSaveSettings(settings) {
|
|
|
50710
49867
|
const settingsPath = getSettingsPath();
|
|
50711
49868
|
await mkdir9(claudeDir, { recursive: true });
|
|
50712
49869
|
let backupPath = null;
|
|
50713
|
-
if (
|
|
49870
|
+
if (existsSync18(settingsPath)) {
|
|
50714
49871
|
await mkdir9(settingsBackupDir, { recursive: true });
|
|
50715
|
-
backupPath =
|
|
49872
|
+
backupPath = join33(settingsBackupDir, `${getBackupTimestamp()}-${settingsFilename}`);
|
|
50716
49873
|
await copyFile2(settingsPath, backupPath);
|
|
50717
49874
|
}
|
|
50718
49875
|
const tempPath = `${settingsPath}.tmp-${Date.now()}`;
|
|
@@ -50749,13 +49906,13 @@ function countMcpServers(settings) {
|
|
|
50749
49906
|
}
|
|
50750
49907
|
var claudeDir, settingsFilename = "settings.json", settingsBackupDir;
|
|
50751
49908
|
var init_settings_reader = __esm(() => {
|
|
50752
|
-
claudeDir =
|
|
50753
|
-
settingsBackupDir =
|
|
49909
|
+
claudeDir = join33(homedir18(), ".claude");
|
|
49910
|
+
settingsBackupDir = join33(claudeDir, ".ck-backups", "settings");
|
|
50754
49911
|
});
|
|
50755
49912
|
|
|
50756
49913
|
// src/services/claude-data/hook-log-reader.ts
|
|
50757
49914
|
import { open as open4 } from "node:fs/promises";
|
|
50758
|
-
import { join as
|
|
49915
|
+
import { join as join34 } from "node:path";
|
|
50759
49916
|
function createEmptySummary() {
|
|
50760
49917
|
return {
|
|
50761
49918
|
total: 0,
|
|
@@ -50803,9 +49960,9 @@ async function resolveProjectPath(projectId) {
|
|
|
50803
49960
|
}
|
|
50804
49961
|
function getHookLogPath(scope, basePath) {
|
|
50805
49962
|
if (scope === "global") {
|
|
50806
|
-
return
|
|
49963
|
+
return join34(basePath, "hooks", ".logs", "hook-log.jsonl");
|
|
50807
49964
|
}
|
|
50808
|
-
return
|
|
49965
|
+
return join34(basePath, ".claude", "hooks", ".logs", "hook-log.jsonl");
|
|
50809
49966
|
}
|
|
50810
49967
|
function parseEntry(line) {
|
|
50811
49968
|
try {
|
|
@@ -50916,409 +50073,1252 @@ var init_hook_log_reader = __esm(() => {
|
|
|
50916
50073
|
};
|
|
50917
50074
|
});
|
|
50918
50075
|
|
|
50919
|
-
// src/services/claude-data/project-scanner.ts
|
|
50920
|
-
import { homedir as homedir19 } from "node:os";
|
|
50921
|
-
import { basename as basename10, join as
|
|
50922
|
-
function decodePath(encoded) {
|
|
50923
|
-
const decoded = encoded.replace(/^-/, "/").replace(/-/g, "/");
|
|
50924
|
-
const normalized = normalize4(decoded);
|
|
50925
|
-
if (normalized.includes("..")) {
|
|
50926
|
-
throw new Error(`Invalid path: contains traversal pattern - ${encoded}`);
|
|
50927
|
-
}
|
|
50928
|
-
return normalized;
|
|
50929
|
-
}
|
|
50930
|
-
function encodePath(path) {
|
|
50931
|
-
return path.replace(/\//g, "-").replace(/^-/, "-");
|
|
50932
|
-
}
|
|
50933
|
-
var projectsDir;
|
|
50934
|
-
var init_project_scanner = __esm(() => {
|
|
50935
|
-
projectsDir =
|
|
50936
|
-
});
|
|
50937
|
-
|
|
50938
|
-
// src/services/claude-data/project-discovery.ts
|
|
50939
|
-
import { constants, access } from "node:fs/promises";
|
|
50940
|
-
import { basename as basename11 } from "node:path";
|
|
50941
|
-
async function discoverProjectsFromHistory() {
|
|
50942
|
-
const result = await parseHistoryFile();
|
|
50943
|
-
return result.projects;
|
|
50944
|
-
}
|
|
50945
|
-
async function pathExists5(path) {
|
|
50946
|
-
try {
|
|
50947
|
-
await access(path, constants.F_OK);
|
|
50948
|
-
return true;
|
|
50949
|
-
} catch {
|
|
50950
|
-
return false;
|
|
50076
|
+
// src/services/claude-data/project-scanner.ts
|
|
50077
|
+
import { homedir as homedir19 } from "node:os";
|
|
50078
|
+
import { basename as basename10, join as join35, normalize as normalize4 } from "node:path";
|
|
50079
|
+
function decodePath(encoded) {
|
|
50080
|
+
const decoded = encoded.replace(/^-/, "/").replace(/-/g, "/");
|
|
50081
|
+
const normalized = normalize4(decoded);
|
|
50082
|
+
if (normalized.includes("..")) {
|
|
50083
|
+
throw new Error(`Invalid path: contains traversal pattern - ${encoded}`);
|
|
50084
|
+
}
|
|
50085
|
+
return normalized;
|
|
50086
|
+
}
|
|
50087
|
+
function encodePath(path) {
|
|
50088
|
+
return path.replace(/\//g, "-").replace(/^-/, "-");
|
|
50089
|
+
}
|
|
50090
|
+
var projectsDir;
|
|
50091
|
+
var init_project_scanner = __esm(() => {
|
|
50092
|
+
projectsDir = join35(homedir19(), ".claude", "projects");
|
|
50093
|
+
});
|
|
50094
|
+
|
|
50095
|
+
// src/services/claude-data/project-discovery.ts
|
|
50096
|
+
import { constants, access } from "node:fs/promises";
|
|
50097
|
+
import { basename as basename11 } from "node:path";
|
|
50098
|
+
async function discoverProjectsFromHistory() {
|
|
50099
|
+
const result = await parseHistoryFile();
|
|
50100
|
+
return result.projects;
|
|
50101
|
+
}
|
|
50102
|
+
async function pathExists5(path) {
|
|
50103
|
+
try {
|
|
50104
|
+
await access(path, constants.F_OK);
|
|
50105
|
+
return true;
|
|
50106
|
+
} catch {
|
|
50107
|
+
return false;
|
|
50108
|
+
}
|
|
50109
|
+
}
|
|
50110
|
+
async function mergeProjectDiscovery(sessionProjects, filterNonExistent = false, historyPath) {
|
|
50111
|
+
const start = Date.now();
|
|
50112
|
+
let error;
|
|
50113
|
+
try {
|
|
50114
|
+
const historyResult = await parseHistoryFile(historyPath);
|
|
50115
|
+
if (historyResult.error) {
|
|
50116
|
+
error = historyResult.error;
|
|
50117
|
+
}
|
|
50118
|
+
const historyMap = new Map;
|
|
50119
|
+
for (const p of historyResult.projects) {
|
|
50120
|
+
historyMap.set(p.path, p);
|
|
50121
|
+
}
|
|
50122
|
+
const projectMap = new Map;
|
|
50123
|
+
for (const sp of sessionProjects) {
|
|
50124
|
+
const existing = projectMap.get(sp.path);
|
|
50125
|
+
if (!existing) {
|
|
50126
|
+
projectMap.set(sp.path, {
|
|
50127
|
+
path: sp.path,
|
|
50128
|
+
name: basename11(sp.path),
|
|
50129
|
+
lastUsed: sp.lastUsed ?? null,
|
|
50130
|
+
source: "session",
|
|
50131
|
+
exists: true
|
|
50132
|
+
});
|
|
50133
|
+
}
|
|
50134
|
+
}
|
|
50135
|
+
for (const [path, hp] of historyMap) {
|
|
50136
|
+
const existing = projectMap.get(path);
|
|
50137
|
+
if (existing) {
|
|
50138
|
+
existing.source = "both";
|
|
50139
|
+
existing.interactionCount = hp.interactionCount;
|
|
50140
|
+
if (hp.lastUsed > (existing.lastUsed ?? 0)) {
|
|
50141
|
+
existing.lastUsed = hp.lastUsed;
|
|
50142
|
+
}
|
|
50143
|
+
} else {
|
|
50144
|
+
projectMap.set(path, {
|
|
50145
|
+
path,
|
|
50146
|
+
name: basename11(path),
|
|
50147
|
+
lastUsed: hp.lastUsed,
|
|
50148
|
+
source: "history",
|
|
50149
|
+
exists: false,
|
|
50150
|
+
interactionCount: hp.interactionCount
|
|
50151
|
+
});
|
|
50152
|
+
}
|
|
50153
|
+
}
|
|
50154
|
+
const projects = Array.from(projectMap.values());
|
|
50155
|
+
await Promise.all(projects.filter((p) => p.source === "history").map(async (p) => {
|
|
50156
|
+
p.exists = await pathExists5(p.path);
|
|
50157
|
+
}));
|
|
50158
|
+
let result = projects;
|
|
50159
|
+
if (filterNonExistent) {
|
|
50160
|
+
result = result.filter((p) => p.exists);
|
|
50161
|
+
}
|
|
50162
|
+
result.sort((a, b) => (b.lastUsed ?? 0) - (a.lastUsed ?? 0));
|
|
50163
|
+
return {
|
|
50164
|
+
projects: result,
|
|
50165
|
+
totalFromSessions: sessionProjects.length,
|
|
50166
|
+
totalFromHistory: historyResult.projects.length,
|
|
50167
|
+
parseTimeMs: Date.now() - start,
|
|
50168
|
+
error
|
|
50169
|
+
};
|
|
50170
|
+
} catch (err) {
|
|
50171
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
50172
|
+
return {
|
|
50173
|
+
projects: [],
|
|
50174
|
+
totalFromSessions: sessionProjects.length,
|
|
50175
|
+
totalFromHistory: 0,
|
|
50176
|
+
parseTimeMs: Date.now() - start,
|
|
50177
|
+
error: `Failed to merge project discovery: ${msg}`
|
|
50178
|
+
};
|
|
50179
|
+
}
|
|
50180
|
+
}
|
|
50181
|
+
var init_project_discovery = __esm(() => {
|
|
50182
|
+
init_history_parser();
|
|
50183
|
+
});
|
|
50184
|
+
|
|
50185
|
+
// src/services/claude-data/user-preferences.ts
|
|
50186
|
+
import { readFile as readFile15, stat as stat7 } from "node:fs/promises";
|
|
50187
|
+
import { homedir as homedir20 } from "node:os";
|
|
50188
|
+
import { join as join36 } from "node:path";
|
|
50189
|
+
function getPreferencesPath() {
|
|
50190
|
+
return join36(homedir20(), ".claude.json");
|
|
50191
|
+
}
|
|
50192
|
+
async function readUserPreferences(filePath) {
|
|
50193
|
+
const path = filePath ?? getPreferencesPath();
|
|
50194
|
+
const existingLock = locks2.get(path);
|
|
50195
|
+
if (existingLock)
|
|
50196
|
+
return existingLock;
|
|
50197
|
+
const start = Date.now();
|
|
50198
|
+
try {
|
|
50199
|
+
const { mtimeMs } = await stat7(path);
|
|
50200
|
+
const cached = cache2.get(path);
|
|
50201
|
+
if (cached && cached.mtime === mtimeMs) {
|
|
50202
|
+
return cached.result;
|
|
50203
|
+
}
|
|
50204
|
+
} catch {
|
|
50205
|
+
return emptyResult2(Date.now() - start, "Cannot read user preferences - file not found");
|
|
50206
|
+
}
|
|
50207
|
+
const parsePromise = (async () => {
|
|
50208
|
+
try {
|
|
50209
|
+
try {
|
|
50210
|
+
const content = await readFile15(path, "utf8");
|
|
50211
|
+
const raw = JSON.parse(content);
|
|
50212
|
+
const result = {
|
|
50213
|
+
preferences: UserPreferencesSchema.parse(raw),
|
|
50214
|
+
tipsHistory: TipsHistorySchema.parse(raw.tipsHistory ?? {}),
|
|
50215
|
+
featureFlags: {
|
|
50216
|
+
statsigGates: raw.cachedStatsigGates ?? {},
|
|
50217
|
+
growthBookFeatures: raw.cachedGrowthBookFeatures ?? {}
|
|
50218
|
+
},
|
|
50219
|
+
rawSize: Buffer.byteLength(content),
|
|
50220
|
+
parseTimeMs: Date.now() - start
|
|
50221
|
+
};
|
|
50222
|
+
const { mtimeMs } = await stat7(path);
|
|
50223
|
+
cache2.set(path, { mtime: mtimeMs, result });
|
|
50224
|
+
return result;
|
|
50225
|
+
} catch (err) {
|
|
50226
|
+
const errorMsg = err instanceof Error ? `Failed to parse user preferences: ${err.message}` : "Failed to parse user preferences";
|
|
50227
|
+
return emptyResult2(Date.now() - start, errorMsg);
|
|
50228
|
+
}
|
|
50229
|
+
} finally {
|
|
50230
|
+
locks2.delete(path);
|
|
50231
|
+
}
|
|
50232
|
+
})();
|
|
50233
|
+
locks2.set(path, parsePromise);
|
|
50234
|
+
return parsePromise;
|
|
50235
|
+
}
|
|
50236
|
+
function emptyResult2(parseTimeMs, error) {
|
|
50237
|
+
return {
|
|
50238
|
+
preferences: {},
|
|
50239
|
+
tipsHistory: {},
|
|
50240
|
+
featureFlags: { statsigGates: {}, growthBookFeatures: {} },
|
|
50241
|
+
rawSize: 0,
|
|
50242
|
+
parseTimeMs,
|
|
50243
|
+
error
|
|
50244
|
+
};
|
|
50245
|
+
}
|
|
50246
|
+
function getEffectiveTheme(prefs) {
|
|
50247
|
+
return prefs.preferences.theme ?? "dark";
|
|
50248
|
+
}
|
|
50249
|
+
function getUsageSummary(prefs) {
|
|
50250
|
+
const tipsCounts = Object.values(prefs.tipsHistory);
|
|
50251
|
+
return {
|
|
50252
|
+
totalSessions: prefs.preferences.numStartups ?? 0,
|
|
50253
|
+
promptQueueUsage: prefs.preferences.promptQueueUseCount ?? 0,
|
|
50254
|
+
firstUsed: prefs.preferences.firstStartTime ? new Date(prefs.preferences.firstStartTime) : null,
|
|
50255
|
+
tipsShown: tipsCounts.reduce((a, b) => a + b, 0)
|
|
50256
|
+
};
|
|
50257
|
+
}
|
|
50258
|
+
var cache2, locks2;
|
|
50259
|
+
var init_user_preferences = __esm(() => {
|
|
50260
|
+
init_types5();
|
|
50261
|
+
cache2 = new Map;
|
|
50262
|
+
locks2 = new Map;
|
|
50263
|
+
});
|
|
50264
|
+
|
|
50265
|
+
// src/services/claude-data/index.ts
|
|
50266
|
+
var init_claude_data = __esm(() => {
|
|
50267
|
+
init_types5();
|
|
50268
|
+
init_history_parser();
|
|
50269
|
+
init_session_parser();
|
|
50270
|
+
init_skill_scanner();
|
|
50271
|
+
init_settings_reader();
|
|
50272
|
+
init_hook_log_reader();
|
|
50273
|
+
init_project_scanner();
|
|
50274
|
+
init_project_discovery();
|
|
50275
|
+
init_user_preferences();
|
|
50276
|
+
});
|
|
50277
|
+
|
|
50278
|
+
// src/domains/web-server/routes/dashboard-routes.ts
|
|
50279
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
50280
|
+
import { readFile as readFile16, readdir as readdir11 } from "node:fs/promises";
|
|
50281
|
+
import { homedir as homedir21 } from "node:os";
|
|
50282
|
+
import { join as join37 } from "node:path";
|
|
50283
|
+
async function readAgents() {
|
|
50284
|
+
const agentsDir = join37(claudeDir2, "agents");
|
|
50285
|
+
if (!existsSync19(agentsDir))
|
|
50286
|
+
return [];
|
|
50287
|
+
try {
|
|
50288
|
+
const files = await readdir11(agentsDir);
|
|
50289
|
+
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
50290
|
+
const agents = await Promise.all(mdFiles.map(async (file) => {
|
|
50291
|
+
try {
|
|
50292
|
+
const { frontmatter: fm } = await parseFrontmatterFile(join37(agentsDir, file));
|
|
50293
|
+
return {
|
|
50294
|
+
name: fm.name || file.replace(/\.md$/, ""),
|
|
50295
|
+
model: fm.model || "unset",
|
|
50296
|
+
description: fm.description || "",
|
|
50297
|
+
color: fm.color
|
|
50298
|
+
};
|
|
50299
|
+
} catch {
|
|
50300
|
+
return null;
|
|
50301
|
+
}
|
|
50302
|
+
}));
|
|
50303
|
+
return agents.filter((a) => a !== null);
|
|
50304
|
+
} catch {
|
|
50305
|
+
return [];
|
|
50306
|
+
}
|
|
50307
|
+
}
|
|
50308
|
+
async function countMdFilesRecursive(dir, depth = 0) {
|
|
50309
|
+
if (!existsSync19(dir) || depth > 10)
|
|
50310
|
+
return 0;
|
|
50311
|
+
try {
|
|
50312
|
+
const entries = await readdir11(dir, { withFileTypes: true });
|
|
50313
|
+
const counts = await Promise.all(entries.map(async (entry) => {
|
|
50314
|
+
if (entry.isDirectory()) {
|
|
50315
|
+
return countMdFilesRecursive(join37(dir, entry.name), depth + 1);
|
|
50316
|
+
}
|
|
50317
|
+
return entry.name.endsWith(".md") ? 1 : 0;
|
|
50318
|
+
}));
|
|
50319
|
+
return counts.reduce((sum, c) => sum + c, 0);
|
|
50320
|
+
} catch {
|
|
50321
|
+
return 0;
|
|
50322
|
+
}
|
|
50323
|
+
}
|
|
50324
|
+
async function countSkills2() {
|
|
50325
|
+
const skillsDir = join37(claudeDir2, "skills");
|
|
50326
|
+
if (!existsSync19(skillsDir))
|
|
50327
|
+
return 0;
|
|
50328
|
+
try {
|
|
50329
|
+
const entries = await readdir11(skillsDir);
|
|
50330
|
+
let count = 0;
|
|
50331
|
+
for (const entry of entries) {
|
|
50332
|
+
if (existsSync19(join37(skillsDir, entry, "SKILL.md")))
|
|
50333
|
+
count++;
|
|
50334
|
+
}
|
|
50335
|
+
return count;
|
|
50336
|
+
} catch {
|
|
50337
|
+
return 0;
|
|
50338
|
+
}
|
|
50339
|
+
}
|
|
50340
|
+
async function countAllMcpServers() {
|
|
50341
|
+
const settings = await readSettings();
|
|
50342
|
+
const baseCount = settings ? countMcpServers(settings) : 0;
|
|
50343
|
+
const seen = new Set;
|
|
50344
|
+
if (settings?.mcpServers && typeof settings.mcpServers === "object") {
|
|
50345
|
+
for (const name of Object.keys(settings.mcpServers)) {
|
|
50346
|
+
seen.add(name);
|
|
50347
|
+
}
|
|
50348
|
+
}
|
|
50349
|
+
let claudeJsonCount = 0;
|
|
50350
|
+
try {
|
|
50351
|
+
const claudeJsonPath = join37(homedir21(), ".claude.json");
|
|
50352
|
+
if (existsSync19(claudeJsonPath)) {
|
|
50353
|
+
const content = await readFile16(claudeJsonPath, "utf-8");
|
|
50354
|
+
const data = JSON.parse(content);
|
|
50355
|
+
if (data.mcpServers && typeof data.mcpServers === "object") {
|
|
50356
|
+
for (const name of Object.keys(data.mcpServers)) {
|
|
50357
|
+
if (!seen.has(name)) {
|
|
50358
|
+
seen.add(name);
|
|
50359
|
+
claudeJsonCount++;
|
|
50360
|
+
}
|
|
50361
|
+
}
|
|
50362
|
+
}
|
|
50363
|
+
}
|
|
50364
|
+
} catch {}
|
|
50365
|
+
let mcpJsonCount = 0;
|
|
50366
|
+
const mcpJsonPath = join37(claudeDir2, ".mcp.json");
|
|
50367
|
+
try {
|
|
50368
|
+
if (existsSync19(mcpJsonPath)) {
|
|
50369
|
+
const content = await readFile16(mcpJsonPath, "utf-8");
|
|
50370
|
+
const data = JSON.parse(content);
|
|
50371
|
+
if (data.mcpServers && typeof data.mcpServers === "object") {
|
|
50372
|
+
for (const name of Object.keys(data.mcpServers)) {
|
|
50373
|
+
if (!seen.has(name)) {
|
|
50374
|
+
seen.add(name);
|
|
50375
|
+
mcpJsonCount++;
|
|
50376
|
+
}
|
|
50377
|
+
}
|
|
50378
|
+
}
|
|
50379
|
+
}
|
|
50380
|
+
} catch {}
|
|
50381
|
+
return baseCount + claudeJsonCount + mcpJsonCount;
|
|
50382
|
+
}
|
|
50383
|
+
function classifyModel(model) {
|
|
50384
|
+
const lower = model.toLowerCase();
|
|
50385
|
+
if (lower === "unset" || !lower)
|
|
50386
|
+
return "unset";
|
|
50387
|
+
if (lower.includes("opus"))
|
|
50388
|
+
return "opus";
|
|
50389
|
+
if (lower.includes("haiku"))
|
|
50390
|
+
return "haiku";
|
|
50391
|
+
if (lower.includes("sonnet"))
|
|
50392
|
+
return "sonnet";
|
|
50393
|
+
return "unset";
|
|
50394
|
+
}
|
|
50395
|
+
function registerDashboardRoutes(app) {
|
|
50396
|
+
app.get("/api/dashboard/stats", async (_req, res) => {
|
|
50397
|
+
try {
|
|
50398
|
+
const [agents, commandCount, skillCount, mcpCount] = await Promise.all([
|
|
50399
|
+
readAgents(),
|
|
50400
|
+
countMdFilesRecursive(join37(claudeDir2, "commands")),
|
|
50401
|
+
countSkills2(),
|
|
50402
|
+
countAllMcpServers()
|
|
50403
|
+
]);
|
|
50404
|
+
const modelDistribution = { opus: 0, sonnet: 0, haiku: 0, unset: 0 };
|
|
50405
|
+
for (const agent of agents) {
|
|
50406
|
+
const tier = classifyModel(agent.model);
|
|
50407
|
+
modelDistribution[tier]++;
|
|
50408
|
+
}
|
|
50409
|
+
res.json({
|
|
50410
|
+
agents: agents.length,
|
|
50411
|
+
commands: commandCount,
|
|
50412
|
+
skills: skillCount,
|
|
50413
|
+
mcpServers: mcpCount,
|
|
50414
|
+
modelDistribution
|
|
50415
|
+
});
|
|
50416
|
+
} catch (error) {
|
|
50417
|
+
res.status(500).json({ error: "Failed to load dashboard stats" });
|
|
50418
|
+
}
|
|
50419
|
+
});
|
|
50420
|
+
app.get("/api/agents/list", async (_req, res) => {
|
|
50421
|
+
try {
|
|
50422
|
+
const agents = await readAgents();
|
|
50423
|
+
res.json({ agents: agents.slice(0, 6) });
|
|
50424
|
+
} catch (error) {
|
|
50425
|
+
res.status(500).json({ error: "Failed to load agents" });
|
|
50426
|
+
}
|
|
50427
|
+
});
|
|
50428
|
+
app.get("/api/suggestions", async (_req, res) => {
|
|
50429
|
+
try {
|
|
50430
|
+
const agents = await readAgents();
|
|
50431
|
+
const suggestions = [];
|
|
50432
|
+
const unsetCount = agents.filter((a) => classifyModel(a.model) === "unset").length;
|
|
50433
|
+
if (unsetCount > 0) {
|
|
50434
|
+
suggestions.push({
|
|
50435
|
+
type: "warning",
|
|
50436
|
+
message: `${unsetCount} agent${unsetCount > 1 ? "s" : ""} have no model set`,
|
|
50437
|
+
target: "agents"
|
|
50438
|
+
});
|
|
50439
|
+
}
|
|
50440
|
+
const skillCount = await countSkills2();
|
|
50441
|
+
if (skillCount === 0) {
|
|
50442
|
+
suggestions.push({
|
|
50443
|
+
type: "info",
|
|
50444
|
+
message: "No skills installed — browse the Skills Marketplace",
|
|
50445
|
+
target: "skills"
|
|
50446
|
+
});
|
|
50447
|
+
}
|
|
50448
|
+
const settings = await readSettings();
|
|
50449
|
+
if (!settings) {
|
|
50450
|
+
suggestions.push({
|
|
50451
|
+
type: "warning",
|
|
50452
|
+
message: "No ~/.claude/settings.json found — run gck init to create one"
|
|
50453
|
+
});
|
|
50454
|
+
}
|
|
50455
|
+
if (agents.length === 0) {
|
|
50456
|
+
suggestions.push({
|
|
50457
|
+
type: "info",
|
|
50458
|
+
message: "No agents configured in ~/.claude/agents/",
|
|
50459
|
+
target: "agents"
|
|
50460
|
+
});
|
|
50461
|
+
}
|
|
50462
|
+
if (suggestions.length === 0) {
|
|
50463
|
+
suggestions.push({
|
|
50464
|
+
type: "success",
|
|
50465
|
+
message: "Everything looks good!"
|
|
50466
|
+
});
|
|
50467
|
+
}
|
|
50468
|
+
res.json({ suggestions });
|
|
50469
|
+
} catch (error) {
|
|
50470
|
+
res.status(500).json({ error: "Failed to load suggestions" });
|
|
50471
|
+
}
|
|
50472
|
+
});
|
|
50473
|
+
}
|
|
50474
|
+
var claudeDir2;
|
|
50475
|
+
var init_dashboard_routes = __esm(() => {
|
|
50476
|
+
init_frontmatter_parser();
|
|
50477
|
+
init_claude_data();
|
|
50478
|
+
claudeDir2 = join37(homedir21(), ".claude");
|
|
50479
|
+
});
|
|
50480
|
+
|
|
50481
|
+
// src/schemas/gck-config.schema.json
|
|
50482
|
+
var gck_config_schema_default;
|
|
50483
|
+
var init_gck_config_schema = __esm(() => {
|
|
50484
|
+
gck_config_schema_default = {
|
|
50485
|
+
$schema: "https://json-schema.org/draft-07/schema#",
|
|
50486
|
+
$id: "https://claudekit.cc/schemas/gck-config.schema.json",
|
|
50487
|
+
title: "ClaudeKit Configuration",
|
|
50488
|
+
description: "Configuration schema for ClaudeKit Engineer .ck.json files",
|
|
50489
|
+
type: "object",
|
|
50490
|
+
properties: {
|
|
50491
|
+
$schema: {
|
|
50492
|
+
type: "string",
|
|
50493
|
+
description: "JSON Schema reference for editor autocompletion"
|
|
50494
|
+
},
|
|
50495
|
+
codingLevel: {
|
|
50496
|
+
type: "integer",
|
|
50497
|
+
minimum: -1,
|
|
50498
|
+
maximum: 5,
|
|
50499
|
+
default: -1,
|
|
50500
|
+
description: "User coding experience level: -1=auto, 0=beginner, 1=intermediate, 2=advanced, 3=expert, 4=tech-lead, 5=god-mode"
|
|
50501
|
+
},
|
|
50502
|
+
privacyBlock: {
|
|
50503
|
+
type: "boolean",
|
|
50504
|
+
default: true,
|
|
50505
|
+
description: "Enable privacy block hook to protect sensitive files (.env, credentials, etc.)"
|
|
50506
|
+
},
|
|
50507
|
+
docs: {
|
|
50508
|
+
type: "object",
|
|
50509
|
+
description: "Documentation settings",
|
|
50510
|
+
properties: {
|
|
50511
|
+
maxLoc: {
|
|
50512
|
+
type: "integer",
|
|
50513
|
+
minimum: 100,
|
|
50514
|
+
maximum: 5000,
|
|
50515
|
+
default: 800,
|
|
50516
|
+
description: "Maximum lines of code per documentation file"
|
|
50517
|
+
}
|
|
50518
|
+
},
|
|
50519
|
+
additionalProperties: false
|
|
50520
|
+
},
|
|
50521
|
+
plan: {
|
|
50522
|
+
type: "object",
|
|
50523
|
+
description: "Plan naming and validation settings",
|
|
50524
|
+
properties: {
|
|
50525
|
+
namingFormat: {
|
|
50526
|
+
type: "string",
|
|
50527
|
+
default: "{date}-{issue}-{slug}",
|
|
50528
|
+
description: "Format for plan directory names. Variables: {date}, {issue}, {slug}"
|
|
50529
|
+
},
|
|
50530
|
+
dateFormat: {
|
|
50531
|
+
type: "string",
|
|
50532
|
+
default: "YYMMDD-HHmm",
|
|
50533
|
+
description: "Date format for plan names (moment.js format)"
|
|
50534
|
+
},
|
|
50535
|
+
issuePrefix: {
|
|
50536
|
+
type: "string",
|
|
50537
|
+
default: "GH-",
|
|
50538
|
+
description: "Prefix for issue references (e.g., GH-, JIRA-)"
|
|
50539
|
+
},
|
|
50540
|
+
reportsDir: {
|
|
50541
|
+
type: "string",
|
|
50542
|
+
default: "reports",
|
|
50543
|
+
description: "Subdirectory name for reports within plans"
|
|
50544
|
+
},
|
|
50545
|
+
resolution: {
|
|
50546
|
+
type: "object",
|
|
50547
|
+
description: "Plan resolution settings",
|
|
50548
|
+
properties: {
|
|
50549
|
+
order: {
|
|
50550
|
+
type: "array",
|
|
50551
|
+
items: {
|
|
50552
|
+
type: "string",
|
|
50553
|
+
enum: ["session", "branch", "directory"]
|
|
50554
|
+
},
|
|
50555
|
+
default: ["session", "branch"],
|
|
50556
|
+
description: "Order of resolution strategies for plan context"
|
|
50557
|
+
},
|
|
50558
|
+
branchPattern: {
|
|
50559
|
+
type: "string",
|
|
50560
|
+
default: "(?:feat|fix|chore|refactor|docs)/(?:[^/]+/)?(.+)",
|
|
50561
|
+
description: "Regex pattern to extract plan slug from branch name"
|
|
50562
|
+
}
|
|
50563
|
+
},
|
|
50564
|
+
additionalProperties: false
|
|
50565
|
+
},
|
|
50566
|
+
validation: {
|
|
50567
|
+
type: "object",
|
|
50568
|
+
description: "Plan validation settings",
|
|
50569
|
+
properties: {
|
|
50570
|
+
mode: {
|
|
50571
|
+
type: "string",
|
|
50572
|
+
enum: ["prompt", "auto", "strict", "none"],
|
|
50573
|
+
default: "prompt",
|
|
50574
|
+
description: "Validation mode: prompt=ask questions, auto=auto-validate, strict=require all, none=skip"
|
|
50575
|
+
},
|
|
50576
|
+
minQuestions: {
|
|
50577
|
+
type: "integer",
|
|
50578
|
+
minimum: 0,
|
|
50579
|
+
maximum: 20,
|
|
50580
|
+
default: 3,
|
|
50581
|
+
description: "Minimum validation questions to ask"
|
|
50582
|
+
},
|
|
50583
|
+
maxQuestions: {
|
|
50584
|
+
type: "integer",
|
|
50585
|
+
minimum: 1,
|
|
50586
|
+
maximum: 20,
|
|
50587
|
+
default: 8,
|
|
50588
|
+
description: "Maximum validation questions to ask"
|
|
50589
|
+
},
|
|
50590
|
+
focusAreas: {
|
|
50591
|
+
type: "array",
|
|
50592
|
+
items: {
|
|
50593
|
+
type: "string",
|
|
50594
|
+
enum: [
|
|
50595
|
+
"assumptions",
|
|
50596
|
+
"risks",
|
|
50597
|
+
"tradeoffs",
|
|
50598
|
+
"architecture",
|
|
50599
|
+
"security",
|
|
50600
|
+
"performance",
|
|
50601
|
+
"testing",
|
|
50602
|
+
"dependencies"
|
|
50603
|
+
]
|
|
50604
|
+
},
|
|
50605
|
+
default: ["assumptions", "risks", "tradeoffs", "architecture"],
|
|
50606
|
+
description: "Focus areas for validation questions"
|
|
50607
|
+
}
|
|
50608
|
+
},
|
|
50609
|
+
additionalProperties: false
|
|
50610
|
+
}
|
|
50611
|
+
},
|
|
50612
|
+
additionalProperties: false
|
|
50613
|
+
},
|
|
50614
|
+
paths: {
|
|
50615
|
+
type: "object",
|
|
50616
|
+
description: "Directory paths configuration",
|
|
50617
|
+
properties: {
|
|
50618
|
+
docs: {
|
|
50619
|
+
type: "string",
|
|
50620
|
+
default: "docs",
|
|
50621
|
+
description: "Path to documentation directory"
|
|
50622
|
+
},
|
|
50623
|
+
plans: {
|
|
50624
|
+
type: "string",
|
|
50625
|
+
default: "plans",
|
|
50626
|
+
description: "Path to plans directory"
|
|
50627
|
+
},
|
|
50628
|
+
globalPlans: {
|
|
50629
|
+
type: "string",
|
|
50630
|
+
default: "plans",
|
|
50631
|
+
description: "Path to global plans directory (relative to ~/.claude or absolute)"
|
|
50632
|
+
}
|
|
50633
|
+
},
|
|
50634
|
+
additionalProperties: false
|
|
50635
|
+
},
|
|
50636
|
+
locale: {
|
|
50637
|
+
type: "object",
|
|
50638
|
+
description: "Language and locale settings",
|
|
50639
|
+
properties: {
|
|
50640
|
+
thinkingLanguage: {
|
|
50641
|
+
type: ["string", "null"],
|
|
50642
|
+
default: null,
|
|
50643
|
+
description: "Language for Claude's thinking (null=English)"
|
|
50644
|
+
},
|
|
50645
|
+
responseLanguage: {
|
|
50646
|
+
type: ["string", "null"],
|
|
50647
|
+
default: null,
|
|
50648
|
+
description: "Language for Claude's responses (null=match user)"
|
|
50649
|
+
}
|
|
50650
|
+
},
|
|
50651
|
+
additionalProperties: false
|
|
50652
|
+
},
|
|
50653
|
+
trust: {
|
|
50654
|
+
type: "object",
|
|
50655
|
+
description: "Trust mode settings for auto-approval",
|
|
50656
|
+
properties: {
|
|
50657
|
+
passphrase: {
|
|
50658
|
+
type: ["string", "null"],
|
|
50659
|
+
default: null,
|
|
50660
|
+
description: "Passphrase to enable trust mode (null=disabled)"
|
|
50661
|
+
},
|
|
50662
|
+
enabled: {
|
|
50663
|
+
type: "boolean",
|
|
50664
|
+
default: false,
|
|
50665
|
+
description: "Whether trust mode is currently enabled"
|
|
50666
|
+
}
|
|
50667
|
+
},
|
|
50668
|
+
additionalProperties: false
|
|
50669
|
+
},
|
|
50670
|
+
project: {
|
|
50671
|
+
type: "object",
|
|
50672
|
+
description: "Project type detection settings",
|
|
50673
|
+
properties: {
|
|
50674
|
+
type: {
|
|
50675
|
+
type: "string",
|
|
50676
|
+
enum: ["auto", "library", "application", "monorepo", "cli", "api", "web", "mobile"],
|
|
50677
|
+
default: "auto",
|
|
50678
|
+
description: "Project type (auto=detect from package.json)"
|
|
50679
|
+
},
|
|
50680
|
+
packageManager: {
|
|
50681
|
+
type: "string",
|
|
50682
|
+
enum: ["auto", "npm", "yarn", "pnpm", "bun"],
|
|
50683
|
+
default: "auto",
|
|
50684
|
+
description: "Package manager (auto=detect from lockfile)"
|
|
50685
|
+
},
|
|
50686
|
+
framework: {
|
|
50687
|
+
type: "string",
|
|
50688
|
+
enum: [
|
|
50689
|
+
"auto",
|
|
50690
|
+
"react",
|
|
50691
|
+
"vue",
|
|
50692
|
+
"angular",
|
|
50693
|
+
"svelte",
|
|
50694
|
+
"nextjs",
|
|
50695
|
+
"nuxt",
|
|
50696
|
+
"express",
|
|
50697
|
+
"nestjs",
|
|
50698
|
+
"fastify",
|
|
50699
|
+
"none"
|
|
50700
|
+
],
|
|
50701
|
+
default: "auto",
|
|
50702
|
+
description: "Framework (auto=detect from dependencies)"
|
|
50703
|
+
}
|
|
50704
|
+
},
|
|
50705
|
+
additionalProperties: false
|
|
50706
|
+
},
|
|
50707
|
+
gemini: {
|
|
50708
|
+
type: "object",
|
|
50709
|
+
description: "Gemini CLI configuration",
|
|
50710
|
+
properties: {
|
|
50711
|
+
model: {
|
|
50712
|
+
type: "string",
|
|
50713
|
+
enum: [
|
|
50714
|
+
"gemini-2.5-flash",
|
|
50715
|
+
"gemini-2.5-pro",
|
|
50716
|
+
"gemini-3-pro-preview",
|
|
50717
|
+
"gemini-3-flash-preview"
|
|
50718
|
+
],
|
|
50719
|
+
default: "gemini-3-flash-preview",
|
|
50720
|
+
description: "Gemini model to use for CLI commands. Legacy gemini-3.0-* ids are auto-mapped to the current preview aliases when loading config."
|
|
50721
|
+
}
|
|
50722
|
+
},
|
|
50723
|
+
additionalProperties: false
|
|
50724
|
+
},
|
|
50725
|
+
skills: {
|
|
50726
|
+
type: "object",
|
|
50727
|
+
description: "Per-skill configuration",
|
|
50728
|
+
properties: {
|
|
50729
|
+
research: {
|
|
50730
|
+
type: "object",
|
|
50731
|
+
properties: {
|
|
50732
|
+
useGemini: {
|
|
50733
|
+
type: "boolean",
|
|
50734
|
+
default: true,
|
|
50735
|
+
description: "Use Gemini CLI for research (false=use WebSearch)"
|
|
50736
|
+
}
|
|
50737
|
+
},
|
|
50738
|
+
additionalProperties: true
|
|
50739
|
+
}
|
|
50740
|
+
},
|
|
50741
|
+
additionalProperties: {
|
|
50742
|
+
type: "object",
|
|
50743
|
+
description: "Custom skill configuration"
|
|
50744
|
+
}
|
|
50745
|
+
},
|
|
50746
|
+
assertions: {
|
|
50747
|
+
type: "array",
|
|
50748
|
+
items: {
|
|
50749
|
+
type: "object",
|
|
50750
|
+
properties: {
|
|
50751
|
+
pattern: {
|
|
50752
|
+
type: "string",
|
|
50753
|
+
description: "Glob pattern or regex for files to match"
|
|
50754
|
+
},
|
|
50755
|
+
rule: {
|
|
50756
|
+
type: "string",
|
|
50757
|
+
description: "Assertion rule to enforce"
|
|
50758
|
+
},
|
|
50759
|
+
message: {
|
|
50760
|
+
type: "string",
|
|
50761
|
+
description: "Error message when assertion fails"
|
|
50762
|
+
}
|
|
50763
|
+
}
|
|
50764
|
+
},
|
|
50765
|
+
default: [],
|
|
50766
|
+
description: "Code assertions and rules to enforce"
|
|
50767
|
+
},
|
|
50768
|
+
statusline: {
|
|
50769
|
+
type: "string",
|
|
50770
|
+
enum: ["full", "compact", "minimal", "none"],
|
|
50771
|
+
default: "full",
|
|
50772
|
+
description: "Statusline display mode: full=all features, compact=2-3 lines, minimal=single text line, none=disabled"
|
|
50773
|
+
},
|
|
50774
|
+
statuslineColors: {
|
|
50775
|
+
type: "boolean",
|
|
50776
|
+
default: true,
|
|
50777
|
+
description: "Enable ANSI colors in statusline output. Set false for plain text. Respects NO_COLOR/FORCE_COLOR env vars."
|
|
50778
|
+
},
|
|
50779
|
+
statuslineQuota: {
|
|
50780
|
+
type: "boolean",
|
|
50781
|
+
default: true,
|
|
50782
|
+
description: "Show the cosmetic 5h / wk quota chips in the statusline. Disable to keep the statusline layout without usage-window percentages."
|
|
50783
|
+
},
|
|
50784
|
+
statuslineLayout: {
|
|
50785
|
+
type: "object",
|
|
50786
|
+
description: "Advanced statusline layout config for the visual builder. When absent, uses hardcoded defaults (backward compatible). Takes precedence over the statusline field when both are set.",
|
|
50787
|
+
properties: {
|
|
50788
|
+
baseMode: {
|
|
50789
|
+
type: "string",
|
|
50790
|
+
enum: ["full", "compact", "minimal", "none"],
|
|
50791
|
+
default: "full",
|
|
50792
|
+
description: "Starting template mode for the layout"
|
|
50793
|
+
},
|
|
50794
|
+
lines: {
|
|
50795
|
+
type: "array",
|
|
50796
|
+
description: "Layout lines. Each inner array is one terminal row containing section IDs in left-to-right order. Sections not listed in any line are hidden.",
|
|
50797
|
+
items: {
|
|
50798
|
+
type: "array",
|
|
50799
|
+
items: {
|
|
50800
|
+
type: "string",
|
|
50801
|
+
enum: [
|
|
50802
|
+
"model",
|
|
50803
|
+
"context",
|
|
50804
|
+
"quota",
|
|
50805
|
+
"directory",
|
|
50806
|
+
"git",
|
|
50807
|
+
"cost",
|
|
50808
|
+
"changes",
|
|
50809
|
+
"agents",
|
|
50810
|
+
"todos"
|
|
50811
|
+
]
|
|
50812
|
+
}
|
|
50813
|
+
},
|
|
50814
|
+
maxItems: 10
|
|
50815
|
+
},
|
|
50816
|
+
sectionConfig: {
|
|
50817
|
+
type: "object",
|
|
50818
|
+
description: "Per-section visual customization keyed by section ID.",
|
|
50819
|
+
additionalProperties: {
|
|
50820
|
+
type: "object",
|
|
50821
|
+
properties: {
|
|
50822
|
+
icon: {
|
|
50823
|
+
type: "string",
|
|
50824
|
+
description: "Custom emoji/icon override",
|
|
50825
|
+
maxLength: 20
|
|
50826
|
+
},
|
|
50827
|
+
label: {
|
|
50828
|
+
type: "string",
|
|
50829
|
+
description: "Custom label override",
|
|
50830
|
+
maxLength: 50
|
|
50831
|
+
},
|
|
50832
|
+
color: {
|
|
50833
|
+
type: "string",
|
|
50834
|
+
description: "ANSI named color (e.g. red, cyan, green). Hex codes not supported.",
|
|
50835
|
+
maxLength: 30,
|
|
50836
|
+
pattern: "^[a-zA-Z]+$"
|
|
50837
|
+
},
|
|
50838
|
+
maxWidth: {
|
|
50839
|
+
type: "integer",
|
|
50840
|
+
minimum: 10,
|
|
50841
|
+
description: "Maximum character width for this section",
|
|
50842
|
+
maximum: 500
|
|
50843
|
+
}
|
|
50844
|
+
},
|
|
50845
|
+
additionalProperties: false
|
|
50846
|
+
}
|
|
50847
|
+
},
|
|
50848
|
+
theme: {
|
|
50849
|
+
type: "object",
|
|
50850
|
+
description: "Color theme overrides",
|
|
50851
|
+
properties: {
|
|
50852
|
+
name: {
|
|
50853
|
+
type: "string",
|
|
50854
|
+
description: "Theme name (informational)",
|
|
50855
|
+
maxLength: 50
|
|
50856
|
+
},
|
|
50857
|
+
contextLow: {
|
|
50858
|
+
type: "string",
|
|
50859
|
+
default: "green",
|
|
50860
|
+
description: "Context bar color when usage <50%",
|
|
50861
|
+
maxLength: 30,
|
|
50862
|
+
pattern: "^[a-zA-Z]+$"
|
|
50863
|
+
},
|
|
50864
|
+
contextMid: {
|
|
50865
|
+
type: "string",
|
|
50866
|
+
default: "yellow",
|
|
50867
|
+
description: "Context bar color when usage 50-75%",
|
|
50868
|
+
maxLength: 30,
|
|
50869
|
+
pattern: "^[a-zA-Z]+$"
|
|
50870
|
+
},
|
|
50871
|
+
contextHigh: {
|
|
50872
|
+
type: "string",
|
|
50873
|
+
default: "red",
|
|
50874
|
+
description: "Context bar color when usage >75%",
|
|
50875
|
+
maxLength: 30,
|
|
50876
|
+
pattern: "^[a-zA-Z]+$"
|
|
50877
|
+
},
|
|
50878
|
+
accent: {
|
|
50879
|
+
type: "string",
|
|
50880
|
+
default: "cyan",
|
|
50881
|
+
description: "Accent color for highlights",
|
|
50882
|
+
maxLength: 30,
|
|
50883
|
+
pattern: "^[a-zA-Z]+$"
|
|
50884
|
+
},
|
|
50885
|
+
muted: {
|
|
50886
|
+
type: "string",
|
|
50887
|
+
default: "dim",
|
|
50888
|
+
description: "Muted text color",
|
|
50889
|
+
maxLength: 30,
|
|
50890
|
+
pattern: "^[a-zA-Z]+$"
|
|
50891
|
+
},
|
|
50892
|
+
separator: {
|
|
50893
|
+
type: "string",
|
|
50894
|
+
default: "dim",
|
|
50895
|
+
description: "Separator color",
|
|
50896
|
+
maxLength: 30,
|
|
50897
|
+
pattern: "^[a-zA-Z]+$"
|
|
50898
|
+
},
|
|
50899
|
+
quotaLow: {
|
|
50900
|
+
type: "string",
|
|
50901
|
+
description: "Quota section color when all windows <85%",
|
|
50902
|
+
maxLength: 30,
|
|
50903
|
+
pattern: "^[a-zA-Z]+$"
|
|
50904
|
+
},
|
|
50905
|
+
quotaHigh: {
|
|
50906
|
+
type: "string",
|
|
50907
|
+
description: "Quota section color when any window >=85%",
|
|
50908
|
+
maxLength: 30,
|
|
50909
|
+
pattern: "^[a-zA-Z]+$"
|
|
50910
|
+
}
|
|
50911
|
+
},
|
|
50912
|
+
additionalProperties: false
|
|
50913
|
+
},
|
|
50914
|
+
responsiveBreakpoint: {
|
|
50915
|
+
type: "number",
|
|
50916
|
+
minimum: 0.5,
|
|
50917
|
+
maximum: 1,
|
|
50918
|
+
default: 0.85,
|
|
50919
|
+
description: "Terminal width fraction at which compact mode activates"
|
|
50920
|
+
},
|
|
50921
|
+
maxAgentRows: {
|
|
50922
|
+
type: "integer",
|
|
50923
|
+
minimum: 1,
|
|
50924
|
+
maximum: 10,
|
|
50925
|
+
default: 4,
|
|
50926
|
+
description: "Maximum rows to show in agents section"
|
|
50927
|
+
},
|
|
50928
|
+
todoTruncation: {
|
|
50929
|
+
type: "integer",
|
|
50930
|
+
minimum: 20,
|
|
50931
|
+
maximum: 100,
|
|
50932
|
+
default: 50,
|
|
50933
|
+
description: "Max characters before truncating todo text"
|
|
50934
|
+
}
|
|
50935
|
+
},
|
|
50936
|
+
additionalProperties: false
|
|
50937
|
+
},
|
|
50938
|
+
hooks: {
|
|
50939
|
+
type: "object",
|
|
50940
|
+
description: "Toggle individual hooks on/off. Default: all enabled.",
|
|
50941
|
+
properties: {
|
|
50942
|
+
"session-init": {
|
|
50943
|
+
type: "boolean",
|
|
50944
|
+
default: true,
|
|
50945
|
+
description: "SessionStart hook - project detection and env setup"
|
|
50946
|
+
},
|
|
50947
|
+
"subagent-init": {
|
|
50948
|
+
type: "boolean",
|
|
50949
|
+
default: true,
|
|
50950
|
+
description: "SubagentStart hook - injects context to subagents"
|
|
50951
|
+
},
|
|
50952
|
+
"session-state": {
|
|
50953
|
+
type: "boolean",
|
|
50954
|
+
default: true,
|
|
50955
|
+
description: "Stop/SubagentStop/PostToolUse hook - persists session state for handoff and statusline context"
|
|
50956
|
+
},
|
|
50957
|
+
"cook-after-plan-reminder": {
|
|
50958
|
+
type: "boolean",
|
|
50959
|
+
default: true,
|
|
50960
|
+
description: "SubagentStop hook - reminds agents to continue implementation after planning"
|
|
50961
|
+
},
|
|
50962
|
+
"descriptive-name": {
|
|
50963
|
+
type: "boolean",
|
|
50964
|
+
default: true,
|
|
50965
|
+
description: "PreToolUse hook - injects language-aware descriptive file naming guidance"
|
|
50966
|
+
},
|
|
50967
|
+
"dev-rules-reminder": {
|
|
50968
|
+
type: "boolean",
|
|
50969
|
+
default: true,
|
|
50970
|
+
description: "UserPromptSubmit hook - injects dev rules context"
|
|
50971
|
+
},
|
|
50972
|
+
"plan-format-kanban": {
|
|
50973
|
+
type: "boolean",
|
|
50974
|
+
default: true,
|
|
50975
|
+
description: "PostToolUse hook - keeps plan kanban metadata synchronized after edits"
|
|
50976
|
+
},
|
|
50977
|
+
"usage-quota-cache-refresh": {
|
|
50978
|
+
type: "boolean",
|
|
50979
|
+
default: true,
|
|
50980
|
+
description: "Lifecycle hook - refreshes cached usage quota information"
|
|
50981
|
+
},
|
|
50982
|
+
"usage-context-awareness": {
|
|
50983
|
+
type: "boolean",
|
|
50984
|
+
default: true,
|
|
50985
|
+
description: "UserPromptSubmit/PostToolUse hook - inject usage-limit awareness into prompt context"
|
|
50986
|
+
},
|
|
50987
|
+
"context-tracking": {
|
|
50988
|
+
type: "boolean",
|
|
50989
|
+
default: true,
|
|
50990
|
+
description: "Context window tracking injection (% used, token counts) in dev-rules-reminder"
|
|
50991
|
+
},
|
|
50992
|
+
"scout-block": {
|
|
50993
|
+
type: "boolean",
|
|
50994
|
+
default: true,
|
|
50995
|
+
description: "PreToolUse hook - blocks heavy directories"
|
|
50996
|
+
},
|
|
50997
|
+
"privacy-block": {
|
|
50998
|
+
type: "boolean",
|
|
50999
|
+
default: true,
|
|
51000
|
+
description: "PreToolUse hook - blocks sensitive files"
|
|
51001
|
+
},
|
|
51002
|
+
"simplify-gate": {
|
|
51003
|
+
type: "boolean",
|
|
51004
|
+
default: true,
|
|
51005
|
+
description: "UserPromptSubmit hook - hard-blocks ship/merge/pr/deploy/publish and soft-warns commit/finalize/release when live diff exceeds simplify thresholds. Bypass via CK_SIMPLIFY_DISABLED=1."
|
|
51006
|
+
}
|
|
51007
|
+
},
|
|
51008
|
+
additionalProperties: false
|
|
51009
|
+
},
|
|
51010
|
+
simplify: {
|
|
51011
|
+
type: "object",
|
|
51012
|
+
description: "Signal-driven simplifier orchestration. Live git diff thresholds + verb-based gate.",
|
|
51013
|
+
properties: {
|
|
51014
|
+
threshold: {
|
|
51015
|
+
type: "object",
|
|
51016
|
+
description: "LOC/file thresholds that trip the gate. Nested object stays open for forward-compat fields.",
|
|
51017
|
+
properties: {
|
|
51018
|
+
locDelta: {
|
|
51019
|
+
type: "integer",
|
|
51020
|
+
minimum: 0,
|
|
51021
|
+
default: 400,
|
|
51022
|
+
description: "Total LOC change (across all files) above which the gate fires."
|
|
51023
|
+
},
|
|
51024
|
+
fileCount: {
|
|
51025
|
+
type: "integer",
|
|
51026
|
+
minimum: 0,
|
|
51027
|
+
default: 8,
|
|
51028
|
+
description: "Number of changed files above which the gate fires."
|
|
51029
|
+
},
|
|
51030
|
+
singleFileLoc: {
|
|
51031
|
+
type: "integer",
|
|
51032
|
+
minimum: 0,
|
|
51033
|
+
default: 200,
|
|
51034
|
+
description: "Per-file LOC change above which the gate fires."
|
|
51035
|
+
}
|
|
51036
|
+
}
|
|
51037
|
+
},
|
|
51038
|
+
gate: {
|
|
51039
|
+
type: "object",
|
|
51040
|
+
description: "UserPromptSubmit gate behavior. Nested object stays open for forward-compat fields.",
|
|
51041
|
+
properties: {
|
|
51042
|
+
enabled: {
|
|
51043
|
+
type: "boolean",
|
|
51044
|
+
default: false,
|
|
51045
|
+
description: "Master toggle. Off by default (opt-in). Set true to activate; env CK_SIMPLIFY_DISABLED=1 always bypasses."
|
|
51046
|
+
},
|
|
51047
|
+
hardVerbs: {
|
|
51048
|
+
type: "array",
|
|
51049
|
+
items: { type: "string" },
|
|
51050
|
+
default: ["ship", "merge", "pr", "deploy", "publish"],
|
|
51051
|
+
description: "Verbs that hard-block (continue:false) when thresholds breached."
|
|
51052
|
+
},
|
|
51053
|
+
softVerbs: {
|
|
51054
|
+
type: "array",
|
|
51055
|
+
items: { type: "string" },
|
|
51056
|
+
default: ["commit", "finalize", "release"],
|
|
51057
|
+
description: "Verbs that soft-warn (additionalContext) when thresholds breached."
|
|
51058
|
+
}
|
|
51059
|
+
}
|
|
51060
|
+
}
|
|
51061
|
+
},
|
|
51062
|
+
additionalProperties: false
|
|
51063
|
+
},
|
|
51064
|
+
updatePipeline: {
|
|
51065
|
+
type: "object",
|
|
51066
|
+
description: "3-step update pipeline: CLI update -> kit init -> provider migrate (each independent)",
|
|
51067
|
+
properties: {
|
|
51068
|
+
autoInitAfterUpdate: {
|
|
51069
|
+
type: "boolean",
|
|
51070
|
+
default: false,
|
|
51071
|
+
description: "Automatically run ck init after ck update when kit has new version"
|
|
51072
|
+
},
|
|
51073
|
+
autoMigrateAfterUpdate: {
|
|
51074
|
+
type: "boolean",
|
|
51075
|
+
default: false,
|
|
51076
|
+
description: "Automatically run ck migrate after ck update for detected providers (independent of init)"
|
|
51077
|
+
},
|
|
51078
|
+
migrateProviders: {
|
|
51079
|
+
oneOf: [
|
|
51080
|
+
{
|
|
51081
|
+
type: "string",
|
|
51082
|
+
const: "auto"
|
|
51083
|
+
},
|
|
51084
|
+
{
|
|
51085
|
+
type: "array",
|
|
51086
|
+
items: {
|
|
51087
|
+
type: "string"
|
|
51088
|
+
}
|
|
51089
|
+
}
|
|
51090
|
+
],
|
|
51091
|
+
default: "auto",
|
|
51092
|
+
description: "Which providers to auto-migrate. 'auto' scans filesystem."
|
|
51093
|
+
}
|
|
51094
|
+
},
|
|
51095
|
+
additionalProperties: false
|
|
51096
|
+
},
|
|
51097
|
+
modelTaxonomy: {
|
|
51098
|
+
type: "object",
|
|
51099
|
+
description: "Custom model mappings for portable migration. Keys are provider names (e.g., 'codex', 'gemini-cli'), values map tiers to model config.",
|
|
51100
|
+
additionalProperties: {
|
|
51101
|
+
type: "object",
|
|
51102
|
+
properties: {
|
|
51103
|
+
heavy: {
|
|
51104
|
+
type: "object",
|
|
51105
|
+
properties: {
|
|
51106
|
+
model: {
|
|
51107
|
+
type: "string",
|
|
51108
|
+
description: "Target model name"
|
|
51109
|
+
},
|
|
51110
|
+
effort: {
|
|
51111
|
+
type: "string",
|
|
51112
|
+
description: "Effort/reasoning level (provider-specific)"
|
|
51113
|
+
}
|
|
51114
|
+
},
|
|
51115
|
+
required: ["model"]
|
|
51116
|
+
},
|
|
51117
|
+
balanced: {
|
|
51118
|
+
type: "object",
|
|
51119
|
+
properties: {
|
|
51120
|
+
model: {
|
|
51121
|
+
type: "string",
|
|
51122
|
+
description: "Target model name"
|
|
51123
|
+
},
|
|
51124
|
+
effort: {
|
|
51125
|
+
type: "string",
|
|
51126
|
+
description: "Effort/reasoning level (provider-specific)"
|
|
51127
|
+
}
|
|
51128
|
+
},
|
|
51129
|
+
required: ["model"]
|
|
51130
|
+
},
|
|
51131
|
+
light: {
|
|
51132
|
+
type: "object",
|
|
51133
|
+
properties: {
|
|
51134
|
+
model: {
|
|
51135
|
+
type: "string",
|
|
51136
|
+
description: "Target model name"
|
|
51137
|
+
},
|
|
51138
|
+
effort: {
|
|
51139
|
+
type: "string",
|
|
51140
|
+
description: "Effort/reasoning level (provider-specific)"
|
|
51141
|
+
}
|
|
51142
|
+
},
|
|
51143
|
+
required: ["model"]
|
|
51144
|
+
}
|
|
51145
|
+
}
|
|
51146
|
+
}
|
|
51147
|
+
}
|
|
51148
|
+
},
|
|
51149
|
+
additionalProperties: true
|
|
51150
|
+
};
|
|
51151
|
+
});
|
|
51152
|
+
|
|
51153
|
+
// src/domains/web-server/routes/gck-config-routes.ts
|
|
51154
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
51155
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
51156
|
+
import { join as join38 } from "node:path";
|
|
51157
|
+
async function resolveProjectDir(projectId) {
|
|
51158
|
+
if (!projectId)
|
|
51159
|
+
return null;
|
|
51160
|
+
if (projectId.startsWith("discovered-")) {
|
|
51161
|
+
const encodedPath = projectId.slice("discovered-".length);
|
|
51162
|
+
return Buffer.from(encodedPath, "base64url").toString("utf-8");
|
|
50951
51163
|
}
|
|
51164
|
+
await Promise.resolve().then(() => init_projects_registry());
|
|
51165
|
+
const project = await ProjectsRegistryManager.getProject(projectId);
|
|
51166
|
+
return project?.path ?? null;
|
|
50952
51167
|
}
|
|
50953
|
-
|
|
50954
|
-
|
|
50955
|
-
|
|
50956
|
-
|
|
50957
|
-
|
|
50958
|
-
|
|
50959
|
-
|
|
50960
|
-
|
|
50961
|
-
|
|
50962
|
-
|
|
50963
|
-
|
|
50964
|
-
|
|
50965
|
-
const projectMap = new Map;
|
|
50966
|
-
for (const sp of sessionProjects) {
|
|
50967
|
-
const existing = projectMap.get(sp.path);
|
|
50968
|
-
if (!existing) {
|
|
50969
|
-
projectMap.set(sp.path, {
|
|
50970
|
-
path: sp.path,
|
|
50971
|
-
name: basename11(sp.path),
|
|
50972
|
-
lastUsed: sp.lastUsed ?? null,
|
|
50973
|
-
source: "session",
|
|
50974
|
-
exists: true
|
|
51168
|
+
function registerCkConfigRoutes(app) {
|
|
51169
|
+
app.get("/api/gck-config", async (req, res) => {
|
|
51170
|
+
try {
|
|
51171
|
+
const { projectId, scope = "merged" } = req.query;
|
|
51172
|
+
const projectDir = await resolveProjectDir(projectId);
|
|
51173
|
+
if (scope === "global") {
|
|
51174
|
+
const config = await CkConfigManager.loadScope("global", null);
|
|
51175
|
+
res.json({
|
|
51176
|
+
config: config || {},
|
|
51177
|
+
sources: {},
|
|
51178
|
+
globalPath: CkConfigManager.getGlobalConfigPath(),
|
|
51179
|
+
projectPath: null
|
|
50975
51180
|
});
|
|
51181
|
+
return;
|
|
50976
51182
|
}
|
|
50977
|
-
|
|
50978
|
-
|
|
50979
|
-
|
|
50980
|
-
|
|
50981
|
-
existing.source = "both";
|
|
50982
|
-
existing.interactionCount = hp.interactionCount;
|
|
50983
|
-
if (hp.lastUsed > (existing.lastUsed ?? 0)) {
|
|
50984
|
-
existing.lastUsed = hp.lastUsed;
|
|
51183
|
+
if (scope === "project") {
|
|
51184
|
+
if (!projectDir) {
|
|
51185
|
+
res.status(400).json({ error: "projectId required for project scope" });
|
|
51186
|
+
return;
|
|
50985
51187
|
}
|
|
50986
|
-
|
|
50987
|
-
|
|
50988
|
-
|
|
50989
|
-
|
|
50990
|
-
|
|
50991
|
-
|
|
50992
|
-
exists: false,
|
|
50993
|
-
interactionCount: hp.interactionCount
|
|
51188
|
+
const config = await CkConfigManager.loadScope("project", projectDir);
|
|
51189
|
+
res.json({
|
|
51190
|
+
config: config || {},
|
|
51191
|
+
sources: {},
|
|
51192
|
+
globalPath: CkConfigManager.getGlobalConfigPath(),
|
|
51193
|
+
projectPath: CkConfigManager.getProjectConfigPath(projectDir)
|
|
50994
51194
|
});
|
|
51195
|
+
return;
|
|
50995
51196
|
}
|
|
51197
|
+
const result = await CkConfigManager.loadFull(projectDir);
|
|
51198
|
+
res.json(result);
|
|
51199
|
+
} catch (error) {
|
|
51200
|
+
logger.error(`Failed to load gck-config: ${error}`);
|
|
51201
|
+
res.status(500).json({ error: "Failed to load configuration" });
|
|
50996
51202
|
}
|
|
50997
|
-
|
|
50998
|
-
|
|
50999
|
-
p.exists = await pathExists5(p.path);
|
|
51000
|
-
}));
|
|
51001
|
-
let result = projects;
|
|
51002
|
-
if (filterNonExistent) {
|
|
51003
|
-
result = result.filter((p) => p.exists);
|
|
51004
|
-
}
|
|
51005
|
-
result.sort((a, b) => (b.lastUsed ?? 0) - (a.lastUsed ?? 0));
|
|
51006
|
-
return {
|
|
51007
|
-
projects: result,
|
|
51008
|
-
totalFromSessions: sessionProjects.length,
|
|
51009
|
-
totalFromHistory: historyResult.projects.length,
|
|
51010
|
-
parseTimeMs: Date.now() - start,
|
|
51011
|
-
error
|
|
51012
|
-
};
|
|
51013
|
-
} catch (err) {
|
|
51014
|
-
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
51015
|
-
return {
|
|
51016
|
-
projects: [],
|
|
51017
|
-
totalFromSessions: sessionProjects.length,
|
|
51018
|
-
totalFromHistory: 0,
|
|
51019
|
-
parseTimeMs: Date.now() - start,
|
|
51020
|
-
error: `Failed to merge project discovery: ${msg}`
|
|
51021
|
-
};
|
|
51022
|
-
}
|
|
51023
|
-
}
|
|
51024
|
-
var init_project_discovery = __esm(() => {
|
|
51025
|
-
init_history_parser();
|
|
51026
|
-
});
|
|
51027
|
-
|
|
51028
|
-
// src/services/claude-data/user-preferences.ts
|
|
51029
|
-
import { readFile as readFile16, stat as stat7 } from "node:fs/promises";
|
|
51030
|
-
import { homedir as homedir20 } from "node:os";
|
|
51031
|
-
import { join as join37 } from "node:path";
|
|
51032
|
-
function getPreferencesPath() {
|
|
51033
|
-
return join37(homedir20(), ".claude.json");
|
|
51034
|
-
}
|
|
51035
|
-
async function readUserPreferences(filePath) {
|
|
51036
|
-
const path = filePath ?? getPreferencesPath();
|
|
51037
|
-
const existingLock = locks2.get(path);
|
|
51038
|
-
if (existingLock)
|
|
51039
|
-
return existingLock;
|
|
51040
|
-
const start = Date.now();
|
|
51041
|
-
try {
|
|
51042
|
-
const { mtimeMs } = await stat7(path);
|
|
51043
|
-
const cached = cache2.get(path);
|
|
51044
|
-
if (cached && cached.mtime === mtimeMs) {
|
|
51045
|
-
return cached.result;
|
|
51046
|
-
}
|
|
51047
|
-
} catch {
|
|
51048
|
-
return emptyResult2(Date.now() - start, "Cannot read user preferences - file not found");
|
|
51049
|
-
}
|
|
51050
|
-
const parsePromise = (async () => {
|
|
51203
|
+
});
|
|
51204
|
+
app.put("/api/gck-config", async (req, res) => {
|
|
51051
51205
|
try {
|
|
51052
|
-
|
|
51053
|
-
|
|
51054
|
-
|
|
51055
|
-
|
|
51056
|
-
preferences: UserPreferencesSchema.parse(raw),
|
|
51057
|
-
tipsHistory: TipsHistorySchema.parse(raw.tipsHistory ?? {}),
|
|
51058
|
-
featureFlags: {
|
|
51059
|
-
statsigGates: raw.cachedStatsigGates ?? {},
|
|
51060
|
-
growthBookFeatures: raw.cachedGrowthBookFeatures ?? {}
|
|
51061
|
-
},
|
|
51062
|
-
rawSize: Buffer.byteLength(content),
|
|
51063
|
-
parseTimeMs: Date.now() - start
|
|
51064
|
-
};
|
|
51065
|
-
const { mtimeMs } = await stat7(path);
|
|
51066
|
-
cache2.set(path, { mtime: mtimeMs, result });
|
|
51067
|
-
return result;
|
|
51068
|
-
} catch (err) {
|
|
51069
|
-
const errorMsg = err instanceof Error ? `Failed to parse user preferences: ${err.message}` : "Failed to parse user preferences";
|
|
51070
|
-
return emptyResult2(Date.now() - start, errorMsg);
|
|
51206
|
+
const { scope, projectId, config } = req.body;
|
|
51207
|
+
if (!scope || !["global", "project"].includes(scope)) {
|
|
51208
|
+
res.status(400).json({ error: "Invalid scope. Must be 'global' or 'project'" });
|
|
51209
|
+
return;
|
|
51071
51210
|
}
|
|
51072
|
-
|
|
51073
|
-
|
|
51074
|
-
|
|
51075
|
-
})();
|
|
51076
|
-
locks2.set(path, parsePromise);
|
|
51077
|
-
return parsePromise;
|
|
51078
|
-
}
|
|
51079
|
-
function emptyResult2(parseTimeMs, error) {
|
|
51080
|
-
return {
|
|
51081
|
-
preferences: {},
|
|
51082
|
-
tipsHistory: {},
|
|
51083
|
-
featureFlags: { statsigGates: {}, growthBookFeatures: {} },
|
|
51084
|
-
rawSize: 0,
|
|
51085
|
-
parseTimeMs,
|
|
51086
|
-
error
|
|
51087
|
-
};
|
|
51088
|
-
}
|
|
51089
|
-
function getEffectiveTheme(prefs) {
|
|
51090
|
-
return prefs.preferences.theme ?? "dark";
|
|
51091
|
-
}
|
|
51092
|
-
function getUsageSummary(prefs) {
|
|
51093
|
-
const tipsCounts = Object.values(prefs.tipsHistory);
|
|
51094
|
-
return {
|
|
51095
|
-
totalSessions: prefs.preferences.numStartups ?? 0,
|
|
51096
|
-
promptQueueUsage: prefs.preferences.promptQueueUseCount ?? 0,
|
|
51097
|
-
firstUsed: prefs.preferences.firstStartTime ? new Date(prefs.preferences.firstStartTime) : null,
|
|
51098
|
-
tipsShown: tipsCounts.reduce((a, b) => a + b, 0)
|
|
51099
|
-
};
|
|
51100
|
-
}
|
|
51101
|
-
var cache2, locks2;
|
|
51102
|
-
var init_user_preferences = __esm(() => {
|
|
51103
|
-
init_types5();
|
|
51104
|
-
cache2 = new Map;
|
|
51105
|
-
locks2 = new Map;
|
|
51106
|
-
});
|
|
51107
|
-
|
|
51108
|
-
// src/services/claude-data/index.ts
|
|
51109
|
-
var init_claude_data = __esm(() => {
|
|
51110
|
-
init_types5();
|
|
51111
|
-
init_history_parser();
|
|
51112
|
-
init_session_parser();
|
|
51113
|
-
init_skill_scanner();
|
|
51114
|
-
init_settings_reader();
|
|
51115
|
-
init_hook_log_reader();
|
|
51116
|
-
init_project_scanner();
|
|
51117
|
-
init_project_discovery();
|
|
51118
|
-
init_user_preferences();
|
|
51119
|
-
});
|
|
51120
|
-
|
|
51121
|
-
// src/domains/web-server/routes/dashboard-routes.ts
|
|
51122
|
-
import { existsSync as existsSync20 } from "node:fs";
|
|
51123
|
-
import { readFile as readFile17, readdir as readdir11 } from "node:fs/promises";
|
|
51124
|
-
import { homedir as homedir21 } from "node:os";
|
|
51125
|
-
import { join as join38 } from "node:path";
|
|
51126
|
-
async function readAgents() {
|
|
51127
|
-
const agentsDir = join38(claudeDir2, "agents");
|
|
51128
|
-
if (!existsSync20(agentsDir))
|
|
51129
|
-
return [];
|
|
51130
|
-
try {
|
|
51131
|
-
const files = await readdir11(agentsDir);
|
|
51132
|
-
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
51133
|
-
const agents = await Promise.all(mdFiles.map(async (file) => {
|
|
51134
|
-
try {
|
|
51135
|
-
const { frontmatter: fm } = await parseFrontmatterFile(join38(agentsDir, file));
|
|
51136
|
-
return {
|
|
51137
|
-
name: fm.name || file.replace(/\.md$/, ""),
|
|
51138
|
-
model: fm.model || "unset",
|
|
51139
|
-
description: fm.description || "",
|
|
51140
|
-
color: fm.color
|
|
51141
|
-
};
|
|
51142
|
-
} catch {
|
|
51143
|
-
return null;
|
|
51211
|
+
if (!config || typeof config !== "object") {
|
|
51212
|
+
res.status(400).json({ error: "Invalid config payload" });
|
|
51213
|
+
return;
|
|
51144
51214
|
}
|
|
51145
|
-
|
|
51146
|
-
|
|
51147
|
-
|
|
51148
|
-
|
|
51149
|
-
|
|
51150
|
-
}
|
|
51151
|
-
|
|
51152
|
-
if (!existsSync20(dir) || depth > 10)
|
|
51153
|
-
return 0;
|
|
51154
|
-
try {
|
|
51155
|
-
const entries = await readdir11(dir, { withFileTypes: true });
|
|
51156
|
-
const counts = await Promise.all(entries.map(async (entry) => {
|
|
51157
|
-
if (entry.isDirectory()) {
|
|
51158
|
-
return countMdFilesRecursive(join38(dir, entry.name), depth + 1);
|
|
51215
|
+
const parseResult = CkConfigSchema.safeParse(normalizeCkConfigInput(config));
|
|
51216
|
+
if (!parseResult.success) {
|
|
51217
|
+
res.status(400).json({
|
|
51218
|
+
error: "Config validation failed",
|
|
51219
|
+
details: parseResult.error.issues
|
|
51220
|
+
});
|
|
51221
|
+
return;
|
|
51159
51222
|
}
|
|
51160
|
-
|
|
51161
|
-
|
|
51162
|
-
|
|
51163
|
-
|
|
51164
|
-
|
|
51165
|
-
|
|
51166
|
-
|
|
51167
|
-
|
|
51168
|
-
|
|
51169
|
-
|
|
51170
|
-
return 0;
|
|
51171
|
-
try {
|
|
51172
|
-
const entries = await readdir11(skillsDir);
|
|
51173
|
-
let count = 0;
|
|
51174
|
-
for (const entry of entries) {
|
|
51175
|
-
if (existsSync20(join38(skillsDir, entry, "SKILL.md")))
|
|
51176
|
-
count++;
|
|
51177
|
-
}
|
|
51178
|
-
return count;
|
|
51179
|
-
} catch {
|
|
51180
|
-
return 0;
|
|
51181
|
-
}
|
|
51182
|
-
}
|
|
51183
|
-
async function countAllMcpServers() {
|
|
51184
|
-
const settings = await readSettings();
|
|
51185
|
-
const baseCount = settings ? countMcpServers(settings) : 0;
|
|
51186
|
-
const seen = new Set;
|
|
51187
|
-
if (settings?.mcpServers && typeof settings.mcpServers === "object") {
|
|
51188
|
-
for (const name of Object.keys(settings.mcpServers)) {
|
|
51189
|
-
seen.add(name);
|
|
51190
|
-
}
|
|
51191
|
-
}
|
|
51192
|
-
let claudeJsonCount = 0;
|
|
51193
|
-
try {
|
|
51194
|
-
const claudeJsonPath = join38(homedir21(), ".claude.json");
|
|
51195
|
-
if (existsSync20(claudeJsonPath)) {
|
|
51196
|
-
const content = await readFile17(claudeJsonPath, "utf-8");
|
|
51197
|
-
const data = JSON.parse(content);
|
|
51198
|
-
if (data.mcpServers && typeof data.mcpServers === "object") {
|
|
51199
|
-
for (const name of Object.keys(data.mcpServers)) {
|
|
51200
|
-
if (!seen.has(name)) {
|
|
51201
|
-
seen.add(name);
|
|
51202
|
-
claudeJsonCount++;
|
|
51203
|
-
}
|
|
51223
|
+
let projectDir = null;
|
|
51224
|
+
if (scope === "project") {
|
|
51225
|
+
if (!projectId) {
|
|
51226
|
+
res.status(400).json({ error: "projectId required for project scope" });
|
|
51227
|
+
return;
|
|
51228
|
+
}
|
|
51229
|
+
projectDir = await resolveProjectDir(projectId);
|
|
51230
|
+
if (!projectDir) {
|
|
51231
|
+
res.status(404).json({ error: "Project not found" });
|
|
51232
|
+
return;
|
|
51204
51233
|
}
|
|
51205
51234
|
}
|
|
51206
|
-
|
|
51207
|
-
|
|
51208
|
-
|
|
51209
|
-
|
|
51210
|
-
|
|
51211
|
-
|
|
51212
|
-
|
|
51213
|
-
|
|
51214
|
-
if (data.mcpServers && typeof data.mcpServers === "object") {
|
|
51215
|
-
for (const name of Object.keys(data.mcpServers)) {
|
|
51216
|
-
if (!seen.has(name)) {
|
|
51217
|
-
seen.add(name);
|
|
51218
|
-
mcpJsonCount++;
|
|
51235
|
+
const savedPath = await CkConfigManager.saveFull(parseResult.data, scope, projectDir);
|
|
51236
|
+
let savedConfig = await CkConfigManager.loadScope(scope, projectDir);
|
|
51237
|
+
if (!savedConfig) {
|
|
51238
|
+
try {
|
|
51239
|
+
const rawContent = await readFile17(savedPath, "utf-8");
|
|
51240
|
+
const rawConfig = JSON.parse(rawContent);
|
|
51241
|
+
if (rawConfig && typeof rawConfig === "object" && !Array.isArray(rawConfig)) {
|
|
51242
|
+
savedConfig = normalizeCkConfigInput(rawConfig);
|
|
51219
51243
|
}
|
|
51244
|
+
} catch {
|
|
51245
|
+
savedConfig = null;
|
|
51220
51246
|
}
|
|
51221
51247
|
}
|
|
51222
|
-
}
|
|
51223
|
-
} catch {}
|
|
51224
|
-
return baseCount + claudeJsonCount + mcpJsonCount;
|
|
51225
|
-
}
|
|
51226
|
-
function classifyModel(model) {
|
|
51227
|
-
const lower = model.toLowerCase();
|
|
51228
|
-
if (lower === "unset" || !lower)
|
|
51229
|
-
return "unset";
|
|
51230
|
-
if (lower.includes("opus"))
|
|
51231
|
-
return "opus";
|
|
51232
|
-
if (lower.includes("haiku"))
|
|
51233
|
-
return "haiku";
|
|
51234
|
-
if (lower.includes("sonnet"))
|
|
51235
|
-
return "sonnet";
|
|
51236
|
-
return "unset";
|
|
51237
|
-
}
|
|
51238
|
-
function registerDashboardRoutes(app) {
|
|
51239
|
-
app.get("/api/dashboard/stats", async (_req, res) => {
|
|
51240
|
-
try {
|
|
51241
|
-
const [agents, commandCount, skillCount, mcpCount] = await Promise.all([
|
|
51242
|
-
readAgents(),
|
|
51243
|
-
countMdFilesRecursive(join38(claudeDir2, "commands")),
|
|
51244
|
-
countSkills2(),
|
|
51245
|
-
countAllMcpServers()
|
|
51246
|
-
]);
|
|
51247
|
-
const modelDistribution = { opus: 0, sonnet: 0, haiku: 0, unset: 0 };
|
|
51248
|
-
for (const agent of agents) {
|
|
51249
|
-
const tier = classifyModel(agent.model);
|
|
51250
|
-
modelDistribution[tier]++;
|
|
51251
|
-
}
|
|
51252
51248
|
res.json({
|
|
51253
|
-
|
|
51254
|
-
|
|
51255
|
-
|
|
51256
|
-
|
|
51257
|
-
modelDistribution
|
|
51249
|
+
success: true,
|
|
51250
|
+
path: savedPath,
|
|
51251
|
+
scope,
|
|
51252
|
+
config: savedConfig || {}
|
|
51258
51253
|
});
|
|
51259
51254
|
} catch (error) {
|
|
51260
|
-
|
|
51255
|
+
logger.error(`Failed to save gck-config: ${error}`);
|
|
51256
|
+
res.status(500).json({ error: "Failed to save configuration" });
|
|
51261
51257
|
}
|
|
51262
51258
|
});
|
|
51263
|
-
app.get("/api/
|
|
51264
|
-
|
|
51265
|
-
const agents = await readAgents();
|
|
51266
|
-
res.json({ agents: agents.slice(0, 6) });
|
|
51267
|
-
} catch (error) {
|
|
51268
|
-
res.status(500).json({ error: "Failed to load agents" });
|
|
51269
|
-
}
|
|
51259
|
+
app.get("/api/gck-config/schema", (_req, res) => {
|
|
51260
|
+
res.json(gck_config_schema_default);
|
|
51270
51261
|
});
|
|
51271
|
-
app.
|
|
51262
|
+
app.patch("/api/gck-config/field", async (req, res) => {
|
|
51272
51263
|
try {
|
|
51273
|
-
const
|
|
51274
|
-
|
|
51275
|
-
|
|
51276
|
-
|
|
51277
|
-
suggestions.push({
|
|
51278
|
-
type: "warning",
|
|
51279
|
-
message: `${unsetCount} agent${unsetCount > 1 ? "s" : ""} have no model set`,
|
|
51280
|
-
target: "agents"
|
|
51281
|
-
});
|
|
51264
|
+
const { scope, projectId, fieldPath, value } = req.body;
|
|
51265
|
+
if (!scope || !["global", "project"].includes(scope)) {
|
|
51266
|
+
res.status(400).json({ error: "Invalid scope" });
|
|
51267
|
+
return;
|
|
51282
51268
|
}
|
|
51283
|
-
|
|
51284
|
-
|
|
51285
|
-
|
|
51286
|
-
type: "info",
|
|
51287
|
-
message: "No skills installed — browse the Skills Marketplace",
|
|
51288
|
-
target: "skills"
|
|
51289
|
-
});
|
|
51269
|
+
if (!fieldPath || typeof fieldPath !== "string") {
|
|
51270
|
+
res.status(400).json({ error: "fieldPath required" });
|
|
51271
|
+
return;
|
|
51290
51272
|
}
|
|
51291
|
-
|
|
51292
|
-
if (
|
|
51293
|
-
|
|
51294
|
-
|
|
51295
|
-
|
|
51296
|
-
}
|
|
51273
|
+
let projectDir = null;
|
|
51274
|
+
if (scope === "project") {
|
|
51275
|
+
if (!projectId) {
|
|
51276
|
+
res.status(400).json({ error: "projectId required for project scope" });
|
|
51277
|
+
return;
|
|
51278
|
+
}
|
|
51279
|
+
projectDir = await resolveProjectDir(projectId);
|
|
51280
|
+
if (!projectDir) {
|
|
51281
|
+
res.status(404).json({ error: "Project not found" });
|
|
51282
|
+
return;
|
|
51283
|
+
}
|
|
51297
51284
|
}
|
|
51298
|
-
|
|
51299
|
-
|
|
51300
|
-
|
|
51301
|
-
|
|
51302
|
-
|
|
51303
|
-
|
|
51285
|
+
await CkConfigManager.updateField(fieldPath, value, scope, projectDir);
|
|
51286
|
+
res.json({ success: true, fieldPath, value, scope });
|
|
51287
|
+
} catch (error) {
|
|
51288
|
+
if (error instanceof ZodError) {
|
|
51289
|
+
res.status(400).json({ error: "Validation failed", issues: error.issues });
|
|
51290
|
+
return;
|
|
51304
51291
|
}
|
|
51305
|
-
|
|
51306
|
-
|
|
51307
|
-
|
|
51308
|
-
|
|
51309
|
-
|
|
51292
|
+
logger.error(`Failed to update gck-config field: ${error}`);
|
|
51293
|
+
res.status(500).json({ error: "Failed to update field" });
|
|
51294
|
+
}
|
|
51295
|
+
});
|
|
51296
|
+
app.get("/api/metadata/global", async (_req, res) => {
|
|
51297
|
+
try {
|
|
51298
|
+
const metadataPath = join38(PathResolver.getGlobalKitDir(), "metadata.json");
|
|
51299
|
+
let metadata = {};
|
|
51300
|
+
if (existsSync20(metadataPath)) {
|
|
51301
|
+
const content = await readFile17(metadataPath, "utf-8");
|
|
51302
|
+
try {
|
|
51303
|
+
metadata = JSON.parse(content);
|
|
51304
|
+
} catch (err) {
|
|
51305
|
+
logger.warning(`Invalid JSON in metadata.json: ${err}`);
|
|
51306
|
+
}
|
|
51310
51307
|
}
|
|
51311
|
-
res.json(
|
|
51308
|
+
res.json(metadata);
|
|
51312
51309
|
} catch (error) {
|
|
51313
|
-
|
|
51310
|
+
logger.error(`Failed to load global metadata: ${error}`);
|
|
51311
|
+
res.status(500).json({ error: "Failed to load metadata" });
|
|
51314
51312
|
}
|
|
51315
51313
|
});
|
|
51316
51314
|
}
|
|
51317
|
-
var
|
|
51318
|
-
|
|
51319
|
-
|
|
51320
|
-
|
|
51321
|
-
|
|
51315
|
+
var init_gck_config_routes = __esm(() => {
|
|
51316
|
+
init_config();
|
|
51317
|
+
init_gck_config_schema();
|
|
51318
|
+
init_logger();
|
|
51319
|
+
init_path_resolver();
|
|
51320
|
+
init_types3();
|
|
51321
|
+
init_zod();
|
|
51322
51322
|
});
|
|
51323
51323
|
|
|
51324
51324
|
// src/domains/web-server/routes/health-routes.ts
|
|
@@ -56660,7 +56660,7 @@ var init_skill_schema = __esm(() => {
|
|
|
56660
56660
|
type: "string",
|
|
56661
56661
|
minLength: 1,
|
|
56662
56662
|
maxLength: 100,
|
|
56663
|
-
description: "Skill identifier, typically prefixed with '
|
|
56663
|
+
description: "Skill identifier, typically prefixed with 'gck:'"
|
|
56664
56664
|
},
|
|
56665
56665
|
description: {
|
|
56666
56666
|
type: "string",
|
|
@@ -56882,7 +56882,7 @@ function toStringArray(v) {
|
|
|
56882
56882
|
function extractCrossRefs(body) {
|
|
56883
56883
|
const stripped = body.replace(/```[\s\S]*?```/g, "").replace(/`[^`]*`/g, "");
|
|
56884
56884
|
const refs = new Set;
|
|
56885
|
-
const pattern = /\/ck:([a-z0-9-]+)/g;
|
|
56885
|
+
const pattern = /\/g?ck:([a-z0-9-]+)/g;
|
|
56886
56886
|
for (const match of stripped.matchAll(pattern)) {
|
|
56887
56887
|
refs.add(match[1]);
|
|
56888
56888
|
}
|
|
@@ -59541,7 +59541,7 @@ function generatePlanMd(options2) {
|
|
|
59541
59541
|
} = options2;
|
|
59542
59542
|
const created = new Date().toISOString();
|
|
59543
59543
|
const escYaml = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
|
|
59544
|
-
const createdBy = source === "skill" ? "
|
|
59544
|
+
const createdBy = source === "skill" ? "gck:plan" : source === "dashboard" ? "dashboard" : "ck-cli";
|
|
59545
59545
|
const branch = getCurrentBranch();
|
|
59546
59546
|
const frontmatterLines = [
|
|
59547
59547
|
`title: "${escYaml(title)}"`,
|
|
@@ -60257,7 +60257,7 @@ function updateRegistryEntry(entry, cwd = process.cwd()) {
|
|
|
60257
60257
|
function registerNewPlan(options2) {
|
|
60258
60258
|
const now = new Date().toISOString();
|
|
60259
60259
|
const source = options2.source ?? "cli";
|
|
60260
|
-
const createdBy = source === "skill" ? "
|
|
60260
|
+
const createdBy = source === "skill" ? "gck:plan" : source === "dashboard" ? "dashboard" : "ck-cli";
|
|
60261
60261
|
updateRegistryEntry({
|
|
60262
60262
|
dir: options2.dir,
|
|
60263
60263
|
title: options2.title,
|
|
@@ -63790,7 +63790,7 @@ var package_default;
|
|
|
63790
63790
|
var init_package = __esm(() => {
|
|
63791
63791
|
package_default = {
|
|
63792
63792
|
name: "@thuanha-groove/gck",
|
|
63793
|
-
version: "0.0.
|
|
63793
|
+
version: "0.0.2",
|
|
63794
63794
|
description: "CLI tool for bootstrapping and updating ClaudeKit projects",
|
|
63795
63795
|
type: "module",
|
|
63796
63796
|
repository: {
|
|
@@ -68259,7 +68259,7 @@ async function promptMigrateUpdate(deps) {
|
|
|
68259
68259
|
}
|
|
68260
68260
|
var import_fs_extra8, execAsync2, SAFE_PROVIDER_NAME, HOOK_DEPENDENCY_EXTENSIONS, MANAGED_HOOKS_MANIFEST = "managed-hooks.json";
|
|
68261
68261
|
var init_post_update_handler = __esm(() => {
|
|
68262
|
-
|
|
68262
|
+
init_gck_config_manager();
|
|
68263
68263
|
init_hook_health_checker();
|
|
68264
68264
|
init_metadata_migration();
|
|
68265
68265
|
init_version_utils();
|
|
@@ -68822,7 +68822,7 @@ function registerSystemRoutes(app) {
|
|
|
68822
68822
|
try {
|
|
68823
68823
|
const [packageJson, installLocation, gitVersion, ghVersion, bunVersion] = await Promise.all([
|
|
68824
68824
|
getPackageJson(),
|
|
68825
|
-
runCommand("which", ["
|
|
68825
|
+
runCommand("which", ["gck"], "not found"),
|
|
68826
68826
|
runCommand("git", ["--version"], "unknown"),
|
|
68827
68827
|
runCommand("gh", ["--version"], "unknown").then((out) => out.split(`
|
|
68828
68828
|
`)[0] ?? "unknown"),
|
|
@@ -69098,9 +69098,9 @@ function registerRoutes(app) {
|
|
|
69098
69098
|
var init_routes = __esm(() => {
|
|
69099
69099
|
init_action_routes();
|
|
69100
69100
|
init_agents_routes();
|
|
69101
|
-
init_ck_config_routes();
|
|
69102
69101
|
init_command_routes();
|
|
69103
69102
|
init_dashboard_routes();
|
|
69103
|
+
init_gck_config_routes();
|
|
69104
69104
|
init_health_routes();
|
|
69105
69105
|
init_hook_log_routes();
|
|
69106
69106
|
init_mcp_routes();
|
|
@@ -77010,11 +77010,11 @@ var require_extract_zip = __commonJS(function(exports2, module2) {
|
|
|
77010
77010
|
class OwnershipDisplay {
|
|
77011
77011
|
static formatOwnership(ownership) {
|
|
77012
77012
|
switch (ownership) {
|
|
77013
|
-
case "
|
|
77013
|
+
case "gck":
|
|
77014
77014
|
return import_picocolors26.default.blue("CK-owned");
|
|
77015
77015
|
case "user":
|
|
77016
77016
|
return import_picocolors26.default.green("User-created");
|
|
77017
|
-
case "
|
|
77017
|
+
case "gck-modified":
|
|
77018
77018
|
return import_picocolors26.default.yellow("CK-modified");
|
|
77019
77019
|
default:
|
|
77020
77020
|
return import_picocolors26.default.gray("Unknown");
|
|
@@ -77043,13 +77043,13 @@ class OwnershipDisplay {
|
|
|
77043
77043
|
};
|
|
77044
77044
|
for (const result of results) {
|
|
77045
77045
|
switch (result.ownership) {
|
|
77046
|
-
case "
|
|
77046
|
+
case "gck":
|
|
77047
77047
|
summary.ckOwned++;
|
|
77048
77048
|
break;
|
|
77049
77049
|
case "user":
|
|
77050
77050
|
summary.userCreated++;
|
|
77051
77051
|
break;
|
|
77052
|
-
case "
|
|
77052
|
+
case "gck-modified":
|
|
77053
77053
|
summary.ckModified++;
|
|
77054
77054
|
break;
|
|
77055
77055
|
}
|
|
@@ -77925,7 +77925,7 @@ ${context.gitEventDetails}
|
|
|
77925
77925
|
- Dimensions: ${dimensions}
|
|
77926
77926
|
- Style: Professional, modern design
|
|
77927
77927
|
- Include relevant visual elements for a tech/dev update
|
|
77928
|
-
- Use the /
|
|
77928
|
+
- Use the /gck:ai-multimodal or /gck:banner-design skill
|
|
77929
77929
|
- Save the image and provide the file path
|
|
77930
77930
|
|
|
77931
77931
|
IMPORTANT: Generate the image and output the path as JSON: {"imagePath": "/path/to/image.png"}`;
|
|
@@ -81205,7 +81205,7 @@ var init_init_command_help = __esm(() => {
|
|
|
81205
81205
|
},
|
|
81206
81206
|
{
|
|
81207
81207
|
flags: "--prefix",
|
|
81208
|
-
description: "Add /
|
|
81208
|
+
description: "Add /gck: prefix to all slash commands"
|
|
81209
81209
|
},
|
|
81210
81210
|
{
|
|
81211
81211
|
flags: "--skip-setup",
|
|
@@ -81467,7 +81467,7 @@ var init_new_command_help = __esm(() => {
|
|
|
81467
81467
|
},
|
|
81468
81468
|
{
|
|
81469
81469
|
flags: "--prefix",
|
|
81470
|
-
description: "Add /
|
|
81470
|
+
description: "Add /gck: prefix to all slash commands"
|
|
81471
81471
|
}
|
|
81472
81472
|
]
|
|
81473
81473
|
},
|
|
@@ -82390,12 +82390,12 @@ var BANNER_LINES;
|
|
|
82390
82390
|
var init_help_banner = __esm(() => {
|
|
82391
82391
|
init_help_colors();
|
|
82392
82392
|
BANNER_LINES = [
|
|
82393
|
-
" ██████╗██╗ ██╗",
|
|
82394
|
-
"██╔════╝██║ ██╔╝",
|
|
82395
|
-
"██║ █████╔╝ ",
|
|
82396
|
-
"██║ ██╔═██╗ ",
|
|
82397
|
-
"
|
|
82398
|
-
" ╚═════╝╚═╝ ╚═╝"
|
|
82393
|
+
" ██████╗ ██████╗██╗ ██╗",
|
|
82394
|
+
"██╔════╝ ██╔════╝██║ ██╔╝",
|
|
82395
|
+
"██║ ███╗██║ █████╔╝ ",
|
|
82396
|
+
"██║ ██║██║ ██╔═██╗ ",
|
|
82397
|
+
"╚██████╔╝╚██████╗██║ ██╗",
|
|
82398
|
+
" ╚═════╝ ╚═════╝╚═╝ ╚═╝"
|
|
82399
82399
|
];
|
|
82400
82400
|
});
|
|
82401
82401
|
|
|
@@ -88239,7 +88239,7 @@ async function findSkillDirs(dir) {
|
|
|
88239
88239
|
function normalizeSkillId(rawName, fallbackId) {
|
|
88240
88240
|
if (!rawName)
|
|
88241
88241
|
return fallbackId;
|
|
88242
|
-
return rawName.
|
|
88242
|
+
return rawName.replace(/^g?ck:/, "");
|
|
88243
88243
|
}
|
|
88244
88244
|
|
|
88245
88245
|
// src/domains/health-checks/checkers/skill-budget-settings.ts
|
|
@@ -91116,7 +91116,7 @@ class OwnershipChecker {
|
|
|
91116
91116
|
if (actualChecksum === tracked.checksum) {
|
|
91117
91117
|
return {
|
|
91118
91118
|
path: filePath,
|
|
91119
|
-
ownership: "
|
|
91119
|
+
ownership: "gck",
|
|
91120
91120
|
expectedChecksum: tracked.checksum,
|
|
91121
91121
|
actualChecksum,
|
|
91122
91122
|
exists: true
|
|
@@ -91124,7 +91124,7 @@ class OwnershipChecker {
|
|
|
91124
91124
|
}
|
|
91125
91125
|
return {
|
|
91126
91126
|
path: filePath,
|
|
91127
|
-
ownership: "
|
|
91127
|
+
ownership: "gck-modified",
|
|
91128
91128
|
expectedChecksum: tracked.checksum,
|
|
91129
91129
|
actualChecksum,
|
|
91130
91130
|
exists: true
|
|
@@ -92024,7 +92024,7 @@ class SyncEngine {
|
|
|
92024
92024
|
plan.autoUpdate.push(file);
|
|
92025
92025
|
continue;
|
|
92026
92026
|
}
|
|
92027
|
-
if (file.ownership === "
|
|
92027
|
+
if (file.ownership === "gck") {
|
|
92028
92028
|
plan.autoUpdate.push(file);
|
|
92029
92029
|
continue;
|
|
92030
92030
|
}
|
|
@@ -92159,7 +92159,7 @@ class SyncEngine {
|
|
|
92159
92159
|
}
|
|
92160
92160
|
// src/shared/deletion-pattern-expander.ts
|
|
92161
92161
|
var LEGACY_COMMAND_PREFIX_BY_KIT = {
|
|
92162
|
-
engineer: "
|
|
92162
|
+
engineer: "gck",
|
|
92163
92163
|
marketing: "mkt"
|
|
92164
92164
|
};
|
|
92165
92165
|
function getLegacyCommandPrefix(kitType) {
|
|
@@ -105083,7 +105083,7 @@ function buildFileTrackingList(options2) {
|
|
|
105083
105083
|
const relativePath = isGlobal ? installedPath : installedPath.replace(/^\.claude\//, "");
|
|
105084
105084
|
const filePath = join114(claudeDir, relativePath);
|
|
105085
105085
|
const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
|
|
105086
|
-
const ownership = manifestEntry ? "
|
|
105086
|
+
const ownership = manifestEntry ? "gck" : "user";
|
|
105087
105087
|
filesToTrack.push({
|
|
105088
105088
|
filePath,
|
|
105089
105089
|
relativePath,
|
|
@@ -105291,13 +105291,13 @@ User-created files (sample):`);
|
|
|
105291
105291
|
trackedFiles.push({
|
|
105292
105292
|
path: relativePath,
|
|
105293
105293
|
checksum: manifestEntry.checksum,
|
|
105294
|
-
ownership: "
|
|
105294
|
+
ownership: "gck",
|
|
105295
105295
|
installedVersion: kitVersion
|
|
105296
105296
|
});
|
|
105297
105297
|
}
|
|
105298
105298
|
}
|
|
105299
105299
|
const filesToChecksum = [
|
|
105300
|
-
...preview.ckModified.map((p) => ({ relativePath: p, ownership: "
|
|
105300
|
+
...preview.ckModified.map((p) => ({ relativePath: p, ownership: "gck-modified" })),
|
|
105301
105301
|
...preview.userCreated.map((p) => ({ relativePath: p, ownership: "user" }))
|
|
105302
105302
|
];
|
|
105303
105303
|
if (filesToChecksum.length > 0) {
|
|
@@ -105555,11 +105555,11 @@ function buildCommandPatterns() {
|
|
|
105555
105555
|
for (const cmd of COMMAND_ROOTS) {
|
|
105556
105556
|
patterns.push({
|
|
105557
105557
|
regex: new RegExp(`(?:^|(?<=[\\s\`]))(/)(${cmd})(:)(?!/)`, "gm"),
|
|
105558
|
-
replacement: "$
|
|
105558
|
+
replacement: "$1gck:$2$3"
|
|
105559
105559
|
});
|
|
105560
105560
|
patterns.push({
|
|
105561
105561
|
regex: new RegExp(`(?:^|(?<=[\\s\`]))(/)(${cmd})(?![?/=&:\\w])(?=[\\s\`\\]\\)]|$)`, "gm"),
|
|
105562
|
-
replacement: "$
|
|
105562
|
+
replacement: "$1gck:$2"
|
|
105563
105563
|
});
|
|
105564
105564
|
}
|
|
105565
105565
|
return patterns;
|
|
@@ -105667,7 +105667,7 @@ async function applyPrefix(extractDir) {
|
|
|
105667
105667
|
logger.verbose("No commands directory found, skipping prefix application");
|
|
105668
105668
|
return;
|
|
105669
105669
|
}
|
|
105670
|
-
logger.info("Applying /
|
|
105670
|
+
logger.info("Applying /gck: prefix to slash commands...");
|
|
105671
105671
|
const backupDir = join118(extractDir, ".commands-backup");
|
|
105672
105672
|
const tempDir = join118(extractDir, ".commands-prefix-temp");
|
|
105673
105673
|
try {
|
|
@@ -105676,19 +105676,19 @@ async function applyPrefix(extractDir) {
|
|
|
105676
105676
|
logger.verbose("Commands directory is empty, skipping prefix application");
|
|
105677
105677
|
return;
|
|
105678
105678
|
}
|
|
105679
|
-
if (entries.length === 1 && entries[0] === "
|
|
105680
|
-
const
|
|
105681
|
-
const ckStat = await stat20(
|
|
105679
|
+
if (entries.length === 1 && entries[0] === "gck") {
|
|
105680
|
+
const gckDir = join118(commandsDir, "gck");
|
|
105681
|
+
const ckStat = await stat20(gckDir);
|
|
105682
105682
|
if (ckStat.isDirectory()) {
|
|
105683
|
-
logger.verbose("Commands already have /
|
|
105683
|
+
logger.verbose("Commands already have /gck: prefix, skipping");
|
|
105684
105684
|
return;
|
|
105685
105685
|
}
|
|
105686
105686
|
}
|
|
105687
105687
|
await import_fs_extra21.copy(commandsDir, backupDir);
|
|
105688
105688
|
logger.verbose("Created backup of commands directory");
|
|
105689
105689
|
await mkdir32(tempDir, { recursive: true });
|
|
105690
|
-
const
|
|
105691
|
-
await mkdir32(
|
|
105690
|
+
const gckDir = join118(tempDir, "gck");
|
|
105691
|
+
await mkdir32(gckDir, { recursive: true });
|
|
105692
105692
|
let processedCount = 0;
|
|
105693
105693
|
for (const entry of entries) {
|
|
105694
105694
|
const sourcePath = join118(commandsDir, entry);
|
|
@@ -105697,13 +105697,13 @@ async function applyPrefix(extractDir) {
|
|
|
105697
105697
|
logger.warning(`Skipping symlink for security: ${entry}`);
|
|
105698
105698
|
continue;
|
|
105699
105699
|
}
|
|
105700
|
-
const destPath = join118(
|
|
105700
|
+
const destPath = join118(gckDir, entry);
|
|
105701
105701
|
await import_fs_extra21.copy(sourcePath, destPath, {
|
|
105702
105702
|
overwrite: false,
|
|
105703
105703
|
errorOnExist: true
|
|
105704
105704
|
});
|
|
105705
105705
|
processedCount++;
|
|
105706
|
-
logger.verbose(`Moved ${entry} to
|
|
105706
|
+
logger.verbose(`Moved ${entry} to gck/${entry}`);
|
|
105707
105707
|
}
|
|
105708
105708
|
if (processedCount === 0) {
|
|
105709
105709
|
logger.warning("No files to move (all were symlinks or invalid)");
|
|
@@ -105714,7 +105714,7 @@ async function applyPrefix(extractDir) {
|
|
|
105714
105714
|
await import_fs_extra21.remove(commandsDir);
|
|
105715
105715
|
await import_fs_extra21.move(tempDir, commandsDir);
|
|
105716
105716
|
await import_fs_extra21.remove(backupDir);
|
|
105717
|
-
logger.success("Successfully reorganized commands to /
|
|
105717
|
+
logger.success("Successfully reorganized commands to /gck: prefix");
|
|
105718
105718
|
const claudeDir = join118(extractDir, ".claude");
|
|
105719
105719
|
logger.info("Transforming command references in file contents...");
|
|
105720
105720
|
const transformResult = await transformCommandReferences(claudeDir, {
|
|
@@ -105738,7 +105738,7 @@ async function applyPrefix(extractDir) {
|
|
|
105738
105738
|
if (await import_fs_extra21.pathExists(tempDir)) {
|
|
105739
105739
|
await import_fs_extra21.remove(tempDir).catch(() => {});
|
|
105740
105740
|
}
|
|
105741
|
-
logger.error("Failed to apply /
|
|
105741
|
+
logger.error("Failed to apply /gck: prefix to commands");
|
|
105742
105742
|
throw error;
|
|
105743
105743
|
} finally {
|
|
105744
105744
|
if (await import_fs_extra21.pathExists(backupDir)) {
|
|
@@ -105782,10 +105782,10 @@ async function scanDirectoryFiles(dir) {
|
|
|
105782
105782
|
async function processFileOwnership(file, relativePath, metadata, claudeDir, options2, accumulator) {
|
|
105783
105783
|
const { dryRun = false, forceOverwrite = false } = options2;
|
|
105784
105784
|
const ownershipResult = await OwnershipChecker.checkOwnership(file, metadata, claudeDir);
|
|
105785
|
-
if (ownershipResult.ownership === "
|
|
105785
|
+
if (ownershipResult.ownership === "gck" && ownershipResult.exists) {
|
|
105786
105786
|
accumulator.results.push({
|
|
105787
105787
|
path: relativePath,
|
|
105788
|
-
ownership: "
|
|
105788
|
+
ownership: "gck",
|
|
105789
105789
|
action: "delete"
|
|
105790
105790
|
});
|
|
105791
105791
|
if (!dryRun) {
|
|
@@ -105795,11 +105795,11 @@ async function processFileOwnership(file, relativePath, metadata, claudeDir, opt
|
|
|
105795
105795
|
accumulator.deletedCount++;
|
|
105796
105796
|
return true;
|
|
105797
105797
|
}
|
|
105798
|
-
if (ownershipResult.ownership === "
|
|
105798
|
+
if (ownershipResult.ownership === "gck-modified") {
|
|
105799
105799
|
if (forceOverwrite) {
|
|
105800
105800
|
accumulator.results.push({
|
|
105801
105801
|
path: relativePath,
|
|
105802
|
-
ownership: "
|
|
105802
|
+
ownership: "gck-modified",
|
|
105803
105803
|
action: "delete",
|
|
105804
105804
|
reason: "force overwrite"
|
|
105805
105805
|
});
|
|
@@ -105812,7 +105812,7 @@ async function processFileOwnership(file, relativePath, metadata, claudeDir, opt
|
|
|
105812
105812
|
}
|
|
105813
105813
|
accumulator.results.push({
|
|
105814
105814
|
path: relativePath,
|
|
105815
|
-
ownership: "
|
|
105815
|
+
ownership: "gck-modified",
|
|
105816
105816
|
action: "preserve",
|
|
105817
105817
|
reason: "modified by user"
|
|
105818
105818
|
});
|
|
@@ -105872,6 +105872,7 @@ function logCleanupSummary(deletedCount, preservedCount, dryRun, results) {
|
|
|
105872
105872
|
|
|
105873
105873
|
// src/services/transformers/commands-prefix/prefix-cleaner.ts
|
|
105874
105874
|
var KIT_PREFIX_MAP = {
|
|
105875
|
+
gck: "engineer",
|
|
105875
105876
|
ck: "engineer",
|
|
105876
105877
|
mkt: "marketing"
|
|
105877
105878
|
};
|
|
@@ -107859,10 +107860,10 @@ async function analyzeFreshInstallation(claudeDir) {
|
|
|
107859
107860
|
const userFiles = [];
|
|
107860
107861
|
for (const file of allFiles) {
|
|
107861
107862
|
switch (file.ownership) {
|
|
107862
|
-
case "
|
|
107863
|
+
case "gck":
|
|
107863
107864
|
ckFiles.push(file);
|
|
107864
107865
|
break;
|
|
107865
|
-
case "
|
|
107866
|
+
case "gck-modified":
|
|
107866
107867
|
ckModifiedFiles.push(file);
|
|
107867
107868
|
break;
|
|
107868
107869
|
case "user":
|
|
@@ -111443,7 +111444,7 @@ async function migrateCommand(options2) {
|
|
|
111443
111444
|
console.log(line);
|
|
111444
111445
|
}
|
|
111445
111446
|
}
|
|
111446
|
-
await Promise.resolve().then(() =>
|
|
111447
|
+
await Promise.resolve().then(() => init_gck_config_manager());
|
|
111447
111448
|
const ckConfigResult = await CkConfigManager.loadFull(process.cwd());
|
|
111448
111449
|
setTaxonomyOverrides(ckConfigResult.config.modelTaxonomy);
|
|
111449
111450
|
const reconcileSpinner = de();
|
|
@@ -114279,10 +114280,10 @@ function normalizeTrackedPath(relativePath) {
|
|
|
114279
114280
|
return relativePath.replace(/\\/g, "/");
|
|
114280
114281
|
}
|
|
114281
114282
|
function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
|
|
114282
|
-
if (ownership === "
|
|
114283
|
+
if (ownership === "gck") {
|
|
114283
114284
|
return { action: "delete", reason: deleteReason };
|
|
114284
114285
|
}
|
|
114285
|
-
if (ownership === "
|
|
114286
|
+
if (ownership === "gck-modified") {
|
|
114286
114287
|
if (forceOverwrite) {
|
|
114287
114288
|
return { action: "delete", reason: "force overwrite" };
|
|
114288
114289
|
}
|
|
@@ -115235,7 +115236,7 @@ Branch \`${branchName}\` may have partial work.`, options2.showBranding, false).
|
|
|
115235
115236
|
}
|
|
115236
115237
|
async function invokeImplementation(options2) {
|
|
115237
115238
|
const { issueNumber, issueTitle, planPath, timeoutSec, cwd } = options2;
|
|
115238
|
-
const prompt = `/
|
|
115239
|
+
const prompt = `/gck:cook --auto ${planPath}
|
|
115239
115240
|
|
|
115240
115241
|
After completing the implementation:
|
|
115241
115242
|
1. Stage all changes: git add -A
|
|
@@ -115567,7 +115568,7 @@ function parseStreamJsonOutput(stdout) {
|
|
|
115567
115568
|
return parseClaudeOutput(stdout);
|
|
115568
115569
|
}
|
|
115569
115570
|
function buildBrainstormPrompt(issue, repoName, skillsAvailable) {
|
|
115570
|
-
const skillPrefix = skillsAvailable ? "/
|
|
115571
|
+
const skillPrefix = skillsAvailable ? "/gck:brainstorm " : "";
|
|
115571
115572
|
return `${skillPrefix}Analyze this GitHub issue for the project "${repoName}".
|
|
115572
115573
|
|
|
115573
115574
|
## Issue #${issue.number}: ${issue.title}
|
|
@@ -115898,7 +115899,7 @@ function buildPlanPrompt(issue, repoName, conversationHistory) {
|
|
|
115898
115899
|
---
|
|
115899
115900
|
|
|
115900
115901
|
`);
|
|
115901
|
-
return `/
|
|
115902
|
+
return `/gck:plan --fast Create an implementation plan for GitHub issue #${issue.number} in "${repoName}".
|
|
115902
115903
|
|
|
115903
115904
|
CRITICAL LANGUAGE RULE: Detect what language the conversation history is written in, then respond ONLY in that same language. Do NOT use your system locale or any other language preference.
|
|
115904
115905
|
|
|
@@ -115910,7 +115911,7 @@ ${sanitizedHistory}
|
|
|
115910
115911
|
</untrusted-content>
|
|
115911
115912
|
|
|
115912
115913
|
IMPORTANT INSTRUCTIONS:
|
|
115913
|
-
- Create the plan in the project's plans/ directory following standard /
|
|
115914
|
+
- Create the plan in the project's plans/ directory following standard /gck:plan structure
|
|
115914
115915
|
- The plan directory should include issue number in the name (e.g., plans/YYMMDD-issue-{number}-{slug}/)
|
|
115915
115916
|
- Create plan.md overview + phase-XX-*.md files for each phase
|
|
115916
115917
|
- After creating plan files, provide a summary suitable for posting as a GitHub comment
|
|
@@ -116217,7 +116218,7 @@ function cleanExpiredIssues(state, ttlDays) {
|
|
|
116217
116218
|
}
|
|
116218
116219
|
|
|
116219
116220
|
// src/commands/watch/phases/state-manager.ts
|
|
116220
|
-
|
|
116221
|
+
init_gck_config_manager();
|
|
116221
116222
|
init_file_io();
|
|
116222
116223
|
init_logger();
|
|
116223
116224
|
import { existsSync as existsSync74 } from "node:fs";
|
|
@@ -116786,13 +116787,13 @@ function sleep(ms) {
|
|
|
116786
116787
|
// src/cli/command-registry.ts
|
|
116787
116788
|
init_logger();
|
|
116788
116789
|
function registerCommands(cli) {
|
|
116789
|
-
cli.command("new", "Bootstrap a new ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use: engineer, marketing, all, or comma-separated").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--force", "Overwrite existing files without confirmation").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--opencode", "Install OpenCode CLI package (non-interactive mode)").option("--gemini", "Install Google Gemini CLI package (non-interactive mode)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /
|
|
116790
|
+
cli.command("new", "Bootstrap a new ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use: engineer, marketing, all, or comma-separated").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--force", "Overwrite existing files without confirmation").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--opencode", "Install OpenCode CLI package (non-interactive mode)").option("--gemini", "Install Google Gemini CLI package (non-interactive mode)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /gck: prefix to all slash commands by moving them to commands/gck/ subdirectory").option("--beta", "Show beta versions in selection prompt").option("--refresh", "Bypass release cache to fetch latest versions from GitHub").option("--docs-dir <name>", "Custom docs folder name (default: docs)").option("--plans-dir <name>", "Custom plans folder name (default: plans)").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--use-git", "Use git clone instead of GitHub API (uses SSH/HTTPS credentials)").option("--archive <path>", "Use local archive file instead of downloading (zip/tar.gz)").option("--kit-path <path>", "Use local kit directory instead of downloading").action(async (options2) => {
|
|
116790
116791
|
if (options2.exclude && !Array.isArray(options2.exclude)) {
|
|
116791
116792
|
options2.exclude = [options2.exclude];
|
|
116792
116793
|
}
|
|
116793
116794
|
await newCommand(options2);
|
|
116794
116795
|
});
|
|
116795
|
-
cli.command("init", "Initialize or update ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use: engineer, marketing, all, or comma-separated").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--only <pattern>", "Include only files matching glob pattern (can be used multiple times)").option("-g, --global", "Use platform-specific user configuration directory").option("--fresh", "Full reset: remove CK files, replace settings.json and CLAUDE.md, reinstall from scratch").option("--force", "Force reinstall even if already at latest version (use with --yes; re-onboards missing files without full reset)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /
|
|
116796
|
+
cli.command("init", "Initialize or update ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use: engineer, marketing, all, or comma-separated").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--only <pattern>", "Include only files matching glob pattern (can be used multiple times)").option("-g, --global", "Use platform-specific user configuration directory").option("--fresh", "Full reset: remove CK files, replace settings.json and CLAUDE.md, reinstall from scratch").option("--force", "Force reinstall even if already at latest version (use with --yes; re-onboards missing files without full reset)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /gck: prefix to all slash commands by moving them to commands/gck/ subdirectory").option("--beta", "Show beta versions in selection prompt").option("--refresh", "Bypass release cache to fetch latest versions from GitHub").option("--dry-run", "Preview changes without applying them (requires --prefix)").option("--force-overwrite", "Override ownership protections and delete user-modified files (requires --prefix)").option("--force-overwrite-settings", "Fully replace settings.json instead of selective merge (destroys user customizations)").option("--restore-ck-hooks", "Restore CK-managed hook registrations during update self-heal").option("--skip-setup", "Skip interactive configuration wizard").option("--docs-dir <name>", "Custom docs folder name (default: docs)").option("--plans-dir <name>", "Custom plans folder name (default: plans)").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--sync", "Sync config files from upstream with interactive hunk-by-hunk merge").option("--use-git", "Use git clone instead of GitHub API (uses SSH/HTTPS credentials)").option("--archive <path>", "Use local archive file instead of downloading (zip/tar.gz)").option("--kit-path <path>", "Use local kit directory instead of downloading").action(async (options2) => {
|
|
116796
116797
|
if (options2.exclude && !Array.isArray(options2.exclude)) {
|
|
116797
116798
|
options2.exclude = [options2.exclude];
|
|
116798
116799
|
}
|