codebyplan 1.10.3 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +534 -209
- package/package.json +1 -1
- package/templates/hooks/README.md +58 -16
- package/templates/hooks/cbp-statusline.mjs +385 -0
- package/templates/hooks/cbp-statusline.py +331 -0
- package/templates/hooks/cbp-statusline.sh +138 -82
- package/templates/hooks/cbp-subagent-statusline.mjs +200 -0
- package/templates/hooks/cbp-subagent-statusline.py +183 -0
- package/templates/hooks/cbp-subagent-statusline.sh +87 -39
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.
|
|
17
|
+
VERSION = "1.11.0";
|
|
18
18
|
PACKAGE_NAME = "codebyplan";
|
|
19
19
|
}
|
|
20
20
|
});
|
|
@@ -151,6 +151,122 @@ var init_local_config = __esm({
|
|
|
151
151
|
}
|
|
152
152
|
});
|
|
153
153
|
|
|
154
|
+
// src/lib/statusline-config.ts
|
|
155
|
+
import { spawnSync } from "node:child_process";
|
|
156
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
157
|
+
import { join as join2 } from "node:path";
|
|
158
|
+
function statuslineConfigPath(projectPath) {
|
|
159
|
+
return join2(projectPath, ".codebyplan", "statusline.json");
|
|
160
|
+
}
|
|
161
|
+
function statuslineLocalConfigPath(projectPath) {
|
|
162
|
+
return join2(projectPath, ".codebyplan", "statusline.local.json");
|
|
163
|
+
}
|
|
164
|
+
function isValidRenderer(value) {
|
|
165
|
+
return typeof value === "string" && VALID_RENDERERS.has(value);
|
|
166
|
+
}
|
|
167
|
+
function mergeOverDefaults(raw) {
|
|
168
|
+
const result = {
|
|
169
|
+
lines: { ...STATUSLINE_DEFAULTS.lines },
|
|
170
|
+
no_color: STATUSLINE_DEFAULTS.no_color
|
|
171
|
+
};
|
|
172
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
const obj = raw;
|
|
176
|
+
if (typeof obj.no_color === "boolean") {
|
|
177
|
+
result.no_color = obj.no_color;
|
|
178
|
+
}
|
|
179
|
+
if (typeof obj.lines === "object" && obj.lines !== null && !Array.isArray(obj.lines)) {
|
|
180
|
+
const lines = obj.lines;
|
|
181
|
+
for (const key of Object.keys(STATUSLINE_DEFAULTS.lines)) {
|
|
182
|
+
if (typeof lines[key] === "boolean") {
|
|
183
|
+
result.lines[key] = lines[key];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
async function readStatuslineConfig(projectPath) {
|
|
190
|
+
try {
|
|
191
|
+
const raw = await readFile2(statuslineConfigPath(projectPath), "utf-8");
|
|
192
|
+
const parsed = JSON.parse(raw);
|
|
193
|
+
return mergeOverDefaults(parsed);
|
|
194
|
+
} catch {
|
|
195
|
+
return mergeOverDefaults(void 0);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async function readStatuslineLocalConfig(projectPath) {
|
|
199
|
+
try {
|
|
200
|
+
const raw = await readFile2(statuslineLocalConfigPath(projectPath), "utf-8");
|
|
201
|
+
const parsed = JSON.parse(raw);
|
|
202
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
203
|
+
const obj = parsed;
|
|
204
|
+
if (isValidRenderer(obj.renderer)) {
|
|
205
|
+
return { renderer: obj.renderer };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
} catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function writeStatuslineLocalConfig(projectPath, localConfig) {
|
|
214
|
+
if (!isValidRenderer(localConfig.renderer)) {
|
|
215
|
+
throw new TypeError(
|
|
216
|
+
`Unknown renderer: "${String(localConfig.renderer)}". Must be one of: bash, node, python.`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
const filePath = statuslineLocalConfigPath(projectPath);
|
|
220
|
+
await mkdir2(join2(projectPath, ".codebyplan"), { recursive: true });
|
|
221
|
+
await writeFile2(
|
|
222
|
+
filePath,
|
|
223
|
+
JSON.stringify(localConfig, null, 2) + "\n",
|
|
224
|
+
"utf-8"
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
async function resolveRenderer(projectPath) {
|
|
228
|
+
const local = await readStatuslineLocalConfig(projectPath);
|
|
229
|
+
return local?.renderer ?? DEFAULT_RENDERER;
|
|
230
|
+
}
|
|
231
|
+
function detectAvailableRenderers() {
|
|
232
|
+
function probe(cmd, args) {
|
|
233
|
+
try {
|
|
234
|
+
const result = spawnSync(cmd, args, {
|
|
235
|
+
timeout: 1e3,
|
|
236
|
+
stdio: "ignore",
|
|
237
|
+
windowsHide: true
|
|
238
|
+
});
|
|
239
|
+
return result.status === 0;
|
|
240
|
+
} catch {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
bash: true,
|
|
246
|
+
node: probe("node", ["--version"]),
|
|
247
|
+
python: probe("python3", ["--version"])
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
var STATUSLINE_DEFAULTS, DEFAULT_RENDERER, VALID_RENDERERS;
|
|
251
|
+
var init_statusline_config = __esm({
|
|
252
|
+
"src/lib/statusline-config.ts"() {
|
|
253
|
+
"use strict";
|
|
254
|
+
STATUSLINE_DEFAULTS = {
|
|
255
|
+
lines: {
|
|
256
|
+
identity: true,
|
|
257
|
+
context: true,
|
|
258
|
+
cost: true,
|
|
259
|
+
rate_limits: true,
|
|
260
|
+
repo_pr: true,
|
|
261
|
+
worktree: true
|
|
262
|
+
},
|
|
263
|
+
no_color: false
|
|
264
|
+
};
|
|
265
|
+
DEFAULT_RENDERER = "bash";
|
|
266
|
+
VALID_RENDERERS = /* @__PURE__ */ new Set(["bash", "node", "python"]);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
154
270
|
// src/oauth/jwt-decode.ts
|
|
155
271
|
function base64UrlDecode(input) {
|
|
156
272
|
const padded = input + "=".repeat((4 - input.length % 4) % 4);
|
|
@@ -181,9 +297,9 @@ var init_jwt_decode = __esm({
|
|
|
181
297
|
});
|
|
182
298
|
|
|
183
299
|
// src/oauth/keychain.ts
|
|
184
|
-
import { chmod, mkdir as
|
|
300
|
+
import { chmod, mkdir as mkdir3, readFile as readFile3, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
185
301
|
import { homedir, platform } from "node:os";
|
|
186
|
-
import { dirname as dirname2, join as
|
|
302
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
187
303
|
async function loadKeyring() {
|
|
188
304
|
if (keyringOverride !== void 0) return keyringOverride;
|
|
189
305
|
try {
|
|
@@ -195,18 +311,18 @@ async function loadKeyring() {
|
|
|
195
311
|
}
|
|
196
312
|
function fallbackDir() {
|
|
197
313
|
if (platform() === "win32") {
|
|
198
|
-
const appData = process.env.APPDATA ??
|
|
199
|
-
return
|
|
314
|
+
const appData = process.env.APPDATA ?? join3(homedir(), "AppData", "Roaming");
|
|
315
|
+
return join3(appData, "codebyplan");
|
|
200
316
|
}
|
|
201
|
-
const xdg = process.env.XDG_CONFIG_HOME ??
|
|
202
|
-
return
|
|
317
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? join3(homedir(), ".config");
|
|
318
|
+
return join3(xdg, "codebyplan");
|
|
203
319
|
}
|
|
204
320
|
function fallbackFile(filename) {
|
|
205
|
-
return
|
|
321
|
+
return join3(fallbackDir(), filename);
|
|
206
322
|
}
|
|
207
323
|
async function readFallback(filename) {
|
|
208
324
|
try {
|
|
209
|
-
const raw = await
|
|
325
|
+
const raw = await readFile3(fallbackFile(filename), "utf-8");
|
|
210
326
|
return JSON.parse(raw);
|
|
211
327
|
} catch {
|
|
212
328
|
return null;
|
|
@@ -214,8 +330,8 @@ async function readFallback(filename) {
|
|
|
214
330
|
}
|
|
215
331
|
async function writeFallback(filename, data) {
|
|
216
332
|
const path6 = fallbackFile(filename);
|
|
217
|
-
await
|
|
218
|
-
await
|
|
333
|
+
await mkdir3(dirname2(path6), { recursive: true });
|
|
334
|
+
await writeFile3(path6, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
219
335
|
if (platform() !== "win32") {
|
|
220
336
|
try {
|
|
221
337
|
await chmod(path6, 384);
|
|
@@ -566,8 +682,8 @@ var init_api = __esm({
|
|
|
566
682
|
});
|
|
567
683
|
|
|
568
684
|
// src/lib/resolve-worktree.ts
|
|
569
|
-
import { readFile as
|
|
570
|
-
import { join as
|
|
685
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
686
|
+
import { join as join4 } from "node:path";
|
|
571
687
|
async function resolveAndCacheWorktreeId(repoId, projectPath, options) {
|
|
572
688
|
let worktreeId;
|
|
573
689
|
try {
|
|
@@ -589,10 +705,10 @@ async function resolveAndCacheWorktreeId(repoId, projectPath, options) {
|
|
|
589
705
|
if (options?.skipWrite) {
|
|
590
706
|
return worktreeId;
|
|
591
707
|
}
|
|
592
|
-
const codebyplanPath =
|
|
708
|
+
const codebyplanPath = join4(projectPath, ".codebyplan.json");
|
|
593
709
|
let currentConfig = {};
|
|
594
710
|
try {
|
|
595
|
-
const raw = await
|
|
711
|
+
const raw = await readFile4(codebyplanPath, "utf-8");
|
|
596
712
|
const parsed = JSON.parse(raw);
|
|
597
713
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
598
714
|
currentConfig = parsed;
|
|
@@ -607,7 +723,7 @@ async function resolveAndCacheWorktreeId(repoId, projectPath, options) {
|
|
|
607
723
|
worktree_id: worktreeId
|
|
608
724
|
};
|
|
609
725
|
try {
|
|
610
|
-
await
|
|
726
|
+
await writeFile4(
|
|
611
727
|
codebyplanPath,
|
|
612
728
|
JSON.stringify(merged, null, 2) + "\n",
|
|
613
729
|
"utf-8"
|
|
@@ -941,13 +1057,13 @@ var setup_exports = {};
|
|
|
941
1057
|
__export(setup_exports, {
|
|
942
1058
|
runSetup: () => runSetup
|
|
943
1059
|
});
|
|
944
|
-
import { mkdir as
|
|
1060
|
+
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
|
|
945
1061
|
import { homedir as homedir2 } from "node:os";
|
|
946
|
-
import { join as
|
|
1062
|
+
import { join as join5 } from "node:path";
|
|
947
1063
|
import { stdin, stdout as stdout2 } from "node:process";
|
|
948
1064
|
import { createInterface } from "node:readline/promises";
|
|
949
1065
|
function getConfigPath(scope) {
|
|
950
|
-
return scope === "user" ?
|
|
1066
|
+
return scope === "user" ? join5(homedir2(), ".claude.json") : join5(process.cwd(), ".mcp.json");
|
|
951
1067
|
}
|
|
952
1068
|
function legacyMcpUrl() {
|
|
953
1069
|
const baseUrl2 = process.env.CODEBYPLAN_API_URL ?? "https://www.codebyplan.com";
|
|
@@ -955,7 +1071,7 @@ function legacyMcpUrl() {
|
|
|
955
1071
|
}
|
|
956
1072
|
async function readConfig(path6) {
|
|
957
1073
|
try {
|
|
958
|
-
const raw = await
|
|
1074
|
+
const raw = await readFile5(path6, "utf-8");
|
|
959
1075
|
const parsed = JSON.parse(raw);
|
|
960
1076
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
961
1077
|
return parsed;
|
|
@@ -978,7 +1094,7 @@ async function writeMcpConfig(scope, auth) {
|
|
|
978
1094
|
config.mcpServers = {};
|
|
979
1095
|
}
|
|
980
1096
|
config.mcpServers.codebyplan = buildMcpEntry(auth);
|
|
981
|
-
await
|
|
1097
|
+
await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
982
1098
|
return configPath;
|
|
983
1099
|
}
|
|
984
1100
|
async function fetchRepos(auth) {
|
|
@@ -1033,8 +1149,8 @@ async function chooseAuthMode(rl) {
|
|
|
1033
1149
|
return { kind: "legacy", apiKey };
|
|
1034
1150
|
}
|
|
1035
1151
|
async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
|
|
1036
|
-
const codebyplanDir =
|
|
1037
|
-
await
|
|
1152
|
+
const codebyplanDir = join5(projectPath, ".codebyplan");
|
|
1153
|
+
await mkdir4(codebyplanDir, { recursive: true });
|
|
1038
1154
|
const repoJson = {
|
|
1039
1155
|
repo_id: selectedRepo.id
|
|
1040
1156
|
};
|
|
@@ -1045,13 +1161,13 @@ async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
|
|
|
1045
1161
|
if (typeof repoAny.project_id === "string") {
|
|
1046
1162
|
repoJson.project_id = repoAny.project_id;
|
|
1047
1163
|
}
|
|
1048
|
-
await
|
|
1049
|
-
|
|
1164
|
+
await writeFile5(
|
|
1165
|
+
join5(codebyplanDir, "repo.json"),
|
|
1050
1166
|
JSON.stringify(repoJson, null, 2) + "\n",
|
|
1051
1167
|
"utf-8"
|
|
1052
1168
|
);
|
|
1053
|
-
await
|
|
1054
|
-
|
|
1169
|
+
await writeFile5(
|
|
1170
|
+
join5(codebyplanDir, "server.json"),
|
|
1055
1171
|
JSON.stringify(
|
|
1056
1172
|
{
|
|
1057
1173
|
server_port: null,
|
|
@@ -1064,40 +1180,68 @@ async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
|
|
|
1064
1180
|
) + "\n",
|
|
1065
1181
|
"utf-8"
|
|
1066
1182
|
);
|
|
1067
|
-
await
|
|
1068
|
-
|
|
1183
|
+
await writeFile5(
|
|
1184
|
+
join5(codebyplanDir, "git.json"),
|
|
1069
1185
|
JSON.stringify({ git_branch: null, branch_config: null }, null, 2) + "\n",
|
|
1070
1186
|
"utf-8"
|
|
1071
1187
|
);
|
|
1072
|
-
await
|
|
1073
|
-
|
|
1188
|
+
await writeFile5(
|
|
1189
|
+
join5(codebyplanDir, "shipment.json"),
|
|
1074
1190
|
JSON.stringify({ shipment: null }, null, 2) + "\n",
|
|
1075
1191
|
"utf-8"
|
|
1076
1192
|
);
|
|
1077
|
-
await
|
|
1078
|
-
|
|
1193
|
+
await writeFile5(
|
|
1194
|
+
join5(codebyplanDir, "vendor.json"),
|
|
1079
1195
|
JSON.stringify({}, null, 2) + "\n",
|
|
1080
1196
|
"utf-8"
|
|
1081
1197
|
);
|
|
1198
|
+
const statuslinePath = join5(codebyplanDir, "statusline.json");
|
|
1199
|
+
let statuslineExists = false;
|
|
1200
|
+
try {
|
|
1201
|
+
await readFile5(statuslinePath, "utf-8");
|
|
1202
|
+
statuslineExists = true;
|
|
1203
|
+
} catch {
|
|
1204
|
+
}
|
|
1205
|
+
if (!statuslineExists) {
|
|
1206
|
+
await writeFile5(
|
|
1207
|
+
statuslinePath,
|
|
1208
|
+
JSON.stringify(STATUSLINE_DEFAULTS, null, 2) + "\n",
|
|
1209
|
+
"utf-8"
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1082
1212
|
await writeLocalConfig(projectPath, { device_id: deviceId });
|
|
1083
1213
|
console.log(` Created ${codebyplanDir}/`);
|
|
1084
1214
|
console.log(
|
|
1085
|
-
` repo.json, server.json, git.json, shipment.json, vendor.json`
|
|
1215
|
+
` repo.json, server.json, git.json, shipment.json, vendor.json, statusline.json`
|
|
1086
1216
|
);
|
|
1087
1217
|
console.log(` device.local.json (gitignored)`);
|
|
1088
|
-
const gitignorePath =
|
|
1218
|
+
const gitignorePath = join5(projectPath, ".gitignore");
|
|
1089
1219
|
const gitignoreEntry = ".codebyplan/device.local.json";
|
|
1220
|
+
const statuslineLocalEntry = ".codebyplan/statusline.local.json";
|
|
1090
1221
|
try {
|
|
1091
|
-
const existing = await
|
|
1222
|
+
const existing = await readFile5(gitignorePath, "utf-8");
|
|
1092
1223
|
const lines = existing.split("\n");
|
|
1224
|
+
let content = existing;
|
|
1093
1225
|
if (!lines.some((l) => l.trimEnd() === gitignoreEntry)) {
|
|
1094
|
-
|
|
1095
|
-
await writeFile4(gitignorePath, appended, "utf-8");
|
|
1226
|
+
content = (content.endsWith("\n") ? content : content + "\n") + gitignoreEntry + "\n";
|
|
1096
1227
|
console.log(` Added '${gitignoreEntry}' to .gitignore`);
|
|
1097
1228
|
}
|
|
1229
|
+
if (!lines.some((l) => l.trimEnd() === statuslineLocalEntry)) {
|
|
1230
|
+
content = (content.endsWith("\n") ? content : content + "\n") + statuslineLocalEntry + "\n";
|
|
1231
|
+
console.log(` Added '${statuslineLocalEntry}' to .gitignore`);
|
|
1232
|
+
}
|
|
1233
|
+
if (content !== existing) {
|
|
1234
|
+
await writeFile5(gitignorePath, content, "utf-8");
|
|
1235
|
+
}
|
|
1098
1236
|
} catch {
|
|
1099
|
-
await
|
|
1100
|
-
|
|
1237
|
+
await writeFile5(
|
|
1238
|
+
gitignorePath,
|
|
1239
|
+
gitignoreEntry + "\n" + statuslineLocalEntry + "\n",
|
|
1240
|
+
"utf-8"
|
|
1241
|
+
);
|
|
1242
|
+
console.log(
|
|
1243
|
+
` Created .gitignore with '${gitignoreEntry}' and '${statuslineLocalEntry}'`
|
|
1244
|
+
);
|
|
1101
1245
|
}
|
|
1102
1246
|
}
|
|
1103
1247
|
async function runSetup() {
|
|
@@ -1190,6 +1334,31 @@ async function runSetup() {
|
|
|
1190
1334
|
const _worktreeId = tupleId ?? pathBasedId;
|
|
1191
1335
|
void _worktreeId;
|
|
1192
1336
|
await writeCodebyplanDirectory(projectPath, selectedRepo, deviceId);
|
|
1337
|
+
const existingRenderer = await readStatuslineLocalConfig(projectPath);
|
|
1338
|
+
const isInteractive = process.stdin.isTTY === true;
|
|
1339
|
+
if (!existingRenderer && isInteractive) {
|
|
1340
|
+
const availability = detectAvailableRenderers();
|
|
1341
|
+
const available = Object.entries(availability).filter(([, v]) => v).map(([k]) => k).join(", ");
|
|
1342
|
+
console.log(
|
|
1343
|
+
`
|
|
1344
|
+
Which statusline renderer would you like to use? (available: ${available})`
|
|
1345
|
+
);
|
|
1346
|
+
console.log(" bash \u2014 shell script, zero dependencies (default)");
|
|
1347
|
+
console.log(" node \u2014 Node.js renderer");
|
|
1348
|
+
console.log(" python \u2014 Python 3 renderer");
|
|
1349
|
+
const rendererInput = (await rl.question(
|
|
1350
|
+
" Select renderer (bash/node/python, default: bash): "
|
|
1351
|
+
)).trim().toLowerCase();
|
|
1352
|
+
const chosen = rendererInput === "node" ? "node" : rendererInput === "python" ? "python" : "bash";
|
|
1353
|
+
await writeStatuslineLocalConfig(projectPath, { renderer: chosen });
|
|
1354
|
+
if (!availability[chosen]) {
|
|
1355
|
+
console.log(
|
|
1356
|
+
` Warning: '${chosen}' was not detected on this machine. You can change it later with \`codebyplan statusline\`.`
|
|
1357
|
+
);
|
|
1358
|
+
} else {
|
|
1359
|
+
console.log(` Renderer set to '${chosen}'.`);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1193
1362
|
console.log(
|
|
1194
1363
|
"\n Run `npx codebyplan config` to sync repo config from the DB."
|
|
1195
1364
|
);
|
|
@@ -1204,6 +1373,7 @@ var init_setup = __esm({
|
|
|
1204
1373
|
"src/cli/setup.ts"() {
|
|
1205
1374
|
"use strict";
|
|
1206
1375
|
init_local_config();
|
|
1376
|
+
init_statusline_config();
|
|
1207
1377
|
init_resolve_worktree();
|
|
1208
1378
|
init_token_refresh();
|
|
1209
1379
|
init_urls();
|
|
@@ -1211,6 +1381,190 @@ var init_setup = __esm({
|
|
|
1211
1381
|
}
|
|
1212
1382
|
});
|
|
1213
1383
|
|
|
1384
|
+
// src/lib/flags.ts
|
|
1385
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
1386
|
+
import { join as join6, resolve } from "node:path";
|
|
1387
|
+
async function findCodebyplanConfig(startDir, maxDepth = 20) {
|
|
1388
|
+
let cursor = resolve(startDir);
|
|
1389
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1390
|
+
const sentinelPath2 = join6(cursor, ".codebyplan", "repo.json");
|
|
1391
|
+
try {
|
|
1392
|
+
const raw = await readFile6(sentinelPath2, "utf-8");
|
|
1393
|
+
const parsed = JSON.parse(raw);
|
|
1394
|
+
return { path: sentinelPath2, contents: parsed };
|
|
1395
|
+
} catch {
|
|
1396
|
+
}
|
|
1397
|
+
const legacyPath = join6(cursor, ".codebyplan.json");
|
|
1398
|
+
try {
|
|
1399
|
+
const raw = await readFile6(legacyPath, "utf-8");
|
|
1400
|
+
const parsed = JSON.parse(raw);
|
|
1401
|
+
return { path: legacyPath, contents: parsed };
|
|
1402
|
+
} catch {
|
|
1403
|
+
}
|
|
1404
|
+
const parent = resolve(cursor, "..");
|
|
1405
|
+
if (parent === cursor) return null;
|
|
1406
|
+
cursor = parent;
|
|
1407
|
+
}
|
|
1408
|
+
return null;
|
|
1409
|
+
}
|
|
1410
|
+
function parseFlags(startIndex) {
|
|
1411
|
+
const flags = {};
|
|
1412
|
+
const args = process.argv.slice(startIndex);
|
|
1413
|
+
for (let i = 0; i < args.length; i++) {
|
|
1414
|
+
const arg = args[i];
|
|
1415
|
+
if (arg.startsWith("--") && i + 1 < args.length) {
|
|
1416
|
+
const key = arg.slice(2);
|
|
1417
|
+
flags[key] = args[++i];
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return flags;
|
|
1421
|
+
}
|
|
1422
|
+
function hasFlag(name, startIndex) {
|
|
1423
|
+
return process.argv.slice(startIndex).includes(`--${name}`);
|
|
1424
|
+
}
|
|
1425
|
+
async function resolveConfig(flags) {
|
|
1426
|
+
const projectPath = flags["path"] ?? process.cwd();
|
|
1427
|
+
let repoId = flags["repo-id"] ?? process.env.CODEBYPLAN_REPO_ID;
|
|
1428
|
+
let worktreeId = flags["worktree-id"] ?? process.env.CODEBYPLAN_WORKTREE_ID;
|
|
1429
|
+
if (!repoId) {
|
|
1430
|
+
const found = await findCodebyplanConfig(projectPath);
|
|
1431
|
+
if (found) {
|
|
1432
|
+
repoId = found.contents.repo_id;
|
|
1433
|
+
if (!worktreeId) worktreeId = found.contents.worktree_id;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (!repoId) {
|
|
1437
|
+
throw new Error(
|
|
1438
|
+
`Could not determine repo_id.
|
|
1439
|
+
|
|
1440
|
+
Provide it via one of:
|
|
1441
|
+
--repo-id <uuid> CLI flag
|
|
1442
|
+
CODEBYPLAN_REPO_ID=<uuid> environment variable
|
|
1443
|
+
.codebyplan/repo.json { "repo_id": "<uuid>" } in project root
|
|
1444
|
+
Run 'codebyplan setup' to initialize this project`
|
|
1445
|
+
);
|
|
1446
|
+
}
|
|
1447
|
+
return { repoId, worktreeId, projectPath };
|
|
1448
|
+
}
|
|
1449
|
+
var init_flags = __esm({
|
|
1450
|
+
"src/lib/flags.ts"() {
|
|
1451
|
+
"use strict";
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
|
|
1455
|
+
// src/cli/statusline.ts
|
|
1456
|
+
var statusline_exports = {};
|
|
1457
|
+
__export(statusline_exports, {
|
|
1458
|
+
runStatusline: () => runStatusline
|
|
1459
|
+
});
|
|
1460
|
+
import { dirname as dirname3 } from "node:path";
|
|
1461
|
+
async function resolveProjectPath() {
|
|
1462
|
+
const found = await findCodebyplanConfig(process.cwd());
|
|
1463
|
+
if (found) {
|
|
1464
|
+
return dirname3(dirname3(found.path));
|
|
1465
|
+
}
|
|
1466
|
+
return process.cwd();
|
|
1467
|
+
}
|
|
1468
|
+
function parseRendererArg(arg) {
|
|
1469
|
+
const normalised = arg.startsWith("--") ? arg.slice(2) : arg;
|
|
1470
|
+
if (VALID_RENDERERS2.has(normalised)) {
|
|
1471
|
+
return normalised;
|
|
1472
|
+
}
|
|
1473
|
+
return null;
|
|
1474
|
+
}
|
|
1475
|
+
async function runStatusline(args) {
|
|
1476
|
+
const positional = args.filter((a) => !a.startsWith("-"));
|
|
1477
|
+
const flagArgs = args.filter((a) => a.startsWith("--"));
|
|
1478
|
+
const rendererTokens = [...positional, ...flagArgs].filter(
|
|
1479
|
+
(a) => parseRendererArg(a) !== null
|
|
1480
|
+
);
|
|
1481
|
+
if (rendererTokens.length > 1) {
|
|
1482
|
+
process.stderr.write(
|
|
1483
|
+
"codebyplan statusline: bash, node, and python are mutually exclusive; pass only one.\n"
|
|
1484
|
+
);
|
|
1485
|
+
process.exitCode = 1;
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
let rendererArg;
|
|
1489
|
+
for (const a of [...positional, ...flagArgs]) {
|
|
1490
|
+
const parsed = parseRendererArg(a);
|
|
1491
|
+
if (parsed !== null) {
|
|
1492
|
+
rendererArg = a;
|
|
1493
|
+
break;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
const unknownPositional = args.filter(
|
|
1497
|
+
(a) => !a.startsWith("-") && !VALID_RENDERERS2.has(a)
|
|
1498
|
+
);
|
|
1499
|
+
if (unknownPositional.length > 0) {
|
|
1500
|
+
process.stderr.write(
|
|
1501
|
+
`codebyplan statusline: unknown argument '${unknownPositional[0]}'.
|
|
1502
|
+
|
|
1503
|
+
Usage:
|
|
1504
|
+
codebyplan statusline Show current renderer and line toggles
|
|
1505
|
+
codebyplan statusline bash|node|python Set the renderer
|
|
1506
|
+
codebyplan statusline --bash|--node|--python Set the renderer (flag form)
|
|
1507
|
+
`
|
|
1508
|
+
);
|
|
1509
|
+
process.exitCode = 1;
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
const unknownFlags = flagArgs.filter((a) => parseRendererArg(a) === null);
|
|
1513
|
+
if (unknownFlags.length > 0) {
|
|
1514
|
+
process.stderr.write(
|
|
1515
|
+
`codebyplan statusline: unknown flag '${unknownFlags[0]}'.
|
|
1516
|
+
|
|
1517
|
+
Usage:
|
|
1518
|
+
codebyplan statusline Show current renderer and line toggles
|
|
1519
|
+
codebyplan statusline bash|node|python Set the renderer
|
|
1520
|
+
codebyplan statusline --bash|--node|--python Set the renderer (flag form)
|
|
1521
|
+
`
|
|
1522
|
+
);
|
|
1523
|
+
process.exitCode = 1;
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
const projectPath = await resolveProjectPath();
|
|
1527
|
+
if (rendererArg !== void 0) {
|
|
1528
|
+
const chosen = parseRendererArg(rendererArg);
|
|
1529
|
+
await writeStatuslineLocalConfig(projectPath, { renderer: chosen });
|
|
1530
|
+
const availability2 = detectAvailableRenderers();
|
|
1531
|
+
if (!availability2[chosen]) {
|
|
1532
|
+
process.stderr.write(
|
|
1533
|
+
`Warning: '${chosen}' was not detected on this machine. The renderer is saved but may not run.
|
|
1534
|
+
`
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
console.log(`Statusline renderer set to '${chosen}'.`);
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
const availability = detectAvailableRenderers();
|
|
1541
|
+
const [renderer, config] = await Promise.all([
|
|
1542
|
+
resolveRenderer(projectPath),
|
|
1543
|
+
readStatuslineConfig(projectPath)
|
|
1544
|
+
]);
|
|
1545
|
+
const availStr = Object.entries(availability).map(([k, v]) => `${k}: ${v ? "yes" : "no"}`).join(", ");
|
|
1546
|
+
console.log(`
|
|
1547
|
+
Statusline configuration`);
|
|
1548
|
+
console.log(` Renderer: ${renderer}`);
|
|
1549
|
+
console.log(` Available: ${availStr}`);
|
|
1550
|
+
console.log(` no_color: ${config.no_color}`);
|
|
1551
|
+
console.log(`
|
|
1552
|
+
Line toggles:`);
|
|
1553
|
+
for (const [key, val] of Object.entries(config.lines)) {
|
|
1554
|
+
console.log(` ${key.padEnd(14)} ${val ? "on" : "off"}`);
|
|
1555
|
+
}
|
|
1556
|
+
console.log();
|
|
1557
|
+
}
|
|
1558
|
+
var VALID_RENDERERS2;
|
|
1559
|
+
var init_statusline = __esm({
|
|
1560
|
+
"src/cli/statusline.ts"() {
|
|
1561
|
+
"use strict";
|
|
1562
|
+
init_flags();
|
|
1563
|
+
init_statusline_config();
|
|
1564
|
+
VALID_RENDERERS2 = /* @__PURE__ */ new Set(["bash", "node", "python"]);
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1214
1568
|
// src/cli/logout.ts
|
|
1215
1569
|
var logout_exports = {};
|
|
1216
1570
|
__export(logout_exports, {
|
|
@@ -1269,15 +1623,15 @@ var upgrade_auth_exports = {};
|
|
|
1269
1623
|
__export(upgrade_auth_exports, {
|
|
1270
1624
|
runUpgradeAuth: () => runUpgradeAuth
|
|
1271
1625
|
});
|
|
1272
|
-
import { readFile as
|
|
1626
|
+
import { readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
|
|
1273
1627
|
import { homedir as homedir3 } from "node:os";
|
|
1274
|
-
import { join as
|
|
1628
|
+
import { join as join7 } from "node:path";
|
|
1275
1629
|
function configPaths() {
|
|
1276
|
-
return [
|
|
1630
|
+
return [join7(homedir3(), ".claude.json"), join7(process.cwd(), ".mcp.json")];
|
|
1277
1631
|
}
|
|
1278
1632
|
async function readConfig2(path6) {
|
|
1279
1633
|
try {
|
|
1280
|
-
const raw = await
|
|
1634
|
+
const raw = await readFile7(path6, "utf-8");
|
|
1281
1635
|
const parsed = JSON.parse(raw);
|
|
1282
1636
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
1283
1637
|
return parsed;
|
|
@@ -1298,7 +1652,7 @@ async function rewriteConfig(path6, config, newUrl) {
|
|
|
1298
1652
|
if (!entry) return false;
|
|
1299
1653
|
if (!entryHasLegacyApiKey(entry) && entry.url === newUrl) return false;
|
|
1300
1654
|
servers.codebyplan = { url: newUrl };
|
|
1301
|
-
await
|
|
1655
|
+
await writeFile6(path6, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1302
1656
|
return true;
|
|
1303
1657
|
}
|
|
1304
1658
|
async function runUpgradeAuth() {
|
|
@@ -1335,77 +1689,6 @@ var init_upgrade_auth = __esm({
|
|
|
1335
1689
|
}
|
|
1336
1690
|
});
|
|
1337
1691
|
|
|
1338
|
-
// src/lib/flags.ts
|
|
1339
|
-
import { readFile as readFile6 } from "node:fs/promises";
|
|
1340
|
-
import { join as join6, resolve } from "node:path";
|
|
1341
|
-
async function findCodebyplanConfig(startDir, maxDepth = 20) {
|
|
1342
|
-
let cursor = resolve(startDir);
|
|
1343
|
-
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1344
|
-
const sentinelPath2 = join6(cursor, ".codebyplan", "repo.json");
|
|
1345
|
-
try {
|
|
1346
|
-
const raw = await readFile6(sentinelPath2, "utf-8");
|
|
1347
|
-
const parsed = JSON.parse(raw);
|
|
1348
|
-
return { path: sentinelPath2, contents: parsed };
|
|
1349
|
-
} catch {
|
|
1350
|
-
}
|
|
1351
|
-
const legacyPath = join6(cursor, ".codebyplan.json");
|
|
1352
|
-
try {
|
|
1353
|
-
const raw = await readFile6(legacyPath, "utf-8");
|
|
1354
|
-
const parsed = JSON.parse(raw);
|
|
1355
|
-
return { path: legacyPath, contents: parsed };
|
|
1356
|
-
} catch {
|
|
1357
|
-
}
|
|
1358
|
-
const parent = resolve(cursor, "..");
|
|
1359
|
-
if (parent === cursor) return null;
|
|
1360
|
-
cursor = parent;
|
|
1361
|
-
}
|
|
1362
|
-
return null;
|
|
1363
|
-
}
|
|
1364
|
-
function parseFlags(startIndex) {
|
|
1365
|
-
const flags = {};
|
|
1366
|
-
const args = process.argv.slice(startIndex);
|
|
1367
|
-
for (let i = 0; i < args.length; i++) {
|
|
1368
|
-
const arg = args[i];
|
|
1369
|
-
if (arg.startsWith("--") && i + 1 < args.length) {
|
|
1370
|
-
const key = arg.slice(2);
|
|
1371
|
-
flags[key] = args[++i];
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
return flags;
|
|
1375
|
-
}
|
|
1376
|
-
function hasFlag(name, startIndex) {
|
|
1377
|
-
return process.argv.slice(startIndex).includes(`--${name}`);
|
|
1378
|
-
}
|
|
1379
|
-
async function resolveConfig(flags) {
|
|
1380
|
-
const projectPath = flags["path"] ?? process.cwd();
|
|
1381
|
-
let repoId = flags["repo-id"] ?? process.env.CODEBYPLAN_REPO_ID;
|
|
1382
|
-
let worktreeId = flags["worktree-id"] ?? process.env.CODEBYPLAN_WORKTREE_ID;
|
|
1383
|
-
if (!repoId) {
|
|
1384
|
-
const found = await findCodebyplanConfig(projectPath);
|
|
1385
|
-
if (found) {
|
|
1386
|
-
repoId = found.contents.repo_id;
|
|
1387
|
-
if (!worktreeId) worktreeId = found.contents.worktree_id;
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
if (!repoId) {
|
|
1391
|
-
throw new Error(
|
|
1392
|
-
`Could not determine repo_id.
|
|
1393
|
-
|
|
1394
|
-
Provide it via one of:
|
|
1395
|
-
--repo-id <uuid> CLI flag
|
|
1396
|
-
CODEBYPLAN_REPO_ID=<uuid> environment variable
|
|
1397
|
-
.codebyplan/repo.json { "repo_id": "<uuid>" } in project root
|
|
1398
|
-
Run 'codebyplan setup' to initialize this project`
|
|
1399
|
-
);
|
|
1400
|
-
}
|
|
1401
|
-
return { repoId, worktreeId, projectPath };
|
|
1402
|
-
}
|
|
1403
|
-
var init_flags = __esm({
|
|
1404
|
-
"src/lib/flags.ts"() {
|
|
1405
|
-
"use strict";
|
|
1406
|
-
}
|
|
1407
|
-
});
|
|
1408
|
-
|
|
1409
1692
|
// src/cli/confirm.ts
|
|
1410
1693
|
var confirm_exports = {};
|
|
1411
1694
|
__export(confirm_exports, {
|
|
@@ -1450,8 +1733,8 @@ var init_confirm = __esm({
|
|
|
1450
1733
|
});
|
|
1451
1734
|
|
|
1452
1735
|
// src/lib/tech-detect.ts
|
|
1453
|
-
import { readFile as
|
|
1454
|
-
import { join as
|
|
1736
|
+
import { readFile as readFile8, access, readdir } from "node:fs/promises";
|
|
1737
|
+
import { join as join8, relative } from "node:path";
|
|
1455
1738
|
async function fileExists(filePath) {
|
|
1456
1739
|
try {
|
|
1457
1740
|
await access(filePath);
|
|
@@ -1464,8 +1747,8 @@ async function discoverMonorepoApps(projectPath) {
|
|
|
1464
1747
|
const apps = [];
|
|
1465
1748
|
const patterns = [];
|
|
1466
1749
|
try {
|
|
1467
|
-
const raw = await
|
|
1468
|
-
|
|
1750
|
+
const raw = await readFile8(
|
|
1751
|
+
join8(projectPath, "pnpm-workspace.yaml"),
|
|
1469
1752
|
"utf-8"
|
|
1470
1753
|
);
|
|
1471
1754
|
const matches = raw.match(/^\s*-\s*['"]?([^'"#\n]+)['"]?/gm);
|
|
@@ -1479,7 +1762,7 @@ async function discoverMonorepoApps(projectPath) {
|
|
|
1479
1762
|
}
|
|
1480
1763
|
if (patterns.length === 0) {
|
|
1481
1764
|
try {
|
|
1482
|
-
const raw = await
|
|
1765
|
+
const raw = await readFile8(join8(projectPath, "package.json"), "utf-8");
|
|
1483
1766
|
const pkg = JSON.parse(raw);
|
|
1484
1767
|
const ws = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
1485
1768
|
if (ws) patterns.push(...ws);
|
|
@@ -1489,14 +1772,14 @@ async function discoverMonorepoApps(projectPath) {
|
|
|
1489
1772
|
for (const pattern of patterns) {
|
|
1490
1773
|
if (pattern.endsWith("/*")) {
|
|
1491
1774
|
const dir = pattern.slice(0, -2);
|
|
1492
|
-
const absDir =
|
|
1775
|
+
const absDir = join8(projectPath, dir);
|
|
1493
1776
|
try {
|
|
1494
1777
|
const entries = await readdir(absDir, { withFileTypes: true });
|
|
1495
1778
|
for (const entry of entries) {
|
|
1496
1779
|
if (entry.isDirectory()) {
|
|
1497
|
-
const relPath =
|
|
1498
|
-
const absPath =
|
|
1499
|
-
if (await fileExists(
|
|
1780
|
+
const relPath = join8(dir, entry.name);
|
|
1781
|
+
const absPath = join8(absDir, entry.name);
|
|
1782
|
+
if (await fileExists(join8(absPath, "package.json"))) {
|
|
1500
1783
|
apps.push({ name: entry.name, path: relPath, absPath });
|
|
1501
1784
|
}
|
|
1502
1785
|
}
|
|
@@ -1515,7 +1798,7 @@ async function hasJsxFile(dir, depth = 0) {
|
|
|
1515
1798
|
const name = entry.name;
|
|
1516
1799
|
if (entry.isDirectory()) {
|
|
1517
1800
|
if (SKIP_DIRS.has(name) || JSX_SKIP_DIRS.has(name)) continue;
|
|
1518
|
-
if (await hasJsxFile(
|
|
1801
|
+
if (await hasJsxFile(join8(dir, name), depth + 1)) return true;
|
|
1519
1802
|
} else if (entry.isFile()) {
|
|
1520
1803
|
if (JSX_TEST_PATTERN.test(name)) continue;
|
|
1521
1804
|
if (name.endsWith(".tsx") || name.endsWith(".jsx")) return true;
|
|
@@ -1534,7 +1817,7 @@ async function hasJsxFile(dir, depth = 0) {
|
|
|
1534
1817
|
async function detectCapabilities(dirPath, pkgJson) {
|
|
1535
1818
|
const caps = /* @__PURE__ */ new Set();
|
|
1536
1819
|
for (const sub of JSX_SCAN_DIRS) {
|
|
1537
|
-
if (await hasJsxFile(
|
|
1820
|
+
if (await hasJsxFile(join8(dirPath, sub))) {
|
|
1538
1821
|
caps.add("jsx");
|
|
1539
1822
|
break;
|
|
1540
1823
|
}
|
|
@@ -1556,7 +1839,7 @@ async function detectCapabilities(dirPath, pkgJson) {
|
|
|
1556
1839
|
}
|
|
1557
1840
|
}
|
|
1558
1841
|
}
|
|
1559
|
-
if (!caps.has("node-server") && await fileExists(
|
|
1842
|
+
if (!caps.has("node-server") && await fileExists(join8(dirPath, "src", "main.ts"))) {
|
|
1560
1843
|
caps.add("node-server");
|
|
1561
1844
|
}
|
|
1562
1845
|
if (pkgJson && pkgJson.bin) {
|
|
@@ -1572,7 +1855,7 @@ async function detectFromDirectory(dirPath) {
|
|
|
1572
1855
|
const seen = /* @__PURE__ */ new Map();
|
|
1573
1856
|
let pkgJson = null;
|
|
1574
1857
|
try {
|
|
1575
|
-
const raw = await
|
|
1858
|
+
const raw = await readFile8(join8(dirPath, "package.json"), "utf-8");
|
|
1576
1859
|
pkgJson = JSON.parse(raw);
|
|
1577
1860
|
const allDeps = {
|
|
1578
1861
|
...pkgJson.dependencies ?? {},
|
|
@@ -1604,7 +1887,7 @@ async function detectFromDirectory(dirPath) {
|
|
|
1604
1887
|
}
|
|
1605
1888
|
for (const { file, rule } of CONFIG_FILE_MAP) {
|
|
1606
1889
|
const key = rule.name.toLowerCase();
|
|
1607
|
-
if (!seen.has(key) && await fileExists(
|
|
1890
|
+
if (!seen.has(key) && await fileExists(join8(dirPath, file))) {
|
|
1608
1891
|
seen.set(key, { name: rule.name, category: rule.category });
|
|
1609
1892
|
}
|
|
1610
1893
|
}
|
|
@@ -1782,7 +2065,7 @@ function categorizeDependency(depName) {
|
|
|
1782
2065
|
async function findPackageJsonFiles(dir, projectPath, depth = 0) {
|
|
1783
2066
|
if (depth > 4) return [];
|
|
1784
2067
|
const results = [];
|
|
1785
|
-
const pkgPath =
|
|
2068
|
+
const pkgPath = join8(dir, "package.json");
|
|
1786
2069
|
if (await fileExists(pkgPath)) {
|
|
1787
2070
|
results.push(pkgPath);
|
|
1788
2071
|
}
|
|
@@ -1791,7 +2074,7 @@ async function findPackageJsonFiles(dir, projectPath, depth = 0) {
|
|
|
1791
2074
|
for (const entry of entries) {
|
|
1792
2075
|
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) continue;
|
|
1793
2076
|
const subResults = await findPackageJsonFiles(
|
|
1794
|
-
|
|
2077
|
+
join8(dir, entry.name),
|
|
1795
2078
|
projectPath,
|
|
1796
2079
|
depth + 1
|
|
1797
2080
|
);
|
|
@@ -1806,7 +2089,7 @@ async function scanAllDependencies(projectPath) {
|
|
|
1806
2089
|
const dependencies = [];
|
|
1807
2090
|
for (const pkgPath of packageJsonPaths) {
|
|
1808
2091
|
try {
|
|
1809
|
-
const raw = await
|
|
2092
|
+
const raw = await readFile8(pkgPath, "utf-8");
|
|
1810
2093
|
const pkg = JSON.parse(raw);
|
|
1811
2094
|
const sourcePath = relative(projectPath, pkgPath);
|
|
1812
2095
|
const depSections = [
|
|
@@ -2406,8 +2689,8 @@ __export(eslint_exports, {
|
|
|
2406
2689
|
eslintInit: () => eslintInit,
|
|
2407
2690
|
runEslint: () => runEslint
|
|
2408
2691
|
});
|
|
2409
|
-
import { readFile as
|
|
2410
|
-
import { join as
|
|
2692
|
+
import { readFile as readFile9, writeFile as writeFile7, access as access2, readdir as readdir2 } from "node:fs/promises";
|
|
2693
|
+
import { join as join9, relative as relative2 } from "node:path";
|
|
2411
2694
|
async function fileExists2(filePath) {
|
|
2412
2695
|
try {
|
|
2413
2696
|
await access2(filePath);
|
|
@@ -2418,7 +2701,7 @@ async function fileExists2(filePath) {
|
|
|
2418
2701
|
}
|
|
2419
2702
|
async function autoDetectIgnorePatterns(absPath) {
|
|
2420
2703
|
const patterns = [];
|
|
2421
|
-
if (await fileExists2(
|
|
2704
|
+
if (await fileExists2(join9(absPath, "esbuild.js"))) {
|
|
2422
2705
|
patterns.push("esbuild.js");
|
|
2423
2706
|
}
|
|
2424
2707
|
let entries = [];
|
|
@@ -2438,19 +2721,19 @@ async function autoDetectIgnorePatterns(absPath) {
|
|
|
2438
2721
|
}
|
|
2439
2722
|
for (const ext of ["ts", "mts", "js", "mjs"]) {
|
|
2440
2723
|
const candidate = `vitest.config.${ext}`;
|
|
2441
|
-
if (await fileExists2(
|
|
2724
|
+
if (await fileExists2(join9(absPath, candidate))) {
|
|
2442
2725
|
patterns.push(candidate);
|
|
2443
2726
|
break;
|
|
2444
2727
|
}
|
|
2445
2728
|
}
|
|
2446
2729
|
for (const ext of ["ts", "mts", "js", "mjs"]) {
|
|
2447
2730
|
const candidate = `vite.config.${ext}`;
|
|
2448
|
-
if (await fileExists2(
|
|
2731
|
+
if (await fileExists2(join9(absPath, candidate))) {
|
|
2449
2732
|
patterns.push(candidate);
|
|
2450
2733
|
break;
|
|
2451
2734
|
}
|
|
2452
2735
|
}
|
|
2453
|
-
if (await fileExists2(
|
|
2736
|
+
if (await fileExists2(join9(absPath, "tauri.conf.json"))) {
|
|
2454
2737
|
patterns.push("src-tauri/**");
|
|
2455
2738
|
patterns.push("**/*.d.ts");
|
|
2456
2739
|
}
|
|
@@ -2458,14 +2741,14 @@ async function autoDetectIgnorePatterns(absPath) {
|
|
|
2458
2741
|
}
|
|
2459
2742
|
function detectPackageManager(projectPath) {
|
|
2460
2743
|
return (async () => {
|
|
2461
|
-
if (await fileExists2(
|
|
2462
|
-
if (await fileExists2(
|
|
2744
|
+
if (await fileExists2(join9(projectPath, "pnpm-lock.yaml"))) return "pnpm";
|
|
2745
|
+
if (await fileExists2(join9(projectPath, "yarn.lock"))) return "yarn";
|
|
2463
2746
|
return "npm";
|
|
2464
2747
|
})();
|
|
2465
2748
|
}
|
|
2466
2749
|
async function getInstalledDeps(pkgJsonPath) {
|
|
2467
2750
|
try {
|
|
2468
|
-
const raw = await
|
|
2751
|
+
const raw = await readFile9(pkgJsonPath, "utf-8");
|
|
2469
2752
|
const pkg = JSON.parse(raw);
|
|
2470
2753
|
const all = /* @__PURE__ */ new Set();
|
|
2471
2754
|
for (const name of Object.keys(pkg.dependencies ?? {})) all.add(name);
|
|
@@ -2578,7 +2861,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
2578
2861
|
ignorePatterns: detectedIgnores
|
|
2579
2862
|
});
|
|
2580
2863
|
const hash = hashConfig(content);
|
|
2581
|
-
const configPath =
|
|
2864
|
+
const configPath = join9(target.absPath, "eslint.config.mjs");
|
|
2582
2865
|
configsToWrite.push({
|
|
2583
2866
|
target,
|
|
2584
2867
|
presets,
|
|
@@ -2600,11 +2883,11 @@ async function eslintInit(repoId, projectPath) {
|
|
|
2600
2883
|
return;
|
|
2601
2884
|
}
|
|
2602
2885
|
const pm = await detectPackageManager(projectPath);
|
|
2603
|
-
const rootPkgJsonPath =
|
|
2886
|
+
const rootPkgJsonPath = join9(projectPath, "package.json");
|
|
2604
2887
|
const installed = await getInstalledDeps(rootPkgJsonPath);
|
|
2605
2888
|
if (isMonorepo) {
|
|
2606
2889
|
for (const { target } of configsToWrite) {
|
|
2607
|
-
const appPkgJson =
|
|
2890
|
+
const appPkgJson = join9(target.absPath, "package.json");
|
|
2608
2891
|
const appDeps = await getInstalledDeps(appPkgJson);
|
|
2609
2892
|
for (const dep of appDeps) {
|
|
2610
2893
|
installed.add(dep);
|
|
@@ -2656,7 +2939,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
2656
2939
|
} of configsToWrite) {
|
|
2657
2940
|
if (await fileExists2(configPath)) {
|
|
2658
2941
|
try {
|
|
2659
|
-
const existing = await
|
|
2942
|
+
const existing = await readFile9(configPath, "utf-8");
|
|
2660
2943
|
const existingHash = hashConfig(existing);
|
|
2661
2944
|
if (existingHash === hash) {
|
|
2662
2945
|
console.log(
|
|
@@ -2676,7 +2959,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
2676
2959
|
}
|
|
2677
2960
|
}
|
|
2678
2961
|
try {
|
|
2679
|
-
await
|
|
2962
|
+
await writeFile7(configPath, content, "utf-8");
|
|
2680
2963
|
} catch (err) {
|
|
2681
2964
|
console.error(
|
|
2682
2965
|
` ${target.name}: Failed to write config: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -2984,7 +3267,7 @@ __export(round_exports, {
|
|
|
2984
3267
|
runRoundSyncApprovals: () => runRoundSyncApprovals
|
|
2985
3268
|
});
|
|
2986
3269
|
import { access as access3 } from "node:fs/promises";
|
|
2987
|
-
import { join as
|
|
3270
|
+
import { join as join10 } from "node:path";
|
|
2988
3271
|
import { execSync as execSync2 } from "node:child_process";
|
|
2989
3272
|
async function runRoundCommand(args) {
|
|
2990
3273
|
const subcommand = args[0];
|
|
@@ -3078,7 +3361,7 @@ async function runRoundSyncApprovals(args) {
|
|
|
3078
3361
|
"sync-approvals: git status failed; proceeding with empty diff\n"
|
|
3079
3362
|
);
|
|
3080
3363
|
}
|
|
3081
|
-
const hookPath =
|
|
3364
|
+
const hookPath = join10(
|
|
3082
3365
|
repoRoot,
|
|
3083
3366
|
".claude",
|
|
3084
3367
|
"hooks",
|
|
@@ -3200,8 +3483,8 @@ var init_round = __esm({
|
|
|
3200
3483
|
});
|
|
3201
3484
|
|
|
3202
3485
|
// src/lib/migrate-branch-model.ts
|
|
3203
|
-
import { readFile as
|
|
3204
|
-
import { join as
|
|
3486
|
+
import { readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
|
|
3487
|
+
import { join as join11 } from "node:path";
|
|
3205
3488
|
import { execSync as execSync3 } from "node:child_process";
|
|
3206
3489
|
function assertValidBranchName(branch) {
|
|
3207
3490
|
if (!/^[a-zA-Z0-9/_.-]+$/.test(branch)) {
|
|
@@ -3211,7 +3494,7 @@ function assertValidBranchName(branch) {
|
|
|
3211
3494
|
}
|
|
3212
3495
|
}
|
|
3213
3496
|
async function readJsonFile(filePath) {
|
|
3214
|
-
const raw = await
|
|
3497
|
+
const raw = await readFile10(filePath, "utf-8");
|
|
3215
3498
|
const parsed = JSON.parse(raw);
|
|
3216
3499
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
3217
3500
|
throw new Error(`${filePath} does not contain a JSON object`);
|
|
@@ -3280,12 +3563,12 @@ async function runBranchMigration(opts) {
|
|
|
3280
3563
|
if (found) {
|
|
3281
3564
|
if (found.path.endsWith("/repo.json")) {
|
|
3282
3565
|
const dir = found.path.slice(0, found.path.lastIndexOf("/"));
|
|
3283
|
-
configPath =
|
|
3566
|
+
configPath = join11(dir, "git.json");
|
|
3284
3567
|
} else {
|
|
3285
3568
|
configPath = found.path;
|
|
3286
3569
|
}
|
|
3287
3570
|
} else {
|
|
3288
|
-
configPath =
|
|
3571
|
+
configPath = join11(cwd, ".codebyplan", "git.json");
|
|
3289
3572
|
}
|
|
3290
3573
|
let fileRaw;
|
|
3291
3574
|
let fileParsed;
|
|
@@ -3359,7 +3642,7 @@ async function runBranchMigration(opts) {
|
|
|
3359
3642
|
const updatedParsed = { ...fileParsed, branch_config: after };
|
|
3360
3643
|
const newJson = JSON.stringify(updatedParsed, null, 2) + "\n";
|
|
3361
3644
|
if (newJson !== fileRaw) {
|
|
3362
|
-
await
|
|
3645
|
+
await writeFile8(configPath, newJson, "utf-8");
|
|
3363
3646
|
}
|
|
3364
3647
|
}
|
|
3365
3648
|
return {
|
|
@@ -3465,9 +3748,9 @@ var init_branch = __esm({
|
|
|
3465
3748
|
});
|
|
3466
3749
|
|
|
3467
3750
|
// src/lib/ship.ts
|
|
3468
|
-
import { readFile as
|
|
3469
|
-
import { join as
|
|
3470
|
-
import { execSync as execSync4, spawnSync } from "node:child_process";
|
|
3751
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
3752
|
+
import { join as join12 } from "node:path";
|
|
3753
|
+
import { execSync as execSync4, spawnSync as spawnSync2 } from "node:child_process";
|
|
3471
3754
|
function assertValidBranchName2(branch, label) {
|
|
3472
3755
|
if (!/^[a-zA-Z0-9/_.-]+$/.test(branch)) {
|
|
3473
3756
|
throw new Error(
|
|
@@ -3481,15 +3764,15 @@ async function readBaseBranch(cwd) {
|
|
|
3481
3764
|
if (found) {
|
|
3482
3765
|
if (found.path.endsWith("/repo.json")) {
|
|
3483
3766
|
const dir = found.path.slice(0, found.path.lastIndexOf("/"));
|
|
3484
|
-
gitJsonPath =
|
|
3767
|
+
gitJsonPath = join12(dir, "git.json");
|
|
3485
3768
|
} else {
|
|
3486
3769
|
gitJsonPath = found.path;
|
|
3487
3770
|
}
|
|
3488
3771
|
} else {
|
|
3489
|
-
gitJsonPath =
|
|
3772
|
+
gitJsonPath = join12(cwd, ".codebyplan", "git.json");
|
|
3490
3773
|
}
|
|
3491
3774
|
try {
|
|
3492
|
-
const raw = await
|
|
3775
|
+
const raw = await readFile11(gitJsonPath, "utf-8");
|
|
3493
3776
|
const parsed = JSON.parse(raw);
|
|
3494
3777
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
3495
3778
|
return "main";
|
|
@@ -3522,7 +3805,7 @@ async function pollChecks(feat, timeoutSeconds, cwd, _sleepMs = (ms) => new Prom
|
|
|
3522
3805
|
let totalGhErrors = 0;
|
|
3523
3806
|
let iteration = 0;
|
|
3524
3807
|
while (Date.now() < deadline) {
|
|
3525
|
-
const ghResult =
|
|
3808
|
+
const ghResult = spawnSync2(
|
|
3526
3809
|
"gh",
|
|
3527
3810
|
["pr", "checks", feat, "--json", "name,state,conclusion"],
|
|
3528
3811
|
{
|
|
@@ -3688,7 +3971,7 @@ ${statusOut.trim()}`
|
|
|
3688
3971
|
} else {
|
|
3689
3972
|
createArgs.push("--body", defaultPrBody(feat, base));
|
|
3690
3973
|
}
|
|
3691
|
-
const createResult =
|
|
3974
|
+
const createResult = spawnSync2("gh", createArgs, {
|
|
3692
3975
|
cwd,
|
|
3693
3976
|
encoding: "utf-8",
|
|
3694
3977
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -3718,7 +4001,7 @@ ${statusOut.trim()}`
|
|
|
3718
4001
|
});
|
|
3719
4002
|
let mergeCommit = null;
|
|
3720
4003
|
try {
|
|
3721
|
-
const prViewResult =
|
|
4004
|
+
const prViewResult = spawnSync2(
|
|
3722
4005
|
"gh",
|
|
3723
4006
|
["pr", "view", feat, "--json", "state,mergeCommit"],
|
|
3724
4007
|
{
|
|
@@ -4021,19 +4304,19 @@ var init_resolve_worktree2 = __esm({
|
|
|
4021
4304
|
});
|
|
4022
4305
|
|
|
4023
4306
|
// src/lib/migrate-local-config.ts
|
|
4024
|
-
import { mkdir as
|
|
4025
|
-
import { join as
|
|
4307
|
+
import { mkdir as mkdir5, readFile as readFile12, unlink as unlink2, writeFile as writeFile9 } from "node:fs/promises";
|
|
4308
|
+
import { join as join13 } from "node:path";
|
|
4026
4309
|
function legacySharedPath(projectPath) {
|
|
4027
|
-
return
|
|
4310
|
+
return join13(projectPath, ".codebyplan.json");
|
|
4028
4311
|
}
|
|
4029
4312
|
function legacyLocalPath(projectPath) {
|
|
4030
|
-
return
|
|
4313
|
+
return join13(projectPath, ".codebyplan.local.json");
|
|
4031
4314
|
}
|
|
4032
4315
|
function newDirPath(projectPath) {
|
|
4033
|
-
return
|
|
4316
|
+
return join13(projectPath, ".codebyplan");
|
|
4034
4317
|
}
|
|
4035
4318
|
function sentinelPath(projectPath) {
|
|
4036
|
-
return
|
|
4319
|
+
return join13(projectPath, ".codebyplan", "repo.json");
|
|
4037
4320
|
}
|
|
4038
4321
|
async function statSafe(p) {
|
|
4039
4322
|
const { stat: stat2 } = await import("node:fs/promises");
|
|
@@ -4072,7 +4355,7 @@ async function runLocalMigration(projectPath) {
|
|
|
4072
4355
|
}
|
|
4073
4356
|
let legacyRaw;
|
|
4074
4357
|
try {
|
|
4075
|
-
legacyRaw = await
|
|
4358
|
+
legacyRaw = await readFile12(legacySharedPath(projectPath), "utf-8");
|
|
4076
4359
|
} catch {
|
|
4077
4360
|
return {
|
|
4078
4361
|
migrated: true,
|
|
@@ -4099,7 +4382,7 @@ async function runLocalMigration(projectPath) {
|
|
|
4099
4382
|
let deviceId;
|
|
4100
4383
|
let deviceWrittenByHelper = false;
|
|
4101
4384
|
try {
|
|
4102
|
-
const localRaw = await
|
|
4385
|
+
const localRaw = await readFile12(legacyLocalPath(projectPath), "utf-8");
|
|
4103
4386
|
const localParsed = JSON.parse(localRaw);
|
|
4104
4387
|
if (typeof localParsed.device_id === "string") {
|
|
4105
4388
|
deviceId = localParsed.device_id;
|
|
@@ -4107,7 +4390,7 @@ async function runLocalMigration(projectPath) {
|
|
|
4107
4390
|
} catch {
|
|
4108
4391
|
}
|
|
4109
4392
|
try {
|
|
4110
|
-
await
|
|
4393
|
+
await mkdir5(newDirPath(projectPath), { recursive: true });
|
|
4111
4394
|
} catch (err) {
|
|
4112
4395
|
const code = err.code;
|
|
4113
4396
|
if (code === "ENOTDIR" || code === "EEXIST") {
|
|
@@ -4126,8 +4409,8 @@ async function runLocalMigration(projectPath) {
|
|
|
4126
4409
|
if ("repo_id" in cfg) repoJson.repo_id = cfg.repo_id;
|
|
4127
4410
|
if ("organization_id" in cfg) repoJson.organization_id = cfg.organization_id;
|
|
4128
4411
|
if ("project_id" in cfg) repoJson.project_id = cfg.project_id;
|
|
4129
|
-
await
|
|
4130
|
-
|
|
4412
|
+
await writeFile9(
|
|
4413
|
+
join13(projectPath, ".codebyplan", "repo.json"),
|
|
4131
4414
|
JSON.stringify(repoJson, null, 2) + "\n",
|
|
4132
4415
|
"utf-8"
|
|
4133
4416
|
);
|
|
@@ -4139,8 +4422,8 @@ async function runLocalMigration(projectPath) {
|
|
|
4139
4422
|
serverJson.auto_push_enabled = cfg.auto_push_enabled;
|
|
4140
4423
|
if ("port_allocations" in cfg)
|
|
4141
4424
|
serverJson.port_allocations = cfg.port_allocations;
|
|
4142
|
-
await
|
|
4143
|
-
|
|
4425
|
+
await writeFile9(
|
|
4426
|
+
join13(projectPath, ".codebyplan", "server.json"),
|
|
4144
4427
|
JSON.stringify(serverJson, null, 2) + "\n",
|
|
4145
4428
|
"utf-8"
|
|
4146
4429
|
);
|
|
@@ -4148,30 +4431,30 @@ async function runLocalMigration(projectPath) {
|
|
|
4148
4431
|
const gitJson = {};
|
|
4149
4432
|
if ("git_branch" in cfg) gitJson.git_branch = cfg.git_branch;
|
|
4150
4433
|
if ("branch_config" in cfg) gitJson.branch_config = cfg.branch_config;
|
|
4151
|
-
await
|
|
4152
|
-
|
|
4434
|
+
await writeFile9(
|
|
4435
|
+
join13(projectPath, ".codebyplan", "git.json"),
|
|
4153
4436
|
JSON.stringify(gitJson, null, 2) + "\n",
|
|
4154
4437
|
"utf-8"
|
|
4155
4438
|
);
|
|
4156
4439
|
filesChanged.push(".codebyplan/git.json");
|
|
4157
4440
|
const shipmentJson = {};
|
|
4158
4441
|
if ("shipment" in cfg) shipmentJson.shipment = cfg.shipment;
|
|
4159
|
-
await
|
|
4160
|
-
|
|
4442
|
+
await writeFile9(
|
|
4443
|
+
join13(projectPath, ".codebyplan", "shipment.json"),
|
|
4161
4444
|
JSON.stringify(shipmentJson, null, 2) + "\n",
|
|
4162
4445
|
"utf-8"
|
|
4163
4446
|
);
|
|
4164
4447
|
filesChanged.push(".codebyplan/shipment.json");
|
|
4165
4448
|
const vendorJson = {};
|
|
4166
|
-
await
|
|
4167
|
-
|
|
4449
|
+
await writeFile9(
|
|
4450
|
+
join13(projectPath, ".codebyplan", "vendor.json"),
|
|
4168
4451
|
JSON.stringify(vendorJson, null, 2) + "\n",
|
|
4169
4452
|
"utf-8"
|
|
4170
4453
|
);
|
|
4171
4454
|
filesChanged.push(".codebyplan/vendor.json");
|
|
4172
4455
|
if (!deviceWrittenByHelper) {
|
|
4173
|
-
await
|
|
4174
|
-
|
|
4456
|
+
await writeFile9(
|
|
4457
|
+
join13(projectPath, ".codebyplan", "device.local.json"),
|
|
4175
4458
|
JSON.stringify({ device_id: deviceId }, null, 2) + "\n",
|
|
4176
4459
|
"utf-8"
|
|
4177
4460
|
);
|
|
@@ -4183,9 +4466,9 @@ async function runLocalMigration(projectPath) {
|
|
|
4183
4466
|
"Migration write incomplete: .codebyplan/repo.json was not persisted. Re-run migration to retry from a clean state."
|
|
4184
4467
|
);
|
|
4185
4468
|
}
|
|
4186
|
-
const gitignorePath =
|
|
4469
|
+
const gitignorePath = join13(projectPath, ".gitignore");
|
|
4187
4470
|
try {
|
|
4188
|
-
const gitignoreContent = await
|
|
4471
|
+
const gitignoreContent = await readFile12(gitignorePath, "utf-8");
|
|
4189
4472
|
const legacyLine = ".codebyplan.local.json";
|
|
4190
4473
|
const newLine = ".codebyplan/device.local.json";
|
|
4191
4474
|
const hasLegacy = gitignoreContent.split("\n").some((l) => l.trimEnd() === legacyLine);
|
|
@@ -4204,7 +4487,7 @@ async function runLocalMigration(projectPath) {
|
|
|
4204
4487
|
updated = gitignoreContent;
|
|
4205
4488
|
}
|
|
4206
4489
|
if (updated !== gitignoreContent) {
|
|
4207
|
-
await
|
|
4490
|
+
await writeFile9(gitignorePath, updated, "utf-8");
|
|
4208
4491
|
filesChanged.push(".gitignore");
|
|
4209
4492
|
}
|
|
4210
4493
|
} catch {
|
|
@@ -4243,8 +4526,8 @@ __export(config_exports, {
|
|
|
4243
4526
|
readVendorConfig: () => readVendorConfig,
|
|
4244
4527
|
runConfig: () => runConfig
|
|
4245
4528
|
});
|
|
4246
|
-
import { mkdir as
|
|
4247
|
-
import { join as
|
|
4529
|
+
import { mkdir as mkdir6, readFile as readFile13, writeFile as writeFile10 } from "node:fs/promises";
|
|
4530
|
+
import { join as join14 } from "node:path";
|
|
4248
4531
|
async function runConfig() {
|
|
4249
4532
|
const flags = parseFlags(3);
|
|
4250
4533
|
const dryRun = hasFlag("dry-run", 3);
|
|
@@ -4277,7 +4560,7 @@ async function runConfig() {
|
|
|
4277
4560
|
console.log("\n Config complete.\n");
|
|
4278
4561
|
}
|
|
4279
4562
|
async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
4280
|
-
const codebyplanDir =
|
|
4563
|
+
const codebyplanDir = join14(projectPath, ".codebyplan");
|
|
4281
4564
|
let resolvedWorktreeId;
|
|
4282
4565
|
try {
|
|
4283
4566
|
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
@@ -4406,7 +4689,7 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
4406
4689
|
console.log(" Config would be updated (dry-run).");
|
|
4407
4690
|
return;
|
|
4408
4691
|
}
|
|
4409
|
-
await
|
|
4692
|
+
await mkdir6(codebyplanDir, { recursive: true });
|
|
4410
4693
|
const files = [
|
|
4411
4694
|
{ name: "repo.json", payload: repoPayload },
|
|
4412
4695
|
{ name: "server.json", payload: serverPayload },
|
|
@@ -4416,15 +4699,15 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
4416
4699
|
];
|
|
4417
4700
|
let anyUpdated = false;
|
|
4418
4701
|
for (const { name, payload } of files) {
|
|
4419
|
-
const filePath =
|
|
4702
|
+
const filePath = join14(codebyplanDir, name);
|
|
4420
4703
|
const newJson = JSON.stringify(payload, null, 2) + "\n";
|
|
4421
4704
|
let currentJson = "";
|
|
4422
4705
|
try {
|
|
4423
|
-
currentJson = await
|
|
4706
|
+
currentJson = await readFile13(filePath, "utf-8");
|
|
4424
4707
|
} catch {
|
|
4425
4708
|
}
|
|
4426
4709
|
if (currentJson === newJson) continue;
|
|
4427
|
-
await
|
|
4710
|
+
await writeFile10(filePath, newJson, "utf-8");
|
|
4428
4711
|
console.log(` Updated .codebyplan/${name}`);
|
|
4429
4712
|
anyUpdated = true;
|
|
4430
4713
|
}
|
|
@@ -4434,8 +4717,8 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
4434
4717
|
}
|
|
4435
4718
|
async function readRepoConfig(projectPath) {
|
|
4436
4719
|
try {
|
|
4437
|
-
const raw = await
|
|
4438
|
-
|
|
4720
|
+
const raw = await readFile13(
|
|
4721
|
+
join14(projectPath, ".codebyplan", "repo.json"),
|
|
4439
4722
|
"utf-8"
|
|
4440
4723
|
);
|
|
4441
4724
|
return JSON.parse(raw);
|
|
@@ -4445,8 +4728,8 @@ async function readRepoConfig(projectPath) {
|
|
|
4445
4728
|
}
|
|
4446
4729
|
async function readServerConfig(projectPath) {
|
|
4447
4730
|
try {
|
|
4448
|
-
const raw = await
|
|
4449
|
-
|
|
4731
|
+
const raw = await readFile13(
|
|
4732
|
+
join14(projectPath, ".codebyplan", "server.json"),
|
|
4450
4733
|
"utf-8"
|
|
4451
4734
|
);
|
|
4452
4735
|
return JSON.parse(raw);
|
|
@@ -4456,8 +4739,8 @@ async function readServerConfig(projectPath) {
|
|
|
4456
4739
|
}
|
|
4457
4740
|
async function readGitConfig(projectPath) {
|
|
4458
4741
|
try {
|
|
4459
|
-
const raw = await
|
|
4460
|
-
|
|
4742
|
+
const raw = await readFile13(
|
|
4743
|
+
join14(projectPath, ".codebyplan", "git.json"),
|
|
4461
4744
|
"utf-8"
|
|
4462
4745
|
);
|
|
4463
4746
|
return JSON.parse(raw);
|
|
@@ -4467,8 +4750,8 @@ async function readGitConfig(projectPath) {
|
|
|
4467
4750
|
}
|
|
4468
4751
|
async function readShipmentConfig(projectPath) {
|
|
4469
4752
|
try {
|
|
4470
|
-
const raw = await
|
|
4471
|
-
|
|
4753
|
+
const raw = await readFile13(
|
|
4754
|
+
join14(projectPath, ".codebyplan", "shipment.json"),
|
|
4472
4755
|
"utf-8"
|
|
4473
4756
|
);
|
|
4474
4757
|
return JSON.parse(raw);
|
|
@@ -4478,8 +4761,8 @@ async function readShipmentConfig(projectPath) {
|
|
|
4478
4761
|
}
|
|
4479
4762
|
async function readVendorConfig(projectPath) {
|
|
4480
4763
|
try {
|
|
4481
|
-
const raw = await
|
|
4482
|
-
|
|
4764
|
+
const raw = await readFile13(
|
|
4765
|
+
join14(projectPath, ".codebyplan", "vendor.json"),
|
|
4483
4766
|
"utf-8"
|
|
4484
4767
|
);
|
|
4485
4768
|
return JSON.parse(raw);
|
|
@@ -4535,14 +4818,14 @@ var init_server_detect = __esm({
|
|
|
4535
4818
|
});
|
|
4536
4819
|
|
|
4537
4820
|
// src/lib/port-verify.ts
|
|
4538
|
-
import { readFile as
|
|
4821
|
+
import { readFile as readFile14 } from "node:fs/promises";
|
|
4539
4822
|
async function verifyPorts(projectPath, portAllocations) {
|
|
4540
4823
|
const mismatches = [];
|
|
4541
4824
|
const allocatedPorts = new Set(portAllocations.map((a) => a.port));
|
|
4542
4825
|
const packageJsonPaths = await findPackageJsonFiles(projectPath, projectPath);
|
|
4543
4826
|
for (const pkgPath of packageJsonPaths) {
|
|
4544
4827
|
try {
|
|
4545
|
-
const raw = await
|
|
4828
|
+
const raw = await readFile14(pkgPath, "utf-8");
|
|
4546
4829
|
const pkg = JSON.parse(raw);
|
|
4547
4830
|
const scriptPort = detectPortFromScripts(pkg);
|
|
4548
4831
|
if (scriptPort !== null && !allocatedPorts.has(scriptPort)) {
|
|
@@ -4605,7 +4888,7 @@ async function findUnallocatedApps(projectPath, portAllocations) {
|
|
|
4605
4888
|
}
|
|
4606
4889
|
let pkg;
|
|
4607
4890
|
try {
|
|
4608
|
-
const raw = await
|
|
4891
|
+
const raw = await readFile14(`${app.absPath}/package.json`, "utf-8");
|
|
4609
4892
|
pkg = JSON.parse(raw);
|
|
4610
4893
|
} catch {
|
|
4611
4894
|
continue;
|
|
@@ -5407,6 +5690,11 @@ async function runInstall(opts, deps = {}) {
|
|
|
5407
5690
|
await Promise.resolve();
|
|
5408
5691
|
const scope = opts.scope ?? "project";
|
|
5409
5692
|
if (scope === "user") {
|
|
5693
|
+
if (opts.renderer) {
|
|
5694
|
+
console.warn(
|
|
5695
|
+
"codebyplan claude install: --bash/--node/--python is ignored for --scope user (no project root for statusline.local.json)."
|
|
5696
|
+
);
|
|
5697
|
+
}
|
|
5410
5698
|
runInstallUser(opts, deps);
|
|
5411
5699
|
return;
|
|
5412
5700
|
}
|
|
@@ -5483,6 +5771,9 @@ async function runInstall(opts, deps = {}) {
|
|
|
5483
5771
|
console.log(
|
|
5484
5772
|
`codebyplan claude install${opts.dryRun ? " (dry-run)" : ""}: ${manifestEntries.length} files, ${countHookEntries(templatesDir)} hook entries.`
|
|
5485
5773
|
);
|
|
5774
|
+
if (opts.renderer && !opts.dryRun) {
|
|
5775
|
+
await writeStatuslineLocalConfig(projectDir, { renderer: opts.renderer });
|
|
5776
|
+
}
|
|
5486
5777
|
} catch (err) {
|
|
5487
5778
|
console.error(
|
|
5488
5779
|
`codebyplan claude install failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -5568,6 +5859,7 @@ var init_install = __esm({
|
|
|
5568
5859
|
init_template_walker();
|
|
5569
5860
|
init_manifest();
|
|
5570
5861
|
init_settings_merge();
|
|
5862
|
+
init_statusline_config();
|
|
5571
5863
|
}
|
|
5572
5864
|
});
|
|
5573
5865
|
|
|
@@ -5695,6 +5987,11 @@ async function runUpdate(opts, deps = {}) {
|
|
|
5695
5987
|
await Promise.resolve();
|
|
5696
5988
|
const scope = opts.scope ?? "project";
|
|
5697
5989
|
if (scope === "user") {
|
|
5990
|
+
if (opts.renderer) {
|
|
5991
|
+
console.warn(
|
|
5992
|
+
"codebyplan claude update: --bash/--node/--python is ignored for --scope user (no project root for statusline.local.json)."
|
|
5993
|
+
);
|
|
5994
|
+
}
|
|
5698
5995
|
runUpdateUser(opts, deps);
|
|
5699
5996
|
return;
|
|
5700
5997
|
}
|
|
@@ -5839,6 +6136,9 @@ async function runUpdate(opts, deps = {}) {
|
|
|
5839
6136
|
console.log(
|
|
5840
6137
|
`codebyplan claude update${opts.dryRun ? " (dry-run)" : ""}: ${finalManifestEntries.length} files tracked.`
|
|
5841
6138
|
);
|
|
6139
|
+
if (opts.renderer && !opts.dryRun) {
|
|
6140
|
+
await writeStatuslineLocalConfig(projectDir, { renderer: opts.renderer });
|
|
6141
|
+
}
|
|
5842
6142
|
} catch (err) {
|
|
5843
6143
|
console.error(
|
|
5844
6144
|
`codebyplan claude update failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -5985,6 +6285,7 @@ var init_update = __esm({
|
|
|
5985
6285
|
init_manifest();
|
|
5986
6286
|
init_settings_merge();
|
|
5987
6287
|
init_prompt();
|
|
6288
|
+
init_statusline_config();
|
|
5988
6289
|
}
|
|
5989
6290
|
});
|
|
5990
6291
|
|
|
@@ -6222,6 +6523,12 @@ void (async () => {
|
|
|
6222
6523
|
await runSetup2();
|
|
6223
6524
|
process.exit(0);
|
|
6224
6525
|
}
|
|
6526
|
+
if (arg === "statusline") {
|
|
6527
|
+
const { runStatusline: runStatusline2 } = await Promise.resolve().then(() => (init_statusline(), statusline_exports));
|
|
6528
|
+
const rest = process.argv.slice(3);
|
|
6529
|
+
await runStatusline2(rest);
|
|
6530
|
+
process.exit(process.exitCode ?? 0);
|
|
6531
|
+
}
|
|
6225
6532
|
if (arg === "login") {
|
|
6226
6533
|
const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
6227
6534
|
try {
|
|
@@ -6357,6 +6664,9 @@ void (async () => {
|
|
|
6357
6664
|
user \u2014 writes only owned base settings into ~/.claude/settings.json;
|
|
6358
6665
|
never copies templates or hooks
|
|
6359
6666
|
--project-dir <path> Override the project root (default: cwd). Cannot be combined with --scope user.
|
|
6667
|
+
--bash Set statusline renderer to bash after install/update
|
|
6668
|
+
--node Set statusline renderer to node after install/update
|
|
6669
|
+
--python Set statusline renderer to python after install/update
|
|
6360
6670
|
`);
|
|
6361
6671
|
process.exit(0);
|
|
6362
6672
|
}
|
|
@@ -6379,6 +6689,7 @@ void (async () => {
|
|
|
6379
6689
|
codebyplan ship Ship current feat branch to production via PR
|
|
6380
6690
|
codebyplan branch migrate Rewrite branch_config from 3-branch to 2-tier model
|
|
6381
6691
|
codebyplan claude Claude asset management (install/update/uninstall)
|
|
6692
|
+
codebyplan statusline Show or set the statusline renderer (bash/node/python)
|
|
6382
6693
|
codebyplan resolve-worktree Resolve active worktree UUID from device+path+branch tuple
|
|
6383
6694
|
codebyplan help Show this help message
|
|
6384
6695
|
codebyplan --version Print version
|
|
@@ -6413,6 +6724,9 @@ void (async () => {
|
|
|
6413
6724
|
--verbose Log every file operation
|
|
6414
6725
|
--scope <user|project> Target scope (default: project)
|
|
6415
6726
|
--project-dir <path> Override the project root (default: cwd)
|
|
6727
|
+
--bash Set statusline renderer to bash after install/update
|
|
6728
|
+
--node Set statusline renderer to node after install/update
|
|
6729
|
+
--python Set statusline renderer to python after install/update
|
|
6416
6730
|
|
|
6417
6731
|
MCP Server:
|
|
6418
6732
|
Claude Code connects to CodeByPlan via remote MCP:
|
|
@@ -6463,8 +6777,19 @@ function parseClaudeFlags(rest) {
|
|
|
6463
6777
|
);
|
|
6464
6778
|
return null;
|
|
6465
6779
|
}
|
|
6780
|
+
const hasBash = rest.includes("--bash");
|
|
6781
|
+
const hasNode = rest.includes("--node");
|
|
6782
|
+
const hasPython = rest.includes("--python");
|
|
6783
|
+
const rendererCount = [hasBash, hasNode, hasPython].filter(Boolean).length;
|
|
6784
|
+
if (rendererCount > 1) {
|
|
6785
|
+
process.stderr.write(
|
|
6786
|
+
"error: --bash, --node, and --python are mutually exclusive; pass only one.\n"
|
|
6787
|
+
);
|
|
6788
|
+
return null;
|
|
6789
|
+
}
|
|
6790
|
+
const renderer = hasBash ? "bash" : hasNode ? "node" : hasPython ? "python" : void 0;
|
|
6466
6791
|
return {
|
|
6467
|
-
opts: { yes, dryRun, verbose, scope },
|
|
6792
|
+
opts: { yes, dryRun, verbose, scope, renderer },
|
|
6468
6793
|
projectDir
|
|
6469
6794
|
};
|
|
6470
6795
|
}
|