hool-cli 0.7.0 → 0.7.1

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.
Files changed (47) hide show
  1. package/agents/claude/be-dev.md +9 -1
  2. package/agents/claude/be-tech-lead.md +8 -0
  3. package/agents/claude/fe-dev.md +9 -1
  4. package/agents/claude/fe-tech-lead.md +8 -0
  5. package/agents/claude/forensic.md +8 -0
  6. package/agents/claude/governor.md +8 -0
  7. package/agents/claude/qa.md +8 -0
  8. package/dist/adapters/adapters.test.d.ts +1 -0
  9. package/dist/adapters/adapters.test.js +189 -0
  10. package/dist/adapters/adapters.test.js.map +1 -0
  11. package/dist/adapters/claude-code.js +21 -11
  12. package/dist/adapters/claude-code.js.map +1 -1
  13. package/dist/core/scaffold.js +20 -0
  14. package/dist/core/scaffold.js.map +1 -1
  15. package/dist/core/scaffold.test.d.ts +1 -0
  16. package/dist/core/scaffold.test.js +329 -0
  17. package/dist/core/scaffold.test.js.map +1 -0
  18. package/dist/core/templates.js +2 -2
  19. package/dist/core/templates.js.map +1 -1
  20. package/dist/core/templates.test.d.ts +1 -0
  21. package/dist/core/templates.test.js +146 -0
  22. package/dist/core/templates.test.js.map +1 -0
  23. package/dist/e2e.test.d.ts +1 -0
  24. package/dist/e2e.test.js +161 -0
  25. package/dist/e2e.test.js.map +1 -0
  26. package/dist/mcps/mcps.test.d.ts +1 -0
  27. package/dist/mcps/mcps.test.js +110 -0
  28. package/dist/mcps/mcps.test.js.map +1 -0
  29. package/hooks/inject-pl-context.sh +22 -3
  30. package/hooks/suggest-compact.sh +8 -12
  31. package/hooks/track-prompt-count.sh +24 -22
  32. package/package.json +13 -3
  33. package/prompts/agents/05-fe-tech-lead.md +3 -0
  34. package/prompts/agents/06-be-tech-lead.md +3 -0
  35. package/prompts/agents/08-be-dev.md +4 -1
  36. package/prompts/agents/08-fe-dev.md +4 -1
  37. package/prompts/agents/10-qa.md +1 -0
  38. package/prompts/agents/11-forensic.md +1 -0
  39. package/prompts/agents/governor.md +1 -0
  40. package/prompts/orchestrator.md +123 -31
  41. package/settings/be-dev.json +17 -0
  42. package/settings/be-tech-lead.json +17 -0
  43. package/settings/fe-dev.json +17 -0
  44. package/settings/fe-tech-lead.json +17 -0
  45. package/settings/forensic.json +17 -0
  46. package/settings/governor.json +17 -0
  47. package/settings/qa.json +17 -0
