harness-bujang 0.8.1 → 0.8.2

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
- printBackendInstructions(opts.chatBackend, opts.commitChat);
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}.`)
@@ -744,6 +755,7 @@ function parseArgs(args) {
744
755
  installTemplate: !args.includes("--no-template"),
745
756
  editClaudeMd: !args.includes("--no-claude-md"),
746
757
  seedLearningLog: !args.includes("--no-learning-log"),
758
+ installDeps: !args.includes("--no-install-deps"),
747
759
  yes: args.includes("--yes") || args.includes("-y"),
748
760
  adapters,
749
761
  modelMap,
@@ -822,21 +834,138 @@ ${toAdd.join("\n")}
822
834
  `;
823
835
  await fs2.writeFile(gitignorePath, existing + block);
824
836
  }
825
- function printBackendInstructions(backend, commitChat) {
837
+ async function ensurePeerDeps(target, backend) {
838
+ const { detectPM, readDeps, installDeps, buildAddCmd } = await import("./pm-DUCX56FQ.js");
839
+ const pm = await detectPM(target);
840
+ const existing = await readDeps(target);
841
+ const wanted = [];
826
842
  if (backend === "sqlite") {
827
- console.log(c.bold(c.cyan("\u{1F4CB} SQLite mode (default) \u2014 next steps:")));
843
+ if (!existing["better-sqlite3"]) wanted.push({ name: "better-sqlite3" });
844
+ if (!existing["@types/better-sqlite3"]) wanted.push({ name: "@types/better-sqlite3", dev: true });
845
+ } else {
846
+ if (!existing["@supabase/supabase-js"]) wanted.push({ name: "@supabase/supabase-js" });
847
+ }
848
+ if (wanted.length === 0) {
849
+ console.log(c.dim(`\u{1F4E6} Peer deps already installed (${backend}) \u2014 skipped`));
850
+ console.log();
851
+ return;
852
+ }
853
+ const summary = wanted.map((d) => d.name + (d.dev ? c.dim(" (dev)") : "")).join(", ");
854
+ console.log(c.bold(`\u{1F4E6} Auto-installing peer deps via ${pm}`));
855
+ console.log(c.dim(` ${summary}`));
856
+ console.log();
857
+ const result = await installDeps(target, pm, wanted);
858
+ if (result.ok) {
859
+ console.log(c.green(` \u2713 installed`));
860
+ } else {
861
+ console.log(c.yellow(` \u26A0 install failed: ${result.err ?? "unknown"}`));
862
+ console.log(c.dim(` \uC218\uB3D9\uC73C\uB85C \uC2E4\uD589\uD574\uC8FC\uC138\uC694 / Run manually:`));
863
+ const prod = wanted.filter((d) => !d.dev).map((d) => d.name);
864
+ const dev = wanted.filter((d) => d.dev).map((d) => d.name);
865
+ if (prod.length > 0) console.log(c.dim(` $ ${buildAddCmd(pm, prod, false)}`));
866
+ if (dev.length > 0) console.log(c.dim(` $ ${buildAddCmd(pm, dev, true)}`));
867
+ }
868
+ console.log();
869
+ }
870
+ async function patchNextConfig(target) {
871
+ const candidates = ["next.config.ts", "next.config.mjs", "next.config.js"];
872
+ let configPath = null;
873
+ for (const cand of candidates) {
874
+ if (await exists2(path2.join(target, cand))) {
875
+ configPath = path2.join(target, cand);
876
+ break;
877
+ }
878
+ }
879
+ if (!configPath) {
880
+ console.log(c.dim(`\u{1F4DD} next.config.{js,mjs,ts} not found \u2014 serverExternalPackages patch skipped`));
881
+ console.log();
882
+ return;
883
+ }
884
+ const filename = path2.basename(configPath);
885
+ const raw = await fs2.readFile(configPath, "utf8");
886
+ if (/serverExternalPackages\s*:\s*\[[^\]]*['"]better-sqlite3['"]/.test(raw)) {
887
+ console.log(c.dim(`\u{1F4DD} ${filename} \uC774\uBBF8 'better-sqlite3' \uB4F1\uB85D\uB428 \u2014 skipped`));
888
+ console.log();
889
+ return;
890
+ }
891
+ if (/serverExternalPackages\s*:\s*\[/.test(raw)) {
892
+ const patched = raw.replace(
893
+ /serverExternalPackages\s*:\s*\[/,
894
+ `serverExternalPackages: ['better-sqlite3', `
895
+ );
896
+ await fs2.writeFile(configPath, patched);
897
+ console.log(c.green(` \u2713 ${filename} \u2190 'better-sqlite3' added to serverExternalPackages`));
828
898
  console.log();
829
- console.log(` ${c.bold("1.")} Install the SQLite driver in your project:`);
830
- console.log(` ${c.dim("$")} ${c.bold("npm i better-sqlite3")}`);
831
- console.log(` ${c.dim("$")} ${c.bold("npm i -D @types/better-sqlite3")}`);
899
+ return;
900
+ }
901
+ const injectPattern = /((?:const\s+\w+(?:\s*:\s*[^=]+)?\s*=\s*|module\.exports\s*=\s*|export\s+default\s*)\{)/;
902
+ if (injectPattern.test(raw)) {
903
+ const patched = raw.replace(
904
+ injectPattern,
905
+ `$1
906
+ serverExternalPackages: ['better-sqlite3'],`
907
+ );
908
+ await fs2.writeFile(configPath, patched);
909
+ console.log(c.green(` \u2713 ${filename} \u2190 injected serverExternalPackages: ['better-sqlite3']`));
832
910
  console.log();
833
- console.log(` ${c.bold("2.")} (optional) Add to ${c.bold(".env.local")}:`);
911
+ return;
912
+ }
913
+ 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:`));
914
+ console.log(c.dim(` {`));
915
+ console.log(c.dim(` serverExternalPackages: ['better-sqlite3'],`));
916
+ console.log(c.dim(` // ...rest of config`));
917
+ console.log(c.dim(` }`));
918
+ console.log();
919
+ }
920
+ async function scaffoldEnvExample(target) {
921
+ const fp = path2.join(target, ".env.local.example");
922
+ let raw = "";
923
+ if (await exists2(fp)) raw = await fs2.readFile(fp, "utf8");
924
+ const HEADER = "# --- Harness-Bujang (Supabase mode) ---";
925
+ const KEYS = [
926
+ "HARNESS_DB=supabase",
927
+ "NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co",
928
+ "SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here",
929
+ "HARNESS_WRITE_SECRET=generate-with-openssl-rand-hex-32",
930
+ "SUPER_ADMIN_EMAILS=you@example.com"
931
+ ];
932
+ const missing = KEYS.filter((line) => {
933
+ const key = line.split("=")[0];
934
+ return !new RegExp(`^${key}=`, "m").test(raw);
935
+ });
936
+ if (missing.length === 0 && raw.includes(HEADER)) {
937
+ console.log(c.dim(`\u{1F4DD} .env.local.example \uC774\uBBF8 \uBAA8\uB4E0 Supabase \uD0A4 \uC788\uC74C \u2014 skipped`));
938
+ console.log();
939
+ return;
940
+ }
941
+ const sep = raw && !raw.endsWith("\n") ? "\n" : "";
942
+ const block = `${sep}
943
+ ${HEADER}
944
+ ${missing.join("\n")}
945
+ `;
946
+ await fs2.writeFile(fp, raw + block);
947
+ console.log(c.green(` \u2713 .env.local.example \u2190 Supabase \uD0A4 ${missing.length}\uAC1C \uCD94\uAC00 (placeholders)`));
948
+ 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.`));
949
+ console.log();
950
+ }
951
+ function printBackendInstructions(backend, commitChat, installedDeps) {
952
+ let n = 1;
953
+ if (backend === "sqlite") {
954
+ console.log(c.bold(c.cyan("\u{1F4CB} SQLite mode (default) \u2014 next steps:")));
955
+ console.log();
956
+ if (!installedDeps) {
957
+ console.log(` ${c.bold(`${n++}.`)} Install the SQLite driver in your project:`);
958
+ console.log(` ${c.dim("$")} ${c.bold("npm i better-sqlite3")}`);
959
+ console.log(` ${c.dim("$")} ${c.bold("npm i -D @types/better-sqlite3")}`);
960
+ console.log();
961
+ }
962
+ console.log(` ${c.bold(`${n++}.`)} (optional) Add to ${c.bold(".env.local")}:`);
834
963
  console.log(` ${c.dim("HARNESS_DB=sqlite # default \u2014 can be omitted")}`);
835
964
  console.log(` ${c.dim("HARNESS_SQLITE_PATH=./.harness/chat.db # default \u2014 can be omitted")}`);
836
965
  console.log(` ${c.bold("HARNESS_WRITE_SECRET=<random> # for bot/script writes")}`);
837
966
  console.log(` ${c.bold("SUPER_ADMIN_EMAILS=you@example.com # comma-separated")}`);
838
967
  console.log();
839
- console.log(` ${c.bold("3.")} Run your dev server and visit ${c.bold("/admin/harness")}.`);
968
+ console.log(` ${c.bold(`${n++}.`)} Run your dev server and visit ${c.bold("/admin/harness")}.`);
840
969
  console.log();
841
970
  if (commitChat) {
842
971
  console.log(c.dim(` \u{1F4E6} ${c.bold("--commit-chat")} mode: .harness/ NOT added to .gitignore.`));
@@ -850,21 +979,26 @@ function printBackendInstructions(backend, commitChat) {
850
979
  } else {
851
980
  console.log(c.bold(c.cyan("\u{1F4CB} Supabase mode \u2014 next steps:")));
852
981
  console.log();
853
- console.log(` ${c.bold("1.")} Apply the migrations to your Supabase project:`);
982
+ if (!installedDeps) {
983
+ console.log(` ${c.bold(`${n++}.`)} Install the Supabase client in your project:`);
984
+ console.log(` ${c.dim("$")} ${c.bold("npm i @supabase/supabase-js")}`);
985
+ console.log();
986
+ }
987
+ console.log(` ${c.bold(`${n++}.`)} Apply the migrations to your Supabase project:`);
854
988
  console.log(` ${c.dim("$")} ${c.bold("supabase db push")}`);
855
989
  console.log(` ${c.dim(" (or run them manually via psql / SQL editor)")}`);
856
990
  console.log();
857
- console.log(` ${c.bold("2.")} Add to ${c.bold(".env.local")}:`);
991
+ console.log(` ${c.bold(`${n++}.`)} Fill in the keys in ${c.bold(".env.local")} ${c.dim("(template scaffolded at .env.local.example)")}:`);
858
992
  console.log(` ${c.bold("HARNESS_DB=supabase")}`);
859
993
  console.log(` ${c.bold("NEXT_PUBLIC_SUPABASE_URL=...")}`);
860
994
  console.log(` ${c.bold("SUPABASE_SERVICE_ROLE_KEY=...")}`);
861
995
  console.log(` ${c.bold("HARNESS_WRITE_SECRET=<random>")}`);
862
996
  console.log(` ${c.bold("SUPER_ADMIN_EMAILS=you@example.com")}`);
863
997
  console.log();
864
- console.log(` ${c.bold("3.")} Implement ${c.bold("verifySuperAdmin()")} at ${c.bold("@/lib/utils/admin")}`);
998
+ console.log(` ${c.bold(`${n++}.`)} Implement ${c.bold("verifySuperAdmin()")} at ${c.bold("@/lib/utils/admin")}`);
865
999
  console.log(` (see ${c.dim("packages/template/README.md")} for an example).`);
866
1000
  console.log();
867
- console.log(` ${c.bold("4.")} Run your dev server and visit ${c.bold("/admin/harness")}.`);
1001
+ console.log(` ${c.bold(`${n++}.`)} Run your dev server and visit ${c.bold("/admin/harness")}.`);
868
1002
  }
869
1003
  console.log();
870
1004
  }
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-WAVPUHNL.js")).runInit(args.slice(1));
194
+ await (await import("./init-GKZGQDJM.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-HAGOKTNL.js")).runUpdate(args.slice(1));
206
+ await (await import("./update-KPWC4YNN.js")).runUpdate(args.slice(1));
205
207
  break;
206
208
  case "migrate":
207
209
  await (await import("./migrate-PISZFX6C.js")).runMigrate(args.slice(1));
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  printRestartReminder,
3
3
  runInit
4
- } from "./chunk-MKHLUAWE.js";
4
+ } from "./chunk-ONHWBZ3Z.js";
5
5
  import "./chunk-7DAHO2GN.js";
6
6
  export {
7
7
  printRestartReminder,
@@ -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
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  printRestartReminder,
3
3
  scanProject
4
- } from "./chunk-MKHLUAWE.js";
4
+ } from "./chunk-ONHWBZ3Z.js";
5
5
  import {
6
6
  renderTemplate
7
7
  } from "./chunk-7DAHO2GN.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-bujang",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
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",