@jjlabsio/claude-crew 0.1.16 → 0.1.18
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.
|
@@ -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.18",
|
|
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.18"
|
|
32
32
|
}
|
package/hud/index.mjs
CHANGED
|
@@ -198,10 +198,32 @@ function getRateLimits(stdin) {
|
|
|
198
198
|
const n = typeof v === 'number' ? v : parseFloat(v);
|
|
199
199
|
return isNaN(n) ? null : Math.round(Math.min(Math.max(n, 0), 100));
|
|
200
200
|
};
|
|
201
|
+
const parseResetAt = (v) => {
|
|
202
|
+
if (typeof v !== 'number' || !Number.isFinite(v) || v <= 0) return null;
|
|
203
|
+
return new Date(v * 1000);
|
|
204
|
+
};
|
|
201
205
|
const fiveHour = parse(rl.five_hour?.used_percentage);
|
|
202
206
|
const sevenDay = parse(rl.seven_day?.used_percentage);
|
|
207
|
+
const fiveHourResetAt = parseResetAt(rl.five_hour?.resets_at);
|
|
208
|
+
const sevenDayResetAt = parseResetAt(rl.seven_day?.resets_at);
|
|
203
209
|
if (fiveHour == null && sevenDay == null) return null;
|
|
204
|
-
return { fiveHour, sevenDay };
|
|
210
|
+
return { fiveHour, sevenDay, fiveHourResetAt, sevenDayResetAt };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function formatResetTime(resetAt) {
|
|
214
|
+
if (!resetAt) return '';
|
|
215
|
+
const diffMs = resetAt.getTime() - Date.now();
|
|
216
|
+
if (diffMs <= 0) return '';
|
|
217
|
+
const diffMins = Math.ceil(diffMs / 60000);
|
|
218
|
+
if (diffMins < 60) return `${diffMins}m`;
|
|
219
|
+
const hours = Math.floor(diffMins / 60);
|
|
220
|
+
const mins = diffMins % 60;
|
|
221
|
+
if (hours >= 24) {
|
|
222
|
+
const days = Math.floor(hours / 24);
|
|
223
|
+
const remHours = hours % 24;
|
|
224
|
+
return remHours > 0 ? `${days}d ${remHours}h` : `${days}d`;
|
|
225
|
+
}
|
|
226
|
+
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
|
205
227
|
}
|
|
206
228
|
|
|
207
229
|
function colorizeRateLimits(limits) {
|
|
@@ -210,9 +232,14 @@ function colorizeRateLimits(limits) {
|
|
|
210
232
|
const color = pct >= 85 ? red : pct >= 70 ? yellow : green;
|
|
211
233
|
return color(`${pct}%`);
|
|
212
234
|
};
|
|
235
|
+
const formatWindow = (label, pct, resetAt) => {
|
|
236
|
+
const reset = formatResetTime(resetAt);
|
|
237
|
+
const resetStr = reset ? ` ${dim(`(${reset})`)}` : '';
|
|
238
|
+
return `${label}:${colorize(pct)}${resetStr}`;
|
|
239
|
+
};
|
|
213
240
|
const parts = [];
|
|
214
|
-
if (limits.fiveHour != null) parts.push(
|
|
215
|
-
if (limits.sevenDay != null) parts.push(
|
|
241
|
+
if (limits.fiveHour != null) parts.push(formatWindow('5h', limits.fiveHour, limits.fiveHourResetAt));
|
|
242
|
+
if (limits.sevenDay != null) parts.push(formatWindow('weekly', limits.sevenDay, limits.sevenDayResetAt));
|
|
216
243
|
return parts.join(' ');
|
|
217
244
|
}
|
|
218
245
|
|
|
@@ -474,13 +501,13 @@ function renderAgentsMultiLine(agents, maxLines = 5) {
|
|
|
474
501
|
const prefix = isLast ? '\u2514\u2500' : '\u251c\u2500';
|
|
475
502
|
|
|
476
503
|
const rawType = a.type.includes(':') ? a.type.split(':').pop() : a.type;
|
|
477
|
-
const name = rawType
|
|
478
|
-
const model = shortModelName(a.model)
|
|
479
|
-
const duration = formatAgentDuration(a.startTime, a.endTime)
|
|
504
|
+
const name = rawType;
|
|
505
|
+
const model = `[${shortModelName(a.model)}]`;
|
|
506
|
+
const duration = formatAgentDuration(a.startTime, a.endTime);
|
|
480
507
|
const desc = a.description.length > 40 ? a.description.slice(0, 37) + '...' : a.description;
|
|
481
508
|
|
|
482
509
|
detailLines.push(
|
|
483
|
-
`${dim(prefix)} ${cyan(name)} ${model}${dim(
|
|
510
|
+
`${dim(prefix)} ${cyan(name)} ${dim(model)} : ${desc} ${dim(`(${duration})`)}`
|
|
484
511
|
);
|
|
485
512
|
});
|
|
486
513
|
|
|
@@ -556,7 +583,13 @@ async function main() {
|
|
|
556
583
|
const cwd = stdin.cwd || process.cwd();
|
|
557
584
|
|
|
558
585
|
// Find git project root for reliable matching against installed_plugins.json
|
|
559
|
-
|
|
586
|
+
// In worktrees, --show-toplevel returns the worktree path, not the main repo.
|
|
587
|
+
// Use --git-common-dir to resolve back to the main repo path.
|
|
588
|
+
const topLevel = gitExec('git rev-parse --show-toplevel', cwd) || cwd;
|
|
589
|
+
const commonDir = gitExec('git rev-parse --git-common-dir', cwd);
|
|
590
|
+
const projectRoot = (commonDir && commonDir !== '.git' && !commonDir.startsWith('.'))
|
|
591
|
+
? dirname(commonDir) // worktree: commonDir is /path/to/main-repo/.git
|
|
592
|
+
: topLevel;
|
|
560
593
|
|
|
561
594
|
// Only show HUD if claude-crew is installed in this project
|
|
562
595
|
const installInfo = getProjectInstallInfo(projectRoot);
|
package/package.json
CHANGED
package/skills/crew-dev/SKILL.md
CHANGED
|
@@ -229,7 +229,7 @@ contract.md, plan.md, brief.md, spec.md, dev-log.md는 읽지 않는다.
|
|
|
229
229
|
6. 에러 처리 적절성
|
|
230
230
|
|
|
231
231
|
## 출력
|
|
232
|
-
|
|
232
|
+
아래 형식으로 리뷰 결과를 텍스트로 반환하라. 파일을 직접 작성하지 않는다.
|
|
233
233
|
|
|
234
234
|
## 판정 규칙
|
|
235
235
|
- 가드레일 위반 → critical
|
|
@@ -269,7 +269,7 @@ contract.md, brief.md, spec.md는 읽지 않는다.
|
|
|
269
269
|
6. E2E / 통합 검증 — plan.md의 테스트 시나리오 기반
|
|
270
270
|
|
|
271
271
|
## 출력
|
|
272
|
-
|
|
272
|
+
아래 형식으로 검증 결과를 텍스트로 반환하라. 파일을 직접 작성하지 않는다.
|
|
273
273
|
|
|
274
274
|
## 판정 규칙
|
|
275
275
|
- 항목 1-6 중 하나라도 FAIL → 전체 FAIL
|
|
@@ -281,6 +281,13 @@ contract.md, brief.md, spec.md는 읽지 않는다.
|
|
|
281
281
|
- 코드를 수정하지 않는다. 검증만 한다.
|
|
282
282
|
```
|
|
283
283
|
|
|
284
|
+
**Phase 3 결과 저장 (오케스트레이터 직접)**:
|
|
285
|
+
|
|
286
|
+
CodeReviewer와 QA 에이전트는 read-only이므로 파일을 직접 작성하지 않는다.
|
|
287
|
+
오케스트레이터가 각 에이전트의 반환 텍스트를 파일로 저장한다:
|
|
288
|
+
- CodeReviewer 결과 → `.crew/plans/{task-id}/review-report.md`
|
|
289
|
+
- QA 결과 → `.crew/plans/{task-id}/qa-report.md`
|
|
290
|
+
|
|
284
291
|
**Phase 3 병렬 실행 방법**:
|
|
285
292
|
|
|
286
293
|
오케스트레이터는 한 번의 메시지에서 두 개의 Agent tool 호출을 동시에 수행한다:
|
|
@@ -318,6 +325,8 @@ PR을 생성한다 (머지하지 않는다).
|
|
|
318
325
|
|
|
319
326
|
**5b. 상태 갱신**
|
|
320
327
|
|
|
328
|
+
contract.md의 `## 수용 기준` 섹션에서 모든 `- [ ]`를 `- [x]`로 변경한다.
|
|
329
|
+
|
|
321
330
|
contract.md의 `## 상태` 섹션을 갱신한다:
|
|
322
331
|
|
|
323
332
|
```markdown
|
|
@@ -132,15 +132,20 @@ Agent(subagent_type="techlead", description="TechLead: {task-id} 사전 분석",
|
|
|
132
132
|
Agent(subagent_type="researcher", description="외부 리서치: {리서치 대상}", prompt="...")
|
|
133
133
|
|
|
134
134
|
## 출력
|
|
135
|
-
|
|
135
|
+
아래 필수 섹션을 포함한 분석 결과를 텍스트로 반환하라. 파일을 직접 작성하지 않는다.
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
필수 섹션: 요구사항 보완, 코드베이스 맥락(관련 파일/기존 패턴/테스트 구조), 아키텍처 방향(권장+대안), 엣지 케이스/리스크, 가드레일(Must/Must NOT), 테스트 인프라(프레임워크/패턴/유무), 외부 리서치(해당 시).
|
|
138
138
|
|
|
139
139
|
## 규칙
|
|
140
140
|
- 요구사항에 빈틈이 있으면 AskUserQuestion으로 유저에게 직접 질문하라.
|
|
141
141
|
- 탐색(양)은 서브에이전트에게, 판단(질)은 직접.
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
**Step 3 결과 저장 (오케스트레이터 직접)**:
|
|
145
|
+
|
|
146
|
+
TechLead 에이전트는 read-only이므로 파일을 직접 작성하지 않는다.
|
|
147
|
+
오케스트레이터가 TechLead의 반환 텍스트를 `.crew/plans/{task-id}/analysis.md`로 저장한다.
|
|
148
|
+
|
|
144
149
|
**실패 조건**: analysis.md가 없거나 가드레일 섹션이 비어 있으면 즉시 에스컬레이션.
|
|
145
150
|
|
|
146
151
|
---
|
|
@@ -314,10 +319,15 @@ Explorer 서브에이전트를 호출하여 plan.md에서 참조하는 파일/
|
|
|
314
319
|
Agent(subagent_type="explorer", description="코드 참조 확인: {파일 목록 요약}", prompt="plan.md에서 참조하는 다음 파일/모듈이 존재하는지 확인하라: {파일 목록}")
|
|
315
320
|
|
|
316
321
|
## 출력
|
|
317
|
-
|
|
318
|
-
|
|
322
|
+
아래 형식으로 검증 결과를 텍스트로 반환하라. 파일을 직접 작성하지 않는다.
|
|
323
|
+
형식: 판정(PASS/FAIL), 항목별 결과(E1-E5 YES/NO + 근거), FAIL 상세(NO 항목의 문제+수정 방향), 근본 원인 분류(FAIL 시).
|
|
319
324
|
```
|
|
320
325
|
|
|
326
|
+
**Step 5 결과 저장 (오케스트레이터 직접)**:
|
|
327
|
+
|
|
328
|
+
PlanEvaluator 에이전트는 read-only이므로 파일을 직접 작성하지 않는다.
|
|
329
|
+
오케스트레이터가 PlanEvaluator의 반환 텍스트를 `.crew/plans/{task-id}/review.md`로 저장한다.
|
|
330
|
+
|
|
321
331
|
---
|
|
322
332
|
|
|
323
333
|
### Step 6 — PASS 처리
|