@@ -0,0 +1,161 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import { execFile } from 'child_process';
6
+ import { promisify } from 'util';
7
+ const exec = promisify(execFile);
8
+ let tmpDir;
9
+ const cliPath = path.resolve(import.meta.dirname, '..', '..', 'cli');
10
+ beforeEach(async () => {
11
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'hool-e2e-'));
12
+ });
13
+ afterEach(async () => {
14
+ await fs.rm(tmpDir, { recursive: true, force: true });
15
+ });
16
+ async function runHool(args) {
17
+ return exec('npx', ['tsx', path.join(cliPath, 'src/index.ts'), ...args], {
18
+ cwd: tmpDir,
19
+ env: { ...process.env, NO_COLOR: '1' },
20
+ });
21
+ }
22
+ describe('hool init (e2e)', () => {
23
+ it('scaffolds a web-app project with all flags', async () => {
24
+ const { stdout } = await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
25
+ expect(stdout).toContain('HOOL initialized');
26
+ // Verify structure
27
+ const ops = await fs.readdir(path.join(tmpDir, '.hool/operations'));
28
+ expect(ops).toContain('current-phase.md');
29
+ expect(ops).toContain('task-board.md');
30
+ expect(ops).toContain('governor-rules.md');
31
+ // Verify phase dirs
32
+ await expect(fs.stat(path.join(tmpDir, '.hool/phases/03-design/cards'))).resolves.toBeTruthy();
33
+ await expect(fs.stat(path.join(tmpDir, '.hool/phases/05-fe-scaffold/pages'))).resolves.toBeTruthy();
34
+ // Verify memory dirs
35
+ const memAgents = await fs.readdir(path.join(tmpDir, '.hool/memory'));
36
+ expect(memAgents).toHaveLength(8);
37
+ // Verify CLAUDE.md
38
+ const claudeMd = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf-8');
39
+ expect(claudeMd).toContain('<!-- HOOL:START -->');
40
+ expect(claudeMd).toContain('<!-- HOOL:END -->');
41
+ // Verify agents.json
42
+ const agents = JSON.parse(await fs.readFile(path.join(tmpDir, '.hool/agents.json'), 'utf-8'));
43
+ expect(agents).toHaveLength(8);
44
+ // Verify mcps.json
45
+ const mcps = JSON.parse(await fs.readFile(path.join(tmpDir, '.hool/mcps.json'), 'utf-8'));
46
+ expect(mcps.domain).toBe('web-app');
47
+ expect(mcps.servers).toHaveProperty('context7');
48
+ }, 30000);
49
+ it('scaffolds a cli-tool project skipping FE phases', async () => {
50
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'cli-tool', '-m', 'interactive']);
51
+ await expect(fs.access(path.join(tmpDir, '.hool/phases/03-design'))).rejects.toThrow();
52
+ await expect(fs.access(path.join(tmpDir, '.hool/phases/05-fe-scaffold'))).rejects.toThrow();
53
+ await expect(fs.stat(path.join(tmpDir, '.hool/phases/06-be-scaffold/services'))).resolves.toBeTruthy();
54
+ }, 30000);
55
+ it('scaffolds full-hool mode', async () => {
56
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'full-hool']);
57
+ const phase = await fs.readFile(path.join(tmpDir, '.hool/operations/current-phase.md'), 'utf-8');
58
+ expect(phase).toContain('full-hool');
59
+ const profile = await fs.readFile(path.join(tmpDir, '.hool/phases/00-init/project-profile.md'), 'utf-8');
60
+ expect(profile).toContain('full-hool');
61
+ }, 30000);
62
+ it('copies per-role settings to .hool/settings/', async () => {
63
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
64
+ const settingsDir = path.join(tmpDir, '.hool/settings');
65
+ const files = await fs.readdir(settingsDir);
66
+ // Should have role settings but NOT claude-settings.json
67
+ expect(files.some(f => f.endsWith('.json'))).toBe(true);
68
+ expect(files).not.toContain('claude-settings.json');
69
+ // Each settings file should be valid JSON
70
+ for (const f of files) {
71
+ if (!f.endsWith('.json'))
72
+ continue;
73
+ const content = await fs.readFile(path.join(settingsDir, f), 'utf-8');
74
+ expect(() => JSON.parse(content)).not.toThrow();
75
+ }
76
+ }, 30000);
77
+ it('copies agent definitions to .claude/agents/', async () => {
78
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
79
+ const agentsDir = path.join(tmpDir, '.claude/agents');
80
+ const files = await fs.readdir(agentsDir);
81
+ expect(files.length).toBeGreaterThan(0);
82
+ // All should be .md files
83
+ expect(files.every(f => f.endsWith('.md'))).toBe(true);
84
+ }, 30000);
85
+ it('creates .claude/settings.json with hooks', async () => {
86
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
87
+ const settings = JSON.parse(await fs.readFile(path.join(tmpDir, '.claude/settings.json'), 'utf-8'));
88
+ expect(settings).toHaveProperty('hooks');
89
+ }, 30000);
90
+ it('creates executable hooks', async () => {
91
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
92
+ const hooksDir = path.join(tmpDir, '.hool/hooks');
93
+ const hooks = await fs.readdir(hooksDir);
94
+ for (const hook of hooks) {
95
+ if (!hook.endsWith('.sh'))
96
+ continue;
97
+ const stat = await fs.stat(path.join(hooksDir, hook));
98
+ expect(stat.mode & 0o111).toBeGreaterThan(0);
99
+ }
100
+ }, 30000);
101
+ it('scaffolds for cursor platform', async () => {
102
+ await runHool(['init', '-d', tmpDir, '-p', 'cursor', '-t', 'web-app', '-m', 'interactive']);
103
+ const rules = await fs.readFile(path.join(tmpDir, '.cursor/rules/hool.mdc'), 'utf-8');
104
+ expect(rules).toContain('HOOL');
105
+ expect(rules).toContain('Product Lead');
106
+ }, 30000);
107
+ it('scaffolds for generic platform', async () => {
108
+ await runHool(['init', '-d', tmpDir, '-p', 'generic', '-t', 'web-app', '-m', 'interactive']);
109
+ const instructions = await fs.readFile(path.join(tmpDir, 'HOOL-INSTRUCTIONS.md'), 'utf-8');
110
+ expect(instructions).toContain('HOOL');
111
+ }, 30000);
112
+ });
113
+ describe('hool onboard (e2e)', () => {
114
+ it('scaffolds onboarding structure', async () => {
115
+ await runHool(['onboard', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
116
+ const phase = await fs.readFile(path.join(tmpDir, '.hool/operations/current-phase.md'), 'utf-8');
117
+ expect(phase).toContain('onboarding');
118
+ const board = await fs.readFile(path.join(tmpDir, '.hool/operations/task-board.md'), 'utf-8');
119
+ expect(board).toContain('ONBOARD-001');
120
+ const profile = await fs.readFile(path.join(tmpDir, '.hool/phases/00-init/project-profile.md'), 'utf-8');
121
+ expect(profile).toContain('onboarded');
122
+ }, 30000);
123
+ });
124
+ describe('hool status (e2e)', () => {
125
+ it('shows status for an initialized project', async () => {
126
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
127
+ const { stdout } = await runHool(['status', '-d', tmpDir]);
128
+ expect(stdout).toContain('Phase');
129
+ expect(stdout).toContain('Tasks');
130
+ expect(stdout).toContain('Bugs');
131
+ }, 30000);
132
+ it('shows error for non-HOOL directory', async () => {
133
+ const { stdout } = await runHool(['status', '-d', tmpDir]);
134
+ expect(stdout).toContain('Not a HOOL project');
135
+ }, 30000);
136
+ });
137
+ describe('hool mode (e2e)', () => {
138
+ it('shows current mode', async () => {
139
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
140
+ const { stdout } = await runHool(['mode', '-d', tmpDir]);
141
+ expect(stdout).toContain('interactive');
142
+ }, 30000);
143
+ it('switches mode from interactive to full-hool', async () => {
144
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
145
+ const { stdout } = await runHool(['mode', 'full-hool', '-d', tmpDir]);
146
+ expect(stdout).toContain('interactive -> full-hool');
147
+ const profile = await fs.readFile(path.join(tmpDir, '.hool/phases/00-init/project-profile.md'), 'utf-8');
148
+ expect(profile).toContain('full-hool');
149
+ }, 30000);
150
+ it('reports when already in requested mode', async () => {
151
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
152
+ const { stdout } = await runHool(['mode', 'interactive', '-d', tmpDir]);
153
+ expect(stdout).toContain('Already in');
154
+ }, 30000);
155
+ it('rejects invalid mode', async () => {
156
+ await runHool(['init', '-d', tmpDir, '-p', 'claude-code', '-t', 'web-app', '-m', 'interactive']);
157
+ const { stdout } = await runHool(['mode', 'invalid', '-d', tmpDir]);
158
+ expect(stdout).toContain('Invalid mode');
159
+ }, 30000);
160
+ });
161
+ //# sourceMappingURL=e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e.test.js","sourceRoot":"","sources":["../src/e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,110 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { MCP_REGISTRY, getRequiredMcps, getRequiredMcpNames } from './registry.js';
3
+ import { checkAndInstallMcps } from './installer.js';
4
+ describe('MCP_REGISTRY', () => {
5
+ it('contains context7, playwright, deepwiki', () => {
6
+ expect(MCP_REGISTRY).toHaveProperty('context7');
7
+ expect(MCP_REGISTRY).toHaveProperty('playwright');
8
+ expect(MCP_REGISTRY).toHaveProperty('deepwiki');
9
+ });
10
+ it('each entry has name, installCommand, configEntry', () => {
11
+ for (const [key, mcp] of Object.entries(MCP_REGISTRY)) {
12
+ expect(mcp.name).toBe(key);
13
+ expect(mcp.installCommand).toBeTruthy();
14
+ expect(mcp.configEntry).toHaveProperty('command');
15
+ expect(mcp.configEntry).toHaveProperty('args');
16
+ }
17
+ });
18
+ });
19
+ describe('getRequiredMcps', () => {
20
+ it('returns context7 + deepwiki + playwright for web-app', () => {
21
+ const mcps = getRequiredMcps('web-app');
22
+ const names = mcps.map(m => m.name);
23
+ expect(names).toContain('context7');
24
+ expect(names).toContain('deepwiki');
25
+ expect(names).toContain('playwright');
26
+ });
27
+ it('returns context7 + deepwiki for cli-tool (no playwright)', () => {
28
+ const mcps = getRequiredMcps('cli-tool');
29
+ const names = mcps.map(m => m.name);
30
+ expect(names).toContain('context7');
31
+ expect(names).toContain('deepwiki');
32
+ expect(names).not.toContain('playwright');
33
+ });
34
+ it('returns context7 + deepwiki + playwright for browser-game', () => {
35
+ const mcps = getRequiredMcps('browser-game');
36
+ const names = mcps.map(m => m.name);
37
+ expect(names).toContain('playwright');
38
+ });
39
+ it('returns context7 + deepwiki for api-only', () => {
40
+ const mcps = getRequiredMcps('api-only');
41
+ const names = mcps.map(m => m.name);
42
+ expect(names).toEqual(['context7', 'deepwiki']);
43
+ });
44
+ it('returns McpDefinition objects with full config', () => {
45
+ const mcps = getRequiredMcps('web-app');
46
+ for (const mcp of mcps) {
47
+ expect(mcp).toHaveProperty('name');
48
+ expect(mcp).toHaveProperty('installCommand');
49
+ expect(mcp).toHaveProperty('configEntry');
50
+ }
51
+ });
52
+ });
53
+ describe('getRequiredMcpNames', () => {
54
+ it('returns string array of MCP names', () => {
55
+ const names = getRequiredMcpNames('web-app');
56
+ expect(names).toEqual(['context7', 'deepwiki', 'playwright']);
57
+ });
58
+ it('returns context7 + deepwiki for desktop', () => {
59
+ expect(getRequiredMcpNames('desktop')).toEqual(['context7', 'deepwiki']);
60
+ });
61
+ });
62
+ describe('checkAndInstallMcps', () => {
63
+ function createMockAdapter(installed = new Set()) {
64
+ return {
65
+ platform: 'claude-code',
66
+ injectInstructions: vi.fn(),
67
+ installMcp: vi.fn(),
68
+ isMcpInstalled: vi.fn(async (name) => installed.has(name)),
69
+ getCompletionMessage: vi.fn(() => ''),
70
+ };
71
+ }
72
+ const config = {
73
+ platform: 'claude-code',
74
+ projectType: 'cli-tool',
75
+ projectDir: '/tmp/test',
76
+ promptsDir: '/tmp/test/prompts',
77
+ mode: 'interactive',
78
+ };
79
+ it('returns already-installed for pre-installed MCPs', async () => {
80
+ const adapter = createMockAdapter(new Set(['context7', 'deepwiki']));
81
+ const results = await checkAndInstallMcps(adapter, config);
82
+ expect(results).toHaveLength(2);
83
+ expect(results.every(r => r.status === 'already-installed')).toBe(true);
84
+ expect(adapter.installMcp).not.toHaveBeenCalled();
85
+ });
86
+ it('installs missing MCPs', async () => {
87
+ const adapter = createMockAdapter(new Set(['context7'])); // deepwiki missing
88
+ const results = await checkAndInstallMcps(adapter, config);
89
+ const deepwikiResult = results.find(r => r.name === 'deepwiki');
90
+ expect(deepwikiResult?.status).toBe('installed');
91
+ expect(adapter.installMcp).toHaveBeenCalledOnce();
92
+ });
93
+ it('handles install failure gracefully', async () => {
94
+ const adapter = createMockAdapter();
95
+ adapter.installMcp.mockRejectedValue(new Error('Network error'));
96
+ const results = await checkAndInstallMcps(adapter, config);
97
+ expect(results.every(r => r.status === 'failed')).toBe(true);
98
+ expect(results[0].error).toBe('Network error');
99
+ });
100
+ it('processes all required MCPs for project type', async () => {
101
+ const webConfig = { ...config, projectType: 'web-app' };
102
+ const adapter = createMockAdapter();
103
+ const results = await checkAndInstallMcps(adapter, webConfig);
104
+ const names = results.map(r => r.name);
105
+ expect(names).toContain('context7');
106
+ expect(names).toContain('deepwiki');
107
+ expect(names).toContain('playwright');
108
+ });
109
+ });
110
+ //# sourceMappingURL=mcps.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcps.test.js","sourceRoot":"","sources":["../../src/mcps/mcps.test.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,24 @@
4
4
  # Outputs JSON with additionalContext to remind the PL of its identity and state
5
5
 
6
6
  PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo '.')"
7
+ METRICS_FILE="$PROJECT_ROOT/.hool/operations/metrics.md"
8
+
9
+ # Ensure metrics file exists with default counters
10
+ if [ ! -f "$METRICS_FILE" ]; then
11
+ mkdir -p "$(dirname "$METRICS_FILE")"
12
+ cat > "$METRICS_FILE" <<'EOF'
13
+ # HOOL Metrics
14
+ - Agent dispatches: 0
15
+ - Tool calls: 0
16
+ - User prompts: 0
17
+ EOF
18
+ fi
19
+
20
+ # Increment user prompts counter
21
+ CURRENT_PROMPTS=$(grep -o 'User prompts: [0-9]*' "$METRICS_FILE" | grep -o '[0-9]*')
22
+ CURRENT_PROMPTS=${CURRENT_PROMPTS:-0}
23
+ NEXT_PROMPTS=$((CURRENT_PROMPTS + 1))
24
+ sed -i '' "s/User prompts: ${CURRENT_PROMPTS}/User prompts: ${NEXT_PROMPTS}/" "$METRICS_FILE"
7
25
 
8
26
  # Read current phase
9
27
  PHASE="unknown"
@@ -80,10 +98,11 @@ if [ -f "$PROJECT_ROOT/.hool/operations/inconsistencies.md" ]; then
80
98
  INCONSISTENCIES=$(grep -c '^- ' "$PROJECT_ROOT/.hool/operations/inconsistencies.md" 2>/dev/null | tr -d '\n' || echo "0")
81
99
  fi
82
100
 
83
- # Check governor dispatch count
101
+ # Check governor dispatch count from metrics.md
84
102
  DISPATCH_COUNT=0
85
- if [ -f "$PROJECT_ROOT/.hool/metrics/dispatch-count.txt" ]; then
86
- DISPATCH_COUNT=$(cat "$PROJECT_ROOT/.hool/metrics/dispatch-count.txt" 2>/dev/null | tr -d '\n' || echo "0")
103
+ if [ -f "$METRICS_FILE" ]; then
104
+ DISPATCH_COUNT=$(grep -o 'Agent dispatches: [0-9]*' "$METRICS_FILE" | grep -o '[0-9]*' || echo "0")
105
+ DISPATCH_COUNT=${DISPATCH_COUNT:-0}
87
106
  fi
88
107
  GOVERNOR_DUE=""
89
108
  if [ "$DISPATCH_COUNT" -gt 0 ] && [ $(( DISPATCH_COUNT % 3 )) -ge 2 ]; then
@@ -5,27 +5,23 @@
5
5
  # Strategic compaction between phases preserves better context.
6
6
 
7
7
  PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo '.')"
