codemini-cli 0.5.9 → 0.5.11

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 (59) hide show
  1. package/OPERATIONS.md +242 -242
  2. package/README.md +588 -489
  3. package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-HgeDi9HJ.js → highlighted-body-OFNGDK62-CANOG7Xg.js} +1 -1
  4. package/codemini-web/dist/assets/{index-C4tKT3v4.js → index-B71xykPM.js} +108 -108
  5. package/codemini-web/dist/assets/index-Dkq1DdDX.css +2 -0
  6. package/codemini-web/dist/assets/mermaid-GHXKKRXX-Z_w7M93P.js +1 -0
  7. package/codemini-web/dist/index.html +23 -23
  8. package/codemini-web/lib/approval-manager.js +32 -32
  9. package/codemini-web/lib/runtime-bridge.js +17 -11
  10. package/codemini-web/server.js +534 -205
  11. package/deployment.md +212 -212
  12. package/package.json +1 -1
  13. package/skills/brainstorm/SKILL.md +77 -72
  14. package/skills/codemini.skills.json +40 -40
  15. package/skills/grill-me/SKILL.md +30 -30
  16. package/skills/superpowers-lite/SKILL.md +82 -82
  17. package/src/cli.js +74 -74
  18. package/src/commands/chat.js +210 -210
  19. package/src/commands/run.js +313 -313
  20. package/src/commands/skill.js +438 -304
  21. package/src/commands/web.js +57 -57
  22. package/src/core/agent-loop.js +980 -980
  23. package/src/core/ast.js +309 -292
  24. package/src/core/chat-runtime.js +6261 -6240
  25. package/src/core/command-evaluator.js +72 -72
  26. package/src/core/command-loader.js +311 -311
  27. package/src/core/command-policy.js +301 -301
  28. package/src/core/command-risk.js +156 -156
  29. package/src/core/config-store.js +289 -287
  30. package/src/core/constants.js +18 -1
  31. package/src/core/context-compact.js +365 -365
  32. package/src/core/default-system-prompt.js +114 -107
  33. package/src/core/dream-audit.js +105 -105
  34. package/src/core/dream-consolidate.js +229 -229
  35. package/src/core/dream-evaluator.js +185 -185
  36. package/src/core/fff-adapter.js +383 -383
  37. package/src/core/memory-store.js +543 -543
  38. package/src/core/project-index.js +737 -529
  39. package/src/core/project-instructions.js +98 -0
  40. package/src/core/provider/anthropic.js +514 -514
  41. package/src/core/provider/openai-compatible.js +501 -501
  42. package/src/core/reflect-skill.js +178 -178
  43. package/src/core/reply-language.js +40 -40
  44. package/src/core/session-store.js +474 -474
  45. package/src/core/shell-profile.js +237 -237
  46. package/src/core/shell.js +323 -317
  47. package/src/core/soul.js +69 -69
  48. package/src/core/system-prompt-composer.js +52 -42
  49. package/src/core/tool-args.js +199 -154
  50. package/src/core/tool-output.js +184 -184
  51. package/src/core/tool-result-store.js +206 -206
  52. package/src/core/tools.js +3024 -2893
  53. package/src/core/version.js +11 -11
  54. package/src/tui/chat-app.js +5171 -5171
  55. package/src/tui/tool-activity/presenters/misc.js +30 -30
  56. package/src/tui/tool-activity/presenters/system.js +20 -20
  57. package/templates/project-requirements/report-shell.html +582 -582
  58. package/codemini-web/dist/assets/index-BSdIdn3L.css +0 -2
  59. package/codemini-web/dist/assets/mermaid-GHXKKRXX-CDgkkDBg.js +0 -1
