@jjlabsio/claude-crew 0.1.30 → 0.1.32
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -1
- package/README.md +11 -1
- package/THIRD_PARTY_NOTICES.md +14 -0
- package/data/provider-catalog.json +36 -14
- package/package.json +2 -1
- package/scripts/crew-codex/LICENSE +201 -0
- package/scripts/crew-codex/NOTICE +16 -0
- package/scripts/crew-codex/app-server-broker.mjs +254 -0
- package/scripts/crew-codex/lib/app-server.mjs +352 -0
- package/scripts/crew-codex/lib/args.mjs +130 -0
- package/scripts/crew-codex/lib/broker-endpoint.mjs +43 -0
- package/scripts/crew-codex/lib/broker-lifecycle.mjs +213 -0
- package/scripts/crew-codex/lib/codex.mjs +1090 -0
- package/scripts/crew-codex/lib/fs.mjs +42 -0
- package/scripts/crew-codex/lib/git.mjs +348 -0
- package/scripts/crew-codex/lib/job-control.mjs +310 -0
- package/scripts/crew-codex/lib/process.mjs +137 -0
- package/scripts/crew-codex/lib/prompts.mjs +15 -0
- package/scripts/crew-codex/lib/render.mjs +466 -0
- package/scripts/crew-codex/lib/state.mjs +424 -0
- package/scripts/crew-codex/lib/tracked-jobs.mjs +279 -0
- package/scripts/crew-codex/lib/workspace.mjs +11 -0
- package/scripts/crew-codex-companion.mjs +863 -0
- package/skills/crew-dev/SKILL.md +68 -18
- package/skills/crew-interview/SKILL.md +34 -6
- package/skills/crew-plan/SKILL.md +43 -15
- package/skills/crew-setup/SKILL.md +38 -34
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Derived from @openai/codex-plugin-cc and modified for claude-crew.
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import process from "node:process";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { createBrokerEndpoint, parseBrokerEndpoint } from "./broker-endpoint.mjs";
|
|
11
|
+
import { resolveStateDir, withStateLockAsync, writeJsonFileAtomic } from "./state.mjs";
|
|
12
|
+
|
|
13
|
+
export const PID_FILE_ENV = "CODEX_COMPANION_APP_SERVER_PID_FILE";
|
|
14
|
+
export const LOG_FILE_ENV = "CODEX_COMPANION_APP_SERVER_LOG_FILE";
|
|
15
|
+
const BROKER_STATE_FILE = "broker.json";
|
|
16
|
+
|
|
17
|
+
export function createBrokerSessionDir(prefix = "cxc-") {
|
|
18
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function connectToEndpoint(endpoint) {
|
|
22
|
+
const target = parseBrokerEndpoint(endpoint);
|
|
23
|
+
return net.createConnection({ path: target.path });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function waitForBrokerEndpoint(endpoint, timeoutMs = 2000) {
|
|
27
|
+
const start = Date.now();
|
|
28
|
+
while (Date.now() - start < timeoutMs) {
|
|
29
|
+
const ready = await new Promise((resolve) => {
|
|
30
|
+
const socket = connectToEndpoint(endpoint);
|
|
31
|
+
socket.on("connect", () => {
|
|
32
|
+
socket.end();
|
|
33
|
+
resolve(true);
|
|
34
|
+
});
|
|
35
|
+
socket.on("error", () => resolve(false));
|
|
36
|
+
});
|
|
37
|
+
if (ready) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function sendBrokerShutdown(endpoint) {
|
|
46
|
+
await new Promise((resolve) => {
|
|
47
|
+
const socket = connectToEndpoint(endpoint);
|
|
48
|
+
socket.setEncoding("utf8");
|
|
49
|
+
socket.on("connect", () => {
|
|
50
|
+
socket.write(`${JSON.stringify({ id: 1, method: "broker/shutdown", params: {} })}\n`);
|
|
51
|
+
});
|
|
52
|
+
socket.on("data", () => {
|
|
53
|
+
socket.end();
|
|
54
|
+
resolve();
|
|
55
|
+
});
|
|
56
|
+
socket.on("error", resolve);
|
|
57
|
+
socket.on("close", resolve);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function spawnBrokerProcess({ scriptPath, cwd, endpoint, pidFile, logFile, env = process.env }) {
|
|
62
|
+
const logFd = fs.openSync(logFile, "a");
|
|
63
|
+
const child = spawn(process.execPath, [scriptPath, "serve", "--endpoint", endpoint, "--cwd", cwd, "--pid-file", pidFile], {
|
|
64
|
+
cwd,
|
|
65
|
+
env,
|
|
66
|
+
detached: true,
|
|
67
|
+
stdio: ["ignore", logFd, logFd]
|
|
68
|
+
});
|
|
69
|
+
child.unref();
|
|
70
|
+
fs.closeSync(logFd);
|
|
71
|
+
return child;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function resolveBrokerStateFile(cwd) {
|
|
75
|
+
return path.join(resolveStateDir(cwd), BROKER_STATE_FILE);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function loadBrokerSession(cwd) {
|
|
79
|
+
const stateFile = resolveBrokerStateFile(cwd);
|
|
80
|
+
if (!fs.existsSync(stateFile)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(fs.readFileSync(stateFile, "utf8"));
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function saveBrokerSession(cwd, session) {
|
|
92
|
+
const stateDir = resolveStateDir(cwd);
|
|
93
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
94
|
+
writeJsonFileAtomic(resolveBrokerStateFile(cwd), session);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function clearBrokerSession(cwd) {
|
|
98
|
+
const stateFile = resolveBrokerStateFile(cwd);
|
|
99
|
+
if (fs.existsSync(stateFile)) {
|
|
100
|
+
fs.unlinkSync(stateFile);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function isBrokerEndpointReady(endpoint) {
|
|
105
|
+
if (!endpoint) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
return await waitForBrokerEndpoint(endpoint, 150);
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function ensureBrokerSession(cwd, options = {}) {
|
|
116
|
+
return withStateLockAsync(cwd, async () => {
|
|
117
|
+
const existing = loadBrokerSession(cwd);
|
|
118
|
+
if (existing && (await isBrokerEndpointReady(existing.endpoint))) {
|
|
119
|
+
return existing;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (existing) {
|
|
123
|
+
teardownBrokerSession({
|
|
124
|
+
endpoint: existing.endpoint ?? null,
|
|
125
|
+
pidFile: existing.pidFile ?? null,
|
|
126
|
+
logFile: existing.logFile ?? null,
|
|
127
|
+
sessionDir: existing.sessionDir ?? null,
|
|
128
|
+
pid: existing.pid ?? null,
|
|
129
|
+
killProcess: options.killProcess ?? null
|
|
130
|
+
});
|
|
131
|
+
clearBrokerSession(cwd);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const sessionDir = createBrokerSessionDir();
|
|
135
|
+
const endpointFactory = options.createBrokerEndpoint ?? createBrokerEndpoint;
|
|
136
|
+
const endpoint = endpointFactory(sessionDir, options.platform);
|
|
137
|
+
const pidFile = path.join(sessionDir, "broker.pid");
|
|
138
|
+
const logFile = path.join(sessionDir, "broker.log");
|
|
139
|
+
const scriptPath =
|
|
140
|
+
options.scriptPath ??
|
|
141
|
+
fileURLToPath(new URL("../app-server-broker.mjs", import.meta.url));
|
|
142
|
+
|
|
143
|
+
const child = spawnBrokerProcess({
|
|
144
|
+
scriptPath,
|
|
145
|
+
cwd,
|
|
146
|
+
endpoint,
|
|
147
|
+
pidFile,
|
|
148
|
+
logFile,
|
|
149
|
+
env: options.env ?? process.env
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const ready = await waitForBrokerEndpoint(endpoint, options.timeoutMs ?? 2000);
|
|
153
|
+
if (!ready) {
|
|
154
|
+
teardownBrokerSession({
|
|
155
|
+
endpoint,
|
|
156
|
+
pidFile,
|
|
157
|
+
logFile,
|
|
158
|
+
sessionDir,
|
|
159
|
+
pid: child.pid ?? null,
|
|
160
|
+
killProcess: options.killProcess ?? null
|
|
161
|
+
});
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const session = {
|
|
166
|
+
endpoint,
|
|
167
|
+
pidFile,
|
|
168
|
+
logFile,
|
|
169
|
+
sessionDir,
|
|
170
|
+
pid: child.pid ?? null
|
|
171
|
+
};
|
|
172
|
+
saveBrokerSession(cwd, session);
|
|
173
|
+
return session;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function teardownBrokerSession({ endpoint = null, pidFile, logFile, sessionDir = null, pid = null, killProcess = null }) {
|
|
178
|
+
if (Number.isFinite(pid) && killProcess) {
|
|
179
|
+
try {
|
|
180
|
+
killProcess(pid);
|
|
181
|
+
} catch {
|
|
182
|
+
// Ignore missing or already-exited broker processes.
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (pidFile && fs.existsSync(pidFile)) {
|
|
187
|
+
fs.unlinkSync(pidFile);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (logFile && fs.existsSync(logFile)) {
|
|
191
|
+
fs.unlinkSync(logFile);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (endpoint) {
|
|
195
|
+
try {
|
|
196
|
+
const target = parseBrokerEndpoint(endpoint);
|
|
197
|
+
if (target.kind === "unix" && fs.existsSync(target.path)) {
|
|
198
|
+
fs.unlinkSync(target.path);
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
// Ignore malformed or already-removed broker endpoints during teardown.
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const resolvedSessionDir = sessionDir ?? (pidFile ? path.dirname(pidFile) : logFile ? path.dirname(logFile) : null);
|
|
206
|
+
if (resolvedSessionDir && fs.existsSync(resolvedSessionDir)) {
|
|
207
|
+
try {
|
|
208
|
+
fs.rmdirSync(resolvedSessionDir);
|
|
209
|
+
} catch {
|
|
210
|
+
// Ignore non-empty or missing directories.
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|