@geminilight/mindos 0.6.34 → 0.6.36

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 (172) hide show
  1. package/.env.local.example +2 -2
  2. package/_standalone/.mindos-build-version +1 -1
  3. package/_standalone/.next/BUILD_ID +1 -1
  4. package/_standalone/.next/app-path-routes-manifest.json +15 -15
  5. package/_standalone/.next/build-manifest.json +2 -2
  6. package/_standalone/.next/cache/.previewinfo +1 -1
  7. package/_standalone/.next/cache/.rscinfo +1 -1
  8. package/_standalone/.next/cache/config.json +3 -3
  9. package/_standalone/.next/prerender-manifest.json +3 -3
  10. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  11. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/_standalone/.next/server/app/_global-error.html +2 -2
  13. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  21. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  25. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  26. package/_standalone/.next/server/app/agents/page.js +1 -1
  27. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  28. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  29. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
  35. package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
  36. package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
  37. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  38. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  39. package/_standalone/.next/server/app/api/ask/route.js +5 -5
  40. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  41. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  42. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/bootstrap/route.js +1 -1
  45. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  46. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  47. package/_standalone/.next/server/app/api/export/route.js +1 -1
  48. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  49. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  50. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  51. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  52. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  53. package/_standalone/.next/server/app/api/git/route.js +1 -1
  54. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  55. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  56. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  57. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  58. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  59. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  60. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  61. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  62. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  63. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/recent-files/route.js +1 -1
  65. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  67. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  69. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  71. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  73. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  74. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  75. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  76. package/_standalone/.next/server/app/api/setup/route.js +1 -1
  77. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  78. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  79. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  80. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  81. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  82. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  83. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  84. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  85. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  86. package/_standalone/.next/server/app/changes/page.js +1 -1
  87. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  88. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  89. package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
  90. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  91. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  92. package/_standalone/.next/server/app/echo/page.js +1 -1
  93. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  94. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  95. package/_standalone/.next/server/app/explore/page.js +1 -1
  96. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  97. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  98. package/_standalone/.next/server/app/help/page.js +1 -1
  99. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  100. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  101. package/_standalone/.next/server/app/login/page.js +1 -1
  102. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  103. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  104. package/_standalone/.next/server/app/page.js +1 -1
  105. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  106. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  107. package/_standalone/.next/server/app/setup/page.js +2 -2
  108. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  109. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  110. package/_standalone/.next/server/app/trash/page.js +3 -3
  111. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  112. package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
  113. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  114. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  115. package/_standalone/.next/server/app-paths-manifest.json +15 -15
  116. package/_standalone/.next/server/chunks/4931.js +27 -27
  117. package/_standalone/.next/server/chunks/6539.js +1 -1
  118. package/_standalone/.next/server/chunks/7670.js +1 -1
  119. package/_standalone/.next/server/chunks/{1225.js → 9360.js} +2 -2
  120. package/_standalone/.next/server/pages/500.html +2 -2
  121. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  122. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  123. package/_standalone/.next/static/chunks/{1263-31c1efe3fd8f3f99.js → 1263-79beb8734dee7bbd.js} +2 -2
  124. package/_standalone/.next/static/chunks/app/{layout-1ad561312a9a5b5b.js → layout-d0f6dc4d3e6f0be4.js} +11 -11
  125. package/_standalone/.next/static/chunks/app/{page-e2a6e96831efa703.js → page-404b0bfb5721b7f8.js} +2 -2
  126. package/_standalone/.next/static/chunks/app/setup/page-01ab1f549d636057.js +1 -0
  127. package/_standalone/.next/static/chunks/app/trash/{page-c6e9de9ca4ab4bf7.js → page-a79804c1df44d3cc.js} +1 -1
  128. package/_standalone/.next/static/chunks/app/view/[...path]/{page-ff57a587c3f5490e.js → page-d3aceca36a1a9eb2.js} +2 -2
  129. package/_standalone/.next/trace +53 -53
  130. package/_standalone/components/SyncStatusBar.tsx +1 -1
  131. package/_standalone/components/TableOfContents.tsx +7 -0
  132. package/_standalone/components/setup/StepAI.tsx +66 -3
  133. package/_standalone/components/setup/StepDots.tsx +18 -10
  134. package/_standalone/components/setup/StepKB.tsx +26 -0
  135. package/_standalone/components/setup/constants.tsx +4 -3
  136. package/_standalone/data/skills/mindos/SKILL.md +4 -22
  137. package/_standalone/data/skills/mindos-zh/SKILL.md +2 -20
  138. package/_standalone/lib/agent/context.ts +29 -20
  139. package/_standalone/lib/i18n/modules/onboarding.ts +14 -6
  140. package/_standalone/tsconfig.tsbuildinfo +1 -1
  141. package/app/app/api/ask/route.ts +77 -24
  142. package/app/app/api/bootstrap/route.ts +4 -0
  143. package/app/app/api/export/route.ts +5 -0
  144. package/app/app/api/git/route.ts +2 -1
  145. package/app/app/api/recent-files/route.ts +3 -2
  146. package/app/app/api/setup/route.ts +1 -0
  147. package/app/components/SyncStatusBar.tsx +1 -1
  148. package/app/components/TableOfContents.tsx +7 -0
  149. package/app/components/setup/StepAI.tsx +66 -3
  150. package/app/components/setup/StepDots.tsx +18 -10
  151. package/app/components/setup/StepKB.tsx +26 -0
  152. package/app/components/setup/constants.tsx +4 -3
  153. package/app/components/setup/index.tsx +22 -32
  154. package/app/data/skills/mindos/SKILL.md +4 -22
  155. package/app/data/skills/mindos-zh/SKILL.md +2 -20
  156. package/app/lib/acp/subprocess.ts +3 -2
  157. package/app/lib/agent/context.ts +29 -20
  158. package/app/lib/fs.ts +77 -0
  159. package/app/lib/i18n/modules/onboarding.ts +14 -6
  160. package/app/lib/settings.ts +6 -0
  161. package/bin/cli.js +14 -4
  162. package/bin/lib/stop.js +5 -2
  163. package/package.json +6 -2
  164. package/scripts/setup.js +16 -0
  165. package/skills/mindos/SKILL.md +4 -22
  166. package/skills/mindos/references/write-supplement.md +35 -0
  167. package/skills/mindos-zh/SKILL.md +2 -20
  168. package/skills/mindos-zh/references/README.md +2 -1
  169. package/skills/mindos-zh/references/write-supplement.md +35 -0
  170. package/_standalone/.next/static/chunks/app/setup/page-f8a85accc3be554f.js +0 -1
  171. /package/_standalone/.next/static/{K1Y12u2jxCR7efUgJVpyl → VWZDVb8DL1Pbykh9EOj_Y}/_buildManifest.js +0 -0
  172. /package/_standalone/.next/static/{K1Y12u2jxCR7efUgJVpyl → VWZDVb8DL1Pbykh9EOj_Y}/_ssgManifest.js +0 -0
