@haposoft/cafekit 0.14.1 → 0.15.0

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 (100) hide show
  1. package/README.md +86 -11
  2. package/bin/__tests__/codex-hooks.test.js +331 -0
  3. package/bin/__tests__/codex-native.test.js +458 -0
  4. package/bin/__tests__/installer-safety.test.js +259 -0
  5. package/bin/install.js +30 -8
  6. package/bin/lib/backup.js +82 -18
  7. package/bin/lib/codex-frontmatter.js +56 -0
  8. package/bin/lib/codex-install.js +195 -0
  9. package/bin/lib/context.js +65 -8
  10. package/bin/lib/copy-utils.js +1 -1
  11. package/bin/lib/i18n.js +12 -3
  12. package/bin/lib/managed-writer.js +17 -2
  13. package/bin/lib/manifest.js +21 -4
  14. package/bin/lib/opencode-install.js +21 -19
  15. package/bin/lib/path-safety.js +40 -0
  16. package/bin/lib/version-check.js +25 -12
  17. package/bin/phases/claude-runtime.js +60 -9
  18. package/bin/phases/codex-runtime.js +110 -0
  19. package/bin/phases/copy-payload.js +36 -16
  20. package/bin/phases/post-install.js +98 -48
  21. package/bin/phases/root-config.js +4 -2
  22. package/bin/phases/select-platform.js +105 -66
  23. package/bin/phases/summary.js +6 -1
  24. package/package.json +3 -2
  25. package/src/claude/CLAUDE.md +1 -1
  26. package/src/claude/agents/code-auditor.md +1 -1
  27. package/src/claude/agents/debugger.md +1 -0
  28. package/src/claude/agents/god-developer.md +10 -1
  29. package/src/claude/agents/inspector.md +1 -0
  30. package/src/claude/agents/spec-maker.md +1 -1
  31. package/src/claude/agents/test-runner.md +2 -2
  32. package/src/claude/hooks/__tests__/privacy-block.test.js +50 -10
  33. package/src/claude/hooks/__tests__/spec-gate.test.js +75 -1
  34. package/src/claude/hooks/__tests__/state.test.js +65 -0
  35. package/src/claude/hooks/docs-sync.cjs +15 -21
  36. package/src/claude/hooks/inspect-block.cjs +1 -1
  37. package/src/claude/hooks/lib/config.cjs +0 -5
  38. package/src/claude/hooks/privacy-block.cjs +39 -62
  39. package/src/claude/hooks/spec-gate.cjs +18 -7
  40. package/src/claude/hooks/spec-state.cjs +4 -0
  41. package/src/claude/hooks/state.cjs +13 -3
  42. package/src/claude/hooks/usage.cjs +6 -0
  43. package/src/claude/rules/orchestrator.md +2 -2
  44. package/src/claude/rules/skill-domain-routing.md +27 -86
  45. package/src/claude/rules/skill-workflow-routing.md +11 -82
  46. package/src/claude/rules/state-sync.md +1 -1
  47. package/src/claude/runtime.json +7 -7
  48. package/src/claude/scripts/generate-skill-catalog.cjs +8 -3
  49. package/src/claude/scripts/validate-spec-output.cjs +6 -0
  50. package/src/claude/skills/code-review/references/spec-compliance-review.md +1 -1
  51. package/src/claude/skills/develop/SKILL.md +26 -5
  52. package/src/claude/skills/develop/references/parallel-waves.md +112 -0
  53. package/src/claude/skills/develop/references/quality-gate.md +10 -2
  54. package/src/claude/skills/hotfix/references/parallel-patterns.md +1 -1
  55. package/src/claude/skills/inspect/SKILL.md +42 -113
  56. package/src/claude/skills/inspect/references/internal-inspection.md +16 -135
  57. package/src/claude/skills/specs/SKILL.md +134 -386
  58. package/src/claude/skills/specs/rules/tasks-generation.md +1 -1
  59. package/src/claude/skills/specs/templates/task.md +3 -1
  60. package/src/claude/skills/sync/SKILL.md +2 -2
  61. package/src/claude/skills/sync/references/sync-protocols.md +4 -4
  62. package/src/claude/skills/test/SKILL.md +1 -1
  63. package/src/claude/status.cjs +9 -6
  64. package/src/codex/AGENTS.md +30 -0
  65. package/src/codex/agents-gitignore +11 -0
  66. package/src/codex/gitignore +12 -0
  67. package/src/codex/hooks/agent.cjs +66 -0
  68. package/src/codex/hooks/docs-sync.cjs +66 -0
  69. package/src/codex/hooks/inspect-block.cjs +40 -0
  70. package/src/codex/hooks/lib/hook-context.cjs +93 -0
  71. package/src/codex/hooks/lib/inspect-paths.cjs +199 -0
  72. package/src/codex/hooks/lib/privacy-state.cjs +199 -0
  73. package/src/codex/hooks/lib/spec-receipt.cjs +78 -0
  74. package/src/codex/hooks/lib/spec-utils.cjs +50 -0
  75. package/src/codex/hooks/lib/state-store.cjs +166 -0
  76. package/src/codex/hooks/privacy-approval.cjs +59 -0
  77. package/src/codex/hooks/privacy-block.cjs +182 -0
  78. package/src/codex/hooks/rules.cjs +79 -0
  79. package/src/codex/hooks/session.cjs +114 -0
  80. package/src/codex/hooks/spec-gate.cjs +80 -0
  81. package/src/codex/hooks/spec-state.cjs +73 -0
  82. package/src/codex/hooks/state.cjs +157 -0
  83. package/src/codex/hooks/task-scaffold-guard.cjs +63 -0
  84. package/src/codex/hooks.json +122 -0
  85. package/src/codex/rules/hook-protocols.md +32 -0
  86. package/src/codex/rules/state-sync.md +41 -0
  87. package/src/codex/runtime.json +28 -0
  88. package/src/opencode/plugins/docs-sync.ts +10 -16
  89. package/src/claude/archive-command/code/SKILL.md +0 -55
  90. package/src/claude/archive-command/code/references/execution-loop.md +0 -21
  91. package/src/claude/archive-command/code.md +0 -17
  92. package/src/claude/archive-command/docs.md +0 -609
  93. package/src/claude/archive-command/review/codebase/parallel.md +0 -76
  94. package/src/claude/archive-command/spec-design.md +0 -247
  95. package/src/claude/archive-command/spec-init.md +0 -120
  96. package/src/claude/archive-command/spec-requirements.md +0 -138
  97. package/src/claude/archive-command/spec-status.md +0 -98
  98. package/src/claude/archive-command/spec-tasks.md +0 -173
  99. package/src/claude/archive-command/spec-validate.md +0 -118
  100. package/src/claude/archive-command/test.md +0 -8