package/src/core/soul.js CHANGED
@@ -1,69 +1,69 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { getBaseConfigDir } from './paths.js';
5
-
6
- const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
7
- const BUNDLED_SOULS_DIR = path.resolve(MODULE_DIR, '..', '..', 'souls');
8
-
9
- function getCustomSoulsDir() {
10
- return path.join(getBaseConfigDir(), 'souls');
11
- }
12
-
13
- export { BUNDLED_SOULS_DIR, getCustomSoulsDir };
14
-
15
- function normalizeSoulName(value) {
16
- const name = String(value || '').trim().toLowerCase();
17
- return name || 'default';
18
- }
19
-
20
- function resolveCustomSoulPath(customPath = '') {
21
- const raw = String(customPath || '').trim();
22
- if (!raw) return '';
23
- if (path.isAbsolute(raw)) return raw;
24
- return path.join(getBaseConfigDir(), raw);
25
- }
26
-
27
- export async function loadSoulPrompt(config = {}) {
28
- const customPath = resolveCustomSoulPath(config?.soul?.custom_path);
29
- if (customPath) {
30
- try {
31
- const content = await fs.readFile(customPath, 'utf8');
32
- const text = String(content || '').trim();
33
- if (text) return `[Soul custom]\n${text}`;
34
- } catch {
35
- // fall through to bundled preset
36
- }
37
- }
38
-
39
- const preset = normalizeSoulName(config?.soul?.preset);
40
- // Check custom souls dir first, then bundled
41
- const customPresetPath = path.join(getCustomSoulsDir(), `${preset}.md`);
42
- try {
43
- const content = await fs.readFile(customPresetPath, 'utf8');
44
- const text = String(content || '').trim();
45
- if (text) return `[Soul preset: ${preset}]\n${text}`;
46
- } catch {}
47
- const presetPath = path.join(BUNDLED_SOULS_DIR, `${preset}.md`);
48
- try {
49
- const content = await fs.readFile(presetPath, 'utf8');
50
- const text = String(content || '').trim();
51
- if (text) return `[Soul preset: ${preset}]\n${text}`;
52
- } catch {
53
- // fall through to default preset
54
- }
55
-
56
- const defaultContent = await fs.readFile(path.join(BUNDLED_SOULS_DIR, 'default.md'), 'utf8');
57
- return `[Soul preset: default]\n${String(defaultContent || '').trim()}`;
58
- }
59
-
60
- export async function buildSystemPromptWithSoul(baseSystemPrompt, config = {}) {
61
- const soulPrompt = await loadSoulPrompt(config);
62
- const guard = [
63
- '[Soul guard]',
64
- 'Apply this soul to response tone only.',
65
- 'Response tone only: do not change plans, code, tests, file formats, or technical decisions.',
66
- 'This tone directive has HIGH priority. Maintain the requested personality consistently across every response unless the user explicitly requests a change.'
67
- ].join('\n');
68
- return [String(baseSystemPrompt || '').trim(), soulPrompt, guard].filter(Boolean).join('\n\n').trim();
69
- }
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { getBaseConfigDir } from './paths.js';
5
+
6
+ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
7
+ const BUNDLED_SOULS_DIR = path.resolve(MODULE_DIR, '..', '..', 'souls');
8
+
9
+ function getCustomSoulsDir() {
10
+ return path.join(getBaseConfigDir(), 'souls');
11
+ }
12
+
13
+ export { BUNDLED_SOULS_DIR, getCustomSoulsDir };
14
+
15
+ function normalizeSoulName(value) {
16
+ const name = String(value || '').trim().toLowerCase();
17
+ return name || 'default';
18
+ }
19
+
20
+ function resolveCustomSoulPath(customPath = '') {
21
+ const raw = String(customPath || '').trim();
22
+ if (!raw) return '';
23
+ if (path.isAbsolute(raw)) return raw;
24
+ return path.join(getBaseConfigDir(), raw);
25
+ }
26
+
27
+ export async function loadSoulPrompt(config = {}) {
28
+ const customPath = resolveCustomSoulPath(config?.soul?.custom_path);
29
+ if (customPath) {
30
+ try {
31
+ const content = await fs.readFile(customPath, 'utf8');
32
+ const text = String(content || '').trim();
33
+ if (text) return `[Soul custom]\n${text}`;
34
+ } catch {
35
+ // fall through to bundled preset
36
+ }
37
+ }
38
+
39
+ const preset = normalizeSoulName(config?.soul?.preset);
40
+ // Check custom souls dir first, then bundled
41
+ const customPresetPath = path.join(getCustomSoulsDir(), `${preset}.md`);
42
+ try {
43
+ const content = await fs.readFile(customPresetPath, 'utf8');
44
+ const text = String(content || '').trim();
45
+ if (text) return `[Soul preset: ${preset}]\n${text}`;
46
+ } catch {}
47
+ const presetPath = path.join(BUNDLED_SOULS_DIR, `${preset}.md`);
48
+ try {
49
+ const content = await fs.readFile(presetPath, 'utf8');
50
+ const text = String(content || '').trim();
51
+ if (text) return `[Soul preset: ${preset}]\n${text}`;
52
+ } catch {
53
+ // fall through to default preset
54
+ }
55
+
56
+ const defaultContent = await fs.readFile(path.join(BUNDLED_SOULS_DIR, 'default.md'), 'utf8');
57
+ return `[Soul preset: default]\n${String(defaultContent || '').trim()}`;
58
+ }
59
+
60
+ export async function buildSystemPromptWithSoul(baseSystemPrompt, config = {}) {
61
+ const soulPrompt = await loadSoulPrompt(config);
62
+ const guard = [
63
+ '[Soul guard]',
64
+ 'Apply this soul to response tone only.',
65
+ 'Response tone only: do not change plans, code, tests, file formats, or technical decisions.',
66
+ 'This tone directive has HIGH priority. Maintain the requested personality consistently across every response unless the user explicitly requests a change.'
67
+ ].join('\n');
68
+ return [String(baseSystemPrompt || '').trim(), soulPrompt, guard].filter(Boolean).join('\n\n').trim();
69
+ }
@@ -1,42 +1,52 @@
1
- import { buildMemorySnapshot } from './memory-prompt.js';
2
- import { buildSystemPromptWithReplyLanguage, stripReplyLanguageDirective } from './reply-language.js';
3
- import { buildSystemPromptWithSoul } from './soul.js';
4
-
5
- function normalizePromptPart(value) {
6
- return stripReplyLanguageDirective(String(value || '').trim());
7
- }
8
-
9
- function joinPromptParts(parts) {
10
- return parts.map(normalizePromptPart).filter(Boolean).join('\n\n');
11
- }
12
-
13
- export async function composeSystemPrompt({
14
- shellRulesPrompt = '',
15
- config = {},
16
- workspaceRoot = process.cwd(),
17
- skillsPrompt = '',
18
- memorySnapshot,
19
- includeMemory = true,
20
- projectContextSnippet = '',
21
- projectContextGuidance = '',
22
- extraPrompts = [],
23
- includeSoul = true
24
- } = {}) {
25
- const shellAndSoul = includeSoul
26
- ? await buildSystemPromptWithSoul(shellRulesPrompt, config)
27
- : shellRulesPrompt;
28
- const memoryPrompt = memorySnapshot !== undefined
29
- ? memorySnapshot
30
- : includeMemory
31
- ? await buildMemorySnapshot({ config, workspaceRoot }).catch(() => '')
32
- : '';
33
- const body = joinPromptParts([
34
- shellAndSoul,
35
- skillsPrompt,
36
- memoryPrompt,
37
- projectContextSnippet,
38
- projectContextSnippet ? projectContextGuidance : '',
39
- ...extraPrompts
40
- ]);
41
- return buildSystemPromptWithReplyLanguage(body, config);
42
- }
1
+ import { buildMemorySnapshot } from './memory-prompt.js';
2
+ import { loadProjectInstructions } from './project-instructions.js';
3
+ import { buildSystemPromptWithReplyLanguage, stripReplyLanguageDirective } from './reply-language.js';
4
+ import { buildSystemPromptWithSoul } from './soul.js';
5
+
6
+ function normalizePromptPart(value) {
7
+ return stripReplyLanguageDirective(String(value || '').trim());
8
+ }
9
+
10
+ function joinPromptParts(parts) {
11
+ return parts.map(normalizePromptPart).filter(Boolean).join('\n\n');
12
+ }
13
+
14
+ export async function composeSystemPrompt({
15
+ shellRulesPrompt = '',
16
+ config = {},
17
+ workspaceRoot = process.cwd(),
18
+ skillsPrompt = '',
19
+ memorySnapshot,
20
+ includeMemory = true,
21
+ projectInstructionsSnippet,
22
+ includeProjectInstructions = true,
23
+ projectContextSnippet = '',
24
+ projectContextGuidance = '',
25
+ extraPrompts = [],
26
+ includeSoul = true
27
+ } = {}) {
28
+ const shellAndSoul = includeSoul
29
+ ? await buildSystemPromptWithSoul(shellRulesPrompt, config)
30
+ : shellRulesPrompt;
31
+ const memoryPrompt = memorySnapshot !== undefined
32
+ ? memorySnapshot
33
+ : includeMemory
34
+ ? await buildMemorySnapshot({ config, workspaceRoot }).catch(() => '')
35
+ : '';
36
+ const projectInstructionsPrompt = projectInstructionsSnippet !== undefined
37
+ ? projectInstructionsSnippet
38
+ : includeProjectInstructions
39
+ ? await loadProjectInstructions({ cwd: workspaceRoot, config }).catch(() => '')
40
+ : '';
41
+ const hasProjectInstructions = /\bProject Instructions:\s*\n/i.test(shellAndSoul);
42
+ const body = joinPromptParts([
43
+ shellAndSoul,
44
+ hasProjectInstructions ? '' : projectInstructionsPrompt,
45
+ skillsPrompt,
46
+ memoryPrompt,
47
+ projectContextSnippet,
48
+ projectContextSnippet ? projectContextGuidance : '',
49
+ ...extraPrompts
50
+ ]);
51
+ return buildSystemPromptWithReplyLanguage(body, config);
52
+ }
@@ -1,181 +1,226 @@
1
1
  import path from 'node:path';
