codebyplan 1.13.23 → 1.13.25
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/dist/cli.js +445 -187
- package/package.json +2 -2
- package/templates/agents/cbp-cc-executor.md +7 -7
- package/templates/agents/cbp-improve-round.md +2 -2
- package/templates/agents/cbp-round-executor.md +20 -4
- package/templates/agents/cbp-testing-qa-agent.md +3 -3
- package/templates/hooks/README.md +1 -1
- package/templates/hooks/cbp-statusline.mjs +106 -11
- package/templates/hooks/cbp-statusline.py +79 -13
- package/templates/hooks/cbp-statusline.sh +97 -17
- package/templates/hooks/validate-structure-patterns.sh +1 -1
- package/templates/skills/cbp-checkpoint-check/SKILL.md +2 -2
- package/templates/skills/cbp-checkpoint-complete/SKILL.md +2 -2
- package/templates/skills/cbp-merge-main/SKILL.md +1 -1
- package/templates/skills/cbp-round-end/SKILL.md +12 -35
- package/templates/skills/cbp-round-end/reference/findings-presentation.md +76 -3
- package/templates/skills/cbp-round-execute/SKILL.md +13 -60
- package/templates/skills/cbp-round-start/SKILL.md +3 -1
- package/templates/skills/cbp-round-update/SKILL.md +1 -1
- package/templates/skills/cbp-session-start/SKILL.md +2 -0
- package/templates/skills/cbp-ship-configure/SKILL.md +1 -1
- package/templates/skills/cbp-ship-configure/reference/supabase.md +2 -2
- package/templates/skills/cbp-ship-main/SKILL.md +2 -0
- package/templates/skills/cbp-standalone-task-create/SKILL.md +1 -1
- package/templates/skills/cbp-task-check/SKILL.md +1 -1
- package/templates/skills/cbp-task-complete/SKILL.md +1 -1
- package/templates/skills/cbp-task-create/SKILL.md +50 -1
- package/templates/skills/cbp-task-start/SKILL.md +2 -2
- package/templates/skills/cbp-task-testing/SKILL.md +2 -2
- package/templates/skills/cbp-todo/SKILL.md +36 -3
- package/templates/skills/cbp-todo/qa-regression.md +8 -1
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ var VERSION, PACKAGE_NAME;
|
|
|
14
14
|
var init_version = __esm({
|
|
15
15
|
"src/lib/version.ts"() {
|
|
16
16
|
"use strict";
|
|
17
|
-
VERSION = "1.13.
|
|
17
|
+
VERSION = "1.13.25";
|
|
18
18
|
PACKAGE_NAME = "codebyplan";
|
|
19
19
|
}
|
|
20
20
|
});
|
|
@@ -149,6 +149,7 @@ var init_gitignore_block = __esm({
|
|
|
149
149
|
".codebyplan/device.local.json",
|
|
150
150
|
".codebyplan/statusline.local.json",
|
|
151
151
|
".codebyplan/worktree.local.json",
|
|
152
|
+
".codebyplan/claude-status.local.json",
|
|
152
153
|
".codebyplan.local.json"
|
|
153
154
|
];
|
|
154
155
|
GITIGNORE_BLOCK_START = "# >>> codebyplan (managed) >>>";
|
|
@@ -225,8 +226,8 @@ async function readLocalConfig(projectPath, onMigrationNotice) {
|
|
|
225
226
|
}
|
|
226
227
|
async function writeLocalConfig(projectPath, config) {
|
|
227
228
|
const content = { device_id: config.device_id };
|
|
228
|
-
const
|
|
229
|
-
const dirPath = dirname(
|
|
229
|
+
const path10 = localConfigPath(projectPath);
|
|
230
|
+
const dirPath = dirname(path10);
|
|
230
231
|
let phase = "stat config directory";
|
|
231
232
|
try {
|
|
232
233
|
try {
|
|
@@ -246,7 +247,7 @@ async function writeLocalConfig(projectPath, config) {
|
|
|
246
247
|
phase = "create config directory";
|
|
247
248
|
await mkdir(dirPath, { recursive: true });
|
|
248
249
|
phase = "write local config";
|
|
249
|
-
await writeFile2(
|
|
250
|
+
await writeFile2(path10, JSON.stringify(content, null, 2) + "\n", "utf-8");
|
|
250
251
|
} catch (err) {
|
|
251
252
|
const code = err.code;
|
|
252
253
|
if (code === "LEGACY_FILE_BLOCKS_DIR") {
|
|
@@ -396,7 +397,8 @@ var init_statusline_config = __esm({
|
|
|
396
397
|
rate_limits: true,
|
|
397
398
|
repo_pr: true,
|
|
398
399
|
worktree: true,
|
|
399
|
-
infra_drift: true
|
|
400
|
+
infra_drift: true,
|
|
401
|
+
package_freshness: true
|
|
400
402
|
},
|
|
401
403
|
no_color: false
|
|
402
404
|
};
|
|
@@ -467,12 +469,12 @@ async function readFallback(filename) {
|
|
|
467
469
|
}
|
|
468
470
|
}
|
|
469
471
|
async function writeFallback(filename, data) {
|
|
470
|
-
const
|
|
471
|
-
await mkdir3(dirname2(
|
|
472
|
-
await writeFile4(
|
|
472
|
+
const path10 = fallbackFile(filename);
|
|
473
|
+
await mkdir3(dirname2(path10), { recursive: true });
|
|
474
|
+
await writeFile4(path10, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
473
475
|
if (platform() !== "win32") {
|
|
474
476
|
try {
|
|
475
|
-
await chmod(
|
|
477
|
+
await chmod(path10, 384);
|
|
476
478
|
} catch {
|
|
477
479
|
}
|
|
478
480
|
}
|
|
@@ -677,8 +679,8 @@ async function getAuthHeaders() {
|
|
|
677
679
|
return { headers: { "x-api-key": key }, via: "api_key" };
|
|
678
680
|
}
|
|
679
681
|
}
|
|
680
|
-
function buildUrl(
|
|
681
|
-
const url = new URL(`${baseUrl()}/api${
|
|
682
|
+
function buildUrl(path10, params) {
|
|
683
|
+
const url = new URL(`${baseUrl()}/api${path10}`);
|
|
682
684
|
if (params) {
|
|
683
685
|
for (const [key, value] of Object.entries(params)) {
|
|
684
686
|
if (value !== void 0) {
|
|
@@ -695,10 +697,10 @@ function isRetryable(err) {
|
|
|
695
697
|
return false;
|
|
696
698
|
}
|
|
697
699
|
function delay(ms) {
|
|
698
|
-
return new Promise((
|
|
700
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
699
701
|
}
|
|
700
|
-
async function request(method,
|
|
701
|
-
const url = buildUrl(
|
|
702
|
+
async function request(method, path10, options) {
|
|
703
|
+
const url = buildUrl(path10, options?.params);
|
|
702
704
|
const auth = await getAuthHeaders();
|
|
703
705
|
let lastError;
|
|
704
706
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
@@ -718,7 +720,7 @@ async function request(method, path8, options) {
|
|
|
718
720
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
719
721
|
});
|
|
720
722
|
if (!res.ok) {
|
|
721
|
-
let message = `API ${method} ${
|
|
723
|
+
let message = `API ${method} ${path10} failed with status ${res.status}`;
|
|
722
724
|
let code;
|
|
723
725
|
try {
|
|
724
726
|
const body = await res.json();
|
|
@@ -752,14 +754,14 @@ async function request(method, path8, options) {
|
|
|
752
754
|
}
|
|
753
755
|
throw lastError;
|
|
754
756
|
}
|
|
755
|
-
async function apiGet(
|
|
756
|
-
return request("GET",
|
|
757
|
+
async function apiGet(path10, params) {
|
|
758
|
+
return request("GET", path10, { params });
|
|
757
759
|
}
|
|
758
|
-
async function apiPost(
|
|
759
|
-
return request("POST",
|
|
760
|
+
async function apiPost(path10, body) {
|
|
761
|
+
return request("POST", path10, { body });
|
|
760
762
|
}
|
|
761
|
-
async function apiPut(
|
|
762
|
-
return request("PUT",
|
|
763
|
+
async function apiPut(path10, body) {
|
|
764
|
+
return request("PUT", path10, { body });
|
|
763
765
|
}
|
|
764
766
|
async function callMcpTool(toolName, params) {
|
|
765
767
|
const url = mcpEndpoint();
|
|
@@ -1053,7 +1055,7 @@ var init_device_flow = __esm({
|
|
|
1053
1055
|
this.name = "OAuthInvalidClientError";
|
|
1054
1056
|
}
|
|
1055
1057
|
};
|
|
1056
|
-
defaultSleep = (ms) => new Promise((
|
|
1058
|
+
defaultSleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
1057
1059
|
}
|
|
1058
1060
|
});
|
|
1059
1061
|
|
|
@@ -1861,9 +1863,9 @@ import { createInterface } from "node:readline/promises";
|
|
|
1861
1863
|
function getConfigPath(scope) {
|
|
1862
1864
|
return scope === "user" ? join9(homedir4(), ".claude.json") : join9(process.cwd(), ".mcp.json");
|
|
1863
1865
|
}
|
|
1864
|
-
async function readConfig(
|
|
1866
|
+
async function readConfig(path10) {
|
|
1865
1867
|
try {
|
|
1866
|
-
const raw = await readFile6(
|
|
1868
|
+
const raw = await readFile6(path10, "utf-8");
|
|
1867
1869
|
const parsed = JSON.parse(raw);
|
|
1868
1870
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
1869
1871
|
return parsed;
|
|
@@ -2088,8 +2090,8 @@ async function runSetup() {
|
|
|
2088
2090
|
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
2089
2091
|
let branch = "main";
|
|
2090
2092
|
try {
|
|
2091
|
-
const { execSync:
|
|
2092
|
-
branch =
|
|
2093
|
+
const { execSync: execSync9 } = await import("node:child_process");
|
|
2094
|
+
branch = execSync9("git symbolic-ref --short HEAD", {
|
|
2093
2095
|
cwd: projectPath,
|
|
2094
2096
|
encoding: "utf-8"
|
|
2095
2097
|
}).trim();
|
|
@@ -2450,9 +2452,9 @@ import { join as join11 } from "node:path";
|
|
|
2450
2452
|
function configPaths() {
|
|
2451
2453
|
return [join11(homedir5(), ".claude.json"), join11(process.cwd(), ".mcp.json")];
|
|
2452
2454
|
}
|
|
2453
|
-
async function readConfig2(
|
|
2455
|
+
async function readConfig2(path10) {
|
|
2454
2456
|
try {
|
|
2455
|
-
const raw = await readFile8(
|
|
2457
|
+
const raw = await readFile8(path10, "utf-8");
|
|
2456
2458
|
const parsed = JSON.parse(raw);
|
|
2457
2459
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
2458
2460
|
return parsed;
|
|
@@ -2466,7 +2468,7 @@ function entryHasLegacyApiKey(entry) {
|
|
|
2466
2468
|
if (!entry || !entry.headers) return false;
|
|
2467
2469
|
return "x-api-key" in entry.headers;
|
|
2468
2470
|
}
|
|
2469
|
-
async function rewriteConfig(
|
|
2471
|
+
async function rewriteConfig(path10, config, newUrl) {
|
|
2470
2472
|
const servers = config.mcpServers;
|
|
2471
2473
|
if (!servers) return false;
|
|
2472
2474
|
const entry = servers.codebyplan;
|
|
@@ -2474,7 +2476,7 @@ async function rewriteConfig(path8, config, newUrl) {
|
|
|
2474
2476
|
if (!entryHasLegacyApiKey(entry) && entry.url === newUrl && entry.type === "http")
|
|
2475
2477
|
return false;
|
|
2476
2478
|
servers.codebyplan = { type: "http", url: newUrl };
|
|
2477
|
-
await writeFile7(
|
|
2479
|
+
await writeFile7(path10, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2478
2480
|
return true;
|
|
2479
2481
|
}
|
|
2480
2482
|
async function runUpgradeAuth() {
|
|
@@ -2482,12 +2484,12 @@ async function runUpgradeAuth() {
|
|
|
2482
2484
|
await runLogin();
|
|
2483
2485
|
const newUrl = mcpEndpoint();
|
|
2484
2486
|
let migrated = 0;
|
|
2485
|
-
for (const
|
|
2486
|
-
const config = await readConfig2(
|
|
2487
|
+
for (const path10 of configPaths()) {
|
|
2488
|
+
const config = await readConfig2(path10);
|
|
2487
2489
|
if (!config) continue;
|
|
2488
|
-
const changed = await rewriteConfig(
|
|
2490
|
+
const changed = await rewriteConfig(path10, config, newUrl);
|
|
2489
2491
|
if (changed) {
|
|
2490
|
-
console.log(` Updated ${
|
|
2492
|
+
console.log(` Updated ${path10}`);
|
|
2491
2493
|
migrated++;
|
|
2492
2494
|
}
|
|
2493
2495
|
}
|
|
@@ -3759,9 +3761,9 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3759
3761
|
Install ${missingPkgs.length} missing packages? [Y/n] `
|
|
3760
3762
|
);
|
|
3761
3763
|
if (confirmed) {
|
|
3762
|
-
const { execSync:
|
|
3764
|
+
const { execSync: execSync9 } = await import("node:child_process");
|
|
3763
3765
|
try {
|
|
3764
|
-
|
|
3766
|
+
execSync9(installCmd, { cwd: projectPath, stdio: "inherit" });
|
|
3765
3767
|
console.log(" Packages installed.\n");
|
|
3766
3768
|
} catch (err) {
|
|
3767
3769
|
console.error(
|
|
@@ -4196,7 +4198,7 @@ function setRetryDelayMs(ms) {
|
|
|
4196
4198
|
RETRY_DELAY_MS = ms;
|
|
4197
4199
|
}
|
|
4198
4200
|
function sleep(ms) {
|
|
4199
|
-
return new Promise((
|
|
4201
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
4200
4202
|
}
|
|
4201
4203
|
function isTransientMcpError(err) {
|
|
4202
4204
|
if (!(err instanceof McpError)) return false;
|
|
@@ -6247,6 +6249,8 @@ var init_create_repo = __esm({
|
|
|
6247
6249
|
// src/cli/version-status.ts
|
|
6248
6250
|
var version_status_exports = {};
|
|
6249
6251
|
__export(version_status_exports, {
|
|
6252
|
+
fetchLatestVersion: () => fetchLatestVersion,
|
|
6253
|
+
resolveGuard: () => resolveGuard,
|
|
6250
6254
|
runVersionStatus: () => runVersionStatus
|
|
6251
6255
|
});
|
|
6252
6256
|
import { execFileSync, execSync as execSync6 } from "node:child_process";
|
|
@@ -6669,8 +6673,8 @@ async function resolveWorktreePortAllocations(repoId, projectPath) {
|
|
|
6669
6673
|
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
6670
6674
|
let branch = "main";
|
|
6671
6675
|
try {
|
|
6672
|
-
const { execSync:
|
|
6673
|
-
branch =
|
|
6676
|
+
const { execSync: execSync9 } = await import("node:child_process");
|
|
6677
|
+
branch = execSync9("git symbolic-ref --short HEAD", {
|
|
6674
6678
|
cwd: projectPath,
|
|
6675
6679
|
encoding: "utf-8"
|
|
6676
6680
|
}).trim();
|
|
@@ -7675,10 +7679,10 @@ async function runTechStack() {
|
|
|
7675
7679
|
);
|
|
7676
7680
|
}
|
|
7677
7681
|
try {
|
|
7678
|
-
const { execSync:
|
|
7682
|
+
const { execSync: execSync9 } = await import("node:child_process");
|
|
7679
7683
|
let branch = "main";
|
|
7680
7684
|
try {
|
|
7681
|
-
branch =
|
|
7685
|
+
branch = execSync9("git symbolic-ref --short HEAD", {
|
|
7682
7686
|
cwd: projectPath,
|
|
7683
7687
|
encoding: "utf-8"
|
|
7684
7688
|
}).trim();
|
|
@@ -7823,6 +7827,301 @@ var init_tech_stack = __esm({
|
|
|
7823
7827
|
}
|
|
7824
7828
|
});
|
|
7825
7829
|
|
|
7830
|
+
// src/lib/claude-plan.ts
|
|
7831
|
+
import * as fs5 from "node:fs";
|
|
7832
|
+
import * as path6 from "node:path";
|
|
7833
|
+
function buildDriftPlan(projectDir, templatesDir, manifest) {
|
|
7834
|
+
const packaged = walkTemplates(templatesDir);
|
|
7835
|
+
const packagedBySrc = new Map(packaged.map((f) => [f.src, f]));
|
|
7836
|
+
const manifestBySrc = new Map(manifest.files.map((f) => [f.src, f]));
|
|
7837
|
+
const plan = {
|
|
7838
|
+
unchanged: [],
|
|
7839
|
+
overwriteSafe: [],
|
|
7840
|
+
overwriteHandEdited: [],
|
|
7841
|
+
newOptIn: [],
|
|
7842
|
+
removedFromPackage: []
|
|
7843
|
+
};
|
|
7844
|
+
for (const pkg of packaged) {
|
|
7845
|
+
const inManifest = manifestBySrc.get(pkg.src);
|
|
7846
|
+
const absDest = path6.join(projectDir, ".claude", pkg.dest);
|
|
7847
|
+
const absSrc = path6.join(templatesDir, pkg.src);
|
|
7848
|
+
if (!inManifest) {
|
|
7849
|
+
plan.newOptIn.push({
|
|
7850
|
+
packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
|
|
7851
|
+
absSrc
|
|
7852
|
+
});
|
|
7853
|
+
continue;
|
|
7854
|
+
}
|
|
7855
|
+
const onDiskExists = fs5.existsSync(absDest);
|
|
7856
|
+
const onDiskContent = onDiskExists ? fs5.readFileSync(absDest) : Buffer.alloc(0);
|
|
7857
|
+
const onDiskHash = onDiskExists ? sha256(onDiskContent) : null;
|
|
7858
|
+
if (pkg.hash === inManifest.hash && onDiskHash === inManifest.hash) {
|
|
7859
|
+
plan.unchanged.push(inManifest);
|
|
7860
|
+
continue;
|
|
7861
|
+
}
|
|
7862
|
+
if (onDiskHash !== null && onDiskHash !== inManifest.hash) {
|
|
7863
|
+
plan.overwriteHandEdited.push({
|
|
7864
|
+
packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
|
|
7865
|
+
absSrc,
|
|
7866
|
+
onDiskContent
|
|
7867
|
+
});
|
|
7868
|
+
} else {
|
|
7869
|
+
plan.overwriteSafe.push({
|
|
7870
|
+
packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
|
|
7871
|
+
absSrc
|
|
7872
|
+
});
|
|
7873
|
+
}
|
|
7874
|
+
}
|
|
7875
|
+
for (const m of manifest.files) {
|
|
7876
|
+
if (!packagedBySrc.has(m.src)) {
|
|
7877
|
+
plan.removedFromPackage.push(m);
|
|
7878
|
+
}
|
|
7879
|
+
}
|
|
7880
|
+
return plan;
|
|
7881
|
+
}
|
|
7882
|
+
var init_claude_plan = __esm({
|
|
7883
|
+
"src/lib/claude-plan.ts"() {
|
|
7884
|
+
"use strict";
|
|
7885
|
+
init_template_walker();
|
|
7886
|
+
init_hash();
|
|
7887
|
+
}
|
|
7888
|
+
});
|
|
7889
|
+
|
|
7890
|
+
// src/cli/claude/status.ts
|
|
7891
|
+
var status_exports = {};
|
|
7892
|
+
__export(status_exports, {
|
|
7893
|
+
runStatus: () => runStatus
|
|
7894
|
+
});
|
|
7895
|
+
import * as fs6 from "node:fs";
|
|
7896
|
+
import * as path7 from "node:path";
|
|
7897
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7898
|
+
import { execSync as execSync8 } from "node:child_process";
|
|
7899
|
+
function makeFailSafe(checked_at) {
|
|
7900
|
+
return {
|
|
7901
|
+
installed: VERSION,
|
|
7902
|
+
manifest_version: null,
|
|
7903
|
+
latest: null,
|
|
7904
|
+
newer: false,
|
|
7905
|
+
version_skip: false,
|
|
7906
|
+
drifted_files: [],
|
|
7907
|
+
new_in_package: [],
|
|
7908
|
+
removed_from_package: [],
|
|
7909
|
+
settings_drift: false,
|
|
7910
|
+
guarded: true,
|
|
7911
|
+
guard_reason: "unknown",
|
|
7912
|
+
in_sync: true,
|
|
7913
|
+
action: null,
|
|
7914
|
+
checked_at
|
|
7915
|
+
};
|
|
7916
|
+
}
|
|
7917
|
+
function resolveTemplatesDirFromInstall() {
|
|
7918
|
+
const here = path7.dirname(fileURLToPath2(import.meta.url));
|
|
7919
|
+
const candidates = [
|
|
7920
|
+
path7.resolve(here, "..", "templates"),
|
|
7921
|
+
path7.resolve(here, "..", "..", "templates"),
|
|
7922
|
+
path7.resolve(here, "..", "..", "..", "templates")
|
|
7923
|
+
];
|
|
7924
|
+
for (const c of candidates) {
|
|
7925
|
+
if (fs6.existsSync(c) && fs6.statSync(c).isDirectory()) {
|
|
7926
|
+
return c;
|
|
7927
|
+
}
|
|
7928
|
+
}
|
|
7929
|
+
throw new Error(
|
|
7930
|
+
`codebyplan claude: could not locate templates/ directory. Probed:
|
|
7931
|
+
${candidates.join(
|
|
7932
|
+
"\n "
|
|
7933
|
+
)}`
|
|
7934
|
+
);
|
|
7935
|
+
}
|
|
7936
|
+
function resolveGitRoot2() {
|
|
7937
|
+
try {
|
|
7938
|
+
return execSync8("git rev-parse --show-toplevel", {
|
|
7939
|
+
encoding: "utf-8"
|
|
7940
|
+
}).trim();
|
|
7941
|
+
} catch {
|
|
7942
|
+
return null;
|
|
7943
|
+
}
|
|
7944
|
+
}
|
|
7945
|
+
function resolveCurrentBranch2() {
|
|
7946
|
+
try {
|
|
7947
|
+
return execSync8("git symbolic-ref --short HEAD", {
|
|
7948
|
+
encoding: "utf-8"
|
|
7949
|
+
}).trim();
|
|
7950
|
+
} catch {
|
|
7951
|
+
try {
|
|
7952
|
+
return execSync8("git rev-parse --abbrev-ref HEAD", {
|
|
7953
|
+
encoding: "utf-8"
|
|
7954
|
+
}).trim();
|
|
7955
|
+
} catch {
|
|
7956
|
+
return "";
|
|
7957
|
+
}
|
|
7958
|
+
}
|
|
7959
|
+
}
|
|
7960
|
+
async function runStatus(argv) {
|
|
7961
|
+
const checked_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
7962
|
+
const writeCache = argv.includes("--write-cache");
|
|
7963
|
+
const quiet = argv.includes("--quiet");
|
|
7964
|
+
try {
|
|
7965
|
+
const projectDir = resolveGitRoot2() ?? process.cwd();
|
|
7966
|
+
let templatesDir;
|
|
7967
|
+
try {
|
|
7968
|
+
templatesDir = resolveTemplatesDirFromInstall();
|
|
7969
|
+
} catch {
|
|
7970
|
+
const gitRoot2 = resolveGitRoot2();
|
|
7971
|
+
const currentBranch2 = resolveCurrentBranch2();
|
|
7972
|
+
const { guardReason: guardReason2 } = await resolveGuard(gitRoot2, currentBranch2);
|
|
7973
|
+
const result2 = {
|
|
7974
|
+
...makeFailSafe(checked_at),
|
|
7975
|
+
// Mirror the main path: only the canonical source repo hides the
|
|
7976
|
+
// segment. A protected_branch consumer whose templates can't be
|
|
7977
|
+
// resolved must still be guarded:false (segment visible).
|
|
7978
|
+
guarded: guardReason2 === "canonical_source",
|
|
7979
|
+
guard_reason: guardReason2 ?? "no_manifest",
|
|
7980
|
+
in_sync: true
|
|
7981
|
+
};
|
|
7982
|
+
emitResult(result2, writeCache, quiet, projectDir);
|
|
7983
|
+
process.exit(0);
|
|
7984
|
+
return;
|
|
7985
|
+
}
|
|
7986
|
+
const gitRoot = resolveGitRoot2();
|
|
7987
|
+
const currentBranch = resolveCurrentBranch2();
|
|
7988
|
+
const { guardReason } = await resolveGuard(gitRoot, currentBranch);
|
|
7989
|
+
const manifest = readManifest(projectDir);
|
|
7990
|
+
if (manifest === null || guardReason === "canonical_source") {
|
|
7991
|
+
const result2 = {
|
|
7992
|
+
installed: VERSION,
|
|
7993
|
+
manifest_version: manifest === null ? null : manifest.version,
|
|
7994
|
+
latest: null,
|
|
7995
|
+
newer: false,
|
|
7996
|
+
version_skip: false,
|
|
7997
|
+
drifted_files: [],
|
|
7998
|
+
new_in_package: [],
|
|
7999
|
+
removed_from_package: [],
|
|
8000
|
+
settings_drift: false,
|
|
8001
|
+
guarded: true,
|
|
8002
|
+
// guarded:true must always pair with a non-null reason. canonical_source
|
|
8003
|
+
// keeps its reason; the bare no-manifest case (never-installed consumer)
|
|
8004
|
+
// gets the "no_manifest" sentinel instead of null.
|
|
8005
|
+
guard_reason: guardReason ?? "no_manifest",
|
|
8006
|
+
in_sync: true,
|
|
8007
|
+
action: null,
|
|
8008
|
+
checked_at
|
|
8009
|
+
};
|
|
8010
|
+
emitResult(result2, writeCache, quiet, projectDir);
|
|
8011
|
+
process.exit(0);
|
|
8012
|
+
return;
|
|
8013
|
+
}
|
|
8014
|
+
const installed = VERSION;
|
|
8015
|
+
const manifest_version = manifest.version;
|
|
8016
|
+
const version_skip = manifest_version != null && compareSemver(installed, manifest_version) > 0;
|
|
8017
|
+
const driftPlan = buildDriftPlan(projectDir, templatesDir, manifest);
|
|
8018
|
+
const drifted_files = driftPlan.overwriteHandEdited.map(
|
|
8019
|
+
(e) => e.packaged.dest
|
|
8020
|
+
);
|
|
8021
|
+
const new_in_package = driftPlan.newOptIn.map((e) => e.packaged.dest);
|
|
8022
|
+
const removed_from_package = driftPlan.removedFromPackage.map(
|
|
8023
|
+
(e) => e.dest
|
|
8024
|
+
);
|
|
8025
|
+
const latest = fetchLatestVersion();
|
|
8026
|
+
const newer = latest !== null && compareSemver(latest, installed) > 0;
|
|
8027
|
+
let settings_drift = false;
|
|
8028
|
+
const settingsPath = path7.join(projectDir, ".claude", "settings.json");
|
|
8029
|
+
const baseSettingsPath = path7.join(
|
|
8030
|
+
templatesDir,
|
|
8031
|
+
"settings.project.base.json"
|
|
8032
|
+
);
|
|
8033
|
+
const hooksJsonPath = path7.join(templatesDir, "hooks", "hooks.json");
|
|
8034
|
+
if (fs6.existsSync(settingsPath)) {
|
|
8035
|
+
try {
|
|
8036
|
+
const settingsRaw = fs6.readFileSync(settingsPath, "utf8");
|
|
8037
|
+
const before = JSON.stringify(JSON.parse(settingsRaw));
|
|
8038
|
+
const cloned = JSON.parse(settingsRaw);
|
|
8039
|
+
if (fs6.existsSync(baseSettingsPath)) {
|
|
8040
|
+
const base = JSON.parse(
|
|
8041
|
+
fs6.readFileSync(baseSettingsPath, "utf8")
|
|
8042
|
+
);
|
|
8043
|
+
mergeBaseSettingsIntoSettings(cloned, base);
|
|
8044
|
+
}
|
|
8045
|
+
if (fs6.existsSync(hooksJsonPath)) {
|
|
8046
|
+
const hooksJson = JSON.parse(
|
|
8047
|
+
fs6.readFileSync(hooksJsonPath, "utf8")
|
|
8048
|
+
);
|
|
8049
|
+
mergeHooksIntoSettings(cloned, hooksJson);
|
|
8050
|
+
}
|
|
8051
|
+
const after = JSON.stringify(cloned);
|
|
8052
|
+
settings_drift = before !== after;
|
|
8053
|
+
} catch {
|
|
8054
|
+
settings_drift = false;
|
|
8055
|
+
}
|
|
8056
|
+
}
|
|
8057
|
+
const in_sync = !version_skip && drifted_files.length === 0 && new_in_package.length === 0 && removed_from_package.length === 0 && !settings_drift;
|
|
8058
|
+
let action = null;
|
|
8059
|
+
if (guardReason !== "protected_branch") {
|
|
8060
|
+
action = !in_sync ? newer ? "newer available + drift: run npx codebyplan claude update" : "run npx codebyplan claude update" : newer ? "newer available: run npx codebyplan claude update" : null;
|
|
8061
|
+
}
|
|
8062
|
+
const result = {
|
|
8063
|
+
installed,
|
|
8064
|
+
manifest_version,
|
|
8065
|
+
latest,
|
|
8066
|
+
newer,
|
|
8067
|
+
version_skip,
|
|
8068
|
+
drifted_files,
|
|
8069
|
+
new_in_package,
|
|
8070
|
+
removed_from_package,
|
|
8071
|
+
settings_drift,
|
|
8072
|
+
// Always false in the full-detection path: a protected_branch consumer
|
|
8073
|
+
// (the only guarded case that reaches here) must still SEE the segment;
|
|
8074
|
+
// only canonical_source / no-manifest (Branch A) report guarded:true.
|
|
8075
|
+
guarded: false,
|
|
8076
|
+
guard_reason: guardReason,
|
|
8077
|
+
in_sync,
|
|
8078
|
+
action,
|
|
8079
|
+
checked_at
|
|
8080
|
+
};
|
|
8081
|
+
emitResult(result, writeCache, quiet, projectDir);
|
|
8082
|
+
process.exit(0);
|
|
8083
|
+
} catch (err) {
|
|
8084
|
+
const safe = makeFailSafe(checked_at);
|
|
8085
|
+
if (!quiet) {
|
|
8086
|
+
try {
|
|
8087
|
+
process.stdout.write(JSON.stringify(safe) + "\n");
|
|
8088
|
+
} catch {
|
|
8089
|
+
}
|
|
8090
|
+
}
|
|
8091
|
+
void err;
|
|
8092
|
+
process.exit(0);
|
|
8093
|
+
}
|
|
8094
|
+
}
|
|
8095
|
+
function emitResult(result, writeCache, quiet, projectDir) {
|
|
8096
|
+
const json = JSON.stringify(result, null, 2);
|
|
8097
|
+
if (writeCache) {
|
|
8098
|
+
try {
|
|
8099
|
+
const cacheDir = path7.join(projectDir, ".codebyplan");
|
|
8100
|
+
fs6.mkdirSync(cacheDir, { recursive: true });
|
|
8101
|
+
fs6.writeFileSync(
|
|
8102
|
+
path7.join(cacheDir, "claude-status.local.json"),
|
|
8103
|
+
json + "\n",
|
|
8104
|
+
"utf8"
|
|
8105
|
+
);
|
|
8106
|
+
} catch {
|
|
8107
|
+
}
|
|
8108
|
+
}
|
|
8109
|
+
if (!quiet) {
|
|
8110
|
+
process.stdout.write(json + "\n");
|
|
8111
|
+
}
|
|
8112
|
+
}
|
|
8113
|
+
var init_status = __esm({
|
|
8114
|
+
"src/cli/claude/status.ts"() {
|
|
8115
|
+
"use strict";
|
|
8116
|
+
init_version();
|
|
8117
|
+
init_bump();
|
|
8118
|
+
init_manifest();
|
|
8119
|
+
init_claude_plan();
|
|
8120
|
+
init_settings_merge();
|
|
8121
|
+
init_version_status();
|
|
8122
|
+
}
|
|
8123
|
+
});
|
|
8124
|
+
|
|
7826
8125
|
// src/lib/prompt.ts
|
|
7827
8126
|
import * as readline from "node:readline";
|
|
7828
8127
|
async function ask(q, opts) {
|
|
@@ -7841,11 +8140,11 @@ async function ask(q, opts) {
|
|
|
7841
8140
|
try {
|
|
7842
8141
|
while (true) {
|
|
7843
8142
|
const choices = q.choices.map((c) => `[${c.key}] ${c.label}`).join(" ");
|
|
7844
|
-
const answer = await new Promise((
|
|
8143
|
+
const answer = await new Promise((resolve9) => {
|
|
7845
8144
|
rl.question(`${q.message}
|
|
7846
8145
|
${choices}
|
|
7847
8146
|
> `, (input) => {
|
|
7848
|
-
|
|
8147
|
+
resolve9(input.trim().toLowerCase());
|
|
7849
8148
|
});
|
|
7850
8149
|
});
|
|
7851
8150
|
const match = q.choices.find(
|
|
@@ -7939,10 +8238,10 @@ var update_exports = {};
|
|
|
7939
8238
|
__export(update_exports, {
|
|
7940
8239
|
runUpdate: () => runUpdate
|
|
7941
8240
|
});
|
|
7942
|
-
import * as
|
|
8241
|
+
import * as fs7 from "node:fs";
|
|
7943
8242
|
import * as os3 from "node:os";
|
|
7944
|
-
import * as
|
|
7945
|
-
import { fileURLToPath as
|
|
8243
|
+
import * as path8 from "node:path";
|
|
8244
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
7946
8245
|
async function runUpdate(opts, deps = {}) {
|
|
7947
8246
|
await Promise.resolve();
|
|
7948
8247
|
const scope = opts.scope ?? "project";
|
|
@@ -7958,7 +8257,7 @@ async function runUpdate(opts, deps = {}) {
|
|
|
7958
8257
|
const projectDir = deps.projectDir ?? process.cwd();
|
|
7959
8258
|
let templatesDir;
|
|
7960
8259
|
try {
|
|
7961
|
-
templatesDir = deps.templatesDir ??
|
|
8260
|
+
templatesDir = deps.templatesDir ?? resolveTemplatesDirFromInstall2();
|
|
7962
8261
|
} catch (err) {
|
|
7963
8262
|
console.error(
|
|
7964
8263
|
err instanceof Error ? err.message : `codebyplan claude update: ${String(err)}`
|
|
@@ -7972,16 +8271,16 @@ async function runUpdate(opts, deps = {}) {
|
|
|
7972
8271
|
await runInstall(opts, { ...deps, templatesDir });
|
|
7973
8272
|
return;
|
|
7974
8273
|
}
|
|
7975
|
-
const plan =
|
|
8274
|
+
const plan = buildDriftPlan(projectDir, templatesDir, manifestBefore);
|
|
7976
8275
|
const finalManifestEntries = [];
|
|
7977
8276
|
for (const e of plan.unchanged) {
|
|
7978
8277
|
finalManifestEntries.push(e);
|
|
7979
8278
|
}
|
|
7980
8279
|
for (const { packaged, absSrc } of plan.overwriteSafe) {
|
|
7981
|
-
const absDest =
|
|
8280
|
+
const absDest = path8.join(projectDir, ".claude", packaged.dest);
|
|
7982
8281
|
if (!opts.dryRun) {
|
|
7983
|
-
|
|
7984
|
-
|
|
8282
|
+
fs7.mkdirSync(path8.dirname(absDest), { recursive: true });
|
|
8283
|
+
fs7.copyFileSync(absSrc, absDest);
|
|
7985
8284
|
if (opts.verbose) console.log(`updated ${packaged.dest}`);
|
|
7986
8285
|
} else if (opts.verbose) {
|
|
7987
8286
|
console.log(`[dry-run] would update ${packaged.dest}`);
|
|
@@ -7993,8 +8292,8 @@ async function runUpdate(opts, deps = {}) {
|
|
|
7993
8292
|
absSrc,
|
|
7994
8293
|
onDiskContent
|
|
7995
8294
|
} of plan.overwriteHandEdited) {
|
|
7996
|
-
const absDest =
|
|
7997
|
-
const newContent =
|
|
8295
|
+
const absDest = path8.join(projectDir, ".claude", packaged.dest);
|
|
8296
|
+
const newContent = fs7.readFileSync(absSrc);
|
|
7998
8297
|
const showDiff = () => {
|
|
7999
8298
|
console.log(
|
|
8000
8299
|
renderDiff(
|
|
@@ -8006,8 +8305,8 @@ async function runUpdate(opts, deps = {}) {
|
|
|
8006
8305
|
const answer = await promptOverwrite(packaged.dest, opts, showDiff);
|
|
8007
8306
|
if (answer === "overwrite") {
|
|
8008
8307
|
if (!opts.dryRun) {
|
|
8009
|
-
|
|
8010
|
-
|
|
8308
|
+
fs7.mkdirSync(path8.dirname(absDest), { recursive: true });
|
|
8309
|
+
fs7.copyFileSync(absSrc, absDest);
|
|
8011
8310
|
}
|
|
8012
8311
|
finalManifestEntries.push(packaged);
|
|
8013
8312
|
} else {
|
|
@@ -8022,10 +8321,10 @@ async function runUpdate(opts, deps = {}) {
|
|
|
8022
8321
|
for (const { packaged, absSrc } of plan.newOptIn) {
|
|
8023
8322
|
const answer = await promptOptIn(packaged.dest, opts);
|
|
8024
8323
|
if (answer === "opt-in") {
|
|
8025
|
-
const absDest =
|
|
8324
|
+
const absDest = path8.join(projectDir, ".claude", packaged.dest);
|
|
8026
8325
|
if (!opts.dryRun) {
|
|
8027
|
-
|
|
8028
|
-
|
|
8326
|
+
fs7.mkdirSync(path8.dirname(absDest), { recursive: true });
|
|
8327
|
+
fs7.copyFileSync(absSrc, absDest);
|
|
8029
8328
|
}
|
|
8030
8329
|
finalManifestEntries.push(packaged);
|
|
8031
8330
|
if (opts.verbose) console.log(`installed new file ${packaged.dest}`);
|
|
@@ -8036,25 +8335,25 @@ async function runUpdate(opts, deps = {}) {
|
|
|
8036
8335
|
for (const e of plan.removedFromPackage) {
|
|
8037
8336
|
const answer = await promptRemove(e.dest, opts);
|
|
8038
8337
|
if (answer === "remove") {
|
|
8039
|
-
const absDest =
|
|
8040
|
-
if (!opts.dryRun &&
|
|
8041
|
-
|
|
8042
|
-
const claudeDir =
|
|
8043
|
-
let cur =
|
|
8044
|
-
while (cur !== claudeDir && cur !==
|
|
8045
|
-
if (
|
|
8338
|
+
const absDest = path8.join(projectDir, ".claude", e.dest);
|
|
8339
|
+
if (!opts.dryRun && fs7.existsSync(absDest)) {
|
|
8340
|
+
fs7.rmSync(absDest);
|
|
8341
|
+
const claudeDir = path8.join(projectDir, ".claude");
|
|
8342
|
+
let cur = path8.dirname(absDest);
|
|
8343
|
+
while (cur !== claudeDir && cur !== path8.dirname(cur)) {
|
|
8344
|
+
if (path8.dirname(cur) === claudeDir) break;
|
|
8046
8345
|
try {
|
|
8047
|
-
|
|
8346
|
+
fs7.rmdirSync(cur);
|
|
8048
8347
|
if (opts.verbose)
|
|
8049
8348
|
console.log(
|
|
8050
|
-
`pruned empty dir ${
|
|
8349
|
+
`pruned empty dir ${path8.relative(claudeDir, cur)}`
|
|
8051
8350
|
);
|
|
8052
|
-
cur =
|
|
8351
|
+
cur = path8.dirname(cur);
|
|
8053
8352
|
} catch (err) {
|
|
8054
8353
|
const code = err.code;
|
|
8055
8354
|
if (code !== "ENOTEMPTY" && code !== "ENOENT") {
|
|
8056
8355
|
console.warn(
|
|
8057
|
-
`codebyplan claude: could not prune empty dir ${
|
|
8356
|
+
`codebyplan claude: could not prune empty dir ${path8.relative(claudeDir, cur)}: ${err.message}`
|
|
8058
8357
|
);
|
|
8059
8358
|
}
|
|
8060
8359
|
break;
|
|
@@ -8066,28 +8365,28 @@ async function runUpdate(opts, deps = {}) {
|
|
|
8066
8365
|
if (opts.verbose) console.log(`kept (untracked) ${e.dest}`);
|
|
8067
8366
|
}
|
|
8068
8367
|
}
|
|
8069
|
-
const hooksJsonPath =
|
|
8070
|
-
const baseSettingsPath =
|
|
8368
|
+
const hooksJsonPath = path8.join(templatesDir, "hooks", "hooks.json");
|
|
8369
|
+
const baseSettingsPath = path8.join(
|
|
8071
8370
|
templatesDir,
|
|
8072
8371
|
"settings.project.base.json"
|
|
8073
8372
|
);
|
|
8074
|
-
const settingsPath =
|
|
8075
|
-
const existingSettings =
|
|
8076
|
-
if (
|
|
8373
|
+
const settingsPath = path8.join(projectDir, ".claude", "settings.json");
|
|
8374
|
+
const existingSettings = fs7.existsSync(settingsPath) ? JSON.parse(fs7.readFileSync(settingsPath, "utf8")) : {};
|
|
8375
|
+
if (fs7.existsSync(baseSettingsPath)) {
|
|
8077
8376
|
const base = JSON.parse(
|
|
8078
|
-
|
|
8377
|
+
fs7.readFileSync(baseSettingsPath, "utf8")
|
|
8079
8378
|
);
|
|
8080
8379
|
mergeBaseSettingsIntoSettings(existingSettings, base);
|
|
8081
8380
|
}
|
|
8082
|
-
if (
|
|
8381
|
+
if (fs7.existsSync(hooksJsonPath)) {
|
|
8083
8382
|
const hooksJson = JSON.parse(
|
|
8084
|
-
|
|
8383
|
+
fs7.readFileSync(hooksJsonPath, "utf8")
|
|
8085
8384
|
);
|
|
8086
8385
|
mergeHooksIntoSettings(existingSettings, hooksJson);
|
|
8087
8386
|
}
|
|
8088
8387
|
if (!opts.dryRun) {
|
|
8089
|
-
|
|
8090
|
-
|
|
8388
|
+
fs7.mkdirSync(path8.dirname(settingsPath), { recursive: true });
|
|
8389
|
+
fs7.writeFileSync(
|
|
8091
8390
|
settingsPath,
|
|
8092
8391
|
JSON.stringify(existingSettings, null, 2) + "\n",
|
|
8093
8392
|
"utf8"
|
|
@@ -8099,7 +8398,7 @@ async function runUpdate(opts, deps = {}) {
|
|
|
8099
8398
|
);
|
|
8100
8399
|
if (opts.verbose && gitignoreAction !== "unchanged") {
|
|
8101
8400
|
console.log(
|
|
8102
|
-
`${opts.dryRun ? "[dry-run] would " : ""}${gitignoreAction} managed .gitignore block in ${
|
|
8401
|
+
`${opts.dryRun ? "[dry-run] would " : ""}${gitignoreAction} managed .gitignore block in ${path8.relative(projectDir, path8.join(projectDir, ".gitignore"))}`
|
|
8103
8402
|
);
|
|
8104
8403
|
}
|
|
8105
8404
|
if (!opts.dryRun) {
|
|
@@ -8125,7 +8424,7 @@ async function runUpdate(opts, deps = {}) {
|
|
|
8125
8424
|
function runUpdateUser(opts, deps) {
|
|
8126
8425
|
let templatesDir;
|
|
8127
8426
|
try {
|
|
8128
|
-
templatesDir = deps.templatesDir ??
|
|
8427
|
+
templatesDir = deps.templatesDir ?? resolveTemplatesDirFromInstall2();
|
|
8129
8428
|
} catch (err) {
|
|
8130
8429
|
console.error(
|
|
8131
8430
|
err instanceof Error ? err.message : `codebyplan claude update: ${String(err)}`
|
|
@@ -8134,9 +8433,9 @@ function runUpdateUser(opts, deps) {
|
|
|
8134
8433
|
return;
|
|
8135
8434
|
}
|
|
8136
8435
|
try {
|
|
8137
|
-
const userDir = deps.userDir ??
|
|
8138
|
-
const settingsPath =
|
|
8139
|
-
const userBaseSettingsPath =
|
|
8436
|
+
const userDir = deps.userDir ?? path8.join(os3.homedir(), ".claude");
|
|
8437
|
+
const settingsPath = path8.join(userDir, "settings.json");
|
|
8438
|
+
const userBaseSettingsPath = path8.join(
|
|
8140
8439
|
templatesDir,
|
|
8141
8440
|
"settings.user.base.json"
|
|
8142
8441
|
);
|
|
@@ -8148,7 +8447,7 @@ function runUpdateUser(opts, deps) {
|
|
|
8148
8447
|
process.exitCode = 1;
|
|
8149
8448
|
return;
|
|
8150
8449
|
}
|
|
8151
|
-
if (!
|
|
8450
|
+
if (!fs7.existsSync(userBaseSettingsPath)) {
|
|
8152
8451
|
console.error(
|
|
8153
8452
|
"codebyplan claude update: settings.user.base.json not found in templates."
|
|
8154
8453
|
);
|
|
@@ -8156,13 +8455,13 @@ function runUpdateUser(opts, deps) {
|
|
|
8156
8455
|
return;
|
|
8157
8456
|
}
|
|
8158
8457
|
const userBase = JSON.parse(
|
|
8159
|
-
|
|
8458
|
+
fs7.readFileSync(userBaseSettingsPath, "utf8")
|
|
8160
8459
|
);
|
|
8161
|
-
const existingSettings =
|
|
8460
|
+
const existingSettings = fs7.existsSync(settingsPath) ? JSON.parse(fs7.readFileSync(settingsPath, "utf8")) : {};
|
|
8162
8461
|
mergeBaseSettingsIntoSettings(existingSettings, userBase);
|
|
8163
8462
|
if (!opts.dryRun) {
|
|
8164
|
-
|
|
8165
|
-
|
|
8463
|
+
fs7.mkdirSync(userDir, { recursive: true });
|
|
8464
|
+
fs7.writeFileSync(
|
|
8166
8465
|
settingsPath,
|
|
8167
8466
|
JSON.stringify(existingSettings, null, 2) + "\n",
|
|
8168
8467
|
"utf8"
|
|
@@ -8185,64 +8484,15 @@ function runUpdateUser(opts, deps) {
|
|
|
8185
8484
|
process.exitCode = 1;
|
|
8186
8485
|
}
|
|
8187
8486
|
}
|
|
8188
|
-
function
|
|
8189
|
-
const
|
|
8190
|
-
const packagedBySrc = new Map(packaged.map((f) => [f.src, f]));
|
|
8191
|
-
const manifestBySrc = new Map(manifest.files.map((f) => [f.src, f]));
|
|
8192
|
-
const plan = {
|
|
8193
|
-
unchanged: [],
|
|
8194
|
-
overwriteSafe: [],
|
|
8195
|
-
overwriteHandEdited: [],
|
|
8196
|
-
newOptIn: [],
|
|
8197
|
-
removedFromPackage: []
|
|
8198
|
-
};
|
|
8199
|
-
for (const pkg of packaged) {
|
|
8200
|
-
const inManifest = manifestBySrc.get(pkg.src);
|
|
8201
|
-
const absDest = path6.join(projectDir, ".claude", pkg.dest);
|
|
8202
|
-
const absSrc = path6.join(templatesDir, pkg.src);
|
|
8203
|
-
if (!inManifest) {
|
|
8204
|
-
plan.newOptIn.push({
|
|
8205
|
-
packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
|
|
8206
|
-
absSrc
|
|
8207
|
-
});
|
|
8208
|
-
continue;
|
|
8209
|
-
}
|
|
8210
|
-
const onDiskExists = fs5.existsSync(absDest);
|
|
8211
|
-
const onDiskContent = onDiskExists ? fs5.readFileSync(absDest) : Buffer.alloc(0);
|
|
8212
|
-
const onDiskHash = onDiskExists ? sha256(onDiskContent) : null;
|
|
8213
|
-
if (pkg.hash === inManifest.hash && onDiskHash === inManifest.hash) {
|
|
8214
|
-
plan.unchanged.push(inManifest);
|
|
8215
|
-
continue;
|
|
8216
|
-
}
|
|
8217
|
-
if (onDiskHash !== null && onDiskHash !== inManifest.hash) {
|
|
8218
|
-
plan.overwriteHandEdited.push({
|
|
8219
|
-
packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
|
|
8220
|
-
absSrc,
|
|
8221
|
-
onDiskContent
|
|
8222
|
-
});
|
|
8223
|
-
} else {
|
|
8224
|
-
plan.overwriteSafe.push({
|
|
8225
|
-
packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
|
|
8226
|
-
absSrc
|
|
8227
|
-
});
|
|
8228
|
-
}
|
|
8229
|
-
}
|
|
8230
|
-
for (const m of manifest.files) {
|
|
8231
|
-
if (!packagedBySrc.has(m.src)) {
|
|
8232
|
-
plan.removedFromPackage.push(m);
|
|
8233
|
-
}
|
|
8234
|
-
}
|
|
8235
|
-
return plan;
|
|
8236
|
-
}
|
|
8237
|
-
function resolveTemplatesDirFromInstall() {
|
|
8238
|
-
const here = path6.dirname(fileURLToPath2(import.meta.url));
|
|
8487
|
+
function resolveTemplatesDirFromInstall2() {
|
|
8488
|
+
const here = path8.dirname(fileURLToPath3(import.meta.url));
|
|
8239
8489
|
const candidates = [
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8490
|
+
path8.resolve(here, "..", "templates"),
|
|
8491
|
+
path8.resolve(here, "..", "..", "templates"),
|
|
8492
|
+
path8.resolve(here, "..", "..", "..", "templates")
|
|
8243
8493
|
];
|
|
8244
8494
|
for (const c of candidates) {
|
|
8245
|
-
if (
|
|
8495
|
+
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) {
|
|
8246
8496
|
return c;
|
|
8247
8497
|
}
|
|
8248
8498
|
}
|
|
@@ -8256,14 +8506,14 @@ function resolveTemplatesDirFromInstall() {
|
|
|
8256
8506
|
var init_update = __esm({
|
|
8257
8507
|
"src/cli/claude/update.ts"() {
|
|
8258
8508
|
"use strict";
|
|
8259
|
-
init_template_walker();
|
|
8260
8509
|
init_gitignore_block();
|
|
8261
|
-
init_hash();
|
|
8262
8510
|
init_manifest();
|
|
8263
8511
|
init_settings_merge();
|
|
8264
8512
|
init_prompt();
|
|
8265
8513
|
init_install();
|
|
8266
8514
|
init_statusline_config();
|
|
8515
|
+
init_hash();
|
|
8516
|
+
init_claude_plan();
|
|
8267
8517
|
}
|
|
8268
8518
|
});
|
|
8269
8519
|
|
|
@@ -8272,9 +8522,9 @@ var uninstall_exports = {};
|
|
|
8272
8522
|
__export(uninstall_exports, {
|
|
8273
8523
|
runUninstall: () => runUninstall
|
|
8274
8524
|
});
|
|
8275
|
-
import * as
|
|
8525
|
+
import * as fs8 from "node:fs";
|
|
8276
8526
|
import * as os4 from "node:os";
|
|
8277
|
-
import * as
|
|
8527
|
+
import * as path9 from "node:path";
|
|
8278
8528
|
async function runUninstall(opts, deps = {}) {
|
|
8279
8529
|
await Promise.resolve();
|
|
8280
8530
|
const scope = opts.scope ?? "project";
|
|
@@ -8303,15 +8553,15 @@ async function runUninstall(opts, deps = {}) {
|
|
|
8303
8553
|
let removed = 0;
|
|
8304
8554
|
let warnings = 0;
|
|
8305
8555
|
for (const entry of manifest.files) {
|
|
8306
|
-
const abs =
|
|
8307
|
-
if (!
|
|
8556
|
+
const abs = path9.join(projectDir, ".claude", entry.dest);
|
|
8557
|
+
if (!fs8.existsSync(abs)) {
|
|
8308
8558
|
console.warn(
|
|
8309
8559
|
`codebyplan claude uninstall: ${entry.dest} already absent (skipping).`
|
|
8310
8560
|
);
|
|
8311
8561
|
warnings += 1;
|
|
8312
8562
|
continue;
|
|
8313
8563
|
}
|
|
8314
|
-
const onDiskHash = sha256(
|
|
8564
|
+
const onDiskHash = sha256(fs8.readFileSync(abs));
|
|
8315
8565
|
if (onDiskHash !== entry.hash) {
|
|
8316
8566
|
console.warn(
|
|
8317
8567
|
`codebyplan claude uninstall: ${entry.dest} has been modified locally; removing anyway.`
|
|
@@ -8319,7 +8569,7 @@ async function runUninstall(opts, deps = {}) {
|
|
|
8319
8569
|
warnings += 1;
|
|
8320
8570
|
}
|
|
8321
8571
|
if (!opts.dryRun) {
|
|
8322
|
-
|
|
8572
|
+
fs8.rmSync(abs);
|
|
8323
8573
|
}
|
|
8324
8574
|
removed += 1;
|
|
8325
8575
|
if (opts.verbose) console.log(`removed ${entry.dest}`);
|
|
@@ -8327,15 +8577,15 @@ async function runUninstall(opts, deps = {}) {
|
|
|
8327
8577
|
if (!opts.dryRun) {
|
|
8328
8578
|
pruneEmptyManagedDirs(projectDir);
|
|
8329
8579
|
}
|
|
8330
|
-
const settingsPath =
|
|
8331
|
-
if (
|
|
8580
|
+
const settingsPath = path9.join(projectDir, ".claude", "settings.json");
|
|
8581
|
+
if (fs8.existsSync(settingsPath)) {
|
|
8332
8582
|
const settings = JSON.parse(
|
|
8333
|
-
|
|
8583
|
+
fs8.readFileSync(settingsPath, "utf8")
|
|
8334
8584
|
);
|
|
8335
|
-
const baseSettingsPath = templatesDir ?
|
|
8336
|
-
if (baseSettingsPath &&
|
|
8585
|
+
const baseSettingsPath = templatesDir ? path9.join(templatesDir, "settings.project.base.json") : null;
|
|
8586
|
+
if (baseSettingsPath && fs8.existsSync(baseSettingsPath)) {
|
|
8337
8587
|
const base = JSON.parse(
|
|
8338
|
-
|
|
8588
|
+
fs8.readFileSync(baseSettingsPath, "utf8")
|
|
8339
8589
|
);
|
|
8340
8590
|
stripBaseSettingsFromSettings(settings, base);
|
|
8341
8591
|
}
|
|
@@ -8343,9 +8593,9 @@ async function runUninstall(opts, deps = {}) {
|
|
|
8343
8593
|
if (!opts.dryRun) {
|
|
8344
8594
|
const isEmpty = Object.keys(settings).length === 0;
|
|
8345
8595
|
if (isEmpty) {
|
|
8346
|
-
|
|
8596
|
+
fs8.rmSync(settingsPath);
|
|
8347
8597
|
} else {
|
|
8348
|
-
|
|
8598
|
+
fs8.writeFileSync(
|
|
8349
8599
|
settingsPath,
|
|
8350
8600
|
JSON.stringify(settings, null, 2) + "\n",
|
|
8351
8601
|
"utf8"
|
|
@@ -8364,11 +8614,11 @@ async function runUninstall(opts, deps = {}) {
|
|
|
8364
8614
|
}
|
|
8365
8615
|
if (!opts.dryRun) {
|
|
8366
8616
|
const m = manifestPath(projectDir);
|
|
8367
|
-
if (
|
|
8617
|
+
if (fs8.existsSync(m)) fs8.rmSync(m);
|
|
8368
8618
|
const mid = midManifestPath(projectDir);
|
|
8369
|
-
if (
|
|
8619
|
+
if (fs8.existsSync(mid)) fs8.rmSync(mid);
|
|
8370
8620
|
const legacy = oldManifestPath(projectDir);
|
|
8371
|
-
if (
|
|
8621
|
+
if (fs8.existsSync(legacy)) fs8.rmSync(legacy);
|
|
8372
8622
|
}
|
|
8373
8623
|
console.log(
|
|
8374
8624
|
`codebyplan claude uninstall${opts.dryRun ? " (dry-run)" : ""}: removed ${removed} files${warnings > 0 ? ` (${warnings} warnings)` : ""}.`
|
|
@@ -8390,7 +8640,7 @@ function runUninstallUser(opts, deps) {
|
|
|
8390
8640
|
}
|
|
8391
8641
|
}
|
|
8392
8642
|
try {
|
|
8393
|
-
const userDir = deps.userDir ??
|
|
8643
|
+
const userDir = deps.userDir ?? path9.join(os4.homedir(), ".claude");
|
|
8394
8644
|
const existingManifest = readManifestForScope("user", userDir);
|
|
8395
8645
|
if (!existingManifest) {
|
|
8396
8646
|
console.error(
|
|
@@ -8399,24 +8649,24 @@ function runUninstallUser(opts, deps) {
|
|
|
8399
8649
|
process.exitCode = 1;
|
|
8400
8650
|
return;
|
|
8401
8651
|
}
|
|
8402
|
-
const settingsPath =
|
|
8403
|
-
if (
|
|
8652
|
+
const settingsPath = path9.join(userDir, "settings.json");
|
|
8653
|
+
if (fs8.existsSync(settingsPath)) {
|
|
8404
8654
|
const settings = JSON.parse(
|
|
8405
|
-
|
|
8655
|
+
fs8.readFileSync(settingsPath, "utf8")
|
|
8406
8656
|
);
|
|
8407
|
-
const userBaseSettingsPath = templatesDir != null ?
|
|
8408
|
-
if (userBaseSettingsPath &&
|
|
8657
|
+
const userBaseSettingsPath = templatesDir != null ? path9.join(templatesDir, "settings.user.base.json") : null;
|
|
8658
|
+
if (userBaseSettingsPath && fs8.existsSync(userBaseSettingsPath)) {
|
|
8409
8659
|
const userBase = JSON.parse(
|
|
8410
|
-
|
|
8660
|
+
fs8.readFileSync(userBaseSettingsPath, "utf8")
|
|
8411
8661
|
);
|
|
8412
8662
|
stripBaseSettingsFromSettings(settings, userBase);
|
|
8413
8663
|
}
|
|
8414
8664
|
if (!opts.dryRun) {
|
|
8415
8665
|
const isEmpty = Object.keys(settings).length === 0;
|
|
8416
8666
|
if (isEmpty) {
|
|
8417
|
-
|
|
8667
|
+
fs8.rmSync(settingsPath);
|
|
8418
8668
|
} else {
|
|
8419
|
-
|
|
8669
|
+
fs8.writeFileSync(
|
|
8420
8670
|
settingsPath,
|
|
8421
8671
|
JSON.stringify(settings, null, 2) + "\n",
|
|
8422
8672
|
"utf8"
|
|
@@ -8426,11 +8676,11 @@ function runUninstallUser(opts, deps) {
|
|
|
8426
8676
|
}
|
|
8427
8677
|
if (!opts.dryRun) {
|
|
8428
8678
|
const m = userManifestPath(userDir);
|
|
8429
|
-
if (
|
|
8679
|
+
if (fs8.existsSync(m)) fs8.rmSync(m);
|
|
8430
8680
|
const midUser = userMidManifestPath(userDir);
|
|
8431
|
-
if (
|
|
8681
|
+
if (fs8.existsSync(midUser)) fs8.rmSync(midUser);
|
|
8432
8682
|
const oldUser = userOldManifestPath(userDir);
|
|
8433
|
-
if (
|
|
8683
|
+
if (fs8.existsSync(oldUser)) fs8.rmSync(oldUser);
|
|
8434
8684
|
}
|
|
8435
8685
|
console.log(
|
|
8436
8686
|
`codebyplan claude uninstall --scope user${opts.dryRun ? " (dry-run)" : ""}: user base settings stripped.`
|
|
@@ -8445,23 +8695,23 @@ function runUninstallUser(opts, deps) {
|
|
|
8445
8695
|
function pruneEmptyManagedDirs(projectDir) {
|
|
8446
8696
|
const managedRoots = ["skills", "agents", "hooks", "rules"];
|
|
8447
8697
|
for (const root of managedRoots) {
|
|
8448
|
-
const abs =
|
|
8449
|
-
if (!
|
|
8698
|
+
const abs = path9.join(projectDir, ".claude", root);
|
|
8699
|
+
if (!fs8.existsSync(abs)) continue;
|
|
8450
8700
|
pruneLeafFirst(abs);
|
|
8451
8701
|
}
|
|
8452
8702
|
}
|
|
8453
8703
|
function pruneLeafFirst(dir) {
|
|
8454
|
-
if (!
|
|
8455
|
-
const stat2 =
|
|
8704
|
+
if (!fs8.existsSync(dir)) return;
|
|
8705
|
+
const stat2 = fs8.statSync(dir);
|
|
8456
8706
|
if (!stat2.isDirectory()) return;
|
|
8457
|
-
for (const entry of
|
|
8707
|
+
for (const entry of fs8.readdirSync(dir, { withFileTypes: true })) {
|
|
8458
8708
|
if (entry.isDirectory()) {
|
|
8459
|
-
pruneLeafFirst(
|
|
8709
|
+
pruneLeafFirst(path9.join(dir, entry.name));
|
|
8460
8710
|
}
|
|
8461
8711
|
}
|
|
8462
|
-
const remaining =
|
|
8712
|
+
const remaining = fs8.readdirSync(dir);
|
|
8463
8713
|
if (remaining.length === 0) {
|
|
8464
|
-
|
|
8714
|
+
fs8.rmdirSync(dir);
|
|
8465
8715
|
}
|
|
8466
8716
|
}
|
|
8467
8717
|
var init_uninstall = __esm({
|
|
@@ -8477,13 +8727,13 @@ var init_uninstall = __esm({
|
|
|
8477
8727
|
|
|
8478
8728
|
// src/index.ts
|
|
8479
8729
|
init_version();
|
|
8480
|
-
import { readFileSync as
|
|
8481
|
-
import { resolve as
|
|
8730
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
8731
|
+
import { resolve as resolve8 } from "node:path";
|
|
8482
8732
|
void (async () => {
|
|
8483
8733
|
if (!process.env.CODEBYPLAN_API_KEY) {
|
|
8484
8734
|
try {
|
|
8485
|
-
const envPath =
|
|
8486
|
-
const content =
|
|
8735
|
+
const envPath = resolve8(process.cwd(), ".env.local");
|
|
8736
|
+
const content = readFileSync10(envPath, "utf-8");
|
|
8487
8737
|
for (const line of content.split("\n")) {
|
|
8488
8738
|
const trimmed = line.trim();
|
|
8489
8739
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -8640,6 +8890,11 @@ void (async () => {
|
|
|
8640
8890
|
const subcommand = process.argv[3];
|
|
8641
8891
|
const flagArgs = process.argv.slice(3);
|
|
8642
8892
|
const parsed = parseClaudeFlags(flagArgs);
|
|
8893
|
+
if (subcommand === "status") {
|
|
8894
|
+
const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
8895
|
+
await runStatus2(process.argv.slice(4));
|
|
8896
|
+
return;
|
|
8897
|
+
}
|
|
8643
8898
|
if (subcommand === "install") {
|
|
8644
8899
|
if (parsed === null) {
|
|
8645
8900
|
process.exit(1);
|
|
@@ -8680,6 +8935,9 @@ void (async () => {
|
|
|
8680
8935
|
codebyplan claude install [flags] Install skills/agents/hooks into ./.claude/
|
|
8681
8936
|
codebyplan claude update [flags] Update installed assets to latest versions
|
|
8682
8937
|
codebyplan claude uninstall [flags] Remove installed assets from ./.claude/
|
|
8938
|
+
codebyplan claude status Check package-sync state (drift, version skip, settings drift)
|
|
8939
|
+
--write-cache Write result to .codebyplan/claude-status.local.json
|
|
8940
|
+
--quiet Suppress stdout output
|
|
8683
8941
|
|
|
8684
8942
|
Flags (apply to install/update/uninstall):
|
|
8685
8943
|
--yes, -y Auto-accept every prompt with its recommended default
|