@@ -39,24 +39,20 @@ try {
39
39
 
40
40
  const lines = [];
41
41
 
42
- // Trường hợp 1: Có Code nhưng chưa có thư mục docs
42
+ // Case 1: source present but docs/ missing
43
43
  if (!fs.existsSync(docsDir)) {
44
44
  lines.push('');
45
- lines.push('### 🔴 URGENT SYSTEM ACTION: MISSING DOCUMENTATION 🔴');
46
- lines.push(`> BẮT BUỘC (MANDATORY): Hệ thống phát hiện dự án đã có Source Code nhưng thư mục \`docs/\` chưa tồn tại.`);
47
- lines.push(`> Nhiệm vụ ĐẦU TIÊN của bạn trong session này là đọc hiểu mã nguồn hiện tại và tạo ra các tài liệu tiêu chuẩn:`);
48
- lines.push(`> 1. \`docs/system-architecture.md\` (Kiến trúc hệ thống, phân tích flow)`);
49
- lines.push(`> 2. \`docs/project-overview-pdr.md\` (Tổng quan, mục tiêu, tính năng)`);
50
- lines.push(`> 3. Khởi tạo file hidden \`docs/.sync_hash\` để theo dõi cập nhật.`);
51
- lines.push(`> `);
52
- lines.push(`> LƯU Ý BẮT BUỘC: Bạn phải chạy lệnh Git này trong Terminal để lấy Hash Code mới nhất: \`git log -1 --format="%H" -- . ":(exclude)docs"\``);
53
- lines.push(`> Sau đó ghi chuẩn giá trị Hash đó vào file \`docs/.sync_hash\`.`);
45
+ lines.push('### Missing docs/');
46
+ lines.push('> Source exists but `docs/` does not. Create baseline docs first:');
47
+ lines.push('> 1. `docs/system-architecture.md` — system architecture and flows');
48
+ lines.push('> 2. `docs/project-overview-pdr.md` — overview, goals, features');
49
+ lines.push('> 3. Write current hash to `docs/.sync_hash` via `git log -1 --format="%H" -- . ":(exclude)docs"`');
54
50
  lines.push('');
55
- }
56
- // Trường hợp 2: Có Code, đã có Docs. Kiểm tra Continuous Sync
51
+ }
52
+ // Case 2: docs exist — check continuous sync vs source-only git hash
57
53
  else {
58
54
  try {
59
- // Lấy hash của lần thay đổi SOURCE CODE gần nhất (Bỏ qua những commit chỉ sửa docs)
55
+ // Latest SOURCE-only hash (ignore docs-only commits)
60
56
  const currentHash = execSync('git log -1 --format="%H" -- . ":(exclude)docs"', {
61
57
  cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore']
62
58
  }).trim();
@@ -69,18 +65,16 @@ try {
69
65
 
70
66
  if (lastSyncHash !== currentHash) {
71
67
  lines.push('');
72
- lines.push('### 🔄 CONTINUOUS DOCS SYNC REQUIRED 🔄');
73
- lines.push(`> Source Code vừa có sự thay đổi mới (Git Hash: \`${currentHash}\`) kể từ lần đồng bộ Docs cuối cùng (\`${lastSyncHash || 'Chưa gán'}\`).`);
74
- lines.push(`> YÊU CẦU TRƯỚC KHI LÀM THÊM TÍNH NĂNG:`);
75
- lines.push(`> 1. Rà soát file bị thay đổi gần đây (dùng \`git diff ${lastSyncHash} ${currentHash}\` hoặc \`git log\`).`);
76
- lines.push(`> 2. Cập nhật lại \`docs/system-architecture.md\` hoặc các chuẩn code nếu cần thiết.`);
77
- lines.push(`> 3. Cập nhật Changelog (nhật ký thay đổi).`);
78
- lines.push(`> 4. KHI HOÀN TẤT, BẠN PHẢI GHI ĐÈ GIÁ TRỊ SAU: \`${currentHash}\` VÀO FILE \`docs/.sync_hash\` ĐỂ CHỐT TRẠNG THÁI HIỆN TẠI.`);
68
+ lines.push('### Docs sync needed');
69
+ lines.push(`> Source changed (\`${currentHash}\`) since last docs sync (\`${lastSyncHash || 'none'}\`).`);
70
+ lines.push(`> 1. Review recent changes (\`git diff ${lastSyncHash} ${currentHash}\` or \`git log\`).`);
71
+ lines.push('> 2. Update architecture/code-standards docs and changelog as needed.');
72
+ lines.push(`> 3. Write \`${currentHash}\` to \`docs/.sync_hash\` when done.`);
79
73
  lines.push('');
80
74
  }
81
75
  }
82
76
  } catch (e) {
83
- // Git chưa init hoặc chưa có commit nào thì im lặng fail-open
77
+ // Git not initialized or no commits — fail-open
84
78
  }
85
79
  }
