harness-bujang 0.8.1 → 0.8.3
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.
|
@@ -452,7 +452,18 @@ async function runInit(args) {
|
|
|
452
452
|
if (opts.chatBackend === "sqlite" && !opts.commitChat) {
|
|
453
453
|
await ensureGitignore(opts.target, [".harness/"]);
|
|
454
454
|
}
|
|
455
|
-
|
|
455
|
+
if (opts.installDeps) {
|
|
456
|
+
await ensurePeerDeps(opts.target, opts.chatBackend);
|
|
457
|
+
} else {
|
|
458
|
+
console.log(c.dim("\u{1F4E6} --no-install-deps \u2014 peer dep auto-install skipped"));
|
|
459
|
+
console.log();
|
|
460
|
+
}
|
|
461
|
+
if (opts.chatBackend === "sqlite") {
|
|
462
|
+
await patchNextConfig(opts.target);
|
|
463
|
+
} else {
|
|
464
|
+
await scaffoldEnvExample(opts.target);
|
|
465
|
+
}
|
|
466
|
+
printBackendInstructions(opts.chatBackend, opts.commitChat, opts.installDeps);
|
|
456
467
|
} else {
|
|
457
468
|
console.log(
|
|
458
469
|
`${c.yellow("\u2139\uFE0E Chat-room UI (Next.js admin route) skipped")} ` + c.dim(`\u2014 your stack is detected as ${scan.framework}.`)
|
|
@@ -481,17 +492,8 @@ async function runInit(args) {
|
|
|
481
492
|
}
|
|
482
493
|
console.log(c.bold(c.green("\u2705 Done.")));
|
|
483
494
|
console.log();
|
|
484
|
-
console.log("Next steps:");
|
|
485
|
-
console.log(` ${c.cyan("1.")} Open Claude Code in this project`);
|
|
486
|
-
console.log(` ${c.cyan("2.")} Run ${c.bold("/bujang-status")} (if the plugin is installed) or just`);
|
|
487
|
-
console.log(` ask ${c.bold('"Director, please add a hello-world endpoint"')}`);
|
|
488
|
-
if (scan.framework.startsWith("Next.js") && opts.installTemplate) {
|
|
489
|
-
console.log(` ${c.cyan("3.")} Watch ${c.bold(context.ADMIN_HARNESS_ROUTE)} for live updates (after env setup)`);
|
|
490
|
-
} else {
|
|
491
|
-
console.log(` ${c.cyan("3.")} Watch the chat room: ${c.bold("npx harness-bujang chat")} ${c.dim("\u2192 http://localhost:7777")}`);
|
|
492
|
-
}
|
|
493
|
-
console.log();
|
|
494
495
|
printRestartReminder(opts.lang);
|
|
496
|
+
printNextSteps(opts.lang, scan.framework.startsWith("Next.js") && opts.installTemplate, context.ADMIN_HARNESS_ROUTE);
|
|
495
497
|
}
|
|
496
498
|
function printRestartReminder(lang) {
|
|
497
499
|
const ko = lang === "ko";
|
|
@@ -499,17 +501,44 @@ function printRestartReminder(lang) {
|
|
|
499
501
|
const top = ` ${c.dim("\u256D" + "\u2500".repeat(64) + "\u256E")}`;
|
|
500
502
|
const bot = ` ${c.dim("\u2570" + "\u2500".repeat(64) + "\u256F")}`;
|
|
501
503
|
console.log(top);
|
|
502
|
-
console.log(line(ko ? c.bold(c.yellow("\u26A0\uFE0F
|
|
504
|
+
console.log(line(ko ? c.bold(c.yellow("\u26A0\uFE0F STEP 1 \u2014 Claude Code \uAED0\uB2E4 \uB2E4\uC2DC \uCF1C\uC8FC\uC138\uC694")) : c.bold(c.yellow("\u26A0\uFE0F STEP 1 \u2014 Restart Claude Code"))));
|
|
503
505
|
console.log(line(""));
|
|
504
|
-
console.log(line(ko ? "\uC5D0\uC774\uC804\uD2B8 \uB4F1\uB85D\uC740 \uC138\uC158 \uC2DC\uC791 \uC2DC\uC810\uC5D0\uB9CC \uC77C\uC5B4\uB098\
|
|
505
|
-
console.log(line(ko ? "\
|
|
506
|
+
console.log(line(ko ? "\uC5D0\uC774\uC804\uD2B8 \uB4F1\uB85D\uC740 \uC138\uC158 \uC2DC\uC791 \uC2DC\uC810\uC5D0\uB9CC \uC77C\uC5B4\uB098\uC11C, \uC9C0\uAE08 \uB5A0 \uC788\uB294" : "Agents register at session start only, so the running session"));
|
|
507
|
+
console.log(line(ko ? "\uC138\uC158\uC5D0\uC11C\uB294 \uC0C8\uB85C \uAE50 \uBD80\uC7A5 + \uD300\uC774 \uC548 \uBCF4\uC785\uB2C8\uB2E4." : "won't see the newly-installed director + teams yet."));
|
|
506
508
|
console.log(line(""));
|
|
507
|
-
console.log(line(` ${c.
|
|
508
|
-
console.log(line(`
|
|
509
|
+
console.log(line(` ${c.bold(ko ? "\uAD8C\uC7A5: " : "Recommended: ")}${c.cyan(ko ? "Claude Code \uC644\uC804 \uC885\uB8CC \u2192 \uAC19\uC740 \uD3F4\uB354\uC5D0\uC11C \uB2E4\uC2DC \uC2DC\uC791" : "Fully quit Claude Code \u2192 relaunch in this folder")}`));
|
|
510
|
+
console.log(line(` ${c.dim(ko ? "\uB300\uC548: " : "Or: ")}${c.cyan("/agents")}${c.dim(ko ? " (\uC5D0\uC774\uC804\uD2B8 \uBA54\uB274\uC5D0\uC11C \uC7AC\uB85C\uB4DC, \uAC00\uB054 \uC548 \uB428)" : " (refresh menu \u2014 sometimes flaky)")}`));
|
|
509
511
|
console.log(line(""));
|
|
510
|
-
console.log(line(
|
|
512
|
+
console.log(line(c.dim(ko ? "\u26A0 /clear \uB294 \uCEE8\uD14D\uC2A4\uD2B8\uB9CC \uBE44\uC6C0 \u2014 \uC5D0\uC774\uC804\uD2B8 \uC7AC\uB4F1\uB85D \uC548 \uB428" : "\u26A0 /clear only wipes context \u2014 it does NOT re-register agents")));
|
|
513
|
+
console.log(bot);
|
|
514
|
+
console.log();
|
|
515
|
+
}
|
|
516
|
+
function printNextSteps(lang, nextjsEmbedded, adminRoute) {
|
|
517
|
+
const ko = lang === "ko";
|
|
518
|
+
const line = (s) => ` ${c.dim("\u2502")} ${s}`;
|
|
519
|
+
const top = ` ${c.dim("\u256D" + "\u2500".repeat(64) + "\u256E")}`;
|
|
520
|
+
const bot = ` ${c.dim("\u2570" + "\u2500".repeat(64) + "\u256F")}`;
|
|
521
|
+
console.log(top);
|
|
522
|
+
console.log(line(c.bold(c.green(ko ? "\u2728 STEP 2 \u2014 \uC7AC\uC2DC\uC791 \uD6C4 \uD1A1\uBC29 \uC5F4\uAE30" : "\u2728 STEP 2 \u2014 After restart, open the chat room"))));
|
|
523
|
+
console.log(line(""));
|
|
524
|
+
console.log(line(` ${c.bold(ko ? "Claude Code \uC548\uC5D0\uC11C: " : "Inside Claude Code: ")}${c.cyan("/open-chat")}`));
|
|
525
|
+
console.log(line(` ${c.dim(ko ? "\u2192 \uBC31\uADF8\uB77C\uC6B4\uB4DC\uB85C \uD1A1\uBC29 \uC11C\uBC84 \uB744\uC6B0\uACE0 \uBE0C\uB77C\uC6B0\uC800 \uC790\uB3D9 \uC624\uD508" : "\u2192 launches the chat-room server in background + opens the browser")}`));
|
|
526
|
+
console.log(line(""));
|
|
527
|
+
console.log(line(c.dim(ko ? "\uD50C\uB7EC\uADF8\uC778 \uC548 \uC124\uCE58\uD558\uC168\uC73C\uBA74 (\uBCC4\uB3C4 \uD130\uBBF8\uB110):" : "No plugin installed? (separate terminal):")));
|
|
528
|
+
console.log(line(` ${c.dim("$")} ${c.cyan("npx harness-bujang chat")} ${c.dim("\u2192 http://localhost:7777")}`));
|
|
529
|
+
if (nextjsEmbedded) {
|
|
530
|
+
console.log(line(""));
|
|
531
|
+
console.log(line(c.dim(ko ? `\uB610\uB294 dev \uC11C\uBC84 \uB744\uC6B0\uACE0 ${c.bold(adminRoute)} \uBC29\uBB38 (\uAC19\uC740 \uD1A1\uBC29, \uB2E4\uB978 surface)` : `Or run your dev server and visit ${c.bold(adminRoute)} (same chat, different surface)`)));
|
|
532
|
+
}
|
|
533
|
+
console.log(bot);
|
|
534
|
+
console.log();
|
|
535
|
+
console.log(top);
|
|
536
|
+
console.log(line(c.bold(c.cyan(ko ? "\u{1F3AF} STEP 3 \u2014 \uBD80\uC7A5\uD55C\uD14C \uCCAB \uC9C0\uC2DC" : "\u{1F3AF} STEP 3 \u2014 Give the Director a first task"))));
|
|
537
|
+
console.log(line(""));
|
|
538
|
+
console.log(line(c.dim(ko ? "\uC608:" : "e.g.:")));
|
|
539
|
+
console.log(line(` ${c.bold(ko ? '"\uBD80\uC7A5\uB2D8, hello-world \uC5D4\uB4DC\uD3EC\uC778\uD2B8 \uD558\uB098 \uB9CC\uB4E4\uC5B4\uC918"' : '"Director, please add a hello-world endpoint"')}`));
|
|
511
540
|
console.log(line(""));
|
|
512
|
-
console.log(line(c.dim(ko ? "
|
|
541
|
+
console.log(line(c.dim(ko ? "\uBD80\uC7A5\uC774 \uD300 \uD638\uCD9C \u2192 \uC791\uC5C5 \u2192 \uD1A1\uBC29\uC5D0 \uBAA8\uB4E0 INSERT \uAC00 \uC2E4\uC2DC\uAC04\uC73C\uB85C \uBCF4\uC784" : "Director dispatches teams \u2192 work \u2192 every INSERT streams to the chat room")));
|
|
513
542
|
console.log(bot);
|
|
514
543
|
console.log();
|
|
515
544
|
}
|
|
@@ -744,6 +773,7 @@ function parseArgs(args) {
|
|
|
744
773
|
installTemplate: !args.includes("--no-template"),
|
|
745
774
|
editClaudeMd: !args.includes("--no-claude-md"),
|
|
746
775
|
seedLearningLog: !args.includes("--no-learning-log"),
|
|
776
|
+
installDeps: !args.includes("--no-install-deps"),
|
|
747
777
|
yes: args.includes("--yes") || args.includes("-y"),
|
|
748
778
|
adapters,
|
|
749
779
|
modelMap,
|
|
@@ -822,21 +852,138 @@ ${toAdd.join("\n")}
|
|
|
822
852
|
`;
|
|
823
853
|
await fs2.writeFile(gitignorePath, existing + block);
|
|
824
854
|
}
|
|
825
|
-
function
|
|
855
|
+
async function ensurePeerDeps(target, backend) {
|
|
856
|
+
const { detectPM, readDeps, installDeps, buildAddCmd } = await import("./pm-DUCX56FQ.js");
|
|
857
|
+
const pm = await detectPM(target);
|
|
858
|
+
const existing = await readDeps(target);
|
|
859
|
+
const wanted = [];
|
|
826
860
|
if (backend === "sqlite") {
|
|
827
|
-
|
|
861
|
+
if (!existing["better-sqlite3"]) wanted.push({ name: "better-sqlite3" });
|
|
862
|
+
if (!existing["@types/better-sqlite3"]) wanted.push({ name: "@types/better-sqlite3", dev: true });
|
|
863
|
+
} else {
|
|
864
|
+
if (!existing["@supabase/supabase-js"]) wanted.push({ name: "@supabase/supabase-js" });
|
|
865
|
+
}
|
|
866
|
+
if (wanted.length === 0) {
|
|
867
|
+
console.log(c.dim(`\u{1F4E6} Peer deps already installed (${backend}) \u2014 skipped`));
|
|
828
868
|
console.log();
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
const summary = wanted.map((d) => d.name + (d.dev ? c.dim(" (dev)") : "")).join(", ");
|
|
872
|
+
console.log(c.bold(`\u{1F4E6} Auto-installing peer deps via ${pm}`));
|
|
873
|
+
console.log(c.dim(` ${summary}`));
|
|
874
|
+
console.log();
|
|
875
|
+
const result = await installDeps(target, pm, wanted);
|
|
876
|
+
if (result.ok) {
|
|
877
|
+
console.log(c.green(` \u2713 installed`));
|
|
878
|
+
} else {
|
|
879
|
+
console.log(c.yellow(` \u26A0 install failed: ${result.err ?? "unknown"}`));
|
|
880
|
+
console.log(c.dim(` \uC218\uB3D9\uC73C\uB85C \uC2E4\uD589\uD574\uC8FC\uC138\uC694 / Run manually:`));
|
|
881
|
+
const prod = wanted.filter((d) => !d.dev).map((d) => d.name);
|
|
882
|
+
const dev = wanted.filter((d) => d.dev).map((d) => d.name);
|
|
883
|
+
if (prod.length > 0) console.log(c.dim(` $ ${buildAddCmd(pm, prod, false)}`));
|
|
884
|
+
if (dev.length > 0) console.log(c.dim(` $ ${buildAddCmd(pm, dev, true)}`));
|
|
885
|
+
}
|
|
886
|
+
console.log();
|
|
887
|
+
}
|
|
888
|
+
async function patchNextConfig(target) {
|
|
889
|
+
const candidates = ["next.config.ts", "next.config.mjs", "next.config.js"];
|
|
890
|
+
let configPath = null;
|
|
891
|
+
for (const cand of candidates) {
|
|
892
|
+
if (await exists2(path2.join(target, cand))) {
|
|
893
|
+
configPath = path2.join(target, cand);
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (!configPath) {
|
|
898
|
+
console.log(c.dim(`\u{1F4DD} next.config.{js,mjs,ts} not found \u2014 serverExternalPackages patch skipped`));
|
|
899
|
+
console.log();
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
const filename = path2.basename(configPath);
|
|
903
|
+
const raw = await fs2.readFile(configPath, "utf8");
|
|
904
|
+
if (/serverExternalPackages\s*:\s*\[[^\]]*['"]better-sqlite3['"]/.test(raw)) {
|
|
905
|
+
console.log(c.dim(`\u{1F4DD} ${filename} \uC774\uBBF8 'better-sqlite3' \uB4F1\uB85D\uB428 \u2014 skipped`));
|
|
906
|
+
console.log();
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
if (/serverExternalPackages\s*:\s*\[/.test(raw)) {
|
|
910
|
+
const patched = raw.replace(
|
|
911
|
+
/serverExternalPackages\s*:\s*\[/,
|
|
912
|
+
`serverExternalPackages: ['better-sqlite3', `
|
|
913
|
+
);
|
|
914
|
+
await fs2.writeFile(configPath, patched);
|
|
915
|
+
console.log(c.green(` \u2713 ${filename} \u2190 'better-sqlite3' added to serverExternalPackages`));
|
|
916
|
+
console.log();
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
const injectPattern = /((?:const\s+\w+(?:\s*:\s*[^=]+)?\s*=\s*|module\.exports\s*=\s*|export\s+default\s*)\{)/;
|
|
920
|
+
if (injectPattern.test(raw)) {
|
|
921
|
+
const patched = raw.replace(
|
|
922
|
+
injectPattern,
|
|
923
|
+
`$1
|
|
924
|
+
serverExternalPackages: ['better-sqlite3'],`
|
|
925
|
+
);
|
|
926
|
+
await fs2.writeFile(configPath, patched);
|
|
927
|
+
console.log(c.green(` \u2713 ${filename} \u2190 injected serverExternalPackages: ['better-sqlite3']`));
|
|
928
|
+
console.log();
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
console.log(c.yellow(`\u{1F4DD} ${filename} \uC790\uB3D9 \uD328\uCE58 \uBABB \uD588\uC2B5\uB2C8\uB2E4 \u2014 \uB2E4\uC74C\uC744 \uCD94\uAC00\uD574\uC8FC\uC138\uC694:`));
|
|
932
|
+
console.log(c.dim(` {`));
|
|
933
|
+
console.log(c.dim(` serverExternalPackages: ['better-sqlite3'],`));
|
|
934
|
+
console.log(c.dim(` // ...rest of config`));
|
|
935
|
+
console.log(c.dim(` }`));
|
|
936
|
+
console.log();
|
|
937
|
+
}
|
|
938
|
+
async function scaffoldEnvExample(target) {
|
|
939
|
+
const fp = path2.join(target, ".env.local.example");
|
|
940
|
+
let raw = "";
|
|
941
|
+
if (await exists2(fp)) raw = await fs2.readFile(fp, "utf8");
|
|
942
|
+
const HEADER = "# --- Harness-Bujang (Supabase mode) ---";
|
|
943
|
+
const KEYS = [
|
|
944
|
+
"HARNESS_DB=supabase",
|
|
945
|
+
"NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co",
|
|
946
|
+
"SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here",
|
|
947
|
+
"HARNESS_WRITE_SECRET=generate-with-openssl-rand-hex-32",
|
|
948
|
+
"SUPER_ADMIN_EMAILS=you@example.com"
|
|
949
|
+
];
|
|
950
|
+
const missing = KEYS.filter((line) => {
|
|
951
|
+
const key = line.split("=")[0];
|
|
952
|
+
return !new RegExp(`^${key}=`, "m").test(raw);
|
|
953
|
+
});
|
|
954
|
+
if (missing.length === 0 && raw.includes(HEADER)) {
|
|
955
|
+
console.log(c.dim(`\u{1F4DD} .env.local.example \uC774\uBBF8 \uBAA8\uB4E0 Supabase \uD0A4 \uC788\uC74C \u2014 skipped`));
|
|
832
956
|
console.log();
|
|
833
|
-
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
const sep = raw && !raw.endsWith("\n") ? "\n" : "";
|
|
960
|
+
const block = `${sep}
|
|
961
|
+
${HEADER}
|
|
962
|
+
${missing.join("\n")}
|
|
963
|
+
`;
|
|
964
|
+
await fs2.writeFile(fp, raw + block);
|
|
965
|
+
console.log(c.green(` \u2713 .env.local.example \u2190 Supabase \uD0A4 ${missing.length}\uAC1C \uCD94\uAC00 (placeholders)`));
|
|
966
|
+
console.log(c.dim(` \u2192 \uC2E4\uC81C \uAC12\uC740 Supabase \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uBCF5\uC0AC\uD574\uC11C .env.local \uC5D0 \uB123\uC5B4\uC8FC\uC138\uC694.`));
|
|
967
|
+
console.log();
|
|
968
|
+
}
|
|
969
|
+
function printBackendInstructions(backend, commitChat, installedDeps) {
|
|
970
|
+
let n = 1;
|
|
971
|
+
if (backend === "sqlite") {
|
|
972
|
+
console.log(c.bold(c.cyan("\u{1F4CB} SQLite mode (default) \u2014 next steps:")));
|
|
973
|
+
console.log();
|
|
974
|
+
if (!installedDeps) {
|
|
975
|
+
console.log(` ${c.bold(`${n++}.`)} Install the SQLite driver in your project:`);
|
|
976
|
+
console.log(` ${c.dim("$")} ${c.bold("npm i better-sqlite3")}`);
|
|
977
|
+
console.log(` ${c.dim("$")} ${c.bold("npm i -D @types/better-sqlite3")}`);
|
|
978
|
+
console.log();
|
|
979
|
+
}
|
|
980
|
+
console.log(` ${c.bold(`${n++}.`)} (optional) Add to ${c.bold(".env.local")}:`);
|
|
834
981
|
console.log(` ${c.dim("HARNESS_DB=sqlite # default \u2014 can be omitted")}`);
|
|
835
982
|
console.log(` ${c.dim("HARNESS_SQLITE_PATH=./.harness/chat.db # default \u2014 can be omitted")}`);
|
|
836
983
|
console.log(` ${c.bold("HARNESS_WRITE_SECRET=<random> # for bot/script writes")}`);
|
|
837
984
|
console.log(` ${c.bold("SUPER_ADMIN_EMAILS=you@example.com # comma-separated")}`);
|
|
838
985
|
console.log();
|
|
839
|
-
console.log(` ${c.bold(
|
|
986
|
+
console.log(` ${c.bold(`${n++}.`)} Run your dev server and visit ${c.bold("/admin/harness")}.`);
|
|
840
987
|
console.log();
|
|
841
988
|
if (commitChat) {
|
|
842
989
|
console.log(c.dim(` \u{1F4E6} ${c.bold("--commit-chat")} mode: .harness/ NOT added to .gitignore.`));
|
|
@@ -850,21 +997,26 @@ function printBackendInstructions(backend, commitChat) {
|
|
|
850
997
|
} else {
|
|
851
998
|
console.log(c.bold(c.cyan("\u{1F4CB} Supabase mode \u2014 next steps:")));
|
|
852
999
|
console.log();
|
|
853
|
-
|
|
1000
|
+
if (!installedDeps) {
|
|
1001
|
+
console.log(` ${c.bold(`${n++}.`)} Install the Supabase client in your project:`);
|
|
1002
|
+
console.log(` ${c.dim("$")} ${c.bold("npm i @supabase/supabase-js")}`);
|
|
1003
|
+
console.log();
|
|
1004
|
+
}
|
|
1005
|
+
console.log(` ${c.bold(`${n++}.`)} Apply the migrations to your Supabase project:`);
|
|
854
1006
|
console.log(` ${c.dim("$")} ${c.bold("supabase db push")}`);
|
|
855
1007
|
console.log(` ${c.dim(" (or run them manually via psql / SQL editor)")}`);
|
|
856
1008
|
console.log();
|
|
857
|
-
console.log(` ${c.bold(
|
|
1009
|
+
console.log(` ${c.bold(`${n++}.`)} Fill in the keys in ${c.bold(".env.local")} ${c.dim("(template scaffolded at .env.local.example)")}:`);
|
|
858
1010
|
console.log(` ${c.bold("HARNESS_DB=supabase")}`);
|
|
859
1011
|
console.log(` ${c.bold("NEXT_PUBLIC_SUPABASE_URL=...")}`);
|
|
860
1012
|
console.log(` ${c.bold("SUPABASE_SERVICE_ROLE_KEY=...")}`);
|
|
861
1013
|
console.log(` ${c.bold("HARNESS_WRITE_SECRET=<random>")}`);
|
|
862
1014
|
console.log(` ${c.bold("SUPER_ADMIN_EMAILS=you@example.com")}`);
|
|
863
1015
|
console.log();
|
|
864
|
-
console.log(` ${c.bold(
|
|
1016
|
+
console.log(` ${c.bold(`${n++}.`)} Implement ${c.bold("verifySuperAdmin()")} at ${c.bold("@/lib/utils/admin")}`);
|
|
865
1017
|
console.log(` (see ${c.dim("packages/template/README.md")} for an example).`);
|
|
866
1018
|
console.log();
|
|
867
|
-
console.log(` ${c.bold(
|
|
1019
|
+
console.log(` ${c.bold(`${n++}.`)} Run your dev server and visit ${c.bold("/admin/harness")}.`);
|
|
868
1020
|
}
|
|
869
1021
|
console.log();
|
|
870
1022
|
}
|
package/dist/index.js
CHANGED
|
@@ -48,6 +48,7 @@ ${c.bold("init \uC635\uC158:")}
|
|
|
48
48
|
--no-template \uD1A1\uBC29 UI \uC124\uCE58 \uAC74\uB108\uB6F0\uAE30
|
|
49
49
|
--no-claude-md CLAUDE.md \uC218\uC815 \uAC74\uB108\uB6F0\uAE30
|
|
50
50
|
--no-learning-log \uD559\uC2B5 \uB85C\uADF8 \uC2DC\uB4DC \uAC74\uB108\uB6F0\uAE30
|
|
51
|
+
--no-install-deps peer dep \uC790\uB3D9 \uC124\uCE58 \uAC74\uB108\uB6F0\uAE30 (better-sqlite3 / @supabase/supabase-js)
|
|
51
52
|
--yes, -y \uD504\uB86C\uD504\uD2B8 \uAC74\uB108\uB6F0\uACE0 \uB36E\uC5B4\uC4F0\uAE30 (CI / \uC2A4\uD06C\uB9BD\uD2B8\uC6A9)
|
|
52
53
|
|
|
53
54
|
${c.dim("--yes \uC548 \uBD99\uC774\uBA74 \uC778\uD130\uB799\uD2F0\uBE0C \uC14B\uC5C5 (\uC5B8\uC5B4 / \uBC31\uC5D4\uB4DC / \uB3C4\uAD6C / \uBAA8\uB378 \uD504\uB9AC\uC14B prompt).")}
|
|
@@ -130,6 +131,7 @@ ${c.bold("Options for init:")}
|
|
|
130
131
|
--no-template Skip chat-room UI install
|
|
131
132
|
--no-claude-md Skip CLAUDE.md edit
|
|
132
133
|
--no-learning-log Skip learning log seed
|
|
134
|
+
--no-install-deps Skip auto-install of peer deps (better-sqlite3 / @supabase/supabase-js)
|
|
133
135
|
--yes, -y Skip prompts and overwrite (non-interactive \u2014 for CI / scripts)
|
|
134
136
|
|
|
135
137
|
${c.dim("Run without --yes for an interactive setup (prompts for language, backend, etc.).")}
|
|
@@ -189,7 +191,7 @@ async function main() {
|
|
|
189
191
|
const command = args[0];
|
|
190
192
|
switch (command) {
|
|
191
193
|
case "init":
|
|
192
|
-
await (await import("./init-
|
|
194
|
+
await (await import("./init-C7AW2PST.js")).runInit(args.slice(1));
|
|
193
195
|
break;
|
|
194
196
|
case "status":
|
|
195
197
|
await (await import("./status-UE2TQQPU.js")).runStatus(args.slice(1));
|
|
@@ -201,7 +203,7 @@ async function main() {
|
|
|
201
203
|
await (await import("./adapt-VPWOYF6W.js")).runAdapt(args.slice(1));
|
|
202
204
|
break;
|
|
203
205
|
case "update":
|
|
204
|
-
await (await import("./update-
|
|
206
|
+
await (await import("./update-H6LOWP46.js")).runUpdate(args.slice(1));
|
|
205
207
|
break;
|
|
206
208
|
case "migrate":
|
|
207
209
|
await (await import("./migrate-PISZFX6C.js")).runMigrate(args.slice(1));
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/pm.ts
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
async function exists(p) {
|
|
6
|
+
try {
|
|
7
|
+
await fs.access(p);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function detectPM(target) {
|
|
14
|
+
try {
|
|
15
|
+
const raw = await fs.readFile(path.join(target, "package.json"), "utf8");
|
|
16
|
+
const pkg = JSON.parse(raw);
|
|
17
|
+
const pmField = pkg.packageManager ?? "";
|
|
18
|
+
if (pmField.startsWith("pnpm")) return "pnpm";
|
|
19
|
+
if (pmField.startsWith("yarn")) return "yarn";
|
|
20
|
+
if (pmField.startsWith("bun")) return "bun";
|
|
21
|
+
if (pmField.startsWith("npm")) return "npm";
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
if (await exists(path.join(target, "pnpm-lock.yaml"))) return "pnpm";
|
|
25
|
+
if (await exists(path.join(target, "yarn.lock"))) return "yarn";
|
|
26
|
+
if (await exists(path.join(target, "bun.lockb"))) return "bun";
|
|
27
|
+
if (await exists(path.join(target, "bun.lock"))) return "bun";
|
|
28
|
+
if (await exists(path.join(target, "package-lock.json"))) return "npm";
|
|
29
|
+
return "npm";
|
|
30
|
+
}
|
|
31
|
+
async function readDeps(target) {
|
|
32
|
+
try {
|
|
33
|
+
const raw = await fs.readFile(path.join(target, "package.json"), "utf8");
|
|
34
|
+
const pkg = JSON.parse(raw);
|
|
35
|
+
return {
|
|
36
|
+
...pkg.dependencies ?? {},
|
|
37
|
+
...pkg.devDependencies ?? {},
|
|
38
|
+
...pkg.peerDependencies ?? {}
|
|
39
|
+
};
|
|
40
|
+
} catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function installDeps(target, pm, deps) {
|
|
45
|
+
if (deps.length === 0) return { ok: true, cmd: "(no deps to install)" };
|
|
46
|
+
const prod = deps.filter((d) => !d.dev).map((d) => d.name);
|
|
47
|
+
const dev = deps.filter((d) => d.dev).map((d) => d.name);
|
|
48
|
+
const cmds = [];
|
|
49
|
+
if (prod.length > 0) cmds.push(buildAddCmd(pm, prod, false));
|
|
50
|
+
if (dev.length > 0) cmds.push(buildAddCmd(pm, dev, true));
|
|
51
|
+
for (const cmd of cmds) {
|
|
52
|
+
const result = await runCmd(cmd, target);
|
|
53
|
+
if (!result.ok) return { ok: false, cmd, err: result.err };
|
|
54
|
+
}
|
|
55
|
+
return { ok: true, cmd: cmds.join(" && ") };
|
|
56
|
+
}
|
|
57
|
+
function buildAddCmd(pm, names, dev) {
|
|
58
|
+
const list = names.join(" ");
|
|
59
|
+
switch (pm) {
|
|
60
|
+
case "pnpm":
|
|
61
|
+
return dev ? `pnpm add -D ${list}` : `pnpm add ${list}`;
|
|
62
|
+
case "yarn":
|
|
63
|
+
return dev ? `yarn add -D ${list}` : `yarn add ${list}`;
|
|
64
|
+
case "bun":
|
|
65
|
+
return dev ? `bun add -d ${list}` : `bun add ${list}`;
|
|
66
|
+
case "npm":
|
|
67
|
+
return dev ? `npm i -D ${list}` : `npm i ${list}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function runCmd(cmd, cwd) {
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
const parts = cmd.split(" ");
|
|
73
|
+
const bin = parts[0];
|
|
74
|
+
const args = parts.slice(1);
|
|
75
|
+
const proc = spawn(bin, args, {
|
|
76
|
+
cwd,
|
|
77
|
+
stdio: "inherit",
|
|
78
|
+
// shell:true on Windows so .cmd shims (npm.cmd, pnpm.cmd) resolve correctly.
|
|
79
|
+
shell: process.platform === "win32"
|
|
80
|
+
});
|
|
81
|
+
proc.on("error", (err) => resolve({ ok: false, err: err.message }));
|
|
82
|
+
proc.on("close", (code) => resolve({
|
|
83
|
+
ok: code === 0,
|
|
84
|
+
err: code === 0 ? void 0 : `exit code ${code}`
|
|
85
|
+
}));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
buildAddCmd,
|
|
90
|
+
detectPM,
|
|
91
|
+
installDeps,
|
|
92
|
+
readDeps
|
|
93
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-bujang",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Install the Harness-Bujang multi-agent harness into any project — Director, 7 specialist teams, real-time chat-room UI. Korean and English personas. Works with Claude Code, Cursor, Cline, Aider, or any tool that reads .claude/agents/.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|