@miphamai/cli 0.5.12 → 0.6.1
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/bin/mipham.ts +11 -0
- package/dist/mipham +0 -0
- package/package.json +1 -1
- package/skills/standard/web-access.SKILL.md +54 -0
- package/src/commands/loop-scaffold.ts +342 -0
- package/src/config/loader.ts +65 -1
- package/src/core/engine.ts +1 -0
- package/src/index.tsx +5 -0
- package/src/plugin/plugin-registry.ts +69 -0
- package/src/providers/openai-compat.ts +85 -15
- package/src/shared/constants.ts +9 -0
- package/src/skills/registry.ts +26 -6
- package/src/ui/app.tsx +5 -1
- package/src/ui/commands.ts +765 -6
package/bin/mipham.ts
CHANGED
|
@@ -174,6 +174,17 @@ async function runPluginCLI(): Promise<boolean> {
|
|
|
174
174
|
process.exit(disabled ? 0 : 1)
|
|
175
175
|
})
|
|
176
176
|
|
|
177
|
+
program
|
|
178
|
+
.command('install-npm <package>')
|
|
179
|
+
.description('Install a plugin from the npm registry')
|
|
180
|
+
.action(async (packageName: string) => {
|
|
181
|
+
const { PluginManager } = await import('../src/plugin/plugin-manager')
|
|
182
|
+
const manager = new PluginManager()
|
|
183
|
+
const result = manager.installFromNpm(packageName)
|
|
184
|
+
console.log(result.message)
|
|
185
|
+
process.exit(result.success ? 0 : 1)
|
|
186
|
+
})
|
|
187
|
+
|
|
177
188
|
await program.parseAsync(process.argv)
|
|
178
189
|
return true
|
|
179
190
|
}
|
package/dist/mipham
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-access
|
|
3
|
+
description: Web access, scraping, browser automation, and online content retrieval — search engines, page fetching, login-required sites, social media
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Web Access Skill
|
|
8
|
+
|
|
9
|
+
Handle all network operations: search, web scraping, authenticated browsing, and dynamic page rendering.
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- **Web search**: Finding current information, documentation, news
|
|
14
|
+
- **Page scraping**: Extracting content from web pages
|
|
15
|
+
- **Authenticated access**: Sites requiring login (via browser automation)
|
|
16
|
+
- **Social media**: Content from Xiaohongshu, Weibo, Twitter, etc.
|
|
17
|
+
- **Dynamic content**: JavaScript-rendered pages requiring a real browser
|
|
18
|
+
- **API interaction**: REST/GraphQL endpoints with proper auth
|
|
19
|
+
|
|
20
|
+
## Tools
|
|
21
|
+
|
|
22
|
+
### WebSearch
|
|
23
|
+
|
|
24
|
+
General web search for current information. Use for:
|
|
25
|
+
|
|
26
|
+
- Documentation lookups, API references
|
|
27
|
+
- News and current events
|
|
28
|
+
- Technical troubleshooting
|
|
29
|
+
- Technology comparisons
|
|
30
|
+
|
|
31
|
+
### WebFetch
|
|
32
|
+
|
|
33
|
+
Fetch and parse a single URL. Use for:
|
|
34
|
+
|
|
35
|
+
- Reading documentation pages
|
|
36
|
+
- Checking API responses
|
|
37
|
+
- Extracting article content
|
|
38
|
+
- Verifying links
|
|
39
|
+
|
|
40
|
+
### Browser Automation
|
|
41
|
+
|
|
42
|
+
For sites requiring login or JavaScript rendering:
|
|
43
|
+
|
|
44
|
+
- Authenticated sessions
|
|
45
|
+
- Single-page applications (SPAs)
|
|
46
|
+
- Form submissions and multi-step workflows
|
|
47
|
+
|
|
48
|
+
## Security Rules
|
|
49
|
+
|
|
50
|
+
- Never submit credentials without explicit user approval
|
|
51
|
+
- Respect robots.txt and rate limiting
|
|
52
|
+
- Do not scrape PII or sensitive data
|
|
53
|
+
- Validate all URLs against SSRF before fetching
|
|
54
|
+
- Only HTTPS for remote requests
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mipham Code — LoopKit Scaffold
|
|
3
|
+
*
|
|
4
|
+
* Creates a LoopKit Vault project structure under .mipham/.
|
|
5
|
+
* Mirrors the Claude Code .claude/ convention adapted for Mipham Code.
|
|
6
|
+
*
|
|
7
|
+
* Reference: LoopKit Vault structure
|
|
8
|
+
* .claude/CLAUDE.md, settings.json, hooks/, agents/, skills/<9 domains>/
|
|
9
|
+
* .mcp.json, MEMORY.md, run.sh, install.sh, README.md
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { mkdirSync, writeFileSync, existsSync, chmodSync } from 'node:fs'
|
|
13
|
+
import { join, resolve } from 'node:path'
|
|
14
|
+
|
|
15
|
+
export interface ScaffoldResult {
|
|
16
|
+
created: string[]
|
|
17
|
+
skipped: string[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** 9 skill domain directories matching the LoopKit Vault convention */
|
|
21
|
+
const SKILL_DOMAINS = [
|
|
22
|
+
'agents-llm',
|
|
23
|
+
'debug',
|
|
24
|
+
'security',
|
|
25
|
+
'frontend',
|
|
26
|
+
'testing',
|
|
27
|
+
'refactor',
|
|
28
|
+
'docs',
|
|
29
|
+
'data',
|
|
30
|
+
'git-ops',
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
function ensureDir(dir: string, created: string[], _skipped: string[]): void {
|
|
34
|
+
if (!existsSync(dir)) {
|
|
35
|
+
mkdirSync(dir, { recursive: true })
|
|
36
|
+
created.push(dir)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function writeTemplate(
|
|
41
|
+
path: string,
|
|
42
|
+
content: string,
|
|
43
|
+
executable: boolean,
|
|
44
|
+
created: string[],
|
|
45
|
+
skipped: string[],
|
|
46
|
+
): void {
|
|
47
|
+
if (existsSync(path)) {
|
|
48
|
+
skipped.push(path)
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
writeFileSync(path, content, 'utf-8')
|
|
52
|
+
if (executable) {
|
|
53
|
+
chmodSync(path, 0o755)
|
|
54
|
+
}
|
|
55
|
+
created.push(path)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create the LoopKit Vault project structure at the given base path.
|
|
60
|
+
*
|
|
61
|
+
* Directory tree created:
|
|
62
|
+
* ```
|
|
63
|
+
* <basePath>/
|
|
64
|
+
* ├── .mipham/
|
|
65
|
+
* │ ├── CLAUDE.md
|
|
66
|
+
* │ ├── settings.json
|
|
67
|
+
* │ ├── hooks/
|
|
68
|
+
* │ │ ├── pre-tool-use.sh
|
|
69
|
+
* │ │ ├── post-tool-use.sh
|
|
70
|
+
* │ │ └── stop.sh
|
|
71
|
+
* │ ├── agents/
|
|
72
|
+
* │ │ └── verifier.md
|
|
73
|
+
* │ └── skills/
|
|
74
|
+
* │ ├── agents-llm/
|
|
75
|
+
* │ ├── debug/
|
|
76
|
+
* │ ├── security/
|
|
77
|
+
* │ ├── frontend/
|
|
78
|
+
* │ ├── testing/
|
|
79
|
+
* │ ├── refactor/
|
|
80
|
+
* │ ├── docs/
|
|
81
|
+
* │ ├── data/
|
|
82
|
+
* │ └── git-ops/
|
|
83
|
+
* ├── .mcp.json
|
|
84
|
+
* ├── MEMORY.md
|
|
85
|
+
* ├── README.md
|
|
86
|
+
* ├── run.sh
|
|
87
|
+
* └── install.sh
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function scaffoldLoopKit(basePath: string): ScaffoldResult {
|
|
91
|
+
const created: string[] = []
|
|
92
|
+
const skipped: string[] = []
|
|
93
|
+
|
|
94
|
+
const resolved = resolve(basePath.replace(/^~/, process.env.HOME || '~'))
|
|
95
|
+
const miphamDir = join(resolved, '.mipham')
|
|
96
|
+
|
|
97
|
+
// ── .mipham/ root ──
|
|
98
|
+
ensureDir(miphamDir, created, skipped)
|
|
99
|
+
|
|
100
|
+
// ── .mipham/CLAUDE.md ──
|
|
101
|
+
writeTemplate(join(miphamDir, 'CLAUDE.md'), TEMPLATES.claudeMd, false, created, skipped)
|
|
102
|
+
|
|
103
|
+
// ── .mipham/settings.json ──
|
|
104
|
+
writeTemplate(join(miphamDir, 'settings.json'), TEMPLATES.settingsJson, false, created, skipped)
|
|
105
|
+
|
|
106
|
+
// ── .mipham/hooks/ ──
|
|
107
|
+
const hooksDir = join(miphamDir, 'hooks')
|
|
108
|
+
ensureDir(hooksDir, created, skipped)
|
|
109
|
+
writeTemplate(join(hooksDir, 'pre-tool-use.sh'), TEMPLATES.preToolUse, true, created, skipped)
|
|
110
|
+
writeTemplate(join(hooksDir, 'post-tool-use.sh'), TEMPLATES.postToolUse, true, created, skipped)
|
|
111
|
+
writeTemplate(join(hooksDir, 'stop.sh'), TEMPLATES.stopHook, true, created, skipped)
|
|
112
|
+
|
|
113
|
+
// ── .mipham/agents/ ──
|
|
114
|
+
const agentsDir = join(miphamDir, 'agents')
|
|
115
|
+
ensureDir(agentsDir, created, skipped)
|
|
116
|
+
writeTemplate(join(agentsDir, 'verifier.md'), TEMPLATES.verifierAgent, false, created, skipped)
|
|
117
|
+
|
|
118
|
+
// ── .mipham/skills/<9 domains>/ ──
|
|
119
|
+
const skillsDir = join(miphamDir, 'skills')
|
|
120
|
+
ensureDir(skillsDir, created, skipped)
|
|
121
|
+
for (const domain of SKILL_DOMAINS) {
|
|
122
|
+
const domainDir = join(skillsDir, domain)
|
|
123
|
+
ensureDir(domainDir, created, skipped)
|
|
124
|
+
// Add .gitkeep so git tracks empty directories
|
|
125
|
+
writeTemplate(join(domainDir, '.gitkeep'), '', false, created, skipped)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── Root-level files ──
|
|
129
|
+
|
|
130
|
+
// .mcp.json
|
|
131
|
+
writeTemplate(join(resolved, '.mcp.json'), TEMPLATES.mcpJson, false, created, skipped)
|
|
132
|
+
|
|
133
|
+
// MEMORY.md
|
|
134
|
+
writeTemplate(join(resolved, 'MEMORY.md'), TEMPLATES.memoryMd, false, created, skipped)
|
|
135
|
+
|
|
136
|
+
// README.md
|
|
137
|
+
writeTemplate(join(resolved, 'README.md'), TEMPLATES.readmeMd, false, created, skipped)
|
|
138
|
+
|
|
139
|
+
// run.sh
|
|
140
|
+
writeTemplate(join(resolved, 'run.sh'), TEMPLATES.runSh, true, created, skipped)
|
|
141
|
+
|
|
142
|
+
// install.sh
|
|
143
|
+
writeTemplate(join(resolved, 'install.sh'), TEMPLATES.installSh, true, created, skipped)
|
|
144
|
+
|
|
145
|
+
return { created, skipped }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════
|
|
149
|
+
// Templates
|
|
150
|
+
// ═══════════════════════════════════════════════════════════════
|
|
151
|
+
|
|
152
|
+
const TEMPLATES = {
|
|
153
|
+
claudeMd: `# CLAUDE.md
|
|
154
|
+
|
|
155
|
+
> Mipham Code project instructions — defines how the AI assistant behaves in this project.
|
|
156
|
+
> Edit this file to customize: coding conventions, project structure, AI preferences.
|
|
157
|
+
|
|
158
|
+
## Project Overview
|
|
159
|
+
|
|
160
|
+
[Describe your project — what it does, who it's for, key technologies]
|
|
161
|
+
|
|
162
|
+
## Coding Conventions
|
|
163
|
+
|
|
164
|
+
- [Add your team's coding rules, style preferences, naming conventions]
|
|
165
|
+
- [Example: Use TypeScript strict mode, ESM modules, pnpm package manager]
|
|
166
|
+
|
|
167
|
+
## AI Interaction Preferences
|
|
168
|
+
|
|
169
|
+
- Response language: [English / 中文]
|
|
170
|
+
- Comment language: [English / 中文]
|
|
171
|
+
- Code style: [functional / OOP / mixed]
|
|
172
|
+
- Testing: [always write tests / only for critical paths]
|
|
173
|
+
`,
|
|
174
|
+
|
|
175
|
+
settingsJson:
|
|
176
|
+
JSON.stringify(
|
|
177
|
+
{
|
|
178
|
+
permissions: {
|
|
179
|
+
allow: [],
|
|
180
|
+
deny: [],
|
|
181
|
+
},
|
|
182
|
+
hooks: {
|
|
183
|
+
PreToolUse: [],
|
|
184
|
+
PostToolUse: [],
|
|
185
|
+
Stop: [],
|
|
186
|
+
SessionStart: [],
|
|
187
|
+
UserPromptSubmit: [],
|
|
188
|
+
Notification: [],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
null,
|
|
192
|
+
2,
|
|
193
|
+
) + '\n',
|
|
194
|
+
|
|
195
|
+
preToolUse: `#!/bin/bash
|
|
196
|
+
# PreToolUse hook — runs before each tool execution.
|
|
197
|
+
# Tool name passed as \$1, input JSON as \$2.
|
|
198
|
+
# Exit non-zero to block the tool.
|
|
199
|
+
# Write JSON to stdout to modify the tool input.
|
|
200
|
+
|
|
201
|
+
TOOL_NAME="\$1"
|
|
202
|
+
TOOL_INPUT="\$2"
|
|
203
|
+
|
|
204
|
+
echo "{\\"decision\\": \\"allow\\"}" >&2
|
|
205
|
+
exit 0
|
|
206
|
+
`,
|
|
207
|
+
|
|
208
|
+
postToolUse: `#!/bin/bash
|
|
209
|
+
# PostToolUse hook — runs after each tool execution.
|
|
210
|
+
# Tool name passed as \$1, result JSON as \$2.
|
|
211
|
+
|
|
212
|
+
TOOL_NAME="\$1"
|
|
213
|
+
TOOL_RESULT="\$2"
|
|
214
|
+
|
|
215
|
+
exit 0
|
|
216
|
+
`,
|
|
217
|
+
|
|
218
|
+
stopHook: `#!/bin/bash
|
|
219
|
+
# Stop hook — runs when the AI session ends.
|
|
220
|
+
# Use for cleanup, notifications, or saving state.
|
|
221
|
+
|
|
222
|
+
echo "Session ended at \$(date)" >&2
|
|
223
|
+
exit 0
|
|
224
|
+
`,
|
|
225
|
+
|
|
226
|
+
verifierAgent: `# Verifier Agent
|
|
227
|
+
|
|
228
|
+
> Pre-commit audit specialist. Dispatched BEFORE committing to verify changes.
|
|
229
|
+
|
|
230
|
+
## Role
|
|
231
|
+
|
|
232
|
+
Verify staged changes against coding standards, security rules, and best practices.
|
|
233
|
+
|
|
234
|
+
## When to Use
|
|
235
|
+
|
|
236
|
+
- Before committing code changes
|
|
237
|
+
- Quality gate in CI/CD pipelines
|
|
238
|
+
- Post-implementation review
|
|
239
|
+
|
|
240
|
+
## Instructions
|
|
241
|
+
|
|
242
|
+
1. Read the staged diff
|
|
243
|
+
2. Check for: security issues, code quality violations, test coverage gaps
|
|
244
|
+
3. Report findings with file:line citations
|
|
245
|
+
4. Return verdict: pass, needs-fix, or block
|
|
246
|
+
`,
|
|
247
|
+
|
|
248
|
+
mcpJson:
|
|
249
|
+
JSON.stringify(
|
|
250
|
+
{
|
|
251
|
+
mcpServers: {
|
|
252
|
+
filesystem: {
|
|
253
|
+
command: 'npx',
|
|
254
|
+
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
null,
|
|
259
|
+
2,
|
|
260
|
+
) + '\n',
|
|
261
|
+
|
|
262
|
+
memoryMd: `# MEMORY.md
|
|
263
|
+
|
|
264
|
+
> Persistent project memory — survives across AI sessions.
|
|
265
|
+
> Add important facts, decisions, and context here.
|
|
266
|
+
|
|
267
|
+
## Key Decisions
|
|
268
|
+
|
|
269
|
+
- [Date]: [Decision made and why]
|
|
270
|
+
|
|
271
|
+
## Project Context
|
|
272
|
+
|
|
273
|
+
- [Important context for AI to remember across sessions]
|
|
274
|
+
|
|
275
|
+
## Resources
|
|
276
|
+
|
|
277
|
+
- [Links, references, external documentation]
|
|
278
|
+
`,
|
|
279
|
+
|
|
280
|
+
readmeMd: `# [Project Name]
|
|
281
|
+
|
|
282
|
+
> Generated by Mipham Code /loop init scaffold.
|
|
283
|
+
|
|
284
|
+
## Quick Start
|
|
285
|
+
|
|
286
|
+
\`\`\`bash
|
|
287
|
+
./install.sh # Install dependencies
|
|
288
|
+
./run.sh # Start the project
|
|
289
|
+
\`\`\`
|
|
290
|
+
|
|
291
|
+
## Structure
|
|
292
|
+
|
|
293
|
+
- \`.mipham/\` — Mipham Code configuration (CLAUDE.md, settings, hooks, agents, skills)
|
|
294
|
+
- \`.mcp.json\` — MCP server configuration
|
|
295
|
+
- \`MEMORY.md\` — AI persistent memory
|
|
296
|
+
- \`run.sh\` — Project launcher
|
|
297
|
+
- \`install.sh\` — Dependency installer
|
|
298
|
+
|
|
299
|
+
## Learn More
|
|
300
|
+
|
|
301
|
+
- Mipham Code: https://mipham.ai/code
|
|
302
|
+
- MCP Protocol: https://modelcontextprotocol.io
|
|
303
|
+
`,
|
|
304
|
+
|
|
305
|
+
runSh: `#!/bin/bash
|
|
306
|
+
# Mipham Code — project launcher
|
|
307
|
+
# Generated by /loop init
|
|
308
|
+
|
|
309
|
+
set -e
|
|
310
|
+
|
|
311
|
+
echo "Starting project..."
|
|
312
|
+
echo "Run 'mipham' to launch the AI coding assistant."
|
|
313
|
+
|
|
314
|
+
# Uncomment to auto-start Mipham Code:
|
|
315
|
+
# mipham
|
|
316
|
+
`,
|
|
317
|
+
|
|
318
|
+
installSh: `#!/bin/bash
|
|
319
|
+
# Mipham Code — dependency installer
|
|
320
|
+
# Generated by /loop init
|
|
321
|
+
|
|
322
|
+
set -e
|
|
323
|
+
|
|
324
|
+
echo "Installing project dependencies..."
|
|
325
|
+
|
|
326
|
+
# Check for Bun
|
|
327
|
+
if command -v bun &> /dev/null; then
|
|
328
|
+
echo "✓ Bun found: \$(bun --version)"
|
|
329
|
+
else
|
|
330
|
+
echo "⚠ Bun not found. Install from https://bun.sh"
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
# Check for Mipham Code
|
|
334
|
+
if command -v mipham &> /dev/null; then
|
|
335
|
+
echo "✓ Mipham Code found: \$(mipham --version 2>&1 | head -1)"
|
|
336
|
+
else
|
|
337
|
+
echo "⚠ Mipham Code not found. Install: npm install -g @miphamai/cli"
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
echo "Done. Run ./run.sh to start."
|
|
341
|
+
`,
|
|
342
|
+
}
|
package/src/config/loader.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { join, dirname } from 'node:path'
|
|
11
11
|
import { homedir } from 'node:os'
|
|
12
12
|
import { parse as parseYaml } from 'yaml'
|
|
13
|
-
import type { MiphamConfig, ProviderConfig } from '../shared/index.ts'
|
|
13
|
+
import type { MiphamConfig, ProviderConfig, McpServerConfig } from '../shared/index.ts'
|
|
14
14
|
import { DEFAULT_CONFIG } from './defaults'
|
|
15
15
|
|
|
16
16
|
const MIPHAM_HOME = join(homedir(), '.mipham')
|
|
@@ -136,6 +136,54 @@ function tryRestoreFromBackup(configPath: string): boolean {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Load MCP servers from a .mcp.json file (Claude Code convention).
|
|
141
|
+
*
|
|
142
|
+
* Format:
|
|
143
|
+
* { "mcpServers": { "name": { "command": "...", "args": [...], "env": {...} } } }
|
|
144
|
+
*
|
|
145
|
+
* Scans both project-level (.mipham/mcp.json) and user-level (~/.mipham/mcp.json).
|
|
146
|
+
* Config.yml entries take precedence over .mcp.json entries with the same name.
|
|
147
|
+
*/
|
|
148
|
+
function loadMcpJson(cwd: string): McpServerConfig[] {
|
|
149
|
+
const servers: McpServerConfig[] = []
|
|
150
|
+
const searchPaths = [
|
|
151
|
+
join(cwd, '.mipham', 'mcp.json'),
|
|
152
|
+
join(cwd, '.mcp.json'),
|
|
153
|
+
join(MIPHAM_HOME, 'mcp.json'),
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
for (const path of searchPaths) {
|
|
157
|
+
try {
|
|
158
|
+
if (!existsSync(path)) continue
|
|
159
|
+
const raw = readFileSync(path, 'utf-8')
|
|
160
|
+
const parsed = JSON.parse(raw) as {
|
|
161
|
+
mcpServers?: Record<
|
|
162
|
+
string,
|
|
163
|
+
{ command: string; args?: string[]; env?: Record<string, string> }
|
|
164
|
+
>
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (parsed.mcpServers) {
|
|
168
|
+
for (const [name, cfg] of Object.entries(parsed.mcpServers)) {
|
|
169
|
+
// Avoid duplicates by name
|
|
170
|
+
if (servers.some((s) => s.name === name)) continue
|
|
171
|
+
servers.push({
|
|
172
|
+
name,
|
|
173
|
+
command: cfg.command,
|
|
174
|
+
args: cfg.args || [],
|
|
175
|
+
env: cfg.env,
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// Silently skip malformed or missing .mcp.json files
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return servers
|
|
185
|
+
}
|
|
186
|
+
|
|
139
187
|
export function loadConfig(cwd: string = process.cwd()): MiphamConfig {
|
|
140
188
|
const configPath = join(cwd, '.mipham', 'config.yml')
|
|
141
189
|
const userConfigPath = join(MIPHAM_HOME, 'config.yml')
|
|
@@ -185,6 +233,22 @@ export function loadConfig(cwd: string = process.cwd()): MiphamConfig {
|
|
|
185
233
|
}
|
|
186
234
|
}
|
|
187
235
|
|
|
236
|
+
// ── Load .mcp.json servers (project + user level) ──
|
|
237
|
+
const mcpJsonServers = loadMcpJson(cwd)
|
|
238
|
+
if (mcpJsonServers.length > 0) {
|
|
239
|
+
const existingServers = config.skills?.mcpServers ?? []
|
|
240
|
+
// Merge: config.yml servers take precedence by name
|
|
241
|
+
const existingNames = new Set(existingServers.map((s) => s.name))
|
|
242
|
+
const newFromJson = mcpJsonServers.filter((s) => !existingNames.has(s.name))
|
|
243
|
+
config = {
|
|
244
|
+
...config,
|
|
245
|
+
skills: {
|
|
246
|
+
paths: config.skills?.paths ?? [],
|
|
247
|
+
mcpServers: [...existingServers, ...newFromJson],
|
|
248
|
+
},
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
188
252
|
// ── Auto-backup: save a copy of the user config if it loaded successfully ──
|
|
189
253
|
if (userConfig) {
|
|
190
254
|
backupConfig(userConfigPath)
|
package/src/core/engine.ts
CHANGED
package/src/index.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { ContextManager } from './core/context'
|
|
|
8
8
|
import { QueryEngine } from './core/engine'
|
|
9
9
|
import { SessionStore } from './core/session-store'
|
|
10
10
|
import { SkillsLoader } from './skills/loader'
|
|
11
|
+
import { PluginManager } from './plugin/plugin-manager'
|
|
11
12
|
import { createToolRegistry } from './tools'
|
|
12
13
|
import { McpClient } from './mcp/client'
|
|
13
14
|
import { HookEngine } from './core/hooks'
|
|
@@ -76,6 +77,9 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
76
77
|
skillsLoader.loadExternal(config.skills.paths)
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
// Initialize plugin manager
|
|
81
|
+
const pluginManager = new PluginManager()
|
|
82
|
+
|
|
79
83
|
// Initialize context — restore saved session if available
|
|
80
84
|
const context = new ContextManager({ maxTokens: 200_000, compactionThreshold: 0.9 })
|
|
81
85
|
|
|
@@ -140,6 +144,7 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
140
144
|
initialModel={defaultModel}
|
|
141
145
|
lang={options.lang}
|
|
142
146
|
skillsLoader={skillsLoader}
|
|
147
|
+
pluginManager={pluginManager}
|
|
143
148
|
version={options.version}
|
|
144
149
|
/>,
|
|
145
150
|
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mipham Code — Community Plugin Registry
|
|
3
|
+
*
|
|
4
|
+
* Provides a discoverable list of community plugins installable via npm.
|
|
5
|
+
* Mirrors the community skill registry pattern.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface CommunityPlugin {
|
|
9
|
+
/** Unique plugin name (kebab-case) */
|
|
10
|
+
name: string
|
|
11
|
+
/** One-line description */
|
|
12
|
+
description: string
|
|
13
|
+
/** npm package name */
|
|
14
|
+
npmPackage: string
|
|
15
|
+
/** Category for grouping */
|
|
16
|
+
category: string
|
|
17
|
+
/** Author */
|
|
18
|
+
author: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const COMMUNITY_PLUGINS: CommunityPlugin[] = [
|
|
22
|
+
{
|
|
23
|
+
name: 'notebooklm',
|
|
24
|
+
description:
|
|
25
|
+
'Google NotebookLM integration — citation-backed Q&A, content generation, 31 tools',
|
|
26
|
+
npmPackage: '@roomi-fields/notebooklm-mcp',
|
|
27
|
+
category: 'AI & Data',
|
|
28
|
+
author: 'roomi-fields',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'plugin-dev',
|
|
32
|
+
description: 'Plugin development toolkit — scaffolding, testing, and build utilities',
|
|
33
|
+
npmPackage: 'mipham-plugin-dev',
|
|
34
|
+
category: 'Development',
|
|
35
|
+
author: 'MiphamAI',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'security-scanner',
|
|
39
|
+
description: 'Enhanced security scanning — dependency audit, SAST, secret detection',
|
|
40
|
+
npmPackage: 'mipham-plugin-security',
|
|
41
|
+
category: 'Security',
|
|
42
|
+
author: 'MiphamAI',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'db-explorer',
|
|
46
|
+
description: 'Database exploration — SQL generation, schema analysis, query optimization',
|
|
47
|
+
npmPackage: 'mipham-plugin-db',
|
|
48
|
+
category: 'Data',
|
|
49
|
+
author: 'MiphamAI',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'i18n',
|
|
53
|
+
description: 'Internationalization helper — translation, locale detection, i18n key management',
|
|
54
|
+
npmPackage: 'mipham-plugin-i18n',
|
|
55
|
+
category: 'Development',
|
|
56
|
+
author: 'MiphamAI',
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
export function getAvailablePlugins(): CommunityPlugin[] {
|
|
61
|
+
return [...COMMUNITY_PLUGINS]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function searchPlugins(query: string): CommunityPlugin[] {
|
|
65
|
+
const q = query.toLowerCase()
|
|
66
|
+
return COMMUNITY_PLUGINS.filter(
|
|
67
|
+
(p) => p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q),
|
|
68
|
+
)
|
|
69
|
+
}
|