@miphamai/cli 0.5.12 → 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/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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miphamai/cli",
3
- "version": "0.5.12",
3
+ "version": "0.6.0",
4
4
  "description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
5
5
  "keywords": [
6
6
  "ai",
@@ -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
+ }
@@ -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/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
+ }
@@ -260,6 +260,15 @@ export const DEFAULT_PROVIDERS: ProviderConfig[] = [
260
260
  vision: true,
261
261
  status: 'active',
262
262
  },
263
+ {
264
+ id: 'kimi-k3',
265
+ name: 'Kimi K3',
266
+ providerId: 'kimi',
267
+ contextWindow: 1_000_000,
268
+ maxOutput: 128_000,
269
+ vision: true,
270
+ status: 'active',
271
+ },
263
272
  {
264
273
  id: 'moonshot-v1-8k',
265
274
  name: 'Moonshot v1 8K',