harness-bujang 0.5.1 → 0.5.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.
- package/README.md +59 -0
- package/dist/index.js +223 -55
- package/package.json +1 -1
- package/templates/agents/en/director.md +119 -476
- package/templates/agents/ko/director.md +108 -465
- package/templates/templates/en/CLAUDE.md.harness-section.template +94 -38
- package/templates/templates/ko/CLAUDE.md.harness-section.template +87 -31
package/README.md
CHANGED
|
@@ -18,8 +18,24 @@ npx harness-bujang init --lang=en
|
|
|
18
18
|
|
|
19
19
|
# Different folder, skip the chat-room UI
|
|
20
20
|
npx harness-bujang init --target=./my-app --no-template
|
|
21
|
+
|
|
22
|
+
# Upgrade an existing install — adds NEW team files only, never touches existing ones
|
|
23
|
+
npx harness-bujang update
|
|
21
24
|
```
|
|
22
25
|
|
|
26
|
+
> ## ⚠️ First-time vs upgrade — read this before re-running
|
|
27
|
+
>
|
|
28
|
+
> | Situation | Command | What it does |
|
|
29
|
+
> |-----------|---------|-------------|
|
|
30
|
+
> | **First install** (empty project) | `npx harness-bujang init` | Full install — director + 16 teams + cofounder |
|
|
31
|
+
> | **Pulling new version** (already installed) | `npx harness-bujang update` | **Adds only NEW files.** Existing agent files are never touched |
|
|
32
|
+
> | **Clean reset** (drop customizations) | `npx harness-bujang init --yes` | **Overwrites every agent file** ⚠️ |
|
|
33
|
+
>
|
|
34
|
+
> **If you've already been using harness-bujang, use `update`.** Running
|
|
35
|
+
> `init --yes` to upgrade will destroy any domain rules / learned customizations
|
|
36
|
+
> you've added to existing agent files. `CLAUDE.md` and
|
|
37
|
+
> `docs/AGENT_LEARNING_LOG.md` are never touched by any of the three commands.
|
|
38
|
+
|
|
23
39
|
### See the chat-room — any stack
|
|
24
40
|
|
|
25
41
|
```bash
|
|
@@ -72,6 +88,49 @@ npx harness-bujang status [path]
|
|
|
72
88
|
|
|
73
89
|
Verifies the install: agent files, `CLAUDE.md` section, learning log, chat-room UI. Counts unfilled `{{...}}` placeholders.
|
|
74
90
|
|
|
91
|
+
### `update`
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
npx harness-bujang update [options]
|
|
95
|
+
|
|
96
|
+
Options:
|
|
97
|
+
--target=<path> Project root (default: cwd)
|
|
98
|
+
--lang=<ko|en> Language for newly-added agents (default: ko)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Safe additive update.** Adds NEW agent files only. Existing files are NEVER touched.
|
|
102
|
+
|
|
103
|
+
Use this when you upgrade `harness-bujang` and want to pull in newly-introduced
|
|
104
|
+
team members (e.g. the cofounder persona in 0.5.1, or the content-production
|
|
105
|
+
teams in 0.5.0) without disturbing any local customizations.
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
$ npx harness-bujang update
|
|
109
|
+
|
|
110
|
+
🔄 Harness-Bujang update
|
|
111
|
+
📂 Checking .claude/agents/
|
|
112
|
+
= consultant.md (exists, kept as-is)
|
|
113
|
+
= dev-team.md (exists, kept as-is)
|
|
114
|
+
+ cofounder.md
|
|
115
|
+
+ image-team.md
|
|
116
|
+
...
|
|
117
|
+
|
|
118
|
+
📋 Summary
|
|
119
|
+
Added: 4 (new files only)
|
|
120
|
+
Kept: 14 (existing files untouched)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
> ⚠️ **`update` vs `init --yes`** — pick the right one:
|
|
124
|
+
>
|
|
125
|
+
> | Command | What happens |
|
|
126
|
+
> |---------|-------------|
|
|
127
|
+
> | `npx harness-bujang update` | **Safe.** Adds only new files. Custom edits preserved. |
|
|
128
|
+
> | `npx harness-bujang init --yes` | **Destructive.** Overwrites every agent file. Customizations lost. |
|
|
129
|
+
>
|
|
130
|
+
> Default to `update` for upgrades. Use `init --yes` only for a clean reset.
|
|
131
|
+
|
|
132
|
+
`CLAUDE.md` and `docs/AGENT_LEARNING_LOG.md` are **never** touched by either command.
|
|
133
|
+
|
|
75
134
|
### `chat`
|
|
76
135
|
|
|
77
136
|
```
|
package/dist/index.js
CHANGED
|
@@ -862,11 +862,11 @@ async function upsertEnvVar(envFile, key, value) {
|
|
|
862
862
|
}
|
|
863
863
|
async function confirm2(message) {
|
|
864
864
|
process.stdout.write(`${message} [y/N] `);
|
|
865
|
-
return new Promise((
|
|
865
|
+
return new Promise((resolve7) => {
|
|
866
866
|
process.stdin.setEncoding("utf8");
|
|
867
867
|
process.stdin.once("data", (chunk) => {
|
|
868
868
|
const ans = chunk.toString().trim().toLowerCase();
|
|
869
|
-
|
|
869
|
+
resolve7(ans === "y" || ans === "yes");
|
|
870
870
|
process.stdin.pause();
|
|
871
871
|
});
|
|
872
872
|
});
|
|
@@ -933,19 +933,6 @@ async function runChat(args) {
|
|
|
933
933
|
}
|
|
934
934
|
const dbPath = resolveDbPath(opts.target);
|
|
935
935
|
if (!fs5.existsSync(dbPath)) {
|
|
936
|
-
if (!opts.create) {
|
|
937
|
-
console.log();
|
|
938
|
-
console.log(c4.red(`\u2716 Chat DB not found at ${c4.bold(dbPath)}`));
|
|
939
|
-
console.log();
|
|
940
|
-
console.log(" This usually means the harness has not posted any messages yet,");
|
|
941
|
-
console.log(" or the project does not use the SQLite chat backend.");
|
|
942
|
-
console.log();
|
|
943
|
-
console.log(" Run with " + c4.bold("--create") + " to create an empty DB and schema:");
|
|
944
|
-
console.log(" " + c4.cyan("bujang chat --create"));
|
|
945
|
-
console.log();
|
|
946
|
-
process.exitCode = 1;
|
|
947
|
-
return;
|
|
948
|
-
}
|
|
949
936
|
fs5.mkdirSync(path5.dirname(dbPath), { recursive: true });
|
|
950
937
|
await runSql(dbPath, SCHEMA_SQL);
|
|
951
938
|
const seedId = `seed-${Date.now()}`;
|
|
@@ -954,7 +941,7 @@ async function runChat(args) {
|
|
|
954
941
|
`INSERT INTO harness_messages (id, "from", "to", type, message, severity)
|
|
955
942
|
VALUES ('${seedId}', '\uBD80\uC7A5', '\uB300\uD45C\uB2D8', 'info', '\uD1A1\uBC29\uC774 \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCCAB \uBA85\uB839\uC744 \uB0B4\uB824\uC8FC\uC138\uC694.', 'info');`
|
|
956
943
|
);
|
|
957
|
-
console.log(c4.dim(` created empty DB
|
|
944
|
+
console.log(c4.dim(` created empty chat DB at ${dbPath}`));
|
|
958
945
|
} else {
|
|
959
946
|
await runSql(dbPath, SCHEMA_SQL);
|
|
960
947
|
}
|
|
@@ -1009,7 +996,7 @@ async function runChat(args) {
|
|
|
1009
996
|
res.writeHead(404);
|
|
1010
997
|
res.end("not found");
|
|
1011
998
|
});
|
|
1012
|
-
await new Promise((
|
|
999
|
+
await new Promise((resolve7) => server.listen(port, "127.0.0.1", resolve7));
|
|
1013
1000
|
const url = `http://localhost:${port}`;
|
|
1014
1001
|
console.log();
|
|
1015
1002
|
console.log(c4.bold(c4.green("\u{1F7E2} \uD558\uB124\uC2A4 \uD1A1\uBC29 viewer")) + c4.dim(" \u2014 " + url));
|
|
@@ -1061,12 +1048,12 @@ async function findOpenPort(preferred) {
|
|
|
1061
1048
|
throw new Error(`Could not find a free port in range ${preferred}-${preferred + 19}`);
|
|
1062
1049
|
}
|
|
1063
1050
|
function portIsFree(port) {
|
|
1064
|
-
return new Promise((
|
|
1051
|
+
return new Promise((resolve7) => {
|
|
1065
1052
|
const tester = http.createServer();
|
|
1066
|
-
tester.once("error", () =>
|
|
1053
|
+
tester.once("error", () => resolve7(false));
|
|
1067
1054
|
tester.once("listening", () => {
|
|
1068
1055
|
tester.close();
|
|
1069
|
-
|
|
1056
|
+
resolve7(true);
|
|
1070
1057
|
});
|
|
1071
1058
|
tester.listen(port, "127.0.0.1");
|
|
1072
1059
|
});
|
|
@@ -1080,10 +1067,10 @@ function openBrowser(url) {
|
|
|
1080
1067
|
}
|
|
1081
1068
|
}
|
|
1082
1069
|
function readBody(req) {
|
|
1083
|
-
return new Promise((
|
|
1070
|
+
return new Promise((resolve7, reject) => {
|
|
1084
1071
|
const chunks = [];
|
|
1085
1072
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
1086
|
-
req.on("end", () =>
|
|
1073
|
+
req.on("end", () => resolve7(Buffer.concat(chunks).toString("utf8")));
|
|
1087
1074
|
req.on("error", reject);
|
|
1088
1075
|
});
|
|
1089
1076
|
}
|
|
@@ -1883,7 +1870,12 @@ async function exists5(p) {
|
|
|
1883
1870
|
}
|
|
1884
1871
|
}
|
|
1885
1872
|
|
|
1886
|
-
// src/
|
|
1873
|
+
// src/update.ts
|
|
1874
|
+
import * as fs7 from "fs/promises";
|
|
1875
|
+
import * as path7 from "path";
|
|
1876
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1877
|
+
var __filename3 = fileURLToPath2(import.meta.url);
|
|
1878
|
+
var __dirname3 = path7.dirname(__filename3);
|
|
1887
1879
|
var c6 = {
|
|
1888
1880
|
bold: (s) => `\x1B[1m${s}\x1B[22m`,
|
|
1889
1881
|
dim: (s) => `\x1B[2m${s}\x1B[22m`,
|
|
@@ -1892,18 +1884,184 @@ var c6 = {
|
|
|
1892
1884
|
yellow: (s) => `\x1B[33m${s}\x1B[39m`,
|
|
1893
1885
|
cyan: (s) => `\x1B[36m${s}\x1B[39m`
|
|
1894
1886
|
};
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1887
|
+
async function runUpdate(args) {
|
|
1888
|
+
const opts = parseArgs5(args);
|
|
1889
|
+
const assets = await resolveAssetPaths2();
|
|
1890
|
+
const agentsSrc = path7.join(assets.agents, opts.lang);
|
|
1891
|
+
const agentsDst = path7.join(opts.target, ".claude/agents");
|
|
1892
|
+
console.log();
|
|
1893
|
+
console.log(c6.bold("\u{1F504} Harness-Bujang update"));
|
|
1894
|
+
console.log(c6.dim(` Target: ${opts.target}`));
|
|
1895
|
+
console.log(c6.dim(` Language: ${opts.lang}`));
|
|
1896
|
+
console.log();
|
|
1897
|
+
if (!await exists6(agentsDst)) {
|
|
1898
|
+
console.log(c6.yellow("\u26A0 No .claude/agents/ directory found."));
|
|
1899
|
+
console.log();
|
|
1900
|
+
console.log(" This project has not been initialized yet. Run:");
|
|
1901
|
+
console.log(` ${c6.cyan("npx harness-bujang init")}`);
|
|
1902
|
+
console.log();
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
const scan = await scanProject(opts.target);
|
|
1906
|
+
const context = await buildContext(opts, scan);
|
|
1907
|
+
const agentFiles = (await fs7.readdir(agentsSrc)).filter((f) => f.endsWith(".md"));
|
|
1908
|
+
agentFiles.sort();
|
|
1909
|
+
const added = [];
|
|
1910
|
+
const kept = [];
|
|
1911
|
+
console.log(c6.bold("\u{1F4C2} Checking .claude/agents/"));
|
|
1912
|
+
for (const f of agentFiles) {
|
|
1913
|
+
const dst = path7.join(agentsDst, f);
|
|
1914
|
+
if (await exists6(dst)) {
|
|
1915
|
+
kept.push(f);
|
|
1916
|
+
console.log(` ${c6.dim("=")} ${f} ${c6.dim("(exists, kept as-is)")}`);
|
|
1917
|
+
continue;
|
|
1918
|
+
}
|
|
1919
|
+
const raw = await fs7.readFile(path7.join(agentsSrc, f), "utf8");
|
|
1920
|
+
await fs7.writeFile(dst, renderTemplate(raw, context));
|
|
1921
|
+
added.push(f);
|
|
1922
|
+
console.log(` ${c6.green("+")} ${f}`);
|
|
1923
|
+
}
|
|
1924
|
+
console.log();
|
|
1925
|
+
console.log(c6.bold("\u{1F4CB} Summary"));
|
|
1926
|
+
console.log(` ${c6.green("Added")}: ${added.length} ${added.length ? c6.dim("(new files only)") : ""}`);
|
|
1927
|
+
console.log(` ${c6.dim("Kept")}: ${kept.length} ${c6.dim("(existing files untouched)")}`);
|
|
1928
|
+
console.log();
|
|
1929
|
+
if (added.length === 0) {
|
|
1930
|
+
console.log(c6.dim(" Nothing new to install \u2014 your harness is already up to date."));
|
|
1931
|
+
console.log();
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
console.log(c6.bold(c6.green("\u2705 Update done.")));
|
|
1935
|
+
console.log();
|
|
1936
|
+
console.log(" Existing agent files were not modified. Your customizations are safe.");
|
|
1937
|
+
console.log(" To overwrite everything (e.g. for a clean reset), use instead:");
|
|
1938
|
+
console.log(` ${c6.cyan("npx harness-bujang init --yes")}`);
|
|
1939
|
+
console.log();
|
|
1940
|
+
}
|
|
1941
|
+
async function resolveAssetPaths2() {
|
|
1942
|
+
const packaged = path7.resolve(__dirname3, "..", "templates");
|
|
1943
|
+
if (await exists6(packaged)) {
|
|
1944
|
+
return {
|
|
1945
|
+
agents: path7.join(packaged, "agents"),
|
|
1946
|
+
templates: path7.join(packaged, "templates"),
|
|
1947
|
+
projectTemplate: path7.join(packaged, "project-template"),
|
|
1948
|
+
mode: "packaged"
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
const monorepoRoot = path7.resolve(__dirname3, "../../..");
|
|
1952
|
+
const sharedDir = path7.join(monorepoRoot, "shared");
|
|
1953
|
+
if (await exists6(sharedDir)) {
|
|
1954
|
+
return {
|
|
1955
|
+
agents: path7.join(sharedDir, "agents"),
|
|
1956
|
+
templates: path7.join(sharedDir, "templates"),
|
|
1957
|
+
projectTemplate: path7.join(monorepoRoot, "packages/template"),
|
|
1958
|
+
mode: "monorepo"
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
throw new Error(
|
|
1962
|
+
`Could not locate harness-bujang assets. Tried:
|
|
1963
|
+
- ${packaged}
|
|
1964
|
+
- ${sharedDir}
|
|
1965
|
+
`
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
async function buildContext(opts, scan) {
|
|
1969
|
+
return {
|
|
1970
|
+
PROJECT_PATH: opts.target,
|
|
1971
|
+
PROJECT_NAME: path7.basename(opts.target),
|
|
1972
|
+
PROJECT_CATEGORY: scan.framework.startsWith("Next.js") ? "Web application" : "Software project",
|
|
1973
|
+
DIFFERENTIATION: "(define your project differentiation here if relevant)",
|
|
1974
|
+
STACK_FRAMEWORK: scan.framework,
|
|
1975
|
+
STACK_LANGUAGE: scan.language,
|
|
1976
|
+
STACK_DB: scan.db,
|
|
1977
|
+
STACK_UI: scan.ui,
|
|
1978
|
+
STACK_PAYMENT: scan.payment,
|
|
1979
|
+
STACK_EXTRA: "(none)",
|
|
1980
|
+
HARNESS_TABLE: "harness_messages",
|
|
1981
|
+
ADMIN_HARNESS_ROUTE: "/admin/harness",
|
|
1982
|
+
LEARNING_LOG_PATH: "docs/AGENT_LEARNING_LOG.md",
|
|
1983
|
+
TASKS_TRACKER_GLOB: "docs/TASKS_*.md",
|
|
1984
|
+
BENCHMARK_DOC_PATH: "docs/BENCHMARK.md",
|
|
1985
|
+
GH_USER: scan.ghUser,
|
|
1986
|
+
BUILD_CMD: scan.buildCmd || "(no build script)",
|
|
1987
|
+
TYPECHECK_CMD: scan.typecheckCmd || "(no type-check command)",
|
|
1988
|
+
TEST_CMD: scan.testCmd || "(no tests configured)",
|
|
1989
|
+
E2E_CMD: scan.e2eCmd || "(no E2E setup)",
|
|
1990
|
+
DEV_URL: "http://localhost:3000",
|
|
1991
|
+
DB_TYPES_PATH: scan.dbTypesPath,
|
|
1992
|
+
DB_CLIENT_PATTERN: `Use the project's existing DB client convention. See ${scan.dbTypesPath}.`,
|
|
1993
|
+
KNOWN_SCHEMA_DRIFT: "(none documented yet)",
|
|
1994
|
+
COMMON_FK_HINTS: "(extract from your schema as you go)",
|
|
1995
|
+
ACCESS_POLICY_NOTES: "(document RLS / middleware as encountered)",
|
|
1996
|
+
MIGRATION_NAMING: "supabase/migrations/XXXXX_name.sql (or per-stack)",
|
|
1997
|
+
MIGRATION_APPLY_CMD: "supabase db push (or stack-specific)",
|
|
1998
|
+
ROUTE_GROUPS: scan.routeGroups,
|
|
1999
|
+
MIDDLEWARE_PATH: scan.middlewarePath,
|
|
2000
|
+
KEY_RELATIONSHIPS: "(document key entity relations as you go)",
|
|
2001
|
+
AUTH_GUARD_PATTERN: "(stack-specific)",
|
|
2002
|
+
ADMIN_GUARD_PATTERN: "(stack-specific)",
|
|
2003
|
+
API_RESPONSE_SHAPE: "{ data, error, message }",
|
|
2004
|
+
PRIMARY_COLOR: "#6366F1",
|
|
2005
|
+
FRAMEWORK_REVIEW_RULES: "",
|
|
2006
|
+
TEST_ACCOUNTS: "(define your test accounts here)",
|
|
2007
|
+
LEGAL_CONTEXT: "(no special legal context)",
|
|
2008
|
+
LANG_CODE: opts.lang,
|
|
2009
|
+
TODAY: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
2010
|
+
COMPLETED_DOCS_PATTERN: "docs/\uC644\uB8CC_*.md"
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
function parseArgs5(args) {
|
|
2014
|
+
const lang = getFlag5(args, "--lang") ?? "ko";
|
|
2015
|
+
if (!["ko", "en"].includes(lang)) {
|
|
2016
|
+
throw new Error(`--lang must be "ko" or "en", got "${lang}"`);
|
|
2017
|
+
}
|
|
2018
|
+
const targetRaw = getFlag5(args, "--target") ?? ".";
|
|
2019
|
+
return {
|
|
2020
|
+
target: path7.resolve(targetRaw),
|
|
2021
|
+
lang
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
function getFlag5(args, name) {
|
|
2025
|
+
for (const a of args) {
|
|
2026
|
+
if (a.startsWith(`${name}=`)) return a.slice(name.length + 1);
|
|
2027
|
+
}
|
|
2028
|
+
const idx = args.indexOf(name);
|
|
2029
|
+
if (idx >= 0 && idx + 1 < args.length && !args[idx + 1].startsWith("--")) {
|
|
2030
|
+
return args[idx + 1];
|
|
2031
|
+
}
|
|
2032
|
+
return void 0;
|
|
2033
|
+
}
|
|
2034
|
+
async function exists6(p) {
|
|
2035
|
+
try {
|
|
2036
|
+
await fs7.access(p);
|
|
2037
|
+
return true;
|
|
2038
|
+
} catch {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
1905
2042
|
|
|
1906
|
-
|
|
2043
|
+
// src/index.ts
|
|
2044
|
+
var c7 = {
|
|
2045
|
+
bold: (s) => `\x1B[1m${s}\x1B[22m`,
|
|
2046
|
+
dim: (s) => `\x1B[2m${s}\x1B[22m`,
|
|
2047
|
+
green: (s) => `\x1B[32m${s}\x1B[39m`,
|
|
2048
|
+
red: (s) => `\x1B[31m${s}\x1B[39m`,
|
|
2049
|
+
yellow: (s) => `\x1B[33m${s}\x1B[39m`,
|
|
2050
|
+
cyan: (s) => `\x1B[36m${s}\x1B[39m`
|
|
2051
|
+
};
|
|
2052
|
+
var HELP = `
|
|
2053
|
+
${c7.bold("harness-bujang")} \u2014 Korean-style multi-agent harness director for Claude Code
|
|
2054
|
+
${c7.dim("https://github.com/bjcho4141/harness-bujang")}
|
|
2055
|
+
|
|
2056
|
+
${c7.bold("Usage:")}
|
|
2057
|
+
npx harness-bujang ${c7.cyan("init")} [options] Install the harness into a project
|
|
2058
|
+
npx harness-bujang ${c7.cyan("update")} [options] Pull NEW agents only \u2014 existing files untouched
|
|
2059
|
+
npx harness-bujang ${c7.cyan("status")} [options] Verify the harness install
|
|
2060
|
+
npx harness-bujang ${c7.cyan("chat")} [options] Open the standalone chat-room viewer (any stack)
|
|
2061
|
+
npx harness-bujang ${c7.cyan("adapt")} --to=<cursor|cline|aider|codex|gemini|all> Convert .claude/agents/ for other tools
|
|
2062
|
+
npx harness-bujang ${c7.cyan("migrate")} --to=<sqlite|supabase> Move chat data between backends
|
|
2063
|
+
|
|
2064
|
+
${c7.bold("Options for init:")}
|
|
1907
2065
|
--lang=<ko|en> Agent language (default: ko \u2014 full \uBD80\uC7A5 persona)
|
|
1908
2066
|
--chat=<sqlite|supabase> Chat-room backend (default: sqlite \u2014 local file, no setup)
|
|
1909
2067
|
--commit-chat Don't gitignore .harness/ (for solo cross-machine sync via git)
|
|
@@ -1915,49 +2073,56 @@ ${c6.bold("Options for init:")}
|
|
|
1915
2073
|
--no-learning-log Skip learning log seed
|
|
1916
2074
|
--yes, -y Skip prompts and overwrite (non-interactive \u2014 for CI / scripts)
|
|
1917
2075
|
|
|
1918
|
-
${
|
|
2076
|
+
${c7.dim("Run without --yes for an interactive setup (prompts for language, backend, etc.).")}
|
|
1919
2077
|
|
|
1920
|
-
${
|
|
2078
|
+
${c7.bold("Options for chat:")}
|
|
1921
2079
|
--target=<path> Project root (default: cwd)
|
|
1922
2080
|
--port=<number> Preferred port (default: 7777, falls forward if busy)
|
|
1923
2081
|
--no-open Don't auto-open the browser
|
|
1924
2082
|
--create Create an empty chat DB + schema if none exists yet
|
|
1925
2083
|
|
|
1926
|
-
${
|
|
2084
|
+
${c7.bold("Options for adapt:")}
|
|
1927
2085
|
--to=<cursor|cline|aider|codex|gemini|all> Required \u2014 comma-separated list also OK
|
|
1928
2086
|
--target=<path> Project root (default: cwd)
|
|
1929
2087
|
--yes, -y Overwrite existing adapter files
|
|
1930
2088
|
|
|
1931
|
-
${
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
${
|
|
1936
|
-
${
|
|
2089
|
+
${c7.bold("Options for update:")}
|
|
2090
|
+
--target=<path> Project root (default: cwd)
|
|
2091
|
+
--lang=<ko|en> Language for newly-added agents (default: ko)
|
|
2092
|
+
|
|
2093
|
+
${c7.dim(" update only adds NEW agent files. Existing files are NEVER touched.")}
|
|
2094
|
+
${c7.dim(" For a clean overwrite (resets all agents), use: bujang init --yes")}
|
|
2095
|
+
|
|
2096
|
+
${c7.dim("Adapter targets:")}
|
|
2097
|
+
${c7.dim(" cursor \u2192 .cursor/rules/bujang-*.mdc (Cursor IDE)")}
|
|
2098
|
+
${c7.dim(" cline \u2192 .clinerules/bujang-*.md (Cline)")}
|
|
2099
|
+
${c7.dim(" aider \u2192 CONVENTIONS.md + .aider.conf.yml (Aider)")}
|
|
2100
|
+
${c7.dim(" codex \u2192 AGENTS.md (Codex CLI / Copilot Coding Agent / Cody)")}
|
|
2101
|
+
${c7.dim(" gemini \u2192 GEMINI.md + .gemini/styleguide.md (Antigravity / Gemini CLI / Code Assist)")}
|
|
1937
2102
|
|
|
1938
|
-
${
|
|
2103
|
+
${c7.bold("Options for migrate:")}
|
|
1939
2104
|
--to=<sqlite|supabase> Required \u2014 target backend
|
|
1940
2105
|
--target=<path> Project root (default: cwd)
|
|
1941
2106
|
--yes, -y Skip confirmation
|
|
1942
2107
|
|
|
1943
|
-
${
|
|
1944
|
-
${
|
|
2108
|
+
${c7.bold("Examples:")}
|
|
2109
|
+
${c7.dim("# Install Korean Bujang persona, SQLite chat (default \u2014 zero setup)")}
|
|
1945
2110
|
npx harness-bujang init --lang=ko
|
|
1946
2111
|
|
|
1947
|
-
${
|
|
2112
|
+
${c7.dim("# Open the standalone chat-room \u2014 works on ANY stack (Next.js, Rails, Django, \u2026)")}
|
|
1948
2113
|
npx harness-bujang chat
|
|
1949
|
-
${
|
|
2114
|
+
${c7.dim("# \u2192 opens http://localhost:7777 in your browser")}
|
|
1950
2115
|
|
|
1951
|
-
${
|
|
2116
|
+
${c7.dim("# Solo, multiple machines \u2014 sync chat history via git")}
|
|
1952
2117
|
npx harness-bujang init --commit-chat
|
|
1953
2118
|
|
|
1954
|
-
${
|
|
2119
|
+
${c7.dim("# Production project with team sharing \u2014 Supabase backend")}
|
|
1955
2120
|
npx harness-bujang init --chat=supabase
|
|
1956
2121
|
|
|
1957
|
-
${
|
|
2122
|
+
${c7.dim("# Started solo, now scaling up \u2014 promote to cloud")}
|
|
1958
2123
|
bujang migrate --to=supabase
|
|
1959
2124
|
|
|
1960
|
-
${
|
|
2125
|
+
${c7.dim("# Going back to solo / archive \u2014 pull cloud data into local SQLite")}
|
|
1961
2126
|
bujang migrate --to=sqlite
|
|
1962
2127
|
`;
|
|
1963
2128
|
async function main() {
|
|
@@ -1976,12 +2141,15 @@ async function main() {
|
|
|
1976
2141
|
case "adapt":
|
|
1977
2142
|
await runAdapt(args.slice(1));
|
|
1978
2143
|
break;
|
|
2144
|
+
case "update":
|
|
2145
|
+
await runUpdate(args.slice(1));
|
|
2146
|
+
break;
|
|
1979
2147
|
case "migrate":
|
|
1980
2148
|
await runMigrate(args.slice(1));
|
|
1981
2149
|
break;
|
|
1982
2150
|
case "--version":
|
|
1983
2151
|
case "-v":
|
|
1984
|
-
console.log("0.5.
|
|
2152
|
+
console.log("0.5.2");
|
|
1985
2153
|
break;
|
|
1986
2154
|
case "--help":
|
|
1987
2155
|
case "-h":
|
|
@@ -1989,13 +2157,13 @@ async function main() {
|
|
|
1989
2157
|
console.log(HELP);
|
|
1990
2158
|
break;
|
|
1991
2159
|
default:
|
|
1992
|
-
console.error(
|
|
2160
|
+
console.error(c7.red(`Unknown command: ${command}`));
|
|
1993
2161
|
console.log(HELP);
|
|
1994
2162
|
process.exit(1);
|
|
1995
2163
|
}
|
|
1996
2164
|
}
|
|
1997
2165
|
main().catch((err) => {
|
|
1998
|
-
console.error(
|
|
2166
|
+
console.error(c7.red(`
|
|
1999
2167
|
\u2716 ${err.message}`));
|
|
2000
2168
|
if (process.env.DEBUG) console.error(err.stack);
|
|
2001
2169
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-bujang",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.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",
|