8
- METRICS_DIR="$PROJECT_ROOT/.hool/metrics"
9
- mkdir -p "$METRICS_DIR"
10
-
11
- COUNTER_FILE="$METRICS_DIR/tool-call-count.txt"
8
+ METRICS_FILE="$PROJECT_ROOT/.hool/operations/metrics.md"
12
9
  THRESHOLD="${HOOL_COMPACT_THRESHOLD:-50}"
13
10
 
14
- # Read and increment counter
15
- CURRENT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
16
- NEXT=$((CURRENT + 1))
17
- echo "$NEXT" > "$COUNTER_FILE"
11
+ # Read tool call count from metrics.md (format: "- Tool calls: N")
12
+ COUNT=$(grep -o 'Tool calls: [0-9]*' "$METRICS_FILE" 2>/dev/null | grep -o '[0-9]*')
13
+ COUNT="${COUNT:-0}"
18
14
 
19
15
  # Suggest at threshold
20
- if [ "$NEXT" -eq "$THRESHOLD" ]; then
16
+ if [ "$COUNT" -eq "$THRESHOLD" ]; then
21
17
  echo "HOOL: ${THRESHOLD} tool calls reached. Consider running /compact if you're between phases or tasks." >&2
22
18
  fi
23
19
 
24
20
  # Suggest every 25 calls after threshold
