harness-bujang 0.5.3 β†’ 0.5.4

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
@@ -36,22 +36,69 @@ npx harness-bujang update
36
36
  > you've added to existing agent files. `CLAUDE.md` and
37
37
  > `docs/AGENT_LEARNING_LOG.md` are never touched by any of the three commands.
38
38
 
39
- ### See the chat-room β€” any stack
39
+ ### πŸ’¬ Open the chat-room β€” two ways (natural language recommended)
40
40
 
41
+ After install, just **say it in plain language** inside Claude Code β€” the Director will spawn the viewer in the background:
42
+
43
+ ```
44
+ "Director, open the chat room"
45
+ "λΆ€μž₯λ‹˜ 톑방 μ—΄μ–΄μ£Όμ„Έμš”"
46
+ "open chat" / "show the chat"
47
+ ```
48
+
49
+ β†’ The Director auto-runs `npx harness-bujang chat` (background) β†’ browser opens β†’ http://localhost:7777 KakaoTalk-style room.
50
+
51
+ To close, also natural language:
52
+ ```
53
+ "close the chat room"
54
+ "톑방 λ‹«μ•„μ€˜"
55
+ ```
56
+
57
+ Manual command (works on any stack β€” Next.js, Rails, Django, Express, …):
41
58
  ```bash
42
- # Standalone viewer (works on Next.js, Rails, Django, Express, …) β€” no setup
43
59
  npx harness-bujang chat
44
- # β†’ opens http://localhost:7777 in your browser
45
60
  ```
46
61
 
47
- The standalone viewer reads `.harness/chat.db` directly, so it works on any
48
- project that uses the SQLite chat backend (the default). For projects that have
49
- not posted any messages yet, pass `--create` to bootstrap an empty DB and seed:
62
+ β†’ Reads `.harness/chat.db` directly. From 0.5.3, the DB is auto-created on first run if missing.
50
63
 
