harness-bujang 0.1.0 → 0.2.1

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/README.md CHANGED
@@ -7,13 +7,16 @@ Install the [Harness-Bujang](https://github.com/bjcho4141/harness-bujang) multi-
7
7
  ## Quick start
8
8
 
9
9
  ```bash
10
- # English agents (default), drop into the current directory
10
+ # Interactive setup prompts for language, backend, etc.
11
11
  npx harness-bujang init
12
12
 
13
+ # Non-interactive (CI / scripts) — accept all defaults
14
+ npx harness-bujang init --yes
15
+
13
16
  # Korean agents (full 부장 persona)
14
17
  npx harness-bujang init --lang=ko
15
18
 
16
- # Into a different folder, skip the chat-room UI
19
+ # Different folder, skip the chat-room UI
17
20
  npx harness-bujang init --target=./my-app --no-template
18
21
  ```
19
22
 
@@ -40,9 +43,11 @@ Options:
40
43
  --no-template Skip chat-room UI install
41
44
  --no-claude-md Skip CLAUDE.md edit
42
45
  --no-learning-log Skip learning log seed
43
- --yes, -y Overwrite existing files without asking
46
+ --yes, -y Skip prompts and overwrite (non-interactive — for CI / scripts)
44
47
  ```
45
48
 
49
+ When `--yes` is omitted and stdin is a TTY, the CLI prompts for language, chat backend, and (for Next.js projects) whether to install the chat-room UI.
50
+
46
51
  ### `status`
47
52
 
48
53
  ```
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  import * as fs2 from "fs/promises";
5
5
  import * as path2 from "path";
6
6
  import { fileURLToPath } from "url";
7
+ import { select, confirm } from "@inquirer/prompts";
7
8
 
8
9
  // src/scan.ts
9
10
  import * as fs from "fs/promises";
@@ -180,13 +181,11 @@ If installed via npm, try reinstalling. If running from source, run "npm run bui
180
181
  );
181
182
  }