25
- if [ "$NEXT" -gt "$THRESHOLD" ]; then
26
- OVER=$((NEXT - THRESHOLD))
21
+ if [ "$COUNT" -gt "$THRESHOLD" ]; then
22
+ OVER=$((COUNT - THRESHOLD))
27
23
  if [ $((OVER % 25)) -eq 0 ]; then
28
- echo "HOOL: ${NEXT} tool calls. Good checkpoint for /compact if context feels stale." >&2
24
+ echo "HOOL: ${COUNT} tool calls. Good checkpoint for /compact if context feels stale." >&2
29
25
  fi
30
26
  fi
31
27
 
@@ -1,39 +1,41 @@
1
1
  #!/bin/bash
2
- # Hook: Track prompt/tool count and trigger governor every 3 dispatches
2
+ # Hook: Track tool calls + agent dispatches in metrics.md, trigger governor every 3 dispatches
3
3
  # Type: PostToolUse
4
4
  # Outputs JSON with additionalContext when governor should run
5
5
 
6
6
  PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo '.')"
7
- METRICS_DIR="$PROJECT_ROOT/.hool/metrics"
8
- mkdir -p "$METRICS_DIR"
9
-
10
- COUNTER_FILE="$METRICS_DIR/prompt-count.log"
11
- SESSION_FILE="$METRICS_DIR/current-session.txt"
12
- DISPATCH_FILE="$METRICS_DIR/dispatch-count.txt"
13
-
14
- # Get or create session ID
15
- if [ ! -f "$SESSION_FILE" ]; then
16
- echo "session-$(date +%Y%m%d-%H%M%S)" > "$SESSION_FILE"
7
+ METRICS_FILE="$PROJECT_ROOT/.hool/operations/metrics.md"
8
+
9
+ # Ensure metrics file exists with default counters
10
+ if [ ! -f "$METRICS_FILE" ]; then
11
+ mkdir -p "$(dirname "$METRICS_FILE")"
12
+ cat > "$METRICS_FILE" <<'EOF'
13
+ # HOOL Metrics
14
+ - Agent dispatches: 0
15
+ - Tool calls: 0
16
+ - User prompts: 0
17
+ EOF
17
18
  fi
