@haposoft/cafekit 0.18.0 → 0.18.2

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 (162) hide show
  1. package/README.md +12 -1
  2. package/bin/__tests__/claude-settings.test.js +123 -0
  3. package/bin/__tests__/codex-ask-guard.test.js +123 -0
  4. package/bin/__tests__/codex-ask-projection.test.js +51 -0
  5. package/bin/__tests__/codex-hooks-ownership.test.js +80 -1
  6. package/bin/__tests__/codex-hooks.test.js +938 -237
  7. package/bin/__tests__/codex-native-hook-boundaries.test.js +136 -0
  8. package/bin/__tests__/codex-native.test.js +222 -149
  9. package/bin/__tests__/codex-projection-parity.test.js +314 -0
  10. package/bin/__tests__/codex-quiet-hooks.test.js +170 -0
  11. package/bin/__tests__/codex-research-projection.test.js +70 -0
  12. package/bin/__tests__/codex-specs-projection.test.js +76 -0
  13. package/bin/__tests__/develop-contract.test.js +102 -1362
  14. package/bin/__tests__/docs-sync-on-demand.test.js +25 -20
  15. package/bin/__tests__/inspect-paths.test.js +183 -0
  16. package/bin/__tests__/installer-safety.test.js +335 -0
  17. package/bin/__tests__/legacy-authority-decoupled.test.js +14 -19
  18. package/bin/__tests__/legacy-hooks-retired.test.js +18 -8
  19. package/bin/__tests__/omp-bridge.test.js +66 -12
  20. package/bin/__tests__/omp-hooks.test.js +11 -5
  21. package/bin/__tests__/orca-session.test.js +7 -0
  22. package/bin/__tests__/package-inventory.test.js +1 -395
  23. package/bin/__tests__/privacy-paths.test.js +317 -0
  24. package/bin/__tests__/spec-narrowing.test.js +30 -134
  25. package/bin/__tests__/specs-v2-execution-closeout.test.js +0 -372
  26. package/bin/__tests__/test-runtime-dependency-closure.cjs +1 -2
  27. package/bin/__tests__/usage-hook-retired.test.js +5 -3
  28. package/bin/__tests__/validator-grounding.test.js +17 -2929
  29. package/bin/install.js +25 -0
  30. package/bin/lib/codex-install.js +134 -12
  31. package/bin/lib/context.js +1 -1
  32. package/bin/lib/managed-writer.js +2 -3
  33. package/bin/phases/claude-runtime.js +91 -25
  34. package/bin/phases/claude-settings.js +22 -1
  35. package/bin/phases/codex-hooks.js +96 -33
  36. package/bin/phases/codex-runtime.js +45 -49
  37. package/bin/phases/copy-payload.js +11 -28
  38. package/bin/phases/omp-runtime.js +6 -3
  39. package/package.json +2 -3
  40. package/src/claude/CLAUDE.md +4 -0
  41. package/src/claude/agents/code-auditor.md +9 -6
  42. package/src/claude/agents/debugger.md +3 -9
  43. package/src/claude/agents/docs-keeper.md +4 -7
  44. package/src/claude/agents/git-ops.md +2 -4
  45. package/src/claude/agents/implementer.md +6 -7
  46. package/src/claude/agents/inspector.md +2 -0
  47. package/src/claude/agents/project-manager.md +2 -2
  48. package/src/claude/agents/spec-maker.md +4 -4
  49. package/src/claude/agents/test-runner.md +8 -11
  50. package/src/claude/agents/ui-ux-designer.md +0 -1
  51. package/src/claude/gitignore +3 -0
  52. package/src/claude/hooks/__tests__/installed-root.test.js +111 -0
  53. package/src/claude/hooks/__tests__/precompact.test.js +206 -10
  54. package/src/claude/hooks/__tests__/privacy-block.test.js +27 -0
  55. package/src/claude/hooks/__tests__/session-writeenv.test.js +25 -0
  56. package/src/claude/hooks/__tests__/spec-gate.test.js +311 -340
  57. package/src/claude/hooks/__tests__/spec-state-touched.test.js +230 -8
  58. package/src/claude/hooks/__tests__/state.test.js +207 -7
  59. package/src/claude/hooks/__tests__/update-check.test.js +88 -0
  60. package/src/claude/hooks/agent.cjs +7 -6
  61. package/src/claude/hooks/inspect-block.cjs +15 -63
  62. package/src/claude/hooks/lib/inspect-paths.cjs +304 -0
  63. package/src/claude/hooks/lib/parser.cjs +1 -1
  64. package/src/claude/hooks/lib/privacy-command-analysis.cjs +191 -17
  65. package/src/claude/hooks/lib/privacy-paths.cjs +313 -0
  66. package/src/claude/hooks/lib/project-root.cjs +15 -0
  67. package/src/claude/hooks/lib/rules-reservation.cjs +36 -0
  68. package/src/claude/hooks/lib/spec-touch.cjs +67 -0
  69. package/src/claude/hooks/lib/update-check.cjs +77 -0
  70. package/src/claude/hooks/precompact.cjs +8 -15
  71. package/src/claude/hooks/privacy-block.cjs +23 -205
  72. package/src/claude/hooks/rules.cjs +6 -28
  73. package/src/claude/hooks/session.cjs +34 -117
  74. package/src/claude/hooks/spec-gate.cjs +17 -89
  75. package/src/claude/hooks/spec-state.cjs +89 -96
  76. package/src/claude/hooks/state.cjs +96 -51
  77. package/src/claude/migration-manifest.json +20 -8
  78. package/src/claude/output-styles/coding-level-0-eli5.md +9 -9
  79. package/src/claude/output-styles/coding-level-1-junior.md +3 -3
  80. package/src/claude/output-styles/coding-level-2-mid.md +3 -3
  81. package/src/claude/output-styles/coding-level-3-senior.md +2 -2
  82. package/src/claude/output-styles/coding-level-4-lead.md +5 -5
  83. package/src/claude/output-styles/coding-level-5-god.md +6 -6
  84. package/src/claude/rules/ai-dev-rules.md +1 -2
  85. package/src/claude/rules/hook-protocols.md +1 -1
  86. package/src/claude/rules/workflow.md +1 -1
  87. package/src/claude/runtime.json +0 -3
  88. package/src/claude/scripts/generate-skill-catalog.cjs +2 -2
  89. package/src/claude/scripts/provenance.cjs +14 -0
  90. package/src/claude/scripts/spec-receipt.cjs +0 -114
  91. package/src/claude/scripts/spec-resolver.cjs +41 -457
  92. package/src/claude/scripts/workflow-policy.cjs +3 -1801
  93. package/src/claude/settings/settings.json +1 -1
  94. package/src/claude/skills/agent-browser/SKILL.md +1 -1
  95. package/src/claude/skills/ai-multimodal/scripts/check_setup.py +8 -68
  96. package/src/claude/skills/ai-multimodal/scripts/gemini_batch_process.py +6 -28
  97. package/src/claude/skills/chrome-devtools/SKILL.md +14 -31
  98. package/src/claude/skills/code-review/SKILL.md +25 -108
  99. package/src/claude/skills/code-review/references/spec-compliance-review.md +4 -7
  100. package/src/claude/skills/develop/SKILL.md +22 -70
  101. package/src/claude/skills/develop/references/subagent-patterns.md +0 -6
  102. package/src/claude/skills/git/SKILL.md +1 -1
  103. package/src/claude/skills/git/references/worktree-blueprint.md +1 -1
  104. package/src/claude/skills/research/SKILL.md +24 -55
  105. package/src/claude/skills/sync/SKILL.md +12 -34
  106. package/src/claude/skills/sync/references/rebind-and-audit.md +2 -2
  107. package/src/claude/skills/test/SKILL.md +39 -123
  108. package/src/claude/skills/test/references/execution-strategy.md +11 -22
  109. package/src/claude/skills/ui-ux/SKILL.md +1 -1
  110. package/src/claude/skills/ui-ux-pro-max/SKILL.md +24 -79
  111. package/src/claude/skills/web-testing/SKILL.md +2 -20
  112. package/src/codex/AGENTS.md +8 -0
  113. package/src/codex/hooks/ask-guard.cjs +54 -0
  114. package/src/codex/hooks/inspect-block.cjs +11 -7
  115. package/src/codex/hooks/lib/inspect-paths.cjs +1 -199
  116. package/src/codex/hooks/lib/privacy-paths.cjs +1 -0
  117. package/src/codex/hooks/lib/privacy-state.cjs +98 -16
  118. package/src/codex/hooks/lib/rules-reservation.cjs +38 -0
  119. package/src/codex/hooks/lib/secret-keywords.cjs +1 -0
  120. package/src/codex/hooks/lib/spec-receipt.cjs +2 -30
  121. package/src/codex/hooks/lib/spec-session-touch.cjs +100 -0
  122. package/src/codex/hooks/lib/spec-touch.cjs +3 -0
  123. package/src/codex/hooks/lib/spec-utils.cjs +6 -45
  124. package/src/codex/hooks/lib/update-check.cjs +1 -0
  125. package/src/codex/hooks/precompact.cjs +39 -0
  126. package/src/codex/hooks/privacy-approval.cjs +28 -17
  127. package/src/codex/hooks/privacy-block.cjs +9 -215
  128. package/src/codex/hooks/rules.cjs +4 -30
  129. package/src/codex/hooks/secret-output-guardrail.cjs +1 -1
  130. package/src/codex/hooks/session.cjs +47 -11
  131. package/src/codex/hooks/spec-gate.cjs +32 -74
  132. package/src/codex/hooks/spec-state.cjs +68 -46
  133. package/src/codex/hooks/state.cjs +25 -4
  134. package/src/codex/hooks.json +39 -7
  135. package/src/codex/rules/hook-protocols.md +13 -6
  136. package/src/common/AGENTS.md +4 -10
  137. package/src/omp/extensions/cafekit-bridge.mjs +12 -4
  138. package/src/omp/hooks/privacy-block.cjs +24 -206
  139. package/bin/__tests__/change-firewall.semantic-firewall.test.js +0 -2133
  140. package/bin/__tests__/finalizer-pass-gate.semantic-firewall.test.js +0 -342
  141. package/bin/__tests__/release-preflight.semantic-firewall.test.js +0 -586
  142. package/bin/__tests__/review-receipt-c2.semantic-firewall.test.js +0 -378
  143. package/bin/__tests__/specs-v2-policy-and-scaffold.test.js +0 -665
  144. package/bin/__tests__/specs-v2-validator-grounder-contract.test.js +0 -1132
  145. package/src/claude/hooks/lib/context.cjs +0 -527
  146. package/src/claude/hooks/lib/detect.cjs +0 -474
  147. package/src/claude/scripts/change-firewall.cjs +0 -1748
  148. package/src/claude/scripts/spec-authoring-digest.cjs +0 -151
  149. package/src/claude/scripts/spec-authoring-validation.cjs +0 -126
  150. package/src/claude/scripts/spec-final-state.cjs +0 -290
  151. package/src/claude/scripts/spec-ground.cjs +0 -650
  152. package/src/claude/scripts/spec-readiness.cjs +0 -425
  153. package/src/claude/scripts/spec-scaffold.cjs +0 -2317
  154. package/src/claude/scripts/spec-semantic-model.cjs +0 -396
  155. package/src/claude/scripts/validate-spec-output.cjs +0 -3914
  156. package/src/claude/skills/code-review/references/verification-gate.md +0 -41
  157. package/src/claude/skills/specs/templates/design.md +0 -82
  158. package/src/claude/skills/specs/templates/requirements-init.md +0 -8
  159. package/src/claude/skills/specs/templates/requirements.md +0 -42
  160. package/src/claude/skills/specs/templates/spec-state.json +0 -56
  161. package/src/claude/skills/specs/templates/task.md +0 -47
  162. package/src/codex/rules/state-sync.md +0 -60