@@ -53,30 +53,67 @@ type MindOSSSEvent =
53
53
 
54
54
  // ---------------------------------------------------------------------------
55
55
  // Type Guards for AgentEvent variants (safe event handling)
56
+ // AgentEvent from pi-coding-agent is a union; these interfaces describe the
57
+ // actual shapes that arrive at runtime for each event.type.
56
58
  // ---------------------------------------------------------------------------
57
59
 
58
- function isTextDeltaEvent(e: AgentEvent): boolean {
59
- return e.type === 'message_update' && (e as any).assistantMessageEvent?.type === 'text_delta';
60
+ /** Fields present on message_update events (text_delta / thinking_delta). */
61
+ type MessageUpdateEvent = AgentEvent & {
62
+ type: 'message_update';
63
+ assistantMessageEvent?: { type: string; delta?: string };
64
+ };
65
+
66
+ /** Fields present on tool_execution_start events. */
67
+ type ToolExecStartEvent = AgentEvent & {
68
+ type: 'tool_execution_start';
69
+ toolCallId?: string;
70
+ toolName?: string;
71
+ args?: unknown;
72
+ };
73
+
74
+ /** Fields present on tool_execution_end events. */
75
+ type ToolExecEndEvent = AgentEvent & {
76
+ type: 'tool_execution_end';
77
+ toolCallId?: string;
78
+ result?: { content?: Array<{ type: string; text?: string }> };
79
+ isError?: boolean;
80
+ };
81
+
82
+ /** Fields present on turn_end events. */
83
+ type TurnEndEvent = AgentEvent & {
84
+ type: 'turn_end';
85
+ toolResults?: Array<{ toolName: string; content: unknown }>;
86
+ usage?: { inputTokens: number; outputTokens?: number };
87
+ };
88
+
89
+ /** Fields present on agent_end events. */
90
+ type AgentEndEvent = AgentEvent & {
91
+ type: 'agent_end';
92
+ messages?: Array<{ role: string; content?: Array<{ type: string; text?: string }> }>;
93
+ };
94
+
95
+ function isTextDeltaEvent(e: AgentEvent): e is MessageUpdateEvent {
96
+ return e.type === 'message_update' && (e as MessageUpdateEvent).assistantMessageEvent?.type === 'text_delta';
60
97
  }
61
98
 
62
99
  function getTextDelta(e: AgentEvent): string {
63
- return (e as any).assistantMessageEvent?.delta ?? '';
100
+ return (e as MessageUpdateEvent).assistantMessageEvent?.delta ?? '';
64
101
  }
65
102
 
66
- function isThinkingDeltaEvent(e: AgentEvent): boolean {
67
- return e.type === 'message_update' && (e as any).assistantMessageEvent?.type === 'thinking_delta';
103
+ function isThinkingDeltaEvent(e: AgentEvent): e is MessageUpdateEvent {
104
+ return e.type === 'message_update' && (e as MessageUpdateEvent).assistantMessageEvent?.type === 'thinking_delta';
68
105
  }
69
106
 
70
107
  function getThinkingDelta(e: AgentEvent): string {
71
- return (e as any).assistantMessageEvent?.delta ?? '';
108
+ return (e as MessageUpdateEvent).assistantMessageEvent?.delta ?? '';
72
109
  }
73
110
 
74
- function isToolExecutionStartEvent(e: AgentEvent): boolean {
111
+ function isToolExecutionStartEvent(e: AgentEvent): e is ToolExecStartEvent {
75
112
  return e.type === 'tool_execution_start';
76
113
  }
77
114
 
78
115
  function getToolExecutionStart(e: AgentEvent): { toolCallId: string; toolName: string; args: unknown } {
79
- const evt = e as any;
116
+ const evt = e as ToolExecStartEvent;
80
117
  return {
81
118
  toolCallId: evt.toolCallId ?? '',
82
119
  toolName: evt.toolName ?? 'unknown',
@@ -84,15 +121,15 @@ function getToolExecutionStart(e: AgentEvent): { toolCallId: string; toolName: s
84
121
  };
85
122
  }
86
123
 
87
- function isToolExecutionEndEvent(e: AgentEvent): boolean {
124
+ function isToolExecutionEndEvent(e: AgentEvent): e is ToolExecEndEvent {
88
125
  return e.type === 'tool_execution_end';
89
126
  }
90
127
 
91
128
  function getToolExecutionEnd(e: AgentEvent): { toolCallId: string; output: string; isError: boolean } {
92
- const evt = e as any;
129
+ const evt = e as ToolExecEndEvent;
93
130
  const outputText = evt.result?.content
94
- ?.filter((p: any) => p.type === 'text')
95
- .map((p: any) => p.text)
131
+ ?.filter((p: { type: string; text?: string }) => p.type === 'text')
132
+ .map((p: { type: string; text?: string }) => p.text ?? '')
96
133
  .join('') ?? '';
97
134
  return {
98
135
  toolCallId: evt.toolCallId ?? '',
@@ -101,13 +138,13 @@ function getToolExecutionEnd(e: AgentEvent): { toolCallId: string; output: strin
101
138
  };
102
139
  }
103
140
 
104
- function isTurnEndEvent(e: AgentEvent): boolean {
141
+ function isTurnEndEvent(e: AgentEvent): e is TurnEndEvent {
105
142
  return e.type === 'turn_end';
106
143
  }
107
144
 
108
145
  function getTurnEndData(e: AgentEvent): { toolResults: Array<{ toolName: string; content: unknown }> } {
109
146
  return {
110
- toolResults: ((e as any).toolResults as any[]) ?? [],
147
+ toolResults: (e as TurnEndEvent).toolResults ?? [],
111
148
  };
112
149
  }
113
150
 
@@ -215,21 +252,21 @@ function textToolResult(text: string): AgentToolResult<Record<string, never>> {
215
252
 
216
253
  function getProtectedPaths(toolName: string, args: Record<string, unknown>): string[] {
217
254
  const pathsToCheck: string[] = [];
218
- if (toolName === 'batch_create_files' && Array.isArray((args as any).files)) {
219
- (args as any).files.forEach((f: any) => { if (f.path) pathsToCheck.push(f.path); });
255
+ if (toolName === 'batch_create_files' && Array.isArray(args.files)) {
256
+ (args.files as Array<{ path?: string }>).forEach((f) => { if (f.path) pathsToCheck.push(f.path); });
220
257
  } else {
221
- const singlePath = (args as any).path ?? (args as any).from_path;
258
+ const singlePath = (args.path ?? args.from_path) as string | undefined;
222
259
  if (typeof singlePath === 'string') pathsToCheck.push(singlePath);
223
260
  }
224
261
  return pathsToCheck;
225
262
  }
226
263
 
227
- function toPiCustomToolDefinitions(tools: AgentTool<any>[]): ToolDefinition[] {
264
+ function toPiCustomToolDefinitions(tools: AgentTool<any>[]): ToolDefinition<any, unknown>[] {
228
265
  return tools.map((tool) => ({
229
266
  name: tool.name,
230
267
  label: tool.label,
231
268
  description: tool.description,
232
- parameters: tool.parameters as any,
269
+ parameters: tool.parameters as Record<string, unknown>,
233
270
  execute: async (toolCallId, params, signal, onUpdate) => {
234
271
  const args = (params ?? {}) as Record<string, unknown>;
235
272
 
@@ -414,7 +451,18 @@ export async function POST(req: NextRequest) {
414
451
  }
415
452
  if (bootstrap.instruction.ok) initContextBlocks.push(`## bootstrap_instruction\n\n${bootstrap.instruction.content}`);
416
453
  if (bootstrap.index?.ok) initContextBlocks.push(`## bootstrap_index\n\n${bootstrap.index.content}`);
417
- if (bootstrap.config_json.ok) initContextBlocks.push(`## bootstrap_config_json\n\n${bootstrap.config_json.content}`);
454
+ if (bootstrap.config_json.ok) {
455
+ // Strip UI-only sections (uiSchema, keySpecs) — they are consumed exclusively
456
+ // by the frontend renderer and add ~1,120 tokens of noise the agent never uses.
457
+ let configContent = bootstrap.config_json.content;
458
+ try {
459
+ const parsed = JSON.parse(configContent);
460
+ delete parsed.uiSchema;
461
+ delete parsed.keySpecs;
462
+ configContent = JSON.stringify(parsed, null, 2);
463
+ } catch { /* keep original if parse fails */ }
464
+ initContextBlocks.push(`## bootstrap_config_json\n\n${configContent}`);
465
+ }
418
466
  if (bootstrap.config_md?.ok) initContextBlocks.push(`## bootstrap_config_md\n\n${bootstrap.config_md.content}`);
419
467
  if (bootstrap.target_readme?.ok) initContextBlocks.push(`## bootstrap_target_readme\n\n${bootstrap.target_readme.content}`);
420
468
  if (bootstrap.target_instruction?.ok) initContextBlocks.push(`## bootstrap_target_instruction\n\n${bootstrap.target_instruction.content}`);
@@ -455,7 +503,11 @@ export async function POST(req: NextRequest) {
455
503
 
456
504
  const promptParts: string[] = [AGENT_SYSTEM_PROMPT];
457
505
  promptParts.push(`---\n\n${timeContext}`);
458
- promptParts.push(`---\n\nInitialization status (auto-loaded at request start):\n\n${initStatus}`);
506
+ // Only inject initStatus when there are failures or truncation warnings.
507
+ // On the happy path (~99% of requests) this saves ~100 tokens.
508
+ if (initFailures.length > 0 || truncationWarnings.length > 0) {
509
+ promptParts.push(`---\n\nInitialization status (auto-loaded at request start):\n\n${initStatus}`);
510
+ }
459
511
 
460
512
  if (initContextBlocks.length > 0) {
461
513
  promptParts.push(`---\n\nInitialization context:\n\n${initContextBlocks.join('\n\n---\n\n')}`);
@@ -604,7 +656,7 @@ export async function POST(req: NextRequest) {
604
656
  stepCount++;
605
657
 
606
658
  // Record token usage if available from the turn
607
- const turnUsage = (event as any).usage;
659
+ const turnUsage = (event as TurnEndEvent).usage;
608
660
  if (turnUsage && typeof turnUsage.inputTokens === 'number') {
609
661
  metrics.recordTokens(turnUsage.inputTokens, turnUsage.outputTokens ?? 0);
610
662
  }
@@ -633,12 +685,13 @@ export async function POST(req: NextRequest) {
633
685
  void session.abort();
634
686
  }
635
687
 
636
- console.log(`[ask] Step ${stepCount}/${stepLimit}`);
688
+ // Step count logged in dev only to avoid polluting production output
689
+ if (process.env.NODE_ENV === 'development') console.log(`[ask] Step ${stepCount}/${stepLimit}`);
637
690
  } else if (event.type === 'agent_end') {
638
691
  // Capture model errors from the last assistant message.
639
692
  // pi-coding-agent resolves prompt() without throwing after retries;
640
693
  // the error is only visible in agent_end event messages.
641
- const msgs = (event as any).messages;
694
+ const msgs = (event as AgentEndEvent).messages;
642
695
  if (Array.isArray(msgs)) {
643
696
  for (let i = msgs.length - 1; i >= 0; i--) {
644
697
  const m = msgs[i];
@@ -25,6 +25,10 @@ export async function GET(req: NextRequest) {
25
25
  };
26
26
 
27
27
  if (targetDir) {
28
+ // Reject path traversal attempts
29
+ if (targetDir.includes('..') || path.isAbsolute(targetDir)) {
30
+ return NextResponse.json({ error: 'invalid target_dir' }, { status: 400 });
31
+ }
28
32
  result.target_readme = tryRead(path.join(targetDir, 'README.md'));
29
33
  result.target_instruction = tryRead(path.join(targetDir, 'INSTRUCTION.md'));
30
34
  result.target_config_json = tryRead(path.join(targetDir, 'CONFIG.json'));
@@ -10,11 +10,16 @@ export async function GET(req: NextRequest) {
10
10
  const { searchParams } = req.nextUrl;
11
11
  const filePath = searchParams.get('path');
12
12
  const format = searchParams.get('format') ?? 'md';
13
+ const VALID_FORMATS = new Set(['md', 'html', 'zip', 'zip-html']);
13
14
 
14
15
  if (!filePath) {
15
16
  return NextResponse.json({ error: 'Missing path parameter' }, { status: 400 });
16
17
  }
17
18
 
19
+ if (!VALID_FORMATS.has(format)) {
20
+ return NextResponse.json({ error: `Invalid format: ${format}. Use: ${[...VALID_FORMATS].join(', ')}` }, { status: 400 });
21
+ }
22
+
18
23
  // Path traversal defense-in-depth
19
24
  if (filePath.includes('..') || filePath.startsWith('/')) {
20
25
  return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
@@ -19,7 +19,8 @@ export async function GET(req: NextRequest) {
19
19
  case 'history': {
20
20
  const filePath = req.nextUrl.searchParams.get('path');
21
21
  if (!filePath) return err('missing path');
22
- const limit = parseInt(req.nextUrl.searchParams.get('limit') ?? '10', 10);
22
+ const rawLimit = parseInt(req.nextUrl.searchParams.get('limit') ?? '10', 10);
23
+ const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 100)) : 10;
23
24
  const entries = gitLog(filePath, limit);
24
25
  return NextResponse.json({ entries });
25
26
  }
@@ -4,7 +4,8 @@ import { getRecentlyModified } from '@/lib/fs';
4
4
 
5
5
  export async function GET(req: NextRequest) {
6
6
  const limitParam = req.nextUrl.searchParams.get('limit');
7
- const limit = limitParam ? parseInt(limitParam, 10) : 10;
8
- const files = getRecentlyModified(Math.min(limit, 30));
7
+ const raw = limitParam ? parseInt(limitParam, 10) : 10;
8
+ const limit = Number.isFinite(raw) ? raw : 10;
9
+ const files = getRecentlyModified(Math.max(1, Math.min(limit, 30)));
9
10
  return NextResponse.json(files);
10
11
  }
@@ -125,6 +125,7 @@ export async function POST(req: NextRequest) {
125
125
  webPassword: webPassword ?? '',
126
126
  startMode: current.startMode,
127
127
  setupPending: false, // clear the flag
128
+ setupPort: undefined, // clear temporary setup port (zombie cleanup)
128
129
  disabledSkills,
129
130
  guideState: {
130
131
  active: true,
@@ -183,7 +183,7 @@ export default function SyncStatusBar({ collapsed, onOpenSyncSettings }: SyncSta
183
183
  const currentLevel = getStatusLevel(status, false);
184
184
  const prev = prevLevelRef.current;
185
185
  if (prev !== currentLevel) {
186
- const syncT = (t as any).sidebar?.sync;
186
+ const syncT = t.sidebar?.sync;
187
187
  // Recovery: was error/conflicts, now synced
188
188
  if ((prev === 'error' || prev === 'conflicts') && currentLevel === 'synced') {
189
189
  setToast(syncT?.syncRestored ?? 'Sync restored');
@@ -14,7 +14,14 @@ function parseHeadings(content: string): Heading[] {
14
14
  const slugger = new GithubSlugger();
15
15
  const lines = content.split('\n');
16
16
  const headings: Heading[] = [];
17
+ let inCodeBlock = false;
17
18
  for (const line of lines) {
19
+ // Toggle code fence state (``` or ~~~, with optional language tag)
20
+ if (/^(`{3,}|~{3,})/.test(line)) {
21
+ inCodeBlock = !inCodeBlock;
22
+ continue;
23
+ }
24
+ if (inCodeBlock) continue;
18
25
  const match = line.match(/^(#{1,4})\s+(.+)/);
19
26
  if (match) {
20
27
  const level = match[1].length;
@@ -1,16 +1,35 @@
1
1
  'use client';
2
2
 
3
- import { Brain, Zap, SkipForward, CheckCircle2 } from 'lucide-react';
3
+ import { useState, useEffect } from 'react';
4
+ import { Brain, Zap, SkipForward, CheckCircle2, ChevronDown, ChevronRight, Copy } from 'lucide-react';
4
5
  import { Field, Input, ApiKeyInput } from '@/components/settings/Primitives';
5
- import type { SetupState, SetupMessages } from './types';
6
+ import type { SetupState, SetupMessages, PortStatus } from './types';
7
+ import StepPorts from './StepPorts';
6
8
 
7
9
  export interface StepAIProps {
8
10
  state: SetupState;
9
11
  update: <K extends keyof SetupState>(key: K, val: SetupState[K]) => void;
10
12
  s: SetupMessages;
13
+ onCopyToken: () => void;
14
+ // Port props (embedded in Advanced section)
15
+ webPortStatus: PortStatus;
16
+ mcpPortStatus: PortStatus;
17
+ setWebPortStatus: (s: PortStatus) => void;
18
+ setMcpPortStatus: (s: PortStatus) => void;
19
+ checkPort: (port: number, which: 'web' | 'mcp') => void;
20
+ portConflict: boolean;
11
21
  }
12
22
 
13
- export default function StepAI({ state, update, s }: StepAIProps) {
23
+ export default function StepAI({ state, update, s, onCopyToken, webPortStatus, mcpPortStatus, setWebPortStatus, setMcpPortStatus, checkPort, portConflict }: StepAIProps) {
24
+ const [portsOpen, setPortsOpen] = useState(false);
25
+
26
+ // Auto-expand Advanced section if port check finds a problem
27
+ useEffect(() => {
28
+ if (!portsOpen && (webPortStatus.available === false || mcpPortStatus.available === false || portConflict)) {
29
+ setPortsOpen(true);
30
+ }
31
+ }, [webPortStatus.available, mcpPortStatus.available, portConflict, portsOpen]);
32
+
14
33
  const providers = [
15
34
  { id: 'anthropic' as const, icon: <Brain size={18} />, label: 'Anthropic', desc: 'Claude — claude-sonnet-4-6' },
16
35
  { id: 'openai' as const, icon: <Zap size={18} />, label: 'OpenAI', desc: 'GPT or any OpenAI-compatible API' },
@@ -70,6 +89,50 @@ export default function StepAI({ state, update, s }: StepAIProps) {
70
89
  )}
71
90
  </div>
72
91
  )}
92
+
93
+ {/* Advanced: Port Settings (collapsed) */}
94
+ <div className="pt-3 mt-1" style={{ borderTop: '1px solid var(--border)' }}>
95
+ <button
96
+ type="button"
97
+ onClick={() => setPortsOpen(!portsOpen)}
98
+ className="flex items-center gap-1.5 text-xs font-medium"
99
+ style={{ color: 'var(--muted-foreground)' }}>
100
+ {portsOpen ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
101
+ {s.advancedPorts}
102
+ </button>
103
+ {portsOpen && (
104
+ <div className="mt-3 space-y-5">
105
+ {/* MCP Auth Token (read-only) */}
106
+ <div className="space-y-1.5">
107
+ <p className="text-xs font-medium" style={{ color: 'var(--foreground)' }}>
108
+ 🔑 {s.tokenSectionTitle}
109
+ </p>
110
+ <div className="flex items-center gap-2">
111
+ <code className="flex-1 truncate text-xs font-mono px-3 py-2 rounded-lg"
112
+ style={{ background: 'var(--muted)', color: 'var(--foreground)' }}>
113
+ {state.authToken}
114
+ </code>
115
+ <button type="button" onClick={onCopyToken}
116
+ className="flex items-center gap-1 px-2.5 py-2 text-xs rounded-lg border transition-colors hover:bg-muted shrink-0"
117
+ style={{ borderColor: 'var(--border)', color: 'var(--foreground)' }}>
118
+ <Copy size={12} /> {s.copyToken}
119
+ </button>
120
+ </div>
121
+ <p className="text-xs" style={{ color: 'var(--muted-foreground)' }}>
122
+ {s.tokenSectionHint}
123
+ </p>
124
+ </div>
125
+
126
+ {/* Port Settings */}
127
+ <StepPorts
128
+ state={state} update={update}
129
+ webPortStatus={webPortStatus} mcpPortStatus={mcpPortStatus}
130
+ setWebPortStatus={setWebPortStatus} setMcpPortStatus={setMcpPortStatus}
131
+ checkPort={checkPort} portConflict={portConflict} s={s}
132
+ />
133
+ </div>
134
+ )}
135
+ </div>
73
136
  </div>
74
137
  );
75
138
  }
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { CheckCircle2 } from 'lucide-react';
3
4
  import { useLocale } from '@/lib/LocaleContext';
4
5
 
5
6
  export interface StepDotsProps {
@@ -7,32 +8,39 @@ export interface StepDotsProps {
7
8
  setStep: (s: number) => void;
8
9
  stepTitles: readonly string[];
9
10
  disabled?: boolean;
11
+ /** Number of "numbered" steps to show (Confirm step is not numbered) */
12
+ numberedSteps?: number;
10
13
  }
11
14
 
12
- export default function StepDots({ step, setStep, stepTitles, disabled }: StepDotsProps) {
15
+ export default function StepDots({ step, setStep, stepTitles, disabled, numberedSteps }: StepDotsProps) {
13
16
  const { t } = useLocale();
17
+ const count = numberedSteps ?? stepTitles.length;
18
+ // Only render dots for numbered steps (exclude Confirm)
19
+ const dotsToShow = stepTitles.slice(0, count);
20
+ const isConfirmStep = step >= count;
21
+
14
22
  return (
15
23
  <div className="flex items-center gap-2 mb-8" role="navigation" aria-label="Setup steps">
16
- {stepTitles.map((title: string, i: number) => (
24
+ {dotsToShow.map((title: string, i: number) => (
17
25
  <div key={i} className="flex items-center gap-2">
18
- {i > 0 && <div className="w-8 h-px" style={{ background: i <= step ? 'var(--amber)' : 'var(--border)' }} />}
26
+ {i > 0 && <div className="w-8 h-px" style={{ background: i <= step || isConfirmStep ? 'var(--amber)' : 'var(--border)' }} />}
19
27
  <button onClick={() => setStep(i)}
20
28
  aria-current={i === step ? 'step' : undefined}
21
29
  aria-label={title}
22
30
  className="flex flex-col items-center gap-1 p-1 -m-1 disabled:cursor-not-allowed disabled:opacity-60"
23
- disabled={disabled || i >= step}
24
- title={(disabled || i >= step) ? t.hints.cannotJumpForward : undefined}>
31
+ disabled={disabled || i > step}
32
+ title={(disabled || i > step) ? t.hints.cannotJumpForward : undefined}>
25
33
  <div
26
34
  className="w-6 h-6 rounded-full text-xs font-medium flex items-center justify-center transition-colors"
27
35
  style={{
28
- background: i <= step ? 'var(--amber)' : 'var(--muted)',
29
- color: i <= step ? 'var(--amber-foreground)' : 'var(--muted-foreground)',
30
- opacity: i <= step ? 1 : 0.5,
36
+ background: (i < step || isConfirmStep) ? 'var(--amber)' : i === step ? 'var(--amber)' : 'var(--muted)',
37
+ color: (i <= step || isConfirmStep) ? 'var(--amber-foreground)' : 'var(--muted-foreground)',
38
+ opacity: (i <= step || isConfirmStep) ? 1 : 0.5,
31
39
  }}>
32
- {i + 1}
40
+ {(i < step || isConfirmStep) ? <CheckCircle2 size={14} /> : i + 1}
33
41
  </div>
34
42
  <span className="text-[10px] leading-tight hidden sm:inline max-w-[4rem] text-center truncate"
35
- style={{ color: i === step ? 'var(--foreground)' : 'var(--muted-foreground)', opacity: i <= step ? 1 : 0.5 }}>
43
+ style={{ color: (i === step && !isConfirmStep) ? 'var(--foreground)' : 'var(--muted-foreground)', opacity: (i <= step || isConfirmStep) ? 1 : 0.5 }}>
36
44
  {title}
37
45
  </span>
38
46
  </button>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect, useRef } from 'react';
4
+ import { AlertCircle } from 'lucide-react';
4
5
  import { Field } from '@/components/settings/Primitives';
5
6
  import type { Messages } from '@/lib/i18n';
6
7
  import type { SetupState } from './types';
@@ -26,6 +27,7 @@ export interface StepKBProps {
26
27
 
27
28
  export default function StepKB({ state, update, t, homeDir }: StepKBProps) {
28
29
  const s = t.setup;
30
+ const [passwordTouched, setPasswordTouched] = useState(false);
29
31
  // Build platform-aware placeholder, e.g. /Users/alice/MindOS/mind or C:\Users\alice\MindOS\mind
30
32
  // Windows homedir always contains \, e.g. C:\Users\Alice — safe to detect by separator
31
33
  const sep = homeDir.includes('\\') ? '\\' : '/';
@@ -232,6 +234,30 @@ export default function StepKB({ state, update, t, homeDir }: StepKBProps) {
232
234
  </div>
233
235
  </div>
234
236
  )}
237
+
238
+ {/* ── Security ── */}
239
+ <div className="pt-2 mt-2" style={{ borderTop: '1px solid var(--border)' }}>
240
+ <Field label={<>{s.webPassword} <span style={{ color: 'var(--error)' }}>*</span></>} hint={s.webPasswordHint}>
241
+ <input
242
+ type="password"
243
+ value={state.webPassword}
244
+ onChange={e => { update('webPassword', e.target.value); setPasswordTouched(true); }}
245
+ onBlur={() => setPasswordTouched(true)}
246
+ placeholder="••••••••"
247
+ className="w-full px-3 py-2 text-sm rounded-lg border outline-none transition-colors focus-visible:ring-1 focus-visible:ring-ring"
248
+ style={{
249
+ background: 'var(--input, var(--card))',
250
+ borderColor: passwordTouched && !state.webPassword.trim() ? 'var(--error)' : 'var(--border)',
251
+ color: 'var(--foreground)',
252
+ }}
253
+ />
254
+ {passwordTouched && !state.webPassword.trim() && (
255
+ <p className="text-xs flex items-center gap-1 mt-1" style={{ color: 'var(--error)' }}>
256
+ <AlertCircle size={11} /> {s.webPasswordRequired}
257
+ </p>
258
+ )}
259
+ </Field>
260
+ </div>
235
261
  </div>
236
262
  );
237
263
  }
@@ -7,7 +7,8 @@ export const TEMPLATES: Array<{ id: Template; icon: React.ReactNode; dirs: strin
7
7
  { id: 'empty', icon: <FileText size={18} />, dirs: ['README.md', 'CONFIG.json', 'INSTRUCTION.md'] },
8
8
  ];
9
9
 
10
- export const TOTAL_STEPS = 6;
10
+ export const TOTAL_STEPS = 4;
11
11
  export const STEP_KB = 0;
12
- export const STEP_PORTS = 2;
13
- export const STEP_AGENTS = 4;
12
+ export const STEP_AI = 1;
13
+ export const STEP_AGENTS = 2;
14
+ export const STEP_REVIEW = 3;
@@ -6,11 +6,9 @@ import { useLocale } from '@/lib/LocaleContext';
6
6
  import { copyToClipboard } from '@/lib/clipboard';
7
7
  import { toast } from '@/lib/toast';
8
8
  import type { SetupState, PortStatus, AgentEntry, AgentInstallStatus } from './types';
9
- import { TOTAL_STEPS, STEP_KB, STEP_PORTS, STEP_AGENTS } from './constants';
9
+ import { TOTAL_STEPS, STEP_KB, STEP_AI, STEP_AGENTS, STEP_REVIEW } from './constants';
10
10
  import StepKB from './StepKB';
11
11
  import StepAI from './StepAI';
12
- import StepPorts from './StepPorts';
13
- import StepSecurity from './StepSecurity';
14
12
  import StepAgents from './StepAgents';
15
13
  import StepReview from './StepReview';
16
14
  import { RestartButton } from './StepReview';
@@ -122,8 +120,8 @@ export default function SetupWizard() {
122
120
  openaiModel: 'gpt-5.4',
123
121
  openaiBaseUrl: '',
124
122
  openaiKeyMask: '',
125
- webPort: 3000,
126
- mcpPort: 8787,
123
+ webPort: 3456,
124
+ mcpPort: 8781,
127
125
  authToken: '',
128
126
  webPassword: '',
129
127
  });
@@ -184,16 +182,16 @@ export default function SetupWizard() {
184
182
  });
185
183
  }, []);
186
184
 
187
- // Auto-check ports when entering Step 3
185
+ // Auto-check ports when entering AI step (ports are in Advanced section)
188
186
  useEffect(() => {
189
- if (step === STEP_PORTS) {
187
+ if (step === STEP_AI) {
190
188
  checkPort(state.webPort, 'web');
191
189
  checkPort(state.mcpPort, 'mcp');
192
190
  }
193
191
  // eslint-disable-next-line react-hooks/exhaustive-deps
194
192
  }, [step]);
195
193
 
196
- // Load agents when entering Step 5
194
+ // Load agents when entering Agents step
197
195
  useEffect(() => {
198
196
  if (step === STEP_AGENTS && !agentsLoaded && !agentsLoading) {
199
197
  setAgentsLoading(true);
@@ -256,15 +254,17 @@ export default function SetupWizard() {
256
254
  const portConflict = state.webPort === state.mcpPort;
257
255
 
258
256
  const canNext = () => {
259
- if (step === STEP_KB) return state.mindRoot.trim().length > 0;
260
- if (step === STEP_PORTS) {
257
+ if (step === STEP_KB) {
258
+ // KB path required + password required
259
+ return state.mindRoot.trim().length > 0 && state.webPassword.trim().length > 0;
260
+ }
261
+ if (step === STEP_AI) {
262
+ // Ports validation (only when Advanced is open and ports were modified)
261
263
  if (portConflict) return false;
262
264
  if (webPortStatus.checking || mcpPortStatus.checking) return false;
263
- if (webPortStatus.available !== true || mcpPortStatus.available !== true) return false;
264
- return (
265
- state.webPort >= 1024 && state.webPort <= 65535 &&
266
- state.mcpPort >= 1024 && state.mcpPort <= 65535
267
- );
265
+ // Allow next if ports haven't been checked yet (user didn't open Advanced)
266
+ if (webPortStatus.available === false || mcpPortStatus.available === false) return false;
267
+ return true;
268
268
  }
269
269
  return true;
270
270
  };
@@ -359,32 +359,22 @@ export default function SetupWizard() {
359
359
  </div>
360
360
 
361
361
  <div className="flex justify-center">
362
- <StepDots step={step} setStep={setStep} stepTitles={s.stepTitles} disabled={submitting || completed} />
362
+ <StepDots step={step} setStep={setStep} stepTitles={s.stepTitles} disabled={submitting || completed} numberedSteps={STEP_REVIEW} />
363
363
  </div>
364
364
 
365
365
  <h2 className="text-lg font-semibold mb-5" style={{ color: 'var(--foreground)' }}>
366
- {s.stepTitles[step]}
366
+ {step === STEP_REVIEW ? `✓ ${s.stepTitles[step]}` : s.stepTitles[step]}
367
367
  </h2>
368
368
 
369
369
  {step === 0 && <StepKB state={state} update={update} t={t} homeDir={homeDir} />}
370
- {step === 1 && <StepAI state={state} update={update} s={s} />}
371
- {step === 2 && (
372
- <StepPorts
373
- state={state} update={update}
370
+ {step === 1 && (
371
+ <StepAI state={state} update={update} s={s} onCopyToken={copyToken}
374
372
  webPortStatus={webPortStatus} mcpPortStatus={mcpPortStatus}
375
373
  setWebPortStatus={setWebPortStatus} setMcpPortStatus={setMcpPortStatus}
376
- checkPort={checkPort} portConflict={portConflict} s={s}
377
- />
378
- )}
379
- {step === 3 && (
380
- <StepSecurity
381
- authToken={state.authToken}
382
- onCopy={copyToken} onGenerate={generateToken}
383
- webPassword={state.webPassword} onPasswordChange={v => update('webPassword', v)}
384
- s={s}
374
+ checkPort={checkPort} portConflict={portConflict}
385
375
  />
386
376
  )}
387
- {step === 4 && (
377
+ {step === 2 && (
388
378
  <StepAgents
389
379
  agents={agents} agentsLoading={agentsLoading}
390
380
  selectedAgents={selectedAgents} setSelectedAgents={setSelectedAgents}
@@ -394,7 +384,7 @@ export default function SetupWizard() {
394
384
  template={state.template}
395
385
  />
396
386
  )}
397
- {step === 5 && (
387
+ {step === 3 && (
398
388
  <StepReview
399
389
  state={state} selectedAgents={selectedAgents}
400
390
  agentStatuses={agentStatuses} onRetryAgent={retryAgent}
@@ -96,21 +96,12 @@ It covers: startup protocol, write tool selection, and step-by-step execution de
96
96
  | List top-level Mind Spaces | `mindos_list_spaces` | Full `mindos_list_files` when you only need zone names and README blurbs |
97
97
  | Find files | `mindos_search_notes` (2-4 parallel keyword variants) | Single-keyword search |
98
98
  | Read content | `mindos_read_file` or `mindos_read_lines` (for large files) | Reading entire large file when you need 10 lines |
99
- | Small text edit | `mindos_update_section` / `mindos_update_lines` / `mindos_insert_after_heading` | `mindos_write_file` for small changes |
100
- | Append to end | `mindos_append_to_file` | Rewriting entire file to add a line |
101
- | Full file replacement | `mindos_write_file` | Using this when a section edit suffices |
102
- | New file | `mindos_create_file` | Creates parent dirs but does NOT scaffold Space files |
103
- | New Mind Space (zone + README + INSTRUCTION) | `mindos_create_space` | The only way to create a Space. `create_file` creates plain folders |
104
- | Rename a Space directory | `mindos_rename_space` | `rename_file` (files only; does not rename folders) |
105
- | Add CSV row | `mindos_append_csv` (validates header) | Manual string append without header check |
106
- | Check impact before rename | `mindos_get_backlinks` | Renaming without checking references |
107
- | Inspect recent changes | `mindos_get_recent` | Guessing what changed recently |
108
- | Recover old version | `mindos_get_file_at_version` | Asking user to recall what was there |
99
+
100
+ Write, structural, and history tools [references/write-supplement.md](./references/write-supplement.md).
109
101
 
110
102
  ### Fallbacks
111
103
 
112
104
  - `mindos_bootstrap` unavailable → manual reads of root `INSTRUCTION.md` + `README.md`.
113
- - Line/section tools unavailable → read + constrained `mindos_write_file` (simulate minimal edit).
114
105
  - Search returns empty → don't give up: (1) scan tree in context, (2) read candidate files directly, (3) `mindos_list_files` on specific subdirectories, (4) try synonym/alternate-language keywords.
115
106
 
116
107
  ---
@@ -120,17 +111,8 @@ It covers: startup protocol, write tool selection, and step-by-step execution de
120
111
  | Pattern | When | Key steps |
121
112
  |---------|------|-----------|
122
113
  | **Read-only Q&A** | Lookup / summarize / quote | Tree reasoning → search → read → answer with citations → state gaps |
123
- | **Single-file edit** | One clear target file | Startup → read target + local conventions → minimal edit → verify → summarize |
124
- | **Multi-file routing** | Unstructured input, multiple destinations | Parse into semantic units routing table → confirm → edit → summarize |
125
- | **Conversation retrospective** | Distill / capture session | Confirm scope → extract decisions/pitfalls/actions → route → trace changes |
126
- | **SOP execution** | Repeatable procedure | Read SOP fully → execute stepwise → update stale sections → propose SOP update if diverged |
127
- | **Structural change** | Rename / move / delete | `get_backlinks` → impact report → confirm → execute → update refs → sync READMEs |
128
- | **CSV append** | Add row to a table | Read header → validate fields → `mindos_append_csv` |
129
- | **Cross-agent handoff** | Continue another agent's work | Read task state + decisions → continue without re-discovery → write back progress |
130
- | **Periodic review** | Summarize recent changes | `get_recent`/`get_history` → read changed files → structured summary |
131
- | **Handoff doc** | Create a briefing | Read sources → synthesize (background, decisions, status, open items) → place in project dir |
132
-
133
- For detailed execution steps on write patterns → [references/write-supplement.md](./references/write-supplement.md).
114
+
115
+ For write, SOP, structural, and handoff patterns[references/write-supplement.md](./references/write-supplement.md).
134
116
 
135
117
  ---
136
118