18
19
 
19
- TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
20
-
21
- # Append to log
22
- echo "$TIMESTAMP | tool-call" >> "$COUNTER_FILE"
20
+ # Increment tool calls counter
21
+ CURRENT_TOOLS=$(grep -o 'Tool calls: [0-9]*' "$METRICS_FILE" | grep -o '[0-9]*')
22
+ CURRENT_TOOLS=${CURRENT_TOOLS:-0}
23
+ NEXT_TOOLS=$((CURRENT_TOOLS + 1))
24
+ sed -i '' "s/Tool calls: ${CURRENT_TOOLS}/Tool calls: ${NEXT_TOOLS}/" "$METRICS_FILE"
23
25
 
24
- # Track dispatch count (Agent tool calls specifically)
25
- # Read from stdin to check tool name
26
+ # Read stdin to check tool name
26
27
  INPUT=$(cat)
27
28
  TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"tool_name"[[:space:]]*:[[:space:]]*"//' | sed 's/"$//')
28
29
 
29
30
  if [ "$TOOL_NAME" = "Agent" ]; then
30
- # Increment dispatch counter
31
- CURRENT=$(cat "$DISPATCH_FILE" 2>/dev/null || echo "0")
32
- NEXT=$((CURRENT + 1))
33
- echo "$NEXT" > "$DISPATCH_FILE"
31
+ # Increment agent dispatches counter
32
+ CURRENT_DISPATCHES=$(grep -o 'Agent dispatches: [0-9]*' "$METRICS_FILE" | grep -o '[0-9]*')
33
+ CURRENT_DISPATCHES=${CURRENT_DISPATCHES:-0}
34
+ NEXT_DISPATCHES=$((CURRENT_DISPATCHES + 1))
35
+ sed -i '' "s/Agent dispatches: ${CURRENT_DISPATCHES}/Agent dispatches: ${NEXT_DISPATCHES}/" "$METRICS_FILE"
34
36
 