@@ -1,527 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * context.cjs - Context/reminder building for session injection
4
- *
5
- * Extracted from hooks for reuse across runtime adapters.
6
- * Builds session context, rules, paths, and plan information.
7
- *
8
- * @module context
9
- */
10
-
11
- const fs = require('fs');
12
- const os = require('os');
13
- const path = require('path');
14
- const { execSync } = require('child_process');
15
-
16
- const WARN_THRESHOLD = 70;
17
- const CRITICAL_THRESHOLD = 90;
18
- const {
19
- loadConfig,
20
- resolvePlanPath,
21
- getReportsPath,
22
- resolveNamingPattern,
23
- normalizePath
24
- } = require('./config.cjs');
25
-
26
- // ═══════════════════════════════════════════════════════════════════════════
27
- // HELPER FUNCTIONS
28
- // ═══════════════════════════════════════════════════════════════════════════
29
-
30
- /**
31
- * Safely execute a command with timeout
32
- * @param {string} cmd - Command to execute
33
- * @returns {string|null} Output or null on error
34
- */
35
- function execSafe(cmd) {
36
- try {
37
- return execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
38
- } catch (e) {
39
- return null;
40
- }
41
- }
42
-
43
- /**
44
- * Resolve rules file path (local or global) with backward compat
45
- * @param {string} filename - Rules filename
46
- * @param {string} [configDirName='.claude'] - Config directory name
47
- * @returns {string|null} Resolved path or null
48
- */
49
- function resolveRulesPath(filename, configDirName = '.claude') {
50
- // Try rules/ first (new location)
51
- const localRulesPath = path.join(process.cwd(), configDirName, 'rules', filename);
52
- const globalRulesPath = path.join(os.homedir(), '.claude', 'rules', filename);
53
-
54
- if (fs.existsSync(localRulesPath)) return `${configDirName}/rules/${filename}`;
55
- if (fs.existsSync(globalRulesPath)) return `~/.claude/rules/${filename}`;
56
-
57
- // Backward compat: try workflows/ (legacy location)
58
- const localWorkflowsPath = path.join(process.cwd(), configDirName, 'workflows', filename);
59
- const globalWorkflowsPath = path.join(os.homedir(), '.claude', 'workflows', filename);
60
-
61
- if (fs.existsSync(localWorkflowsPath)) return `${configDirName}/workflows/${filename}`;
62
- if (fs.existsSync(globalWorkflowsPath)) return `~/.claude/workflows/${filename}`;
63
-
64
- return null;
65
- }
66
-
67
- /**
68
- * Resolve script file path (local or global)
69
- * @param {string} filename - Script filename
70
- * @param {string} [configDirName='.claude'] - Config directory name
71
- * @returns {string|null} Resolved path or null
72
- */
73
- function resolveScriptPath(filename, configDirName = '.claude') {
74
- const localPath = path.join(process.cwd(), configDirName, 'scripts', filename);
75
- const globalPath = path.join(os.homedir(), '.claude', 'scripts', filename);
76
- if (fs.existsSync(localPath)) return `${configDirName}/scripts/${filename}`;
77
- if (fs.existsSync(globalPath)) return `~/.claude/scripts/${filename}`;
78
- return null;
79
- }
80
-
81
- /**
82
- * Resolve skills venv Python path (local or global)
83
- * @param {string} [configDirName='.claude'] - Config directory name
84
- * @returns {string|null} Resolved venv Python path or null
85
- */
86
- function resolveSkillsVenv(configDirName = '.claude') {
87
- const isWindows = process.platform === 'win32';
88
- const venvBin = isWindows ? 'Scripts' : 'bin';
89
- const pythonExe = isWindows ? 'python.exe' : 'python3';
90
-
91
- const localVenv = path.join(process.cwd(), configDirName, 'skills', '.venv', venvBin, pythonExe);
92
- const globalVenv = path.join(os.homedir(), '.claude', 'skills', '.venv', venvBin, pythonExe);
93
-
94
- if (fs.existsSync(localVenv)) {
95
- return isWindows
96
- ? `${configDirName}\\skills\\.venv\\Scripts\\python.exe`
97
- : `${configDirName}/skills/.venv/bin/python3`;
98
- }
99
- if (fs.existsSync(globalVenv)) {
100
- return isWindows
101
- ? '~\\.claude\\skills\\.venv\\Scripts\\python.exe'
102
- : '~/.claude/skills/.venv/bin/python3';
103
- }
104
- return null;
105
- }
106
-
107
- /**
108
- * Build plan context from config and git info
109
- * @param {string|null} sessionId - Session ID
110
- * @param {Object} config - Loaded config
111
- * @returns {Object} Plan context object
112
- */
113
- function buildPlanContext(sessionId, config) {
114
- const { plan, paths } = config;
115
- const gitBranch = execSafe('git branch --show-current');
116
- const resolved = resolvePlanPath(sessionId, config);
117
- const reportsPath = getReportsPath(resolved.path, resolved.resolvedBy, plan, paths);
118
-
119
- // Compute naming pattern directly for reliable injection
120
- const namePattern = resolveNamingPattern(plan, gitBranch);
121
-
122
- const planLine = resolved.resolvedBy === 'session'
123
- ? `- Plan: ${resolved.path}`
124
- : resolved.resolvedBy === 'branch'
125
- ? `- Plan: none | Suggested: ${resolved.path}`
126
- : `- Plan: none`;
127
-
128
- // Validation config (injected so LLM can reference it)
129
- const validation = plan.validation || {};
130
- const validationMode = validation.mode || 'prompt';
131
- const validationMin = validation.minQuestions || 3;
132
- const validationMax = validation.maxQuestions || 8;
133
-
134
- return { reportsPath, gitBranch, planLine, namePattern, validationMode, validationMin, validationMax };
135
- }
136
-
137
- /**
138
- * Check if context was recently injected (prevent duplicate injection)
139
- * @param {string} transcriptPath - Path to transcript file
140
- * @returns {boolean} true if recently injected
141
- */
142
- function wasRecentlyInjected(transcriptPath) {
143
- try {
144
- if (!transcriptPath || !fs.existsSync(transcriptPath)) return false;
145
- const transcript = fs.readFileSync(transcriptPath, 'utf-8');
146
- // Check last 150 lines (hook output is ~30 lines, so this covers ~5 user prompts)
147
- return transcript.split('\n').slice(-150).some(line => line.includes('[IMPORTANT] Consider Modularization'));
148
- } catch (e) {
149
- return false;
150
- }
151
- }
152
-
153
- // ═══════════════════════════════════════════════════════════════════════════
154
- // SECTION BUILDERS
155
- // ═══════════════════════════════════════════════════════════════════════════
156
-
157
- /**
158
- * Build language section
159
- * @param {Object} params
160
- * @param {string} [params.thinkingLanguage] - Language for thinking
161
- * @param {string} [params.responseLanguage] - Language for response
162
- * @returns {string[]} Lines for language section
163
- */
164
- function buildLanguageSection({ thinkingLanguage, responseLanguage }) {
165
- // Auto-default thinkingLanguage to 'en' when only responseLanguage is set
166
- const effectiveThinking = thinkingLanguage || (responseLanguage ? 'en' : null);
167
- const hasThinking = effectiveThinking && effectiveThinking !== responseLanguage;
168
- const hasResponse = responseLanguage;
169
- const lines = [];
170
-
171
- if (hasThinking || hasResponse) {
172
- lines.push(`## Language`);
173
- if (hasThinking) {
174
- lines.push(`- Thinking: Use ${effectiveThinking} for reasoning (logic, precision).`);
175
- }
176
- if (hasResponse) {
177
- lines.push(`- Response: Respond in ${responseLanguage} (natural, fluent).`);
178
- }
179
- lines.push(``);
180
- }
181
-
182
- return lines;
183
- }
184
-
185
- /**
186
- * Build session section
187
- * @param {Object} [staticEnv] - Pre-computed static environment info
188
- * @returns {string[]} Lines for session section
189
- */
190
- function buildSessionSection(staticEnv = {}) {
191
- const memUsed = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
192
- const memTotal = Math.round(os.totalmem() / 1024 / 1024);
193
- const memPercent = Math.round((memUsed / memTotal) * 100);
194
- const cpuUsage = Math.round((process.cpuUsage().user / 1000000) * 100);
195
- const cpuSystem = Math.round((process.cpuUsage().system / 1000000) * 100);
196
-
197
- return [
198
- `## Session`,
199
- `- DateTime: ${new Date().toLocaleString()}`,
200
- `- CWD: ${staticEnv.cwd || process.cwd()}`,
201
- `- Timezone: ${staticEnv.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone}`,
202
- `- Working directory: ${staticEnv.cwd || process.cwd()}`,
203
- `- OS: ${staticEnv.osPlatform || process.platform}`,
204
- `- User: ${staticEnv.user || process.env.USERNAME || process.env.USER}`,
205
- `- Locale: ${staticEnv.locale || process.env.LANG || ''}`,
206
- `- Memory usage: ${memUsed}MB/${memTotal}MB (${memPercent}%)`,
207
- `- CPU usage: ${cpuUsage}% user / ${cpuSystem}% system`,
208
- `- Spawning multiple subagents can cause performance issues, spawn and delegate tasks intelligently based on the available system resources.`,
209
- `- Remember that each subagent only has 200K tokens in context window, spawn and delegate tasks intelligently to make sure their context windows don't get bloated.`,
210
- `- IMPORTANT: Include these environment information when prompting subagents to perform tasks.`,
211
- ``
212
- ];
213
- }
214
-
215
- /**
216
- * Build context window section from statusline cache
217
- * @param {string} sessionId - Session ID
218
- * @returns {string[]} Lines for context section
219
- */
220
- function buildContextSection(sessionId) {
221
- if (!sessionId) return [];
222
-
223
- try {
224
- const contextPath = path.join(os.tmpdir(), `ck-context-${sessionId}.json`);
225
- if (!fs.existsSync(contextPath)) return [];
226
-
227
- const data = JSON.parse(fs.readFileSync(contextPath, 'utf-8'));
228
- // Only use fresh data (< 5 min old - statusline updates every 300ms when active)
229
- if (Date.now() - data.timestamp > 300000) return [];
230
-
231
- const lines = [`## Current Session's Context`];
232
-
233
- // Format: 48% used (96K/200K tokens)
234
- const usedK = Math.round(data.tokens / 1000);
235
- const sizeK = Math.round(data.size / 1000);
236
- lines.push(`- Context: ${data.percent}% used (${usedK}K/${sizeK}K tokens)`);
237
- lines.push(`- **NOTE:** Optimize the workflow for token efficiency`);
238
-
239
- // Warning if high usage
240
- if (data.percent >= CRITICAL_THRESHOLD) {
241
- lines.push(`- **CRITICAL:** Context nearly full - consider compaction or being concise, update current phase's status before the compaction.`);
242
- } else if (data.percent >= WARN_THRESHOLD) {
243
- lines.push(`- **WARNING:** Context usage moderate - being concise and optimize token efficiency.`);
244
- }
245
-
246
- lines.push(``);
247
- return lines;
248
- } catch {
249
- return [];
250
- }
251
- }
252
-
253
- /**
254
- * Build rules section
255
- * @param {Object} params
256
- * @param {string} [params.devRulesPath] - Path to dev rules
257
- * @param {string} [params.catalogScript] - Path to catalog script
258
- * @param {string} [params.skillsVenv] - Path to skills venv
259
- * @param {string} [params.plansPath] - Absolute plans path (Issue #476: prevents wrong subdirectory creation)
260
- * @param {string} [params.docsPath] - Absolute docs path
261
- * @returns {string[]} Lines for rules section
262
- */
263
- function buildRulesSection({ devRulesPath, catalogScript, skillsVenv, plansPath, docsPath }) {
264
- const lines = [`## Rules`];
265
-
266
- if (devRulesPath) {
267
- lines.push(`- Read and follow development rules: "${devRulesPath}"`);
268
- }
269
-
270
- // Issue #476: Use absolute paths to prevent LLM confusion in multi-CLAUDE.md projects
271
- const plansRef = plansPath || 'plans';
272
- const docsRef = docsPath || 'docs';
273
- lines.push(`- Markdown files are organized in: Plans → "${plansRef}" directory, Docs → "${docsRef}" directory`);
274
- lines.push(`- **IMPORTANT:** DO NOT create markdown files outside of "${plansRef}" or "${docsRef}" UNLESS the user explicitly requests it.`);
275
-
276
- if (catalogScript) {
277
- lines.push(`- Activate skills: Use \`.claude/rules/skill-workflow-routing.md\` and \`.claude/rules/skill-domain-routing.md\`; run \`node ${catalogScript} --skills\` when you need a generated skills catalog.`);
278
- }
279
-
280
- if (skillsVenv) {
281
- lines.push(`- Python scripts in .claude/skills/: Use \`${skillsVenv}\``);
282
- }
283
-
284
- lines.push(`- When skills' scripts are failed to execute, always fix them and run again, repeat until success.`);
285
- lines.push(`- Follow **YAGNI - KISS - DRY** principles`);
286
- lines.push(`- Sacrifice grammar for the sake of concision when writing reports.`);
287
- lines.push(`- In reports, list any unresolved questions at the end, if any.`);
288
- lines.push(`- IMPORTANT: Ensure token consumption efficiency while maintaining high quality.`);
289
- lines.push(``);
290
-
291
- return lines;
292
- }
293
-
294
- /**
295
- * Build modularization section
296
- * @returns {string[]} Lines for modularization section
297
- */
298
- function buildModularizationSection() {
299
- return [
300
- `## **[IMPORTANT] Consider Modularization:**`,
301
- `- Check existing modules before creating new`,
302
- `- Analyze logical separation boundaries (functions, classes, concerns)`,
303
- `- Prefer kebab-case for JS/TS/Python/shell; respect language conventions (C#/Java use PascalCase, Go/Rust use snake_case)`,
304
- `- Write descriptive code comments`,
305
- `- After modularization, continue with main task`,
306
- `- When not to modularize: Markdown files, plain text files, bash scripts, configuration files, environment variables files, etc.`,
307
- ``
308
- ];
309
- }
310
-
311
- /**
312
- * Build paths section
313
- * @param {Object} params
314
- * @param {string} params.reportsPath - Reports path
315
- * @param {string} params.plansPath - Plans path
316
- * @param {string} params.docsPath - Docs path
317
- * @param {number} [params.docsMaxLoc=800] - Max lines of code for docs
318
- * @returns {string[]} Lines for paths section
319
- */
320
- function buildPathsSection({ reportsPath, plansPath, docsPath, docsMaxLoc = 800 }) {
321
- return [
322
- `## Paths`,
323
- `Reports: ${reportsPath} | Plans: ${plansPath}/ | Docs: ${docsPath}/ | docs.maxLoc: ${docsMaxLoc}`,
324
- ``
325
- ];
326
- }
327
-
328
- /**
329
- * Build plan context section
330
- * @param {Object} params
331
- * @param {string} params.planLine - Plan status line
332
- * @param {string} params.reportsPath - Reports path
333
- * @param {string} [params.gitBranch] - Git branch
334
- * @param {string} params.validationMode - Validation mode
335
- * @param {number} params.validationMin - Min questions
336
- * @param {number} params.validationMax - Max questions
337
- * @returns {string[]} Lines for plan context section
338
- */
339
- function buildPlanContextSection({ planLine, reportsPath, gitBranch, validationMode, validationMin, validationMax }) {
340
- const lines = [
341
- `## Plan Context`,
342
- planLine,
343
- `- Reports: ${reportsPath}`
344
- ];
345
-
346
- if (gitBranch) {
347
- lines.push(`- Branch: ${gitBranch}`);
348
- }
349
-
350
- lines.push(`- Validation: mode=${validationMode}, questions=${validationMin}-${validationMax}`);
351
- lines.push(``);
352
-
353
- return lines;
354
- }
355
-
356
- /**
357
- * Build naming section
358
- * @param {Object} params
359
- * @param {string} params.reportsPath - Reports path
360
- * @param {string} params.plansPath - Plans path
361
- * @param {string} params.namePattern - Naming pattern
362
- * @returns {string[]} Lines for naming section
363
- */
364
- function buildNamingSection({ reportsPath, plansPath, namePattern }) {
365
- return [
366
- `## Naming`,
367
- `- Report: \`${reportsPath}{type}-${namePattern}.md\``,
368
- `- Plan dir: \`${plansPath}/${namePattern}/\``,
369
- `- Replace \`{type}\` with: agent name, report type, or context`,
370
- `- Replace \`{slug}\` in pattern with: descriptive-kebab-slug`
371
- ];
372
- }
373
-
374
- // ═══════════════════════════════════════════════════════════════════════════
375
- // MAIN ENTRY POINTS
376
- // ═══════════════════════════════════════════════════════════════════════════
377
-
378
- /**
379
- * Build full reminder content from all sections
380
- * @param {Object} params - All parameters for building reminder
381
- * @returns {string[]} Array of lines
382
- */
383
- function buildReminder(params) {
384
- const {
385
- sessionId,
386
- thinkingLanguage,
387
- responseLanguage,
388
- devRulesPath,
389
- catalogScript,
390
- skillsVenv,
391
- reportsPath,
392
- plansPath,
393
- docsPath,
394
- docsMaxLoc,
395
- planLine,
396
- gitBranch,
397
- namePattern,
398
- validationMode,
399
- validationMin,
400
- validationMax,
401
- staticEnv,
402
- hooks
403
- } = params;
404
-
405
- // Respect hooks config — skip sections when their corresponding hook is disabled
406
- const hooksConfig = hooks || {};
407
- const contextEnabled = hooksConfig['context-tracking'] !== false;
408
-
409
- return [
410
- ...buildLanguageSection({ thinkingLanguage, responseLanguage }),
411
- ...buildSessionSection(staticEnv),
412
- ...(contextEnabled ? buildContextSection(sessionId) : []),
413
- ...buildRulesSection({ devRulesPath, catalogScript, skillsVenv, plansPath, docsPath }),
414
- ...buildModularizationSection(),
415
- ...buildPathsSection({ reportsPath, plansPath, docsPath, docsMaxLoc }),
416
- ...buildPlanContextSection({ planLine, reportsPath, gitBranch, validationMode, validationMin, validationMax }),
417
- ...buildNamingSection({ reportsPath, plansPath, namePattern })
418
- ];
419
- }
420
-
421
- /**
422
- * Build complete reminder context (unified entry point for plugins)
423
- *
424
- * @param {Object} [params]
425
- * @param {string} [params.sessionId] - Session ID
426
- * @param {Object} [params.config] - CK config (auto-loaded if not provided)
427
- * @param {Object} [params.staticEnv] - Pre-computed static environment info
428
- * @param {string} [params.configDirName='.claude'] - Config directory name
429
- * @param {string} [params.baseDir] - Base directory for absolute path resolution (Issue #327)
430
- * @returns {{
431
- * content: string,
432
- * lines: string[],
433
- * sections: Object
434
- * }}
435
- */
436
- function buildReminderContext({ sessionId, config, staticEnv, configDirName = '.claude', baseDir } = {}) {
437
- // Load config if not provided
438
- const cfg = config || loadConfig({ includeProject: false, includeAssertions: false });
439
-
440
- // Resolve paths
441
- const devRulesPath = resolveRulesPath('ai-dev-rules.md', configDirName);
442
- const catalogScript = resolveScriptPath('generate-skill-catalog.cjs', configDirName);
443
- const skillsVenv = resolveSkillsVenv(configDirName);
444
-
445
- // Build plan context
446
- const planCtx = buildPlanContext(sessionId, cfg);
447
-
448
- // Issue #327: Use baseDir for absolute path resolution (subdirectory workflow support)
449
- // If baseDir provided, resolve paths as absolute; otherwise use relative paths
450
- const effectiveBaseDir = baseDir || null;
451
- const plansPathRel = normalizePath(cfg.paths?.plans) || 'plans';
452
- const docsPathRel = normalizePath(cfg.paths?.docs) || 'docs';
453
-
454
- // Build all parameters with absolute paths if baseDir provided
455
- const params = {
456
- sessionId,
457
- thinkingLanguage: cfg.locale?.thinkingLanguage,
458
- responseLanguage: cfg.locale?.responseLanguage,
459
- devRulesPath,
460
- catalogScript,
461
- skillsVenv,
462
- reportsPath: effectiveBaseDir ? path.join(effectiveBaseDir, planCtx.reportsPath) : planCtx.reportsPath,
463
- plansPath: effectiveBaseDir ? path.join(effectiveBaseDir, plansPathRel) : plansPathRel,
464
- docsPath: effectiveBaseDir ? path.join(effectiveBaseDir, docsPathRel) : docsPathRel,
465
- docsMaxLoc: Math.max(1, parseInt(cfg.docs?.maxLoc, 10) || 800),
466
- planLine: planCtx.planLine,
467
- gitBranch: planCtx.gitBranch,
468
- namePattern: planCtx.namePattern,
469
- validationMode: planCtx.validationMode,
470
- validationMin: planCtx.validationMin,
471
- validationMax: planCtx.validationMax,
472
- staticEnv,
473
- hooks: cfg.hooks
474
- };
475
-
476
- const lines = buildReminder(params);
477
-
478
- // Respect hooks config for sections object too
479
- const hooksConfig = cfg.hooks || {};
480
- const contextEnabled = hooksConfig['context-tracking'] !== false;
481
-
482
- return {
483
- content: lines.join('\n'),
484
- lines,
485
- sections: {
486
- language: buildLanguageSection({ thinkingLanguage: params.thinkingLanguage, responseLanguage: params.responseLanguage }),
487
- session: buildSessionSection(staticEnv),
488
- context: contextEnabled ? buildContextSection(sessionId) : [],
489
- rules: buildRulesSection({ devRulesPath, catalogScript, skillsVenv, plansPath: params.plansPath, docsPath: params.docsPath }),
490
- modularization: buildModularizationSection(),
491
- paths: buildPathsSection({ reportsPath: params.reportsPath, plansPath: params.plansPath, docsPath: params.docsPath, docsMaxLoc: params.docsMaxLoc }),
492
- planContext: buildPlanContextSection(planCtx),
493
- naming: buildNamingSection({ reportsPath: params.reportsPath, plansPath: params.plansPath, namePattern: params.namePattern })
494
- }
495
- };
496
- }
497
-
498
- // ═══════════════════════════════════════════════════════════════════════════
499
- // EXPORTS
500
- // ═══════════════════════════════════════════════════════════════════════════
501
-
502
- module.exports = {
503
- // Main entry points
504
- buildReminderContext,
505
- buildReminder,
506
-
507
- // Section builders
508
- buildLanguageSection,
509
- buildSessionSection,
510
- buildContextSection,
511
- buildRulesSection,
512
- buildModularizationSection,
513
- buildPathsSection,
514
- buildPlanContextSection,
515
- buildNamingSection,
516
-
517
- // Helpers
518
- execSafe,
519
- resolveRulesPath,
520
- resolveScriptPath,
521
- resolveSkillsVenv,
522
- buildPlanContext,
523
- wasRecentlyInjected,
524
-
525
- // Backward compat alias
526
- resolveWorkflowPath: resolveRulesPath
527
- };