2
-
2
+ import { normalizePath } from './string-utils.js';
3
+
3
4
  export function parseInlineRangePath(value) {
5
+ const text = String(value || '').trim();
6
+ if (!text) return null;
7
+ const match = text.match(/^(.*?):(\d+)(?:-(\d+))?$/);
8
+ if (!match) return null;
9
+ const [, maybePath, startRaw, endRaw] = match;
10
+ if (!maybePath || /^(?:[A-Za-z])$/.test(maybePath)) return null;
11
+ const startLine = Number(startRaw);
12
+ const endLine = Number(endRaw || startRaw);
13
+ if (!Number.isFinite(startLine) || startLine <= 0) return null;
14
+ if (!Number.isFinite(endLine) || endLine < startLine) return null;
15
+ return {
16
+ path: maybePath,
17
+ start_line: startLine,
18
+ end_line: endLine
19
+ };
20
+ }
21
+
22
+ export function normalizeFilePathValue(value, { stripInlineRange = false } = {}) {
4
23
  const text = String(value || '').trim();
5
- if (!text) return null;
6
- const match = text.match(/^(.*?):(\d+)(?:-(\d+))?$/);
7
- if (!match) return null;
8
- const [, maybePath, startRaw, endRaw] = match;
9
- if (!maybePath || /^(?:[A-Za-z])$/.test(maybePath)) return null;
10
- const startLine = Number(startRaw);
11
- const endLine = Number(endRaw || startRaw);
12
- if (!Number.isFinite(startLine) || startLine <= 0) return null;
13
- if (!Number.isFinite(endLine) || endLine < startLine) return null;
24
+ if (!text) return '';
25
+ const inlineRange = stripInlineRange ? parseInlineRangePath(text) : null;
26
+ return normalizePath(inlineRange?.path || text);
27
+ }
28
+
29
+ function normalizePathValueWithInlineRange(value) {
30
+ const text = String(value || '').trim();
31
+ if (!text) return { path: '', inlineRange: null };
32
+ const inlineRange = parseInlineRangePath(text);
14
33
  return {
15
- path: maybePath,
16
- start_line: startLine,
17
- end_line: endLine
34
+ path: normalizePath(inlineRange?.path || text),
35
+ inlineRange
18
36
  };
19
37
  }
