@jjlabsio/claude-crew 0.1.13 → 0.1.14

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.13",
14
+ "version": "0.1.14",
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.13"
31
+ "version": "0.1.14"
32
32
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-crew",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "1인 SaaS 개발자를 위한 멀티 에이전트 오케스트레이션 — 개발, 마케팅, 일정을 한 대화에서 통합 관리",
5
5
  "author": {
6
6
  "name": "Jaejin Song",
@@ -21,7 +21,11 @@
21
21
  "./agents/planner.md",
22
22
  "./agents/dev.md",
23
23
  "./agents/qa.md",
24
- "./agents/marketing.md"
24
+ "./agents/code-reviewer.md",
25
+ "./agents/techlead.md",
26
+ "./agents/researcher.md",
27
+ "./agents/explorer.md",
28
+ "./agents/plan-evaluator.md"
25
29
  ],
26
30
  "skills": [
27
31
  "./skills/"
package/hud/index.mjs CHANGED
@@ -13,6 +13,7 @@ import { execSync } from 'node:child_process';
13
13
  import { readFileSync, existsSync, readdirSync } from 'node:fs';
14
14
  import { join, dirname, basename } from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
+ import { homedir } from 'node:os';
16
17
 
17
18
  // ---------------------------------------------------------------------------
18
19
  // ANSI helpers
@@ -43,10 +44,37 @@ async function readStdin(timeoutMs = 1000) {
43
44
  });
44
45
  }
45
46
 
47
+ // ---------------------------------------------------------------------------
48
+ // Project installation info from installed_plugins.json
49
+ // ---------------------------------------------------------------------------
50
+ function getProjectInstallInfo(projectRoot) {
51
+ try {
52
+ const pluginsJsonPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
53
+ if (!existsSync(pluginsJsonPath)) return null;
54
+ const data = JSON.parse(readFileSync(pluginsJsonPath, 'utf-8'));
55
+ const crewEntries = data.plugins?.['claude-crew@claude-crew'] || [];
56
+ return crewEntries.find(e => e.projectPath === projectRoot) || null;
57
+ } catch { return null; }
58
+ }
59
+
46
60
  // ---------------------------------------------------------------------------
47
61
  // Version
48
62
  // ---------------------------------------------------------------------------
