@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.
- package/.env.local.example +2 -2
- package/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +15 -15
- package/_standalone/.next/build-manifest.json +2 -2
- package/_standalone/.next/cache/.previewinfo +1 -1
- package/_standalone/.next/cache/.rscinfo +1 -1
- package/_standalone/.next/cache/config.json +3 -3
- package/_standalone/.next/prerender-manifest.json +3 -3
- package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error.html +2 -2
- package/_standalone/.next/server/app/_global-error.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/_standalone/.next/server/app/_not-found/page.js +1 -1
- package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/page.js +1 -1
- package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js +5 -5
- package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route.js +1 -1
- package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route.js +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/route.js +1 -1
- package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +1 -1
- package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/page.js +1 -1
- package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/explore/page.js +1 -1
- package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/help/page.js +1 -1
- package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/login/page.js +1 -1
- package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/page.js +1 -1
- package/_standalone/.next/server/app/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/setup/page.js +2 -2
- package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/trash/page.js +3 -3
- package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
- package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +15 -15
- package/_standalone/.next/server/chunks/4931.js +27 -27
- package/_standalone/.next/server/chunks/6539.js +1 -1
- package/_standalone/.next/server/chunks/7670.js +1 -1
- package/_standalone/.next/server/chunks/{1225.js → 9360.js} +2 -2
- package/_standalone/.next/server/pages/500.html +2 -2
- package/_standalone/.next/server/server-reference-manifest.js +1 -1
- package/_standalone/.next/server/server-reference-manifest.json +1 -1
- package/_standalone/.next/static/chunks/{1263-31c1efe3fd8f3f99.js → 1263-79beb8734dee7bbd.js} +2 -2
- package/_standalone/.next/static/chunks/app/{layout-1ad561312a9a5b5b.js → layout-d0f6dc4d3e6f0be4.js} +11 -11
- package/_standalone/.next/static/chunks/app/{page-e2a6e96831efa703.js → page-404b0bfb5721b7f8.js} +2 -2
- package/_standalone/.next/static/chunks/app/setup/page-01ab1f549d636057.js +1 -0
- package/_standalone/.next/static/chunks/app/trash/{page-c6e9de9ca4ab4bf7.js → page-a79804c1df44d3cc.js} +1 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/{page-ff57a587c3f5490e.js → page-d3aceca36a1a9eb2.js} +2 -2
- package/_standalone/.next/trace +53 -53
- package/_standalone/components/SyncStatusBar.tsx +1 -1
- package/_standalone/components/TableOfContents.tsx +7 -0
- package/_standalone/components/setup/StepAI.tsx +66 -3
- package/_standalone/components/setup/StepDots.tsx +18 -10
- package/_standalone/components/setup/StepKB.tsx +26 -0
- package/_standalone/components/setup/constants.tsx +4 -3
- package/_standalone/data/skills/mindos/SKILL.md +4 -22
- package/_standalone/data/skills/mindos-zh/SKILL.md +2 -20
- package/_standalone/lib/agent/context.ts +29 -20
- package/_standalone/lib/i18n/modules/onboarding.ts +14 -6
- package/_standalone/tsconfig.tsbuildinfo +1 -1
- package/app/app/api/ask/route.ts +77 -24
- package/app/app/api/bootstrap/route.ts +4 -0
- package/app/app/api/export/route.ts +5 -0
- package/app/app/api/git/route.ts +2 -1
- package/app/app/api/recent-files/route.ts +3 -2
- package/app/app/api/setup/route.ts +1 -0
- package/app/components/SyncStatusBar.tsx +1 -1
- package/app/components/TableOfContents.tsx +7 -0
- package/app/components/setup/StepAI.tsx +66 -3
- package/app/components/setup/StepDots.tsx +18 -10
- package/app/components/setup/StepKB.tsx +26 -0
- package/app/components/setup/constants.tsx +4 -3
- package/app/components/setup/index.tsx +22 -32
- package/app/data/skills/mindos/SKILL.md +4 -22
- package/app/data/skills/mindos-zh/SKILL.md +2 -20
- package/app/lib/acp/subprocess.ts +3 -2
- package/app/lib/agent/context.ts +29 -20
- package/app/lib/fs.ts +77 -0
- package/app/lib/i18n/modules/onboarding.ts +14 -6
- package/app/lib/settings.ts +6 -0
- package/bin/cli.js +14 -4
- package/bin/lib/stop.js +5 -2
- package/package.json +6 -2
- package/scripts/setup.js +16 -0
- package/skills/mindos/SKILL.md +4 -22
- package/skills/mindos/references/write-supplement.md +35 -0
- package/skills/mindos-zh/SKILL.md +2 -20
- package/skills/mindos-zh/references/README.md +2 -1
- package/skills/mindos-zh/references/write-supplement.md +35 -0
- package/_standalone/.next/static/chunks/app/setup/page-f8a85accc3be554f.js +0 -1
- /package/_standalone/.next/static/{K1Y12u2jxCR7efUgJVpyl → VWZDVb8DL1Pbykh9EOj_Y}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{K1Y12u2jxCR7efUgJVpyl → VWZDVb8DL1Pbykh9EOj_Y}/_ssgManifest.js +0 -0
package/app/app/api/ask/route.ts
CHANGED
|
@@ -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
|
-
|
|
59
|
-
|
|
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
|
|
100
|
+
return (e as MessageUpdateEvent).assistantMessageEvent?.delta ?? '';
|
|
64
101
|
}
|
|
65
102
|
|
|
66
|
-
function isThinkingDeltaEvent(e: AgentEvent):
|
|
67
|
-
return e.type === 'message_update' && (e as
|
|
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
|
|
108
|
+
return (e as MessageUpdateEvent).assistantMessageEvent?.delta ?? '';
|
|
72
109
|
}
|
|
73
110
|
|
|
74
|
-
function isToolExecutionStartEvent(e: AgentEvent):
|
|
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
|
|
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):
|
|
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
|
|
129
|
+
const evt = e as ToolExecEndEvent;
|
|
93
130
|
const outputText = evt.result?.content
|
|
94
|
-
?.filter((p:
|
|
95
|
-
.map((p:
|
|
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):
|
|
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: (
|
|
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(
|
|
219
|
-
(args as
|
|
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
|
|
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
|
|
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)
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 });
|
package/app/app/api/git/route.ts
CHANGED
|
@@ -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
|
|
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
|
|
8
|
-
const
|
|
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 =
|
|
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 {
|
|
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
|
-
{
|
|
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
|
|
24
|
-
title={(disabled || i
|
|
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
|
|
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 =
|
|
10
|
+
export const TOTAL_STEPS = 4;
|
|
11
11
|
export const STEP_KB = 0;
|
|
12
|
-
export const
|
|
13
|
-
export const STEP_AGENTS =
|
|
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,
|
|
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:
|
|
126
|
-
mcpPort:
|
|
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
|
|
185
|
+
// Auto-check ports when entering AI step (ports are in Advanced section)
|
|
188
186
|
useEffect(() => {
|
|
189
|
-
if (step ===
|
|
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
|
|
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)
|
|
260
|
-
|
|
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
|
|
264
|
-
return
|
|
265
|
-
|
|
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 &&
|
|
371
|
-
|
|
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}
|
|
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 ===
|
|
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 ===
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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
|
|