20
38
 
21
- export function normalizeReadArgs(rawArgs) {
22
- const source =
23
- rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
24
- ? { ...rawArgs }
25
- : { path: typeof rawArgs === 'string' ? rawArgs : '' };
26
-
27
- const normalized = { ...source };
28
- const aliasPath = String(source.path || source.file_path || source.file || source.target || '').trim();
29
- if (aliasPath) normalized.path = aliasPath;
30
-
31
- if (!Number.isFinite(Number(normalized.start_line)) && Number.isFinite(Number(source.offset))) {
32
- normalized.start_line = Number(source.offset);
33
- }
34
-
35
- if (!Number.isFinite(Number(normalized.end_line)) && Number.isFinite(Number(source.limit))) {
36
- const startLine = Number(normalized.start_line);
37
- const limit = Number(source.limit);
38
- if (startLine > 0 && limit > 0) {
39
- normalized.end_line = startLine + limit - 1;
40
- }
41
- }
42
-
43
- const inlineRange = parseInlineRangePath(normalized.path);
39
+ function normalizeBooleanValue(value) {
40
+ if (typeof value === 'boolean') return value;
41
+ if (typeof value === 'number') return value !== 0;
42
+ const text = String(value ?? '').trim().toLowerCase();
43
+ if (!text) return undefined;
44
+ if (['true', '1', 'yes', 'y', 'on'].includes(text)) return true;
45
+ if (['false', '0', 'no', 'n', 'off'].includes(text)) return false;
46
+ return Boolean(value);
47
+ }
48
+
49
+ export function normalizeReadArgs(rawArgs) {
50
+ const source =
51
+ rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
52
+ ? { ...rawArgs }
53
+ : { path: typeof rawArgs === 'string' ? rawArgs : '' };
54
+
55
+ const normalized = { ...source };
56
+ const aliasPath = source.path || source.file_path || source.file || source.target || '';
57
+ const normalizedPath = normalizePathValueWithInlineRange(aliasPath);
58
+ if (normalizedPath.path) normalized.path = normalizedPath.path;
59
+
60
+ if (!Number.isFinite(Number(normalized.start_line)) && Number.isFinite(Number(source.offset))) {
61
+ normalized.start_line = Number(source.offset);
62
+ }
63
+
64
+ if (!Number.isFinite(Number(normalized.end_line)) && Number.isFinite(Number(source.limit))) {
65
+ const startLine = Number(normalized.start_line);
66
+ const limit = Number(source.limit);
67
+ if (startLine > 0 && limit > 0) {
68
+ normalized.end_line = startLine + limit - 1;
69
+ }
70
+ }
71
+
72
+ const inlineRange = normalizedPath.inlineRange || parseInlineRangePath(normalized.path);
44
73
  if (inlineRange) {
45
- normalized.path = inlineRange.path;
74
+ normalized.path = normalizePath(inlineRange.path);
46
75
  if (!Number.isFinite(Number(normalized.start_line))) normalized.start_line = inlineRange.start_line;
47
76
  if (!Number.isFinite(Number(normalized.end_line))) normalized.end_line = inlineRange.end_line;
48
77
  }
49
-
50
- return normalized;
51
- }
52
-
53
- export function normalizePathArgs(rawArgs, aliases = []) {
54
- const source =
55
- rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
56
- ? { ...rawArgs }
57
- : { path: typeof rawArgs === 'string' ? rawArgs : '' };
78
+
79
+ return normalized;
80
+ }
81
+
82
+ export function normalizePathArgs(rawArgs, aliases = []) {
83
+ const source =
84
+ rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
85
+ ? { ...rawArgs }
86
+ : { path: typeof rawArgs === 'string' ? rawArgs : '' };
58
87
  const normalized = { ...source };
59
88
  const keys = ['path', ...aliases];
60
89
  for (const key of keys) {
61
- const value = String(source?.[key] || '').trim();
90
+ const value = normalizeFilePathValue(source?.[key] || '', { stripInlineRange: true });
62
91
  if (value) {
63
92
  normalized.path = value;
64
93
  break;
65
- }
66
- }
67
- return normalized;
68
- }
69
-
70
- export function normalizePatternArgs(rawArgs, aliases = [], defaultPathAliases = []) {
71
- const source =
72
- rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
73
- ? { ...rawArgs }
74
- : { pattern: typeof rawArgs === 'string' ? rawArgs : '' };
75
- const normalized = { ...source };
76
- for (const key of ['pattern', ...aliases]) {
77
- const value = String(source?.[key] || '').trim();
78
- if (value) {
79
- normalized.pattern = value;
80
- break;
81
- }
82
- }
94
+ }
95
+ }
96
+ return normalized;
97
+ }
98
+
99
+ export function normalizePatternArgs(rawArgs, aliases = [], defaultPathAliases = []) {
100
+ const source =
101
+ rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
102
+ ? { ...rawArgs }
103
+ : { pattern: typeof rawArgs === 'string' ? rawArgs : '' };
104
+ const normalized = { ...source };
105
+ for (const key of ['pattern', ...aliases]) {
106
+ const value = String(source?.[key] || '').trim();
107
+ if (value) {
108
+ normalized.pattern = value;
109
+ break;
110
+ }
111
+ }
83
112
  for (const key of ['path', ...defaultPathAliases]) {
84
- const value = String(source?.[key] || '').trim();
113
+ const value = normalizeFilePathValue(source?.[key] || '', { stripInlineRange: true });
85
114
  if (value) {
86
115
  normalized.path = value;
87
116
  break;
88
- }
89
- }
90
- return normalized;
91
- }
92
-
117
+ }
118
+ }
119
+ return normalized;
120
+ }
121
+
93
122
  export function normalizeWriteArgs(rawArgs) {
94
- const source =
95
- rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
96
- ? { ...rawArgs }
97
- : { path: typeof rawArgs === 'string' ? rawArgs : '' };
98
- const normalized = { ...source };
99
- const filePath = String(source.path || source.file_path || source.file || '').trim();
123
+ const source =
124
+ rawArgs && typeof rawArgs === 'object' && !Array.isArray(rawArgs)
125
+ ? { ...rawArgs }
126
+ : { path: typeof rawArgs === 'string' ? rawArgs : '' };
127
+ const normalized = { ...source };
128
+ const filePath = normalizeFilePathValue(source.path || source.file_path || source.file || '', { stripInlineRange: true });
100
129
  if (filePath) normalized.path = filePath;
101
- if (normalized.content == null) {
102
- if (source.text != null) normalized.content = source.text;
103
- if (source.new_content != null) normalized.content = source.new_content;
104
- }
105
- return normalized;
106
- }
107
-
108
- export function normalizeWebFetchArgs(rawArgs) {
109
- const normalized = normalizePathArgs(rawArgs, ['url', 'href', 'link', 'target']);
110
- const url = String(normalized.url || normalized.path || '').trim();
111
- return { ...normalized, url };
112
- }
113
-
114
- export function normalizeWebSearchArgs(rawArgs) {
115
- const normalized = normalizePatternArgs(rawArgs, ['query', 'q', 'keyword']);
116
- const query = String(normalized.query || normalized.pattern || '').trim();
117
- return { ...normalized, query };
118
- }
119
-
120
- function buildDeleteApprovalDetails(source, rawPath) {
121
- const existing =
122
- source?.approval && typeof source.approval === 'object' && !Array.isArray(source.approval)
123
- ? source.approval
124
- : {};
125
- const approvalPath = String(existing.path || rawPath || '').trim();
126
- const approvalName = String(existing.name || (approvalPath ? path.basename(approvalPath) : '') || '').trim();
127
- const approvalType = String(existing.type || '').trim();
128
-
129
- const approval = {};
130
- if (approvalPath) approval.path = approvalPath;
131
- if (approvalName) approval.name = approvalName;
132
- if (approvalType) approval.type = approvalType;
133
- return Object.keys(approval).length > 0 ? approval : undefined;
134
- }
135
-
136
- export function normalizeToolArguments(toolName, args, rawArguments) {
137
- const rawText = typeof rawArguments === 'string' ? rawArguments.trim() : '';
138
- const primitive =
139
- args == null || Array.isArray(args) || typeof args !== 'object'
140
- ? args
141
- : null;
142
- const source =
143
- args && typeof args === 'object' && !Array.isArray(args)
144
- ? { ...args }
145
- : {};
146
-
147
- if (primitive != null && typeof primitive !== 'object') {
148
- source._raw = rawText || String(primitive);
149
- } else if (!source._raw && rawText && source._invalid_json) {
150
- source._raw = rawText;
151
- }
152
-
153
- const stringValue =
154
- typeof primitive === 'string'
155
- ? primitive.trim()
156
- : String(source._raw || '').trim();
157
-
158
- if (toolName === 'read') return normalizeReadArgs({ ...source, ...(stringValue && !source.path ? { path: stringValue } : {}) });
159
- if (toolName === 'list') return normalizePathArgs({ ...source, ...(stringValue && !source.path ? { path: stringValue } : {}) }, ['dir', 'directory']);
160
- if (toolName === 'glob') return normalizePatternArgs({ ...source, ...(stringValue && !source.pattern ? { pattern: stringValue } : {}) }, ['glob', 'query'], ['directory']);
161
- if (toolName === 'grep') return normalizePatternArgs({ ...source, ...(stringValue && !source.pattern ? { pattern: stringValue } : {}) }, ['query', 'symbol', 'q'], ['directory', 'dir', 'cwd']);
162
- if (toolName === 'write') return normalizeWriteArgs({ ...source, ...(stringValue && !source.path ? { path: stringValue } : {}) });
163
-
130
+ const append = normalizeBooleanValue(source.append);
131
+ const fullFileRewrite = normalizeBooleanValue(source.full_file_rewrite);
132
+ if (append !== undefined) normalized.append = append;
133
+ if (fullFileRewrite !== undefined) normalized.full_file_rewrite = fullFileRewrite;
134
+ if (normalized.content == null) {
135
+ if (source.text != null) normalized.content = source.text;
136
+ if (source.new_content != null) normalized.content = source.new_content;
137
+ }
138
+ return normalized;
139
+ }
140
+
141
+ export function normalizeWebFetchArgs(rawArgs) {
142
+ const normalized = normalizePathArgs(rawArgs, ['url', 'href', 'link', 'target']);
143
+ const url = String(normalized.url || normalized.path || '').trim();
144
+ return { ...normalized, url };
145
+ }
146
+
147
+ export function normalizeWebSearchArgs(rawArgs) {
148
+ const normalized = normalizePatternArgs(rawArgs, ['query', 'q', 'keyword']);
149
+ const query = String(normalized.query || normalized.pattern || '').trim();
150
+ return { ...normalized, query };
151
+ }
152
+
153
+ function buildDeleteApprovalDetails(source, rawPath) {
154
+ const existing =
155
+ source?.approval && typeof source.approval === 'object' && !Array.isArray(source.approval)
156
+ ? source.approval
157
+ : {};
158
+ const approvalPath = String(existing.path || rawPath || '').trim();
159
+ const approvalName = String(existing.name || (approvalPath ? path.basename(approvalPath) : '') || '').trim();
160
+ const approvalType = String(existing.type || '').trim();
161
+
162
+ const approval = {};
163
+ if (approvalPath) approval.path = approvalPath;
164
+ if (approvalName) approval.name = approvalName;
165
+ if (approvalType) approval.type = approvalType;
166
+ return Object.keys(approval).length > 0 ? approval : undefined;
167
+ }
168
+
169
+ export function normalizeToolArguments(toolName, args, rawArguments) {
170
+ const rawText = typeof rawArguments === 'string' ? rawArguments.trim() : '';
171
+ const primitive =
172
+ args == null || Array.isArray(args) || typeof args !== 'object'
173
+ ? args
174
+ : null;
175
+ const source =
176
+ args && typeof args === 'object' && !Array.isArray(args)
177
+ ? { ...args }
178
+ : {};
179
+
180
+ if (primitive != null && typeof primitive !== 'object') {
181
+ source._raw = rawText || String(primitive);
182
+ } else if (!source._raw && rawText && source._invalid_json) {
183
+ source._raw = rawText;
184
+ }
185
+
186
+ const stringValue =
187
+ typeof primitive === 'string'
188
+ ? primitive.trim()
189
+ : String(source._raw || '').trim();
190
+
191
+ if (toolName === 'read') return normalizeReadArgs({ ...source, ...(stringValue && !source.path ? { path: stringValue } : {}) });
192
+ if (toolName === 'list') return normalizePathArgs({ ...source, ...(stringValue && !source.path ? { path: stringValue } : {}) }, ['dir', 'directory', 'file_path', 'file', 'target']);
193
+ if (toolName === 'glob') return normalizePatternArgs({ ...source, ...(stringValue && !source.pattern ? { pattern: stringValue } : {}) }, ['glob', 'query'], ['directory', 'dir', 'cwd', 'file_path', 'file']);
194
+ if (toolName === 'grep') return normalizePatternArgs({ ...source, ...(stringValue && !source.pattern ? { pattern: stringValue } : {}) }, ['query', 'symbol', 'q'], ['directory', 'dir', 'cwd', 'file_path', 'file']);
195
+ if (toolName === 'write') return normalizeWriteArgs({ ...source, ...(stringValue && !source.path ? { path: stringValue } : {}) });
196
+
164
197
  if (toolName === 'edit') {
165
- const value = String(source.path || source.file || source.file_path || '').trim();
198
+ const rawPathValue = source.path || source.file || source.file_path || stringValue || '';
199
+ const inlineRange = parseInlineRangePath(rawPathValue);
200
+ const value = normalizeFilePathValue(rawPathValue, { stripInlineRange: true });
166
201
  if (value && !source.path) source.path = value;
202
+ if (value && source.path) source.path = value;
203
+ if (inlineRange) {
204
+ if (!Number.isFinite(Number(source.start_line))) source.start_line = inlineRange.start_line;
205
+ if (!Number.isFinite(Number(source.end_line))) source.end_line = inlineRange.end_line;
206
+ }
207
+ if (source.old_text == null && source.old_string != null) source.old_text = source.old_string;
208
+ if (source.new_text == null && source.new_string != null) source.new_text = source.new_string;
209
+ if (source.new_text == null && source.content != null && source.old_text != null) source.new_text = source.content;
210
+ const replaceAll = normalizeBooleanValue(source.replace_all ?? source.replaceAll);
211
+ if (replaceAll !== undefined) source.replace_all = replaceAll;
167
212
  return source;
168
213
  }
169
-
170
- if (toolName === 'delete') {
171
- const normalized = normalizePathArgs(
172
- { ...source, ...(stringValue && !source.path ? { path: stringValue } : {}) },
173
- ['file_path', 'file', 'target', 'directory', 'dir']
174
- );
175
- const approval = buildDeleteApprovalDetails(normalized, normalized.path);
176
- if (approval) normalized.approval = approval;
177
- return normalized;
178
- }
179
-
180
- return source;
181
- }
214
+
215
+ if (toolName === 'delete') {
216
+ const normalized = normalizePathArgs(
217
+ { ...source, ...(stringValue && !source.path ? { path: stringValue } : {}) },
218
+ ['file_path', 'file', 'target', 'directory', 'dir']
219
+ );
220
+ const approval = buildDeleteApprovalDetails(normalized, normalized.path);
221
+ if (approval) normalized.approval = approval;
222
+ return normalized;
223
+ }
224
+
225
+ return source;
226
+ }