harness-bujang 0.5.5 → 0.5.7
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/dist/index.js
CHANGED
|
@@ -925,9 +925,8 @@ console.log(' \u2713 Transferred ' + total + ' messages');
|
|
|
925
925
|
import * as http from "http";
|
|
926
926
|
import * as path5 from "path";
|
|
927
927
|
import * as fs5 from "fs";
|
|
928
|
-
import {
|
|
929
|
-
import
|
|
930
|
-
var execFileP = promisify(execFile);
|
|
928
|
+
import { spawn } from "child_process";
|
|
929
|
+
import Database from "better-sqlite3";
|
|
931
930
|
var c4 = {
|
|
932
931
|
bold: (s) => `\x1B[1m${s}\x1B[22m`,
|
|
933
932
|
dim: (s) => `\x1B[2m${s}\x1B[22m`,
|
|
@@ -938,34 +937,37 @@ var c4 = {
|
|
|
938
937
|
};
|
|
939
938
|
async function runChat(args) {
|
|
940
939
|
const opts = parseArgs3(args);
|
|
940
|
+
const dbPath = resolveDbPath(opts.target);
|
|
941
|
+
const dbIsNew = !fs5.existsSync(dbPath);
|
|
942
|
+
if (dbIsNew) fs5.mkdirSync(path5.dirname(dbPath), { recursive: true });
|
|
943
|
+
let db;
|
|
941
944
|
try {
|
|
942
|
-
|
|
943
|
-
} catch {
|
|
945
|
+
db = new Database(dbPath);
|
|
946
|
+
} catch (err) {
|
|
944
947
|
console.log();
|
|
945
|
-
console.log(c4.red("\u2716
|
|
948
|
+
console.log(c4.red("\u2716 Failed to open chat DB at " + dbPath));
|
|
949
|
+
console.log(" " + c4.dim(String(err)));
|
|
946
950
|
console.log();
|
|
947
|
-
console.log("
|
|
948
|
-
console.log("
|
|
949
|
-
console.log(" Fedora: " + c4.bold("sudo dnf install sqlite"));
|
|
950
|
-
console.log(" Windows: https://www.sqlite.org/download.html (sqlite-tools-win-x64)");
|
|
951
|
+
console.log(" This usually means better-sqlite3 could not load its native binding.");
|
|
952
|
+
console.log(" Try " + c4.bold("npm i -g harness-bujang@latest") + " to fetch a fresh prebuild.");
|
|
951
953
|
console.log();
|
|
952
954
|
process.exitCode = 1;
|
|
953
955
|
return;
|
|
954
956
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
await runSql(dbPath, SCHEMA_SQL);
|
|
957
|
+
db.pragma("journal_mode = WAL");
|
|
958
|
+
db.exec(SCHEMA_SQL);
|
|
959
|
+
if (dbIsNew) {
|
|
959
960
|
const seedId = `seed-${Date.now()}`;
|
|
960
|
-
|
|
961
|
-
dbPath,
|
|
961
|
+
db.prepare(
|
|
962
962
|
`INSERT INTO harness_messages (id, "from", "to", type, message, severity)
|
|
963
|
-
VALUES (
|
|
964
|
-
);
|
|
963
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
964
|
+
).run(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");
|
|
965
965
|
console.log(c4.dim(` created empty chat DB at ${dbPath}`));
|
|
966
|
-
} else {
|
|
967
|
-
await runSql(dbPath, SCHEMA_SQL);
|
|
968
966
|
}
|
|
967
|
+
const insertStmt = db.prepare(
|
|
968
|
+
`INSERT INTO harness_messages (id, "from", "to", type, message, severity)
|
|
969
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
970
|
+
);
|
|
969
971
|
const port = await findOpenPort(opts.port);
|
|
970
972
|
const server = http.createServer(async (req, res) => {
|
|
971
973
|
const url2 = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
@@ -977,7 +979,7 @@ async function runChat(args) {
|
|
|
977
979
|
if (req.method === "GET" && url2.pathname === "/api/messages") {
|
|
978
980
|
const days = parseInt(url2.searchParams.get("days") ?? "7", 10);
|
|
979
981
|
try {
|
|
980
|
-
const rows =
|
|
982
|
+
const rows = readMessages(db, days);
|
|
981
983
|
res.writeHead(200, { "content-type": "application/json" });
|
|
982
984
|
res.end(JSON.stringify({ data: rows }));
|
|
983
985
|
} catch (err) {
|
|
@@ -1001,11 +1003,7 @@ async function runChat(args) {
|
|
|
1001
1003
|
res.end(JSON.stringify({ error: "message is required" }));
|
|
1002
1004
|
return;
|
|
1003
1005
|
}
|
|
1004
|
-
|
|
1005
|
-
dbPath,
|
|
1006
|
-
`INSERT INTO harness_messages (id, "from", "to", type, message, severity)
|
|
1007
|
-
VALUES (${q(id)}, ${q(from)}, ${q(to)}, ${q(type)}, ${q(message)}, ${q(severity)});`
|
|
1008
|
-
);
|
|
1006
|
+
insertStmt.run(id, from, to, type, message, severity);
|
|
1009
1007
|
res.writeHead(200, { "content-type": "application/json" });
|
|
1010
1008
|
res.end(JSON.stringify({ data: { id } }));
|
|
1011
1009
|
} catch (err) {
|
|
@@ -1031,6 +1029,7 @@ async function runChat(args) {
|
|
|
1031
1029
|
console.log();
|
|
1032
1030
|
console.log(c4.dim(" bye \u{1F44B}"));
|
|
1033
1031
|
server.close();
|
|
1032
|
+
db.close();
|
|
1034
1033
|
process.exit(0);
|
|
1035
1034
|
});
|
|
1036
1035
|
}
|
|
@@ -1095,18 +1094,15 @@ function readBody(req) {
|
|
|
1095
1094
|
req.on("error", reject);
|
|
1096
1095
|
});
|
|
1097
1096
|
}
|
|
1098
|
-
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
});
|
|
1108
|
-
if (!stdout.trim()) return [];
|
|
1109
|
-
const raw = JSON.parse(stdout);
|
|
1097
|
+
function readMessages(db, days) {
|
|
1098
|
+
const safeDays = Math.max(1, days | 0);
|
|
1099
|
+
const stmt = db.prepare(
|
|
1100
|
+
`SELECT id, timestamp, "from" AS sender, "to" AS recipient, type, message, severity
|
|
1101
|
+
FROM harness_messages
|
|
1102
|
+
WHERE timestamp >= datetime('now', '-' || ? || ' day')
|
|
1103
|
+
ORDER BY timestamp ASC`
|
|
1104
|
+
);
|
|
1105
|
+
const raw = stmt.all(safeDays);
|
|
1110
1106
|
return raw.map((r) => ({
|
|
1111
1107
|
id: r.id,
|
|
1112
1108
|
timestamp: r.timestamp,
|
|
@@ -1117,12 +1113,6 @@ async function readMessages(dbPath, days) {
|
|
|
1117
1113
|
severity: r.severity
|
|
1118
1114
|
}));
|
|
1119
1115
|
}
|
|
1120
|
-
async function runSql(dbPath, sql) {
|
|
1121
|
-
await execFileP("sqlite3", [dbPath, sql], { maxBuffer: 1024 * 1024 });
|
|
1122
|
-
}
|
|
1123
|
-
function q(value) {
|
|
1124
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
1125
|
-
}
|
|
1126
1116
|
var SCHEMA_SQL = `
|
|
1127
1117
|
CREATE TABLE IF NOT EXISTS harness_messages (
|
|
1128
1118
|
id TEXT PRIMARY KEY,
|
|
@@ -1198,8 +1188,9 @@ const ROLES = {
|
|
|
1198
1188
|
};
|
|
1199
1189
|
|
|
1200
1190
|
const ROOMS = [
|
|
1201
|
-
// Top-level
|
|
1202
|
-
|
|
1191
|
+
// Top-level \u2014 kept narrow on purpose so the "smallest matching room wins"
|
|
1192
|
+
// filter routes director\u2192principal reports to \uB300\uD45C \uBCF4\uACE0 (not \uACF5\uB3D9\uB300\uD45C).
|
|
1193
|
+
{ id: '\uB300\uD45C\uB2D8', name: '\uB300\uD45C \uBCF4\uACE0', icon: '\u{1F454}', members: ['\uB300\uD45C\uB2D8', '\uBD80\uC7A5'] },
|
|
1203
1194
|
{ id: '\uACF5\uB3D9\uB300\uD45C', name: '\uACF5\uB3D9\uB300\uD45C', icon: '\u2B50', members: ['\uB300\uD45C\uB2D8', '\uACF5\uB3D9\uB300\uD45C', '\uBD80\uC7A5'] },
|
|
1204
1195
|
{ id: 'consultant', name: '\uCEE8\uC124\uD134\uD2B8', icon: '\u{1F91D}', members: ['consultant', '\uBD80\uC7A5'] },
|
|
1205
1196
|
// Engineering teams
|
|
@@ -2199,7 +2190,7 @@ async function main() {
|
|
|
2199
2190
|
break;
|
|
2200
2191
|
case "--version":
|
|
2201
2192
|
case "-v":
|
|
2202
|
-
console.log("0.5.
|
|
2193
|
+
console.log("0.5.6");
|
|
2203
2194
|
break;
|
|
2204
2195
|
case "--help":
|
|
2205
2196
|
case "-h":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-bujang",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
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",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"prepublishOnly": "npm run build"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
+
"@types/better-sqlite3": "^7.6.10",
|
|
50
51
|
"@types/node": "^20.11.0",
|
|
51
52
|
"tsup": "^8.3.0",
|
|
52
53
|
"tsx": "^4.19.0",
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"node": ">=20"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@inquirer/prompts": "^8.4.2"
|
|
60
|
+
"@inquirer/prompts": "^8.4.2",
|
|
61
|
+
"better-sqlite3": "^11.7.0"
|
|
60
62
|
}
|
|
61
63
|
}
|
|
@@ -51,8 +51,10 @@ const ROLES: Record<string, { icon: string; color: string; bg: string; label?: s
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
const ROOMS = [
|
|
54
|
-
// Top
|
|
55
|
-
|
|
54
|
+
// Top — kept narrow on purpose so director→principal reports route to 대표 보고
|
|
55
|
+
// (not 공동대표). The "smallest matching room wins" filter would otherwise
|
|
56
|
+
// funnel them to 공동대표 because that room also includes both 부장 and 대표님.
|
|
57
|
+
{ id: '대표님', name: '대표 보고', icon: '👔', members: ['대표님', '부장'] },
|
|
56
58
|
{ id: '공동대표', name: '공동대표', icon: '⭐', members: ['대표님', '공동대표', '부장'] },
|
|
57
59
|
{ id: 'consultant', name: '컨설턴트', icon: '🤝', members: ['consultant', '부장'] },
|
|
58
60
|
// Engineering
|