codebyplan 1.4.3 → 1.5.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 +431 -155
- package/package.json +2 -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.
|
|
17
|
+
VERSION = "1.5.0";
|
|
18
18
|
PACKAGE_NAME = "codebyplan";
|
|
19
19
|
}
|
|
20
20
|
});
|
|
@@ -198,6 +198,25 @@ async function resolveAndCacheWorktreeId(repoId, projectPath, options) {
|
|
|
198
198
|
}
|
|
199
199
|
return worktreeId;
|
|
200
200
|
}
|
|
201
|
+
async function resolveWorktreeId({
|
|
202
|
+
repoId,
|
|
203
|
+
repoPath,
|
|
204
|
+
branch,
|
|
205
|
+
deviceId
|
|
206
|
+
}) {
|
|
207
|
+
try {
|
|
208
|
+
const res = await apiPost(
|
|
209
|
+
"/worktrees/resolve",
|
|
210
|
+
{ repo_id: repoId, device_id: deviceId, repo_path: repoPath, branch }
|
|
211
|
+
);
|
|
212
|
+
return res.worktree_id ?? null;
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.error(
|
|
215
|
+
`Tuple worktree resolve failed: ${err instanceof Error ? err.message : String(err)}`
|
|
216
|
+
);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
201
220
|
var init_resolve_worktree = __esm({
|
|
202
221
|
"src/lib/resolve-worktree.ts"() {
|
|
203
222
|
"use strict";
|
|
@@ -205,6 +224,78 @@ var init_resolve_worktree = __esm({
|
|
|
205
224
|
}
|
|
206
225
|
});
|
|
207
226
|
|
|
227
|
+
// src/lib/local-config.ts
|
|
228
|
+
import { execSync } from "node:child_process";
|
|
229
|
+
import { createHash } from "node:crypto";
|
|
230
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
231
|
+
import { hostname } from "node:os";
|
|
232
|
+
import { join as join2 } from "node:path";
|
|
233
|
+
function localConfigPath(projectPath) {
|
|
234
|
+
return join2(projectPath, ".codebyplan.local.json");
|
|
235
|
+
}
|
|
236
|
+
async function readLocalConfig(projectPath) {
|
|
237
|
+
try {
|
|
238
|
+
const raw = await readFile2(localConfigPath(projectPath), "utf-8");
|
|
239
|
+
const parsed = JSON.parse(raw);
|
|
240
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && typeof parsed.device_id === "string") {
|
|
241
|
+
return parsed;
|
|
242
|
+
}
|
|
243
|
+
console.error("Failed to read local config: invalid shape");
|
|
244
|
+
return null;
|
|
245
|
+
} catch (err) {
|
|
246
|
+
console.error(
|
|
247
|
+
`Failed to read local config: ${err instanceof Error ? err.message : String(err)}`
|
|
248
|
+
);
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async function writeLocalConfig(projectPath, config) {
|
|
253
|
+
const content = { device_id: config.device_id };
|
|
254
|
+
try {
|
|
255
|
+
await writeFile2(
|
|
256
|
+
localConfigPath(projectPath),
|
|
257
|
+
JSON.stringify(content, null, 2) + "\n",
|
|
258
|
+
"utf-8"
|
|
259
|
+
);
|
|
260
|
+
} catch (err) {
|
|
261
|
+
console.error(
|
|
262
|
+
`Failed to write local config: ${err instanceof Error ? err.message : String(err)}`
|
|
263
|
+
);
|
|
264
|
+
throw err;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function resolveMachineSeed() {
|
|
268
|
+
try {
|
|
269
|
+
const raw = await readFile2("/etc/machine-id", "utf-8");
|
|
270
|
+
const trimmed = raw.trim();
|
|
271
|
+
if (trimmed) return trimmed;
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
if (process.platform === "darwin") {
|
|
275
|
+
try {
|
|
276
|
+
const out = execSync("sysctl -n kern.uuid", { encoding: "utf-8" }).trim();
|
|
277
|
+
if (out) return out;
|
|
278
|
+
} catch {
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return hostname();
|
|
282
|
+
}
|
|
283
|
+
async function getOrCreateDeviceId(projectPath) {
|
|
284
|
+
const existing = await readLocalConfig(projectPath);
|
|
285
|
+
if (existing?.device_id) {
|
|
286
|
+
return existing.device_id;
|
|
287
|
+
}
|
|
288
|
+
const seed = await resolveMachineSeed();
|
|
289
|
+
const deviceId = createHash("sha256").update(seed).digest("hex").slice(0, 16);
|
|
290
|
+
await writeLocalConfig(projectPath, { device_id: deviceId });
|
|
291
|
+
return deviceId;
|
|
292
|
+
}
|
|
293
|
+
var init_local_config = __esm({
|
|
294
|
+
"src/lib/local-config.ts"() {
|
|
295
|
+
"use strict";
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
208
299
|
// src/lib/settings-merge.ts
|
|
209
300
|
function mergeSettings(template, local) {
|
|
210
301
|
const merged = { ...local };
|
|
@@ -270,8 +361,8 @@ var init_settings_merge = __esm({
|
|
|
270
361
|
});
|
|
271
362
|
|
|
272
363
|
// src/lib/hook-registry.ts
|
|
273
|
-
import { readdir, readFile as
|
|
274
|
-
import { join as
|
|
364
|
+
import { readdir, readFile as readFile3 } from "node:fs/promises";
|
|
365
|
+
import { join as join3 } from "node:path";
|
|
275
366
|
function parseHookMeta(content) {
|
|
276
367
|
const lineMatch = content.match(/^#\s*@hook:(.*)$/m);
|
|
277
368
|
if (!lineMatch) return null;
|
|
@@ -293,7 +384,7 @@ async function discoverHooks(hooksDir) {
|
|
|
293
384
|
return discovered;
|
|
294
385
|
}
|
|
295
386
|
for (const filename of filenames) {
|
|
296
|
-
const content = await
|
|
387
|
+
const content = await readFile3(join3(hooksDir, filename), "utf-8");
|
|
297
388
|
const meta = parseHookMeta(content);
|
|
298
389
|
if (meta) {
|
|
299
390
|
discovered.set(filename.replace(/\.sh$/, ""), meta);
|
|
@@ -440,45 +531,45 @@ __export(sync_engine_exports, {
|
|
|
440
531
|
});
|
|
441
532
|
import {
|
|
442
533
|
readdir as readdir2,
|
|
443
|
-
readFile as
|
|
444
|
-
writeFile as
|
|
534
|
+
readFile as readFile4,
|
|
535
|
+
writeFile as writeFile3,
|
|
445
536
|
unlink,
|
|
446
537
|
mkdir,
|
|
447
538
|
rmdir,
|
|
448
539
|
chmod,
|
|
449
540
|
stat
|
|
450
541
|
} from "node:fs/promises";
|
|
451
|
-
import { join as
|
|
542
|
+
import { join as join4, dirname } from "node:path";
|
|
452
543
|
function getTypeDir(claudeDir, dir) {
|
|
453
|
-
if (dir === "commands") return
|
|
454
|
-
return
|
|
544
|
+
if (dir === "commands") return join4(claudeDir, dir, "cbp");
|
|
545
|
+
return join4(claudeDir, dir);
|
|
455
546
|
}
|
|
456
547
|
function getFilePath(claudeDir, typeName, file) {
|
|
457
548
|
const cfg = typeConfig[typeName];
|
|
458
549
|
const typeDir = getTypeDir(claudeDir, cfg.dir);
|
|
459
550
|
if (cfg.subfolder) {
|
|
460
|
-
return
|
|
551
|
+
return join4(typeDir, file.name, `${cfg.subfolder}${cfg.ext}`);
|
|
461
552
|
}
|
|
462
553
|
if (typeName === "command" && file.category) {
|
|
463
|
-
return
|
|
554
|
+
return join4(typeDir, file.category, `${file.name}${cfg.ext}`);
|
|
464
555
|
}
|
|
465
556
|
if (typeName === "template") {
|
|
466
|
-
return
|
|
557
|
+
return join4(typeDir, file.name);
|
|
467
558
|
}
|
|
468
|
-
return
|
|
559
|
+
return join4(typeDir, `${file.name}${cfg.ext}`);
|
|
469
560
|
}
|
|
470
561
|
async function readDirRecursive(dir, base = dir) {
|
|
471
562
|
const result = /* @__PURE__ */ new Map();
|
|
472
563
|
try {
|
|
473
564
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
474
565
|
for (const entry of entries) {
|
|
475
|
-
const fullPath =
|
|
566
|
+
const fullPath = join4(dir, entry.name);
|
|
476
567
|
if (entry.isDirectory()) {
|
|
477
568
|
const sub = await readDirRecursive(fullPath, base);
|
|
478
569
|
for (const [k, v] of sub) result.set(k, v);
|
|
479
570
|
} else {
|
|
480
571
|
const relPath = fullPath.slice(base.length + 1);
|
|
481
|
-
const fileContent = await
|
|
572
|
+
const fileContent = await readFile4(fullPath, "utf-8");
|
|
482
573
|
result.set(relPath, fileContent);
|
|
483
574
|
}
|
|
484
575
|
}
|
|
@@ -488,7 +579,7 @@ async function readDirRecursive(dir, base = dir) {
|
|
|
488
579
|
}
|
|
489
580
|
async function isGitWorktree(projectPath) {
|
|
490
581
|
try {
|
|
491
|
-
const gitPath =
|
|
582
|
+
const gitPath = join4(projectPath, ".git");
|
|
492
583
|
const info = await stat(gitPath);
|
|
493
584
|
return info.isFile();
|
|
494
585
|
} catch {
|
|
@@ -515,7 +606,7 @@ async function executeSyncToLocal(options) {
|
|
|
515
606
|
const syncData = syncRes.data;
|
|
516
607
|
const repoData = repoRes.data;
|
|
517
608
|
syncData.claude_md = [];
|
|
518
|
-
const claudeDir =
|
|
609
|
+
const claudeDir = join4(projectPath, ".claude");
|
|
519
610
|
const worktree = await isGitWorktree(projectPath);
|
|
520
611
|
const byType = {};
|
|
521
612
|
const totals = { created: 0, updated: 0, deleted: 0, unchanged: 0 };
|
|
@@ -551,7 +642,7 @@ async function executeSyncToLocal(options) {
|
|
|
551
642
|
remotePathMap.set(relPath, { content: substituted, name: remote.name });
|
|
552
643
|
}
|
|
553
644
|
for (const [relPath, { content, name }] of remotePathMap) {
|
|
554
|
-
const fullPath =
|
|
645
|
+
const fullPath = join4(targetDir, relPath);
|
|
555
646
|
const localContent = localFiles.get(relPath);
|
|
556
647
|
if (localContent === void 0) {
|
|
557
648
|
const remoteFile = remoteFiles.find((f) => f.name === name);
|
|
@@ -563,14 +654,14 @@ async function executeSyncToLocal(options) {
|
|
|
563
654
|
});
|
|
564
655
|
if (!dryRun) {
|
|
565
656
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
566
|
-
await
|
|
657
|
+
await writeFile3(fullPath, content, "utf-8");
|
|
567
658
|
if (typeName === "hook") await chmod(fullPath, 493);
|
|
568
659
|
}
|
|
569
660
|
result.created.push(name);
|
|
570
661
|
totals.created++;
|
|
571
662
|
} else if (localContent !== content) {
|
|
572
663
|
if (!dryRun) {
|
|
573
|
-
await
|
|
664
|
+
await writeFile3(fullPath, content, "utf-8");
|
|
574
665
|
if (typeName === "hook") await chmod(fullPath, 493);
|
|
575
666
|
}
|
|
576
667
|
result.updated.push(name);
|
|
@@ -582,7 +673,7 @@ async function executeSyncToLocal(options) {
|
|
|
582
673
|
}
|
|
583
674
|
for (const [relPath] of localFiles) {
|
|
584
675
|
if (!remotePathMap.has(relPath)) {
|
|
585
|
-
const fullPath =
|
|
676
|
+
const fullPath = join4(targetDir, relPath);
|
|
586
677
|
if (!dryRun) {
|
|
587
678
|
await unlink(fullPath);
|
|
588
679
|
await removeEmptyParents(fullPath, targetDir);
|
|
@@ -597,7 +688,7 @@ async function executeSyncToLocal(options) {
|
|
|
597
688
|
{
|
|
598
689
|
const typeName = "docs_stack";
|
|
599
690
|
const syncKey = "docs_stack";
|
|
600
|
-
const targetDir =
|
|
691
|
+
const targetDir = join4(projectPath, "docs", "stack");
|
|
601
692
|
const remoteFiles = syncData[syncKey] ?? [];
|
|
602
693
|
const result = {
|
|
603
694
|
created: [],
|
|
@@ -611,7 +702,7 @@ async function executeSyncToLocal(options) {
|
|
|
611
702
|
const localFiles = await readDirRecursive(targetDir);
|
|
612
703
|
const remotePathMap = /* @__PURE__ */ new Map();
|
|
613
704
|
for (const remote of remoteFiles) {
|
|
614
|
-
const relPath = remote.category ?
|
|
705
|
+
const relPath = remote.category ? join4(remote.category, remote.name) : remote.name;
|
|
615
706
|
const substituted = substituteVariables(remote.content, repoData);
|
|
616
707
|
remotePathMap.set(relPath, {
|
|
617
708
|
content: substituted,
|
|
@@ -619,18 +710,18 @@ async function executeSyncToLocal(options) {
|
|
|
619
710
|
});
|
|
620
711
|
}
|
|
621
712
|
for (const [relPath, { content, name }] of remotePathMap) {
|
|
622
|
-
const fullPath =
|
|
713
|
+
const fullPath = join4(targetDir, relPath);
|
|
623
714
|
const localContent = localFiles.get(relPath);
|
|
624
715
|
if (localContent === void 0) {
|
|
625
716
|
if (!dryRun) {
|
|
626
717
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
627
|
-
await
|
|
718
|
+
await writeFile3(fullPath, content, "utf-8");
|
|
628
719
|
}
|
|
629
720
|
result.created.push(name);
|
|
630
721
|
totals.created++;
|
|
631
722
|
} else if (localContent !== content) {
|
|
632
723
|
if (!dryRun) {
|
|
633
|
-
await
|
|
724
|
+
await writeFile3(fullPath, content, "utf-8");
|
|
634
725
|
}
|
|
635
726
|
result.updated.push(name);
|
|
636
727
|
totals.updated++;
|
|
@@ -641,7 +732,7 @@ async function executeSyncToLocal(options) {
|
|
|
641
732
|
}
|
|
642
733
|
for (const [relPath] of localFiles) {
|
|
643
734
|
if (!remotePathMap.has(relPath)) {
|
|
644
|
-
const fullPath =
|
|
735
|
+
const fullPath = join4(targetDir, relPath);
|
|
645
736
|
if (!dryRun) {
|
|
646
737
|
await unlink(fullPath);
|
|
647
738
|
await removeEmptyParents(fullPath, targetDir);
|
|
@@ -661,8 +752,8 @@ async function executeSyncToLocal(options) {
|
|
|
661
752
|
globalSettings = { ...globalSettings, ...parsed };
|
|
662
753
|
}
|
|
663
754
|
const specialTypes = {
|
|
664
|
-
claude_md: () =>
|
|
665
|
-
settings: () =>
|
|
755
|
+
claude_md: () => join4(projectPath, "CLAUDE.md"),
|
|
756
|
+
settings: () => join4(projectPath, ".claude", "settings.json")
|
|
666
757
|
};
|
|
667
758
|
for (const [typeName, getPath] of Object.entries(specialTypes)) {
|
|
668
759
|
const remoteFiles = syncData[typeName] ?? [];
|
|
@@ -677,7 +768,7 @@ async function executeSyncToLocal(options) {
|
|
|
677
768
|
const remoteContent = substituteVariables(remote.content, repoData);
|
|
678
769
|
let localContent;
|
|
679
770
|
try {
|
|
680
|
-
localContent = await
|
|
771
|
+
localContent = await readFile4(targetPath, "utf-8");
|
|
681
772
|
} catch {
|
|
682
773
|
}
|
|
683
774
|
if (typeName === "settings") {
|
|
@@ -686,7 +777,7 @@ async function executeSyncToLocal(options) {
|
|
|
686
777
|
globalSettings,
|
|
687
778
|
repoSettings
|
|
688
779
|
);
|
|
689
|
-
const hooksDir =
|
|
780
|
+
const hooksDir = join4(projectPath, ".claude", "hooks");
|
|
690
781
|
const discovered = await discoverHooks(hooksDir);
|
|
691
782
|
if (localContent === void 0) {
|
|
692
783
|
const finalSettings = stripPermissionsAllow(combinedTemplate);
|
|
@@ -698,7 +789,7 @@ async function executeSyncToLocal(options) {
|
|
|
698
789
|
}
|
|
699
790
|
if (!dryRun) {
|
|
700
791
|
await mkdir(dirname(targetPath), { recursive: true });
|
|
701
|
-
await
|
|
792
|
+
await writeFile3(
|
|
702
793
|
targetPath,
|
|
703
794
|
JSON.stringify(finalSettings, null, 2) + "\n",
|
|
704
795
|
"utf-8"
|
|
@@ -719,7 +810,7 @@ async function executeSyncToLocal(options) {
|
|
|
719
810
|
const mergedContent = JSON.stringify(merged, null, 2) + "\n";
|
|
720
811
|
if (localContent !== mergedContent) {
|
|
721
812
|
if (!dryRun) {
|
|
722
|
-
await
|
|
813
|
+
await writeFile3(targetPath, mergedContent, "utf-8");
|
|
723
814
|
}
|
|
724
815
|
result.updated.push(remote.name);
|
|
725
816
|
totals.updated++;
|
|
@@ -732,13 +823,13 @@ async function executeSyncToLocal(options) {
|
|
|
732
823
|
if (localContent === void 0) {
|
|
733
824
|
if (!dryRun) {
|
|
734
825
|
await mkdir(dirname(targetPath), { recursive: true });
|
|
735
|
-
await
|
|
826
|
+
await writeFile3(targetPath, remoteContent, "utf-8");
|
|
736
827
|
}
|
|
737
828
|
result.created.push(remote.name);
|
|
738
829
|
totals.created++;
|
|
739
830
|
} else if (localContent !== remoteContent) {
|
|
740
831
|
if (!dryRun) {
|
|
741
|
-
await
|
|
832
|
+
await writeFile3(targetPath, remoteContent, "utf-8");
|
|
742
833
|
}
|
|
743
834
|
result.updated.push(remote.name);
|
|
744
835
|
totals.updated++;
|
|
@@ -839,15 +930,15 @@ __export(setup_exports, {
|
|
|
839
930
|
});
|
|
840
931
|
import { createInterface } from "node:readline/promises";
|
|
841
932
|
import { stdin, stdout } from "node:process";
|
|
842
|
-
import { readFile as
|
|
933
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
|
|
843
934
|
import { homedir } from "node:os";
|
|
844
|
-
import { join as
|
|
935
|
+
import { join as join5 } from "node:path";
|
|
845
936
|
function getConfigPath(scope) {
|
|
846
|
-
return scope === "user" ?
|
|
937
|
+
return scope === "user" ? join5(homedir(), ".claude.json") : join5(process.cwd(), ".mcp.json");
|
|
847
938
|
}
|
|
848
939
|
async function readConfig(path) {
|
|
849
940
|
try {
|
|
850
|
-
const raw = await
|
|
941
|
+
const raw = await readFile5(path, "utf-8");
|
|
851
942
|
const parsed = JSON.parse(raw);
|
|
852
943
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
853
944
|
return parsed;
|
|
@@ -871,7 +962,7 @@ async function writeMcpConfig(scope, apiKey) {
|
|
|
871
962
|
config.mcpServers = {};
|
|
872
963
|
}
|
|
873
964
|
config.mcpServers.codebyplan = buildMcpEntry(apiKey);
|
|
874
|
-
await
|
|
965
|
+
await writeFile4(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
875
966
|
return configPath;
|
|
876
967
|
}
|
|
877
968
|
async function verifyMcpConfig(scope, apiKey) {
|
|
@@ -976,17 +1067,34 @@ async function runSetup() {
|
|
|
976
1067
|
Selected: ${selectedRepo.name}
|
|
977
1068
|
`);
|
|
978
1069
|
const projectPath = process.cwd();
|
|
979
|
-
const
|
|
1070
|
+
const pathBasedId = await resolveAndCacheWorktreeId(
|
|
980
1071
|
selectedRepo.id,
|
|
981
1072
|
projectPath,
|
|
982
1073
|
{ skipWrite: true }
|
|
983
1074
|
);
|
|
984
|
-
const
|
|
1075
|
+
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
1076
|
+
let branch = "main";
|
|
1077
|
+
try {
|
|
1078
|
+
const { execSync: execSync3 } = await import("node:child_process");
|
|
1079
|
+
branch = execSync3("git symbolic-ref --short HEAD", {
|
|
1080
|
+
cwd: projectPath,
|
|
1081
|
+
encoding: "utf-8"
|
|
1082
|
+
}).trim();
|
|
1083
|
+
} catch {
|
|
1084
|
+
}
|
|
1085
|
+
const tupleId = await resolveWorktreeId({
|
|
1086
|
+
repoId: selectedRepo.id,
|
|
1087
|
+
repoPath: projectPath,
|
|
1088
|
+
branch,
|
|
1089
|
+
deviceId
|
|
1090
|
+
});
|
|
1091
|
+
const worktreeId = tupleId ?? pathBasedId;
|
|
1092
|
+
const codebyplanPath = join5(projectPath, ".codebyplan.json");
|
|
985
1093
|
const codebyplanConfig = {
|
|
986
1094
|
repo_id: selectedRepo.id
|
|
987
1095
|
};
|
|
988
1096
|
if (worktreeId) codebyplanConfig.worktree_id = worktreeId;
|
|
989
|
-
await
|
|
1097
|
+
await writeFile4(
|
|
990
1098
|
codebyplanPath,
|
|
991
1099
|
JSON.stringify(codebyplanConfig, null, 2) + "\n",
|
|
992
1100
|
"utf-8"
|
|
@@ -1032,18 +1140,19 @@ var init_setup = __esm({
|
|
|
1032
1140
|
"src/cli/setup.ts"() {
|
|
1033
1141
|
"use strict";
|
|
1034
1142
|
init_resolve_worktree();
|
|
1143
|
+
init_local_config();
|
|
1035
1144
|
}
|
|
1036
1145
|
});
|
|
1037
1146
|
|
|
1038
1147
|
// src/cli/config.ts
|
|
1039
|
-
import { readFile as
|
|
1040
|
-
import { join as
|
|
1148
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
1149
|
+
import { join as join6, resolve } from "node:path";
|
|
1041
1150
|
async function findCodebyplanConfig(startDir, maxDepth = 20) {
|
|
1042
1151
|
let cursor = resolve(startDir);
|
|
1043
1152
|
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1044
|
-
const configPath =
|
|
1153
|
+
const configPath = join6(cursor, ".codebyplan.json");
|
|
1045
1154
|
try {
|
|
1046
|
-
const raw = await
|
|
1155
|
+
const raw = await readFile6(configPath, "utf-8");
|
|
1047
1156
|
const parsed = JSON.parse(raw);
|
|
1048
1157
|
return { path: configPath, contents: parsed };
|
|
1049
1158
|
} catch {
|
|
@@ -1094,8 +1203,8 @@ var init_config = __esm({
|
|
|
1094
1203
|
});
|
|
1095
1204
|
|
|
1096
1205
|
// src/cli/fileMapper.ts
|
|
1097
|
-
import { readdir as readdir3, readFile as
|
|
1098
|
-
import { join as
|
|
1206
|
+
import { readdir as readdir3, readFile as readFile7 } from "node:fs/promises";
|
|
1207
|
+
import { join as join7, extname } from "node:path";
|
|
1099
1208
|
function extractScope(content, type) {
|
|
1100
1209
|
if (type === "hook") {
|
|
1101
1210
|
const match = content.match(/^#\s*@scope:\s*(\S+)/m);
|
|
@@ -1125,29 +1234,29 @@ function compositeKey(type, name, category) {
|
|
|
1125
1234
|
}
|
|
1126
1235
|
async function scanLocalFiles(claudeDir, projectPath) {
|
|
1127
1236
|
const result = /* @__PURE__ */ new Map();
|
|
1128
|
-
await scanCommands(
|
|
1237
|
+
await scanCommands(join7(claudeDir, "commands", "cbp"), result);
|
|
1129
1238
|
await scanSubfolderType(
|
|
1130
|
-
|
|
1239
|
+
join7(claudeDir, "agents"),
|
|
1131
1240
|
"agent",
|
|
1132
1241
|
"AGENT.md",
|
|
1133
1242
|
result
|
|
1134
1243
|
);
|
|
1135
1244
|
await scanSubfolderType(
|
|
1136
|
-
|
|
1245
|
+
join7(claudeDir, "skills"),
|
|
1137
1246
|
"skill",
|
|
1138
1247
|
"SKILL.md",
|
|
1139
1248
|
result
|
|
1140
1249
|
);
|
|
1141
|
-
await scanFlatType(
|
|
1142
|
-
await scanFlatType(
|
|
1143
|
-
await scanTemplates(
|
|
1250
|
+
await scanFlatType(join7(claudeDir, "rules"), "rule", ".md", result);
|
|
1251
|
+
await scanFlatType(join7(claudeDir, "hooks"), "hook", ".sh", result);
|
|
1252
|
+
await scanTemplates(join7(claudeDir, "templates"), result);
|
|
1144
1253
|
await scanCategorizedType(
|
|
1145
|
-
|
|
1254
|
+
join7(claudeDir, "context"),
|
|
1146
1255
|
"context",
|
|
1147
1256
|
".md",
|
|
1148
1257
|
result
|
|
1149
1258
|
);
|
|
1150
|
-
await scanDocsRecursive(
|
|
1259
|
+
await scanDocsRecursive(join7(claudeDir, "docs"), result);
|
|
1151
1260
|
await scanSettings(claudeDir, projectPath, result);
|
|
1152
1261
|
return result;
|
|
1153
1262
|
}
|
|
@@ -1165,12 +1274,12 @@ async function scanCommandsRecursive(baseDir, currentDir, result) {
|
|
|
1165
1274
|
if (entry.isDirectory()) {
|
|
1166
1275
|
await scanCommandsRecursive(
|
|
1167
1276
|
baseDir,
|
|
1168
|
-
|
|
1277
|
+
join7(currentDir, entry.name),
|
|
1169
1278
|
result
|
|
1170
1279
|
);
|
|
1171
1280
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
1172
1281
|
const name = entry.name.slice(0, -3);
|
|
1173
|
-
const content = await
|
|
1282
|
+
const content = await readFile7(join7(currentDir, entry.name), "utf-8");
|
|
1174
1283
|
const relDir = currentDir.slice(baseDir.length + 1);
|
|
1175
1284
|
const category = relDir || null;
|
|
1176
1285
|
const scope = extractScope(content, "command");
|
|
@@ -1188,9 +1297,9 @@ async function scanSubfolderType(dir, type, fileName, result) {
|
|
|
1188
1297
|
}
|
|
1189
1298
|
for (const entry of entries) {
|
|
1190
1299
|
if (entry.isDirectory()) {
|
|
1191
|
-
const filePath =
|
|
1300
|
+
const filePath = join7(dir, entry.name, fileName);
|
|
1192
1301
|
try {
|
|
1193
|
-
const content = await
|
|
1302
|
+
const content = await readFile7(filePath, "utf-8");
|
|
1194
1303
|
const scope = extractScope(content, type);
|
|
1195
1304
|
const key = compositeKey(type, entry.name, null);
|
|
1196
1305
|
result.set(key, {
|
|
@@ -1215,7 +1324,7 @@ async function scanFlatType(dir, type, ext, result) {
|
|
|
1215
1324
|
for (const entry of entries) {
|
|
1216
1325
|
if (entry.isFile() && entry.name.endsWith(ext)) {
|
|
1217
1326
|
const name = entry.name.slice(0, -ext.length);
|
|
1218
|
-
const content = await
|
|
1327
|
+
const content = await readFile7(join7(dir, entry.name), "utf-8");
|
|
1219
1328
|
const scope = extractScope(content, type);
|
|
1220
1329
|
const key = compositeKey(type, name, null);
|
|
1221
1330
|
result.set(key, { type, name, category: null, content, scope });
|
|
@@ -1234,7 +1343,7 @@ async function scanCategorizedType(dir, type, ext, result) {
|
|
|
1234
1343
|
const category = entry.name;
|
|
1235
1344
|
let subEntries;
|
|
1236
1345
|
try {
|
|
1237
|
-
subEntries = await readdir3(
|
|
1346
|
+
subEntries = await readdir3(join7(dir, category), {
|
|
1238
1347
|
withFileTypes: true
|
|
1239
1348
|
});
|
|
1240
1349
|
} catch {
|
|
@@ -1243,8 +1352,8 @@ async function scanCategorizedType(dir, type, ext, result) {
|
|
|
1243
1352
|
for (const sub of subEntries) {
|
|
1244
1353
|
if (sub.isFile() && sub.name.endsWith(ext)) {
|
|
1245
1354
|
const name = sub.name.slice(0, -ext.length);
|
|
1246
|
-
const content = await
|
|
1247
|
-
|
|
1355
|
+
const content = await readFile7(
|
|
1356
|
+
join7(dir, category, sub.name),
|
|
1248
1357
|
"utf-8"
|
|
1249
1358
|
);
|
|
1250
1359
|
const scope = extractScope(content, type);
|
|
@@ -1254,7 +1363,7 @@ async function scanCategorizedType(dir, type, ext, result) {
|
|
|
1254
1363
|
}
|
|
1255
1364
|
} else if (entry.isFile() && entry.name.endsWith(ext)) {
|
|
1256
1365
|
const name = entry.name.slice(0, -ext.length);
|
|
1257
|
-
const content = await
|
|
1366
|
+
const content = await readFile7(join7(dir, entry.name), "utf-8");
|
|
1258
1367
|
const scope = extractScope(content, type);
|
|
1259
1368
|
const key = compositeKey(type, name, null);
|
|
1260
1369
|
result.set(key, { type, name, category: null, content, scope });
|
|
@@ -1273,10 +1382,10 @@ async function scanDocsDir(baseDir, currentDir, result) {
|
|
|
1273
1382
|
}
|
|
1274
1383
|
for (const entry of entries) {
|
|
1275
1384
|
if (entry.isDirectory()) {
|
|
1276
|
-
await scanDocsDir(baseDir,
|
|
1385
|
+
await scanDocsDir(baseDir, join7(currentDir, entry.name), result);
|
|
1277
1386
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
1278
1387
|
const name = entry.name.slice(0, -3);
|
|
1279
|
-
const content = await
|
|
1388
|
+
const content = await readFile7(join7(currentDir, entry.name), "utf-8");
|
|
1280
1389
|
const scope = extractScope(content, "docs");
|
|
1281
1390
|
const relDir = currentDir.slice(baseDir.length + 1);
|
|
1282
1391
|
const category = relDir || null;
|
|
@@ -1294,7 +1403,7 @@ async function scanTemplates(dir, result) {
|
|
|
1294
1403
|
}
|
|
1295
1404
|
for (const entry of entries) {
|
|
1296
1405
|
if (entry.isFile() && extname(entry.name)) {
|
|
1297
|
-
const content = await
|
|
1406
|
+
const content = await readFile7(join7(dir, entry.name), "utf-8");
|
|
1298
1407
|
const scope = extractScope(content, "template");
|
|
1299
1408
|
const key = compositeKey("template", entry.name, null);
|
|
1300
1409
|
result.set(key, {
|
|
@@ -1308,10 +1417,10 @@ async function scanTemplates(dir, result) {
|
|
|
1308
1417
|
}
|
|
1309
1418
|
}
|
|
1310
1419
|
async function scanSettings(claudeDir, projectPath, result) {
|
|
1311
|
-
const settingsPath =
|
|
1420
|
+
const settingsPath = join7(claudeDir, "settings.json");
|
|
1312
1421
|
let raw;
|
|
1313
1422
|
try {
|
|
1314
|
-
raw = await
|
|
1423
|
+
raw = await readFile7(settingsPath, "utf-8");
|
|
1315
1424
|
} catch {
|
|
1316
1425
|
return;
|
|
1317
1426
|
}
|
|
@@ -1323,7 +1432,7 @@ async function scanSettings(claudeDir, projectPath, result) {
|
|
|
1323
1432
|
}
|
|
1324
1433
|
parsed = stripPermissionsAllow(parsed);
|
|
1325
1434
|
if (parsed.hooks && typeof parsed.hooks === "object") {
|
|
1326
|
-
const hooksDir = projectPath ?
|
|
1435
|
+
const hooksDir = projectPath ? join7(projectPath, ".claude", "hooks") : join7(claudeDir, "hooks");
|
|
1327
1436
|
const discovered = await discoverHooks(hooksDir);
|
|
1328
1437
|
if (discovered.size > 0) {
|
|
1329
1438
|
parsed.hooks = stripDiscoveredHooks(
|
|
@@ -1655,8 +1764,8 @@ var init_confirm = __esm({
|
|
|
1655
1764
|
});
|
|
1656
1765
|
|
|
1657
1766
|
// src/lib/tech-detect.ts
|
|
1658
|
-
import { readFile as
|
|
1659
|
-
import { join as
|
|
1767
|
+
import { readFile as readFile8, access, readdir as readdir4 } from "node:fs/promises";
|
|
1768
|
+
import { join as join8, relative } from "node:path";
|
|
1660
1769
|
async function fileExists(filePath) {
|
|
1661
1770
|
try {
|
|
1662
1771
|
await access(filePath);
|
|
@@ -1669,8 +1778,8 @@ async function discoverMonorepoApps(projectPath) {
|
|
|
1669
1778
|
const apps = [];
|
|
1670
1779
|
const patterns = [];
|
|
1671
1780
|
try {
|
|
1672
|
-
const raw = await
|
|
1673
|
-
|
|
1781
|
+
const raw = await readFile8(
|
|
1782
|
+
join8(projectPath, "pnpm-workspace.yaml"),
|
|
1674
1783
|
"utf-8"
|
|
1675
1784
|
);
|
|
1676
1785
|
const matches = raw.match(/^\s*-\s*['"]?([^'"#\n]+)['"]?/gm);
|
|
@@ -1684,7 +1793,7 @@ async function discoverMonorepoApps(projectPath) {
|
|
|
1684
1793
|
}
|
|
1685
1794
|
if (patterns.length === 0) {
|
|
1686
1795
|
try {
|
|
1687
|
-
const raw = await
|
|
1796
|
+
const raw = await readFile8(join8(projectPath, "package.json"), "utf-8");
|
|
1688
1797
|
const pkg = JSON.parse(raw);
|
|
1689
1798
|
const ws = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages;
|
|
1690
1799
|
if (ws) patterns.push(...ws);
|
|
@@ -1694,14 +1803,14 @@ async function discoverMonorepoApps(projectPath) {
|
|
|
1694
1803
|
for (const pattern of patterns) {
|
|
1695
1804
|
if (pattern.endsWith("/*")) {
|
|
1696
1805
|
const dir = pattern.slice(0, -2);
|
|
1697
|
-
const absDir =
|
|
1806
|
+
const absDir = join8(projectPath, dir);
|
|
1698
1807
|
try {
|
|
1699
1808
|
const entries = await readdir4(absDir, { withFileTypes: true });
|
|
1700
1809
|
for (const entry of entries) {
|
|
1701
1810
|
if (entry.isDirectory()) {
|
|
1702
|
-
const relPath =
|
|
1703
|
-
const absPath =
|
|
1704
|
-
if (await fileExists(
|
|
1811
|
+
const relPath = join8(dir, entry.name);
|
|
1812
|
+
const absPath = join8(absDir, entry.name);
|
|
1813
|
+
if (await fileExists(join8(absPath, "package.json"))) {
|
|
1705
1814
|
apps.push({ name: entry.name, path: relPath, absPath });
|
|
1706
1815
|
}
|
|
1707
1816
|
}
|
|
@@ -1720,7 +1829,7 @@ async function hasJsxFile(dir, depth = 0) {
|
|
|
1720
1829
|
const name = entry.name;
|
|
1721
1830
|
if (entry.isDirectory()) {
|
|
1722
1831
|
if (SKIP_DIRS.has(name) || JSX_SKIP_DIRS.has(name)) continue;
|
|
1723
|
-
if (await hasJsxFile(
|
|
1832
|
+
if (await hasJsxFile(join8(dir, name), depth + 1)) return true;
|
|
1724
1833
|
} else if (entry.isFile()) {
|
|
1725
1834
|
if (JSX_TEST_PATTERN.test(name)) continue;
|
|
1726
1835
|
if (name.endsWith(".tsx") || name.endsWith(".jsx")) return true;
|
|
@@ -1739,7 +1848,7 @@ async function hasJsxFile(dir, depth = 0) {
|
|
|
1739
1848
|
async function detectCapabilities(dirPath, pkgJson) {
|
|
1740
1849
|
const caps = /* @__PURE__ */ new Set();
|
|
1741
1850
|
for (const sub of JSX_SCAN_DIRS) {
|
|
1742
|
-
if (await hasJsxFile(
|
|
1851
|
+
if (await hasJsxFile(join8(dirPath, sub))) {
|
|
1743
1852
|
caps.add("jsx");
|
|
1744
1853
|
break;
|
|
1745
1854
|
}
|
|
@@ -1761,7 +1870,7 @@ async function detectCapabilities(dirPath, pkgJson) {
|
|
|
1761
1870
|
}
|
|
1762
1871
|
}
|
|
1763
1872
|
}
|
|
1764
|
-
if (!caps.has("node-server") && await fileExists(
|
|
1873
|
+
if (!caps.has("node-server") && await fileExists(join8(dirPath, "src", "main.ts"))) {
|
|
1765
1874
|
caps.add("node-server");
|
|
1766
1875
|
}
|
|
1767
1876
|
if (pkgJson && pkgJson.bin) {
|
|
@@ -1777,7 +1886,7 @@ async function detectFromDirectory(dirPath) {
|
|
|
1777
1886
|
const seen = /* @__PURE__ */ new Map();
|
|
1778
1887
|
let pkgJson = null;
|
|
1779
1888
|
try {
|
|
1780
|
-
const raw = await
|
|
1889
|
+
const raw = await readFile8(join8(dirPath, "package.json"), "utf-8");
|
|
1781
1890
|
pkgJson = JSON.parse(raw);
|
|
1782
1891
|
const allDeps = {
|
|
1783
1892
|
...pkgJson.dependencies ?? {},
|
|
@@ -1809,7 +1918,7 @@ async function detectFromDirectory(dirPath) {
|
|
|
1809
1918
|
}
|
|
1810
1919
|
for (const { file, rule } of CONFIG_FILE_MAP) {
|
|
1811
1920
|
const key = rule.name.toLowerCase();
|
|
1812
|
-
if (!seen.has(key) && await fileExists(
|
|
1921
|
+
if (!seen.has(key) && await fileExists(join8(dirPath, file))) {
|
|
1813
1922
|
seen.set(key, { name: rule.name, category: rule.category });
|
|
1814
1923
|
}
|
|
1815
1924
|
}
|
|
@@ -1987,7 +2096,7 @@ function categorizeDependency(depName) {
|
|
|
1987
2096
|
async function findPackageJsonFiles(dir, projectPath, depth = 0) {
|
|
1988
2097
|
if (depth > 4) return [];
|
|
1989
2098
|
const results = [];
|
|
1990
|
-
const pkgPath =
|
|
2099
|
+
const pkgPath = join8(dir, "package.json");
|
|
1991
2100
|
if (await fileExists(pkgPath)) {
|
|
1992
2101
|
results.push(pkgPath);
|
|
1993
2102
|
}
|
|
@@ -1996,7 +2105,7 @@ async function findPackageJsonFiles(dir, projectPath, depth = 0) {
|
|
|
1996
2105
|
for (const entry of entries) {
|
|
1997
2106
|
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name)) continue;
|
|
1998
2107
|
const subResults = await findPackageJsonFiles(
|
|
1999
|
-
|
|
2108
|
+
join8(dir, entry.name),
|
|
2000
2109
|
projectPath,
|
|
2001
2110
|
depth + 1
|
|
2002
2111
|
);
|
|
@@ -2011,7 +2120,7 @@ async function scanAllDependencies(projectPath) {
|
|
|
2011
2120
|
const dependencies = [];
|
|
2012
2121
|
for (const pkgPath of packageJsonPaths) {
|
|
2013
2122
|
try {
|
|
2014
|
-
const raw = await
|
|
2123
|
+
const raw = await readFile8(pkgPath, "utf-8");
|
|
2015
2124
|
const pkg = JSON.parse(raw);
|
|
2016
2125
|
const sourcePath = relative(projectPath, pkgPath);
|
|
2017
2126
|
const depSections = [
|
|
@@ -2260,14 +2369,14 @@ var init_server_detect = __esm({
|
|
|
2260
2369
|
});
|
|
2261
2370
|
|
|
2262
2371
|
// src/lib/port-verify.ts
|
|
2263
|
-
import { readFile as
|
|
2372
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
2264
2373
|
async function verifyPorts(projectPath, portAllocations) {
|
|
2265
2374
|
const mismatches = [];
|
|
2266
2375
|
const allocatedPorts = new Set(portAllocations.map((a) => a.port));
|
|
2267
2376
|
const packageJsonPaths = await findPackageJsonFiles(projectPath, projectPath);
|
|
2268
2377
|
for (const pkgPath of packageJsonPaths) {
|
|
2269
2378
|
try {
|
|
2270
|
-
const raw = await
|
|
2379
|
+
const raw = await readFile9(pkgPath, "utf-8");
|
|
2271
2380
|
const pkg = JSON.parse(raw);
|
|
2272
2381
|
const scriptPort = detectPortFromScripts(pkg);
|
|
2273
2382
|
if (scriptPort !== null && !allocatedPorts.has(scriptPort)) {
|
|
@@ -2330,7 +2439,7 @@ async function findUnallocatedApps(projectPath, portAllocations) {
|
|
|
2330
2439
|
}
|
|
2331
2440
|
let pkg;
|
|
2332
2441
|
try {
|
|
2333
|
-
const raw = await
|
|
2442
|
+
const raw = await readFile9(`${app.absPath}/package.json`, "utf-8");
|
|
2334
2443
|
pkg = JSON.parse(raw);
|
|
2335
2444
|
} catch {
|
|
2336
2445
|
continue;
|
|
@@ -2374,8 +2483,70 @@ var init_port_verify = __esm({
|
|
|
2374
2483
|
}
|
|
2375
2484
|
});
|
|
2376
2485
|
|
|
2486
|
+
// src/lib/migrate-local-config.ts
|
|
2487
|
+
import { readFile as readFile10, writeFile as writeFile5 } from "node:fs/promises";
|
|
2488
|
+
import { join as join9 } from "node:path";
|
|
2489
|
+
function sharedConfigPath(projectPath) {
|
|
2490
|
+
return join9(projectPath, ".codebyplan.json");
|
|
2491
|
+
}
|
|
2492
|
+
async function needsLocalMigration(projectPath) {
|
|
2493
|
+
try {
|
|
2494
|
+
const raw = await readFile10(sharedConfigPath(projectPath), "utf-8");
|
|
2495
|
+
const parsed = JSON.parse(raw);
|
|
2496
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2497
|
+
return false;
|
|
2498
|
+
}
|
|
2499
|
+
const cfg = parsed;
|
|
2500
|
+
if (typeof cfg.worktree_id !== "string" || cfg.worktree_id === "") {
|
|
2501
|
+
return false;
|
|
2502
|
+
}
|
|
2503
|
+
const local = await readLocalConfig(projectPath);
|
|
2504
|
+
if (local?.device_id) {
|
|
2505
|
+
return false;
|
|
2506
|
+
}
|
|
2507
|
+
return true;
|
|
2508
|
+
} catch {
|
|
2509
|
+
return false;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
async function runLocalMigration(projectPath) {
|
|
2513
|
+
const raw = await readFile10(sharedConfigPath(projectPath), "utf-8");
|
|
2514
|
+
const parsed = JSON.parse(raw);
|
|
2515
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2516
|
+
throw new Error(
|
|
2517
|
+
".codebyplan.json does not contain a JSON object \u2014 cannot migrate"
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
const cfg = parsed;
|
|
2521
|
+
const hadWorktreeId = "worktree_id" in cfg;
|
|
2522
|
+
const localBefore = await readLocalConfig(projectPath);
|
|
2523
|
+
const localWillBeCreated = !localBefore?.device_id;
|
|
2524
|
+
const device_id = await getOrCreateDeviceId(projectPath);
|
|
2525
|
+
const cleaned = { ...cfg };
|
|
2526
|
+
delete cleaned.worktree_id;
|
|
2527
|
+
await writeFile5(
|
|
2528
|
+
sharedConfigPath(projectPath),
|
|
2529
|
+
JSON.stringify(cleaned, null, 2) + "\n",
|
|
2530
|
+
"utf-8"
|
|
2531
|
+
);
|
|
2532
|
+
const files_changed = [".codebyplan.json"];
|
|
2533
|
+
if (localWillBeCreated) files_changed.push(".codebyplan.local.json");
|
|
2534
|
+
return {
|
|
2535
|
+
migrated: true,
|
|
2536
|
+
was_dirty: hadWorktreeId || localWillBeCreated,
|
|
2537
|
+
files_changed,
|
|
2538
|
+
device_id
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
var init_migrate_local_config = __esm({
|
|
2542
|
+
"src/lib/migrate-local-config.ts"() {
|
|
2543
|
+
"use strict";
|
|
2544
|
+
init_local_config();
|
|
2545
|
+
}
|
|
2546
|
+
});
|
|
2547
|
+
|
|
2377
2548
|
// src/lib/eslint-generator.ts
|
|
2378
|
-
import { createHash } from "node:crypto";
|
|
2549
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2379
2550
|
function importedIdentifiers(importLines) {
|
|
2380
2551
|
const names = /* @__PURE__ */ new Set();
|
|
2381
2552
|
for (const line of importLines) {
|
|
@@ -2443,7 +2614,7 @@ function collectDependencies(presets) {
|
|
|
2443
2614
|
return deps;
|
|
2444
2615
|
}
|
|
2445
2616
|
function hashConfig(content) {
|
|
2446
|
-
return
|
|
2617
|
+
return createHash2("sha256").update(content).digest("hex");
|
|
2447
2618
|
}
|
|
2448
2619
|
function buildRules(presets) {
|
|
2449
2620
|
const merged = {};
|
|
@@ -2763,8 +2934,8 @@ __export(eslint_exports, {
|
|
|
2763
2934
|
eslintSync: () => eslintSync,
|
|
2764
2935
|
runEslint: () => runEslint
|
|
2765
2936
|
});
|
|
2766
|
-
import { readFile as
|
|
2767
|
-
import { join as
|
|
2937
|
+
import { readFile as readFile11, writeFile as writeFile6, access as access2, readdir as readdir5 } from "node:fs/promises";
|
|
2938
|
+
import { join as join10, relative as relative2 } from "node:path";
|
|
2768
2939
|
async function fileExists2(filePath) {
|
|
2769
2940
|
try {
|
|
2770
2941
|
await access2(filePath);
|
|
@@ -2775,7 +2946,7 @@ async function fileExists2(filePath) {
|
|
|
2775
2946
|
}
|
|
2776
2947
|
async function autoDetectIgnorePatterns(absPath) {
|
|
2777
2948
|
const patterns = [];
|
|
2778
|
-
if (await fileExists2(
|
|
2949
|
+
if (await fileExists2(join10(absPath, "esbuild.js"))) {
|
|
2779
2950
|
patterns.push("esbuild.js");
|
|
2780
2951
|
}
|
|
2781
2952
|
let entries = [];
|
|
@@ -2795,19 +2966,19 @@ async function autoDetectIgnorePatterns(absPath) {
|
|
|
2795
2966
|
}
|
|
2796
2967
|
for (const ext of ["ts", "mts", "js", "mjs"]) {
|
|
2797
2968
|
const candidate = `vitest.config.${ext}`;
|
|
2798
|
-
if (await fileExists2(
|
|
2969
|
+
if (await fileExists2(join10(absPath, candidate))) {
|
|
2799
2970
|
patterns.push(candidate);
|
|
2800
2971
|
break;
|
|
2801
2972
|
}
|
|
2802
2973
|
}
|
|
2803
2974
|
for (const ext of ["ts", "mts", "js", "mjs"]) {
|
|
2804
2975
|
const candidate = `vite.config.${ext}`;
|
|
2805
|
-
if (await fileExists2(
|
|
2976
|
+
if (await fileExists2(join10(absPath, candidate))) {
|
|
2806
2977
|
patterns.push(candidate);
|
|
2807
2978
|
break;
|
|
2808
2979
|
}
|
|
2809
2980
|
}
|
|
2810
|
-
if (await fileExists2(
|
|
2981
|
+
if (await fileExists2(join10(absPath, "tauri.conf.json"))) {
|
|
2811
2982
|
patterns.push("src-tauri/**");
|
|
2812
2983
|
patterns.push("**/*.d.ts");
|
|
2813
2984
|
}
|
|
@@ -2815,14 +2986,14 @@ async function autoDetectIgnorePatterns(absPath) {
|
|
|
2815
2986
|
}
|
|
2816
2987
|
function detectPackageManager(projectPath) {
|
|
2817
2988
|
return (async () => {
|
|
2818
|
-
if (await fileExists2(
|
|
2819
|
-
if (await fileExists2(
|
|
2989
|
+
if (await fileExists2(join10(projectPath, "pnpm-lock.yaml"))) return "pnpm";
|
|
2990
|
+
if (await fileExists2(join10(projectPath, "yarn.lock"))) return "yarn";
|
|
2820
2991
|
return "npm";
|
|
2821
2992
|
})();
|
|
2822
2993
|
}
|
|
2823
2994
|
async function getInstalledDeps(pkgJsonPath) {
|
|
2824
2995
|
try {
|
|
2825
|
-
const raw = await
|
|
2996
|
+
const raw = await readFile11(pkgJsonPath, "utf-8");
|
|
2826
2997
|
const pkg = JSON.parse(raw);
|
|
2827
2998
|
const all = /* @__PURE__ */ new Set();
|
|
2828
2999
|
for (const name of Object.keys(pkg.dependencies ?? {})) all.add(name);
|
|
@@ -2935,7 +3106,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
2935
3106
|
ignorePatterns: detectedIgnores
|
|
2936
3107
|
});
|
|
2937
3108
|
const hash = hashConfig(content);
|
|
2938
|
-
const configPath =
|
|
3109
|
+
const configPath = join10(target.absPath, "eslint.config.mjs");
|
|
2939
3110
|
configsToWrite.push({
|
|
2940
3111
|
target,
|
|
2941
3112
|
presets,
|
|
@@ -2957,11 +3128,11 @@ async function eslintInit(repoId, projectPath) {
|
|
|
2957
3128
|
return;
|
|
2958
3129
|
}
|
|
2959
3130
|
const pm = await detectPackageManager(projectPath);
|
|
2960
|
-
const rootPkgJsonPath =
|
|
3131
|
+
const rootPkgJsonPath = join10(projectPath, "package.json");
|
|
2961
3132
|
const installed = await getInstalledDeps(rootPkgJsonPath);
|
|
2962
3133
|
if (isMonorepo) {
|
|
2963
3134
|
for (const { target } of configsToWrite) {
|
|
2964
|
-
const appPkgJson =
|
|
3135
|
+
const appPkgJson = join10(target.absPath, "package.json");
|
|
2965
3136
|
const appDeps = await getInstalledDeps(appPkgJson);
|
|
2966
3137
|
for (const dep of appDeps) {
|
|
2967
3138
|
installed.add(dep);
|
|
@@ -2987,9 +3158,9 @@ async function eslintInit(repoId, projectPath) {
|
|
|
2987
3158
|
Install ${missingPkgs.length} missing packages? [Y/n] `
|
|
2988
3159
|
);
|
|
2989
3160
|
if (confirmed) {
|
|
2990
|
-
const { execSync } = await import("node:child_process");
|
|
3161
|
+
const { execSync: execSync3 } = await import("node:child_process");
|
|
2991
3162
|
try {
|
|
2992
|
-
|
|
3163
|
+
execSync3(installCmd, { cwd: projectPath, stdio: "inherit" });
|
|
2993
3164
|
console.log(" Packages installed.\n");
|
|
2994
3165
|
} catch (err) {
|
|
2995
3166
|
console.error(
|
|
@@ -3013,7 +3184,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3013
3184
|
} of configsToWrite) {
|
|
3014
3185
|
if (await fileExists2(configPath)) {
|
|
3015
3186
|
try {
|
|
3016
|
-
const existing = await
|
|
3187
|
+
const existing = await readFile11(configPath, "utf-8");
|
|
3017
3188
|
const existingHash = hashConfig(existing);
|
|
3018
3189
|
if (existingHash === hash) {
|
|
3019
3190
|
console.log(
|
|
@@ -3033,7 +3204,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3033
3204
|
}
|
|
3034
3205
|
}
|
|
3035
3206
|
try {
|
|
3036
|
-
await
|
|
3207
|
+
await writeFile6(configPath, content, "utf-8");
|
|
3037
3208
|
} catch (err) {
|
|
3038
3209
|
console.error(
|
|
3039
3210
|
` ${target.name}: Failed to write config: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -3083,8 +3254,8 @@ async function eslintSync(repoId, projectPath) {
|
|
|
3083
3254
|
let skippedCount = 0;
|
|
3084
3255
|
let driftCount = 0;
|
|
3085
3256
|
for (const config of configs) {
|
|
3086
|
-
const absPath = config.source_path === "." ? projectPath :
|
|
3087
|
-
const configPath =
|
|
3257
|
+
const absPath = config.source_path === "." ? projectPath : join10(projectPath, config.source_path);
|
|
3258
|
+
const configPath = join10(absPath, "eslint.config.mjs");
|
|
3088
3259
|
const detected = await detectTechStack(absPath);
|
|
3089
3260
|
const techNames = detected.flat.map((t) => t.name).filter((n) => n !== SYNTHETIC_CARRIER_NAME);
|
|
3090
3261
|
const capabilities = collectCapabilities(detected.flat);
|
|
@@ -3098,7 +3269,7 @@ async function eslintSync(repoId, projectPath) {
|
|
|
3098
3269
|
if (!presetsChanged) {
|
|
3099
3270
|
if (await fileExists2(configPath)) {
|
|
3100
3271
|
try {
|
|
3101
|
-
const currentContent = await
|
|
3272
|
+
const currentContent = await readFile11(configPath, "utf-8");
|
|
3102
3273
|
const currentHash = hashConfig(currentContent);
|
|
3103
3274
|
if (config.generated_hash && currentHash !== config.generated_hash) {
|
|
3104
3275
|
console.log(
|
|
@@ -3131,7 +3302,7 @@ async function eslintSync(repoId, projectPath) {
|
|
|
3131
3302
|
ignorePatterns: detectedIgnores
|
|
3132
3303
|
});
|
|
3133
3304
|
try {
|
|
3134
|
-
await
|
|
3305
|
+
await writeFile6(configPath, content, "utf-8");
|
|
3135
3306
|
} catch (err) {
|
|
3136
3307
|
console.error(
|
|
3137
3308
|
` ${config.source_path}: Failed to write config: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -3167,11 +3338,11 @@ async function checkEslintDrift(repoId, projectPath) {
|
|
|
3167
3338
|
const configs = res.data ?? [];
|
|
3168
3339
|
for (const config of configs) {
|
|
3169
3340
|
if (!config.generated_hash) continue;
|
|
3170
|
-
const absPath = config.source_path === "." ? projectPath :
|
|
3171
|
-
const configPath =
|
|
3341
|
+
const absPath = config.source_path === "." ? projectPath : join10(projectPath, config.source_path);
|
|
3342
|
+
const configPath = join10(absPath, "eslint.config.mjs");
|
|
3172
3343
|
if (!await fileExists2(configPath)) continue;
|
|
3173
3344
|
try {
|
|
3174
|
-
const content = await
|
|
3345
|
+
const content = await readFile11(configPath, "utf-8");
|
|
3175
3346
|
const currentHash = hashConfig(content);
|
|
3176
3347
|
if (currentHash !== config.generated_hash) {
|
|
3177
3348
|
return true;
|
|
@@ -3222,11 +3393,11 @@ var sync_exports = {};
|
|
|
3222
3393
|
__export(sync_exports, {
|
|
3223
3394
|
runSync: () => runSync
|
|
3224
3395
|
});
|
|
3225
|
-
import { createHash as
|
|
3226
|
-
import { readFile as
|
|
3227
|
-
import { join as
|
|
3396
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
3397
|
+
import { readFile as readFile12, writeFile as writeFile7, mkdir as mkdir2, chmod as chmod2, unlink as unlink2 } from "node:fs/promises";
|
|
3398
|
+
import { join as join11, dirname as dirname2 } from "node:path";
|
|
3228
3399
|
function contentHash(content) {
|
|
3229
|
-
return
|
|
3400
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
3230
3401
|
}
|
|
3231
3402
|
async function runSync() {
|
|
3232
3403
|
const flags = parseFlags(3);
|
|
@@ -3292,7 +3463,7 @@ async function runSync() {
|
|
|
3292
3463
|
}
|
|
3293
3464
|
async function runSyncInner(repoId, projectPath, dryRun, force, fix = false) {
|
|
3294
3465
|
console.log(" Reading local and remote state...");
|
|
3295
|
-
const claudeDir =
|
|
3466
|
+
const claudeDir = join11(projectPath, ".claude");
|
|
3296
3467
|
let localFiles = /* @__PURE__ */ new Map();
|
|
3297
3468
|
try {
|
|
3298
3469
|
localFiles = await scanLocalFiles(claudeDir, projectPath);
|
|
@@ -3527,7 +3698,7 @@ async function runSyncInner(repoId, projectPath, dryRun, force, fix = false) {
|
|
|
3527
3698
|
for (const p of toPull) {
|
|
3528
3699
|
if (p.filePath && p.remoteContent !== null) {
|
|
3529
3700
|
await mkdir2(dirname2(p.filePath), { recursive: true });
|
|
3530
|
-
await
|
|
3701
|
+
await writeFile7(p.filePath, p.remoteContent, "utf-8");
|
|
3531
3702
|
if (p.isHook) await chmod2(p.filePath, 493);
|
|
3532
3703
|
}
|
|
3533
3704
|
}
|
|
@@ -3681,7 +3852,7 @@ async function runSyncInner(repoId, projectPath, dryRun, force, fix = false) {
|
|
|
3681
3852
|
console.log("\n Sync complete.\n");
|
|
3682
3853
|
}
|
|
3683
3854
|
async function syncSettings(claudeDir, projectPath, syncData, repoData, dryRun) {
|
|
3684
|
-
const settingsPath =
|
|
3855
|
+
const settingsPath = join11(claudeDir, "settings.json");
|
|
3685
3856
|
const globalSettingsFiles = syncData.global_settings ?? [];
|
|
3686
3857
|
let globalSettings = {};
|
|
3687
3858
|
for (const gf of globalSettingsFiles) {
|
|
@@ -3701,11 +3872,11 @@ async function syncSettings(claudeDir, projectPath, syncData, repoData, dryRun)
|
|
|
3701
3872
|
globalSettings,
|
|
3702
3873
|
repoSettings
|
|
3703
3874
|
);
|
|
3704
|
-
const hooksDir =
|
|
3875
|
+
const hooksDir = join11(projectPath, ".claude", "hooks");
|
|
3705
3876
|
const discovered = await discoverHooks(hooksDir);
|
|
3706
3877
|
let localSettings = {};
|
|
3707
3878
|
try {
|
|
3708
|
-
const raw = await
|
|
3879
|
+
const raw = await readFile12(settingsPath, "utf-8");
|
|
3709
3880
|
localSettings = JSON.parse(raw);
|
|
3710
3881
|
} catch {
|
|
3711
3882
|
}
|
|
@@ -3720,7 +3891,7 @@ async function syncSettings(claudeDir, projectPath, syncData, repoData, dryRun)
|
|
|
3720
3891
|
const mergedContent = JSON.stringify(merged, null, 2) + "\n";
|
|
3721
3892
|
let currentContent = "";
|
|
3722
3893
|
try {
|
|
3723
|
-
currentContent = await
|
|
3894
|
+
currentContent = await readFile12(settingsPath, "utf-8");
|
|
3724
3895
|
} catch {
|
|
3725
3896
|
}
|
|
3726
3897
|
if (currentContent === mergedContent) {
|
|
@@ -3732,30 +3903,74 @@ async function syncSettings(claudeDir, projectPath, syncData, repoData, dryRun)
|
|
|
3732
3903
|
return;
|
|
3733
3904
|
}
|
|
3734
3905
|
await mkdir2(dirname2(settingsPath), { recursive: true });
|
|
3735
|
-
await
|
|
3906
|
+
await writeFile7(settingsPath, mergedContent, "utf-8");
|
|
3736
3907
|
console.log(" Updated settings.json");
|
|
3737
3908
|
}
|
|
3738
3909
|
async function syncConfig(repoId, projectPath, dryRun) {
|
|
3739
|
-
const configPath =
|
|
3910
|
+
const configPath = join11(projectPath, ".codebyplan.json");
|
|
3740
3911
|
let currentConfig = {};
|
|
3741
3912
|
try {
|
|
3742
|
-
const raw = await
|
|
3913
|
+
const raw = await readFile12(configPath, "utf-8");
|
|
3743
3914
|
currentConfig = JSON.parse(raw);
|
|
3744
3915
|
} catch {
|
|
3745
3916
|
currentConfig = { repo_id: repoId };
|
|
3746
3917
|
}
|
|
3918
|
+
if (dryRun) {
|
|
3919
|
+
try {
|
|
3920
|
+
if (await needsLocalMigration(projectPath)) {
|
|
3921
|
+
console.log(
|
|
3922
|
+
` Would migrate .codebyplan.json -> worktree_id to .codebyplan.local.json (dry-run, skipping actual write).`
|
|
3923
|
+
);
|
|
3924
|
+
}
|
|
3925
|
+
} catch {
|
|
3926
|
+
}
|
|
3927
|
+
} else {
|
|
3928
|
+
try {
|
|
3929
|
+
if (await needsLocalMigration(projectPath)) {
|
|
3930
|
+
const result = await runLocalMigration(projectPath);
|
|
3931
|
+
delete currentConfig.worktree_id;
|
|
3932
|
+
console.log(
|
|
3933
|
+
` Migrated .codebyplan.json -> moved worktree_id to gitignored .codebyplan.local.json (device_id=${result.device_id.slice(0, 8)})`
|
|
3934
|
+
);
|
|
3935
|
+
console.log(
|
|
3936
|
+
` Suggest /cbp-git-commit to stage the cleaned shared file.`
|
|
3937
|
+
);
|
|
3938
|
+
}
|
|
3939
|
+
} catch (err) {
|
|
3940
|
+
console.warn(
|
|
3941
|
+
` Warning: local migration failed (continuing): ${err instanceof Error ? err.message : String(err)}`
|
|
3942
|
+
);
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3747
3945
|
let resolvedWorktreeId;
|
|
3748
3946
|
try {
|
|
3749
|
-
|
|
3947
|
+
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
3948
|
+
let branch = "main";
|
|
3949
|
+
try {
|
|
3950
|
+
const { execSync: execSync3 } = await import("node:child_process");
|
|
3951
|
+
branch = execSync3("git symbolic-ref --short HEAD", {
|
|
3952
|
+
cwd: projectPath,
|
|
3953
|
+
encoding: "utf-8"
|
|
3954
|
+
}).trim();
|
|
3955
|
+
} catch {
|
|
3956
|
+
}
|
|
3957
|
+
const tupleId = await resolveWorktreeId({
|
|
3958
|
+
repoId,
|
|
3959
|
+
repoPath: projectPath,
|
|
3960
|
+
branch,
|
|
3961
|
+
deviceId
|
|
3962
|
+
});
|
|
3963
|
+
if (tupleId) {
|
|
3964
|
+
resolvedWorktreeId = tupleId;
|
|
3965
|
+
} else {
|
|
3966
|
+
resolvedWorktreeId = await resolveAndCacheWorktreeId(repoId, projectPath) ?? void 0;
|
|
3967
|
+
}
|
|
3750
3968
|
} catch (err) {
|
|
3751
3969
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3752
3970
|
console.warn(
|
|
3753
3971
|
` Warning: failed to cache worktree_id (self-heal skipped): ${msg}`
|
|
3754
3972
|
);
|
|
3755
3973
|
}
|
|
3756
|
-
if (resolvedWorktreeId && currentConfig.worktree_id !== resolvedWorktreeId) {
|
|
3757
|
-
currentConfig = { ...currentConfig, worktree_id: resolvedWorktreeId };
|
|
3758
|
-
}
|
|
3759
3974
|
const repoRes = await apiGet(`/repos/${repoId}`);
|
|
3760
3975
|
const repo = repoRes.data;
|
|
3761
3976
|
let portAllocations = [];
|
|
@@ -3765,8 +3980,8 @@ async function syncConfig(repoId, projectPath, dryRun) {
|
|
|
3765
3980
|
{ repo_id: repoId }
|
|
3766
3981
|
);
|
|
3767
3982
|
const allAllocations = portsRes.data ?? [];
|
|
3768
|
-
const
|
|
3769
|
-
const filtered =
|
|
3983
|
+
const filteredByWorktree = resolvedWorktreeId;
|
|
3984
|
+
const filtered = filteredByWorktree ? allAllocations.filter((a) => a.worktree_id === filteredByWorktree) : allAllocations.filter((a) => !a.worktree_id);
|
|
3770
3985
|
const ALLOWED_FIELDS = [
|
|
3771
3986
|
"id",
|
|
3772
3987
|
"repo_id",
|
|
@@ -3794,7 +4009,7 @@ async function syncConfig(repoId, projectPath, dryRun) {
|
|
|
3794
4009
|
` Warning: failed to fetch port allocations: ${err instanceof Error ? err.message : String(err)}`
|
|
3795
4010
|
);
|
|
3796
4011
|
}
|
|
3797
|
-
const worktreeId =
|
|
4012
|
+
const worktreeId = resolvedWorktreeId;
|
|
3798
4013
|
const matchingAlloc = portAllocations[0];
|
|
3799
4014
|
const defaultBranchConfig = {
|
|
3800
4015
|
protected: ["main", "development"],
|
|
@@ -3805,7 +4020,9 @@ async function syncConfig(repoId, projectPath, dryRun) {
|
|
|
3805
4020
|
const branchConfig = repo.branch_config ?? defaultBranchConfig;
|
|
3806
4021
|
const newConfig = {
|
|
3807
4022
|
repo_id: repoId,
|
|
3808
|
-
|
|
4023
|
+
// worktree_id is intentionally omitted — it is never persisted in
|
|
4024
|
+
// .codebyplan.json (CHK-108). The in-memory worktreeId is used only
|
|
4025
|
+
// for server_port / server_type resolution immediately below.
|
|
3809
4026
|
server_port: worktreeId && matchingAlloc ? matchingAlloc.port : repo.server_port,
|
|
3810
4027
|
server_type: worktreeId && matchingAlloc ? matchingAlloc.server_type : repo.server_type,
|
|
3811
4028
|
git_branch: repo.git_branch ?? "development",
|
|
@@ -3823,7 +4040,7 @@ async function syncConfig(repoId, projectPath, dryRun) {
|
|
|
3823
4040
|
console.log(" Config would be updated (dry-run).");
|
|
3824
4041
|
return;
|
|
3825
4042
|
}
|
|
3826
|
-
await
|
|
4043
|
+
await writeFile7(configPath, newJson + "\n", "utf-8");
|
|
3827
4044
|
console.log(" Updated .codebyplan.json");
|
|
3828
4045
|
}
|
|
3829
4046
|
async function syncTechStack(repoId, projectPath, dryRun) {
|
|
@@ -3975,28 +4192,28 @@ function getLocalFilePath(claudeDir, projectPath, remote) {
|
|
|
3975
4192
|
hook: { dir: "hooks", ext: ".sh" },
|
|
3976
4193
|
template: { dir: "templates", ext: "" },
|
|
3977
4194
|
context: { dir: "context", ext: ".md" },
|
|
3978
|
-
docs_stack: { dir:
|
|
4195
|
+
docs_stack: { dir: join11("docs", "stack"), ext: ".md" },
|
|
3979
4196
|
docs: { dir: "docs", ext: ".md" },
|
|
3980
4197
|
claude_md: { dir: "", ext: "" },
|
|
3981
4198
|
settings: { dir: "", ext: "" }
|
|
3982
4199
|
};
|
|
3983
|
-
if (remote.type === "claude_md") return
|
|
3984
|
-
if (remote.type === "settings") return
|
|
4200
|
+
if (remote.type === "claude_md") return join11(projectPath, "CLAUDE.md");
|
|
4201
|
+
if (remote.type === "settings") return join11(claudeDir, "settings.json");
|
|
3985
4202
|
const cfg = typeConfig2[remote.type];
|
|
3986
|
-
if (!cfg) return
|
|
3987
|
-
const typeDir = remote.type === "command" ?
|
|
4203
|
+
if (!cfg) return join11(claudeDir, remote.name);
|
|
4204
|
+
const typeDir = remote.type === "command" ? join11(claudeDir, cfg.dir, "cbp") : join11(claudeDir, cfg.dir);
|
|
3988
4205
|
if (cfg.subfolder)
|
|
3989
|
-
return
|
|
4206
|
+
return join11(typeDir, remote.name, `${cfg.subfolder}${cfg.ext}`);
|
|
3990
4207
|
if (remote.type === "command" && remote.category)
|
|
3991
|
-
return
|
|
3992
|
-
if (remote.type === "template") return
|
|
4208
|
+
return join11(typeDir, remote.category, `${remote.name}${cfg.ext}`);
|
|
4209
|
+
if (remote.type === "template") return join11(typeDir, remote.name);
|
|
3993
4210
|
if (remote.category && (remote.type === "context" || remote.type === "docs_stack" || remote.type === "docs"))
|
|
3994
|
-
return
|
|
3995
|
-
return
|
|
4211
|
+
return join11(typeDir, remote.category, `${remote.name}${cfg.ext}`);
|
|
4212
|
+
return join11(typeDir, `${remote.name}${cfg.ext}`);
|
|
3996
4213
|
}
|
|
3997
4214
|
function getSyncVersion() {
|
|
3998
4215
|
try {
|
|
3999
|
-
return "1.
|
|
4216
|
+
return "1.5.0";
|
|
4000
4217
|
} catch {
|
|
4001
4218
|
return "unknown";
|
|
4002
4219
|
}
|
|
@@ -4046,10 +4263,63 @@ var init_sync = __esm({
|
|
|
4046
4263
|
init_hook_registry();
|
|
4047
4264
|
init_port_verify();
|
|
4048
4265
|
init_resolve_worktree();
|
|
4266
|
+
init_local_config();
|
|
4267
|
+
init_migrate_local_config();
|
|
4049
4268
|
init_eslint();
|
|
4050
4269
|
}
|
|
4051
4270
|
});
|
|
4052
4271
|
|
|
4272
|
+
// src/cli/resolve-worktree.ts
|
|
4273
|
+
var resolve_worktree_exports = {};
|
|
4274
|
+
__export(resolve_worktree_exports, {
|
|
4275
|
+
runResolveWorktree: () => runResolveWorktree
|
|
4276
|
+
});
|
|
4277
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
4278
|
+
async function runResolveWorktree() {
|
|
4279
|
+
try {
|
|
4280
|
+
const projectPath = process.cwd();
|
|
4281
|
+
const found = await findCodebyplanConfig(projectPath);
|
|
4282
|
+
if (!found?.contents.repo_id) {
|
|
4283
|
+
process.exit(0);
|
|
4284
|
+
}
|
|
4285
|
+
const repoId = found.contents.repo_id;
|
|
4286
|
+
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
4287
|
+
let branch = "";
|
|
4288
|
+
try {
|
|
4289
|
+
branch = execSync2("git symbolic-ref --short HEAD", {
|
|
4290
|
+
cwd: projectPath,
|
|
4291
|
+
encoding: "utf-8"
|
|
4292
|
+
}).trim();
|
|
4293
|
+
} catch {
|
|
4294
|
+
}
|
|
4295
|
+
const worktreeId = await resolveWorktreeId({
|
|
4296
|
+
repoId,
|
|
4297
|
+
repoPath: projectPath,
|
|
4298
|
+
branch,
|
|
4299
|
+
deviceId
|
|
4300
|
+
});
|
|
4301
|
+
if (worktreeId) {
|
|
4302
|
+
process.stdout.write(worktreeId);
|
|
4303
|
+
}
|
|
4304
|
+
process.exit(0);
|
|
4305
|
+
} catch (err) {
|
|
4306
|
+
if (process.env.CODEBYPLAN_DEBUG === "1") {
|
|
4307
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4308
|
+
process.stderr.write(`resolve-worktree: ${msg}
|
|
4309
|
+
`);
|
|
4310
|
+
}
|
|
4311
|
+
process.exit(0);
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
var init_resolve_worktree2 = __esm({
|
|
4315
|
+
"src/cli/resolve-worktree.ts"() {
|
|
4316
|
+
"use strict";
|
|
4317
|
+
init_config();
|
|
4318
|
+
init_local_config();
|
|
4319
|
+
init_resolve_worktree();
|
|
4320
|
+
}
|
|
4321
|
+
});
|
|
4322
|
+
|
|
4053
4323
|
// src/index.ts
|
|
4054
4324
|
init_version();
|
|
4055
4325
|
import { readFileSync } from "node:fs";
|
|
@@ -4114,16 +4384,22 @@ void (async () => {
|
|
|
4114
4384
|
}
|
|
4115
4385
|
process.exit(0);
|
|
4116
4386
|
}
|
|
4387
|
+
if (arg === "resolve-worktree") {
|
|
4388
|
+
const { runResolveWorktree: runResolveWorktree2 } = await Promise.resolve().then(() => (init_resolve_worktree2(), resolve_worktree_exports));
|
|
4389
|
+
await runResolveWorktree2();
|
|
4390
|
+
process.exit(0);
|
|
4391
|
+
}
|
|
4117
4392
|
if (arg === "help" || arg === "--help" || arg === "-h" || arg === void 0) {
|
|
4118
4393
|
console.log(`
|
|
4119
4394
|
CodeByPlan CLI v${VERSION}
|
|
4120
4395
|
|
|
4121
4396
|
Usage:
|
|
4122
|
-
codebyplan setup
|
|
4123
|
-
codebyplan sync
|
|
4124
|
-
codebyplan eslint
|
|
4125
|
-
codebyplan
|
|
4126
|
-
codebyplan
|
|
4397
|
+
codebyplan setup Interactive setup (API key + project init + first sync)
|
|
4398
|
+
codebyplan sync Bidirectional sync (pull + push + config)
|
|
4399
|
+
codebyplan eslint ESLint config management (init, sync)
|
|
4400
|
+
codebyplan resolve-worktree Resolve active worktree UUID from device+path+branch tuple
|
|
4401
|
+
codebyplan help Show this help message
|
|
4402
|
+
codebyplan --version Print version
|
|
4127
4403
|
|
|
4128
4404
|
Sync options:
|
|
4129
4405
|
--path <dir> Project root directory (default: cwd)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codebyplan",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "CLI for CodeByPlan — AI-powered development planning and tracking",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"eslint-plugin-prettier": "^5.2.2",
|
|
55
55
|
"eslint-plugin-security": "^3.0.1",
|
|
56
56
|
"globals": "^17.0.0",
|
|
57
|
+
"prettier": "^3.8.1",
|
|
57
58
|
"typescript": "^5",
|
|
58
59
|
"typescript-eslint": "^8.20.0",
|
|
59
60
|
"vitest": "^4.0.18"
|