tsunami-code 3.6.1 → 3.8.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 (3) hide show
  1. package/index.js +16 -2
  2. package/lib/prompt.js +144 -99
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -9,7 +9,7 @@ import { injectServerContext } from './lib/tools.js';
9
9
  import { loadSkills, getSkillCommand, createSkill, listSkills } from './lib/skills.js';
10
10
  import { isCoordinatorTask, stripCoordinatorPrefix, buildCoordinatorSystemPrompt } from './lib/coordinator.js';
11
11
  import { getDueTasks, markDone, cancelTask, listTasks, formatTaskList } from './lib/kairos.js';
12
- import { buildSystemPrompt } from './lib/prompt.js';
12
+ import { buildSystemPrompt, buildUserContext } from './lib/prompt.js';
13
13
  import { runPreflight, checkServer } from './lib/preflight.js';
14
14
  import { setSession, undo, undoStackSize, beginUndoTurn, registerMcpTools, linesChanged } from './lib/tools.js';
15
15
  import { connectMcpServers, getMcpToolObjects, getMcpStatus, getMcpConfigPath, disconnectAll as disconnectMcp } from './lib/mcp.js';
@@ -26,7 +26,7 @@ import {
26
26
  } from './lib/memory.js';
27
27
  import { listMemories, readMemory, saveMemory, deleteMemory, getMemdirPath } from './lib/memdir.js';
28
28
 
29
- const VERSION = '3.6.1';
29
+ const VERSION = '3.8.0';
30
30
  const CONFIG_DIR = join(os.homedir(), '.tsunami-code');
31
31
  const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
32
32
  const DEFAULT_SERVER = 'https://radiometric-reita-amuck.ngrok-free.dev';