35
37
  # Check if divisible by 3
36
- if [ $((NEXT % 3)) -eq 0 ]; then
38
+ if [ $((NEXT_DISPATCHES % 3)) -eq 0 ]; then
37
39
  # Output JSON to inject governor reminder into conversation
38
40
  cat <<'JSONEOF'
39
41
  {
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "hool-cli",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Agent-Driven SDLC — scaffold and configure HOOL for any project",
5
5
  "bin": {
6
6
  "hool": "./dist/index.js"
7
7
  },
8
8
  "scripts": {
9
9
  "build": "tsc",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest",
10
12
  "dev": "tsx src/index.ts",
11
13
  "prepublishOnly": "npm run build && rm -rf ./prompts ./agents ./rules ./hooks ./settings && cp -R ../hool-mini/prompts ./prompts && cp -R ../hool-mini/agents ./agents && cp -R ../hool-mini/rules ./rules && cp -R ../hool-mini/hooks ./hooks && cp -R ../hool-mini/settings ./settings"
12
14
  },
@@ -27,7 +29,8 @@
27
29
  "devDependencies": {
28
30
  "@types/node": "^20.11.0",
29
31
  "tsx": "^4.7.0",
30
- "typescript": "^5.4.0"
32
+ "typescript": "^5.4.0",
33
+ "vitest": "^4.0.18"
31
34
  },
32
35
  "type": "module",
33
36
  "license": "MIT",
@@ -35,7 +38,14 @@
35
38
  "type": "git",
36
39
  "url": "git+https://github.com/the-wise-agents/hool.git"
37
40
  },
