@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.16",
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.16"
31
+ "version": "0.1.18"
32
32
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-crew",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "1인 SaaS 개발자를 위한 멀티 에이전트 오케스트레이션 — 개발, 마케팅, 일정을 한 대화에서 통합 관리",
5
5
  "author": {
6
6
  "name": "Jaejin Song",
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(`5h:${colorize(limits.fiveHour)}`);
215
- if (limits.sevenDay != null) parts.push(`weekly:${colorize(limits.sevenDay)}`);
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.padEnd(7);
478
- const model = shortModelName(a.model).padEnd(8);
479
- const duration = formatAgentDuration(a.startTime, a.endTime).padStart(6);
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(duration)} ${desc}`
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
- const projectRoot = gitExec('git rev-parse --show-toplevel', cwd) || cwd;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlabsio/claude-crew",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "1인 SaaS 개발자를 위한 멀티 에이전트 오케스트레이션 — 개발, 마케팅, 일정을 한 대화에서 통합 관리",
5
5
  "author": "Jaejin Song <wowlxx28@gmail.com>",
6
6
  "license": "MIT",
@@ -229,7 +229,7 @@ contract.md, plan.md, brief.md, spec.md, dev-log.md는 읽지 않는다.
229
229
  6. 에러 처리 적절성
230
230
 
231
231
  ## 출력
232
- .crew/plans/{task-id}/review-report.md 작성하라.
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
- .crew/plans/{task-id}/qa-report.md 작성하라.
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
- .crew/plans/{task-id}/analysis.md 작성하라.
135
+ 아래 필수 섹션을 포함한 분석 결과를 텍스트로 반환하라. 파일을 직접 작성하지 않는다.
136
136
 
137
- analysis.md 필수 섹션: 요구사항 보완, 코드베이스 맥락(관련 파일/기존 패턴/테스트 구조), 아키텍처 방향(권장+대안), 엣지 케이스/리스크, 가드레일(Must/Must NOT), 테스트 인프라(프레임워크/패턴/유무), 외부 리서치(해당 시).
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
- .crew/plans/{task-id}/review.md 작성하라.
318
- review.md 형식: 판정(PASS/FAIL), 항목별 결과(E1-E4 YES/NO + 근거), FAIL 상세(NO 항목의 문제+수정 방향), 근본 원인 분류(FAIL 시).
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 처리