@jjlabsio/claude-crew 0.1.46 → 0.1.48
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 +1 -1
- package/data/provider-catalog.json +1 -1
- package/package.json +1 -1
- package/scripts/crew-codex/lib/codex.mjs +12 -2
- package/scripts/crew-codex-companion.mjs +8 -3
- package/scripts/lib/dispatch.mjs +4 -0
- package/scripts/lib/resolve.mjs +2 -0
- package/scripts/setup-hud.mjs +36 -0
- package/skills/crew-agent-runner/SKILL.md +17 -4
- package/skills/crew-setup/SKILL.md +21 -0
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"name": "claude-crew",
|
|
12
12
|
"source": "./",
|
|
13
13
|
"description": "오케스트레이터 + PM, 플래너, 개발, QA, 마케팅 에이전트 팀으로 단일 제품의 개발과 마케팅을 통합 관리",
|
|
14
|
-
"version": "0.1.
|
|
14
|
+
"version": "0.1.48",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "Jaejin Song",
|
|
17
17
|
"email": "wowlxx28@gmail.com"
|
|
@@ -28,5 +28,5 @@
|
|
|
28
28
|
"category": "workflow"
|
|
29
29
|
}
|
|
30
30
|
],
|
|
31
|
-
"version": "0.1.
|
|
31
|
+
"version": "0.1.48"
|
|
32
32
|
}
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"explorer": { "codex_sandbox": "read-only" },
|
|
45
45
|
"researcher": { "codex_sandbox": "read-only" },
|
|
46
46
|
"qa": { "codex_sandbox": "read-only" },
|
|
47
|
-
"dev": { "codex_sandbox": "workspace-write" },
|
|
47
|
+
"dev": { "codex_sandbox": "workspace-write", "codex_network_access": true },
|
|
48
48
|
"code-reviewer": { "codex_sandbox": "read-only" }
|
|
49
49
|
}
|
|
50
50
|
}
|
package/package.json
CHANGED
|
@@ -56,7 +56,7 @@ function cleanCodexStderr(stderr) {
|
|
|
56
56
|
|
|
57
57
|
/** @returns {ThreadStartParams} */
|
|
58
58
|
function buildThreadParams(cwd, options = {}) {
|
|
59
|
-
|
|
59
|
+
const params = {
|
|
60
60
|
cwd,
|
|
61
61
|
model: options.model ?? null,
|
|
62
62
|
approvalPolicy: options.approvalPolicy ?? "never",
|
|
@@ -65,17 +65,25 @@ function buildThreadParams(cwd, options = {}) {
|
|
|
65
65
|
ephemeral: options.ephemeral ?? true,
|
|
66
66
|
experimentalRawEvents: false
|
|
67
67
|
};
|
|
68
|
+
if (options.networkAccess) {
|
|
69
|
+
params.networkAccess = true;
|
|
70
|
+
}
|
|
71
|
+
return params;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
/** @returns {ThreadResumeParams} */
|
|
71
75
|
function buildResumeParams(threadId, cwd, options = {}) {
|
|
72
|
-
|
|
76
|
+
const params = {
|
|
73
77
|
threadId,
|
|
74
78
|
cwd,
|
|
75
79
|
model: options.model ?? null,
|
|
76
80
|
approvalPolicy: options.approvalPolicy ?? "never",
|
|
77
81
|
sandbox: options.sandbox ?? "read-only"
|
|
78
82
|
};
|
|
83
|
+
if (options.networkAccess) {
|
|
84
|
+
params.networkAccess = true;
|
|
85
|
+
}
|
|
86
|
+
return params;
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
/** @returns {UserInput[]} */
|
|
@@ -977,6 +985,7 @@ export async function runAppServerTurn(cwd, options = {}) {
|
|
|
977
985
|
const response = await resumeThread(client, options.resumeThreadId, cwd, {
|
|
978
986
|
model: options.model,
|
|
979
987
|
sandbox: options.sandbox,
|
|
988
|
+
networkAccess: options.networkAccess,
|
|
980
989
|
ephemeral: false
|
|
981
990
|
});
|
|
982
991
|
threadId = response.thread.id;
|
|
@@ -985,6 +994,7 @@ export async function runAppServerTurn(cwd, options = {}) {
|
|
|
985
994
|
const response = await startThread(client, cwd, {
|
|
986
995
|
model: options.model,
|
|
987
996
|
sandbox: options.sandbox,
|
|
997
|
+
networkAccess: options.networkAccess,
|
|
988
998
|
ephemeral: options.persistThread ? false : true,
|
|
989
999
|
threadName: options.persistThread ? options.threadName : options.threadName ?? null
|
|
990
1000
|
});
|
|
@@ -73,7 +73,7 @@ function printUsage() {
|
|
|
73
73
|
console.log(
|
|
74
74
|
[
|
|
75
75
|
"Usage:",
|
|
76
|
-
" node scripts/crew-codex-companion.mjs task [--background] [--write] [--expect-crew-result] [--resume-last|--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [prompt]",
|
|
76
|
+
" node scripts/crew-codex-companion.mjs task [--background] [--write] [--network-access] [--expect-crew-result] [--resume-last|--resume|--fresh] [--model <model|spark>] [--effort <none|minimal|low|medium|high|xhigh>] [prompt]",
|
|
77
77
|
" node scripts/crew-codex-companion.mjs status [job-id] [--all] [--json]",
|
|
78
78
|
" node scripts/crew-codex-companion.mjs result [job-id] [--json]",
|
|
79
79
|
" node scripts/crew-codex-companion.mjs cancel [job-id] [--json]"
|
|
@@ -341,6 +341,7 @@ async function executeTaskRun(request) {
|
|
|
341
341
|
model: request.model,
|
|
342
342
|
effort: request.effort,
|
|
343
343
|
sandbox: request.write ? "workspace-write" : "read-only",
|
|
344
|
+
networkAccess: Boolean(request.networkAccess),
|
|
344
345
|
onProgress: request.onProgress,
|
|
345
346
|
persistThread: true,
|
|
346
347
|
threadName: resumeThreadId ? null : buildPersistentTaskThreadName(request.prompt || DEFAULT_CONTINUE_PROMPT)
|
|
@@ -455,13 +456,14 @@ function buildTaskJob(workspaceRoot, taskMetadata, write) {
|
|
|
455
456
|
});
|
|
456
457
|
}
|
|
457
458
|
|
|
458
|
-
function buildTaskRequest({ cwd, model, effort, prompt, write, resumeLast, expectCrewResult, jobId }) {
|
|
459
|
+
function buildTaskRequest({ cwd, model, effort, prompt, write, networkAccess, resumeLast, expectCrewResult, jobId }) {
|
|
459
460
|
return {
|
|
460
461
|
cwd,
|
|
461
462
|
model,
|
|
462
463
|
effort,
|
|
463
464
|
prompt,
|
|
464
465
|
write,
|
|
466
|
+
networkAccess,
|
|
465
467
|
resumeLast,
|
|
466
468
|
expectCrewResult,
|
|
467
469
|
jobId
|
|
@@ -591,7 +593,7 @@ function enqueueBackgroundTask(cwd, job, request) {
|
|
|
591
593
|
async function handleTask(argv) {
|
|
592
594
|
const { options, positionals } = parseCommandInput(argv, {
|
|
593
595
|
valueOptions: ["model", "effort", "cwd", "prompt-file"],
|
|
594
|
-
booleanOptions: ["json", "write", "expect-crew-result", "resume-last", "resume", "fresh", "background"],
|
|
596
|
+
booleanOptions: ["json", "write", "network-access", "expect-crew-result", "resume-last", "resume", "fresh", "background"],
|
|
595
597
|
aliasMap: {
|
|
596
598
|
m: "model"
|
|
597
599
|
}
|
|
@@ -609,6 +611,7 @@ async function handleTask(argv) {
|
|
|
609
611
|
throw new Error("Choose either --resume/--resume-last or --fresh.");
|
|
610
612
|
}
|
|
611
613
|
const write = Boolean(options.write);
|
|
614
|
+
const networkAccess = Boolean(options["network-access"]);
|
|
612
615
|
const expectCrewResult = Boolean(options["expect-crew-result"]);
|
|
613
616
|
const taskMetadata = buildTaskRunMetadata({
|
|
614
617
|
prompt,
|
|
@@ -626,6 +629,7 @@ async function handleTask(argv) {
|
|
|
626
629
|
effort,
|
|
627
630
|
prompt,
|
|
628
631
|
write,
|
|
632
|
+
networkAccess,
|
|
629
633
|
resumeLast,
|
|
630
634
|
expectCrewResult,
|
|
631
635
|
jobId: job.id
|
|
@@ -645,6 +649,7 @@ async function handleTask(argv) {
|
|
|
645
649
|
effort,
|
|
646
650
|
prompt,
|
|
647
651
|
write,
|
|
652
|
+
networkAccess,
|
|
648
653
|
resumeLast,
|
|
649
654
|
expectCrewResult,
|
|
650
655
|
jobId: job.id,
|
package/scripts/lib/dispatch.mjs
CHANGED
|
@@ -264,6 +264,10 @@ function buildTaskArgs(input, promptFile) {
|
|
|
264
264
|
args.push("--write");
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
if (input.resolved?.codex_network_access) {
|
|
268
|
+
args.push("--network-access");
|
|
269
|
+
}
|
|
270
|
+
|
|
267
271
|
if (input.resolved?.model) {
|
|
268
272
|
args.push("--model", input.resolved.model);
|
|
269
273
|
}
|
package/scripts/lib/resolve.mjs
CHANGED
|
@@ -23,6 +23,7 @@ export function resolveRole(input) {
|
|
|
23
23
|
if (!["read-only", "workspace-write"].includes(codexSandbox)) {
|
|
24
24
|
throw new Error(`Missing codex_sandbox for role ${role}`);
|
|
25
25
|
}
|
|
26
|
+
const codexNetworkAccess = catalog?.agent_runtime?.[role]?.codex_network_access === true;
|
|
26
27
|
|
|
27
28
|
const warnings = [];
|
|
28
29
|
const workspaceAccess = contract?.capabilities?.workspaceAccess;
|
|
@@ -38,6 +39,7 @@ export function resolveRole(input) {
|
|
|
38
39
|
model,
|
|
39
40
|
reasoning: provider === "codex" ? defaults.reasoning ?? null : null,
|
|
40
41
|
codex_sandbox: codexSandbox,
|
|
42
|
+
codex_network_access: codexNetworkAccess,
|
|
41
43
|
contract,
|
|
42
44
|
dispatch: {
|
|
43
45
|
path: provider === "codex" ? "codex" : "claude",
|
package/scripts/setup-hud.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { execSync } from 'node:child_process';
|
|
|
11
11
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
12
12
|
import { join, dirname } from 'node:path';
|
|
13
13
|
import { homedir } from 'node:os';
|
|
14
|
+
import { PLUGIN_ROOT } from './lib/pluginRoot.mjs';
|
|
14
15
|
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
// Read stdin (with timeout)
|
|
@@ -33,6 +34,39 @@ function gitExec(cmd, cwd) {
|
|
|
33
34
|
} catch { return null; }
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
function readPluginVersion() {
|
|
38
|
+
const packageJson = JSON.parse(readFileSync(join(PLUGIN_ROOT, 'package.json'), 'utf-8'));
|
|
39
|
+
return packageJson.version;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function updatePluginRootCache(pluginRoot) {
|
|
43
|
+
try {
|
|
44
|
+
const crewDir = join(homedir(), '.claude', 'crew');
|
|
45
|
+
const cachePath = join(crewDir, 'plugin-root.json');
|
|
46
|
+
|
|
47
|
+
let shouldWrite = !existsSync(cachePath);
|
|
48
|
+
if (!shouldWrite) {
|
|
49
|
+
try {
|
|
50
|
+
const current = JSON.parse(readFileSync(cachePath, 'utf-8'));
|
|
51
|
+
shouldWrite = current.pluginRoot !== pluginRoot;
|
|
52
|
+
} catch {
|
|
53
|
+
shouldWrite = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!shouldWrite) return;
|
|
58
|
+
|
|
59
|
+
mkdirSync(crewDir, { recursive: true });
|
|
60
|
+
writeFileSync(cachePath, JSON.stringify({
|
|
61
|
+
pluginRoot,
|
|
62
|
+
version: readPluginVersion(),
|
|
63
|
+
updatedAt: new Date().toISOString(),
|
|
64
|
+
}, null, 2));
|
|
65
|
+
} catch {
|
|
66
|
+
// Best-effort cache for runner fallback. HUD setup must continue on failure.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
36
70
|
// ---------------------------------------------------------------------------
|
|
37
71
|
// Main
|
|
38
72
|
// ---------------------------------------------------------------------------
|
|
@@ -74,6 +108,8 @@ async function main() {
|
|
|
74
108
|
writeFileSync(localSettingsPath, JSON.stringify(localSettings, null, 2));
|
|
75
109
|
}
|
|
76
110
|
|
|
111
|
+
updatePluginRootCache(pluginRoot);
|
|
112
|
+
|
|
77
113
|
console.log(JSON.stringify({ continue: true }));
|
|
78
114
|
} catch (e) {
|
|
79
115
|
console.log(JSON.stringify({
|
|
@@ -7,20 +7,33 @@ description: 모든 crew 에이전트 dispatch의 중앙 규약 — provider별
|
|
|
7
7
|
|
|
8
8
|
crew 업무 스킬은 에이전트 provider별 호출 세부사항을 직접 구현하지 않고 이 중앙 규약을 따른다. 본 스킬은 prepare, resolve, dispatch, resume, followup 주입, retry/fallback/escalate 판단의 공통 표면을 정의한다.
|
|
9
9
|
|
|
10
|
-
설치 후 drift 차단용 pre-commit hook은 `node scripts/crew-agent-runner.mjs install-hooks`로 설치한다.
|
|
10
|
+
설치 후 drift 차단용 pre-commit hook은 `node "$CREW_ROOT/scripts/crew-agent-runner.mjs" install-hooks`로 설치한다.
|
|
11
11
|
(plugin 개발자 전용 — 사용자는 호출하지 않습니다. build/validate는 plugin source repo의 drift 차단 도구입니다.)
|
|
12
12
|
|
|
13
13
|
## Dispatch 절차
|
|
14
14
|
|
|
15
15
|
업무 스킬(crew-plan/crew-interview/crew-dev)이 role을 실행해야 할 때 본 절차를 따른다.
|
|
16
16
|
|
|
17
|
+
### Step 0. Runner 경로 결정
|
|
18
|
+
|
|
19
|
+
오케스트레이터는 runner 호출 전에 plugin root를 결정하고, 결정된 경로를 `CREW_ROOT`로 사용한다.
|
|
20
|
+
|
|
21
|
+
Fallback 체인 (순서 중요):
|
|
22
|
+
|
|
23
|
+
1. `$CLAUDE_PLUGIN_ROOT`가 있으면 사용한다. 정상 Claude Code plugin 환경이므로 추가 검증하지 않는다.
|
|
24
|
+
2. `~/.claude/crew/plugin-root.json`의 `pluginRoot`를 읽는다. 해당 경로에 `scripts/crew-agent-runner.mjs`가 존재하면 사용하고, 없으면 다음 fallback으로 진행한다.
|
|
25
|
+
3. `git rev-parse --show-toplevel` 결과를 dev 환경 전용 후보로 사용한다. 해당 경로의 `package.json`에서 `name`이 `@jjlabsio/claude-crew`인지 검증하고, 맞으면 사용한다. 아니면 다음 fallback으로 진행한다.
|
|
26
|
+
4. 모든 fallback이 실패하면 `'/crew-setup을 먼저 실행하세요'` 에러 메시지로 중단한다.
|
|
27
|
+
|
|
28
|
+
이후 모든 runner 호출은 `node "$CREW_ROOT/scripts/crew-agent-runner.mjs"` 형식을 사용한다.
|
|
29
|
+
|
|
17
30
|
### 1. request 객체 작성
|
|
18
31
|
|
|
19
32
|
`{ role, inputs (path+content), instruction, successGate, failureHandling, taskId }` 형태의 임시 JSON 파일을 작성한다.
|
|
20
33
|
|
|
21
34
|
### 2. prepare
|
|
22
35
|
|
|
23
|
-
오케스트레이터는 `node "$
|
|
36
|
+
오케스트레이터는 `node "$CREW_ROOT/scripts/crew-agent-runner.mjs" prepare --role <role> --request-file <request-file> --json`을 실행한다.
|
|
24
37
|
prepare는 provider/model/contract를 해석하고 다음 action 중 하나를 반환한다.
|
|
25
38
|
|
|
26
39
|
### 3a. dispatch action
|
|
@@ -67,8 +80,8 @@ capability를 넘어선 도구 요청이다. 오케스트레이터가 `contract.
|
|
|
67
80
|
|
|
68
81
|
### Codex 경로
|
|
69
82
|
|
|
70
|
-
1. `node scripts/crew-agent-runner.mjs render-followup --previous-result <file> --new-input <file>` 실행 → followup prompt 문자열 → 임시 파일에 저장.
|
|
71
|
-
2. `node scripts/crew-agent-runner.mjs dispatch --role <role> --request-file <new-request-with-followup-prompt> --resume-handle <agent_handle> --json` 실행.
|
|
83
|
+
1. `node "$CREW_ROOT/scripts/crew-agent-runner.mjs" render-followup --previous-result <file> --new-input <file>` 실행 → followup prompt 문자열 → 임시 파일에 저장.
|
|
84
|
+
2. `node "$CREW_ROOT/scripts/crew-agent-runner.mjs" dispatch --role <role> --request-file <new-request-with-followup-prompt> --resume-handle <agent_handle> --json` 실행.
|
|
72
85
|
- 내부적으로 runner가 `crew-codex-companion.mjs task-resume-candidate`로 thread 일치 검증 후 `task --resume-last`를 호출하고 AgentResult를 정규화한다.
|
|
73
86
|
3. AgentResult JSON을 받아 다음 상태 처리.
|
|
74
87
|
|
|
@@ -102,6 +102,27 @@ push는 하지 않는다.
|
|
|
102
102
|
|
|
103
103
|
---
|
|
104
104
|
|
|
105
|
+
## Step 2b — Plugin Root 영속화
|
|
106
|
+
|
|
107
|
+
`$CLAUDE_PLUGIN_ROOT`가 살아있는 setup 시점에 runner fallback용 plugin root를 사용자 데이터 영역에 저장한다.
|
|
108
|
+
|
|
109
|
+
1. `~/.claude/crew/` 디렉토리가 없으면 생성한다.
|
|
110
|
+
2. `~/.claude/crew/plugin-root.json`을 다음 형식으로 쓴다. 기존 파일이 있으면 덮어쓴다.
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"pluginRoot": "<$CLAUDE_PLUGIN_ROOT 값>",
|
|
114
|
+
"version": "<현재 플러그인 버전>",
|
|
115
|
+
"updatedAt": "<ISO timestamp>"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
3. plugin 자체 데이터인 version은 plugin root 기준 `package.json`에서 읽는다.
|
|
119
|
+
|
|
120
|
+
**주의**:
|
|
121
|
+
- plugin root는 `$CLAUDE_PLUGIN_ROOT` 값을 사용한다.
|
|
122
|
+
- `~/.claude/crew/plugin-root.json`은 사용자 데이터이므로 home 기준 경로에 저장한다.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
105
126
|
## Step 3 — Provider 설정
|
|
106
127
|
|
|
107
128
|
에이전트별로 어떤 provider(claude/codex)와 model을 사용할지 설정한다.
|