182
183
  async function runInit(args) {
183
- const opts = parseArgs(args);
184
+ let opts = parseArgs(args);
184
185
  const assets = await resolveAssetPaths();
185
186
  console.log();
186
187
  console.log(c.bold("\u{1F4E6} Harness-Bujang init"));
187
188
  console.log(c.dim(` Target: ${opts.target}`));
188
- console.log(c.dim(` Language: ${opts.lang}`));
189
- console.log(c.dim(` Chat backend: ${opts.chatBackend}${opts.chatBackend === "sqlite" ? c.dim(" (default \u2014 local file)") : c.dim(" (cloud Postgres)")}`));
190
189
  console.log(c.dim(` Assets: ${assets.mode}`));
191
190
  console.log();
192
191
  if (!await exists2(opts.target)) {
@@ -201,6 +200,49 @@ async function runInit(args) {
201
200
  console.log(` Payment: ${scan.payment}`);
202
201
  console.log(` GitHub: ${scan.ghUser}`);
203
202
  console.log();
203
+ const interactive = !opts.yes && Boolean(process.stdin.isTTY);
204
+ if (interactive) {
205
+ try {
206
+ opts = await promptInteractive(opts, scan);
207
+ if (await isExistingInstall(opts.target)) {
208
+ const overwrite = await confirm({
209
+ message: "Existing harness install detected. Overwrite all files to apply your selections?",
210
+ default: false
211
+ });
212
+ if (overwrite) opts.yes = true;
213
+ }
214
+ } catch (err) {
215
+ if (err && typeof err === "object" && "name" in err && err.name === "ExitPromptError") {
216
+ console.log(c.dim(" (aborted)"));
217
+ return;
218
+ }
219
+ throw err;
220
+ }
221
+ }
222
+ console.log(c.bold("\u{1F4CB} Configuration"));
223
+ console.log(c.dim(` Language: ${opts.lang}`));
224
+ console.log(c.dim(` Chat backend: ${opts.chatBackend}${opts.chatBackend === "sqlite" ? " (local file)" : " (cloud Postgres)"}`));
225
+ if (scan.framework.startsWith("Next.js")) {
226
+ console.log(c.dim(` Chat-room UI: ${opts.installTemplate ? "install" : "skip"}`));
227
+ }
228
+ console.log(c.dim(` On conflict: ${opts.yes ? "overwrite" : "skip existing files"}`));
229
+ console.log();
230
+ if (interactive) {
231
+ try {
232
+ const proceed = await confirm({ message: "Proceed with these settings?", default: true });
233
+ if (!proceed) {
234
+ console.log(c.dim(" (aborted)"));
235
+ return;
236
+ }
237
+ console.log();
238
+ } catch (err) {
239
+ if (err && typeof err === "object" && "name" in err && err.name === "ExitPromptError") {
240
+ console.log(c.dim(" (aborted)"));
241
+ return;
242
+ }
243
+ throw err;
244
+ }
245
+ }
204
246
  const context = {
205
247
  PROJECT_PATH: opts.target,
206
248
  PROJECT_NAME: path2.basename(opts.target),
@@ -355,6 +397,32 @@ async function runInit(args) {
355
397
  console.log(` ${c.cyan("3.")} Watch ${c.bold(context.ADMIN_HARNESS_ROUTE)} for live updates (after env setup)`);
356
398
  console.log();
357
399
  }
400
+ async function promptInteractive(opts, scan) {
401
+ const lang = await select({
402
+ message: "Agent language",
403
+ choices: [
404
+ { name: "English", value: "en" },
405
+ { name: "Korean \u2014 full \uBD80\uC7A5 persona (\uD55C\uAD6D\uC5B4)", value: "ko" }
406
+ ],
407
+ default: opts.lang
408
+ });
409
+ const chatBackend = await select({
410
+ message: "Chat backend",
411
+ choices: [
412
+ { name: "SQLite \u2014 local file, zero setup (recommended)", value: "sqlite" },
413
+ { name: "Supabase \u2014 cloud Postgres for team sharing", value: "supabase" }
414
+ ],
415
+ default: opts.chatBackend
416
+ });
417
+ let installTemplate = opts.installTemplate;
418
+ if (scan.framework.startsWith("Next.js") && opts.installTemplate) {
419
+ installTemplate = await confirm({
420
+ message: "Install chat-room UI (Next.js admin route at /admin/harness)?",
421
+ default: true
422
+ });
423
+ }
424
+ return { ...opts, lang, chatBackend, installTemplate };
425
+ }
358
426
  function parseArgs(args) {
359
427
  const lang = getFlag(args, "--lang") ?? "en";
360
428
  if (!["ko", "en"].includes(lang)) {
@@ -388,6 +456,16 @@ function getFlag(args, name) {
388
456
  }
389
457
  return void 0;
390
458
  }
459
+ async function isExistingInstall(target) {
460
+ const probes = [
461
+ path2.join(target, ".claude/agents/director.md"),
462
+ path2.join(target, ".claude/agents/dev-team.md")
463
+ ];
464
+ for (const p of probes) {
465
+ if (await exists2(p)) return true;
466
+ }
467
+ return false;
468
+ }
391
469
  async function exists2(p) {
392
470
  try {
393
471
  await fs2.access(p);
@@ -663,7 +741,7 @@ Run "npx harness-bujang init" first.`
663
741
  }
664
742
  console.log();
665
743
  console.log(c3.dim("Pass --yes to skip this confirmation."));
666
- if (!await confirm("Continue?")) {
744
+ if (!await confirm2("Continue?")) {
667
745
  console.log(c3.dim("Aborted."));
668
746
  return;
669
747
  }
@@ -775,7 +853,7 @@ async function upsertEnvVar(envFile, key, value) {
775
853
  }
776
854
  await fs4.writeFile(envFile, content);
777
855
  }
778
- async function confirm(message) {
856
+ async function confirm2(message) {
779
857
  process.stdout.write(`${message} [y/N] `);
780
858
  return new Promise((resolve4) => {
781
859
  process.stdin.setEncoding("utf8");
@@ -843,7 +921,9 @@ ${c4.bold("Options for init:")}
843
921
  --no-template Skip chat-room UI install
844
922
  --no-claude-md Skip CLAUDE.md edit
845
923
  --no-learning-log Skip learning log seed
846
- --yes, -y Overwrite without asking
924
+ --yes, -y Skip prompts and overwrite (non-interactive \u2014 for CI / scripts)
925
+
926
+ ${c4.dim("Run without --yes for an interactive setup (prompts for language, backend, etc.).")}
847
927
 
848
928
  ${c4.bold("Options for migrate:")}
849
929
  --to=<sqlite|supabase> Required \u2014 target backend
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-bujang",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
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",
@@ -53,5 +53,8 @@
53
53
  },
54
54
  "engines": {
55
55
  "node": ">=20"
56
+ },
57
+ "dependencies": {
58
+ "@inquirer/prompts": "^8.4.2"
56
59
  }
57
60
  }