@geminilight/mindos 0.5.69 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/app/api/ask/route.ts +122 -92
- package/app/app/api/file/import/route.ts +197 -0
- package/app/app/api/mcp/agents/route.ts +53 -2
- package/app/app/api/mcp/status/route.ts +1 -1
- package/app/app/api/skills/route.ts +10 -114
- package/app/components/ActivityBar.tsx +5 -7
- package/app/components/CreateSpaceModal.tsx +31 -6
- package/app/components/FileTree.tsx +68 -11
- package/app/components/GuideCard.tsx +197 -131
- package/app/components/HomeContent.tsx +85 -18
- package/app/components/ImportModal.tsx +415 -0
- package/app/components/OnboardingView.tsx +9 -0
- package/app/components/Panel.tsx +4 -2
- package/app/components/SidebarLayout.tsx +96 -8
- package/app/components/SpaceInitToast.tsx +173 -0
- package/app/components/TableOfContents.tsx +1 -0
- package/app/components/agents/AgentDetailContent.tsx +69 -45
- package/app/components/agents/AgentsContentPage.tsx +2 -1
- package/app/components/agents/AgentsMcpSection.tsx +16 -12
- package/app/components/agents/AgentsOverviewSection.tsx +37 -36
- package/app/components/agents/AgentsPrimitives.tsx +41 -20
- package/app/components/agents/AgentsSkillsSection.tsx +16 -7
- package/app/components/agents/SkillDetailPopover.tsx +11 -11
- package/app/components/agents/agents-content-model.ts +16 -8
- package/app/components/ask/AskContent.tsx +148 -50
- package/app/components/ask/MentionPopover.tsx +16 -8
- package/app/components/ask/SlashCommandPopover.tsx +62 -0
- package/app/components/panels/AgentsPanelAgentGroups.tsx +8 -6
- package/app/components/panels/AgentsPanelHubNav.tsx +3 -3
- package/app/components/panels/DiscoverPanel.tsx +88 -2
- package/app/components/settings/KnowledgeTab.tsx +61 -0
- package/app/components/walkthrough/steps.ts +11 -6
- package/app/hooks/useFileImport.ts +191 -0
- package/app/hooks/useFileUpload.ts +11 -0
- package/app/hooks/useMention.ts +14 -6
- package/app/hooks/useSlashCommand.ts +114 -0
- package/app/lib/actions.ts +79 -2
- package/app/lib/agent/index.ts +1 -1
- package/app/lib/agent/prompt.ts +2 -0
- package/app/lib/agent/tools.ts +252 -0
- package/app/lib/core/create-space.ts +11 -4
- package/app/lib/core/file-convert.ts +97 -0
- package/app/lib/core/index.ts +1 -1
- package/app/lib/core/organize.ts +105 -0
- package/app/lib/i18n-en.ts +102 -46
- package/app/lib/i18n-zh.ts +101 -45
- package/app/lib/mcp-agents.ts +8 -0
- package/app/lib/pdf-extract.ts +33 -0
- package/app/lib/pi-integration/extensions.ts +45 -0
- package/app/lib/pi-integration/mcporter.ts +219 -0
- package/app/lib/pi-integration/session-store.ts +62 -0
- package/app/lib/pi-integration/skills.ts +116 -0
- package/app/lib/settings.ts +1 -1
- package/app/next-env.d.ts +1 -1
- package/app/next.config.ts +1 -1
- package/app/package.json +2 -0
- package/mcp/src/index.ts +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { Type } from '@sinclair/typebox';
|
|
4
|
+
import type { AgentTool, AgentToolResult } from '@mariozechner/pi-agent-core';
|
|
5
|
+
import {
|
|
6
|
+
createRuntime,
|
|
7
|
+
createCallResult,
|
|
8
|
+
type Runtime,
|
|
9
|
+
type ServerToolInfo,
|
|
10
|
+
} from 'mcporter';
|
|
11
|
+
|
|
12
|
+
export interface McporterServerSummary {
|
|
13
|
+
name: string;
|
|
14
|
+
status: string;
|
|
15
|
+
durationMs?: number;
|
|
16
|
+
transport?: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
issue?: { kind?: string; rawMessage?: string };
|
|
19
|
+
source?: { kind?: string; path?: string; importKind?: string };
|
|
20
|
+
tools?: McporterToolSummary[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface McporterToolSummary {
|
|
24
|
+
name: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
inputSchema?: Record<string, unknown>;
|
|
27
|
+
options?: Array<Record<string, unknown>>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface McporterServerList {
|
|
31
|
+
mode?: string;
|
|
32
|
+
counts?: Record<string, number>;
|
|
33
|
+
servers: McporterServerSummary[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function textResult(text: string): AgentToolResult<Record<string, never>> {
|
|
37
|
+
return { content: [{ type: 'text', text }], details: {} };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeNameSegment(value: string): string {
|
|
41
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
|
|
42
|
+
return normalized || 'tool';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toToolSchema(inputSchema?: Record<string, unknown>) {
|
|
46
|
+
if (!inputSchema || Object.keys(inputSchema).length === 0) {
|
|
47
|
+
return Type.Object({}, { additionalProperties: true });
|
|
48
|
+
}
|
|
49
|
+
return Type.Unsafe(inputSchema as any);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function extractJsonObject(text: string): string {
|
|
53
|
+
const first = text.indexOf('{');
|
|
54
|
+
const last = text.lastIndexOf('}');
|
|
55
|
+
if (first === -1 || last === -1 || last < first) {
|
|
56
|
+
throw new Error('Failed to parse mcporter output as JSON');
|
|
57
|
+
}
|
|
58
|
+
return text.slice(first, last + 1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── Singleton mcporter Runtime ──────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
const MCP_CONFIG_PATH = path.join(os.homedir(), '.mindos', 'mcp.json');
|
|
64
|
+
const TOOL_TIMEOUT_MS = 30_000;
|
|
65
|
+
|
|
66
|
+
let _runtime: Runtime | null = null;
|
|
67
|
+
let _runtimePromise: Promise<Runtime | null> | null = null;
|
|
68
|
+
|
|
69
|
+
async function getRuntime(): Promise<Runtime | null> {
|
|
70
|
+
if (_runtime) return _runtime;
|
|
71
|
+
if (_runtimePromise) return _runtimePromise;
|
|
72
|
+
|
|
73
|
+
_runtimePromise = (async () => {
|
|
74
|
+
try {
|
|
75
|
+
const rt = await createRuntime({
|
|
76
|
+
configPath: MCP_CONFIG_PATH,
|
|
77
|
+
clientInfo: { name: 'mindos', version: '1.0.0' },
|
|
78
|
+
});
|
|
79
|
+
_runtime = rt;
|
|
80
|
+
return rt;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.warn('[mcporter] Failed to create runtime:', err instanceof Error ? err.message : err);
|
|
83
|
+
_runtimePromise = null;
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
return _runtimePromise;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (typeof process !== 'undefined') {
|
|
91
|
+
const cleanup = () => {
|
|
92
|
+
if (_runtime) {
|
|
93
|
+
_runtime.close().catch(() => {});
|
|
94
|
+
_runtime = null;
|
|
95
|
+
_runtimePromise = null;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
process.on('exit', cleanup);
|
|
99
|
+
process.on('SIGINT', cleanup);
|
|
100
|
+
process.on('SIGTERM', cleanup);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function listMcporterServers(): Promise<McporterServerList> {
|
|
104
|
+
const rt = await getRuntime();
|
|
105
|
+
if (!rt) return { servers: [] };
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const names = rt.listServers();
|
|
109
|
+
if (names.length === 0) return { servers: [] };
|
|
110
|
+
|
|
111
|
+
const servers: McporterServerSummary[] = await Promise.all(
|
|
112
|
+
names.map(async (name) => {
|
|
113
|
+
try {
|
|
114
|
+
const def = rt.getDefinition(name);
|
|
115
|
+
const transport = def.command.kind;
|
|
116
|
+
const tools = await rt.listTools(name, { includeSchema: false });
|
|
117
|
+
return {
|
|
118
|
+
name,
|
|
119
|
+
status: 'ok',
|
|
120
|
+
transport,
|
|
121
|
+
tools: tools.map(toToolSummary),
|
|
122
|
+
};
|
|
123
|
+
} catch (err) {
|
|
124
|
+
return {
|
|
125
|
+
name,
|
|
126
|
+
status: 'error',
|
|
127
|
+
error: err instanceof Error ? err.message : String(err),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return { servers };
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.warn('[mcporter] listServers failed:', err instanceof Error ? err.message : err);
|
|
136
|
+
return { servers: [] };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function toToolSummary(tool: ServerToolInfo): McporterToolSummary {
|
|
141
|
+
return {
|
|
142
|
+
name: tool.name,
|
|
143
|
+
description: tool.description,
|
|
144
|
+
inputSchema: tool.inputSchema as Record<string, unknown> | undefined,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function listMcporterTools(serverName: string): Promise<McporterServerSummary> {
|
|
149
|
+
const rt = await getRuntime();
|
|
150
|
+
if (!rt) return { name: serverName, status: 'not_configured', tools: [] };
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const tools = await rt.listTools(serverName, { includeSchema: true });
|
|
154
|
+
return {
|
|
155
|
+
name: serverName,
|
|
156
|
+
status: 'ok',
|
|
157
|
+
tools: tools.map(toToolSummary),
|
|
158
|
+
};
|
|
159
|
+
} catch (err) {
|
|
160
|
+
return {
|
|
161
|
+
name: serverName,
|
|
162
|
+
status: 'error',
|
|
163
|
+
error: err instanceof Error ? err.message : String(err),
|
|
164
|
+
tools: [],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function callMcporterTool(
|
|
170
|
+
serverName: string,
|
|
171
|
+
toolName: string,
|
|
172
|
+
args: Record<string, unknown>,
|
|
173
|
+
): Promise<string> {
|
|
174
|
+
const rt = await getRuntime();
|
|
175
|
+
if (!rt) throw new Error(`MCP runtime not available. Ensure ~/.mindos/mcp.json is configured.`);
|
|
176
|
+
|
|
177
|
+
const raw = await rt.callTool(serverName, toolName, {
|
|
178
|
+
args,
|
|
179
|
+
timeoutMs: TOOL_TIMEOUT_MS,
|
|
180
|
+
});
|
|
181
|
+
const result = createCallResult(raw);
|
|
182
|
+
return result.text('\n') ?? JSON.stringify(raw);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function createMcporterAgentTools(servers: McporterServerSummary[]): AgentTool<any>[] {
|
|
186
|
+
const seenNames = new Set<string>();
|
|
187
|
+
const tools: AgentTool<any>[] = [];
|
|
188
|
+
|
|
189
|
+
for (const server of servers) {
|
|
190
|
+
if (server.status !== 'ok' || !server.tools?.length) continue;
|
|
191
|
+
|
|
192
|
+
for (const tool of server.tools) {
|
|
193
|
+
const baseName = `mcp__${normalizeNameSegment(server.name)}__${normalizeNameSegment(tool.name)}`;
|
|
194
|
+
let name = baseName;
|
|
195
|
+
let suffix = 2;
|
|
196
|
+
while (seenNames.has(name)) {
|
|
197
|
+
name = `${baseName}_${suffix++}`;
|
|
198
|
+
}
|
|
199
|
+
seenNames.add(name);
|
|
200
|
+
|
|
201
|
+
tools.push({
|
|
202
|
+
name,
|
|
203
|
+
label: `MCP ${server.name}: ${tool.name}`,
|
|
204
|
+
description: `MCP tool "${tool.name}" from server "${server.name}".${tool.description ? ` ${tool.description}` : ''}`,
|
|
205
|
+
parameters: toToolSchema(tool.inputSchema),
|
|
206
|
+
execute: async (_toolCallId, params) => {
|
|
207
|
+
try {
|
|
208
|
+
const output = await callMcporterTool(server.name, tool.name, (params ?? {}) as Record<string, unknown>);
|
|
209
|
+
return textResult(output || '(empty MCP response)');
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return textResult(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return tools;
|
|
219
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { SessionManager } from '@mariozechner/pi-coding-agent';
|
|
5
|
+
|
|
6
|
+
function getSessionsRoot(): string {
|
|
7
|
+
return path.join(os.homedir(), '.mindos', 'sessions');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getSessionDir(sessionId: string): string {
|
|
11
|
+
const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
12
|
+
return path.join(getSessionsRoot(), safe);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function sessionDirExists(sessionId: string): boolean {
|
|
16
|
+
const sessionDir = getSessionDir(sessionId);
|
|
17
|
+
if (!fs.existsSync(sessionDir)) return false;
|
|
18
|
+
// Check if there's at least one .jsonl file
|
|
19
|
+
try {
|
|
20
|
+
return fs.readdirSync(sessionDir).some((f) => f.endsWith('.jsonl'));
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getOrCreateSessionManager(
|
|
27
|
+
sessionId: string | undefined,
|
|
28
|
+
cwd: string,
|
|
29
|
+
): SessionManager {
|
|
30
|
+
if (!sessionId) {
|
|
31
|
+
return SessionManager.inMemory(cwd);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const sessionDir = getSessionDir(sessionId);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
fs.mkdirSync(sessionDir, { recursive: true });
|
|
38
|
+
|
|
39
|
+
if (sessionDirExists(sessionId)) {
|
|
40
|
+
// Reuse the most recent session in this directory
|
|
41
|
+
return SessionManager.continueRecent(cwd, sessionDir);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Brand new — create fresh session file
|
|
45
|
+
return SessionManager.create(cwd, sessionDir);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(`[session-store] Failed to open/create session ${sessionId}, falling back to inMemory:`, error);
|
|
48
|
+
return SessionManager.inMemory(cwd);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function deleteSessionDir(sessionId: string): boolean {
|
|
53
|
+
const sessionDir = getSessionDir(sessionId);
|
|
54
|
+
if (!fs.existsSync(sessionDir)) return false;
|
|
55
|
+
try {
|
|
56
|
+
fs.rmSync(sessionDir, { recursive: true, force: true });
|
|
57
|
+
return true;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(`[session-store] Failed to delete session dir ${sessionDir}:`, error);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export interface PiSkillInfo {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
path: string;
|
|
9
|
+
source: 'builtin' | 'user';
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
editable: boolean;
|
|
12
|
+
origin: 'app-builtin' | 'project-builtin' | 'mindos-user' | 'mindos-global';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ScanSkillOptions {
|
|
16
|
+
projectRoot: string;
|
|
17
|
+
mindRoot: string;
|
|
18
|
+
disabledSkills?: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function parseSkillMd(content: string): { name: string; description: string } {
|
|
22
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
23
|
+
if (!match) return { name: '', description: '' };
|
|
24
|
+
const yaml = match[1];
|
|
25
|
+
const nameMatch = yaml.match(/^name:\s*(.+)/m);
|
|
26
|
+
const descMatch = yaml.match(/^description:\s*>?\s*\n?([\s\S]*?)(?=\n\w|\n---)/m);
|
|
27
|
+
const name = nameMatch ? nameMatch[1].trim() : '';
|
|
28
|
+
let description = '';
|
|
29
|
+
if (descMatch) {
|
|
30
|
+
description = descMatch[1].trim().split('\n').map((l) => l.trim()).join(' ').slice(0, 200);
|
|
31
|
+
} else {
|
|
32
|
+
const simpleDesc = yaml.match(/^description:\s*(.+)/m);
|
|
33
|
+
if (simpleDesc) description = simpleDesc[1].trim().slice(0, 200);
|
|
34
|
+
}
|
|
35
|
+
return { name, description };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getPiSkillSearchDirs(projectRoot: string, mindRoot: string) {
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
origin: 'app-builtin' as const,
|
|
42
|
+
dir: path.join(projectRoot, 'app', 'data', 'skills'),
|
|
43
|
+
pathLabel: 'app/data/skills',
|
|
44
|
+
source: 'builtin' as const,
|
|
45
|
+
editable: false,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
origin: 'project-builtin' as const,
|
|
49
|
+
dir: path.join(projectRoot, 'skills'),
|
|
50
|
+
pathLabel: 'skills',
|
|
51
|
+
source: 'builtin' as const,
|
|
52
|
+
editable: false,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
origin: 'mindos-user' as const,
|
|
56
|
+
dir: path.join(mindRoot, '.skills'),
|
|
57
|
+
pathLabel: '{mindRoot}/.skills',
|
|
58
|
+
source: 'user' as const,
|
|
59
|
+
editable: true,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
origin: 'mindos-global' as const,
|
|
63
|
+
dir: path.join(os.homedir(), '.mindos', 'skills'),
|
|
64
|
+
pathLabel: '~/.mindos/skills',
|
|
65
|
+
source: 'user' as const,
|
|
66
|
+
editable: true,
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function scanSkillDirs(options: ScanSkillOptions): PiSkillInfo[] {
|
|
72
|
+
const { projectRoot, mindRoot, disabledSkills = [] } = options;
|
|
73
|
+
const skills: PiSkillInfo[] = [];
|
|
74
|
+
const seen = new Set<string>();
|
|
75
|
+
|
|
76
|
+
for (const sourceDef of getPiSkillSearchDirs(projectRoot, mindRoot)) {
|
|
77
|
+
if (!fs.existsSync(sourceDef.dir)) continue;
|
|
78
|
+
|
|
79
|
+
for (const entry of fs.readdirSync(sourceDef.dir, { withFileTypes: true })) {
|
|
80
|
+
if (!entry.isDirectory()) continue;
|
|
81
|
+
const skillFile = path.join(sourceDef.dir, entry.name, 'SKILL.md');
|
|
82
|
+
if (!fs.existsSync(skillFile)) continue;
|
|
83
|
+
|
|
84
|
+
const content = fs.readFileSync(skillFile, 'utf-8');
|
|
85
|
+
const { name, description } = parseSkillMd(content);
|
|
86
|
+
const skillName = name || entry.name;
|
|
87
|
+
if (!skillName || seen.has(skillName)) continue;
|
|
88
|
+
|
|
89
|
+
seen.add(skillName);
|
|
90
|
+
skills.push({
|
|
91
|
+
name: skillName,
|
|
92
|
+
description,
|
|
93
|
+
path: `${sourceDef.pathLabel}/${entry.name}/SKILL.md`,
|
|
94
|
+
source: sourceDef.source,
|
|
95
|
+
enabled: !disabledSkills.includes(skillName),
|
|
96
|
+
editable: sourceDef.editable,
|
|
97
|
+
origin: sourceDef.origin,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return skills;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function readSkillContentByName(name: string, options: Omit<ScanSkillOptions, 'disabledSkills'>): string | null {
|
|
106
|
+
const { projectRoot, mindRoot } = options;
|
|
107
|
+
|
|
108
|
+
for (const sourceDef of getPiSkillSearchDirs(projectRoot, mindRoot)) {
|
|
109
|
+
const file = path.join(sourceDef.dir, name, 'SKILL.md');
|
|
110
|
+
if (fs.existsSync(file)) {
|
|
111
|
+
return fs.readFileSync(file, 'utf-8');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return null;
|
|
116
|
+
}
|
package/app/lib/settings.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface GuideState {
|
|
|
32
32
|
step1Done: boolean; // 至少浏览过 1 个文件
|
|
33
33
|
askedAI: boolean; // 至少发过 1 条 AI 消息
|
|
34
34
|
nextStepIndex: number; // 0=C2, 1=C3, 2=C4, 3=全部完成
|
|
35
|
-
walkthroughStep?: number; //
|
|
35
|
+
walkthroughStep?: number; // undefined=not started, 0-3=current step, 4=completed
|
|
36
36
|
walkthroughDismissed?: boolean; // user skipped walkthrough
|
|
37
37
|
}
|
|
38
38
|
|
package/app/next-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/types/routes.d.ts";
|
|
3
|
+
import "./.next/dev/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
package/app/next.config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import path from "path";
|
|
|
3
3
|
|
|
4
4
|
const nextConfig: NextConfig = {
|
|
5
5
|
transpilePackages: ['github-slugger'],
|
|
6
|
-
serverExternalPackages: ['chokidar', 'openai', '@mariozechner/pi-ai', '@mariozechner/pi-agent-core'],
|
|
6
|
+
serverExternalPackages: ['chokidar', 'openai', '@mariozechner/pi-ai', '@mariozechner/pi-agent-core', '@mariozechner/pi-coding-agent', 'mcporter'],
|
|
7
7
|
output: 'standalone',
|
|
8
8
|
outputFileTracingRoot: path.join(__dirname),
|
|
9
9
|
turbopack: {
|
package/app/package.json
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"@codemirror/view": "^6.39.16",
|
|
20
20
|
"@mariozechner/pi-agent-core": "^0.60.0",
|
|
21
21
|
"@mariozechner/pi-ai": "^0.60.0",
|
|
22
|
+
"@mariozechner/pi-coding-agent": "^0.61.1",
|
|
22
23
|
"@sinclair/typebox": "^0.34.33",
|
|
23
24
|
"@tiptap/extension-image": "^3.20.1",
|
|
24
25
|
"@tiptap/extension-link": "^3.20.1",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"github-slugger": "^2.0.0",
|
|
41
42
|
"lucide-react": "^0.577.0",
|
|
42
43
|
"nanoid": "^5.1.0",
|
|
44
|
+
"mcporter": "^0.7.3",
|
|
43
45
|
"next": "16.1.6",
|
|
44
46
|
"papaparse": "^5.5.3",
|
|
45
47
|
"pdfjs-dist": "^4.10.38",
|
package/mcp/src/index.ts
CHANGED
|
@@ -203,6 +203,35 @@ server.registerTool("mindos_create_file", {
|
|
|
203
203
|
} catch (e) { logOp("mindos_create_file", { path }, "error", String(e)); return error(String(e)); }
|
|
204
204
|
});
|
|
205
205
|
|
|
206
|
+
// ── mindos_batch_create_files ────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
server.registerTool("mindos_batch_create_files", {
|
|
209
|
+
title: "Batch Create Files",
|
|
210
|
+
description:
|
|
211
|
+
"Create multiple new files in a single operation. Only .md and .csv files allowed. Returns a summary of created files and any errors.",
|
|
212
|
+
inputSchema: z.object({
|
|
213
|
+
files: z.array(z.object({
|
|
214
|
+
path: z.string().min(1).regex(/\.(md|csv)$/).describe("Relative file path (must end in .md or .csv)"),
|
|
215
|
+
content: z.string().default("").describe("Initial file content"),
|
|
216
|
+
})).min(1).max(50).describe("List of files to create (max 50 per call)"),
|
|
217
|
+
}),
|
|
218
|
+
}, async ({ files }) => {
|
|
219
|
+
const created: string[] = [];
|
|
220
|
+
const errors: string[] = [];
|
|
221
|
+
for (const file of files) {
|
|
222
|
+
try {
|
|
223
|
+
await post("/api/file", { op: "create_file", path: file.path, content: file.content });
|
|
224
|
+
created.push(file.path);
|
|
225
|
+
} catch (e) {
|
|
226
|
+
errors.push(`${file.path}: ${String(e)}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
let msg = `Batch creation complete.\nCreated ${created.length} file(s): ${created.join(", ")}`;
|
|
230
|
+
if (errors.length > 0) msg += `\n\nFailed to create ${errors.length} file(s):\n${errors.join("\n")}`;
|
|
231
|
+
logOp("mindos_batch_create_files", { count: files.length }, created.length === files.length ? "ok" : "error", msg.slice(0, 200));
|
|
232
|
+
return created.length === files.length ? ok(msg) : error(msg);
|
|
233
|
+
});
|
|
234
|
+
|
|
206
235
|
// ── mindos_create_space ─────────────────────────────────────────────────────
|
|
207
236
|
|
|
208
237
|
server.registerTool("mindos_create_space", {
|
package/package.json
CHANGED