51
- ```bash
52
- npx harness-bujang chat --create
64
+ ### 🎬 How to use it β€” call by name
65
+
66
+ After install, the harness runs **inside Claude Code** by addressing personas:
67
+
68
+ ```
69
+ "Director, please add a refund API"
70
+ "λΆ€μž₯λ‹˜, 결제 ν™˜λΆˆ API λ§Œλ“€μ–΄μ£Όμ„Έμš”"
71
+ ↓ Director persona
72
+ β”œβ”€ Pre-confirm: "Plan to invoke dev-team + security-team + db-guard. Proceed?"
73
+ β”œβ”€ Principal OK β†’ parallel team dispatch (per mapping table)
74
+ β”œβ”€ Each step β†’ chat-room INSERT (live in viewer)
75
+ └─ Consolidated principal report
76
+
77
+ "Cofounder, is our BM viable?"
78
+ "κ³΅λ™λŒ€ν‘œ, 우리 BM μ΄λŒ€λ‘œ 가도 될까?"
79
+ ↓ Cofounder persona (peer, not subordinate)
80
+ β”œβ”€ Equal debate + push-back
81
+ β”œβ”€ Calls consultant / research-team / analysis-team if data needed
82
+ └─ Pushes the decision
53
83
  ```
54
84
 
85
+ **Calling rules**:
86
+
87
+ | Trigger | What happens |
88
+ |---------|--------------|
89
+ | **"Director, ..."** / "λΆ€μž₯λ‹˜, ..." | Director persona β€” pre-confirm, mapping, chat INSERT, consolidated report |
90
+ | **"Cofounder, ..."** / "κ³΅λ™λŒ€ν‘œ, ..." | Cofounder persona β€” peer debate / strategy / decision push |
91
+ | **plain "..."** (no name) | Plain Claude β€” knows harness rules but skips the full workflow |
92
+ | **"dev-team, ..."** directly | ❌ Won't work β€” the 16 teams are dispatched **by** Director / Cofounder, not addressed directly by the principal |
93
+
94
+ > πŸ’‘ **For the full workflow (pre-confirm + mapping + chat + consolidated report), name the persona.** For quick one-offs, plain Claude is fine.
95
+
96
+ Natural-language triggers:
97
+ - Open chat: `"λΆ€μž₯λ‹˜ 톑방 μ—΄μ–΄μ£Όμ„Έμš”"` / `"open the chat room"`
98
+ - Code review: `"Director, please review the PR"` / `"λΆ€μž₯λ‹˜ PR 리뷰 λΆ€νƒλ“œλ¦½λ‹ˆλ‹€"`
99
+ - Onboard team: `"Director, hire a marketing team"` / `"λΆ€μž₯λ‹˜ λ§ˆμΌ€νŒ…νŒ€ μ±„μš©ν•΄μ£Όμ„Έμš”"`
100
+ - Strategy: `"Cofounder, what do you think?"` / `"κ³΅λ™λŒ€ν‘œ 의견 μ’€"`
101
+
55
102
  ## What it does
56
103
 
57
104
  1. **Scans** the project β€” framework (Next.js / SvelteKit / Astro / Rails / Django / …), language, DB (Supabase / Prisma / Drizzle / TypeORM), UI lib, payment integration, GitHub user.
package/dist/index.js CHANGED
@@ -400,9 +400,30 @@ async function runInit(args) {
400
400
  if (scan.framework.startsWith("Next.js") && opts.installTemplate) {
401
401
  console.log(` ${c.cyan("3.")} Watch ${c.bold(context.ADMIN_HARNESS_ROUTE)} for live updates (after env setup)`);
402
402
  } else {
403
- console.log(` ${c.cyan("3.")} Watch the chat room: ${c.bold("npx harness-bujang chat --create")} ${c.dim("\u2192 http://localhost:7777")}`);
403
+ console.log(` ${c.cyan("3.")} Watch the chat room: ${c.bold("npx harness-bujang chat")} ${c.dim("\u2192 http://localhost:7777")}`);
404
404
  }
405
405
  console.log();
406
+ printRestartReminder(opts.lang);
407
+ }
408
+ function printRestartReminder(lang) {
409
+ const ko = lang === "ko";
410
+ const line = (s) => ` ${c.dim("\u2502")} ${s}`;
411
+ const top = ` ${c.dim("\u256D" + "\u2500".repeat(64) + "\u256E")}`;
412
+ const bot = ` ${c.dim("\u2570" + "\u2500".repeat(64) + "\u256F")}`;
413
+ console.log(top);
414
+ console.log(line(ko ? c.bold(c.yellow("\u26A0\uFE0F Claude Code \uAC00 \uC774\uBBF8 \uB5A0 \uC788\uB2E4\uBA74 \uC5D0\uC774\uC804\uD2B8 \uC7AC\uB85C\uB4DC \uD544\uC694!")) : c.bold(c.yellow("\u26A0\uFE0F Claude Code already running? Reload agents!"))));
415
+ console.log(line(""));
416
+ console.log(line(ko ? "\uC5D0\uC774\uC804\uD2B8 \uB4F1\uB85D\uC740 \uC138\uC158 \uC2DC\uC791 \uC2DC\uC810\uC5D0\uB9CC \uC77C\uC5B4\uB098\uBBC0\uB85C \uC0C8\uB85C \uAE50 \uD300\uC774" : "Agents are registered at session start only, so the new teams"));
417
+ console.log(line(ko ? "\uBC14\uB85C \uC778\uC2DD\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB2E4\uC74C \uC911 \uD55C \uAC00\uC9C0\uB97C \uD574\uC8FC\uC138\uC694:" : "won't be visible yet. Pick one:"));
418
+ console.log(line(""));
419
+ console.log(line(` ${c.green("A.")} ${c.bold(ko ? "Claude Code \uC548\uC5D0\uC11C: " : "Inside Claude Code: ")}${c.cyan("/agents")}`));
420
+ console.log(line(` ${c.dim(ko ? "\u2192 \uC5D0\uC774\uC804\uD2B8 \uBA54\uB274\uC5D0\uC11C \uC7AC\uB85C\uB4DC" : "\u2192 open the agent menu / refresh")}`));
421
+ console.log(line(""));
422
+ console.log(line(` ${c.green("B.")} ${c.bold(ko ? "\uC548\uB418\uBA74 Claude Code \uC644\uC804 \uC885\uB8CC \u2192 \uAC19\uC740 \uD3F4\uB354\uC5D0\uC11C \uB2E4\uC2DC \uC2DC\uC791" : "Or fully restart Claude Code in this folder")}`));
423
+ console.log(line(""));
424
+ console.log(line(c.dim(ko ? "/clear \uB294 \uCEE8\uD14D\uC2A4\uD2B8\uB9CC \uBE44\uC6C1\uB2C8\uB2E4 \u2014 \uC5D0\uC774\uC804\uD2B8 \uC7AC\uB4F1\uB85D \uC548 \uB428" : "/clear only wipes context \u2014 it does NOT re-register agents")));
425
+ console.log(bot);
426
+ console.log();
406
427
  }
407
428
  async function promptInteractive(opts, scan) {
408
429
  const lang = await select({
@@ -1937,6 +1958,7 @@ async function runUpdate(args) {
1937
1958
  console.log(" To overwrite everything (e.g. for a clean reset), use instead:");
1938
1959
  console.log(` ${c6.cyan("npx harness-bujang init --yes")}`);
1939
1960
  console.log();
1961
+ printRestartReminder(opts.lang);
1940
1962
  }
1941
1963
  async function resolveAssetPaths2() {
1942
1964
  const packaged = path7.resolve(__dirname3, "..", "templates");
@@ -2149,7 +2171,7 @@ async function main() {
2149
2171
  break;
2150
2172
  case "--version":
2151
2173
  case "-v":
2152
- console.log("0.5.2");
2174
+ console.log("0.5.4");
2153
2175
  break;
2154
2176
  case "--help":
2155
2177
  case "-h":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harness-bujang",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
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",
@@ -28,7 +28,7 @@ export async function GET(request: NextRequest) {
28
28
  const before = searchParams.get('before') ?? undefined;
29
29
  const limit = parseInt(searchParams.get('limit') ?? '50', 10);
30
30
 
31
- const db = getHarnessDb();
31
+ const db = await getHarnessDb();
32
32
  const messages = before ? await db.list({ before, limit }) : await db.list({ days });
33
33
 
34
34
  return NextResponse.json({ data: messages });
@@ -81,7 +81,7 @@ export async function POST(request: NextRequest) {
81
81
  from = trimmed;
82
82
  }
83
83
 
84
- const db = getHarnessDb();
84
+ const db = await getHarnessDb();
85
85
 
86
86
  let msgId = id;
87
87
  if (!msgId) {
@@ -22,7 +22,7 @@ export async function POST(request: NextRequest) {
22
22
  return NextResponse.json({ error: 'message is required' }, { status: 400 });
23
23
  }
24
24
 
25
- const db = getHarnessDb();
25
+ const db = await getHarnessDb();
26
26
  const total = await db.count();
27
27
 
28
28
  const msg: ChatMessage = {
@@ -5,20 +5,30 @@
5
5
  // - "supabase" β€” cloud Postgres via Supabase, prod-ready
6
6
  //
7
7
  // To switch later, run `bujang migrate --to=supabase` (or back to sqlite).
8
+ //
9
+ // IMPORTANT: adapters are loaded with **dynamic `import()`** so that a project
10
+ // using only the SQLite backend doesn't pay for `@supabase/supabase-js` at
11
+ // bundle / install time, and vice versa. (Static imports here would force
12
+ // Next.js / webpack / turbopack to compile both adapters into every chunk
13
+ // that touches the DB β€” discovered in nextjs-e2e-test against a sqlite-only
14
+ // install where supabase-js wasn't installed and the build failed.)
8
15
 
9
16
  import type { HarnessDb, HarnessDbMode } from './types';
10
- import { createSqliteAdapter } from './sqlite';
11
- import { createSupabaseAdapter } from './supabase';
12
17
 
13
18
  export type { HarnessDb, ChatMessage, ListOptions, HarnessDbMode } from './types';
14
- export { createSqliteAdapter, createSupabaseAdapter };
15
19
 
16
20
  let _instance: HarnessDb | null = null;
21
+ let _pending: Promise<HarnessDb> | null = null;
17
22
 
18
- export function getHarnessDb(): HarnessDb {
19
- if (_instance) return _instance;
20
- _instance = createAdapter(currentMode());
21
- return _instance;
23
+ export function getHarnessDb(): Promise<HarnessDb> {
24
+ if (_instance) return Promise.resolve(_instance);
25
+ if (_pending) return _pending;
26
+ _pending = createAdapter(currentMode()).then((db) => {
27
+ _instance = db;
28
+ _pending = null;
29
+ return db;
30
+ });
31
+ return _pending;
22
32
  }
23
33
 
24
34
  export function currentMode(): HarnessDbMode {
@@ -29,11 +39,13 @@ export function currentMode(): HarnessDbMode {
29
39
  );
30
40
  }
31
41
 
32
- export function createAdapter(mode: HarnessDbMode): HarnessDb {
33
- switch (mode) {
34
- case 'sqlite': return createSqliteAdapter();
35
- case 'supabase': return createSupabaseAdapter();
42
+ export async function createAdapter(mode: HarnessDbMode): Promise<HarnessDb> {
43
+ if (mode === 'sqlite') {
44
+ const { createSqliteAdapter } = await import('./sqlite');
45
+ return createSqliteAdapter();
36
46
  }
47
+ const { createSupabaseAdapter } = await import('./supabase');
48
+ return createSupabaseAdapter();
37
49
  }
38
50
 
39
51
  /**
@@ -42,4 +54,5 @@ export function createAdapter(mode: HarnessDbMode): HarnessDb {
42
54
  */
43
55
  export function resetHarnessDb(): void {
44
56
  _instance = null;
57
+ _pending = null;
45
58
  }