claudekit-cli 3.41.4-dev.47 → 3.41.4-dev.49
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 +19 -0
- package/cli-manifest.json +35 -8
- package/dist/index.js +891 -125
- package/dist/ui/assets/index-CRtJwrzd.css +1 -0
- package/dist/ui/assets/index-DyvVqUTe.js +311 -0
- package/dist/ui/index.html +2 -2
- package/package.json +4 -1
- package/dist/ui/assets/index-CKG4dpEV.js +0 -311
- package/dist/ui/assets/index-D3aRuZaq.css +0 -1
package/dist/index.js
CHANGED
|
@@ -12416,6 +12416,50 @@ function normalizeChecksum(checksum) {
|
|
|
12416
12416
|
function isUnknownChecksum(checksum) {
|
|
12417
12417
|
return normalizeChecksum(checksum) === UNKNOWN_CHECKSUM;
|
|
12418
12418
|
}
|
|
12419
|
+
function getReasonCopy(code, _ctx) {
|
|
12420
|
+
switch (code) {
|
|
12421
|
+
case "new-item":
|
|
12422
|
+
return "New — not previously installed";
|
|
12423
|
+
case "new-provider-for-item":
|
|
12424
|
+
return "New provider for existing item";
|
|
12425
|
+
case "target-deleted-source-changed":
|
|
12426
|
+
return "You deleted this, CK has updates — reinstalling";
|
|
12427
|
+
case "target-dir-empty-reinstall":
|
|
12428
|
+
return "Provider directory is empty — reinstalling";
|
|
12429
|
+
case "force-reinstall":
|
|
12430
|
+
return "Force reinstall (target was deleted)";
|
|
12431
|
+
case "force-overwrite":
|
|
12432
|
+
return "Force overwrite (you edited this, --force active)";
|
|
12433
|
+
case "registry-upgrade-reinstall":
|
|
12434
|
+
return "Target deleted — reinstalling after registry upgrade";
|
|
12435
|
+
case "source-changed":
|
|
12436
|
+
return "CK updated, you didn't edit — safe to overwrite";
|
|
12437
|
+
case "registry-upgrade-heal":
|
|
12438
|
+
return "Healing stale target after registry upgrade";
|
|
12439
|
+
case "no-changes":
|
|
12440
|
+
return "Already up to date";
|
|
12441
|
+
case "user-edits-preserved":
|
|
12442
|
+
return "You edited this, CK unchanged — keeping your edits";
|
|
12443
|
+
case "user-deleted-respected":
|
|
12444
|
+
return "You deleted this, CK unchanged — respecting your choice";
|
|
12445
|
+
case "target-up-to-date-backfill":
|
|
12446
|
+
return "Already up to date — registry checksums will be backfilled";
|
|
12447
|
+
case "provider-checksum-unavailable":
|
|
12448
|
+
return "Provider checksum unavailable — cannot verify safely";
|
|
12449
|
+
case "target-state-unknown":
|
|
12450
|
+
return "Target state unavailable, CK unchanged — preserving target";
|
|
12451
|
+
case "source-removed-orphan":
|
|
12452
|
+
return "No longer shipped by CK — will be removed";
|
|
12453
|
+
case "renamed-cleanup":
|
|
12454
|
+
return "Renamed — cleaning up old path";
|
|
12455
|
+
case "path-migrated-cleanup":
|
|
12456
|
+
return "Path migrated — cleaning up old location";
|
|
12457
|
+
case "both-changed":
|
|
12458
|
+
return "Both you and CK changed this — pick one";
|
|
12459
|
+
case "target-state-unknown-source-changed":
|
|
12460
|
+
return "Target state unavailable while CK changed — manual review required";
|
|
12461
|
+
}
|
|
12462
|
+
}
|
|
12419
12463
|
var UNKNOWN_CHECKSUM = "unknown";
|
|
12420
12464
|
|
|
12421
12465
|
// src/commands/portable/portable-registry.ts
|
|
@@ -53250,24 +53294,37 @@ async function _ensureFeatureFlagLocked(configTomlPath) {
|
|
|
53250
53294
|
};
|
|
53251
53295
|
}
|
|
53252
53296
|
}
|
|
53253
|
-
const
|
|
53254
|
-
|
|
53255
|
-
|
|
53256
|
-
|
|
53297
|
+
const { content: stripped, removed: hadManagedBlock } = stripAllManagedBlocks(existing);
|
|
53298
|
+
let content = stripped;
|
|
53299
|
+
let mutated = hadManagedBlock;
|
|
53300
|
+
const featuresHeaderIdx = findFeaturesSectionStart(content);
|
|
53301
|
+
if (featuresHeaderIdx !== -1) {
|
|
53302
|
+
const { updated, changed } = ensureFlagInFeaturesSection(content, featuresHeaderIdx);
|
|
53303
|
+
content = updated;
|
|
53304
|
+
mutated = mutated || changed;
|
|
53305
|
+
if (!mutated) {
|
|
53306
|
+
return { status: "already-set", configPath: configTomlPath };
|
|
53307
|
+
}
|
|
53308
|
+
try {
|
|
53309
|
+
await atomicWrite(configTomlPath, content);
|
|
53310
|
+
} catch (err) {
|
|
53311
|
+
return {
|
|
53312
|
+
status: "failed",
|
|
53313
|
+
configPath: configTomlPath,
|
|
53314
|
+
error: `Failed to write ${configTomlPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
53315
|
+
};
|
|
53316
|
+
}
|
|
53257
53317
|
return { status: "updated", configPath: configTomlPath };
|
|
53258
53318
|
}
|
|
53259
|
-
|
|
53260
|
-
return { status: "already-set", configPath: configTomlPath };
|
|
53261
|
-
}
|
|
53262
|
-
const separator = existing.length > 0 && !existing.endsWith(`
|
|
53319
|
+
const separator = content.length === 0 ? "" : content.endsWith(`
|
|
53263
53320
|
`) ? `
|
|
53264
|
-
|
|
53265
53321
|
` : `
|
|
53322
|
+
|
|
53266
53323
|
`;
|
|
53267
|
-
const
|
|
53324
|
+
const withBlock = `${content}${separator}${MANAGED_BLOCK}
|
|
53268
53325
|
`;
|
|
53269
53326
|
try {
|
|
53270
|
-
await atomicWrite(configTomlPath,
|
|
53327
|
+
await atomicWrite(configTomlPath, withBlock);
|
|
53271
53328
|
} catch (err) {
|
|
53272
53329
|
return {
|
|
53273
53330
|
status: "failed",
|
|
@@ -53275,34 +53332,80 @@ async function _ensureFeatureFlagLocked(configTomlPath) {
|
|
|
53275
53332
|
error: `Failed to write ${configTomlPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
53276
53333
|
};
|
|
53277
53334
|
}
|
|
53278
|
-
return { status: "written", configPath: configTomlPath };
|
|
53335
|
+
return { status: hadManagedBlock ? "updated" : "written", configPath: configTomlPath };
|
|
53279
53336
|
}
|
|
53280
|
-
function
|
|
53281
|
-
const
|
|
53282
|
-
return
|
|
53337
|
+
function findFeaturesSectionStart(content) {
|
|
53338
|
+
const match = /^[ \t]*\[features\][ \t]*(?:#[^\r\n]*)?$/m.exec(content);
|
|
53339
|
+
return match ? match.index : -1;
|
|
53283
53340
|
}
|
|
53284
|
-
function
|
|
53285
|
-
const
|
|
53286
|
-
|
|
53287
|
-
|
|
53288
|
-
|
|
53341
|
+
function ensureFlagInFeaturesSection(content, headerStartIdx) {
|
|
53342
|
+
const headerLineEnd = content.indexOf(`
|
|
53343
|
+
`, headerStartIdx);
|
|
53344
|
+
const bodyStart = headerLineEnd === -1 ? content.length : headerLineEnd + 1;
|
|
53345
|
+
const rest = content.slice(bodyStart);
|
|
53346
|
+
const nextHeaderMatch = /\n\[[^\]]+\]/.exec(rest);
|
|
53347
|
+
const bodyEnd = nextHeaderMatch ? bodyStart + nextHeaderMatch.index + 1 : content.length;
|
|
53348
|
+
const body = content.slice(bodyStart, bodyEnd);
|
|
53349
|
+
const flagRegex = /^([ \t]*codex_hooks[ \t]*=[ \t]*)(true|false)([ \t]*#[^\r\n]*)?[ \t]*$/m;
|
|
53350
|
+
const flagMatch = flagRegex.exec(body);
|
|
53351
|
+
if (flagMatch) {
|
|
53352
|
+
if (flagMatch[2] === "true") {
|
|
53353
|
+
return { updated: content, changed: false };
|
|
53354
|
+
}
|
|
53355
|
+
const newBody = body.replace(flagRegex, (_m, prefix, _v, trailing) => `${prefix}true${trailing ?? ""}`);
|
|
53356
|
+
return {
|
|
53357
|
+
updated: content.slice(0, bodyStart) + newBody + content.slice(bodyEnd),
|
|
53358
|
+
changed: true
|
|
53359
|
+
};
|
|
53289
53360
|
}
|
|
53290
|
-
|
|
53291
|
-
|
|
53292
|
-
|
|
53293
|
-
|
|
53294
|
-
|
|
53295
|
-
|
|
53296
|
-
|
|
53297
|
-
|
|
53298
|
-
|
|
53299
|
-
|
|
53300
|
-
|
|
53301
|
-
|
|
53302
|
-
|
|
53303
|
-
const
|
|
53304
|
-
`
|
|
53305
|
-
|
|
53361
|
+
if (headerLineEnd === -1) {
|
|
53362
|
+
return { updated: `${content}
|
|
53363
|
+
codex_hooks = true
|
|
53364
|
+
`, changed: true };
|
|
53365
|
+
}
|
|
53366
|
+
let insertAt = bodyEnd;
|
|
53367
|
+
while (insertAt > bodyStart && content[insertAt - 1] === `
|
|
53368
|
+
` && content[insertAt - 2] === `
|
|
53369
|
+
`) {
|
|
53370
|
+
insertAt -= 1;
|
|
53371
|
+
}
|
|
53372
|
+
const needsLeadingNewline = insertAt > bodyStart && content[insertAt - 1] !== `
|
|
53373
|
+
`;
|
|
53374
|
+
const insertion = `${needsLeadingNewline ? `
|
|
53375
|
+
` : ""}codex_hooks = true
|
|
53376
|
+
`;
|
|
53377
|
+
return {
|
|
53378
|
+
updated: content.slice(0, insertAt) + insertion + content.slice(insertAt),
|
|
53379
|
+
changed: true
|
|
53380
|
+
};
|
|
53381
|
+
}
|
|
53382
|
+
function stripAllManagedBlocks(content) {
|
|
53383
|
+
let result = content;
|
|
53384
|
+
let removed = false;
|
|
53385
|
+
while (true) {
|
|
53386
|
+
const startIdx = result.indexOf(SENTINEL_START2);
|
|
53387
|
+
if (startIdx === -1)
|
|
53388
|
+
break;
|
|
53389
|
+
const endIdx = result.indexOf(SENTINEL_END2, startIdx);
|
|
53390
|
+
if (endIdx === -1)
|
|
53391
|
+
break;
|
|
53392
|
+
const endOfBlock = endIdx + SENTINEL_END2.length;
|
|
53393
|
+
const afterBlockStart = result[endOfBlock] === `
|
|
53394
|
+
` ? endOfBlock + 1 : endOfBlock;
|
|
53395
|
+
let beforeBlockEnd = startIdx;
|
|
53396
|
+
if (beforeBlockEnd >= 1 && result[beforeBlockEnd - 1] === `
|
|
53397
|
+
`) {
|
|
53398
|
+
beforeBlockEnd -= 1;
|
|
53399
|
+
if (beforeBlockEnd >= 1 && result[beforeBlockEnd - 1] === `
|
|
53400
|
+
`) {
|
|
53401
|
+
beforeBlockEnd -= 1;
|
|
53402
|
+
}
|
|
53403
|
+
beforeBlockEnd += 1;
|
|
53404
|
+
}
|
|
53405
|
+
result = result.slice(0, beforeBlockEnd) + result.slice(afterBlockStart);
|
|
53406
|
+
removed = true;
|
|
53407
|
+
}
|
|
53408
|
+
return { content: result, removed };
|
|
53306
53409
|
}
|
|
53307
53410
|
async function atomicWrite(filePath, content) {
|
|
53308
53411
|
const tempPath = `${filePath}.ck-tmp`;
|
|
@@ -54298,7 +54401,7 @@ var init_reconcile_registry_backfill = __esm(() => {
|
|
|
54298
54401
|
});
|
|
54299
54402
|
|
|
54300
54403
|
// src/commands/portable/reconcile-state-builders.ts
|
|
54301
|
-
import { existsSync as existsSync27 } from "node:fs";
|
|
54404
|
+
import { existsSync as existsSync27, readdirSync as readdirSync3, statSync as statSync5 } from "node:fs";
|
|
54302
54405
|
import { readFile as readFile23 } from "node:fs/promises";
|
|
54303
54406
|
function getProviderPathKeyForPortableType2(type) {
|
|
54304
54407
|
switch (type) {
|
|
@@ -54394,6 +54497,95 @@ function buildSourceItemState(item, type, selectedProviders, options2) {
|
|
|
54394
54497
|
targetChecksums
|
|
54395
54498
|
};
|
|
54396
54499
|
}
|
|
54500
|
+
function buildTypeDirectoryStates(providerConfigs, types4) {
|
|
54501
|
+
const results = [];
|
|
54502
|
+
for (const { provider, global: isGlobal } of providerConfigs) {
|
|
54503
|
+
const providerConfig = providers[provider];
|
|
54504
|
+
if (!providerConfig)
|
|
54505
|
+
continue;
|
|
54506
|
+
for (const type of types4) {
|
|
54507
|
+
const pathKey = portableTypeToProviderPathKey(type);
|
|
54508
|
+
const pathConfig = providerConfig[pathKey];
|
|
54509
|
+
if (!pathConfig)
|
|
54510
|
+
continue;
|
|
54511
|
+
if (pathConfig.writeStrategy === "merge-single" || pathConfig.writeStrategy === "single-file") {
|
|
54512
|
+
continue;
|
|
54513
|
+
}
|
|
54514
|
+
const dirPath = isGlobal ? pathConfig.globalPath : pathConfig.projectPath;
|
|
54515
|
+
if (!dirPath)
|
|
54516
|
+
continue;
|
|
54517
|
+
const exists = existsSync27(dirPath);
|
|
54518
|
+
if (!exists) {
|
|
54519
|
+
results.push({
|
|
54520
|
+
provider,
|
|
54521
|
+
type,
|
|
54522
|
+
global: isGlobal,
|
|
54523
|
+
path: dirPath,
|
|
54524
|
+
exists: false,
|
|
54525
|
+
isEmpty: true,
|
|
54526
|
+
fileCount: 0
|
|
54527
|
+
});
|
|
54528
|
+
continue;
|
|
54529
|
+
}
|
|
54530
|
+
let stat7 = null;
|
|
54531
|
+
try {
|
|
54532
|
+
stat7 = statSync5(dirPath);
|
|
54533
|
+
} catch {
|
|
54534
|
+
results.push({
|
|
54535
|
+
provider,
|
|
54536
|
+
type,
|
|
54537
|
+
global: isGlobal,
|
|
54538
|
+
path: dirPath,
|
|
54539
|
+
exists: false,
|
|
54540
|
+
isEmpty: true,
|
|
54541
|
+
fileCount: 0
|
|
54542
|
+
});
|
|
54543
|
+
continue;
|
|
54544
|
+
}
|
|
54545
|
+
if (!stat7.isDirectory()) {
|
|
54546
|
+
results.push({
|
|
54547
|
+
provider,
|
|
54548
|
+
type,
|
|
54549
|
+
global: isGlobal,
|
|
54550
|
+
path: dirPath,
|
|
54551
|
+
exists: true,
|
|
54552
|
+
isEmpty: false,
|
|
54553
|
+
fileCount: 1
|
|
54554
|
+
});
|
|
54555
|
+
continue;
|
|
54556
|
+
}
|
|
54557
|
+
const ext = pathConfig.fileExtension ?? "";
|
|
54558
|
+
let entries = [];
|
|
54559
|
+
try {
|
|
54560
|
+
entries = readdirSync3(dirPath);
|
|
54561
|
+
} catch {
|
|
54562
|
+
results.push({
|
|
54563
|
+
provider,
|
|
54564
|
+
type,
|
|
54565
|
+
global: isGlobal,
|
|
54566
|
+
path: dirPath,
|
|
54567
|
+
exists: true,
|
|
54568
|
+
isEmpty: true,
|
|
54569
|
+
fileCount: 0
|
|
54570
|
+
});
|
|
54571
|
+
continue;
|
|
54572
|
+
}
|
|
54573
|
+
const managedFiles = ext === "" ? entries.filter((f3) => {
|
|
54574
|
+
return f3.endsWith(".json") || f3.endsWith(".sh") || f3.endsWith(".js");
|
|
54575
|
+
}) : entries.filter((f3) => f3.endsWith(ext));
|
|
54576
|
+
results.push({
|
|
54577
|
+
provider,
|
|
54578
|
+
type,
|
|
54579
|
+
global: isGlobal,
|
|
54580
|
+
path: dirPath,
|
|
54581
|
+
exists: true,
|
|
54582
|
+
isEmpty: managedFiles.length === 0,
|
|
54583
|
+
fileCount: managedFiles.length
|
|
54584
|
+
});
|
|
54585
|
+
}
|
|
54586
|
+
}
|
|
54587
|
+
return results;
|
|
54588
|
+
}
|
|
54397
54589
|
async function buildTargetStates(entries, options2) {
|
|
54398
54590
|
const targetStates = new Map;
|
|
54399
54591
|
const entriesByPath = new Map;
|
|
@@ -54422,11 +54614,13 @@ async function buildTargetStates(entries, options2) {
|
|
|
54422
54614
|
}
|
|
54423
54615
|
return targetStates;
|
|
54424
54616
|
}
|
|
54617
|
+
var portableTypeToProviderPathKey;
|
|
54425
54618
|
var init_reconcile_state_builders = __esm(() => {
|
|
54426
54619
|
init_checksum_utils();
|
|
54427
54620
|
init_converters();
|
|
54428
54621
|
init_merge_single_sections();
|
|
54429
54622
|
init_provider_registry();
|
|
54623
|
+
portableTypeToProviderPathKey = getProviderPathKeyForPortableType2;
|
|
54430
54624
|
});
|
|
54431
54625
|
|
|
54432
54626
|
// src/commands/portable/reconciler.ts
|
|
@@ -54479,6 +54673,9 @@ function makeItemTypeKey(item, type) {
|
|
|
54479
54673
|
function makeRegistryIdentityKey(entry) {
|
|
54480
54674
|
return JSON.stringify([entry.item, entry.type, entry.provider, entry.global]);
|
|
54481
54675
|
}
|
|
54676
|
+
function makeDirStateKey(provider, type, global3) {
|
|
54677
|
+
return JSON.stringify([provider, type, global3]);
|
|
54678
|
+
}
|
|
54482
54679
|
function dedupeProviderConfigs(providerConfigs) {
|
|
54483
54680
|
const seen = new Set;
|
|
54484
54681
|
const unique = [];
|
|
@@ -54505,6 +54702,14 @@ function buildTargetStateIndex(targetStates) {
|
|
|
54505
54702
|
}
|
|
54506
54703
|
return index;
|
|
54507
54704
|
}
|
|
54705
|
+
function buildDirStateIndex(dirStates) {
|
|
54706
|
+
const index = new Map;
|
|
54707
|
+
for (const ds of dirStates) {
|
|
54708
|
+
const key = makeDirStateKey(ds.provider, ds.type, ds.global);
|
|
54709
|
+
index.set(key, ds);
|
|
54710
|
+
}
|
|
54711
|
+
return index;
|
|
54712
|
+
}
|
|
54508
54713
|
function lookupTargetState(targetStateIndex, pathValue) {
|
|
54509
54714
|
return targetStateIndex.get(normalizePortablePath(pathValue));
|
|
54510
54715
|
}
|
|
@@ -54588,6 +54793,66 @@ function suppressOverlappingActions(actions) {
|
|
|
54588
54793
|
}
|
|
54589
54794
|
return filtered;
|
|
54590
54795
|
}
|
|
54796
|
+
function applyEmptyDirOverride(actions, dirStates, respectDeletions) {
|
|
54797
|
+
if (dirStates.length === 0) {
|
|
54798
|
+
return { actions, banners: [] };
|
|
54799
|
+
}
|
|
54800
|
+
const dirIndex = buildDirStateIndex(dirStates);
|
|
54801
|
+
const banners = [];
|
|
54802
|
+
const flippedGroups = new Map;
|
|
54803
|
+
for (const action of actions) {
|
|
54804
|
+
if (action.action !== "skip" || action.reasonCode !== "user-deleted-respected") {
|
|
54805
|
+
continue;
|
|
54806
|
+
}
|
|
54807
|
+
const key = makeDirStateKey(action.provider, action.type, action.global);
|
|
54808
|
+
const dirState = dirIndex.get(key);
|
|
54809
|
+
if (!dirState?.isEmpty)
|
|
54810
|
+
continue;
|
|
54811
|
+
if (respectDeletions) {
|
|
54812
|
+
const existing2 = flippedGroups.get(key);
|
|
54813
|
+
if (existing2) {
|
|
54814
|
+
existing2.count++;
|
|
54815
|
+
} else {
|
|
54816
|
+
flippedGroups.set(key, { dirState, count: 1 });
|
|
54817
|
+
}
|
|
54818
|
+
continue;
|
|
54819
|
+
}
|
|
54820
|
+
action.action = "install";
|
|
54821
|
+
action.reasonCode = "target-dir-empty-reinstall";
|
|
54822
|
+
action.reasonCopy = getReasonCopy("target-dir-empty-reinstall");
|
|
54823
|
+
action.reason = action.reasonCopy;
|
|
54824
|
+
const existing = flippedGroups.get(key);
|
|
54825
|
+
if (existing) {
|
|
54826
|
+
existing.count++;
|
|
54827
|
+
} else {
|
|
54828
|
+
flippedGroups.set(key, { dirState, count: 1 });
|
|
54829
|
+
}
|
|
54830
|
+
}
|
|
54831
|
+
for (const [, { dirState, count }] of flippedGroups) {
|
|
54832
|
+
if (respectDeletions) {
|
|
54833
|
+
banners.push({
|
|
54834
|
+
kind: "empty-dir-respected",
|
|
54835
|
+
provider: dirState.provider,
|
|
54836
|
+
type: dirState.type,
|
|
54837
|
+
global: dirState.global,
|
|
54838
|
+
path: dirState.path,
|
|
54839
|
+
itemCount: count,
|
|
54840
|
+
message: `Detected empty ${dirState.path} — respecting your deletions (${count} items skipped).`
|
|
54841
|
+
});
|
|
54842
|
+
} else {
|
|
54843
|
+
banners.push({
|
|
54844
|
+
kind: "empty-dir",
|
|
54845
|
+
provider: dirState.provider,
|
|
54846
|
+
type: dirState.type,
|
|
54847
|
+
global: dirState.global,
|
|
54848
|
+
path: dirState.path,
|
|
54849
|
+
itemCount: count,
|
|
54850
|
+
message: `Detected empty ${dirState.path} — ${count} item${count === 1 ? "" : "s"} will be reinstalled. Uncheck any to skip.`
|
|
54851
|
+
});
|
|
54852
|
+
}
|
|
54853
|
+
}
|
|
54854
|
+
return { actions, banners };
|
|
54855
|
+
}
|
|
54591
54856
|
function reconcile(input) {
|
|
54592
54857
|
const actions = [];
|
|
54593
54858
|
const targetStateIndex = buildTargetStateIndex(input.targetStates);
|
|
@@ -54617,7 +54882,10 @@ function reconcile(input) {
|
|
|
54617
54882
|
const orphanActions = detectOrphans(input, renamedFromKeys);
|
|
54618
54883
|
actions.push(...orphanActions);
|
|
54619
54884
|
const normalizedActions = suppressOverlappingActions(dedupeActions(actions));
|
|
54620
|
-
|
|
54885
|
+
const dirStates = input.typeDirectoryStates ?? [];
|
|
54886
|
+
const respectDeletions = input.respectDeletions ?? false;
|
|
54887
|
+
const { actions: finalActions, banners } = applyEmptyDirOverride(normalizedActions, dirStates, respectDeletions);
|
|
54888
|
+
return buildPlan(finalActions, banners);
|
|
54621
54889
|
}
|
|
54622
54890
|
function determineAction(source, providerConfig, input, targetStateIndex, deletedIdentityKeys) {
|
|
54623
54891
|
let registryEntry = findRegistryEntry(source, providerConfig, input.registry);
|
|
@@ -54630,12 +54898,14 @@ function determineAction(source, providerConfig, input, targetStateIndex, delete
|
|
|
54630
54898
|
if (registryEntry && deletedIdentityKeys.has(identityKey)) {
|
|
54631
54899
|
registryEntry = null;
|
|
54632
54900
|
}
|
|
54901
|
+
const isDirectoryItem = source.type === "skill";
|
|
54633
54902
|
const common = {
|
|
54634
54903
|
item: source.item,
|
|
54635
54904
|
type: source.type,
|
|
54636
54905
|
provider: providerConfig.provider,
|
|
54637
54906
|
global: providerConfig.global,
|
|
54638
|
-
targetPath: ""
|
|
54907
|
+
targetPath: "",
|
|
54908
|
+
isDirectoryItem: isDirectoryItem || undefined
|
|
54639
54909
|
};
|
|
54640
54910
|
const convertedChecksumRaw = source.convertedChecksums[providerConfig.provider];
|
|
54641
54911
|
const convertedChecksum = normalizeChecksum(convertedChecksumRaw);
|
|
@@ -54643,30 +54913,39 @@ function determineAction(source, providerConfig, input, targetStateIndex, delete
|
|
|
54643
54913
|
if (!convertedChecksumRaw || isUnknownChecksum(convertedChecksumRaw)) {
|
|
54644
54914
|
if (registryEntry) {
|
|
54645
54915
|
common.targetPath = registryEntry.path;
|
|
54916
|
+
const code3 = "provider-checksum-unavailable";
|
|
54646
54917
|
return {
|
|
54647
54918
|
...common,
|
|
54648
54919
|
action: "skip",
|
|
54649
54920
|
reason: "Provider checksum unavailable — cannot verify safely",
|
|
54921
|
+
reasonCode: code3,
|
|
54922
|
+
reasonCopy: getReasonCopy(code3),
|
|
54650
54923
|
sourceChecksum: UNKNOWN_CHECKSUM,
|
|
54651
54924
|
registeredSourceChecksum: normalizeChecksum(registryEntry.sourceChecksum),
|
|
54652
54925
|
registeredTargetChecksum: normalizeChecksum(registryEntry.targetChecksum)
|
|
54653
54926
|
};
|
|
54654
54927
|
}
|
|
54655
54928
|
const itemExistsElsewhere = input.registry.installations.some((i) => i.item === source.item && i.type === source.type);
|
|
54929
|
+
const code2 = itemExistsElsewhere ? "new-provider-for-item" : "new-item";
|
|
54656
54930
|
return {
|
|
54657
54931
|
...common,
|
|
54658
54932
|
action: "install",
|
|
54659
54933
|
reason: itemExistsElsewhere ? "New provider for existing item" : "New item, not previously installed",
|
|
54934
|
+
reasonCode: code2,
|
|
54935
|
+
reasonCopy: getReasonCopy(code2),
|
|
54660
54936
|
sourceChecksum: UNKNOWN_CHECKSUM
|
|
54661
54937
|
};
|
|
54662
54938
|
}
|
|
54663
54939
|
if (!registryEntry) {
|
|
54664
54940
|
const itemExistsElsewhere = input.registry.installations.some((i) => i.item === source.item && i.type === source.type);
|
|
54941
|
+
const code2 = itemExistsElsewhere ? "new-provider-for-item" : "new-item";
|
|
54665
54942
|
const reason = itemExistsElsewhere ? "New provider for existing item" : "New item, not previously installed";
|
|
54666
54943
|
return {
|
|
54667
54944
|
...common,
|
|
54668
54945
|
action: "install",
|
|
54669
54946
|
reason,
|
|
54947
|
+
reasonCode: code2,
|
|
54948
|
+
reasonCopy: getReasonCopy(code2),
|
|
54670
54949
|
sourceChecksum: convertedChecksum
|
|
54671
54950
|
};
|
|
54672
54951
|
}
|
|
@@ -54678,36 +54957,48 @@ function determineAction(source, providerConfig, input, targetStateIndex, delete
|
|
|
54678
54957
|
const targetMatchesExpectedOutput = targetState?.exists === true && !isUnknownChecksum(expectedTargetChecksum) && currentTargetChecksum === expectedTargetChecksum;
|
|
54679
54958
|
if (isUnknownChecksum(registeredSourceChecksum)) {
|
|
54680
54959
|
if (targetMatchesExpectedOutput) {
|
|
54960
|
+
const code3 = "target-up-to-date-backfill";
|
|
54681
54961
|
return {
|
|
54682
54962
|
...common,
|
|
54683
54963
|
action: "skip",
|
|
54684
54964
|
reason: "Target up-to-date after registry upgrade — checksums will be backfilled",
|
|
54965
|
+
reasonCode: code3,
|
|
54966
|
+
reasonCopy: getReasonCopy(code3),
|
|
54685
54967
|
sourceChecksum: convertedChecksum,
|
|
54686
54968
|
currentTargetChecksum,
|
|
54687
54969
|
backfillRegistry: true
|
|
54688
54970
|
};
|
|
54689
54971
|
}
|
|
54690
54972
|
if (!targetState || !targetState.exists) {
|
|
54973
|
+
const code3 = "registry-upgrade-reinstall";
|
|
54691
54974
|
return {
|
|
54692
54975
|
...common,
|
|
54693
54976
|
action: "install",
|
|
54694
54977
|
reason: "Target deleted — reinstalling after registry upgrade",
|
|
54978
|
+
reasonCode: code3,
|
|
54979
|
+
reasonCopy: getReasonCopy(code3),
|
|
54695
54980
|
sourceChecksum: convertedChecksum
|
|
54696
54981
|
};
|
|
54697
54982
|
}
|
|
54983
|
+
const code2 = "registry-upgrade-heal";
|
|
54698
54984
|
return {
|
|
54699
54985
|
...common,
|
|
54700
54986
|
action: "update",
|
|
54701
54987
|
reason: "Healing stale target after registry upgrade",
|
|
54988
|
+
reasonCode: code2,
|
|
54989
|
+
reasonCopy: getReasonCopy(code2),
|
|
54702
54990
|
sourceChecksum: convertedChecksum,
|
|
54703
54991
|
currentTargetChecksum
|
|
54704
54992
|
};
|
|
54705
54993
|
}
|
|
54706
54994
|
if (targetMatchesExpectedOutput && (convertedChecksum !== registeredSourceChecksum || currentTargetChecksum !== registeredTargetChecksum)) {
|
|
54995
|
+
const code2 = "target-up-to-date-backfill";
|
|
54707
54996
|
return {
|
|
54708
54997
|
...common,
|
|
54709
54998
|
action: "skip",
|
|
54710
54999
|
reason: "Target up-to-date — registry checksums will be backfilled",
|
|
55000
|
+
reasonCode: code2,
|
|
55001
|
+
reasonCopy: getReasonCopy(code2),
|
|
54711
55002
|
sourceChecksum: convertedChecksum,
|
|
54712
55003
|
registeredSourceChecksum,
|
|
54713
55004
|
currentTargetChecksum,
|
|
@@ -54719,19 +55010,49 @@ function determineAction(source, providerConfig, input, targetStateIndex, delete
|
|
|
54719
55010
|
const targetChangeState = getTargetChangeState(targetState, registryEntry, registeredTargetChecksum);
|
|
54720
55011
|
if (targetChangeState === "deleted") {
|
|
54721
55012
|
const forceReinstall = input.force && !sourceChanged;
|
|
55013
|
+
if (sourceChanged) {
|
|
55014
|
+
const code3 = "target-deleted-source-changed";
|
|
55015
|
+
return {
|
|
55016
|
+
...common,
|
|
55017
|
+
action: "install",
|
|
55018
|
+
reason: "Target was deleted, CK has updates — reinstalling",
|
|
55019
|
+
reasonCode: code3,
|
|
55020
|
+
reasonCopy: getReasonCopy(code3),
|
|
55021
|
+
sourceChecksum: convertedChecksum,
|
|
55022
|
+
registeredSourceChecksum
|
|
55023
|
+
};
|
|
55024
|
+
}
|
|
55025
|
+
if (forceReinstall) {
|
|
55026
|
+
const code3 = "force-reinstall";
|
|
55027
|
+
return {
|
|
55028
|
+
...common,
|
|
55029
|
+
action: "install",
|
|
55030
|
+
reason: "Force reinstall (target was deleted)",
|
|
55031
|
+
reasonCode: code3,
|
|
55032
|
+
reasonCopy: getReasonCopy(code3),
|
|
55033
|
+
sourceChecksum: convertedChecksum,
|
|
55034
|
+
registeredSourceChecksum
|
|
55035
|
+
};
|
|
55036
|
+
}
|
|
55037
|
+
const code2 = "user-deleted-respected";
|
|
54722
55038
|
return {
|
|
54723
55039
|
...common,
|
|
54724
|
-
action:
|
|
54725
|
-
reason:
|
|
55040
|
+
action: "skip",
|
|
55041
|
+
reason: "Target was deleted by user, CK unchanged — respecting deletion",
|
|
55042
|
+
reasonCode: code2,
|
|
55043
|
+
reasonCopy: getReasonCopy(code2),
|
|
54726
55044
|
sourceChecksum: convertedChecksum,
|
|
54727
55045
|
registeredSourceChecksum
|
|
54728
55046
|
};
|
|
54729
55047
|
}
|
|
54730
55048
|
if (targetChangeState === "unknown") {
|
|
55049
|
+
const code2 = sourceChanged ? "target-state-unknown-source-changed" : "target-state-unknown";
|
|
54731
55050
|
return {
|
|
54732
55051
|
...common,
|
|
54733
55052
|
action: sourceChanged ? "conflict" : "skip",
|
|
54734
55053
|
reason: sourceChanged ? "Target state unavailable while CK changed — manual review required" : "Target state unavailable, CK unchanged — preserving target",
|
|
55054
|
+
reasonCode: code2,
|
|
55055
|
+
reasonCopy: getReasonCopy(code2),
|
|
54735
55056
|
sourceChecksum: convertedChecksum,
|
|
54736
55057
|
registeredSourceChecksum,
|
|
54737
55058
|
currentTargetChecksum,
|
|
@@ -54740,19 +55061,38 @@ function determineAction(source, providerConfig, input, targetStateIndex, delete
|
|
|
54740
55061
|
}
|
|
54741
55062
|
const targetChanged = targetChangeState === "changed";
|
|
54742
55063
|
if (!sourceChanged && !targetChanged) {
|
|
55064
|
+
const code2 = "no-changes";
|
|
54743
55065
|
return {
|
|
54744
55066
|
...common,
|
|
54745
55067
|
action: "skip",
|
|
54746
55068
|
reason: "No changes",
|
|
55069
|
+
reasonCode: code2,
|
|
55070
|
+
reasonCopy: getReasonCopy(code2),
|
|
54747
55071
|
sourceChecksum: convertedChecksum,
|
|
54748
55072
|
currentTargetChecksum
|
|
54749
55073
|
};
|
|
54750
55074
|
}
|
|
54751
55075
|
if (!sourceChanged && targetChanged) {
|
|
55076
|
+
if (input.force) {
|
|
55077
|
+
return {
|
|
55078
|
+
...common,
|
|
55079
|
+
action: "install",
|
|
55080
|
+
reason: "Force overwrite (user edits)",
|
|
55081
|
+
reasonCode: "force-overwrite",
|
|
55082
|
+
reasonCopy: getReasonCopy("force-overwrite"),
|
|
55083
|
+
sourceChecksum: convertedChecksum,
|
|
55084
|
+
registeredSourceChecksum,
|
|
55085
|
+
currentTargetChecksum,
|
|
55086
|
+
registeredTargetChecksum
|
|
55087
|
+
};
|
|
55088
|
+
}
|
|
55089
|
+
const code2 = "user-edits-preserved";
|
|
54752
55090
|
return {
|
|
54753
55091
|
...common,
|
|
54754
|
-
action:
|
|
54755
|
-
reason:
|
|
55092
|
+
action: "skip",
|
|
55093
|
+
reason: "User edited, CK unchanged — preserving edits",
|
|
55094
|
+
reasonCode: code2,
|
|
55095
|
+
reasonCopy: getReasonCopy(code2),
|
|
54756
55096
|
sourceChecksum: convertedChecksum,
|
|
54757
55097
|
registeredSourceChecksum,
|
|
54758
55098
|
currentTargetChecksum,
|
|
@@ -54760,20 +55100,26 @@ function determineAction(source, providerConfig, input, targetStateIndex, delete
|
|
|
54760
55100
|
};
|
|
54761
55101
|
}
|
|
54762
55102
|
if (sourceChanged && !targetChanged) {
|
|
55103
|
+
const code2 = "source-changed";
|
|
54763
55104
|
return {
|
|
54764
55105
|
...common,
|
|
54765
55106
|
action: "update",
|
|
54766
55107
|
reason: "CK updated, no user edits — safe overwrite",
|
|
55108
|
+
reasonCode: code2,
|
|
55109
|
+
reasonCopy: getReasonCopy(code2),
|
|
54767
55110
|
sourceChecksum: convertedChecksum,
|
|
54768
55111
|
registeredSourceChecksum,
|
|
54769
55112
|
currentTargetChecksum,
|
|
54770
55113
|
registeredTargetChecksum
|
|
54771
55114
|
};
|
|
54772
55115
|
}
|
|
55116
|
+
const code = "both-changed";
|
|
54773
55117
|
return {
|
|
54774
55118
|
...common,
|
|
54775
55119
|
action: "conflict",
|
|
54776
55120
|
reason: "Both CK and user modified this item",
|
|
55121
|
+
reasonCode: code,
|
|
55122
|
+
reasonCopy: getReasonCopy(code),
|
|
54777
55123
|
sourceChecksum: convertedChecksum,
|
|
54778
55124
|
registeredSourceChecksum,
|
|
54779
55125
|
currentTargetChecksum,
|
|
@@ -54809,6 +55155,7 @@ function detectOrphans(input, renamedFromKeys) {
|
|
|
54809
55155
|
if (entry.type === "config" && hasConfigSource)
|
|
54810
55156
|
continue;
|
|
54811
55157
|
if (!sourceItemKeys.has(sourceItemKey)) {
|
|
55158
|
+
const code = "source-removed-orphan";
|
|
54812
55159
|
actions.push({
|
|
54813
55160
|
action: "delete",
|
|
54814
55161
|
item: entry.item,
|
|
@@ -54816,7 +55163,9 @@ function detectOrphans(input, renamedFromKeys) {
|
|
|
54816
55163
|
provider: entry.provider,
|
|
54817
55164
|
global: entry.global,
|
|
54818
55165
|
targetPath: entry.path,
|
|
54819
|
-
reason: "Item no longer in CK source — orphaned"
|
|
55166
|
+
reason: "Item no longer in CK source — orphaned",
|
|
55167
|
+
reasonCode: code,
|
|
55168
|
+
reasonCopy: getReasonCopy(code)
|
|
54820
55169
|
});
|
|
54821
55170
|
}
|
|
54822
55171
|
}
|
|
@@ -54835,6 +55184,7 @@ function detectRenames(input) {
|
|
|
54835
55184
|
const normalizedFrom = normalizePortablePath(rename8.from);
|
|
54836
55185
|
const oldEntries = input.registry.installations.filter((e2) => normalizePortablePath(e2.sourcePath) === normalizedFrom);
|
|
54837
55186
|
for (const oldEntry of oldEntries) {
|
|
55187
|
+
const code = "renamed-cleanup";
|
|
54838
55188
|
actions.push({
|
|
54839
55189
|
deleteAction: {
|
|
54840
55190
|
action: "delete",
|
|
@@ -54844,6 +55194,8 @@ function detectRenames(input) {
|
|
|
54844
55194
|
global: oldEntry.global,
|
|
54845
55195
|
targetPath: oldEntry.path,
|
|
54846
55196
|
reason: `Renamed: ${rename8.from} -> ${rename8.to}`,
|
|
55197
|
+
reasonCode: code,
|
|
55198
|
+
reasonCopy: getReasonCopy(code),
|
|
54847
55199
|
previousItem: oldEntry.item
|
|
54848
55200
|
},
|
|
54849
55201
|
newItem: oldEntry.item
|
|
@@ -54860,6 +55212,7 @@ function detectPathMigrations(input) {
|
|
|
54860
55212
|
for (const migration of applicable) {
|
|
54861
55213
|
const affectedEntries = input.registry.installations.filter((e2) => e2.provider === migration.provider && e2.type === migration.type && pathContainsSegments(e2.path, migration.from));
|
|
54862
55214
|
for (const entry of affectedEntries) {
|
|
55215
|
+
const code = "path-migrated-cleanup";
|
|
54863
55216
|
actions.push({
|
|
54864
55217
|
deleteAction: {
|
|
54865
55218
|
action: "delete",
|
|
@@ -54869,6 +55222,8 @@ function detectPathMigrations(input) {
|
|
|
54869
55222
|
global: entry.global,
|
|
54870
55223
|
targetPath: entry.path,
|
|
54871
55224
|
reason: `Provider path migrated: ${migration.from} -> ${migration.to}`,
|
|
55225
|
+
reasonCode: code,
|
|
55226
|
+
reasonCopy: getReasonCopy(code),
|
|
54872
55227
|
previousPath: entry.path
|
|
54873
55228
|
}
|
|
54874
55229
|
});
|
|
@@ -54879,7 +55234,7 @@ function detectPathMigrations(input) {
|
|
|
54879
55234
|
function detectSectionRenames(_input) {
|
|
54880
55235
|
return [];
|
|
54881
55236
|
}
|
|
54882
|
-
function buildPlan(actions) {
|
|
55237
|
+
function buildPlan(actions, banners) {
|
|
54883
55238
|
const summary = { install: 0, update: 0, skip: 0, conflict: 0, delete: 0 };
|
|
54884
55239
|
for (const action of actions) {
|
|
54885
55240
|
summary[action.action]++;
|
|
@@ -54887,7 +55242,8 @@ function buildPlan(actions) {
|
|
|
54887
55242
|
return {
|
|
54888
55243
|
actions,
|
|
54889
55244
|
summary,
|
|
54890
|
-
hasConflicts: summary.conflict > 0
|
|
55245
|
+
hasConflicts: summary.conflict > 0,
|
|
55246
|
+
banners
|
|
54891
55247
|
};
|
|
54892
55248
|
}
|
|
54893
55249
|
var init_reconciler = __esm(() => {
|
|
@@ -55910,6 +56266,28 @@ function registerMigrationRoutes(app) {
|
|
|
55910
56266
|
return;
|
|
55911
56267
|
}
|
|
55912
56268
|
const configSource = sourceParsed.value;
|
|
56269
|
+
const reinstallEmptyDirsParsed = parseBooleanLike(req.query.reinstallEmptyDirs);
|
|
56270
|
+
if (!reinstallEmptyDirsParsed.ok) {
|
|
56271
|
+
res.status(400).json({ error: `reinstallEmptyDirs ${reinstallEmptyDirsParsed.error}` });
|
|
56272
|
+
return;
|
|
56273
|
+
}
|
|
56274
|
+
const reinstallEmptyDirs = reinstallEmptyDirsParsed.value !== false;
|
|
56275
|
+
const respectDeletionsParsed = parseBooleanLike(req.query.respectDeletions);
|
|
56276
|
+
if (!respectDeletionsParsed.ok) {
|
|
56277
|
+
res.status(400).json({ error: `respectDeletions ${respectDeletionsParsed.error}` });
|
|
56278
|
+
return;
|
|
56279
|
+
}
|
|
56280
|
+
const respectDeletions = respectDeletionsParsed.value === true;
|
|
56281
|
+
const modeRaw = req.query.mode;
|
|
56282
|
+
let reconcileMode = "reconcile";
|
|
56283
|
+
if (modeRaw !== undefined) {
|
|
56284
|
+
const modeStr = String(modeRaw).trim().toLowerCase();
|
|
56285
|
+
if (modeStr !== "reconcile" && modeStr !== "install") {
|
|
56286
|
+
res.status(400).json({ error: "mode must be 'reconcile' or 'install'" });
|
|
56287
|
+
return;
|
|
56288
|
+
}
|
|
56289
|
+
reconcileMode = modeStr;
|
|
56290
|
+
}
|
|
55913
56291
|
const discovered = await discoverMigrationItems(include, configSource);
|
|
55914
56292
|
const sourceItems = [];
|
|
55915
56293
|
for (const agent of discovered.agents) {
|
|
@@ -55995,20 +56373,33 @@ function registerMigrationRoutes(app) {
|
|
|
55995
56373
|
provider,
|
|
55996
56374
|
global: globalParam
|
|
55997
56375
|
}));
|
|
56376
|
+
const enabledTypes = ["agent", "command", "skill", "config", "rules", "hooks"].filter((type) => {
|
|
56377
|
+
const key = type === "agent" ? "agents" : type === "command" ? "commands" : type === "skill" ? "skills" : type === "config" ? "config" : type === "rules" ? "rules" : "hooks";
|
|
56378
|
+
return include[key];
|
|
56379
|
+
});
|
|
56380
|
+
const typeDirectoryStates = reinstallEmptyDirs ? buildTypeDirectoryStates(providerConfigs.map((p) => ({
|
|
56381
|
+
provider: p.provider,
|
|
56382
|
+
global: p.global
|
|
56383
|
+
})), enabledTypes) : undefined;
|
|
55998
56384
|
const input = {
|
|
55999
56385
|
sourceItems,
|
|
56000
56386
|
registry,
|
|
56001
56387
|
targetStates,
|
|
56002
56388
|
manifest,
|
|
56003
|
-
providerConfigs
|
|
56389
|
+
providerConfigs,
|
|
56390
|
+
typeDirectoryStates,
|
|
56391
|
+
respectDeletions
|
|
56004
56392
|
};
|
|
56005
56393
|
const plan = reconcile(input);
|
|
56394
|
+
const hasUnknownChecksum = registry.installations.some((inst) => isUnknownChecksum(inst.sourceChecksum) || isUnknownChecksum(inst.targetChecksum));
|
|
56395
|
+
const suggestedMode = hasUnknownChecksum ? "install" : "reconcile";
|
|
56006
56396
|
const planWithMeta = {
|
|
56007
56397
|
...plan,
|
|
56008
56398
|
meta: {
|
|
56009
56399
|
include,
|
|
56010
56400
|
providers: selectedProviders,
|
|
56011
56401
|
source: configSource,
|
|
56402
|
+
mode: reconcileMode,
|
|
56012
56403
|
items: {
|
|
56013
56404
|
agents: discovered.agents.map((item) => item.name),
|
|
56014
56405
|
commands: discovered.commands.map((item) => item.name),
|
|
@@ -56019,7 +56410,10 @@ function registerMigrationRoutes(app) {
|
|
|
56019
56410
|
}
|
|
56020
56411
|
}
|
|
56021
56412
|
};
|
|
56022
|
-
res.status(200).json({
|
|
56413
|
+
res.status(200).json({
|
|
56414
|
+
plan: planWithMeta,
|
|
56415
|
+
suggestedMode
|
|
56416
|
+
});
|
|
56023
56417
|
} catch (error) {
|
|
56024
56418
|
res.status(500).json({
|
|
56025
56419
|
error: "Failed to compute reconcile plan",
|
|
@@ -56027,6 +56421,123 @@ function registerMigrationRoutes(app) {
|
|
|
56027
56421
|
});
|
|
56028
56422
|
}
|
|
56029
56423
|
});
|
|
56424
|
+
app.get("/api/migrate/install-discovery", async (req, res) => {
|
|
56425
|
+
try {
|
|
56426
|
+
let addCandidates = function(items, type, isDirectoryItem) {
|
|
56427
|
+
for (const item of items) {
|
|
56428
|
+
const sourcePath = item.sourcePath ?? item.path ?? "";
|
|
56429
|
+
for (const provider of selectedProviders) {
|
|
56430
|
+
const registryEntry = registry.installations.find((inst) => inst.item === item.name && inst.type === type && inst.provider === provider && inst.global === globalParam);
|
|
56431
|
+
const alreadyInstalled = registryEntry !== undefined;
|
|
56432
|
+
candidates.push({
|
|
56433
|
+
item: item.name,
|
|
56434
|
+
type,
|
|
56435
|
+
provider,
|
|
56436
|
+
global: globalParam,
|
|
56437
|
+
isDirectoryItem,
|
|
56438
|
+
description: item.description,
|
|
56439
|
+
sourcePath,
|
|
56440
|
+
alreadyInstalled,
|
|
56441
|
+
registryPath: alreadyInstalled ? registryEntry?.path : undefined
|
|
56442
|
+
});
|
|
56443
|
+
}
|
|
56444
|
+
}
|
|
56445
|
+
};
|
|
56446
|
+
const providersParsed = parseProvidersFromQuery(req.query.providers);
|
|
56447
|
+
if (!providersParsed.ok || !providersParsed.value) {
|
|
56448
|
+
res.status(400).json({ error: providersParsed.error || "Invalid providers parameter" });
|
|
56449
|
+
return;
|
|
56450
|
+
}
|
|
56451
|
+
const selectedProviders = providersParsed.value;
|
|
56452
|
+
const includeParsed = parseIncludeOptionsStrict({
|
|
56453
|
+
agents: req.query.agents,
|
|
56454
|
+
commands: req.query.commands,
|
|
56455
|
+
skills: req.query.skills,
|
|
56456
|
+
config: req.query.config,
|
|
56457
|
+
rules: req.query.rules,
|
|
56458
|
+
hooks: req.query.hooks
|
|
56459
|
+
}, "");
|
|
56460
|
+
if (!includeParsed.ok || !includeParsed.value) {
|
|
56461
|
+
res.status(400).json({ error: includeParsed.error || "Invalid include options" });
|
|
56462
|
+
return;
|
|
56463
|
+
}
|
|
56464
|
+
const include = includeParsed.value;
|
|
56465
|
+
const globalParsed = parseBooleanLike(req.query.global);
|
|
56466
|
+
if (!globalParsed.ok) {
|
|
56467
|
+
res.status(400).json({ error: `global ${globalParsed.error}` });
|
|
56468
|
+
return;
|
|
56469
|
+
}
|
|
56470
|
+
const globalParam = globalParsed.value === true;
|
|
56471
|
+
const sourceParsed = parseConfigSource(req.query.source);
|
|
56472
|
+
if (!sourceParsed.ok) {
|
|
56473
|
+
res.status(400).json({ error: sourceParsed.error || "Invalid source value" });
|
|
56474
|
+
return;
|
|
56475
|
+
}
|
|
56476
|
+
const configSource = sourceParsed.value;
|
|
56477
|
+
const discovered = await discoverMigrationItems(include, configSource);
|
|
56478
|
+
const registry = await readPortableRegistry();
|
|
56479
|
+
const candidates = [];
|
|
56480
|
+
if (include.agents) {
|
|
56481
|
+
addCandidates(discovered.agents.map((a3) => ({
|
|
56482
|
+
name: a3.name,
|
|
56483
|
+
description: a3.description,
|
|
56484
|
+
sourcePath: a3.sourcePath ?? ""
|
|
56485
|
+
})), "agent", false);
|
|
56486
|
+
}
|
|
56487
|
+
if (include.commands) {
|
|
56488
|
+
addCandidates(discovered.commands.map((c2) => ({
|
|
56489
|
+
name: c2.name,
|
|
56490
|
+
description: c2.description,
|
|
56491
|
+
sourcePath: c2.sourcePath ?? ""
|
|
56492
|
+
})), "command", false);
|
|
56493
|
+
}
|
|
56494
|
+
if (include.skills) {
|
|
56495
|
+
addCandidates(discovered.skills.map((s) => ({
|
|
56496
|
+
name: s.name,
|
|
56497
|
+
description: s.description,
|
|
56498
|
+
path: s.path
|
|
56499
|
+
})), "skill", true);
|
|
56500
|
+
}
|
|
56501
|
+
if (include.config && discovered.configItem) {
|
|
56502
|
+
addCandidates([
|
|
56503
|
+
{
|
|
56504
|
+
name: discovered.configItem.name,
|
|
56505
|
+
description: undefined,
|
|
56506
|
+
sourcePath: discovered.configItem.sourcePath ?? ""
|
|
56507
|
+
}
|
|
56508
|
+
], "config", false);
|
|
56509
|
+
}
|
|
56510
|
+
if (include.rules) {
|
|
56511
|
+
addCandidates(discovered.ruleItems.map((r2) => ({
|
|
56512
|
+
name: r2.name,
|
|
56513
|
+
description: undefined,
|
|
56514
|
+
sourcePath: r2.sourcePath ?? ""
|
|
56515
|
+
})), "rules", false);
|
|
56516
|
+
}
|
|
56517
|
+
if (include.hooks) {
|
|
56518
|
+
addCandidates(discovered.hookItems.map((h2) => ({
|
|
56519
|
+
name: h2.name,
|
|
56520
|
+
description: undefined,
|
|
56521
|
+
sourcePath: h2.sourcePath ?? ""
|
|
56522
|
+
})), "hooks", false);
|
|
56523
|
+
}
|
|
56524
|
+
const providerConfigs = selectedProviders.map((provider) => ({
|
|
56525
|
+
provider,
|
|
56526
|
+
global: globalParam
|
|
56527
|
+
}));
|
|
56528
|
+
const enabledTypes = ["agent", "command", "skill", "config", "rules", "hooks"].filter((type) => {
|
|
56529
|
+
const key = type === "agent" ? "agents" : type === "command" ? "commands" : type === "skill" ? "skills" : type === "config" ? "config" : type === "rules" ? "rules" : "hooks";
|
|
56530
|
+
return include[key];
|
|
56531
|
+
});
|
|
56532
|
+
const typeDirectoryStates = buildTypeDirectoryStates(providerConfigs, enabledTypes);
|
|
56533
|
+
res.status(200).json({ candidates, typeDirectoryStates });
|
|
56534
|
+
} catch (error) {
|
|
56535
|
+
res.status(500).json({
|
|
56536
|
+
error: "Failed to discover install candidates",
|
|
56537
|
+
message: sanitizeUntrusted(error, 260)
|
|
56538
|
+
});
|
|
56539
|
+
}
|
|
56540
|
+
});
|
|
56030
56541
|
app.post("/api/migrate/execute", async (req, res) => {
|
|
56031
56542
|
try {
|
|
56032
56543
|
const planBased = req.body?.plan !== undefined;
|
|
@@ -56429,7 +56940,7 @@ function registerMigrationRoutes(app) {
|
|
|
56429
56940
|
}
|
|
56430
56941
|
});
|
|
56431
56942
|
}
|
|
56432
|
-
var MIGRATION_TYPES, MAX_PROVIDER_COUNT = 20, MAX_PLAN_ACTIONS = 5000, ALLOWED_CONFIG_SOURCE_KEYS, CONFLICT_RESOLUTION_SCHEMA, RECONCILE_ACTION_SCHEMA, RECONCILE_PLAN_SCHEMA, PLAN_EXECUTE_PAYLOAD_SCHEMA, PLURAL_TO_SINGULAR, shellHookWarningShown = false;
|
|
56943
|
+
var MIGRATION_TYPES, MAX_PROVIDER_COUNT = 20, MAX_PLAN_ACTIONS = 5000, ALLOWED_CONFIG_SOURCE_KEYS, CONFLICT_RESOLUTION_SCHEMA, RECONCILE_ACTION_SCHEMA, RECONCILE_BANNER_SCHEMA, RECONCILE_PLAN_SCHEMA, PLAN_EXECUTE_PAYLOAD_SCHEMA, PLURAL_TO_SINGULAR, shellHookWarningShown = false;
|
|
56433
56944
|
var init_migration_routes = __esm(() => {
|
|
56434
56945
|
init_agents_discovery();
|
|
56435
56946
|
init_commands_discovery();
|
|
@@ -56481,8 +56992,20 @@ var init_migration_routes = __esm(() => {
|
|
|
56481
56992
|
ownedSections: exports_external.array(exports_external.string()).optional(),
|
|
56482
56993
|
affectedSections: exports_external.array(exports_external.string()).optional(),
|
|
56483
56994
|
diff: exports_external.string().optional(),
|
|
56484
|
-
resolution: CONFLICT_RESOLUTION_SCHEMA.optional()
|
|
56995
|
+
resolution: CONFLICT_RESOLUTION_SCHEMA.optional(),
|
|
56996
|
+
reasonCode: exports_external.string().optional(),
|
|
56997
|
+
reasonCopy: exports_external.string().optional(),
|
|
56998
|
+
isDirectoryItem: exports_external.boolean().optional()
|
|
56485
56999
|
}).passthrough();
|
|
57000
|
+
RECONCILE_BANNER_SCHEMA = exports_external.object({
|
|
57001
|
+
kind: exports_external.enum(["empty-dir", "empty-dir-respected"]),
|
|
57002
|
+
provider: exports_external.string(),
|
|
57003
|
+
type: exports_external.string(),
|
|
57004
|
+
global: exports_external.boolean(),
|
|
57005
|
+
path: exports_external.string(),
|
|
57006
|
+
itemCount: exports_external.number().int().nonnegative(),
|
|
57007
|
+
message: exports_external.string()
|
|
57008
|
+
});
|
|
56486
57009
|
RECONCILE_PLAN_SCHEMA = exports_external.object({
|
|
56487
57010
|
actions: exports_external.array(RECONCILE_ACTION_SCHEMA).max(MAX_PLAN_ACTIONS),
|
|
56488
57011
|
summary: exports_external.object({
|
|
@@ -56492,11 +57015,13 @@ var init_migration_routes = __esm(() => {
|
|
|
56492
57015
|
conflict: exports_external.number().int().nonnegative(),
|
|
56493
57016
|
delete: exports_external.number().int().nonnegative()
|
|
56494
57017
|
}),
|
|
56495
|
-
hasConflicts: exports_external.boolean()
|
|
57018
|
+
hasConflicts: exports_external.boolean(),
|
|
57019
|
+
banners: exports_external.array(RECONCILE_BANNER_SCHEMA).optional().default([])
|
|
56496
57020
|
}).passthrough();
|
|
56497
57021
|
PLAN_EXECUTE_PAYLOAD_SCHEMA = exports_external.object({
|
|
56498
57022
|
plan: RECONCILE_PLAN_SCHEMA,
|
|
56499
|
-
resolutions: exports_external.record(CONFLICT_RESOLUTION_SCHEMA).optional().default({})
|
|
57023
|
+
resolutions: exports_external.record(CONFLICT_RESOLUTION_SCHEMA).optional().default({}),
|
|
57024
|
+
mode: exports_external.enum(["reconcile", "install"]).optional()
|
|
56500
57025
|
}).passthrough();
|
|
56501
57026
|
PLURAL_TO_SINGULAR = {
|
|
56502
57027
|
agents: "agent",
|
|
@@ -56509,7 +57034,7 @@ var init_migration_routes = __esm(() => {
|
|
|
56509
57034
|
});
|
|
56510
57035
|
|
|
56511
57036
|
// src/domains/plan-parser/plan-metadata.ts
|
|
56512
|
-
import { readFileSync as readFileSync6, statSync as
|
|
57037
|
+
import { readFileSync as readFileSync6, statSync as statSync6 } from "node:fs";
|
|
56513
57038
|
function readMatter(filePath) {
|
|
56514
57039
|
try {
|
|
56515
57040
|
return import_gray_matter6.default(readFileSync6(filePath, "utf8")).data;
|
|
@@ -56586,7 +57111,7 @@ function parseEffortHours(value) {
|
|
|
56586
57111
|
}
|
|
56587
57112
|
function readPlanMetadata(planFile, counts) {
|
|
56588
57113
|
const frontmatter = readMatter(planFile);
|
|
56589
|
-
const stats =
|
|
57114
|
+
const stats = statSync6(planFile);
|
|
56590
57115
|
return {
|
|
56591
57116
|
title: typeof frontmatter.title === "string" ? frontmatter.title : undefined,
|
|
56592
57117
|
description: typeof frontmatter.description === "string" ? frontmatter.description : undefined,
|
|
@@ -56603,7 +57128,7 @@ function readPlanMetadata(planFile, counts) {
|
|
|
56603
57128
|
}
|
|
56604
57129
|
function readPhaseMetadata(phaseFile) {
|
|
56605
57130
|
const frontmatter = readMatter(phaseFile);
|
|
56606
|
-
const stats =
|
|
57131
|
+
const stats = statSync6(phaseFile);
|
|
56607
57132
|
return {
|
|
56608
57133
|
title: typeof frontmatter.title === "string" ? frontmatter.title : undefined,
|
|
56609
57134
|
status: normalizePhaseStatus(frontmatter.status),
|
|
@@ -57000,7 +57525,7 @@ var init_plan_table_parser = __esm(() => {
|
|
|
57000
57525
|
|
|
57001
57526
|
// src/domains/plan-parser/activity-tracker.ts
|
|
57002
57527
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
57003
|
-
import { readdirSync as
|
|
57528
|
+
import { readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
|
|
57004
57529
|
import { join as join42, relative as relative8 } from "node:path";
|
|
57005
57530
|
function startOfDay(date) {
|
|
57006
57531
|
const copy = new Date(date);
|
|
@@ -57010,7 +57535,7 @@ function startOfDay(date) {
|
|
|
57010
57535
|
function enumerateMarkdownFiles(dir, depth = 0) {
|
|
57011
57536
|
if (depth >= MAX_DEPTH)
|
|
57012
57537
|
return [];
|
|
57013
|
-
const entries =
|
|
57538
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
57014
57539
|
return entries.flatMap((entry) => {
|
|
57015
57540
|
const entryPath = join42(dir, entry.name);
|
|
57016
57541
|
if (entry.isDirectory())
|
|
@@ -57054,7 +57579,7 @@ function getGitSamples(dir, samples) {
|
|
|
57054
57579
|
}
|
|
57055
57580
|
function getMtimeSamples(dir, samples) {
|
|
57056
57581
|
for (const file of enumerateMarkdownFiles(dir)) {
|
|
57057
|
-
const mtimeKey = startOfDay(
|
|
57582
|
+
const mtimeKey = startOfDay(statSync7(file).mtime).toISOString();
|
|
57058
57583
|
const sample = samples.get(mtimeKey);
|
|
57059
57584
|
if (!sample)
|
|
57060
57585
|
continue;
|
|
@@ -57117,13 +57642,13 @@ var DAY_MS = 86400000, CELL_COUNT = 84, MAX_DEPTH = 10;
|
|
|
57117
57642
|
var init_activity_tracker = () => {};
|
|
57118
57643
|
|
|
57119
57644
|
// src/domains/plan-parser/plan-scanner.ts
|
|
57120
|
-
import { existsSync as existsSync29, readdirSync as
|
|
57645
|
+
import { existsSync as existsSync29, readdirSync as readdirSync5 } from "node:fs";
|
|
57121
57646
|
import { join as join43 } from "node:path";
|
|
57122
57647
|
function scanPlanDir(dir) {
|
|
57123
57648
|
if (!existsSync29(dir))
|
|
57124
57649
|
return [];
|
|
57125
57650
|
try {
|
|
57126
|
-
return
|
|
57651
|
+
return readdirSync5(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join43(dir, entry.name, "plan.md")).filter(existsSync29);
|
|
57127
57652
|
} catch {
|
|
57128
57653
|
return [];
|
|
57129
57654
|
}
|
|
@@ -60169,7 +60694,7 @@ var init_skill_browser_routes = __esm(() => {
|
|
|
60169
60694
|
});
|
|
60170
60695
|
|
|
60171
60696
|
// src/commands/skills/agents.ts
|
|
60172
|
-
import { existsSync as existsSync37, readdirSync as
|
|
60697
|
+
import { existsSync as existsSync37, readdirSync as readdirSync6, statSync as statSync8 } from "node:fs";
|
|
60173
60698
|
import { homedir as homedir37, platform as platform5 } from "node:os";
|
|
60174
60699
|
import { join as join54 } from "node:path";
|
|
60175
60700
|
function hasInstallSignal2(path6) {
|
|
@@ -60177,9 +60702,9 @@ function hasInstallSignal2(path6) {
|
|
|
60177
60702
|
return false;
|
|
60178
60703
|
}
|
|
60179
60704
|
try {
|
|
60180
|
-
const stat10 =
|
|
60705
|
+
const stat10 = statSync8(path6);
|
|
60181
60706
|
if (stat10.isDirectory()) {
|
|
60182
|
-
return
|
|
60707
|
+
return readdirSync6(path6).length > 0;
|
|
60183
60708
|
}
|
|
60184
60709
|
if (stat10.isFile()) {
|
|
60185
60710
|
return true;
|
|
@@ -61558,7 +62083,7 @@ var package_default;
|
|
|
61558
62083
|
var init_package = __esm(() => {
|
|
61559
62084
|
package_default = {
|
|
61560
62085
|
name: "claudekit-cli",
|
|
61561
|
-
version: "3.41.4-dev.
|
|
62086
|
+
version: "3.41.4-dev.49",
|
|
61562
62087
|
description: "CLI tool for bootstrapping and updating ClaudeKit projects",
|
|
61563
62088
|
type: "module",
|
|
61564
62089
|
repository: {
|
|
@@ -61601,6 +62126,8 @@ var init_package = __esm(() => {
|
|
|
61601
62126
|
"test:integration": "CK_RUN_CLI_INTEGRATION=1 bun test tests/integration/cli.test.ts",
|
|
61602
62127
|
"test:watch": "bun test --watch",
|
|
61603
62128
|
"test:quick": "./scripts/dev-quick-start.sh test",
|
|
62129
|
+
"test:e2e": "playwright test --config=playwright.config.ts",
|
|
62130
|
+
"test:e2e:ui": "playwright test --ui --config=playwright.config.ts",
|
|
61604
62131
|
lint: "biome check .",
|
|
61605
62132
|
"lint:fix": "biome check --fix .",
|
|
61606
62133
|
"lint:fix-unsafe": "biome check --fix --unsafe .",
|
|
@@ -61659,6 +62186,7 @@ var init_package = __esm(() => {
|
|
|
61659
62186
|
},
|
|
61660
62187
|
devDependencies: {
|
|
61661
62188
|
"@biomejs/biome": "^1.9.4",
|
|
62189
|
+
"@playwright/test": "^1.59.1",
|
|
61662
62190
|
"@semantic-release/changelog": "^6.0.3",
|
|
61663
62191
|
"@semantic-release/git": "^10.0.1",
|
|
61664
62192
|
"@tauri-apps/cli": "^2",
|
|
@@ -72856,7 +73384,7 @@ var init_content_validator = __esm(() => {
|
|
|
72856
73384
|
|
|
72857
73385
|
// src/commands/content/phases/context-cache-manager.ts
|
|
72858
73386
|
import { createHash as createHash9 } from "node:crypto";
|
|
72859
|
-
import { existsSync as existsSync75, mkdirSync as mkdirSync5, readFileSync as readFileSync18, readdirSync as
|
|
73387
|
+
import { existsSync as existsSync75, mkdirSync as mkdirSync5, readFileSync as readFileSync18, readdirSync as readdirSync11, statSync as statSync14 } from "node:fs";
|
|
72860
73388
|
import { rename as rename14, writeFile as writeFile38 } from "node:fs/promises";
|
|
72861
73389
|
import { homedir as homedir52 } from "node:os";
|
|
72862
73390
|
import { basename as basename30, join as join158 } from "node:path";
|
|
@@ -72892,7 +73420,7 @@ function computeSourceHash(repoPath) {
|
|
|
72892
73420
|
const paths = getDocSourcePaths(repoPath);
|
|
72893
73421
|
for (const filePath of paths) {
|
|
72894
73422
|
try {
|
|
72895
|
-
const stat25 =
|
|
73423
|
+
const stat25 = statSync14(filePath);
|
|
72896
73424
|
hash.update(`${filePath}:${stat25.mtimeMs}`);
|
|
72897
73425
|
} catch {
|
|
72898
73426
|
hash.update(`${filePath}:0`);
|
|
@@ -72905,7 +73433,7 @@ function getDocSourcePaths(repoPath) {
|
|
|
72905
73433
|
const docsDir = join158(repoPath, "docs");
|
|
72906
73434
|
if (existsSync75(docsDir)) {
|
|
72907
73435
|
try {
|
|
72908
|
-
const files =
|
|
73436
|
+
const files = readdirSync11(docsDir);
|
|
72909
73437
|
for (const f3 of files) {
|
|
72910
73438
|
if (f3.endsWith(".md"))
|
|
72911
73439
|
paths.push(join158(docsDir, f3));
|
|
@@ -72918,7 +73446,7 @@ function getDocSourcePaths(repoPath) {
|
|
|
72918
73446
|
const stylesDir = join158(repoPath, "assets", "writing-styles");
|
|
72919
73447
|
if (existsSync75(stylesDir)) {
|
|
72920
73448
|
try {
|
|
72921
|
-
const files =
|
|
73449
|
+
const files = readdirSync11(stylesDir);
|
|
72922
73450
|
for (const f3 of files) {
|
|
72923
73451
|
paths.push(join158(stylesDir, f3));
|
|
72924
73452
|
}
|
|
@@ -73113,7 +73641,7 @@ function extractContentFromResponse(response) {
|
|
|
73113
73641
|
|
|
73114
73642
|
// src/commands/content/phases/docs-summarizer.ts
|
|
73115
73643
|
import { execSync as execSync7 } from "node:child_process";
|
|
73116
|
-
import { existsSync as existsSync76, readFileSync as readFileSync19, readdirSync as
|
|
73644
|
+
import { existsSync as existsSync76, readFileSync as readFileSync19, readdirSync as readdirSync12 } from "node:fs";
|
|
73117
73645
|
import { join as join159 } from "node:path";
|
|
73118
73646
|
async function summarizeProjectDocs(repoPath, contentLogger) {
|
|
73119
73647
|
const rawContent = collectRawDocs(repoPath);
|
|
@@ -73171,7 +73699,7 @@ function collectRawDocs(repoPath) {
|
|
|
73171
73699
|
const docsDir = join159(repoPath, "docs");
|
|
73172
73700
|
if (existsSync76(docsDir)) {
|
|
73173
73701
|
try {
|
|
73174
|
-
const files =
|
|
73702
|
+
const files = readdirSync12(docsDir).filter((f3) => f3.endsWith(".md")).sort();
|
|
73175
73703
|
for (const f3 of files) {
|
|
73176
73704
|
const content = readCapped(join159(docsDir, f3), 5000);
|
|
73177
73705
|
if (content) {
|
|
@@ -73195,7 +73723,7 @@ ${content}`);
|
|
|
73195
73723
|
const stylesDir = join159(repoPath, "assets", "writing-styles");
|
|
73196
73724
|
if (existsSync76(stylesDir)) {
|
|
73197
73725
|
try {
|
|
73198
|
-
const files =
|
|
73726
|
+
const files = readdirSync12(stylesDir).slice(0, 3);
|
|
73199
73727
|
styles3 = files.map((f3) => readCapped(join159(stylesDir, f3), 1000)).filter(Boolean).join(`
|
|
73200
73728
|
|
|
73201
73729
|
`);
|
|
@@ -73386,7 +73914,7 @@ IMPORTANT: Generate the image and output the path as JSON: {"imagePath": "/path/
|
|
|
73386
73914
|
|
|
73387
73915
|
// src/commands/content/phases/photo-generator.ts
|
|
73388
73916
|
import { execSync as execSync8 } from "node:child_process";
|
|
73389
|
-
import { existsSync as existsSync77, mkdirSync as mkdirSync6, readdirSync as
|
|
73917
|
+
import { existsSync as existsSync77, mkdirSync as mkdirSync6, readdirSync as readdirSync13 } from "node:fs";
|
|
73390
73918
|
import { homedir as homedir53 } from "node:os";
|
|
73391
73919
|
import { join as join160 } from "node:path";
|
|
73392
73920
|
async function generatePhoto(_content, context, config, platform17, contentId, contentLogger) {
|
|
@@ -73411,7 +73939,7 @@ async function generatePhoto(_content, context, config, platform17, contentId, c
|
|
|
73411
73939
|
return { path: imagePath, ...dimensions, format: "png" };
|
|
73412
73940
|
}
|
|
73413
73941
|
}
|
|
73414
|
-
const files =
|
|
73942
|
+
const files = readdirSync13(mediaDir);
|
|
73415
73943
|
const imageFile = files.find((f3) => /\.(png|jpg|jpeg|webp)$/i.test(f3));
|
|
73416
73944
|
if (imageFile) {
|
|
73417
73945
|
const ext2 = imageFile.split(".").pop() ?? "png";
|
|
@@ -73503,7 +74031,7 @@ var init_content_creator = __esm(() => {
|
|
|
73503
74031
|
});
|
|
73504
74032
|
|
|
73505
74033
|
// src/commands/content/phases/content-logger.ts
|
|
73506
|
-
import { createWriteStream as createWriteStream4, existsSync as existsSync78, mkdirSync as mkdirSync7, statSync as
|
|
74034
|
+
import { createWriteStream as createWriteStream4, existsSync as existsSync78, mkdirSync as mkdirSync7, statSync as statSync15 } from "node:fs";
|
|
73507
74035
|
import { homedir as homedir54 } from "node:os";
|
|
73508
74036
|
import { join as join161 } from "node:path";
|
|
73509
74037
|
|
|
@@ -73569,7 +74097,7 @@ class ContentLogger {
|
|
|
73569
74097
|
if (this.maxBytes > 0 && this.stream) {
|
|
73570
74098
|
const logPath = join161(this.logDir, `content-${this.currentDate}.log`);
|
|
73571
74099
|
try {
|
|
73572
|
-
const stat25 =
|
|
74100
|
+
const stat25 = statSync15(logPath);
|
|
73573
74101
|
if (stat25.size >= this.maxBytes) {
|
|
73574
74102
|
this.close();
|
|
73575
74103
|
const suffix = Date.now();
|
|
@@ -73798,7 +74326,7 @@ function isNoiseCommit(title, author) {
|
|
|
73798
74326
|
|
|
73799
74327
|
// src/commands/content/phases/change-detector.ts
|
|
73800
74328
|
import { execSync as execSync10, spawnSync as spawnSync9 } from "node:child_process";
|
|
73801
|
-
import { existsSync as existsSync80, readFileSync as readFileSync20, readdirSync as
|
|
74329
|
+
import { existsSync as existsSync80, readFileSync as readFileSync20, readdirSync as readdirSync14, statSync as statSync16 } from "node:fs";
|
|
73802
74330
|
import { join as join162 } from "node:path";
|
|
73803
74331
|
function detectCommits(repo, since) {
|
|
73804
74332
|
try {
|
|
@@ -73914,7 +74442,7 @@ function detectCompletedPlans(repo, since) {
|
|
|
73914
74442
|
const sinceMs = new Date(since).getTime();
|
|
73915
74443
|
const events = [];
|
|
73916
74444
|
try {
|
|
73917
|
-
const entries =
|
|
74445
|
+
const entries = readdirSync14(plansDir, { withFileTypes: true });
|
|
73918
74446
|
for (const entry of entries) {
|
|
73919
74447
|
if (!entry.isDirectory())
|
|
73920
74448
|
continue;
|
|
@@ -73922,7 +74450,7 @@ function detectCompletedPlans(repo, since) {
|
|
|
73922
74450
|
if (!existsSync80(planFile))
|
|
73923
74451
|
continue;
|
|
73924
74452
|
try {
|
|
73925
|
-
const stat25 =
|
|
74453
|
+
const stat25 = statSync16(planFile);
|
|
73926
74454
|
if (stat25.mtimeMs < sinceMs)
|
|
73927
74455
|
continue;
|
|
73928
74456
|
const content = readFileSync20(planFile, "utf-8");
|
|
@@ -73995,7 +74523,7 @@ function classifyCommit(event) {
|
|
|
73995
74523
|
|
|
73996
74524
|
// src/commands/content/phases/repo-discoverer.ts
|
|
73997
74525
|
import { execSync as execSync11 } from "node:child_process";
|
|
73998
|
-
import { readdirSync as
|
|
74526
|
+
import { readdirSync as readdirSync15 } from "node:fs";
|
|
73999
74527
|
import { join as join163 } from "node:path";
|
|
74000
74528
|
function discoverRepos2(cwd2) {
|
|
74001
74529
|
const repos = [];
|
|
@@ -74005,7 +74533,7 @@ function discoverRepos2(cwd2) {
|
|
|
74005
74533
|
repos.push(info);
|
|
74006
74534
|
}
|
|
74007
74535
|
try {
|
|
74008
|
-
const entries =
|
|
74536
|
+
const entries = readdirSync15(cwd2, { withFileTypes: true });
|
|
74009
74537
|
for (const entry of entries) {
|
|
74010
74538
|
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
74011
74539
|
continue;
|
|
@@ -76715,19 +77243,40 @@ var init_migrate_command_help = __esm(() => {
|
|
|
76715
77243
|
usage: "ck migrate [options]",
|
|
76716
77244
|
examples: [
|
|
76717
77245
|
{
|
|
76718
|
-
command: "ck migrate --
|
|
76719
|
-
description: "
|
|
77246
|
+
command: "ck migrate --install",
|
|
77247
|
+
description: "Pick items to install interactively (install picker mode)"
|
|
76720
77248
|
},
|
|
76721
77249
|
{
|
|
76722
|
-
command: "ck migrate --agent codex -
|
|
76723
|
-
description: "
|
|
77250
|
+
command: "ck migrate --agent codex --dry-run",
|
|
77251
|
+
description: "Preview the destination-aware reconcile plan before writing files"
|
|
76724
77252
|
},
|
|
76725
77253
|
{
|
|
76726
|
-
command: "
|
|
76727
|
-
description: "
|
|
77254
|
+
command: "ck migrate --respect-deletions",
|
|
77255
|
+
description: "Preserve empty directories — do not auto-reinstall deleted items"
|
|
76728
77256
|
}
|
|
76729
77257
|
],
|
|
76730
77258
|
optionGroups: [
|
|
77259
|
+
{
|
|
77260
|
+
title: "Mode Options",
|
|
77261
|
+
options: [
|
|
77262
|
+
{
|
|
77263
|
+
flags: "--install",
|
|
77264
|
+
description: "Opt-in install picker mode — interactively select which items to install (default when registry is empty or has unknown checksums)"
|
|
77265
|
+
},
|
|
77266
|
+
{
|
|
77267
|
+
flags: "--reconcile",
|
|
77268
|
+
description: "Force reconcile mode — compute diff vs registry and apply only changes (default when registry is valid)"
|
|
77269
|
+
},
|
|
77270
|
+
{
|
|
77271
|
+
flags: "--reinstall-empty-dirs",
|
|
77272
|
+
description: "Reinstall all items when their type directory is empty or missing (default: true). Use --respect-deletions to disable."
|
|
77273
|
+
},
|
|
77274
|
+
{
|
|
77275
|
+
flags: "--respect-deletions",
|
|
77276
|
+
description: "Preserve deletion even when a type directory is empty — skip reinstall heuristic. Mutually exclusive with --reinstall-empty-dirs."
|
|
77277
|
+
}
|
|
77278
|
+
]
|
|
77279
|
+
},
|
|
76731
77280
|
{
|
|
76732
77281
|
title: "Target Options",
|
|
76733
77282
|
options: [
|
|
@@ -76790,6 +77339,19 @@ var init_migrate_command_help = __esm(() => {
|
|
|
76790
77339
|
}
|
|
76791
77340
|
]
|
|
76792
77341
|
}
|
|
77342
|
+
],
|
|
77343
|
+
sections: [
|
|
77344
|
+
{
|
|
77345
|
+
title: "Gotchas",
|
|
77346
|
+
content: [
|
|
77347
|
+
" --install and --reconcile are mutually exclusive — pass only one",
|
|
77348
|
+
" --reinstall-empty-dirs and --respect-deletions are mutually exclusive — pass only one",
|
|
77349
|
+
" Default mode is smart-detected: no/stale registry → install, valid registry → reconcile",
|
|
77350
|
+
" --respect-deletions disables the auto-reinstall heuristic for empty directories",
|
|
77351
|
+
" --force overrides skip decisions per item; --reinstall-empty-dirs is a per-directory heuristic"
|
|
77352
|
+
].join(`
|
|
77353
|
+
`)
|
|
77354
|
+
}
|
|
76793
77355
|
]
|
|
76794
77356
|
};
|
|
76795
77357
|
});
|
|
@@ -84267,7 +84829,7 @@ async function checkCliInstallMethod() {
|
|
|
84267
84829
|
};
|
|
84268
84830
|
}
|
|
84269
84831
|
// src/domains/health-checks/checkers/claude-md-checker.ts
|
|
84270
|
-
import { existsSync as existsSync48, statSync as
|
|
84832
|
+
import { existsSync as existsSync48, statSync as statSync9 } from "node:fs";
|
|
84271
84833
|
import { join as join76 } from "node:path";
|
|
84272
84834
|
function checkClaudeMd(setup, projectDir) {
|
|
84273
84835
|
const results = [];
|
|
@@ -84293,7 +84855,7 @@ function checkClaudeMdFile(path6, name, id) {
|
|
|
84293
84855
|
};
|
|
84294
84856
|
}
|
|
84295
84857
|
try {
|
|
84296
|
-
const stat13 =
|
|
84858
|
+
const stat13 = statSync9(path6);
|
|
84297
84859
|
const sizeKB = (stat13.size / 1024).toFixed(1);
|
|
84298
84860
|
if (stat13.size === 0) {
|
|
84299
84861
|
return {
|
|
@@ -85304,7 +85866,7 @@ init_command_normalizer();
|
|
|
85304
85866
|
init_logger();
|
|
85305
85867
|
init_path_resolver();
|
|
85306
85868
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
85307
|
-
import { existsSync as existsSync55, readFileSync as readFileSync15, statSync as
|
|
85869
|
+
import { existsSync as existsSync55, readFileSync as readFileSync15, statSync as statSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
85308
85870
|
import { readdir as readdir21 } from "node:fs/promises";
|
|
85309
85871
|
import { homedir as homedir43, tmpdir as tmpdir2 } from "node:os";
|
|
85310
85872
|
import { join as join86, resolve as resolve30 } from "node:path";
|
|
@@ -86152,7 +86714,7 @@ async function checkHookLogs(projectDir) {
|
|
|
86152
86714
|
};
|
|
86153
86715
|
}
|
|
86154
86716
|
try {
|
|
86155
|
-
const logStats =
|
|
86717
|
+
const logStats = statSync10(logPath);
|
|
86156
86718
|
if (logStats.size > MAX_LOG_FILE_SIZE_BYTES) {
|
|
86157
86719
|
return {
|
|
86158
86720
|
id: "hook-logs",
|
|
@@ -98194,7 +98756,7 @@ async function handleDownload(ctx) {
|
|
|
98194
98756
|
import { join as join122 } from "node:path";
|
|
98195
98757
|
|
|
98196
98758
|
// src/domains/installation/deletion-handler.ts
|
|
98197
|
-
import { existsSync as existsSync61, lstatSync as lstatSync3, readdirSync as
|
|
98759
|
+
import { existsSync as existsSync61, lstatSync as lstatSync3, readdirSync as readdirSync7, rmSync as rmSync2, rmdirSync, unlinkSync as unlinkSync4 } from "node:fs";
|
|
98198
98760
|
import { dirname as dirname35, join as join108, relative as relative17, resolve as resolve34, sep as sep10 } from "node:path";
|
|
98199
98761
|
|
|
98200
98762
|
// src/services/file-operations/manifest/manifest-reader.ts
|
|
@@ -98388,7 +98950,7 @@ function collectFilesRecursively(dir, baseDir) {
|
|
|
98388
98950
|
if (!existsSync61(dir))
|
|
98389
98951
|
return results;
|
|
98390
98952
|
try {
|
|
98391
|
-
const entries =
|
|
98953
|
+
const entries = readdirSync7(dir, { withFileTypes: true });
|
|
98392
98954
|
for (const entry of entries) {
|
|
98393
98955
|
const fullPath = join108(dir, entry.name);
|
|
98394
98956
|
const relativePath = relative17(baseDir, fullPath);
|
|
@@ -98426,7 +98988,7 @@ function cleanupEmptyDirectories(filePath, claudeDir3) {
|
|
|
98426
98988
|
while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir) && iterations < MAX_CLEANUP_ITERATIONS) {
|
|
98427
98989
|
iterations++;
|
|
98428
98990
|
try {
|
|
98429
|
-
const entries =
|
|
98991
|
+
const entries = readdirSync7(currentDir);
|
|
98430
98992
|
if (entries.length === 0) {
|
|
98431
98993
|
rmdirSync(currentDir);
|
|
98432
98994
|
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
@@ -104104,7 +104666,7 @@ async function runPreflightChecks() {
|
|
|
104104
104666
|
|
|
104105
104667
|
// src/domains/installation/fresh-installer.ts
|
|
104106
104668
|
init_metadata_migration();
|
|
104107
|
-
import { existsSync as existsSync63, readdirSync as
|
|
104669
|
+
import { existsSync as existsSync63, readdirSync as readdirSync8, rmSync as rmSync3, rmdirSync as rmdirSync2, unlinkSync as unlinkSync5 } from "node:fs";
|
|
104108
104670
|
import { basename as basename24, dirname as dirname39, join as join134, resolve as resolve36 } from "node:path";
|
|
104109
104671
|
init_logger();
|
|
104110
104672
|
init_safe_spinner();
|
|
@@ -104157,7 +104719,7 @@ function cleanupEmptyDirectories2(filePath, claudeDir3) {
|
|
|
104157
104719
|
let currentDir = resolve36(dirname39(filePath));
|
|
104158
104720
|
while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir)) {
|
|
104159
104721
|
try {
|
|
104160
|
-
const entries =
|
|
104722
|
+
const entries = readdirSync8(currentDir);
|
|
104161
104723
|
if (entries.length === 0) {
|
|
104162
104724
|
rmdirSync2(currentDir);
|
|
104163
104725
|
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
@@ -106857,6 +107419,30 @@ function buildProviderScopeSubtitle(selectedProviders, global3) {
|
|
|
106857
107419
|
}
|
|
106858
107420
|
return `${selectedProviders.length} providers -> ${scope}`;
|
|
106859
107421
|
}
|
|
107422
|
+
function renderBannerLines(banner) {
|
|
107423
|
+
const width = 64;
|
|
107424
|
+
const bar = `+${"=".repeat(width)}+`;
|
|
107425
|
+
const homePath = process.env.HOME ?? "";
|
|
107426
|
+
const displayPath = banner.path.replace(homePath, "~");
|
|
107427
|
+
if (banner.kind === "empty-dir") {
|
|
107428
|
+
return [
|
|
107429
|
+
bar,
|
|
107430
|
+
`| [i] Detected empty ${displayPath}`,
|
|
107431
|
+
`| ${banner.itemCount} item(s) below will be reinstalled.`,
|
|
107432
|
+
"| Use --respect-deletions to preserve deletion.",
|
|
107433
|
+
bar
|
|
107434
|
+
];
|
|
107435
|
+
}
|
|
107436
|
+
if (banner.kind === "empty-dir-respected") {
|
|
107437
|
+
return [
|
|
107438
|
+
bar,
|
|
107439
|
+
`| [i] Detected empty ${displayPath}`,
|
|
107440
|
+
`| ${banner.itemCount} item(s) skipped (--respect-deletions active).`,
|
|
107441
|
+
bar
|
|
107442
|
+
];
|
|
107443
|
+
}
|
|
107444
|
+
return [];
|
|
107445
|
+
}
|
|
106860
107446
|
function buildSourceSummaryLines(counts, origins) {
|
|
106861
107447
|
const parts = [];
|
|
106862
107448
|
if (counts.agents > 0)
|
|
@@ -106880,6 +107466,148 @@ function buildSourceSummaryLines(counts, origins) {
|
|
|
106880
107466
|
|
|
106881
107467
|
// src/commands/migrate/migrate-command.ts
|
|
106882
107468
|
init_skill_directory_installer();
|
|
107469
|
+
function validateMutualExclusion(options2) {
|
|
107470
|
+
if (options2.install && options2.reconcile) {
|
|
107471
|
+
return "Pass either --install or --reconcile, not both.";
|
|
107472
|
+
}
|
|
107473
|
+
if (options2.reinstallEmptyDirs && options2.respectDeletions) {
|
|
107474
|
+
return "Pass either --reinstall-empty-dirs or --respect-deletions, not both.";
|
|
107475
|
+
}
|
|
107476
|
+
return null;
|
|
107477
|
+
}
|
|
107478
|
+
function resolveMigrationMode(options2, hasUnknownChecksums) {
|
|
107479
|
+
if (options2.install)
|
|
107480
|
+
return "install";
|
|
107481
|
+
if (options2.reconcile)
|
|
107482
|
+
return "reconcile";
|
|
107483
|
+
if (hasUnknownChecksums)
|
|
107484
|
+
return "install";
|
|
107485
|
+
return "reconcile";
|
|
107486
|
+
}
|
|
107487
|
+
function renderBanners(banners) {
|
|
107488
|
+
if (banners.length === 0)
|
|
107489
|
+
return;
|
|
107490
|
+
for (const banner of banners) {
|
|
107491
|
+
const lines = renderBannerLines(banner);
|
|
107492
|
+
if (lines.length > 0) {
|
|
107493
|
+
console.log();
|
|
107494
|
+
for (const line of lines) {
|
|
107495
|
+
console.log(line);
|
|
107496
|
+
}
|
|
107497
|
+
}
|
|
107498
|
+
}
|
|
107499
|
+
}
|
|
107500
|
+
async function runInstallMode(options2, discoveredItems, _selectedProviders, _installGlobally) {
|
|
107501
|
+
const interactive = process.stdout.isTTY && !options2.yes;
|
|
107502
|
+
if (!interactive) {
|
|
107503
|
+
return discoveredItems;
|
|
107504
|
+
}
|
|
107505
|
+
const toOption = (item) => ({
|
|
107506
|
+
value: item.name,
|
|
107507
|
+
label: item.name,
|
|
107508
|
+
hint: item.recommended ? "Recommended" : undefined
|
|
107509
|
+
});
|
|
107510
|
+
let selectedAgents = discoveredItems.agents;
|
|
107511
|
+
let selectedCommands = discoveredItems.commands;
|
|
107512
|
+
let selectedSkills = discoveredItems.skills;
|
|
107513
|
+
let selectedConfig = discoveredItems.configItem;
|
|
107514
|
+
let selectedRules = discoveredItems.ruleItems;
|
|
107515
|
+
let selectedHooks = discoveredItems.hookItems;
|
|
107516
|
+
if (discoveredItems.agents.length > 0) {
|
|
107517
|
+
const picked = await ae({
|
|
107518
|
+
message: `Select agents to install (${discoveredItems.agents.length} available)`,
|
|
107519
|
+
options: discoveredItems.agents.map(toOption),
|
|
107520
|
+
initialValues: discoveredItems.agents.map((a3) => a3.name),
|
|
107521
|
+
required: false
|
|
107522
|
+
});
|
|
107523
|
+
if (lD(picked)) {
|
|
107524
|
+
ue("Migrate cancelled");
|
|
107525
|
+
process.exit(0);
|
|
107526
|
+
}
|
|
107527
|
+
const pickedSet = new Set(picked);
|
|
107528
|
+
selectedAgents = discoveredItems.agents.filter((a3) => pickedSet.has(a3.name));
|
|
107529
|
+
}
|
|
107530
|
+
if (discoveredItems.commands.length > 0) {
|
|
107531
|
+
const picked = await ae({
|
|
107532
|
+
message: `Select commands to install (${discoveredItems.commands.length} available)`,
|
|
107533
|
+
options: discoveredItems.commands.map(toOption),
|
|
107534
|
+
initialValues: discoveredItems.commands.map((c2) => c2.name),
|
|
107535
|
+
required: false
|
|
107536
|
+
});
|
|
107537
|
+
if (lD(picked)) {
|
|
107538
|
+
ue("Migrate cancelled");
|
|
107539
|
+
process.exit(0);
|
|
107540
|
+
}
|
|
107541
|
+
const pickedSet = new Set(picked);
|
|
107542
|
+
selectedCommands = discoveredItems.commands.filter((c2) => pickedSet.has(c2.name));
|
|
107543
|
+
}
|
|
107544
|
+
if (discoveredItems.skills.length > 0) {
|
|
107545
|
+
const picked = await ae({
|
|
107546
|
+
message: `Select skills to install (${discoveredItems.skills.length} available, directory-level)`,
|
|
107547
|
+
options: discoveredItems.skills.map((s) => ({
|
|
107548
|
+
value: s.name,
|
|
107549
|
+
label: s.name,
|
|
107550
|
+
hint: "skill directory"
|
|
107551
|
+
})),
|
|
107552
|
+
initialValues: discoveredItems.skills.map((s) => s.name),
|
|
107553
|
+
required: false
|
|
107554
|
+
});
|
|
107555
|
+
if (lD(picked)) {
|
|
107556
|
+
ue("Migrate cancelled");
|
|
107557
|
+
process.exit(0);
|
|
107558
|
+
}
|
|
107559
|
+
const pickedSet = new Set(picked);
|
|
107560
|
+
selectedSkills = discoveredItems.skills.filter((s) => pickedSet.has(s.name));
|
|
107561
|
+
}
|
|
107562
|
+
if (discoveredItems.configItem) {
|
|
107563
|
+
const include = await se({
|
|
107564
|
+
message: "Include CLAUDE.md config?",
|
|
107565
|
+
initialValue: true
|
|
107566
|
+
});
|
|
107567
|
+
if (lD(include)) {
|
|
107568
|
+
ue("Migrate cancelled");
|
|
107569
|
+
process.exit(0);
|
|
107570
|
+
}
|
|
107571
|
+
if (!include)
|
|
107572
|
+
selectedConfig = null;
|
|
107573
|
+
}
|
|
107574
|
+
if (discoveredItems.ruleItems.length > 0) {
|
|
107575
|
+
const picked = await ae({
|
|
107576
|
+
message: `Select rules to install (${discoveredItems.ruleItems.length} available)`,
|
|
107577
|
+
options: discoveredItems.ruleItems.map(toOption),
|
|
107578
|
+
initialValues: discoveredItems.ruleItems.map((r2) => r2.name),
|
|
107579
|
+
required: false
|
|
107580
|
+
});
|
|
107581
|
+
if (lD(picked)) {
|
|
107582
|
+
ue("Migrate cancelled");
|
|
107583
|
+
process.exit(0);
|
|
107584
|
+
}
|
|
107585
|
+
const pickedSet = new Set(picked);
|
|
107586
|
+
selectedRules = discoveredItems.ruleItems.filter((r2) => pickedSet.has(r2.name));
|
|
107587
|
+
}
|
|
107588
|
+
if (discoveredItems.hookItems.length > 0) {
|
|
107589
|
+
const picked = await ae({
|
|
107590
|
+
message: `Select hooks to install (${discoveredItems.hookItems.length} available)`,
|
|
107591
|
+
options: discoveredItems.hookItems.map(toOption),
|
|
107592
|
+
initialValues: discoveredItems.hookItems.map((h2) => h2.name),
|
|
107593
|
+
required: false
|
|
107594
|
+
});
|
|
107595
|
+
if (lD(picked)) {
|
|
107596
|
+
ue("Migrate cancelled");
|
|
107597
|
+
process.exit(0);
|
|
107598
|
+
}
|
|
107599
|
+
const pickedSet = new Set(picked);
|
|
107600
|
+
selectedHooks = discoveredItems.hookItems.filter((h2) => pickedSet.has(h2.name));
|
|
107601
|
+
}
|
|
107602
|
+
return {
|
|
107603
|
+
agents: selectedAgents,
|
|
107604
|
+
commands: selectedCommands,
|
|
107605
|
+
skills: selectedSkills,
|
|
107606
|
+
configItem: selectedConfig,
|
|
107607
|
+
ruleItems: selectedRules,
|
|
107608
|
+
hookItems: selectedHooks
|
|
107609
|
+
};
|
|
107610
|
+
}
|
|
106883
107611
|
function getProviderPathKey(type) {
|
|
106884
107612
|
switch (type) {
|
|
106885
107613
|
case "agent":
|
|
@@ -106894,8 +107622,6 @@ function getProviderPathKey(type) {
|
|
|
106894
107622
|
return "hooks";
|
|
106895
107623
|
case "skill":
|
|
106896
107624
|
return "skills";
|
|
106897
|
-
default:
|
|
106898
|
-
return type;
|
|
106899
107625
|
}
|
|
106900
107626
|
}
|
|
106901
107627
|
function shouldExecuteAction2(action) {
|
|
@@ -106977,6 +107703,11 @@ function inferKitTypeFromSourceMetadata(sourceMetadata) {
|
|
|
106977
107703
|
}
|
|
106978
107704
|
async function migrateCommand(options2) {
|
|
106979
107705
|
console.log();
|
|
107706
|
+
const mutexError = validateMutualExclusion(options2);
|
|
107707
|
+
if (mutexError) {
|
|
107708
|
+
f2.error(mutexError);
|
|
107709
|
+
process.exit(1);
|
|
107710
|
+
}
|
|
106980
107711
|
try {
|
|
106981
107712
|
const scope = resolveMigrationScope(process.argv.slice(2), options2);
|
|
106982
107713
|
const spinner = de();
|
|
@@ -107170,31 +107901,66 @@ async function migrateCommand(options2) {
|
|
|
107170
107901
|
setTaxonomyOverrides(ckConfigResult.config.modelTaxonomy);
|
|
107171
107902
|
const reconcileSpinner = de();
|
|
107172
107903
|
reconcileSpinner.start("Computing migration plan...");
|
|
107904
|
+
const registry = await readPortableRegistry();
|
|
107905
|
+
const hasUnknownChecksums = registry.installations.some((entry) => !entry.sourceChecksum || entry.sourceChecksum === "unknown" || !entry.targetChecksum || entry.targetChecksum === "unknown");
|
|
107906
|
+
const migrationMode = resolveMigrationMode(options2, hasUnknownChecksums);
|
|
107907
|
+
let effectiveAgents = agents2;
|
|
107908
|
+
let effectiveCommands = commands;
|
|
107909
|
+
let effectiveSkills = skills;
|
|
107910
|
+
let effectiveConfigItem = configItem;
|
|
107911
|
+
let effectiveRuleItems = ruleItems;
|
|
107912
|
+
let effectiveHookItems = hookItems;
|
|
107913
|
+
if (migrationMode === "install") {
|
|
107914
|
+
reconcileSpinner.stop("Discovery complete");
|
|
107915
|
+
if (!options2.yes && process.stdout.isTTY) {
|
|
107916
|
+
f2.info(`[i] Smart default: ${hasUnknownChecksums ? "unknown checksums detected" : "--install flag"} — entering install picker mode.`);
|
|
107917
|
+
}
|
|
107918
|
+
const picked = await runInstallMode(options2, {
|
|
107919
|
+
agents: agents2,
|
|
107920
|
+
commands,
|
|
107921
|
+
skills,
|
|
107922
|
+
configItem,
|
|
107923
|
+
ruleItems,
|
|
107924
|
+
hookItems
|
|
107925
|
+
}, selectedProviders, installGlobally);
|
|
107926
|
+
effectiveAgents = picked.agents;
|
|
107927
|
+
effectiveCommands = picked.commands;
|
|
107928
|
+
effectiveSkills = picked.skills;
|
|
107929
|
+
effectiveConfigItem = picked.configItem;
|
|
107930
|
+
effectiveRuleItems = picked.ruleItems;
|
|
107931
|
+
effectiveHookItems = picked.hookItems;
|
|
107932
|
+
reconcileSpinner.start("Computing migration plan...");
|
|
107933
|
+
}
|
|
107173
107934
|
const sourceStates = await computeSourceStates({
|
|
107174
|
-
agents:
|
|
107175
|
-
commands,
|
|
107176
|
-
config:
|
|
107177
|
-
rules:
|
|
107178
|
-
hooks:
|
|
107935
|
+
agents: effectiveAgents,
|
|
107936
|
+
commands: effectiveCommands,
|
|
107937
|
+
config: effectiveConfigItem,
|
|
107938
|
+
rules: effectiveRuleItems,
|
|
107939
|
+
hooks: effectiveHookItems
|
|
107179
107940
|
}, selectedProviders);
|
|
107180
107941
|
const targetStates = await computeTargetStates(selectedProviders, installGlobally);
|
|
107181
|
-
const registry = await readPortableRegistry();
|
|
107182
107942
|
const providerConfigs = selectedProviders.map((provider) => ({
|
|
107183
107943
|
provider,
|
|
107184
107944
|
global: installGlobally
|
|
107185
107945
|
}));
|
|
107946
|
+
const portableTypes = ["agent", "command", "config", "rules", "hooks"];
|
|
107947
|
+
const typeDirectoryStates = buildTypeDirectoryStates(selectedProviders.map((provider) => ({ provider, global: installGlobally })), [...portableTypes]);
|
|
107948
|
+
const reinstallEmptyDirs = options2.respectDeletions ? false : options2.reinstallEmptyDirs ?? true;
|
|
107186
107949
|
const plan = reconcile({
|
|
107187
107950
|
sourceItems: sourceStates,
|
|
107188
107951
|
registry,
|
|
107189
107952
|
targetStates,
|
|
107190
107953
|
providerConfigs,
|
|
107191
|
-
force: options2.force
|
|
107954
|
+
force: options2.force,
|
|
107955
|
+
typeDirectoryStates,
|
|
107956
|
+
respectDeletions: !reinstallEmptyDirs
|
|
107192
107957
|
});
|
|
107193
107958
|
reconcileSpinner.stop("Plan computed");
|
|
107194
107959
|
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
107960
|
+
renderBanners(plan.banners);
|
|
107195
107961
|
displayReconcilePlan(plan, { color: useColor });
|
|
107196
107962
|
if (options2.dryRun) {
|
|
107197
|
-
displayMigrationSummary(plan, buildDryRunFallbackResults(
|
|
107963
|
+
displayMigrationSummary(plan, buildDryRunFallbackResults(effectiveSkills, selectedProviders, installGlobally, plan.actions), { color: useColor, dryRun: true });
|
|
107198
107964
|
return;
|
|
107199
107965
|
}
|
|
107200
107966
|
if (plan.hasConflicts) {
|
|
@@ -107204,7 +107970,7 @@ async function migrateCommand(options2) {
|
|
|
107204
107970
|
if (!action.diff && action.targetPath && existsSync64(action.targetPath)) {
|
|
107205
107971
|
try {
|
|
107206
107972
|
const targetContent = await readFile61(action.targetPath, "utf-8");
|
|
107207
|
-
const sourceItem =
|
|
107973
|
+
const sourceItem = effectiveAgents.find((a3) => a3.name === action.item) || effectiveCommands.find((c2) => c2.name === action.item) || (effectiveConfigItem?.name === action.item ? effectiveConfigItem : null) || effectiveRuleItems.find((r2) => r2.name === action.item) || effectiveHookItems.find((h2) => h2.name === action.item);
|
|
107208
107974
|
if (sourceItem) {
|
|
107209
107975
|
const providerConfig = providers[action.provider];
|
|
107210
107976
|
const pathConfigKey = getProviderPathKey(action.type);
|
|
@@ -107242,12 +108008,12 @@ async function migrateCommand(options2) {
|
|
|
107242
108008
|
}
|
|
107243
108009
|
let allResults = [];
|
|
107244
108010
|
const installOpts = { global: installGlobally };
|
|
107245
|
-
const agentByName = new Map(
|
|
107246
|
-
const commandByName = new Map(
|
|
107247
|
-
const skillByName = new Map(
|
|
107248
|
-
const configByName = new Map(
|
|
107249
|
-
const ruleByName = new Map(
|
|
107250
|
-
const hookByName = new Map(
|
|
108011
|
+
const agentByName = new Map(effectiveAgents.map((item) => [item.name, item]));
|
|
108012
|
+
const commandByName = new Map(effectiveCommands.map((item) => [item.name, item]));
|
|
108013
|
+
const skillByName = new Map(effectiveSkills.map((item) => [item.name, item]));
|
|
108014
|
+
const configByName = new Map(effectiveConfigItem ? [[effectiveConfigItem.name, effectiveConfigItem]] : []);
|
|
108015
|
+
const ruleByName = new Map(effectiveRuleItems.map((item) => [item.name, item]));
|
|
108016
|
+
const hookByName = new Map(effectiveHookItems.map((item) => [item.name, item]));
|
|
107251
108017
|
const successfulHookFiles = new Map;
|
|
107252
108018
|
const successfulHookAbsPaths = new Map;
|
|
107253
108019
|
const postProgressWarnings = [];
|
|
@@ -107299,10 +108065,10 @@ async function migrateCommand(options2) {
|
|
|
107299
108065
|
}
|
|
107300
108066
|
}
|
|
107301
108067
|
const plannedSkillActions = plannedExecActions.filter((action) => action.type === "skill").length;
|
|
107302
|
-
if (
|
|
108068
|
+
if (effectiveSkills.length > 0 && plannedSkillActions === 0) {
|
|
107303
108069
|
const skillProviders = selectedProviders.filter((pv) => getProvidersSupporting("skills").includes(pv));
|
|
107304
108070
|
for (const provider of skillProviders) {
|
|
107305
|
-
for (const skill of
|
|
108071
|
+
for (const skill of effectiveSkills) {
|
|
107306
108072
|
writeTasks.push({ item: skill, provider, type: "skill" });
|
|
107307
108073
|
}
|
|
107308
108074
|
}
|
|
@@ -108010,7 +108776,7 @@ Please use only one download method.`);
|
|
|
108010
108776
|
}
|
|
108011
108777
|
// src/commands/plan/plan-command.ts
|
|
108012
108778
|
init_output_manager();
|
|
108013
|
-
import { existsSync as existsSync67, statSync as
|
|
108779
|
+
import { existsSync as existsSync67, statSync as statSync12 } from "node:fs";
|
|
108014
108780
|
import { dirname as dirname46, isAbsolute as isAbsolute11, join as join147, parse as parse7, resolve as resolve45 } from "node:path";
|
|
108015
108781
|
|
|
108016
108782
|
// src/commands/plan/plan-read-handlers.ts
|
|
@@ -108020,7 +108786,7 @@ init_plans_registry();
|
|
|
108020
108786
|
init_logger();
|
|
108021
108787
|
init_output_manager();
|
|
108022
108788
|
var import_picocolors32 = __toESM(require_picocolors(), 1);
|
|
108023
|
-
import { existsSync as existsSync66, statSync as
|
|
108789
|
+
import { existsSync as existsSync66, statSync as statSync11 } from "node:fs";
|
|
108024
108790
|
import { basename as basename27, dirname as dirname44, join as join146, relative as relative27, resolve as resolve43 } from "node:path";
|
|
108025
108791
|
|
|
108026
108792
|
// src/commands/plan/plan-dependencies.ts
|
|
@@ -108190,7 +108956,7 @@ async function handleStatus(target, options2) {
|
|
|
108190
108956
|
}
|
|
108191
108957
|
const effectiveTarget = !resolvedTarget && globalBaseDir ? globalBaseDir : resolvedTarget;
|
|
108192
108958
|
const t = effectiveTarget ? resolve43(effectiveTarget) : null;
|
|
108193
|
-
const plansDir = t && existsSync66(t) &&
|
|
108959
|
+
const plansDir = t && existsSync66(t) && statSync11(t).isDirectory() && !existsSync66(join146(t, "plan.md")) ? t : null;
|
|
108194
108960
|
if (plansDir) {
|
|
108195
108961
|
const planFiles = scanPlanDir(plansDir);
|
|
108196
108962
|
if (planFiles.length === 0) {
|
|
@@ -108602,7 +109368,7 @@ function resolveTargetPath(target, baseDir) {
|
|
|
108602
109368
|
function resolvePlanFile(target, baseDir) {
|
|
108603
109369
|
const t = target ? resolveTargetPath(target, baseDir) : baseDir ? resolve45(baseDir) : process.cwd();
|
|
108604
109370
|
if (existsSync67(t)) {
|
|
108605
|
-
const stat23 =
|
|
109371
|
+
const stat23 = statSync12(t);
|
|
108606
109372
|
if (stat23.isFile())
|
|
108607
109373
|
return t;
|
|
108608
109374
|
const candidate = join147(t, "plan.md");
|
|
@@ -109815,7 +110581,7 @@ async function detectInstallations() {
|
|
|
109815
110581
|
}
|
|
109816
110582
|
|
|
109817
110583
|
// src/commands/uninstall/removal-handler.ts
|
|
109818
|
-
import { readdirSync as
|
|
110584
|
+
import { readdirSync as readdirSync10, rmSync as rmSync5 } from "node:fs";
|
|
109819
110585
|
import { basename as basename29, join as join150, resolve as resolve47, sep as sep12 } from "node:path";
|
|
109820
110586
|
init_logger();
|
|
109821
110587
|
init_safe_prompts();
|
|
@@ -109824,7 +110590,7 @@ var import_fs_extra44 = __toESM(require_lib3(), 1);
|
|
|
109824
110590
|
|
|
109825
110591
|
// src/commands/uninstall/analysis-handler.ts
|
|
109826
110592
|
init_metadata_migration();
|
|
109827
|
-
import { readdirSync as
|
|
110593
|
+
import { readdirSync as readdirSync9, rmSync as rmSync4 } from "node:fs";
|
|
109828
110594
|
import { dirname as dirname47, join as join149 } from "node:path";
|
|
109829
110595
|
init_logger();
|
|
109830
110596
|
init_safe_prompts();
|
|
@@ -109850,7 +110616,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
|
|
|
109850
110616
|
let currentDir = dirname47(filePath);
|
|
109851
110617
|
while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
|
|
109852
110618
|
try {
|
|
109853
|
-
const entries =
|
|
110619
|
+
const entries = readdirSync9(currentDir);
|
|
109854
110620
|
if (entries.length === 0) {
|
|
109855
110621
|
rmSync4(currentDir, { recursive: true });
|
|
109856
110622
|
cleaned++;
|
|
@@ -110091,7 +110857,7 @@ async function removeInstallations(installations, options2) {
|
|
|
110091
110857
|
}
|
|
110092
110858
|
}
|
|
110093
110859
|
try {
|
|
110094
|
-
const remaining =
|
|
110860
|
+
const remaining = readdirSync10(installation.path);
|
|
110095
110861
|
if (remaining.length === 0) {
|
|
110096
110862
|
rmSync5(installation.path, { recursive: true });
|
|
110097
110863
|
logger.debug(`Removed empty installation directory: ${installation.path}`);
|
|
@@ -111944,7 +112710,7 @@ Run this command from a directory with a GitHub remote.`);
|
|
|
111944
112710
|
// src/commands/watch/phases/watch-logger.ts
|
|
111945
112711
|
init_logger();
|
|
111946
112712
|
init_path_resolver();
|
|
111947
|
-
import { createWriteStream as createWriteStream3, statSync as
|
|
112713
|
+
import { createWriteStream as createWriteStream3, statSync as statSync13 } from "node:fs";
|
|
111948
112714
|
import { existsSync as existsSync73 } from "node:fs";
|
|
111949
112715
|
import { mkdir as mkdir38, rename as rename13 } from "node:fs/promises";
|
|
111950
112716
|
import { join as join156 } from "node:path";
|
|
@@ -112012,7 +112778,7 @@ class WatchLogger {
|
|
|
112012
112778
|
return;
|
|
112013
112779
|
if (this.maxBytes > 0 && this.logPath) {
|
|
112014
112780
|
try {
|
|
112015
|
-
const stats =
|
|
112781
|
+
const stats = statSync13(this.logPath);
|
|
112016
112782
|
if (stats.size >= this.maxBytes) {
|
|
112017
112783
|
this.rotateLog();
|
|
112018
112784
|
}
|
|
@@ -112390,7 +113156,7 @@ function registerCommands(cli) {
|
|
|
112390
113156
|
cli.command("api [action] [service] [path]", "Interact with ClaudeKit API and proxy services").option("--method <method>", "HTTP method for proxy requests (default: GET)").option("--body <json>", "Request body as JSON string (proxy only)").option("--query <json>", "Query params as JSON string (proxy only)").option("--key <key>", "API key to use (setup only)").option("--force", "Force re-setup even if key exists (setup only)").option("--json", "Output raw JSON instead of formatted display").option("--locale <locale>", "Locale for vidcap summary/caption (default: en)").option("--max-results <n>", "Max results for vidcap search").option("--second <s>", "Timestamp in seconds for vidcap screenshot").option("--order <order>", "Sort order for vidcap comments (time/relevance)").option("--format <fmt>", "Summary format for reviewweb (bullet/paragraph)").option("--max-length <n>", "Max summary length for reviewweb").option("--instructions <text>", "Extraction instructions for reviewweb extract").option("--template <json>", "JSON template for reviewweb extract").option("--type <type>", "Link type filter for reviewweb links (web/image/file/all)").option("--country <code>", "Country code for reviewweb SEO commands").action(async (action, service, path16, options2) => {
|
|
112391
113157
|
await apiCommand(action, service, path16, options2);
|
|
112392
113158
|
});
|
|
112393
|
-
cli.command("migrate", "Migrate agents, commands, skills, config, rules, and hooks to other providers").option("-a, --agent <agents...>", "Target providers (cursor, codex, droid, opencode, etc.)").option("-g, --global", "Install globally instead of project-level").option("--all", "Migrate to all supported providers").option("-y, --yes", "Skip confirmation prompts").option("--config", "Migrate CLAUDE.md config only").option("--rules", "Migrate .claude/rules/ only").option("--hooks", "Migrate .claude/hooks/ only").option("--skip-config", "Skip config migration").option("--skip-rules", "Skip rules migration").option("--skip-hooks", "Skip hooks migration").option("--source <path>", "Custom CLAUDE.md source path (config only, not agents/commands/skills/hooks)").option("--dry-run", "Preview migration targets without writing files").option("-f, --force", "Force reinstall deleted/edited items").action(async (options2) => {
|
|
113159
|
+
cli.command("migrate", "Migrate agents, commands, skills, config, rules, and hooks to other providers").option("-a, --agent <agents...>", "Target providers (cursor, codex, droid, opencode, etc.)").option("-g, --global", "Install globally instead of project-level").option("--all", "Migrate to all supported providers").option("-y, --yes", "Skip confirmation prompts").option("--config", "Migrate CLAUDE.md config only").option("--rules", "Migrate .claude/rules/ only").option("--hooks", "Migrate .claude/hooks/ only").option("--skip-config", "Skip config migration").option("--skip-rules", "Skip rules migration").option("--skip-hooks", "Skip hooks migration").option("--source <path>", "Custom CLAUDE.md source path (config only, not agents/commands/skills/hooks)").option("--dry-run", "Preview migration targets without writing files").option("-f, --force", "Force reinstall deleted/edited items").option("--install", "Opt-in install picker mode (select specific items to install)").option("--reconcile", "Force reconcile mode (current default when registry is valid)").option("--reinstall-empty-dirs", "Reinstall all items when their type directory is empty (default: true)").option("--respect-deletions", "Preserve deletion even when type directory is empty (disables reinstall-empty-dirs)").action(async (options2) => {
|
|
112394
113160
|
if (options2.agent && !Array.isArray(options2.agent)) {
|
|
112395
113161
|
options2.agent = [options2.agent];
|
|
112396
113162
|
}
|