38
- "keywords": ["agent", "sdlc", "ai", "cli", "scaffold", "autonomous"],
41
+ "keywords": [
42
+ "agent",
43
+ "sdlc",
44
+ "ai",
45
+ "cli",
46
+ "scaffold",
47
+ "autonomous"
48
+ ],
39
49
  "engines": {
40
50
  "node": ">=18"
41
51
  }
@@ -156,6 +156,9 @@ How logging works. Where logs go. How to read them.
156
156
  - Code inconsistency found -> write to .hool/operations/inconsistencies.md with INC-XXX format
157
157
  - Doc inconsistency found (spec says X, contract says Y) -> escalate to Product Lead via .hool/operations/inconsistencies.md
158
158
 
159
+ ## Forbidden Actions
160
+ - NEVER run git commands (add, commit, push, etc.) — the Product Lead commits your work after you return
161
+
159
162
  ## Work Log
160
163
  ### Tags
161
164
  - [ARCH-FE] — FE architectural decision -> best-practices.md
@@ -177,6 +177,9 @@ Order matters. List in execution order:
177
177
  - Code inconsistency found -> write to .hool/operations/inconsistencies.md with INC-XXX format
178
178
  - Doc inconsistency found (spec says X, contract says Y) -> escalate to Product Lead via .hool/operations/inconsistencies.md
179
179
 
180
+ ## Forbidden Actions
181
+ - NEVER run git commands (add, commit, push, etc.) — the Product Lead commits your work after you return
182
+
180
183
  ## Work Log
181
184
  ### Tags
182
185
  - [ARCH-BE] — BE architectural decision -> best-practices.md
@@ -58,7 +58,7 @@ If you believe your own process or rules should change based on experience, esca
58
58
  5. **Logs**: Every request, DB query, and error gets a log statement.
59
59
  6. **Contracts**: Your API responses MUST match .hool/phases/04-architecture/contracts/ exactly. Field names, types, status codes — zero deviation.
60
60
  7. **Schema**: Your queries MUST work with .hool/phases/04-architecture/schema.md. Never modify schema without logging an inconsistency.
