thatgfsj-code 1.0.3 → 3.0.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 (184) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/DEVELOPMENT.md +286 -0
  3. package/dist/app/index.d.ts +40 -3
  4. package/dist/app/index.d.ts.map +1 -1
  5. package/dist/app/index.js +87 -12
  6. package/dist/app/index.js.map +1 -1
  7. package/dist/cache/fingerprint.d.ts +49 -0
  8. package/dist/cache/fingerprint.d.ts.map +1 -0
  9. package/dist/cache/fingerprint.js +100 -0
  10. package/dist/cache/fingerprint.js.map +1 -0
  11. package/dist/cache/index.d.ts +15 -0
  12. package/dist/cache/index.d.ts.map +1 -0
  13. package/dist/cache/index.js +15 -0
  14. package/dist/cache/index.js.map +1 -0
  15. package/dist/cache/smartModel.d.ts +40 -0
  16. package/dist/cache/smartModel.d.ts.map +1 -0
  17. package/dist/cache/smartModel.js +43 -0
  18. package/dist/cache/smartModel.js.map +1 -0
  19. package/dist/cache/stats.d.ts +93 -0
  20. package/dist/cache/stats.d.ts.map +1 -0
  21. package/dist/cache/stats.js +155 -0
  22. package/dist/cache/stats.js.map +1 -0
  23. package/dist/cache/volatile.d.ts +32 -0
  24. package/dist/cache/volatile.d.ts.map +1 -0
  25. package/dist/cache/volatile.js +44 -0
  26. package/dist/cache/volatile.js.map +1 -0
  27. package/dist/cmd/index.d.ts +21 -0
  28. package/dist/cmd/index.d.ts.map +1 -1
  29. package/dist/cmd/index.js +155 -31
  30. package/dist/cmd/index.js.map +1 -1
  31. package/dist/config/index.d.ts.map +1 -1
  32. package/dist/config/index.js +8 -0
  33. package/dist/config/index.js.map +1 -1
  34. package/dist/config/types.d.ts +14 -0
  35. package/dist/config/types.d.ts.map +1 -1
  36. package/dist/llm/anthropic.d.ts +55 -5
  37. package/dist/llm/anthropic.d.ts.map +1 -1
  38. package/dist/llm/anthropic.js +172 -26
  39. package/dist/llm/anthropic.js.map +1 -1
  40. package/dist/llm/index.d.ts +26 -8
  41. package/dist/llm/index.d.ts.map +1 -1
  42. package/dist/llm/index.js +83 -15
  43. package/dist/llm/index.js.map +1 -1
  44. package/dist/llm/openai.d.ts +28 -8
  45. package/dist/llm/openai.d.ts.map +1 -1
  46. package/dist/llm/openai.js +58 -8
  47. package/dist/llm/openai.js.map +1 -1
  48. package/dist/llm/provider.d.ts +40 -8
  49. package/dist/llm/provider.d.ts.map +1 -1
  50. package/dist/llm/provider.js +8 -1
  51. package/dist/llm/provider.js.map +1 -1
  52. package/dist/prompts/index.d.ts +32 -0
  53. package/dist/prompts/index.d.ts.map +1 -1
  54. package/dist/prompts/index.js +33 -11
  55. package/dist/prompts/index.js.map +1 -1
  56. package/dist/session/index.d.ts +43 -3
  57. package/dist/session/index.d.ts.map +1 -1
  58. package/dist/session/index.js +114 -17
  59. package/dist/session/index.js.map +1 -1
  60. package/dist/skills/brainstorming.js +33 -33
  61. package/dist/skills/code-review.js +35 -35
  62. package/dist/skills/executing-plans.js +18 -18
  63. package/dist/skills/frontend-design.js +25 -25
  64. package/dist/skills/git-workflow.js +26 -26
  65. package/dist/skills/improve-architecture.js +28 -28
  66. package/dist/skills/neuroweave.js +37 -37
  67. package/dist/skills/playwright.js +62 -62
  68. package/dist/skills/prototype.js +20 -20
  69. package/dist/skills/subagent.js +19 -19
  70. package/dist/skills/supabase.js +34 -34
  71. package/dist/skills/systematic-debugging.js +34 -34
  72. package/dist/skills/tdd.js +29 -29
  73. package/dist/skills/triage.js +25 -25
  74. package/dist/skills/verification.js +22 -22
  75. package/dist/skills/writing-plans.js +32 -32
  76. package/dist/tools/nwt.js +16 -16
  77. package/dist/tui/app.d.ts.map +1 -1
  78. package/dist/tui/app.js +9 -2
  79. package/dist/tui/app.js.map +1 -1
  80. package/dist/tui/components/ChatList.d.ts.map +1 -1
  81. package/dist/tui/components/ChatList.js +4 -3
  82. package/dist/tui/components/ChatList.js.map +1 -1
  83. package/dist/tui/components/Header.d.ts +8 -0
  84. package/dist/tui/components/Header.d.ts.map +1 -1
  85. package/dist/tui/components/Header.js +17 -3
  86. package/dist/tui/components/Header.js.map +1 -1
  87. package/dist/tui/components/InitWizard.d.ts +12 -1
  88. package/dist/tui/components/InitWizard.d.ts.map +1 -1
  89. package/dist/tui/components/InitWizard.js +58 -3
  90. package/dist/tui/components/InitWizard.js.map +1 -1
  91. package/dist/tui/components/ToolCall.d.ts +1 -1
  92. package/dist/tui/components/ToolCall.d.ts.map +1 -1
  93. package/dist/tui/components/ToolCall.js +4 -4
  94. package/dist/tui/components/ToolCall.js.map +1 -1
  95. package/dist/tui/hooks/useChat.d.ts +22 -0
  96. package/dist/tui/hooks/useChat.d.ts.map +1 -1
  97. package/dist/tui/hooks/useChat.js +127 -50
  98. package/dist/tui/hooks/useChat.js.map +1 -1
  99. package/dist/tui/hooks/useCommands.d.ts.map +1 -1
  100. package/dist/tui/hooks/useCommands.js +78 -0
  101. package/dist/tui/hooks/useCommands.js.map +1 -1
  102. package/dist/types.d.ts +79 -6
  103. package/dist/types.d.ts.map +1 -1
  104. package/dist/types.js +7 -0
  105. package/dist/types.js.map +1 -1
  106. package/dist/utils/stableStringify.d.ts +21 -0
  107. package/dist/utils/stableStringify.d.ts.map +1 -0
  108. package/dist/utils/stableStringify.js +53 -0
  109. package/dist/utils/stableStringify.js.map +1 -0
  110. package/dist/utils/thinking.d.ts +59 -0
  111. package/dist/utils/thinking.d.ts.map +1 -0
  112. package/dist/utils/thinking.js +104 -0
  113. package/dist/utils/thinking.js.map +1 -0
  114. package/install.bat +63 -0
  115. package/install.ps1 +238 -0
  116. package/install.sh +113 -0
  117. package/package.json +7 -4
  118. package/src/app/index.ts +180 -108
  119. package/src/cache/fingerprint.ts +101 -0
  120. package/src/cache/index.ts +15 -0
  121. package/src/cache/smartModel.ts +52 -0
  122. package/src/cache/stats.ts +199 -0
  123. package/src/cache/volatile.ts +47 -0
  124. package/src/cmd/index.tsx +288 -160
  125. package/src/config/index.ts +156 -148
  126. package/src/config/providers.ts +234 -234
  127. package/src/config/types.ts +67 -53
  128. package/src/hooks/index.ts +111 -111
  129. package/src/llm/anthropic.ts +388 -243
  130. package/src/llm/gemini.ts +169 -169
  131. package/src/llm/index.ts +265 -200
  132. package/src/llm/openai.ts +243 -196
  133. package/src/llm/provider.ts +66 -34
  134. package/src/prompts/index.ts +260 -220
  135. package/src/session/compactor.ts +103 -103
  136. package/src/session/index.ts +181 -87
  137. package/src/session/message.ts +42 -42
  138. package/src/skills/brainstorming.ts +43 -43
  139. package/src/skills/code-review.ts +45 -45
  140. package/src/skills/executing-plans.ts +27 -27
  141. package/src/skills/frontend-design.ts +35 -35
  142. package/src/skills/git-workflow.ts +36 -36
  143. package/src/skills/improve-architecture.ts +38 -38
  144. package/src/skills/index.ts +136 -136
  145. package/src/skills/neuroweave.ts +47 -47
  146. package/src/skills/playwright.ts +72 -72
  147. package/src/skills/prototype.ts +30 -30
  148. package/src/skills/subagent.ts +28 -28
  149. package/src/skills/supabase.ts +44 -44
  150. package/src/skills/systematic-debugging.ts +44 -44
  151. package/src/skills/tdd.ts +39 -39
  152. package/src/skills/triage.ts +35 -35
  153. package/src/skills/verification.ts +31 -31
  154. package/src/skills/writing-plans.ts +42 -42
  155. package/src/tools/nwt.ts +598 -598
  156. package/src/tools/types.ts +122 -122
  157. package/src/tui/app.tsx +199 -186
  158. package/src/tui/components/ChatList.tsx +41 -32
  159. package/src/tui/components/ChatMessage.tsx +54 -54
  160. package/src/tui/components/Header.tsx +56 -28
  161. package/src/tui/components/InitWizard.tsx +217 -132
  162. package/src/tui/components/Markdown.tsx +35 -35
  163. package/src/tui/components/ModelSelector.tsx +87 -87
  164. package/src/tui/components/StatusBar.tsx +30 -30
  165. package/src/tui/components/Thinking.tsx +17 -17
  166. package/src/tui/components/ToolCall.tsx +102 -71
  167. package/src/tui/components/UserInput.tsx +131 -131
  168. package/src/tui/hooks/useChat.ts +287 -194
  169. package/src/tui/hooks/useCommands.ts +234 -154
  170. package/src/tui/index.ts +6 -6
  171. package/src/tui/welcome.ts +177 -177
  172. package/src/types.ts +104 -42
  173. package/src/utils/diff.ts +71 -71
  174. package/src/utils/project.ts +99 -99
  175. package/src/utils/stableStringify.ts +47 -0
  176. package/src/utils/thinking.ts +119 -0
  177. package/tests/cache/fingerprint.test.ts +75 -0
  178. package/tests/cache/stableStringify.test.ts +43 -0
  179. package/tests/cache/stats.test.ts +146 -0
  180. package/tests/cache/volatile.test.ts +75 -0
  181. package/tests/smoke-pollution.mjs +78 -0
  182. package/tests/smoke-thinking.mjs +110 -0
  183. package/tests/smoke-tool-stream.mjs +69 -0
  184. package/tests/smoke-tool.mjs +117 -0