86
80
 
@@ -21,7 +21,7 @@ try {
21
21
  // Directories that should never be read (too large / irrelevant to LLM)
22
22
  const BLOCKED_DIRS = [
23
23
  'node_modules', 'dist', 'build', '.next', '.nuxt', '.output',
24
- '__pycache__', '.venv', 'venv', '.env',
24
+ '__pycache__', '.venv', 'venv',
25
25
  'vendor', 'target',
26
26
  '.git', 'coverage', '.nyc_output',
27
27
  ];
@@ -52,11 +52,6 @@ const DEFAULT_CONFIG = {
52
52
  packageManager: 'auto',
53
53
  framework: 'auto'
54
54
  },
55
- skills: {
56
- research: {
57
- useGemini: true // Toggle Gemini CLI usage in research skill
58
- }
59
- },
60
55
  assertions: [],
61
56
  statusline: 'full',
62
57
  statuslineColors: true,
@@ -7,12 +7,11 @@
7
7
  * Claude Code CLI privacy gate for sensitive files.
8
8
  *
9
9
  * Runtime contract:
10
- * - Non-bash file access to sensitive files is blocked with a JSON marker
11
- * - Assistant must use AskUserQuestion with that JSON payload
12
- * - If user approves, assistant should read via `bash cat "file"`
13
- * - Bash access is allowed with a warning to enable the approved follow-up path
10
+ * - Sensitive access returns Claude Code's native PreToolUse "ask" decision
11
+ * - The same decision applies to direct tools and Bash commands
12
+ * - Symlinks are classified by their resolved target before their alias
14
13
  *
15
- * Exit: 0 = allow, 2 = block
14
+ * Exit: 0 (the JSON hook output carries the permission decision)
16
15
  */
17
16
 
18
17
  try {
@@ -20,7 +19,7 @@ try {
20
19
  const path = require('path');
21
20
 
22
21
  const RESTRICTED_PATTERNS = [
23
- /^\.env(\.|$)/i,
22
+ /^\.env(?:[.\[*?{]|$)/i,
24
23
  /^credentials/i,
25
24
  /secrets?\.(ya?ml|json)$/i,
26
25
  /\.pem$/i,
@@ -66,22 +65,28 @@ try {
66
65
  * when the path cannot be resolved (missing file, broken link) — fail-open to
67
66
  * the original-path check in that case.
68
67
  */
69
- function resolveTarget(filePath) {
68
+ function resolveTarget(filePath, cwd) {
70
69
  try {
71
- return fs.realpathSync(filePath);
70
+ const requested = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
71
+ return fs.realpathSync(requested);
72
72
  } catch {
73
73
  return null;
74
74
  }
75
75
  }
76
76
 
77
77
  function extractBashPaths(command) {
78
- const paths = [];
79
- const regex = /(?:cat|less|more|head|tail|source|\.)\s+(?:"([^"]+)"|'([^']+)'|([^\s]+))/g;
80
- let match;
81
- while ((match = regex.exec(command)) !== null) {
82
- paths.push(match[1] || match[2] || match[3]);
78
+ const paths = new Set();
79
+ // Quotes are delimiters rather than grouped tokens so nested shell
80
+ // commands such as `bash -c 'cat .env'` cannot hide the sensitive path.
81
+ const tokens = command.split(/[\s|;&<>"']+/).filter(Boolean);
82
+ for (const token of tokens) {
83
+ const cleaned = token.replace(/^["'()`]+|["'(),]+$/g, '');
84
+ for (const candidate of cleaned.split('=')) {
85
+ const value = candidate.replace(/^["'()`]+|["'(),]+$/g, '');
86
+ if (value && (isSensitive(value) || isSafe(value))) paths.add(value);
87
+ }
83
88
  }
84
- return paths;
89
+ return [...paths];
85
90
  }
86
91
 
87
92
  function extractPaths(toolName, input) {
@@ -107,42 +112,15 @@ try {
107
112
  return paths.filter(Boolean);
108
113
  }
109
114
 
110
- function formatBlockMessage(filePath) {
115
+ function askForPermission(filePath) {
111
116
  const basename = path.basename(filePath);
112
- const promptData = {
113
- type: 'PRIVACY_PROMPT',
114
- file: filePath,
115
- basename,
116
- question: {
117
- header: 'File Access',
118
- text: `I need to read "${basename}" which may contain sensitive data (API keys, passwords, tokens). Do you approve?`,
119
- options: [
120
- {
121
- label: 'Yes, approve access',
122
- description: `Allow reading ${basename} this time`
123
- },
124
- {
125
- label: 'No, skip this file',
126
- description: 'Continue without accessing this file'
127
- }
128
- ]
117
+ process.stdout.write(JSON.stringify({
118
+ hookSpecificOutput: {
119
+ hookEventName: 'PreToolUse',
120
+ permissionDecision: 'ask',
121
+ permissionDecisionReason: `Sensitive file access requires approval: ${basename}`
129
122
  }
130
- };
131
-
132
- return [
133
- 'NOTE: This is not an error. This block protects sensitive data.',
134
- '',
135
- `PRIVACY BLOCK: Sensitive file access requires user approval`,
136
- `File: ${filePath}`,
137
- '',
138
- '@@PRIVACY_PROMPT_START@@',
139
- JSON.stringify(promptData, null, 2),
140
- '@@PRIVACY_PROMPT_END@@',
141
- '',
142
- 'Claude Code follow-up:',
143
- `- If approved: use bash to read: cat "${filePath}"`,
144
- '- If denied: continue without this file'
145
- ].join('\n');
123
+ }) + '\n');
146
124
  }
147
125
 
148
126
  const stdin = fs.readFileSync(0, 'utf8').trim();
@@ -160,22 +138,21 @@ try {
160
138
  if (!paths.length) process.exit(0);
161
139
 
162
140
  for (const filePath of paths) {
163
- // Check the requested path and, when it is a symlink, its real target.
164
- const target = resolveTarget(filePath);
165
- const candidates = target && target !== filePath ? [filePath, target] : [filePath];
166
-
167
- // An exemption on either name wins (e.g. a symlink to .env.example).
168
- if (candidates.some(isSafe)) continue;
169
- // Sensitive if the requested path OR its symlink target is sensitive.
170
- if (!candidates.some(isSensitive)) continue;
171
-
172
- if (toolName === 'Bash') {
173
- console.error(`WARN: Privacy-sensitive file access via bash allowed for approved follow-up: ${path.basename(filePath)}`);
174
- process.exit(0);
141
+ const target = resolveTarget(filePath, cwd);
142
+
143
+ // The real target is authoritative: an exempt-looking alias must not hide
144
+ // a sensitive target, while a real .env.example target remains safe.
145
+ if (target) {
146
+ if (isSensitive(target) && !isSafe(target)) {
147
+ askForPermission(filePath);
148
+ process.exit(0);
149
+ }
150
+ if (isSafe(target)) continue;
175
151
  }
176
152
 
177
- console.error(formatBlockMessage(filePath));
178
- process.exit(2);
153
+ if (isSafe(filePath) || !isSensitive(filePath)) continue;
154
+ askForPermission(filePath);
155
+ process.exit(0);
179
156
  }
180
157
 
181
158
  process.exit(0);
@@ -84,11 +84,12 @@ try {
84
84
  const newlyDone = Object.keys(taskRegistry).filter((tp) =>
85
85
  (taskRegistry[tp]?.status || 'pending') === 'done' && featureCache[tp] !== 'done'
86
86
  );
87
- if (newlyDone.length === 0) process.exit(0);
88
-
89
87
  // /m so ^ matches line starts (Evidence is never at byte 0 of the file).
88
+ // legacy heading aliases: read-compat only, no longer advertised
90
89
  const EVID_RE = /^#{2,3}\s+(Evidence|Task Test Plan & Verification Evidence|Verification & Evidence)\b/m;
91
- const PROOF_RE = /(PASS|FAIL|exit code|passed|✓)/;
90
+ const PASS_MARKER_RE = /^\s*Verification:\s*PASS\s*$/m;
91
+ const LEGACY_SUCCESS_RE = /^\s*(?:PASS(?:ED)?|✓)(?:\s*:|$)|exit\s+code\s*[:=]?\s*0\b/im;
92
+ const EXPLICIT_FAILURE_RE = /\bFAIL(?:ED|URE|URES|ING)?\b|tests?\s+failed|exit\s+code\s*[:=]?\s*[1-9]\d*|\b0\s+tests?\b/i;
92
93
 
93
94
  /** Body of first Evidence heading until next same-or-higher heading. */
94
95
  function evidenceBody(text) {
@@ -122,12 +123,20 @@ try {
122
123
  const body = evidenceBody(text);
123
124
  if (body === null || !EVID_RE.test(text)) {
124
125
  fails.push('b');
125
- } else if (/\{\{[^}]+\}\}/.test(body) || !(/```/.test(body) || PROOF_RE.test(body))) {
126
+ } else if (
127
+ /\{\{[^}]+\}\}/.test(body) ||
128
+ EXPLICIT_FAILURE_RE.test(body) ||
129
+ !(PASS_MARKER_RE.test(body) || LEGACY_SUCCESS_RE.test(body))
130
+ ) {
126
131
  fails.push('c');
127
132
  }
128
133
 
129
134
  const at = taskRegistry[taskPath]?.completed_at;
130
- if (typeof at !== 'string' || at.trim() === '') fails.push('d');
135
+ if (
136
+ typeof at !== 'string' ||
137
+ !/^\d{4}-\d{2}-\d{2}T/.test(at) ||
138
+ Number.isNaN(Date.parse(at))
139
+ ) fails.push('d');
131
140
  return fails;
132
141
  }
133
142
 
@@ -135,7 +144,8 @@ try {
135
144
  .map((tp) => ({ taskPath: tp, fails: checkReceipt(tp) }))
136
145
  .filter((f) => f.fails.length > 0);
137
146
 
138
- // Leave failing newly-done at old cache status so the gate re-fires next Stop.
147
+ // Always persist status transitions, including done → pending. Leave failing
148
+ // newly-done tasks at their old status so the gate re-fires next Stop.
139
149
  try {
140
150
  const nextFeature = { ...featureCache, ...currentStatuses };
141
151
  for (const { taskPath } of failures) {
@@ -147,6 +157,7 @@ try {
147
157
  fs.writeFileSync(cacheFile, JSON.stringify(cache));
148
158
  } catch { /* fail-open */ }
149
159
 
160
+ if (newlyDone.length === 0) process.exit(0);
150
161
  if (failures.length === 0) process.exit(0);
151
162
 
152
163
  const lines = [
@@ -155,7 +166,7 @@ try {
155
166
  for (const { taskPath, fails } of failures) {
156
167
  lines.push(`- \`${taskPath}\`: failed check(s) ${fails.join(', ')}`);
157
168
  lines.push(
158
- ` Fix: add a verification receipt to \`## Evidence\` in \`specs/${featureName}/${taskPath}\`: commands run + outcomes, then re-sync spec.json`
169
+ ` Fix: add \`Verification: PASS\` plus commands and successful outcomes to \`## Evidence\` in \`specs/${featureName}/${taskPath}\`, then re-sync spec.json`
159
170
  );
160
171
  }
161
172
  process.stdout.write(JSON.stringify({ decision: 'block', reason: lines.slice(0, 8).join('\n') }) + '\n');
@@ -30,6 +30,10 @@ try {
30
30
  if (fs.existsSync(p)) runtime = JSON.parse(fs.readFileSync(p, 'utf8'));
31
31
  } catch { /* ignore */ }
32
32
 
33
+ // Reminder toggle — same key the OpenCode spec-state plugin honors.
34
+ // (The Stop completion gate has its own toggle: spec.completion_gate.)
35
+ if (runtime.spec && runtime.spec.tollgate === false) process.exit(0);
36
+
33
37
  const baseDir = process.env.PROJECT_ROOT || cwd;
34
38
  const specsPath = path.join(baseDir, runtime.paths?.specs || 'specs');
35
39
 
@@ -120,19 +120,29 @@ try {
120
120
  }
121
121
  }
122
122
 
123
+ const changed = [];
123
124
  try {
124
125
  const diff = execSync('git diff --name-only HEAD', {
125
126
  encoding: 'utf8',
126
127
  timeout: 3000,
127
128
  stdio: ['pipe', 'pipe', 'pipe']
128
129
  }).trim();
130
+ if (diff) changed.push(...diff.split('\n'));
131
+ } catch {
132
+ // A repository without HEAD can still report untracked files below.
133
+ }
129
134
 
130
- if (diff) {
131
- data.modifiedFiles = diff.split('\n').slice(0, MAX_MODIFIED_FILES_DISPLAY);
132
- }
135
+ try {
136
+ const untracked = execSync('git ls-files --others --exclude-standard', {
137
+ encoding: 'utf8',
138
+ timeout: 3000,
139
+ stdio: ['pipe', 'pipe', 'pipe']
140
+ }).trim();
141
+ if (untracked) changed.push(...untracked.split('\n'));
133
142
  } catch {
134
143
  // fail-open
135
144
  }
145
+ data.modifiedFiles = [...new Set(changed)].slice(0, MAX_MODIFIED_FILES_DISPLAY);
136
146
 
137
147
  return data;
138
148
  }
@@ -8,6 +8,12 @@
8
8
  * Fetches Claude Code usage limits from Anthropic OAuth API and writes to cache.
9
9
  * Cache is read by status.cjs for statusline display.
10
10
  *
11
+ * ⚠ EXPERIMENTAL / UNDOCUMENTED API: api.anthropic.com/api/oauth/usage with the
12
+ * "oauth-2025-04-20" beta header is not a public contract and may break or
13
+ * change shape without notice. Every failure path degrades to
14
+ * status:"unavailable" (statusline hides the segment) — never block on it.
15
+ * Disable entirely with "usage": { "enabled": false } if it misbehaves.
16
+ *
11
17
  * Features:
12
18
  * - Cross-platform credential retrieval (macOS Keychain, file-based)
13
19
  * - Throttled API calls: 1 min (prompt) / 5 min (tool use)
@@ -23,7 +23,7 @@ Example prompt:
23
23
  ## Resource Constraints
24
24
 
25
25
  - Each subagent has a **200K token context window** — scope tasks to fit comfortably within it
26
- - Spawning many parallel agents degrades system performance — check available CPU/memory before scaling out
26
+ - Spawning many parallel agents degrades system performance — check available CPU/memory before scaling out. For parallel implementation waves the cap is 3 concurrent agents by default, never more than 5 (each agent carries the 200K budget above)
27
27
  - Prefer fewer, well-scoped agents over many overlapping ones
28
28
  - Include system resource info (from hook injection) when delegating tasks so subagents can self-regulate
29
29
 
@@ -48,7 +48,7 @@ For implementation tasks, keep the chain explicit:
48
48
  2. Verify with `test-runner` using task files and exact evidence commands.
49
49
  3. Review with `code-auditor` using task files, design contracts, and the diff.
50
50
 
51
- Do not dispatch multiple implementation agents against the same files or same task. Parallelize only independent scopes with distinct file ownership.
51
+ Do not dispatch multiple implementation agents against the same files or same task **within the same working tree**. Parallelize only independent scopes with distinct file ownership — the sanctioned pattern for parallel implementation is **worktree isolation** (one agent per task, each in its own git worktree) with a **single writer per file per wave** and orchestrator-only spec-state writes, per `skills/develop/references/parallel-waves.md`.
52
52
 
53
53
  ### Parallel (independent tasks)
54
54
 
@@ -1,88 +1,29 @@
1
1
  # Skill Domain Routing
2
2
 
3
- Use this decision tree to choose domain skills from the installed CafeKit skill set. This is advisory routing, not an automatic hook.
4
-
5
- ## Frontend And UI
6
-
7
- ```text
8
- User wants to...
9
- ├── choose style, color, layout, UX, design system -> /hapo:frontend-design
10
- ├── build React/TypeScript UI components -> /hapo:frontend-development
11
- ├── optimize React/Next.js performance or rerenders -> /hapo:react-best-practices
12
- ├── apply deep UI/UX intelligence, visual polish -> /hapo:ui-ux-pro-max
13
- └── test UI in browser -> /hapo:web-testing or /hapo:agent-browser
14
- ```
15
-
16
- Use `/hapo:react-best-practices` for performance/re-render patterns. Use `/hapo:frontend-development` for normal implementation.
17
-
18
- ## Backend, Data, And Infrastructure
19
-
20
- ```text
21
- User wants to...
22
- ├── API, service, auth, database-backed feature -> /hapo:backend-development
23
- ├── deployment, Docker, Kubernetes, Cloudflare, CI/CD -> /hapo:devops
24
- └── mobile apps, iOS, Android, React Native, Flutter -> /hapo:mobile-development
25
- ```
26
-
27
- ## Codebase Understanding
28
-
29
- ```text
30
- User wants to...
31
- ├── answer questions from source code/docs/specs/config -> /hapo:question
32
- ├── find files, locate code, scan project structure -> /hapo:inspect
33
- ├── diagnose an error or failing check -> /hapo:debug
34
- ├── understand possible side effects -> /hapo:inspect
35
- └── compare external approaches or best practices -> /hapo:research
36
- ```
37
-
38
- Local source truth comes from `/hapo:inspect`; external/current knowledge comes from `/hapo:research`.
39
-
40
- ## Documentation
41
-
42
- ```text
43
- User wants to...
44
- ├── ask/answer documentation questions from source -> /hapo:question
45
- ├── create missing project docs -> /hapo:docs --init
46
- ├── update docs after source changes -> /hapo:docs --update
47
- ├── summarize codebase quickly -> /hapo:docs --summarize
48
- ├── reconstruct as-is docs from legacy source code -> /hapo:docs --reconstruct <scope>
49
- └── create a future feature spec -> /hapo:specs
50
- ```
51
-
52
- Keep as-is reconstruction separate from future requirements. Reconstructed docs document current behavior; specs define requested changes.
53
-
54
- ## Testing And Browser
55
-
56
- ```text
57
- User wants to...
58
- ├── run test suites, coverage, scoped verification -> /hapo:test
59
- ├── E2E/unit/integration/load/security/a11y strategy -> /hapo:web-testing
60
- ├── drive a browser with snapshots or Browserbase -> /hapo:agent-browser
61
- └── Chrome DevTools/Puppeteer evidence -> /hapo:chrome-devtools
62
- ```
63
-
64
- ## Documents And Media
65
-
66
- ```text
67
- User wants to...
68
- ├── PowerPoint/slides -> /hapo:pptx
69
- ├── Word documents -> /hapo:docx
70
- ├── PDFs -> /hapo:pdf
71
- ├── spreadsheets/CSV/XLSX -> /hapo:xlsx
72
- └── image/audio/video/document multimodal analysis -> /hapo:ai-multimodal
73
- ```
74
-
75
- ## Git And Release
76
-
77
- ```text
78
- User wants to...
79
- ├── commit, branch, tag, push, release prep -> /hapo:git
80
- └── review before release -> /hapo:code-review
81
- ```
82
-
83
- ## Usage Notes
84
-
85
- - Read the selected skill's `SKILL.md` before acting.
86
- - Do not activate a skill only because one keyword appears. Use the whole user intent.
87
- - If a task spans multiple domains, name the primary skill and mention the secondary skill in the plan.
88
- - If no domain skill fits, use the core CafeKit workflow in `skill-workflow-routing.md`.
3
+ Advisory routing for installed domain skills (not an automatic hook). Read the selected skill's `SKILL.md` before acting.
4
+
5
+ | Domain / intent | Skill |
6
+ |---|---|
7
+ | answer questions from source code/docs/specs/config | `/hapo:question` |
8
+ | file discovery, structure, blast radius | `/hapo:inspect` |
9
+ | style / layout / design system | `/hapo:frontend-design` |
10
+ | React/TypeScript UI implementation | `/hapo:frontend-development` |
11
+ | React/Next performance / re-renders | `/hapo:react-best-practices` |
12
+ | deep UI/UX polish | `/hapo:ui-ux-pro-max` |
13
+ | API, service, auth, database | `/hapo:backend-development` |
14
+ | deploy / Docker / K8s / Cloudflare / CI | `/hapo:devops` |
15
+ | mobile (RN / Flutter / iOS / Android) | `/hapo:mobile-development` |
16
+ | docs init / update / summarize | `/hapo:docs --init` / `--update` / `--summarize` |
17
+ | as-is reconstruction from legacy code | `/hapo:docs --reconstruct <scope>` |
18
+ | future feature requirements | `/hapo:specs` |
19
+ | run suites / scoped verification | `/hapo:test` |
20
+ | E2E / load / a11y strategy | `/hapo:web-testing` |
21
+ | browser snapshots / Browserbase | `/hapo:agent-browser` |
22
+ | Chrome DevTools evidence | `/hapo:chrome-devtools` |
23
+ | diagnose error / CI failure | `/hapo:debug` |
24
+ | external best practices / tool compare | `/hapo:research` |
25
+ | pptx / docx / pdf / xlsx / multimodal | `/hapo:pptx` / `docx` / `pdf` / `xlsx` / `ai-multimodal` |
26
+ | commit / branch / release prep | `/hapo:git` |
27
+ | review before release | `/hapo:code-review` |
28
+
29
+ Pick one primary domain skill; name a secondary only when it changes execution. If none fit, use `skill-workflow-routing.md`.
@@ -4,92 +4,21 @@ Use this rule instead of automatic prompt routing. Read the user's intent, choos
4
4
 
5
5
  Do not inject or force a skill when the user explicitly asks for a direct answer, a specific command, or a different workflow.
6
6
 
7
- ## Core Specification Workflow
7
+ Claude Code selects skills from frontmatter descriptions; trust it for clear intents.
8
8
 
9
- ```text
10
- /hapo:question -> /hapo:brainstorm -> /hapo:specs -> /hapo:develop -> /hapo:test -> /hapo:code-review -> /hapo:git
11
- ```
12
-
13
- | User intent | Suggested start |
14
- |---|---|
15
- | ask about source code, docs, specs, config, dependencies, or external/current technical facts | `/hapo:question` |
16
- | vague request, missing acceptance criteria, unclear scope, stakeholder questions | ask one clarification or use `/hapo:brainstorm` |
17
- | unclear idea, possible approaches, scope tradeoffs | `/hapo:brainstorm` |
18
- | create requirements, design, task breakdown, validate spec | `/hapo:specs` |
19
- | implement an approved spec or a specific task file | `/hapo:develop` |
20
- | run tests, QA, runtime verification | `/hapo:test` |
21
- | review code quality, security, regressions, maintainability | `/hapo:code-review` |
22
- | commit, tag, push, branch, release prep | `/hapo:git` |
23
-
24
- ## Bug And Incident Workflow
25
-
26
- ```text
27
- /hapo:debug -> /hapo:hotfix -> /hapo:test -> /hapo:code-review
28
- ```
29
-
30
- | User intent | Suggested start |
31
- |---|---|
32
- | diagnose why something fails, find root cause, inspect CI/logs | `/hapo:debug` |
33
- | fix a known bug after diagnosis, urgent regression, production issue | `/hapo:hotfix` |
34
- | verify the fix and affected behavior | `/hapo:test` |
35
- | check side effects before closeout | `/hapo:code-review` |
36
-
37
- `/hapo:debug` is diagnostic-only. Do not edit product code there. Use `/hapo:hotfix` when the user asks to fix.
38
-
39
- ## Existing System Documentation Workflow
40
-
41
- ```text
42
- /hapo:inspect -> /hapo:docs --reconstruct -> human review -> /hapo:specs -> /hapo:develop
43
- ```
44
-
45
- | User intent | Suggested start |
46
- |---|---|
47
- | understand source structure or locate implementation areas | `/hapo:inspect` |
48
- | rebuild as-is docs/requirements from a legacy codebase | `/hapo:docs --reconstruct <scope>` |
49
- | update normal project docs | `/hapo:docs --update` |
50
- | initialize missing docs | `/hapo:docs --init` |
51
- | summarize a codebase without full docs work | `/hapo:docs --summarize` |
52
-
53
- Do not turn reconstructed docs directly into implementation. Human review must approve the as-is docs before `/hapo:specs` defines future changes.
54
-
55
- ## Research And Analysis Workflow
9
+ ## Core chain
56
10
 
57
11
  ```text
58
- /hapo:inspect -> /hapo:research
12
+ /hapo:question -> /hapo:brainstorm -> /hapo:specs -> /hapo:develop -> /hapo:test -> /hapo:code-review -> /hapo:git
59
13
  ```
60
14
 
61
- | User intent | Suggested start |
62
- |---|---|
63
- | file discovery, codebase structure, source scouting | `/hapo:inspect` |
64
- | affected files, blast radius, dependency impact, side effects | `/hapo:inspect` |
65
- | external best practices, tool comparison, technical investigation | `/hapo:research` |
66
-
67
- Use `/hapo:research` for external knowledge. Use `/hapo:inspect` for local source truth.
68
-
69
- ## Delegation Workflow
70
-
71
- | User intent | Suggested skill |
72
- |---|---|
73
- | assign/offload an implementation task to an external agent CLI (codex, grok) | `/hapo:delegate` |
74
-
75
- Claude Code remains the orchestrator: it writes the task brief, dispatches, monitors, and independently verifies the returned work.
76
-
77
- ## Output Artifact Workflow
78
-
79
- | User intent | Suggested skill |
80
- |---|---|
81
- | PowerPoint or slide deck creation/editing | `/hapo:pptx` |
82
- | Word documents | `/hapo:docx` |
83
- | PDF processing | `/hapo:pdf` |
84
- | spreadsheets | `/hapo:xlsx` |
85
- | screenshots, images, audio, video, OCR, multimodal understanding | `/hapo:ai-multimodal` |
15
+ Bug path: `/hapo:debug -> /hapo:hotfix` then test/review. As-is docs: `/hapo:inspect` → `/hapo:docs --reconstruct <scope>` → human review → specs → develop. Delegate implementation to an external agent CLI: `/hapo:delegate`.
86
16
 
87
- ## Selection Rules
17
+ ## Ambiguous cases
88
18
 
89
- - Pick one primary workflow first.
90
- - Add a secondary skill only when it materially changes execution.
91
- - Prefer explicit user commands over this table.
92
- - If two workflows conflict, ask one concise clarification.
93
- - If the user asks a factual or project question before workflow selection, use `/hapo:question`.
94
- - If missing information blocks workflow selection, ask one concise clarification or use `/hapo:brainstorm`.
95
- - If the user asks to "fix" and root cause is unknown, start with `/hapo:debug` unless the issue is trivial and local.
19
+ | Ambiguity | Prefer | Not when |
20
+ |---|---|---|
21
+ | debug-vs-hotfix | `/hapo:debug` first when root cause unknown | Root cause is known / trivial local fix → `/hapo:hotfix` |
22
+ | question-vs-research | `/hapo:question` to ask about source code, docs, specs, config, dependencies, or project facts | External best-practice comparison / multi-source investigation → `/hapo:research` |
23
+ | question-vs-brainstorm | `/hapo:question` for factual answers with evidence | Vague idea, missing acceptance criteria, unclear scope, multi-approach tradeoffs → `/hapo:brainstorm` |
24
+ | specs-vs-brainstorm | `/hapo:specs` when intent and acceptance are concrete enough | Unclear idea / architecture choices still open → `/hapo:brainstorm` first |
@@ -28,4 +28,4 @@ Before returning control to the user or orchestrator, the agent **MUST**:
28
28
 
29
29
  ### Completion gate (Stop)
30
30
 
31
- Marking a task `done` is now machine-gated on the **Stop** hook (`spec-gate.cjs`), not only reminded on each prompt. When a turn ends with *newly-done* tasks, the gate requires a verification receipt in each task markdown: `Status: done`, an Evidence section (or legacy `Task Test Plan & Verification Evidence` / `Verification & Evidence`) with real proof (no `{{...}}` placeholders; at least one fenced command block or PASS/FAIL line), and a non-empty `task_registry[path].completed_at`. First adoption (no cache yet) seeds history without blocking. Escape hatch for humans: `"spec": { "completion_gate": false }` in `.claude/runtime.json` (missing key keeps the gate ON).
31
+ Marking a task `done` is now machine-gated on the **Stop** hook (`spec-gate.cjs`), not only reminded on each prompt. When a turn ends with *newly-done* tasks, the gate requires a verification receipt in each task markdown: `Status: done`, `## Evidence` (legacy heading aliases still parse) with real proof (no `{{...}}` placeholders; at least one fenced command block or PASS/FAIL line), and a non-empty `task_registry[path].completed_at`. First adoption (no cache yet) seeds history without blocking. Escape hatch for humans: `"spec": { "completion_gate": false }` in `.claude/runtime.json` (missing key keeps the gate ON).
@@ -13,21 +13,21 @@
13
13
  },
14
14
  "paths": {
15
15
  "docs": "docs",
16
- "specs": "specs"
16
+ "specs": "specs",
17
+ "plans": "plans"
17
18
  },
18
19
  "spec": {
19
20
  "scaffold_guard": true,
20
- "completion_gate": true
21
+ "completion_gate": true,
22
+ "tollgate": true
23
+ },
24
+ "develop": {
25
+ "parallel": true
21
26
  },
22
27
  "locale": {
23
28
  "thinkingLanguage": null,
24
29
  "responseLanguage": null
25
30
  },
26
- "skills": {
27
- "research": {
28
- "useGemini": true
29
- }
30
- },
31
31
  "usage": {
32
32
  "enabled": true
33
33
  },