61
- 8. **Small commits**: Each task = one logical unit of work.
61
+ 8. **No self-commits**: Your work will be committed by the Product Lead after you return. Focus on the implementation, not version control.
62
62
  9. **Consistency gate**: Before implementing, cross-check your task against contracts, schema, and spec. If you find ANY inconsistency between docs, DO NOT proceed — log to .hool/operations/inconsistencies.md.
63
63
 
64
64
  ## Test Execution Requirement (MANDATORY)
@@ -133,6 +133,9 @@ logger.info('query result:', result) // log the shape, not the data (PII risk
133
133
  - Need a schema change -> DON'T make it. Log to .hool/operations/inconsistencies.md for BE Tech Lead to review
134
134
  - Architecture seems wrong -> DON'T change it. Log to .hool/operations/inconsistencies.md for BE Tech Lead to review
135
135
 
136
+ ## Forbidden Actions
137
+ - NEVER run git commands (add, commit, push, etc.) — the Product Lead commits your work after you return
138
+
136
139
  ## Work Log
137
140
  ### Tags
138
141
  - [BE-IMPL] — endpoint/service implemented
@@ -56,7 +56,7 @@ If you believe your own process or rules should change based on experience, esca
56
56
  5. **Logs**: Every significant user action and API call gets a log statement.
57
57
  6. **Design fidelity**: Your UI MUST match .hool/phases/03-design/cards/. Compare visually.
58
58
  7. **Contracts**: Your API calls MUST use the shapes from .hool/phases/04-architecture/contracts/ exactly.
59
- 8. **Small commits**: Each task = one logical unit of work.
59
+ 8. **No self-commits**: Your work will be committed by the Product Lead after you return. Focus on the implementation, not version control.
60
60
  9. **Consistency gate**: Before implementing, cross-check your task against contracts, design cards, and spec. If you find ANY inconsistency between docs, DO NOT proceed — log to .hool/operations/inconsistencies.md.
61
61
 
62
62
  ## Test Execution Requirement (MANDATORY)
@@ -122,6 +122,9 @@ logger.info('useEffect fired') // use React DevTools
122
122
  - Design seems wrong -> DON'T change design. Log to .hool/operations/inconsistencies.md for FE Tech Lead to review
123
123
  - Need a BE endpoint that doesn't exist -> DON'T build it. Log to .hool/operations/inconsistencies.md for FE Tech Lead to review
124
124
 
125
+ ## Forbidden Actions
126
+ - NEVER run git commands (add, commit, push, etc.) — the Product Lead commits your work after you return
127
+
125
128
  ## Work Log
126
129
  ### Tags
127
130
  - [FE-IMPL] — component/page implemented
@@ -264,6 +264,7 @@ Log the score in your work log: `[QA-SCORE] [total]/100 — [verdict]`
264
264
  - Don't review code quality. That's the Tech Lead's job.
265
265
  - Don't suggest architectural changes.
266
266
  - Don't modify source code.
267
+ - Don't run git commands (add, commit, push, etc.) — the Product Lead commits your work after you return.
267
268
 
268
269
  ## MCP Tools Available
269
270
  - playwright: E2E testing, screenshot capture, browser automation
@@ -108,6 +108,7 @@ Don't fabricate a root cause. Honesty saves time.
108
108
  - Don't apply fixes — document them for devs.
109
109
  - Don't refactor surrounding code.
110
110
  - Don't make architectural recommendations.
111
+ - Don't run git commands (add, commit, push, etc.) — the Product Lead commits your work after you return.
111
112
 
112
113
  ## MCP Tools Available
113
114
  - playwright: reproduce UI bugs in browser
@@ -32,6 +32,7 @@ Agents self-enforce rules, but self-enforcement fails (as observed: Product Lead
32
32
  - **NEVER** remove or modify existing entries in `.hool/operations/governor-rules.md` — append only (unless a rule is provably wrong, in which case escalate to human)
33
33
  - **NEVER** edit application code (`src/`, `tests/`)
34
34
  - **NEVER** modify `.hool/operations/task-board.md` or `.hool/operations/current-phase.md` — that's the Product Lead's job
35
+ - **NEVER** run git commands (add, commit, push, etc.) — the Product Lead commits your work after you return
35
36
 
36
37
  ## Audit Process
37
38