49
- function getVersion() {
63
+ function getVersion(installInfo) {
64
+ // Read from the project-specific install path
65
+ if (installInfo?.installPath) {
66
+ try {
67
+ const pkgPath = join(installInfo.installPath, 'package.json');
68
+ if (existsSync(pkgPath)) {
69
+ return JSON.parse(readFileSync(pkgPath, 'utf-8')).version || '0.0.0';
70
+ }
71
+ } catch { /* ignore */ }
72
+ }
73
+ // Fallback to version field from install record
74
+ if (installInfo?.version && installInfo.version !== 'unknown') {
75
+ return installInfo.version;
76
+ }
77
+ // Final fallback: own package.json (dev/local run)
50
78
  try {
51
79
  const __dirname = dirname(fileURLToPath(import.meta.url));
52
80
  const pkgPath = join(__dirname, '..', 'package.json');
@@ -364,7 +392,15 @@ async function main() {
364
392
  }
365
393
 
366
394
  const cwd = stdin.cwd || process.cwd();
367
- const version = getVersion();
395
+
396
+ // Find git project root for reliable matching against installed_plugins.json
397
+ const projectRoot = gitExec('git rev-parse --show-toplevel', cwd) || cwd;
398
+
399
+ // Only show HUD if claude-crew is installed in this project
400
+ const installInfo = getProjectInstallInfo(projectRoot);
401
+ if (!installInfo) return;
402
+
403
+ const version = getVersion(installInfo);
368
404
 
369
405
  // --- Top line ---
370
406
  const topElements = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlabsio/claude-crew",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "1인 SaaS 개발자를 위한 멀티 에이전트 오케스트레이션 — 개발, 마케팅, 일정을 한 대화에서 통합 관리",
5
5
  "author": "Jaejin Song <wowlxx28@gmail.com>",
6
6
  "license": "MIT",
@@ -2,12 +2,13 @@
2
2
  /**
3
3
  * CREW Session Start Hook
4
4
  *
5
- * Checks if statusLine is configured for CREW HUD.
6
- * If not, automatically sets it up.
7
- * Reads stdin JSON from Claude Code (SessionStart hook input).
5
+ * Writes statusLine to the project's .claude/settings.local.json so the HUD
6
+ * only appears in projects where claude-crew is installed.
7
+ * Also removes the legacy global statusLine from ~/.claude/settings.json.
8
8
  */
9
9
 
10
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
10
+ import { execSync } from 'node:child_process';
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
11
12
  import { join } from 'node:path';
12
13
  import { homedir } from 'node:os';
13
14
 
@@ -26,61 +27,67 @@ async function readStdin(timeoutMs = 3000) {
26
27
  });
27
28
  }
28
29
 
30
+ function gitExec(cmd, cwd) {
31
+ try {
32
+ return execSync(cmd, { cwd, encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
33
+ } catch { return null; }
34
+ }
35
+
29
36
  // ---------------------------------------------------------------------------
30
37
  // Main
31
38
  // ---------------------------------------------------------------------------
32
39
  async function main() {
33
- // Consume stdin (required by hook protocol)
34
- await readStdin();
40
+ const raw = await readStdin();
35
41
 
36
- const configDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');
37
- const settingsPath = join(configDir, 'settings.json');
38
42
  const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
39
-
40
43
  if (!pluginRoot) {
41
- // Not running as a plugin — skip
42
44
  console.log(JSON.stringify({ continue: true }));
43
45
  return;
44
46
  }
45
47
 
48
+ let cwd = process.cwd();
49
+ if (raw) {
50
+ try { cwd = JSON.parse(raw).cwd || cwd; } catch { /* ignore */ }
51
+ }
52
+
53
+ // Use git toplevel as the reliable project root
54
+ const projectRoot = gitExec('git rev-parse --show-toplevel', cwd) || cwd;
55
+
46
56
  const hudCommand = `node "${pluginRoot}/hud/index.mjs"`;
57
+ const localSettingsPath = join(projectRoot, '.claude', 'settings.local.json');
47
58
 
48
59
  try {
49
- let settings = {};
50
- if (existsSync(settingsPath)) {
51
- settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
60
+ // --- Write statusLine to project-level settings.local.json ---
61
+ let localSettings = {};
62
+ if (existsSync(localSettingsPath)) {
63
+ try { localSettings = JSON.parse(readFileSync(localSettingsPath, 'utf-8')); } catch { /* ignore */ }
52
64
  }
53
65
 
54
- // Check if statusLine is already set to the *current* plugin path
55
- const currentCommand = settings.statusLine?.command || '';
56
- if (currentCommand === hudCommand) {
57
- // Already configured with this exact version
58
- console.log(JSON.stringify({ continue: true }));
59
- return;
66
+ if (localSettings.statusLine?.command !== hudCommand) {
67
+ localSettings.statusLine = { type: 'command', command: hudCommand };
68
+ mkdirSync(join(projectRoot, '.claude'), { recursive: true });
69
+ writeFileSync(localSettingsPath, JSON.stringify(localSettings, null, 2));
60
70
  }
61
71
 
62
- // Set statusLine to crew HUD
63
- settings.statusLine = {
64
- type: 'command',
65
- command: hudCommand,
66
- };
67
-
68
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
72
+ // --- Remove legacy global statusLine from ~/.claude/settings.json ---
73
+ const globalSettingsPath = join(process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude'), 'settings.json');
74
+ if (existsSync(globalSettingsPath)) {
75
+ try {
76
+ const globalSettings = JSON.parse(readFileSync(globalSettingsPath, 'utf-8'));
77
+ if (globalSettings.statusLine) {
78
+ delete globalSettings.statusLine;
79
+ writeFileSync(globalSettingsPath, JSON.stringify(globalSettings, null, 2));
80
+ }
81
+ } catch { /* ignore */ }
82
+ }
69
83
 
70
- console.log(JSON.stringify({
71
- continue: true,
72
- hookSpecificOutput: {
73
- hookEventName: 'SessionStart',
74
- additionalContext: 'CREW HUD가 자동 설정되었습니다. 다음 세션부터 statusline에 표시됩니다.',
75
- },
76
- }));
84
+ console.log(JSON.stringify({ continue: true }));
77
85
  } catch (e) {
78
- // Non-fatal — don't block session start
79
86
  console.log(JSON.stringify({
80
87
  continue: true,
81
88
  hookSpecificOutput: {
82
89
  hookEventName: 'SessionStart',
83
- additionalContext: `CREW HUD 자동 설정 실패: ${e.message}. /crew-setup을 수동 실행해주세요.`,
90
+ additionalContext: `CREW HUD 자동 설정 실패: ${e.message}`,
84
91
  },
85
92
  }));
86
93
  }