@@ -1,220 +1,260 @@
1
- /**
2
- * System Prompt Builder - Dynamic prompt construction
3
- */
4
-
5
- import { readFileSync, existsSync, readdirSync } from 'fs';
6
- import { join } from 'path';
7
- import type { Tool } from '../tools/types.js';
8
-
9
- export interface SystemPromptConfig {
10
- cwd?: string;
11
- tools?: Tool[];
12
- includeProjectMd?: boolean;
13
- permissionMode?: 'accept' | 'deny' | 'ask';
14
- date?: Date;
15
- skillsPrompt?: string;
16
- }
17
-
18
- export class SystemPromptBuilder {
19
- private config: Required<SystemPromptConfig>;
20
-
21
- constructor(config: SystemPromptConfig = {}) {
22
- this.config = {
23
- cwd: config.cwd ?? process.cwd(),
24
- tools: config.tools ?? [],
25
- includeProjectMd: config.includeProjectMd ?? true,
26
- permissionMode: config.permissionMode ?? 'ask',
27
- date: config.date ?? new Date(),
28
- skillsPrompt: config.skillsPrompt ?? '',
29
- };
30
- }
31
-
32
- build(): string {
33
- const fragments: string[] = [
34
- this.buildProjectInstructions(), // User config FIRST for better compliance
35
- this.buildIdentity(),
36
- this.buildToolInstructions(),
37
- this.buildEnvironment(),
38
- this.buildPermissionMode(),
39
- this.buildNwtHistory(),
40
- this.buildSkills(),
41
- this.buildDateInfo(),
42
- ];
43
- return fragments.filter(Boolean).join('\n\n');
44
- }
45
-
46
- private buildIdentity(): string {
47
- return [
48
- 'You are Thatgfsj Code, an interactive coding agent.',
49
- '',
50
- 'IMPORTANT: You MUST follow the user configuration above (CLAUDE.md, SKILLS.md, etc).',
51
- 'At the start of each task, read SKILLS.md to check for relevant skills.',
52
- '',
53
- 'Tools available: file, shell, git, search, nwt.',
54
- '',
55
- 'Rules:',
56
- '- Follow the user\'s technical preferences from their config',
57
- '- Be concise but thorough',
58
- '- Verify changes after making them',
59
- '',
60
- '## NWT Auto-Logging (IMPORTANT)',
61
- '',
62
- 'After completing any meaningful task, you MUST evaluate whether to log it to NWT.',
63
- 'Call `nwt log` automatically when ANY of these conditions are true:',
64
- '',
65
- '1. You created, modified, or deleted 2+ files',
66
- '2. You made an architectural decision (chose a library, pattern, or approach)',
67
- '3. You fixed a bug',
68
- '4. You added a new feature or capability',
69
- '5. You refactored existing code',
70
- '6. You set up a new project or module',
71
- '7. You resolved a security issue',
72
- '8. You completed a multi-step task',
73
- '',
74
- 'Do NOT log:',
75
- '- Simple file reads or searches',
76
- '- Trivial single-line changes',
77
- '- Conversational questions with no code changes',
78
- '',
79
- 'When logging, use:',
80
- ' nwt log task="<imperative verb> <what>" summary="<what was done>" files="<files>" tags="<tags>"',
81
- '',
82
- 'Tags: feature, fix, refactor, decision, milestone, test, docs, config, security, init',
83
- '',
84
- 'IMPORTANT: Log silently without mentioning it to the user unless they ask.',
85
- ].join('\n');
86
- }
87
-
88
- private buildToolInstructions(): string {
89
- const tools = this.config.tools;
90
- if (tools.length === 0) return '## Tools\n\nNo tools are currently registered.';
91
-
92
- const toolDescs = tools.map(t => {
93
- const params = t.parameters
94
- .map(p => ` - ${p.name} (${p.type}${p.required ? ', required' : ''}): ${p.description}`)
95
- .join('\n');
96
- return `### ${t.name}\n${t.description}\n\nParameters:\n${params}`;
97
- }).join('\n\n');
98
-
99
- return `## Tools\n\nYou have access to the following tools:\n\n${toolDescs}\n\nTo use a tool, respond with a tool call.`;
100
- }
101
-
102
- private buildEnvironment(): string {
103
- return `## Environment\n\nWorking directory: ${this.config.cwd}`;
104
- }
105
-
106
- private buildPermissionMode(): string {
107
- const mode = this.config.permissionMode;
108
- const explanations: Record<string, string> = {
109
- accept: 'All tool calls are automatically allowed without confirmation.',
110
- deny: 'All tool calls are blocked. You may only read and discuss.',
111
- ask: 'Dangerous or destructive commands require user confirmation before execution.',
112
- };
113
- return `## Permission Mode\n\nCurrent mode: ${mode}\n\n${explanations[mode] || ''}`;
114
- }
115
-
116
- /**
117
- * Read project instruction files (generic, works for any user)
118
- */
119
- private buildProjectInstructions(): string {
120
- if (!this.config.includeProjectMd) return '';
121
-
122
- const cwd = this.config.cwd;
123
- const home = process.env.USERPROFILE || process.env.HOME || '';
124
-
125
- // Project-level files (any project can have these)
126
- const projectFiles = [
127
- 'CLAUDE.md', '.claude.md', 'AGENTS.md',
128
- 'Codex.md', '.Codex.md', 'CODEX.md', '.codex.md',
129
- 'CONVENTIONS.md', 'CONTRIBUTING.md',
130
- ];
131
-
132
- // User-level files (in home directory)
133
- const userDirs = ['.claude', '.Codex', '.agents'];
134
- const userFiles = ['CLAUDE.md', 'SKILLS.md', 'AGENTS.md', 'CONVENTIONS.md'];
135
-
136
- const paths: string[] = [];
137
-
138
- // Project-level
139
- for (const f of projectFiles) {
140
- paths.push(join(cwd, f));
141
- }
142
-
143
- // User-level
144
- for (const dir of userDirs) {
145
- for (const f of userFiles) {
146
- paths.push(join(home, dir, f));
147
- }
148
- }
149
-
150
- const sections: string[] = [];
151
- const MAX_LEN = 3000;
152
-
153
- for (const path of paths) {
154
- if (existsSync(path)) {
155
- try {
156
- let content = readFileSync(path, 'utf-8').trim();
157
- if (content) {
158
- const filename = path.split(/[/\\]/).pop();
159
- if (content.length > MAX_LEN) {
160
- content = content.slice(0, MAX_LEN) + '\n... (truncated)';
161
- }
162
- sections.push(`[${filename}]\n${content}`);
163
- }
164
- } catch {}
165
- }
166
- }
167
-
168
- if (sections.length === 0) return '';
169
- return `## User Configuration\n\n${sections.join('\n\n')}`;
170
- }
171
-
172
- /**
173
- * Auto-inject recent NWT history into system prompt
174
- */
175
- private buildNwtHistory(): string {
176
- try {
177
- const nwtDir = join(this.config.cwd, '.nwt', 'events');
178
- if (!existsSync(nwtDir)) return '';
179
-
180
- const files = readdirSync(nwtDir)
181
- .filter(f => f.endsWith('.json'))
182
- .sort()
183
- .slice(-10); // Last 10 events
184
-
185
- if (files.length === 0) return '';
186
-
187
- const events = files.map(f => {
188
- try {
189
- const ev = JSON.parse(readFileSync(join(nwtDir, f), 'utf-8'));
190
- const time = ev.timestamp?.split('T')[0] || '';
191
- const files = ev.files?.length > 0 ? ` [${ev.files.join(', ')}]` : '';
192
- return `- [${ev.id}] ${time} ${ev.task}: ${ev.summary}${files}`;
193
- } catch {
194
- return null;
195
- }
196
- }).filter(Boolean);
197
-
198
- if (events.length === 0) return '';
199
-
200
- return `## Project History (NWT)\n\nRecent evolution events:\n\n${events.join('\n')}`;
201
- } catch {
202
- return '';
203
- }
204
- }
205
-
206
- private buildSkills(): string {
207
- if (!this.config.skillsPrompt) return '';
208
- return `## Active Skills\n\n${this.config.skillsPrompt}`;
209
- }
210
-
211
- private buildDateInfo(): string {
212
- const now = this.config.date;
213
- const iso = now.toISOString().replace('T', ' ').split('.')[0];
214
- return `## Current Time\n\n${iso}`;
215
- }
216
-
217
- setTools(tools: Tool[]): this { this.config.tools = tools; return this; }
218
- setCwd(cwd: string): this { this.config.cwd = cwd; return this; }
219
- setPermissionMode(mode: 'accept' | 'deny' | 'ask'): this { this.config.permissionMode = mode; return this; }
220
- }
1
+ /**
2
+ * System Prompt Builder - Dynamic prompt construction
3
+ */
4
+
5
+ import { readFileSync, existsSync, readdirSync } from 'fs';
6
+ import { join } from 'path';
7
+ import type { Tool } from '../tools/types.js';
8
+
9
+ export interface SystemPromptConfig {
10
+ cwd?: string;
11
+ tools?: Tool[];
12
+ includeProjectMd?: boolean;
13
+ permissionMode?: 'accept' | 'deny' | 'ask';
14
+ date?: Date;
15
+ skillsPrompt?: string;
16
+ }
17
+
18
+ /**
19
+ * v3.0.0: a single named fragment of the system prompt. The provider layer
20
+ * attaches cache_control markers to volatile=false segments (Anthropic) and
21
+ * the fingerprint module hashes only the non-volatile portion.
22
+ */
23
+ export interface SystemSegment {
24
+ /** Stable identifier, used in fingerprints and logging. */
25
+ name: string;
26
+ /** Rendered text content. May be empty string if the section was a no-op. */
27
+ content: string;
28
+ /**
29
+ * volatile=false: content never changes between requests within a session.
30
+ * volatile=true: content may change (NWT events, current time).
31
+ */
32
+ volatile: boolean;
33
+ }
34
+
35
+ export class SystemPromptBuilder {
36
+ private config: Required<SystemPromptConfig>;
37
+
38
+ constructor(config: SystemPromptConfig = {}) {
39
+ this.config = {
40
+ cwd: config.cwd ?? process.cwd(),
41
+ tools: config.tools ?? [],
42
+ includeProjectMd: config.includeProjectMd ?? true,
43
+ permissionMode: config.permissionMode ?? 'ask',
44
+ date: config.date ?? new Date(),
45
+ skillsPrompt: config.skillsPrompt ?? '',
46
+ };
47
+ }
48
+
49
+ build(): string {
50
+ return this.buildSegments()
51
+ .map(s => s.content)
52
+ .filter(Boolean)
53
+ .join('\n\n');
54
+ }
55
+
56
+ /**
57
+ * v3.0.0: build the system prompt as named segments instead of a flat
58
+ * string. The provider layer (Anthropic) can attach cache_control markers
59
+ * to the segments it caches, and the fingerprint module can hash the
60
+ * static portion to detect when the immutable prefix changed.
61
+ *
62
+ * Ordering rule (Reasonix-style): immutable prefix first, volatile tail
63
+ * last. NWT history and Date move to the tail because both can change
64
+ * between rounds (Date: every build; NWT: when a new event is logged).
65
+ * Keeping them out of the prefix means the upstream cache keeps its
66
+ * prefix checkpoint stable across those changes.
67
+ *
68
+ * Segments with empty content are filtered out — they neither contribute
69
+ * to the string output nor to any cache fingerprint.
70
+ */
71
+ buildSegments(): SystemSegment[] {
72
+ return [
73
+ // Immutable prefix (cacheable as a single block on Anthropic)
74
+ { name: 'project-instructions', content: this.buildProjectInstructions(), volatile: false },
75
+ { name: 'identity', content: this.buildIdentity(), volatile: false },
76
+ { name: 'tool-instructions', content: this.buildToolInstructions(), volatile: false },
77
+ { name: 'environment', content: this.buildEnvironment(), volatile: false },
78
+ { name: 'permission-mode', content: this.buildPermissionMode(), volatile: false },
79
+ { name: 'skills', content: this.buildSkills(), volatile: false },
80
+ // Volatile tail not part of the cache prefix
81
+ { name: 'nwt-history', content: this.buildNwtHistory(), volatile: true },
82
+ { name: 'date-info', content: this.buildDateInfo(), volatile: true },
83
+ ].filter(s => s.content);
84
+ }
85
+
86
+ private buildIdentity(): string {
87
+ return [
88
+ 'You are Thatgfsj Code, an interactive coding agent.',
89
+ '',
90
+ 'IMPORTANT: You MUST follow the user configuration above (CLAUDE.md, SKILLS.md, etc).',
91
+ 'At the start of each task, read SKILLS.md to check for relevant skills.',
92
+ '',
93
+ 'Tools available: file, shell, git, search, nwt.',
94
+ '',
95
+ 'Rules:',
96
+ '- Follow the user\'s technical preferences from their config',
97
+ '- Be concise but thorough',
98
+ '- Verify changes after making them',
99
+ '',
100
+ '## NWT Auto-Logging (IMPORTANT)',
101
+ '',
102
+ 'After completing any meaningful task, you MUST evaluate whether to log it to NWT.',
103
+ 'Call `nwt log` automatically when ANY of these conditions are true:',
104
+ '',
105
+ '1. You created, modified, or deleted 2+ files',
106
+ '2. You made an architectural decision (chose a library, pattern, or approach)',
107
+ '3. You fixed a bug',
108
+ '4. You added a new feature or capability',
109
+ '5. You refactored existing code',
110
+ '6. You set up a new project or module',
111
+ '7. You resolved a security issue',
112
+ '8. You completed a multi-step task',
113
+ '',
114
+ 'Do NOT log:',
115
+ '- Simple file reads or searches',
116
+ '- Trivial single-line changes',
117
+ '- Conversational questions with no code changes',
118
+ '',
119
+ 'When logging, use:',
120
+ ' nwt log task="<imperative verb> <what>" summary="<what was done>" files="<files>" tags="<tags>"',
121
+ '',
122
+ 'Tags: feature, fix, refactor, decision, milestone, test, docs, config, security, init',
123
+ '',
124
+ 'IMPORTANT: Log silently without mentioning it to the user unless they ask.',
125
+ ].join('\n');
126
+ }
127
+
128
+ private buildToolInstructions(): string {
129
+ const tools = this.config.tools;
130
+ if (tools.length === 0) return '## Tools\n\nNo tools are currently registered.';
131
+
132
+ const toolDescs = tools.map(t => {
133
+ const params = t.parameters
134
+ .map(p => ` - ${p.name} (${p.type}${p.required ? ', required' : ''}): ${p.description}`)
135
+ .join('\n');
136
+ return `### ${t.name}\n${t.description}\n\nParameters:\n${params}`;
137
+ }).join('\n\n');
138
+
139
+ return `## Tools\n\nYou have access to the following tools:\n\n${toolDescs}\n\nTo use a tool, respond with a tool call.`;
140
+ }
141
+
142
+ private buildEnvironment(): string {
143
+ return `## Environment\n\nWorking directory: ${this.config.cwd}`;
144
+ }
145
+
146
+ private buildPermissionMode(): string {
147
+ const mode = this.config.permissionMode;
148
+ const explanations: Record<string, string> = {
149
+ accept: 'All tool calls are automatically allowed without confirmation.',
150
+ deny: 'All tool calls are blocked. You may only read and discuss.',
151
+ ask: 'Dangerous or destructive commands require user confirmation before execution.',
152
+ };
153
+ return `## Permission Mode\n\nCurrent mode: ${mode}\n\n${explanations[mode] || ''}`;
154
+ }
155
+
156
+ /**
157
+ * Read project instruction files (generic, works for any user)
158
+ */
159
+ private buildProjectInstructions(): string {
160
+ if (!this.config.includeProjectMd) return '';
161
+
162
+ const cwd = this.config.cwd;
163
+ const home = process.env.USERPROFILE || process.env.HOME || '';
164
+
165
+ // Project-level files (any project can have these)
166
+ const projectFiles = [
167
+ 'CLAUDE.md', '.claude.md', 'AGENTS.md',
168
+ 'Codex.md', '.Codex.md', 'CODEX.md', '.codex.md',
169
+ 'CONVENTIONS.md', 'CONTRIBUTING.md',
170
+ ];
171
+
172
+ // User-level files (in home directory)
173
+ const userDirs = ['.claude', '.Codex', '.agents'];
174
+ const userFiles = ['CLAUDE.md', 'SKILLS.md', 'AGENTS.md', 'CONVENTIONS.md'];
175
+
176
+ const paths: string[] = [];
177
+
178
+ // Project-level
179
+ for (const f of projectFiles) {
180
+ paths.push(join(cwd, f));
181
+ }
182
+
183
+ // User-level
184
+ for (const dir of userDirs) {
185
+ for (const f of userFiles) {
186
+ paths.push(join(home, dir, f));
187
+ }
188
+ }
189
+
190
+ const sections: string[] = [];
191
+ const MAX_LEN = 3000;
192
+
193
+ for (const path of paths) {
194
+ if (existsSync(path)) {
195
+ try {
196
+ let content = readFileSync(path, 'utf-8').trim();
197
+ if (content) {
198
+ const filename = path.split(/[/\\]/).pop();
199
+ if (content.length > MAX_LEN) {
200
+ content = content.slice(0, MAX_LEN) + '\n... (truncated)';
201
+ }
202
+ sections.push(`[${filename}]\n${content}`);
203
+ }
204
+ } catch {}
205
+ }
206
+ }
207
+
208
+ if (sections.length === 0) return '';
209
+ return `## User Configuration\n\n${sections.join('\n\n')}`;
210
+ }
211
+
212
+ /**
213
+ * Auto-inject recent NWT history into system prompt
214
+ */
215
+ private buildNwtHistory(): string {
216
+ try {
217
+ const nwtDir = join(this.config.cwd, '.nwt', 'events');
218
+ if (!existsSync(nwtDir)) return '';
219
+
220
+ const files = readdirSync(nwtDir)
221
+ .filter(f => f.endsWith('.json'))
222
+ .sort()
223
+ .slice(-10); // Last 10 events
224
+
225
+ if (files.length === 0) return '';
226
+
227
+ const events = files.map(f => {
228
+ try {
229
+ const ev = JSON.parse(readFileSync(join(nwtDir, f), 'utf-8'));
230
+ const time = ev.timestamp?.split('T')[0] || '';
231
+ const files = ev.files?.length > 0 ? ` [${ev.files.join(', ')}]` : '';
232
+ return `- [${ev.id}] ${time} ${ev.task}: ${ev.summary}${files}`;
233
+ } catch {
234
+ return null;
235
+ }
236
+ }).filter(Boolean);
237
+
238
+ if (events.length === 0) return '';
239
+
240
+ return `## Project History (NWT)\n\nRecent evolution events:\n\n${events.join('\n')}`;
241
+ } catch {
242
+ return '';
243
+ }
244
+ }
245
+
246
+ private buildSkills(): string {
247
+ if (!this.config.skillsPrompt) return '';
248
+ return `## Active Skills\n\n${this.config.skillsPrompt}`;
249
+ }
250
+
251
+ private buildDateInfo(): string {
252
+ const now = this.config.date;
253
+ const iso = now.toISOString().replace('T', ' ').split('.')[0];
254
+ return `## Current Time\n\n${iso}`;
255
+ }
256
+
257
+ setTools(tools: Tool[]): this { this.config.tools = tools; return this; }
258
+ setCwd(cwd: string): this { this.config.cwd = cwd; return this; }
259
+ setPermissionMode(mode: 'accept' | 'deny' | 'ask'): this { this.config.permissionMode = mode; return this; }
260
+ }