buildcrew 1.8.7 โ 1.9.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 +129 -1
- package/agents/architect.md +26 -0
- package/agents/browser-qa.md +29 -0
- package/agents/buildcrew.md +31 -3
- package/agents/canary-monitor.md +22 -0
- package/agents/coherence-auditor.md +347 -0
- package/agents/design-reviewer.md +36 -0
- package/agents/designer.md +29 -0
- package/agents/developer.md +34 -0
- package/agents/health-checker.md +23 -0
- package/agents/investigator.md +39 -0
- package/agents/planner.md +26 -0
- package/agents/qa-auditor.md +32 -0
- package/agents/qa-tester.md +29 -0
- package/agents/reviewer.md +35 -0
- package/agents/security-auditor.md +23 -0
- package/agents/shipper.md +23 -0
- package/agents/thinker.md +32 -0
- package/bin/hook.js +17 -0
- package/bin/setup.js +166 -7
- package/bin/watch.js +594 -0
- package/lib/hook.js +230 -0
- package/lib/install-hooks.js +165 -0
- package/package.json +7 -3
package/lib/hook.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code hook handler for buildcrew.
|
|
4
|
+
*
|
|
5
|
+
* Reads the CC hook JSON from stdin and does two things:
|
|
6
|
+
* 1. Prints a compact color-coded banner to the user's terminal so the
|
|
7
|
+
* agent lifecycle is visible inline with the CC output.
|
|
8
|
+
* 2. Appends the event to .claude/buildcrew/events.jsonl so
|
|
9
|
+
* `npx buildcrew watch` (or later tooling) can show a live view.
|
|
10
|
+
*
|
|
11
|
+
* Called as: node dashboard/hooks/emit.js <kind>
|
|
12
|
+
* Kinds: pre-agent | post-agent | file-written | user-prompt
|
|
13
|
+
* | subagent-stop | session-end
|
|
14
|
+
*
|
|
15
|
+
* MUST be fast and MUST silent-fail (hooks block Claude Code if they hang).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { openSync, writeSync, closeSync, mkdirSync, appendFileSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
|
|
21
|
+
const kind = process.argv[2];
|
|
22
|
+
const EVENTS_PATH = join(process.cwd(), ".claude", "buildcrew", "events.jsonl");
|
|
23
|
+
|
|
24
|
+
// ------------------------------------------------------------------
|
|
25
|
+
// Terminal banner output โ writes a compact styled line straight to
|
|
26
|
+
// /dev/tty so it appears in the user's CC terminal regardless of how
|
|
27
|
+
// the hook process's stdio is captured. NO_COLOR env disables ANSI.
|
|
28
|
+
// ------------------------------------------------------------------
|
|
29
|
+
const NO_COLOR = !!process.env.NO_COLOR || process.env.BUILDCREW_HOOK_QUIET === "1";
|
|
30
|
+
const C = NO_COLOR
|
|
31
|
+
? new Proxy({}, { get: () => "" })
|
|
32
|
+
: {
|
|
33
|
+
reset: "\x1b[0m",
|
|
34
|
+
bold: "\x1b[1m",
|
|
35
|
+
dim: "\x1b[2m",
|
|
36
|
+
red: "\x1b[31m",
|
|
37
|
+
green: "\x1b[32m",
|
|
38
|
+
gold: "\x1b[33m",
|
|
39
|
+
blue: "\x1b[34m",
|
|
40
|
+
mag: "\x1b[35m",
|
|
41
|
+
cyan: "\x1b[36m",
|
|
42
|
+
gray: "\x1b[90m",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const AGENT_EMOJI = {
|
|
46
|
+
buildcrew: "๐ฉ", planner: "๐", designer: "๐จ", developer: "๐ป",
|
|
47
|
+
"qa-tester": "๐งช", "browser-qa": "๐", reviewer: "๐ง", "health-checker": "๐ฉบ",
|
|
48
|
+
"security-auditor": "๐ก", "canary-monitor": "๐ค", shipper: "๐ข",
|
|
49
|
+
thinker: "๐ค", architect: "๐", "design-reviewer": "๐",
|
|
50
|
+
investigator: "๐ต", "qa-auditor": "โ",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function writeTTY(line) {
|
|
54
|
+
if (process.env.BUILDCREW_HOOK_QUIET === "1") return;
|
|
55
|
+
let fd;
|
|
56
|
+
try {
|
|
57
|
+
fd = openSync("/dev/tty", "w");
|
|
58
|
+
writeSync(fd, line + "\n");
|
|
59
|
+
} catch {
|
|
60
|
+
// No TTY (CI, pipe, etc.) โ fall back to stderr
|
|
61
|
+
try { process.stderr.write(line + "\n"); } catch {}
|
|
62
|
+
} finally {
|
|
63
|
+
if (fd !== undefined) try { closeSync(fd); } catch {}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function banner(kind, data) {
|
|
68
|
+
switch (kind) {
|
|
69
|
+
case "pre-agent": {
|
|
70
|
+
const agent = data?.tool_input?.subagent_type ?? "agent";
|
|
71
|
+
const icon = AGENT_EMOJI[agent] ?? "โ";
|
|
72
|
+
const prompt = truncate(data?.tool_input?.prompt ?? data?.tool_input?.description ?? "", 90);
|
|
73
|
+
return `${C.gold}โถ${C.reset} ${icon} ${C.bold}${agent}${C.reset} ${C.gray}ยท ${prompt}${C.reset}`;
|
|
74
|
+
}
|
|
75
|
+
case "post-agent": {
|
|
76
|
+
const agent = data?.tool_input?.subagent_type ?? "agent";
|
|
77
|
+
const icon = AGENT_EMOJI[agent] ?? "โ";
|
|
78
|
+
return `${C.green}โ${C.reset} ${icon} ${C.bold}${agent}${C.reset} ${C.gray}done${C.reset}`;
|
|
79
|
+
}
|
|
80
|
+
case "file-written": {
|
|
81
|
+
const p = data?.tool_input?.file_path;
|
|
82
|
+
if (!p) return null;
|
|
83
|
+
const rel = p.split("/").slice(-2).join("/");
|
|
84
|
+
return `${C.blue}๐${C.reset} ${rel} ${C.gray}(${data?.tool_name ?? "edit"})${C.reset}`;
|
|
85
|
+
}
|
|
86
|
+
case "session-end":
|
|
87
|
+
return `${C.mag}โผ${C.reset} ${C.dim}session ended${C.reset}`;
|
|
88
|
+
default:
|
|
89
|
+
return null; // user-prompt + subagent-stop are too noisy for terminal
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function main() {
|
|
94
|
+
if (!kind) process.exit(0);
|
|
95
|
+
|
|
96
|
+
// Read stdin (hooks send JSON on stdin)
|
|
97
|
+
let input = "";
|
|
98
|
+
try {
|
|
99
|
+
process.stdin.setEncoding("utf8");
|
|
100
|
+
for await (const chunk of process.stdin) input += chunk;
|
|
101
|
+
} catch {
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let data = {};
|
|
106
|
+
if (input.trim()) {
|
|
107
|
+
try { data = JSON.parse(input); } catch { /* ignore malformed */ }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Print styled banner to the user's terminal (this is the primary UX now โ
|
|
111
|
+
// dashboard HTTP broadcast is optional and only works when server is up).
|
|
112
|
+
try {
|
|
113
|
+
const line = banner(kind, data);
|
|
114
|
+
if (line) writeTTY(line);
|
|
115
|
+
} catch {}
|
|
116
|
+
|
|
117
|
+
const events = toEvents(kind, data);
|
|
118
|
+
if (!events.length) process.exit(0);
|
|
119
|
+
|
|
120
|
+
// Append events to the JSONL log โ silent-fail so hooks never block CC.
|
|
121
|
+
try {
|
|
122
|
+
mkdirSync(join(process.cwd(), ".claude", "buildcrew"), { recursive: true });
|
|
123
|
+
const lines = events
|
|
124
|
+
.map((e) => JSON.stringify({ ...e, at: e.at ?? new Date().toISOString() }))
|
|
125
|
+
.join("\n") + "\n";
|
|
126
|
+
appendFileSync(EVENTS_PATH, lines);
|
|
127
|
+
} catch {
|
|
128
|
+
// Disk full / permission โ silently ignore; banner already printed.
|
|
129
|
+
}
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function toEvents(kind, data) {
|
|
134
|
+
// Every event carries session_id so the dashboard can disambiguate
|
|
135
|
+
// concurrent Claude Code sessions running in the same project.
|
|
136
|
+
const sessionId = data?.session_id ?? "unknown";
|
|
137
|
+
|
|
138
|
+
switch (kind) {
|
|
139
|
+
case "pre-agent": {
|
|
140
|
+
const subagent = data?.tool_input?.subagent_type ?? "agent";
|
|
141
|
+
const prompt = truncate(data?.tool_input?.prompt ?? data?.tool_input?.description ?? "", 400);
|
|
142
|
+
return [{
|
|
143
|
+
type: "agent.dispatched",
|
|
144
|
+
agent: subagent, from: "buildcrew", prompt,
|
|
145
|
+
session_id: sessionId,
|
|
146
|
+
}];
|
|
147
|
+
}
|
|
148
|
+
case "post-agent": {
|
|
149
|
+
const subagent = data?.tool_input?.subagent_type ?? "agent";
|
|
150
|
+
const resp = data?.tool_response;
|
|
151
|
+
let summary = "";
|
|
152
|
+
if (typeof resp === "string") summary = resp.slice(0, 500);
|
|
153
|
+
else if (resp?.content?.[0]?.text) summary = String(resp.content[0].text).slice(0, 500);
|
|
154
|
+
return [{
|
|
155
|
+
type: "agent.completed",
|
|
156
|
+
agent: subagent, output_summary: summary,
|
|
157
|
+
session_id: sessionId,
|
|
158
|
+
}];
|
|
159
|
+
}
|
|
160
|
+
case "file-written": {
|
|
161
|
+
const path = data?.tool_input?.file_path;
|
|
162
|
+
if (!path) return [];
|
|
163
|
+
// CC hook payload doesn't include "current subagent" info. We default to
|
|
164
|
+
// "buildcrew" (team lead) so the dashboard HONESTLY reveals when the lead
|
|
165
|
+
// is writing files directly โ which is a pipeline violation per Feature
|
|
166
|
+
// mode rules. Dashboard's pipeline-integrity check warns on this.
|
|
167
|
+
return [{
|
|
168
|
+
type: "file.written",
|
|
169
|
+
agent: data?.agent ?? "buildcrew", path,
|
|
170
|
+
tool_name: data?.tool_name,
|
|
171
|
+
session_id: sessionId,
|
|
172
|
+
}];
|
|
173
|
+
}
|
|
174
|
+
case "user-prompt": {
|
|
175
|
+
const sid = sessionId === "unknown" ? `cc-${Date.now()}` : sessionId;
|
|
176
|
+
const events = [{
|
|
177
|
+
type: "session.start",
|
|
178
|
+
session_id: sid,
|
|
179
|
+
mode: "feature",
|
|
180
|
+
}];
|
|
181
|
+
// CC's @mention invocations don't fire PreToolUse:Agent โ they spawn
|
|
182
|
+
// subagents directly. Parse @<agent-name> from the prompt so the town
|
|
183
|
+
// shows a bubble when the user routes via @buildcrew (or any agent).
|
|
184
|
+
const prompt = String(data?.prompt ?? "");
|
|
185
|
+
const seen = new Set();
|
|
186
|
+
const mentionRe = /(?:^|\s)@([a-z][a-z0-9-]*)/gi;
|
|
187
|
+
let m;
|
|
188
|
+
while ((m = mentionRe.exec(prompt)) !== null) {
|
|
189
|
+
const name = m[1].toLowerCase();
|
|
190
|
+
if (seen.has(name)) continue;
|
|
191
|
+
seen.add(name);
|
|
192
|
+
events.push({
|
|
193
|
+
type: "agent.dispatched",
|
|
194
|
+
agent: name,
|
|
195
|
+
from: "user",
|
|
196
|
+
prompt: truncate(prompt, 400),
|
|
197
|
+
session_id: sid,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return events;
|
|
201
|
+
}
|
|
202
|
+
case "subagent-stop": {
|
|
203
|
+
// SubagentStop fires when an @-mention subagent finishes. CC's payload
|
|
204
|
+
// doesn't tell us which agent stopped, so we emit a sweep โ the client
|
|
205
|
+
// idles every currently-active agent for this session.
|
|
206
|
+
return [{
|
|
207
|
+
type: "agent.completed",
|
|
208
|
+
agent: null,
|
|
209
|
+
sweep: true,
|
|
210
|
+
session_id: sessionId,
|
|
211
|
+
}];
|
|
212
|
+
}
|
|
213
|
+
case "session-end": {
|
|
214
|
+
return [{
|
|
215
|
+
type: "session.end",
|
|
216
|
+
session_id: sessionId,
|
|
217
|
+
outcome: "success",
|
|
218
|
+
}];
|
|
219
|
+
}
|
|
220
|
+
default:
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function truncate(s, n) {
|
|
226
|
+
const t = String(s);
|
|
227
|
+
return t.length <= n ? t : t.slice(0, n - 1) + "โฆ";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
main();
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* buildcrew CC hook installer.
|
|
3
|
+
*
|
|
4
|
+
* Registers hook entries in .claude/settings.json that invoke
|
|
5
|
+
* `npx buildcrew-hook <kind>` on each agent/file event. The hook writes
|
|
6
|
+
* a styled banner to the terminal AND appends to events.jsonl so that
|
|
7
|
+
* `npx buildcrew watch` can show a live view in a separate pane.
|
|
8
|
+
*
|
|
9
|
+
* Idempotent โ re-install replaces prior buildcrew entries without
|
|
10
|
+
* touching other hooks or permissions in the file.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { promises as fsp } from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import os from "node:os";
|
|
16
|
+
|
|
17
|
+
const BUILDCREW_TAG = "buildcrew-hook";
|
|
18
|
+
|
|
19
|
+
export function resolveSettingsPath({ scope, cwd }) {
|
|
20
|
+
if (scope === "global") return path.join(os.homedir(), ".claude", "settings.json");
|
|
21
|
+
return path.join(cwd, ".claude", "settings.json");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolvePermissionsPath({ scope, cwd }) {
|
|
25
|
+
if (scope === "global") return path.join(os.homedir(), ".claude", "settings.local.json");
|
|
26
|
+
return path.join(cwd, ".claude", "settings.local.json");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function buildcrewHooks() {
|
|
30
|
+
const cmd = (kind) => `npx buildcrew-hook ${kind}`;
|
|
31
|
+
const mk = (kind, matcher) => ({
|
|
32
|
+
[BUILDCREW_TAG]: true,
|
|
33
|
+
...(matcher ? { matcher } : {}),
|
|
34
|
+
hooks: [{ type: "command", command: cmd(kind) }],
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
PreToolUse: [mk("pre-agent", "Agent")],
|
|
38
|
+
PostToolUse: [
|
|
39
|
+
mk("post-agent", "Agent"),
|
|
40
|
+
mk("file-written", "Write|Edit|MultiEdit"),
|
|
41
|
+
],
|
|
42
|
+
UserPromptSubmit: [mk("user-prompt")],
|
|
43
|
+
Stop: [mk("session-end")],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function buildcrewPermissions() {
|
|
48
|
+
return {
|
|
49
|
+
allow: [
|
|
50
|
+
"Agent", "Task", "Read", "Glob", "Grep", "Write", "Edit", "MultiEdit",
|
|
51
|
+
"NotebookEdit", "WebFetch", "WebSearch",
|
|
52
|
+
"Bash(npm *)", "Bash(npx *)", "Bash(node *)",
|
|
53
|
+
"Bash(git status*)", "Bash(git diff*)", "Bash(git log*)",
|
|
54
|
+
"Bash(git branch*)", "Bash(git show*)", "Bash(git add:*)", "Bash(git commit:*)",
|
|
55
|
+
"Bash(ls *)", "Bash(pwd)", "Bash(which *)",
|
|
56
|
+
"Bash(cat *)", "Bash(head *)", "Bash(tail *)",
|
|
57
|
+
"Bash(mkdir *)", "Bash(touch *)", "Bash(echo *)",
|
|
58
|
+
],
|
|
59
|
+
deny: [
|
|
60
|
+
"Bash(rm -rf *)", "Bash(sudo *)",
|
|
61
|
+
"Bash(git push --force*)", "Bash(git reset --hard*)",
|
|
62
|
+
"Bash(curl * -X POST*)",
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function install({ scope = "project", cwd = process.cwd(), withPermissions = false } = {}) {
|
|
68
|
+
const settingsPath = resolveSettingsPath({ scope, cwd });
|
|
69
|
+
const { current, existed } = await readJson(settingsPath);
|
|
70
|
+
const next = mergeHooks(current, buildcrewHooks());
|
|
71
|
+
|
|
72
|
+
let permissionsResult = null;
|
|
73
|
+
if (withPermissions) {
|
|
74
|
+
permissionsResult = await installPermissions({ scope, cwd });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (existed) await backup(settingsPath);
|
|
78
|
+
await atomicWrite(settingsPath, JSON.stringify(next, null, 2) + "\n");
|
|
79
|
+
return { action: "installed", settingsPath, existed, permissions: permissionsResult };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function installPermissions({ scope = "project", cwd = process.cwd() } = {}) {
|
|
83
|
+
const permPath = resolvePermissionsPath({ scope, cwd });
|
|
84
|
+
const { current, existed } = await readJson(permPath);
|
|
85
|
+
const rec = buildcrewPermissions();
|
|
86
|
+
|
|
87
|
+
const nextPerms = current.permissions && typeof current.permissions === "object"
|
|
88
|
+
? { ...current.permissions } : {};
|
|
89
|
+
nextPerms.allow = mergeUnique(nextPerms.allow, rec.allow);
|
|
90
|
+
nextPerms.deny = mergeUnique(nextPerms.deny, rec.deny);
|
|
91
|
+
|
|
92
|
+
if (existed) await backup(permPath);
|
|
93
|
+
await atomicWrite(permPath, JSON.stringify({ ...current, permissions: nextPerms }, null, 2) + "\n");
|
|
94
|
+
return { action: "installed", permPath, existed };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function uninstall({ scope = "project", cwd = process.cwd() } = {}) {
|
|
98
|
+
const settingsPath = resolveSettingsPath({ scope, cwd });
|
|
99
|
+
const { current, existed } = await readJson(settingsPath);
|
|
100
|
+
if (!existed) return { action: "noop", reason: "no settings file", settingsPath };
|
|
101
|
+
const next = stripBuildcrewHooks(current);
|
|
102
|
+
await backup(settingsPath);
|
|
103
|
+
await atomicWrite(settingsPath, JSON.stringify(next, null, 2) + "\n");
|
|
104
|
+
return { action: "uninstalled", settingsPath };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ------------ internals ------------
|
|
108
|
+
|
|
109
|
+
async function readJson(p) {
|
|
110
|
+
try {
|
|
111
|
+
const buf = await fsp.readFile(p, "utf8");
|
|
112
|
+
return { current: JSON.parse(buf), existed: true };
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err.code === "ENOENT") return { current: {}, existed: false };
|
|
115
|
+
throw new Error(`failed to read ${p}: ${err.message}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function mergeHooks(current, add) {
|
|
120
|
+
const out = { ...current };
|
|
121
|
+
const existing = out.hooks && typeof out.hooks === "object" ? { ...out.hooks } : {};
|
|
122
|
+
for (const [event, entries] of Object.entries(add)) {
|
|
123
|
+
const prev = Array.isArray(existing[event]) ? existing[event] : [];
|
|
124
|
+
// Remove prior buildcrew entries to stay idempotent
|
|
125
|
+
const filtered = prev.filter((e) => !(e && e[BUILDCREW_TAG]));
|
|
126
|
+
existing[event] = [...filtered, ...entries];
|
|
127
|
+
}
|
|
128
|
+
out.hooks = existing;
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function stripBuildcrewHooks(current) {
|
|
133
|
+
const out = { ...current };
|
|
134
|
+
if (!out.hooks || typeof out.hooks !== "object") return out;
|
|
135
|
+
const cleaned = {};
|
|
136
|
+
for (const [event, entries] of Object.entries(out.hooks)) {
|
|
137
|
+
if (!Array.isArray(entries)) { cleaned[event] = entries; continue; }
|
|
138
|
+
const filtered = entries.filter((e) => !(e && e[BUILDCREW_TAG]));
|
|
139
|
+
if (filtered.length > 0) cleaned[event] = filtered;
|
|
140
|
+
}
|
|
141
|
+
if (Object.keys(cleaned).length === 0) delete out.hooks;
|
|
142
|
+
else out.hooks = cleaned;
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function mergeUnique(existing, additions) {
|
|
147
|
+
const base = Array.isArray(existing) ? existing.slice() : [];
|
|
148
|
+
const seen = new Set(base);
|
|
149
|
+
for (const a of additions) if (!seen.has(a)) { base.push(a); seen.add(a); }
|
|
150
|
+
return base;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function backup(p) {
|
|
154
|
+
try {
|
|
155
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
156
|
+
await fsp.copyFile(p, `${p}.buildcrew-backup-${ts}`);
|
|
157
|
+
} catch { /* best-effort */ }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function atomicWrite(p, content) {
|
|
161
|
+
await fsp.mkdir(path.dirname(p), { recursive: true });
|
|
162
|
+
const tmp = `${p}.tmp-${process.pid}`;
|
|
163
|
+
await fsp.writeFile(tmp, content, "utf8");
|
|
164
|
+
await fsp.rename(tmp, p);
|
|
165
|
+
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buildcrew",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "15 AI agents for Claude Code โ full development lifecycle from product thinking to production monitoring",
|
|
5
5
|
"homepage": "https://buildcrew-landing.vercel.app",
|
|
6
6
|
"author": "z1nun",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"bin": {
|
|
10
|
-
"buildcrew": "./bin/setup.js"
|
|
10
|
+
"buildcrew": "./bin/setup.js",
|
|
11
|
+
"buildcrew-hook": "./bin/hook.js",
|
|
12
|
+
"buildcrew-watch": "./bin/watch.js"
|
|
11
13
|
},
|
|
12
14
|
"files": [
|
|
13
15
|
"bin/",
|
|
16
|
+
"lib/",
|
|
14
17
|
"agents/",
|
|
15
18
|
"templates/",
|
|
16
19
|
"README.md",
|
|
@@ -34,7 +37,8 @@
|
|
|
34
37
|
"url": "https://github.com/z1nun/buildcrew.git"
|
|
35
38
|
},
|
|
36
39
|
"scripts": {
|
|
37
|
-
"test": "vitest run"
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"watch": "node bin/watch.js"
|
|
38
42
|
},
|
|
39
43
|
"devDependencies": {
|
|
40
44
|
"vitest": "^4.1.0"
|