@@ -396,6 +396,15 @@ async function run() {
396
396
  let currentServerUrl = serverUrl;
397
397
  let messages = [];
398
398
  let systemPrompt = buildSystemPrompt();
399
+
400
+ // Prepend user context as first human message — mirrors CC's prependUserContext().
401
+ // Contains TSUNAMI.md + current date. Lives in messages, NOT the system prompt.
402
+ const userCtx = buildUserContext();
403
+ if (userCtx) {
404
+ messages.push({ role: 'user', content: userCtx });
405
+ messages.push({ role: 'assistant', content: 'Understood.' });
406
+ }
407
+
399
408
  let _inputTokens = 0;
400
409
  let _outputTokens = 0;
401
410
 
@@ -433,6 +442,11 @@ async function run() {
433
442
  function resetSession() {
434
443
  messages = [];
435
444
  systemPrompt = buildSystemPrompt();
445
+ const ctx = buildUserContext();
446
+ if (ctx) {
447
+ messages.push({ role: 'user', content: ctx });
448
+ messages.push({ role: 'assistant', content: 'Understood.' });
449
+ }
436
450
  }
437
451
 
438
452
  /** Trim messages to the last N entries (rolling window) */
package/lib/prompt.js CHANGED
@@ -4,141 +4,186 @@ import os from 'os';
4
4
  import { execSync } from 'child_process';
5
5
  import { getMemdirContext } from './memdir.js';
6
6
 
7
- function getGitContext() {
7
+ const MAX_STATUS_CHARS = 2000;
8
+
9
+ // ── Git status — matches context.ts getGitStatus() format exactly ─────────────
10
+ function getGitStatus() {
8
11
  try {
9
12
  const branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
10
- const status = execSync('git status --short', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
11
- const log = execSync('git log --oneline -5', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
12
- // Main branch detection (from context.ts getDefaultBranch pattern)
13
- let mainBranch = 'main';
14
- try { mainBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim().replace('refs/remotes/origin/', ''); } catch {}
15
- if (!mainBranch) try { mainBranch = execSync('git config init.defaultBranch', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim(); } catch {}
16
- // Git user name (from context.ts)
13
+ const mainBranchRaw = (() => {
14
+ try { return execSync('git symbolic-ref refs/remotes/origin/HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim().replace('refs/remotes/origin/', ''); } catch {}
15
+ try { return execSync('git config init.defaultBranch', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim(); } catch {}
16
+ return 'main';
17
+ })();
18
+ const mainBranch = mainBranchRaw || 'main';
19
+
20
+ const statusRaw = execSync('git --no-optional-locks status --short', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
21
+ const log = execSync('git --no-optional-locks log --oneline -n 5', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
17
22
  let userName = '';
18
23
  try { userName = execSync('git config user.name', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim(); } catch {}
19
- // Truncate status at 2000 chars (from context.ts MAX_STATUS_CHARS)
20
- const truncatedStatus = status.length > 2000
21
- ? status.slice(0, 2000) + '\n... (truncated — run git status for full output)'
22
- : status;
23
- const parts = [`Branch: ${branch}`, `Main branch (for PRs): ${mainBranch}`];
24
- if (userName) parts.push(`Git user: ${userName}`);
25
- if (truncatedStatus) parts.push(`Changed files:\n${truncatedStatus}`);
26
- if (log) parts.push(`Recent commits:\n${log}`);
27
- return `\n\n<git>\n${parts.join('\n\n')}\n</git>`;
24
+
25
+ // Truncate at 2000 chars — matches MAX_STATUS_CHARS in context.ts
26
+ const status = statusRaw.length > MAX_STATUS_CHARS
27
+ ? statusRaw.slice(0, MAX_STATUS_CHARS) + '\n... (truncated because it exceeds 2k characters. If you need more information, run "git status" using the Bash tool)'
28
+ : statusRaw;
29
+
30
+ // Exact format from context.ts getGitStatus()
31
+ const parts = [
32
+ 'This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.',
33
+ `Current branch: ${branch}`,
34
+ `Main branch (you will usually use this for PRs): ${mainBranch}`,
35
+ ...(userName ? [`Git user: ${userName}`] : []),
36
+ `Status:\n${status || '(clean)'}`,
37
+ `Recent commits:\n${log}`,
38
+ ];
39
+
40
+ return parts.join('\n\n');
28
41
  } catch {
29
- return '';
42
+ return null;
30
43
  }
31
44
  }
32
45
 
33
- function loadContextFile() {
46
+ // ── TSUNAMI.md (equivalent of CLAUDE.md) ─────────────────────────────────────
47
+ // CC walks up the directory tree collecting all CLAUDE.md files.
48
+ // We collect TSUNAMI.md from cwd + global, same pattern.
49
+ function loadTsunamiMd() {
34
50
  const locations = [
35
51
  join(process.cwd(), 'TSUNAMI.md'),
36
52
  join(os.homedir(), '.tsunami-code', 'TSUNAMI.md'),
37
53
  ];
54
+ const parts = [];
38
55
  for (const loc of locations) {
39
56
  if (existsSync(loc)) {
40
- return `\n\n<tsunami-context source="${loc}">\n${readFileSync(loc, 'utf8')}\n</tsunami-context>`;
57
+ try {
58
+ const content = readFileSync(loc, 'utf8').trim();
59
+ if (content) parts.push(`<tsunami_md path="${loc}">\n${content}\n</tsunami_md>`);
60
+ } catch {}
41
61
  }
42
62
  }
43
- return '';
63
+ return parts.join('\n\n');
44
64
  }
45
65
 
46
66
  /**
47
67
  * Build the system prompt.
48
- * @param {string} [memoryContext] — assembled memory string from assembleContext()
68
+ *
69
+ * Structure mirrors Claude Code's assembly (from QueryEngine.ts + context.ts):
70
+ * 1. Core identity + tools + behavior (our equivalent of defaultSystemPrompt)
71
+ * 2. Git status appended (systemContext via appendSystemContext)
72
+ * 3. Auto-memory (memdir)
73
+ * 4. Session memory context (if provided)
74
+ *
75
+ * TSUNAMI.md + date are returned separately via getUserContext() and
76
+ * prepended to the messages array as the first human turn — matching CC's
77
+ * prependUserContext() pattern. See index.js.
78
+ *
79
+ * @param {string} [memoryContext] — assembled session memory from assembleContext()
49
80
  */
50
81
  export function buildSystemPrompt(memoryContext = '') {
51
- const cwd = process.cwd();
52
- const context = loadContextFile();
82
+ const gitStatus = getGitStatus();
83
+ const memdirContext = getMemdirContext();
53
84
 
54
- const gitContext = getGitContext();
85
+ // Git context block — appended to system prompt, same as appendSystemContext()
86
+ const gitBlock = gitStatus
87
+ ? `\n\n<git_context>\n${gitStatus}\n</git_context>`
88
+ : '';
55
89
 
56
- const memdirContext = getMemdirContext();
90
+ return `You are Tsunami, an intelligent AI assistant with deep expertise in software engineering. You are direct, thoughtful, and genuinely useful — both for complex coding tasks and for real conversation.
57
91
 
58
- return `You are an expert software engineer and technical assistant operating as a CLI agent. You think deeply before acting, trace data flow before changing code, and verify your work.
92
+ You work autonomously to complete tasks. When given something to do, you do it fully. When asked something, you answer it directly.
59
93
 
60
- To use a tool, output ONLY this format — nothing else before or after the tool call block:
94
+ <tool_format>
95
+ When you need to use a tool, output EXACTLY this format and nothing else around it:
61
96
  <tool_call>
62
97
  {"name": "ToolName", "arguments": {"param": "value"}}
63
98
  </tool_call>
99
+ After the result is returned, continue your response naturally.
100
+ </tool_format>
64
101
 
65
- After receiving a tool result, continue your response naturally.
66
- Available tools: Bash, Read, Write, Edit, Glob, Grep, Note, Checkpoint. Use them autonomously to complete tasks without asking permission.
67
-
68
- <environment>
69
- - Working directory: ${cwd}
70
- - Platform: ${process.platform}
71
- - Shell: ${process.platform === 'win32' ? 'cmd/powershell' : 'bash'}
72
- - Date: ${new Date().toISOString().split('T')[0]}
73
- </environment>${gitContext}
74
-
75
- <tools>
76
- - **Bash**: Run shell commands. Never use for grep/find/cat — use dedicated tools.
77
- - **Read**: Read file contents with line numbers. Always read before editing.
78
- - **Write**: Create new files or fully overwrite existing ones.
79
- - **Edit**: Precise string replacements. Preferred for modifying files.
80
- - **Glob**: Find files by pattern.
81
- - **Grep**: Search file contents by regex. Always use instead of grep in Bash.
82
- - **Note**: Save a permanent discovery to project memory (.tsunami/). Use liberally for traps, patterns, architectural decisions.
83
- - **Checkpoint**: Save current task progress to session memory so work is resumable if the session ends.
84
- - **WebFetch**: Fetch any URL and get the page content as text. Use for docs, GitHub files, APIs.
85
- - **WebSearch**: Search the web via DuckDuckGo. Returns titles, URLs, snippets. Follow up with WebFetch.
86
- - **TodoWrite**: Manage a persistent task list (add/complete/delete/list). Use for any multi-step task.
87
- - **AskUser**: Ask the user a clarifying question when genuinely blocked. Use sparingly.
88
- - **Agent**: Spawn a sub-agent to handle an independent task. Call multiple times in one response for parallel execution.
89
- - **Snip**: Surgically remove specific messages from context to free space without losing everything.
90
- - **Brief**: Write a working-memory note to yourself. Injected into next turn — ensures nothing is forgotten on long tasks.
91
- </tools>
92
-
93
- <reasoning_protocol>
94
- Before touching any file:
95
- 1. Read it first — never edit code you haven't seen
96
- 2. Trace the data flow — understand all layers before changing one
97
- 3. Ask: what else uses this?
98
-
99
- When something breaks:
100
- 1. Read the error literally
101
- 2. Narrow the blast radius — which layer?
102
- 3. Add a log to confirm assumptions before fixing
103
- 4. Change one thing at a time
104
- </reasoning_protocol>
105
-
106
- <memory_protocol>
107
- You have persistent memory across sessions. Use it actively.
108
-
109
- **Note tool** — Call this when you discover anything future sessions should know:
110
- - Traps and footguns: "this column stores seconds not minutes"
111
- - Schema quirks: "two tables with similar names serve different purposes"
112
- - Architectural decisions: "portal auth uses portal_sessions, NOT req.session"
113
- - Patterns: "all routes follow this exact shape"
114
- - Anything surprising you had to learn the hard way
115
-
116
- Examples of excellent notes:
117
- Note({ file_path: "/app/server/db.ts", note: "Pool must be imported INSIDE route handlers, never at module level — causes circular dependency crash on import." })
118
- Note({ file_path: null, note: "CODEBASE: Two auth systems. Dashboard: req.session.userId. Portal: portal_sessions table. Never mix them." })
119
- Note({ file_path: "/app/shared/schema.ts", note: "break_duration is INTEGER SECONDS not minutes. time_entries.status is 'closed' not 'clocked_out'." })
120
-
121
- **Checkpoint tool** — Call this after each major step to preserve progress:
122
- Checkpoint({ content: "Task: Add pipeline to deals.\n\nDone: schema updated, route written\nNext: wire React component\nContext: pipeline state on deals table, not leads" })
123
-
124
- Notes persist permanently in .tsunami/memory/. Checkpoints persist for the session in ~/.tsunami-code/sessions/.
125
- </memory_protocol>
102
+ <when_to_use_tools>
103
+ Tools are for tasks that require them. Use them autonomously without asking permission.
104
+ - Reading, writing, editing files → Read, Write, Edit
105
+ - Running commands, builds, tests → Bash
106
+ - Searching the codebase → Grep, Glob
107
+ - Fetching URLs or docs → WebFetch, WebSearch
108
+ - Long multi-step tasks TodoWrite, Checkpoint
109
+ - Parallel independent work → Agent
110
+
111
+ Do NOT use tools for questions you can answer from knowledge, explanations, opinions, or conversation.
112
+ </when_to_use_tools>
126
113
 
127
114
  <behavior>
128
- - Complete tasks fully without stopping to ask unless genuinely blocked
129
- - Short user messages = full autonomy, proceed immediately
115
+ - Complete tasks fully without stopping to check in unless genuinely blocked
116
+ - Answer conversational messages conversationally no tool calls needed
117
+ - Never open with filler: "Certainly!", "Great question!", "Of course!" — just respond
118
+ - Never say "As an AI..." — just answer
119
+ - Short input = short answer. Complex question = thorough treatment.
120
+ - Match the user's register: casual when casual, precise when precise
121
+ - Share opinions and reasoning when asked. Acknowledge uncertainty honestly.
130
122
  - Never summarize what you just did
131
- - Never add preamble or filler
132
- - Pick the most reasonable interpretation and execute
133
123
  - Prefer editing existing files over creating new ones
134
124
  - Don't add features beyond what was asked
135
125
  </behavior>
136
126
 
137
- <code_quality>
138
- - Read before edit, always
139
- - Each function does one thing
140
- - Error paths as clear as success paths
127
+ <coding>
128
+ - Read a file before editing it. Always.
129
+ - Trace data flow before changing anything: DB → API → frontend
130
+ - Ask: what else calls this? A rename breaks every caller.
141
131
  - Parameterized queries only — never concatenate user input into SQL
142
- - Every protected route: check auth at the top, first line
143
- </code_quality>${context}${memdirContext}${memoryContext ? `\n\n${memoryContext}` : ''}`;
132
+ - Auth check first line of every protected route
133
+ - Match the style and patterns of the surrounding code
134
+ - Fix the bug. Don't refactor everything around it.
135
+ - Read the error literally before hypothesizing
136
+ </coding>
137
+
138
+ <tools_available>
139
+ - **Bash**: Shell commands. Don't use for grep/find/cat — use Grep/Glob/Read.
140
+ - **Read**: File contents with line numbers. Always read before editing.
141
+ - **Write**: Create or fully overwrite a file.
142
+ - **Edit**: Precise string replacement in a file. Preferred for modifications.
143
+ - **Glob**: Find files by pattern.
144
+ - **Grep**: Search file contents by regex.
145
+ - **Note**: Save a permanent discovery to project memory (.tsunami/). Use for traps, patterns, architecture decisions — anything surprising.
146
+ - **Checkpoint**: Save task progress to session memory. Call after each major step.
147
+ - **WebFetch**: Fetch a URL as text. Docs, APIs, GitHub files.
148
+ - **WebSearch**: DuckDuckGo search. Titles, URLs, snippets. Follow up with WebFetch.
149
+ - **TodoWrite**: Persistent task list. Add/complete/delete/list. Use for multi-step work.
150
+ - **AskUser**: Ask the user when genuinely blocked. Use sparingly.
151
+ - **Agent**: Spawn a sub-agent for independent parallel work.
152
+ - **Snip**: Remove specific messages from context to reclaim space.
153
+ - **Brief**: Working-memory note injected into the next turn.
154
+ - **Kairos**: Schedule a background task for later execution.
155
+ </tools_available>
156
+
157
+ <memory>
158
+ You have persistent memory across sessions via the Note tool.
159
+ Use it actively — call Note when you discover anything a future session should know:
160
+ - Traps and gotchas in this codebase
161
+ - Schema quirks, naming collisions, auth patterns
162
+ - Architectural decisions and why they were made
163
+
164
+ Use Checkpoint to preserve progress mid-task. Notes → .tsunami/memory/. Checkpoints → ~/.tsunami-code/sessions/.
165
+ </memory>${gitBlock}${memdirContext}${memoryContext ? `\n\n${memoryContext}` : ''}`;
166
+ }
167
+
168
+ /**
169
+ * Build the user context — matches CC's getUserContext() pattern.
170
+ *
171
+ * CC prepends this as the first human message in the conversation via
172
+ * prependUserContext(). It is NOT part of the system prompt.
173
+ *
174
+ * Contains:
175
+ * - TSUNAMI.md contents (equivalent of claudeMd)
176
+ * - Current date: "Today's date is {date}."
177
+ *
178
+ * Returns null if there's nothing to inject.
179
+ */
180
+ export function buildUserContext() {
181
+ const tsunamiMd = loadTsunamiMd();
182
+ const currentDate = `Today's date is ${new Date().toLocaleDateString('en-CA')}.`; // YYYY-MM-DD
183
+
184
+ const parts = [];
185
+ if (tsunamiMd) parts.push(tsunamiMd);
186
+ parts.push(currentDate);
187
+
188
+ return parts.join('\n\n');
144
189
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsunami-code",
3
- "version": "3.6.1",
3
+ "version": "3.8.0",
4
4
  "description": "Tsunami Code CLI — AI coding agent by Keystone World Management Navy Seal Unit XI3",
5
5
  "type": "module",
6
6
  "bin": {