@miphamai/cli 0.5.7 → 0.5.9

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.
@@ -49,107 +49,127 @@ type CommandHandler = (
49
49
  // Session & Identity
50
50
  // ═══════════════════════════════════════════════════════════════
51
51
 
52
- const helpCmd: CommandHandler = (_ctx) => ({
53
- content: stripIndent`
54
- Mipham Code v${PACKAGE_VERSION} — Commands
55
-
56
- ── Session ──────────────────────────
57
- /help Show this help
58
- /version Show version info
59
- /clear Clear conversation
60
- /compact Compact context window
61
- /context Show context stats
62
- /status Session and system status
63
- /cost Token usage estimate
64
- /usage Detailed usage dashboard
65
- /rename <name> Rename current session
66
- /goal <text> Set session goal
67
- /recap Summarize session so far
68
- /export Export conversation to file
69
- /doctor System diagnostics
70
- /resume List saved sessions
71
- /branch <name> Fork conversation
72
-
73
- ── History ─────────────────────────
74
- /rewind Undo last AI turn
75
- /undo Same as /rewind
76
- /copy [N] Copy last response to clipboard
77
- /focus Toggle focus view (last exchange only)
78
-
79
- ── Model & Provider ────────────────
80
- /pick Open model picker (or Ctrl+P)
81
- /model Show current model
82
- /models List all available models
83
- /provider Show current provider
84
- /providers List configured providers
85
- /switch <p> <m> Switch provider and model
86
- /config View configuration
87
- /fast [on|off] Toggle fast mode
88
- /effort <lvl> Set reasoning effort (low|medium|high|xhigh|max)
89
- /theme [dark|light|auto] Set terminal theme
90
-
91
- ── Tools & Skills ──────────────────
92
- /tools List available tools (16 total)
93
- /skills List loaded skills (14 built-in)
94
- /reload-skills Reload all skills
95
- /mcp MCP server status
96
-
97
- ── Workflow ────────────────────────
98
- /plan Enter plan mode (read-only)
99
- /no-plan Exit plan mode
100
- /tdd TDD mode [stub]
101
- /todos Task management
102
- /tasks Background tasks
103
- /review Code review workflow
104
- /pr-comments PR review summary
105
- /diff Show git diff
106
- /workflows List workflow scripts
107
- /loop <int> <p> Run prompt on interval
108
- /schedule View scheduled tasks [stub]
109
-
110
- ── Project ─────────────────────────
111
- /init Initialize .mipham config
112
- /setup Guided project setup wizard
113
- /permissions Show permission settings
114
- /add-dir <dir> Add workspace directory
115
- /security Security review checklist
116
- /audit Same as /security
117
-
118
- ── Environment ─────────────────────
119
- /upgrade Show upgrade instructions
120
- /release-notes View version changelog
121
- /ide IDE integration guide
122
- /terminal-setup Shell & terminal config
123
- /memory Manage AI memories
124
-
125
- ── Account ─────────────────────────
126
- /login Show API key status
127
- /logout Clear credentials guide
128
- /feedback Send feedback
129
-
130
- ── Agents ──────────────────────────
131
- /agents Agent view dashboard
132
- /bg <prompt> Run a background agent task
133
-
134
- Type /exit or Esc to quit.
135
- `,
136
- })
52
+ const helpCmd: CommandHandler = (ctx) => {
53
+ const skillCount = ctx.skillsLoader?.list().length ?? 14
54
+ const toolsCount = ctx.engine.getTools().size
55
+ const cmdCount = getCommandNames().length
137
56
 
138
- const versionCmd: CommandHandler = (ctx) => ({
139
- content: stripIndent`
140
- Mipham Code v${ctx.version}
57
+ return {
58
+ content: stripIndent`
59
+ Mipham Code v${PACKAGE_VERSION} — Commands
60
+
61
+ ── Session ──────────────────────────
62
+ /help Show this help
63
+ /commands List all ${cmdCount} commands
64
+ /version Show version info
65
+ /clear Clear conversation
66
+ /compact Compact context window
67
+ /context Show context stats
68
+ /status Session and system status
69
+ /cost Token usage estimate
70
+ /usage Detailed usage dashboard
71
+ /rename <name> Rename current session
72
+ /goal <text> Set session goal
73
+ /recap Summarize session so far
74
+ /export Export conversation to file
75
+ /doctor System diagnostics
76
+ /resume List saved sessions
77
+ /branch <name> Fork conversation
78
+
79
+ ── History ─────────────────────────
80
+ /rewind Undo last AI turn
81
+ /undo Same as /rewind
82
+ /copy [N] Copy last response to clipboard
83
+ /focus Toggle focus view (last exchange only)
84
+
85
+ ── Model & Provider ────────────────
86
+ /pick Open model picker (or Ctrl+P)
87
+ /model Show current model
88
+ /models List all available models
89
+ /provider Show current provider
90
+ /providers List configured providers
91
+ /switch <p> <m> Switch provider and model
92
+ /config View configuration
93
+ /fast [on|off] Toggle fast mode
94
+ /effort <lvl> Set reasoning effort (low|medium|high|xhigh|max)
95
+ /theme [dark|light|auto] Set terminal theme
96
+
97
+ ── Tools & Skills ──────────────────
98
+ /tools List available tools (${toolsCount} total)
99
+ /skills List loaded skills (${skillCount} built-in)
100
+ /reload-skills Reload all skills
101
+ /browse-skills Browse community skill marketplace
102
+ /install-skill Install a skill by name or URL
103
+ /remove-skill Remove an installed skill
104
+ /commands List all slash commands
105
+ /mcp MCP server status
106
+
107
+ ── Workflow ────────────────────────
108
+ /plan Enter plan mode (read-only)
109
+ /no-plan Exit plan mode
110
+ /tdd TDD mode [stub]
111
+ /todos Task management
112
+ /tasks Background tasks
113
+ /review Code review workflow
114
+ /pr-comments PR review summary
115
+ /diff Show git diff
116
+ /workflows List workflow scripts
117
+ /loop <int> <p> Run prompt on interval
118
+ /schedule View scheduled tasks [stub]
119
+
120
+ ── Project ─────────────────────────
121
+ /init Initialize .mipham config
122
+ /setup Guided project setup wizard
123
+ /recommend Analyze project + recommend setup
124
+ /permissions Show permission settings
125
+ /add-dir <dir> Add workspace directory
126
+ /security Security review checklist
127
+ /audit Same as /security
128
+
129
+ ── Environment ─────────────────────
130
+ /upgrade Show upgrade instructions
131
+ /release-notes View version changelog
132
+ /ide IDE integration guide
133
+ /terminal-setup Shell & terminal config
134
+ /memory Manage AI memories
135
+
136
+ ── Account ─────────────────────────
137
+ /login Show API key status
138
+ /logout Clear credentials guide
139
+ /feedback Send feedback
140
+
141
+ ── Agents ──────────────────────────
142
+ /agents Agent view dashboard
143
+ /bg <prompt> Run a background agent task
144
+
145
+ Type /exit or Esc to quit.
146
+ `,
147
+ }
148
+ }
141
149
 
142
- Runtime: Bun ${typeof Bun !== 'undefined' ? Bun.version : '(Node.js)'}
143
- Platform: ${process.platform} ${process.arch}
144
- Node: ${process.version}
145
- CWD: ${process.cwd()}
150
+ const versionCmd: CommandHandler = (ctx) => {
151
+ const skillCounts = ctx.skillsLoader?.countByType()
152
+ const skillsLine = skillCounts
153
+ ? `Skills: ${skillCounts.total} built-in (${skillCounts.standard} standard + ${skillCounts.mipham} mipham)`
154
+ : `Skills: (loader unavailable)`
155
+ const toolsCount = ctx.engine.getTools().size
146
156
 
147
- Provider: ${ctx.providerId} / ${ctx.modelId}
148
- Tools: 16 built-in
149
- Skills: 14 built-in (12 standard + 2 mipham)
150
- License: Apache 2.0
151
- `,
152
- })
157
+ return {
158
+ content: stripIndent`
159
+ Mipham Code v${ctx.version}
160
+
161
+ Runtime: Bun ${typeof Bun !== 'undefined' ? Bun.version : '(Node.js)'}
162
+ Platform: ${process.platform} ${process.arch}
163
+ Node: ${process.version}
164
+ CWD: ${process.cwd()}
165
+
166
+ Provider: ${ctx.providerId} / ${ctx.modelId}
167
+ Tools: ${toolsCount} built-in
168
+ ${skillsLine}
169
+ License: Apache 2.0
170
+ `,
171
+ }
172
+ }
153
173
 
154
174
  const clearCmd: CommandHandler = (ctx) => {
155
175
  ctx.engine.getContext().clear()
@@ -317,29 +337,25 @@ const toolsCmd: CommandHandler = (ctx) => {
317
337
  return { content: `Available tools (${tools.size}):\n\n${sections.join('\n\n')}` }
318
338
  }
319
339
 
320
- const skillsCmd: CommandHandler = (_ctx) => ({
321
- content: stripIndent`
322
- ── Standard Skills (12) ──
323
- code-review Code review automation (v2.0)
324
- compassionate-communication Warm, respectful, user-centered interaction
325
- doc-generator Generate technical docs from code
326
- github-ops GitHub PR/issues/releases management
327
- memory Persistent memory read/write
328
- mipham-code-setup Mipham Code install & config guide
329
- security-review Security audit (OWASP, secrets, supply chain)
330
- self-review Self-review diff for bugs and cleanup
331
- superpower Skill discovery and usage guide
332
- tdd Test-Driven Development workflow
333
- web-search Web search for current information
334
- code-review (classic) Legacy code review
335
-
336
- ── Mipham Exclusive (2) ──
337
- om-security Security analysis (injection, PII, adversarial)
338
- om-model-optimize Model optimization (context, caching, tokens)
339
-
340
- 14 skills loaded. Use Skill tool to invoke.
341
- `,
342
- })
340
+ const skillsCmd: CommandHandler = (ctx) => {
341
+ if (!ctx.skillsLoader) {
342
+ return { content: 'SkillsLoader not available.' }
343
+ }
344
+ const counts = ctx.skillsLoader.countByType()
345
+ const standard = ctx.skillsLoader.listByType('standard')
346
+ const mipham = ctx.skillsLoader.listByType('mipham')
347
+
348
+ const lines: string[] = [
349
+ `── Standard Skills (${counts.standard}) ──`,
350
+ ...standard.map((s) => ` ${s.name.padEnd(28)} ${s.description}`),
351
+ '',
352
+ `── Mipham Exclusive (${counts.mipham}) ──`,
353
+ ...mipham.map((s) => ` ${s.name.padEnd(20)} ${s.description}`),
354
+ '',
355
+ `${counts.total} skills loaded. Use Skill tool to invoke.`,
356
+ ]
357
+ return { content: lines.join('\n') }
358
+ }
343
359
 
344
360
  // ═══════════════════════════════════════════════════════════════
345
361
  // Workflow
@@ -382,24 +398,89 @@ const todosCmd: CommandHandler = (_ctx) => ({
382
398
  // Project
383
399
  // ═══════════════════════════════════════════════════════════════
384
400
 
385
- const initCmd: CommandHandler = (_ctx) => ({
386
- content: stripIndent`
387
- ── Initialize Mipham Code ──
401
+ const initCmd: CommandHandler = async (ctx) => {
402
+ const { existsSync, mkdirSync, writeFileSync } = await import('node:fs')
403
+ const { join } = await import('node:path')
404
+ const { homedir } = await import('node:os')
405
+
406
+ const home = homedir()
407
+ const userConfigPath = join(home, '.mipham', 'config.yml')
408
+
409
+ // Generate user-friendly config if it doesn't exist yet
410
+ if (!existsSync(userConfigPath)) {
411
+ mkdirSync(join(home, '.mipham'), { recursive: true })
412
+
413
+ const activeProviders = ctx.config.providers.filter((p) => p.status === 'active')
414
+ const providerYaml = activeProviders
415
+ .map((p) => {
416
+ const tips: Record<string, string> = {
417
+ anthropic: '# Get key: https://console.anthropic.com/',
418
+ openai: '# Get key: https://platform.openai.com/api-keys',
419
+ deepseek: '# Get key: https://platform.deepseek.com/api_keys',
420
+ kimi: '# Get key: https://platform.moonshot.cn/',
421
+ doubao: '# Get key: https://console.volcengine.com/ark',
422
+ hunyuan: '# Get key: https://console.cloud.tencent.com/hunyuan',
423
+ qwen: '# Get key: https://dashscope.console.aliyun.com/apiKey',
424
+ google: '# Get key: https://aistudio.google.com/apikey',
425
+ }
426
+ const comment = tips[p.id] || ''
427
+ const baseUrlLine = p.baseUrl ? `\n baseUrl: "${p.baseUrl}"` : ''
428
+ return ` ${comment}
429
+ - id: ${p.id}
430
+ name: "${p.name}"${baseUrlLine}
431
+ apiKey: "\${${p.id.toUpperCase()}_API_KEY}"`
432
+ })
433
+ .join('\n\n')
434
+
435
+ const configContent = `# Mipham Code — User Configuration
436
+ # Location: ~/.mipham/config.yml
437
+ # Docs: https://mipham.ai/code/docs/config
438
+ #
439
+ # ═══ Quick Start ═══
440
+ # 1. Replace the API key placeholders below with your real keys
441
+ # 2. Save the file
442
+ # 3. Run 'mipham' — it auto-detects configured providers
443
+ #
444
+ # ═══ Environment Variables (Alternative) ═══
445
+ # Instead of editing this file, you can set env vars:
446
+ # export ANTHROPIC_API_KEY="sk-ant-..."
447
+ # export OPENAI_API_KEY="sk-..."
448
+ # (The \${VAR} syntax below reads from environment variables)
449
+
450
+ # ── Defaults ──
451
+ defaultProvider: ${ctx.providerId}
452
+ defaultModel: ${ctx.modelId}
453
+ permission: ask
388
454
 
389
- To initialize a project:
455
+ # ── Providers (${activeProviders.length} pre-configured — just add your API keys) ──
456
+ providers:
457
+ ${providerYaml}
458
+ `
390
459
 
391
- 1. Create .mipham/config.yml:
392
- version: "0.1.0"
393
- defaultProvider: anthropic
394
- defaultModel: claude-sonnet-4-6
395
- permission: auto
460
+ writeFileSync(userConfigPath, configContent, 'utf-8')
461
+ return {
462
+ content: `✅ Mipham Code initialized!
396
463
 
397
- 2. Or use the Config tool:
398
- Config set defaultProvider anthropic
464
+ Created: ~/.mipham/config.yml (${activeProviders.length} providers pre-configured)
399
465
 
400
- Run /config to view current configuration.
401
- `,
402
- })
466
+ Next steps:
467
+ 1. Edit ~/.mipham/config.yml — replace API key placeholders with your real keys
468
+ 2. Run mipham to start
469
+
470
+ Providers configured:
471
+ ${activeProviders.map((p) => ` • ${p.name} — ${p.id.toUpperCase()}_API_KEY`).join('\n')}
472
+
473
+ Tip: /setup for the full 6-step wizard.`,
474
+ }
475
+ }
476
+
477
+ // Config already exists — show status
478
+ return {
479
+ content: `~/.mipham/config.yml already exists.
480
+
481
+ Run /setup for the full wizard, or /config to view current settings.`,
482
+ }
483
+ }
403
484
 
404
485
  // ═══════════════════════════════════════════════════════════════
405
486
  // Phase 1 — New Session Commands
@@ -508,6 +589,83 @@ const reloadSkillsCmd: CommandHandler = (ctx) => {
508
589
  }
509
590
  }
510
591
 
592
+ // ═══════════════════════════════════════════════════════════════
593
+ // Skill Marketplace — Community skill registry + installation
594
+ // ═══════════════════════════════════════════════════════════════
595
+
596
+ const browseSkillsCmd: CommandHandler = async () => {
597
+ const { getAvailableSkills, listInstalledSkills } = await import('../skills/registry')
598
+ const available = getAvailableSkills()
599
+ const installed = new Set(
600
+ listInstalledSkills().map((f: string) => f.replace(/\.(SKILL\.)?md$/i, '')),
601
+ )
602
+
603
+ const categories = new Map<string, string[]>()
604
+ for (const s of available) {
605
+ const list = categories.get(s.category) || []
606
+ list.push(s.name)
607
+ categories.set(s.category, list)
608
+ }
609
+
610
+ const lines: string[] = [
611
+ '── Community Skills ──',
612
+ '',
613
+ `${available.length} skills available · ${installed.size} installed`,
614
+ '',
615
+ ]
616
+
617
+ for (const [cat, names] of categories) {
618
+ lines.push(` ${cat}:`)
619
+ for (const name of names) {
620
+ const entry = available.find((s) => s.name === name)!
621
+ const marker = installed.has(name) ? '✅' : '⬜'
622
+ lines.push(` ${marker} /${name.padEnd(26)} ${entry.description}`)
623
+ }
624
+ lines.push('')
625
+ }
626
+
627
+ lines.push('Install: /install-skill <name>')
628
+ lines.push('Install from URL: /install-skill <github-url>')
629
+ lines.push('Remove: /remove-skill <name>')
630
+
631
+ return { content: lines.join('\n') }
632
+ }
633
+
634
+ const installSkillCmd: CommandHandler = async (_ctx, args) => {
635
+ const { installSkill, installSkillFromUrl } = await import('../skills/registry')
636
+
637
+ const target = args[0]
638
+ if (!target) {
639
+ return { content: 'Usage: /install-skill <skill-name> or /install-skill <url>' }
640
+ }
641
+
642
+ let result: { success: boolean; name: string; message: string }
643
+
644
+ if (target.startsWith('http://') || target.startsWith('https://')) {
645
+ result = installSkillFromUrl(target)
646
+ } else {
647
+ result = installSkill(target)
648
+ }
649
+
650
+ return {
651
+ content: result.success ? `✅ ${result.message}` : `❌ ${result.message}`,
652
+ }
653
+ }
654
+
655
+ const removeSkillCmd: CommandHandler = async (_ctx, args) => {
656
+ const { removeSkill } = await import('../skills/registry')
657
+
658
+ const name = args[0]
659
+ if (!name) {
660
+ return { content: 'Usage: /remove-skill <skill-name>' }
661
+ }
662
+
663
+ const result = removeSkill(name)
664
+ return {
665
+ content: result.success ? `✅ ${result.message}` : `❌ ${result.message}`,
666
+ }
667
+ }
668
+
511
669
  // ═══════════════════════════════════════════════════════════════
512
670
  // Phase 1 — History & Checkpoint Commands
513
671
  // ═══════════════════════════════════════════════════════════════
@@ -1135,24 +1293,72 @@ const memoryCmd: CommandHandler = async () => {
1135
1293
  // Upgrade
1136
1294
  // ═══════════════════════════════════════════════════════════════
1137
1295
 
1138
- const upgradeCmd: CommandHandler = () => ({
1139
- content: `─ Upgrade Mipham Code
1296
+ const upgradeCmd: CommandHandler = async () => {
1297
+ const { checkForUpdates, backupConfig, performUpdate, restoreConfig, getConfigPath } =
1298
+ await import('../shared/update')
1140
1299
 
1141
- Current version: v0.1.0
1300
+ const update = checkForUpdates()
1142
1301
 
1143
- To upgrade:
1144
- curl -fsSL https://mipham.ai/install.sh | bash
1302
+ if (!update.available) {
1303
+ return {
1304
+ content: `── Upgrade Mipham Code ──
1145
1305
 
1146
- Or if installed via npm:
1147
- ${NPM_UPDATE_COMMAND}
1306
+ Current version: v${update.current}
1307
+ Latest: v${update.latest}
1148
1308
 
1149
- Release channels:
1150
- • stable — recommended for most users
1151
- • beta — early access to new features
1152
- • nightly — latest commits, may be unstable
1309
+ Already up to date.
1153
1310
 
1154
- Check for updates: https://mipham.ai/code/releases`,
1155
- })
1311
+ To check manually: https://www.npmjs.com/package/@miphamai/cli`,
1312
+ }
1313
+ }
1314
+
1315
+ // Update available — back up config and perform the upgrade
1316
+ const backupPath = backupConfig(`upgrade-v${update.current}`)
1317
+
1318
+ const lines: string[] = [
1319
+ '── Upgrade Mipham Code ──',
1320
+ '',
1321
+ `Current version: v${update.current}`,
1322
+ `Latest: v${update.latest}`,
1323
+ '',
1324
+ `→ New version available! Updating...`,
1325
+ '',
1326
+ ]
1327
+
1328
+ if (backupPath) {
1329
+ lines.push(`Config backed up to: ${backupPath}`)
1330
+ }
1331
+
1332
+ const ok = performUpdate(update.latest)
1333
+
1334
+ if (ok) {
1335
+ const configPath = getConfigPath()
1336
+ const { existsSync } = await import('node:fs')
1337
+ lines.push('')
1338
+ lines.push(`✓ Updated to @miphamai/cli v${update.latest}`)
1339
+
1340
+ if (existsSync(configPath)) {
1341
+ lines.push(`✓ Config preserved: ${configPath}`)
1342
+ } else if (backupPath) {
1343
+ if (restoreConfig(backupPath)) {
1344
+ lines.push('✓ Config restored from backup.')
1345
+ }
1346
+ }
1347
+
1348
+ lines.push('')
1349
+ lines.push('⚠ The running Mipham Code process is still the old version.')
1350
+ lines.push(' Run `mipham` again to use the new version, or `mipham --version` to verify.')
1351
+ } else {
1352
+ lines.push('')
1353
+ lines.push('✗ Update failed.')
1354
+ lines.push(` Try manually: ${NPM_UPDATE_COMMAND}`)
1355
+ if (backupPath) {
1356
+ lines.push(` Your config backup is at: ${backupPath}`)
1357
+ }
1358
+ }
1359
+
1360
+ return { content: lines.join('\n') }
1361
+ }
1156
1362
 
1157
1363
  // ═══════════════════════════════════════════════════════════════
1158
1364
  // No-Plan — exit plan mode
@@ -1255,6 +1461,168 @@ Tool execution is sandboxed to the project directory by default.`,
1255
1461
  }
1256
1462
  }
1257
1463
 
1464
+ // ═══════════════════════════════════════════════════════════════
1465
+ // Recommend — analyze project and recommend setup (like Claude Code
1466
+ // automation recommender)
1467
+ // ═══════════════════════════════════════════════════════════════
1468
+
1469
+ const recommendCmd: CommandHandler = async (ctx) => {
1470
+ const { existsSync, readFileSync } = await import('node:fs')
1471
+ const { join } = await import('node:path')
1472
+ const { getAvailableSkills } = await import('../skills/registry')
1473
+
1474
+ const cwd = process.cwd()
1475
+ const lines: string[] = ['── Mipham Code: Setup Recommendations ──', '', `Project: ${cwd}`, '']
1476
+
1477
+ // ── Detect project type ──
1478
+ const hasPackageJson = existsSync(join(cwd, 'package.json'))
1479
+ const hasTsConfig = existsSync(join(cwd, 'tsconfig.json'))
1480
+ const hasPyProject = existsSync(join(cwd, 'pyproject.toml'))
1481
+ const hasRequirements = existsSync(join(cwd, 'requirements.txt'))
1482
+ const hasDockerfile = existsSync(join(cwd, 'Dockerfile'))
1483
+ const hasGitHubActions = existsSync(join(cwd, '.github', 'workflows'))
1484
+ const hasNextConfig =
1485
+ existsSync(join(cwd, 'next.config.js')) || existsSync(join(cwd, 'next.config.ts'))
1486
+ const hasVueConfig =
1487
+ existsSync(join(cwd, 'vite.config.ts')) || existsSync(join(cwd, 'vite.config.js'))
1488
+ const hasTailwind =
1489
+ existsSync(join(cwd, 'tailwind.config.ts')) || existsSync(join(cwd, 'tailwind.config.js'))
1490
+
1491
+ let pkgData: Record<string, unknown> = {}
1492
+ if (hasPackageJson) {
1493
+ try {
1494
+ pkgData = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8'))
1495
+ } catch {
1496
+ /* ignore */
1497
+ }
1498
+ }
1499
+ const deps = {
1500
+ ...((pkgData.dependencies as Record<string, string>) || {}),
1501
+ ...((pkgData.devDependencies as Record<string, string>) || {}),
1502
+ }
1503
+ const isTypeScript = hasTsConfig || 'typescript' in deps
1504
+ const isReact = 'react' in deps || 'next' in deps
1505
+ const isVue = 'vue' in deps
1506
+ const isNode = hasPackageJson
1507
+ const isPython = hasPyProject || hasRequirements
1508
+ const isFastAPI = 'fastapi' in deps
1509
+ const isNextJS = 'next' in deps
1510
+ const isExpress = 'express' in deps || 'fastify' in deps
1511
+ const isDocker = hasDockerfile
1512
+
1513
+ // ── Detection summary ──
1514
+ const tags: string[] = []
1515
+ if (isTypeScript) tags.push('TypeScript')
1516
+ if (isReact) tags.push('React')
1517
+ if (isVue) tags.push('Vue')
1518
+ if (isNextJS) tags.push('Next.js')
1519
+ if (isExpress) tags.push('Node.js API')
1520
+ if (isFastAPI) tags.push('FastAPI')
1521
+ if (isPython) tags.push('Python')
1522
+ if (isDocker) tags.push('Docker')
1523
+ if (hasTailwind) tags.push('Tailwind CSS')
1524
+ if (hasGitHubActions) tags.push('CI/CD')
1525
+
1526
+ lines.push('── Detected Stack ──')
1527
+ lines.push('')
1528
+ if (tags.length > 0) {
1529
+ lines.push(` ${tags.join(' · ')}`)
1530
+ } else {
1531
+ lines.push(' (generic project — no specific framework detected)')
1532
+ }
1533
+ lines.push('')
1534
+
1535
+ // ── Skill recommendations ──
1536
+ const communitySkills = getAvailableSkills()
1537
+ const recommendedSkills: string[] = []
1538
+
1539
+ // Always useful
1540
+ recommendedSkills.push('code-review')
1541
+ recommendedSkills.push('systematic-debugging')
1542
+
1543
+ if (isTypeScript || isNode) {
1544
+ recommendedSkills.push('github-ops')
1545
+ }
1546
+ if (isReact || isVue || isNextJS) {
1547
+ recommendedSkills.push('frontend-design')
1548
+ }
1549
+ if (hasGitHubActions) {
1550
+ recommendedSkills.push('security-review')
1551
+ }
1552
+ if (isPython) {
1553
+ recommendedSkills.push('doc-generator')
1554
+ }
1555
+
1556
+ // Filter to only those in the registry
1557
+ const available = recommendedSkills.filter((name) => communitySkills.some((s) => s.name === name))
1558
+
1559
+ lines.push('── Recommended Skills ──')
1560
+ lines.push('')
1561
+ if (available.length > 0) {
1562
+ for (const name of available) {
1563
+ const entry = communitySkills.find((s) => s.name === name)!
1564
+ lines.push(` /install-skill ${name.padEnd(26)} ${entry.description}`)
1565
+ }
1566
+ }
1567
+ lines.push(' /browse-skills Browse all community skills')
1568
+ lines.push('')
1569
+
1570
+ // ── Provider recommendations ──
1571
+ const activeProviders = ctx.config.providers.filter((p) => p.status === 'active')
1572
+ const configured = activeProviders.filter((p) => p.apiKey && p.apiKey.trim() !== '')
1573
+
1574
+ lines.push('── Provider Status ──')
1575
+ lines.push('')
1576
+ if (configured.length === 0) {
1577
+ lines.push(' ⚠ No providers have API keys configured.')
1578
+ lines.push(' Run /setup 2 to configure providers.')
1579
+ lines.push('')
1580
+ lines.push(' Recommended for this project:')
1581
+ if (isTypeScript || isNode || isReact) {
1582
+ lines.push(' • anthropic — Claude (code generation, review)')
1583
+ lines.push(' • openai — GPT-5 (general purpose)')
1584
+ }
1585
+ if (isPython) {
1586
+ lines.push(' • anthropic — Claude (data science, ML)')
1587
+ }
1588
+ } else {
1589
+ lines.push(` ${configured.length}/${activeProviders.length} providers configured`)
1590
+ for (const p of configured) {
1591
+ lines.push(` ✅ ${p.id.padEnd(14)} ${p.name}`)
1592
+ }
1593
+ }
1594
+ lines.push('')
1595
+
1596
+ // ── Config recommendations ──
1597
+ lines.push('── Configuration Tips ──')
1598
+ lines.push('')
1599
+
1600
+ const hasProjectMipham = existsSync(join(cwd, '.mipham'))
1601
+ if (!hasProjectMipham) {
1602
+ lines.push(' /setup 1 Initialize .mipham/ + MIPHAM.md + config.yml')
1603
+ }
1604
+
1605
+ const { listInstalledSkills } = await import('../skills/registry')
1606
+ const installed = listInstalledSkills()
1607
+ if (installed.length === 0 && available.length > 0) {
1608
+ lines.push(' Tip: Install recommended skills above for better AI assistance')
1609
+ }
1610
+
1611
+ if (isDocker && !hasProjectMipham) {
1612
+ lines.push(' Tip: Add .mipham/ to .dockerignore for smaller images')
1613
+ }
1614
+
1615
+ if (hasGitHubActions) {
1616
+ lines.push(' /setup 5 Configure tool permissions for CI/CD safety')
1617
+ }
1618
+
1619
+ lines.push(' /setup Full setup wizard (6 steps)')
1620
+ lines.push(' /help All commands')
1621
+ lines.push('')
1622
+
1623
+ return { content: lines.join('\n') }
1624
+ }
1625
+
1258
1626
  // ═══════════════════════════════════════════════════════════════
1259
1627
  // Setup — guided project initialization (mirrors Claude Code /setup)
1260
1628
  // ═══════════════════════════════════════════════════════════════
@@ -1356,17 +1724,60 @@ async function setupStep1(ctx: CommandContext): Promise<CommandResult> {
1356
1724
 
1357
1725
  // Create project config if missing
1358
1726
  if (!existsSync(configPath)) {
1359
- const defaultConfig = `# Mipham Code Project Configuration
1360
- # See https://mipham.ai/code/docs/config for all options
1361
-
1727
+ // Generate a user-friendly config with all providers pre-populated.
1728
+ // Users just need to replace the API key placeholders with their real keys.
1729
+ const activeProviders = ctx.config.providers.filter((p) => p.status === 'active')
1730
+ const providerYaml = activeProviders
1731
+ .map((p) => {
1732
+ const comment =
1733
+ p.id === 'anthropic'
1734
+ ? '# Get key: https://console.anthropic.com/'
1735
+ : p.id === 'openai'
1736
+ ? '# Get key: https://platform.openai.com/api-keys'
1737
+ : p.id === 'deepseek'
1738
+ ? '# Get key: https://platform.deepseek.com/api_keys'
1739
+ : p.id === 'kimi'
1740
+ ? '# Get key: https://platform.moonshot.cn/'
1741
+ : p.id === 'doubao'
1742
+ ? '# Get key: https://console.volcengine.com/ark'
1743
+ : p.id === 'hunyuan'
1744
+ ? '# Get key: https://console.cloud.tencent.com/hunyuan'
1745
+ : p.id === 'qwen'
1746
+ ? '# Get key: https://dashscope.console.aliyun.com/apiKey'
1747
+ : p.id === 'google'
1748
+ ? '# Get key: https://aistudio.google.com/apikey'
1749
+ : ''
1750
+ const baseUrlLine = p.baseUrl ? `\n baseUrl: "${p.baseUrl}"` : ''
1751
+ return ` ${comment}
1752
+ - id: ${p.id}
1753
+ name: "${p.name}"${baseUrlLine}
1754
+ apiKey: "\${${p.id.toUpperCase()}_API_KEY}"`
1755
+ })
1756
+ .join('\n\n')
1757
+
1758
+ const defaultConfig = `# Mipham Code — User Configuration
1759
+ # Location: ~/.mipham/config.yml
1760
+ # Docs: https://mipham.ai/code/docs/config
1761
+ #
1762
+ # ═══ Quick Start ═══
1763
+ # 1. Set your API keys below (replace the placeholder values)
1764
+ # 2. Save the file
1765
+ # 3. Run 'mipham' — it auto-detects configured providers
1766
+ #
1767
+ # ═══ Environment Variables ═══
1768
+ # Instead of editing this file, you can set env vars:
1769
+ # export ANTHROPIC_API_KEY="sk-ant-..."
1770
+ # export OPENAI_API_KEY="sk-..."
1771
+ # (The \${VAR} syntax below reads from environment variables)
1772
+
1773
+ # ── Defaults ──
1362
1774
  defaultProvider: ${ctx.providerId}
1363
1775
  defaultModel: ${ctx.modelId}
1364
1776
  permission: ask
1365
1777
 
1366
- # Uncomment to add project-specific providers:
1367
- # providers:
1368
- # - id: anthropic
1369
- # apiKey: \$ANTHROPIC_API_KEY
1778
+ # ── Providers (8 configured, just add your API keys) ──
1779
+ providers:
1780
+ ${providerYaml}
1370
1781
  `
1371
1782
  writeFileSync(configPath, defaultConfig, 'utf-8')
1372
1783
  created.push('.mipham/config.yml')
@@ -1500,16 +1911,19 @@ async function setupStep3(ctx: CommandContext): Promise<CommandResult> {
1500
1911
  return { content: lines.join('\n') }
1501
1912
  }
1502
1913
 
1503
- async function setupStep4(_ctx: CommandContext): Promise<CommandResult> {
1914
+ async function setupStep4(ctx: CommandContext): Promise<CommandResult> {
1915
+ const counts = ctx.skillsLoader?.countByType() ?? { standard: 0, mipham: 0, total: 0 }
1916
+ const standardNames = ctx.skillsLoader?.getNamesByType('standard') ?? []
1917
+ const miphamNames = ctx.skillsLoader?.getNamesByType('mipham') ?? []
1918
+
1504
1919
  return {
1505
1920
  content: `── Step 4: Install Skills ──
1506
1921
 
1507
1922
  Skills extend Mipham Code with specialized capabilities.
1508
1923
 
1509
- Built-in skills (11 total):
1510
- Standard (9): code-review, compassionate-communication, doc-generator,
1511
- github-ops, memory, self-review, superpower, tdd, web-search
1512
- Mipham (2): om-model-optimize, om-security
1924
+ Built-in skills (${counts.total} total):
1925
+ Standard (${counts.standard}): ${standardNames.join(', ')}
1926
+ Mipham (${counts.mipham}): ${miphamNames.join(', ')}
1513
1927
 
1514
1928
  Community skills:
1515
1929
  Coming soon — the Mipham Code skills marketplace will let you
@@ -1705,6 +2119,7 @@ const securityCmd: CommandHandler = async () => {
1705
2119
  }
1706
2120
 
1707
2121
  // 2. Check for hardcoded secrets (quick grep for common patterns)
2122
+ // ⚠ Security: results are redacted — only file:line locations are shown, never values
1708
2123
  try {
1709
2124
  const { execSync } = await import('node:child_process')
1710
2125
  const secretPatterns = execSync(
@@ -1712,11 +2127,20 @@ const securityCmd: CommandHandler = async () => {
1712
2127
  { encoding: 'utf-8', timeout: 5000 },
1713
2128
  ).trim()
1714
2129
  if (secretPatterns) {
2130
+ // Redact the actual values — only show file:line locations
2131
+ const redacted = secretPatterns
2132
+ .split('\n')
2133
+ .map((l) => {
2134
+ const colonIdx = l.indexOf(':')
2135
+ const secondColon = l.indexOf(':', colonIdx + 1)
2136
+ if (secondColon > 0) {
2137
+ return ' ' + l.slice(0, secondColon) + ' [VALUE REDACTED]'
2138
+ }
2139
+ return ' ' + l + ' [REDACTED]'
2140
+ })
2141
+ .join('\n')
1715
2142
  findings.push(
1716
- `Possible hardcoded secrets found:\n${secretPatterns
1717
- .split('\n')
1718
- .map((l) => ' ' + l)
1719
- .join('\n')}`,
2143
+ `⚠ Hardcoded secrets detected (values redacted for security):\n${redacted}\n\n Replace with env vars: \${VAR_NAME} syntax`,
1720
2144
  )
1721
2145
  } else {
1722
2146
  ok.push('No hardcoded secrets detected')
@@ -2273,6 +2697,118 @@ const artifactBaseCmd: CommandHandler = (ctx, args) => {
2273
2697
  // Remaining low-priority stubs (backend not yet available)
2274
2698
  // ═══════════════════════════════════════════════════════════════
2275
2699
 
2700
+ // ═══════════════════════════════════════════════════════════════
2701
+ // Commands — list all registered slash commands
2702
+ // ═══════════════════════════════════════════════════════════════
2703
+
2704
+ const commandsListCmd: CommandHandler = () => {
2705
+ const names = getCommandNames()
2706
+ const categories: Record<string, string[]> = {
2707
+ 'Session & Identity': [],
2708
+ History: [],
2709
+ 'Model & Provider': [],
2710
+ 'Tools & Skills': [],
2711
+ Workflow: [],
2712
+ Project: [],
2713
+ Environment: [],
2714
+ Account: [],
2715
+ Agents: [],
2716
+ Artifacts: [],
2717
+ Other: [],
2718
+ }
2719
+
2720
+ const catMap: Record<string, string> = {
2721
+ '/help': 'Session & Identity',
2722
+ '/version': 'Session & Identity',
2723
+ '/clear': 'Session & Identity',
2724
+ '/exit': 'Session & Identity',
2725
+ '/quit': 'Session & Identity',
2726
+ '/compact': 'Session & Identity',
2727
+ '/context': 'Session & Identity',
2728
+ '/status': 'Session & Identity',
2729
+ '/cost': 'Session & Identity',
2730
+ '/usage': 'Session & Identity',
2731
+ '/rename': 'Session & Identity',
2732
+ '/goal': 'Session & Identity',
2733
+ '/recap': 'Session & Identity',
2734
+ '/export': 'Session & Identity',
2735
+ '/doctor': 'Session & Identity',
2736
+ '/resume': 'Session & Identity',
2737
+ '/branch': 'Session & Identity',
2738
+ '/rewind': 'History',
2739
+ '/undo': 'History',
2740
+ '/copy': 'History',
2741
+ '/focus': 'History',
2742
+ '/pick': 'Model & Provider',
2743
+ '/model': 'Model & Provider',
2744
+ '/models': 'Model & Provider',
2745
+ '/provider': 'Model & Provider',
2746
+ '/providers': 'Model & Provider',
2747
+ '/switch': 'Model & Provider',
2748
+ '/config': 'Model & Provider',
2749
+ '/fast': 'Model & Provider',
2750
+ '/effort': 'Model & Provider',
2751
+ '/theme': 'Model & Provider',
2752
+ '/upgrade': 'Model & Provider',
2753
+ '/tools': 'Tools & Skills',
2754
+ '/skills': 'Tools & Skills',
2755
+ '/reload-skills': 'Tools & Skills',
2756
+ '/browse-skills': 'Tools & Skills',
2757
+ '/install-skill': 'Tools & Skills',
2758
+ '/remove-skill': 'Tools & Skills',
2759
+ '/mcp': 'Tools & Skills',
2760
+ '/commands': 'Tools & Skills',
2761
+ '/plan': 'Workflow',
2762
+ '/no-plan': 'Workflow',
2763
+ '/tdd': 'Workflow',
2764
+ '/todos': 'Workflow',
2765
+ '/tasks': 'Workflow',
2766
+ '/review': 'Workflow',
2767
+ '/pr-comments': 'Workflow',
2768
+ '/diff': 'Workflow',
2769
+ '/workflows': 'Workflow',
2770
+ '/loop': 'Workflow',
2771
+ '/init': 'Project',
2772
+ '/setup': 'Project',
2773
+ '/permissions': 'Project',
2774
+ '/add-dir': 'Project',
2775
+ '/recommend': 'Project',
2776
+ '/security': 'Project',
2777
+ '/audit': 'Project',
2778
+ '/ide': 'Environment',
2779
+ '/terminal-setup': 'Environment',
2780
+ '/memory': 'Environment',
2781
+ '/release-notes': 'Environment',
2782
+ '/login': 'Account',
2783
+ '/logout': 'Account',
2784
+ '/feedback': 'Account',
2785
+ '/agents': 'Agents',
2786
+ '/bg': 'Agents',
2787
+ '/artifact': 'Artifacts',
2788
+ '/schedule': 'Other',
2789
+ }
2790
+
2791
+ for (const name of names) {
2792
+ const cat = catMap[name] ?? 'Other'
2793
+ categories[cat]?.push(name)
2794
+ }
2795
+
2796
+ const lines: string[] = [`── All Slash Commands (${names.length}) ──`, '']
2797
+
2798
+ for (const [cat, cmds] of Object.entries(categories)) {
2799
+ if (cmds.length === 0) continue
2800
+ lines.push(`── ${cat} (${cmds.length}) ──`)
2801
+ for (const c of cmds) {
2802
+ lines.push(` ${c}`)
2803
+ }
2804
+ lines.push('')
2805
+ }
2806
+
2807
+ lines.push('Type /help for details, or /<command> to run.')
2808
+
2809
+ return { content: lines.join('\n') }
2810
+ }
2811
+
2276
2812
  // ═══════════════════════════════════════════════════════════════
2277
2813
  // Command Registry
2278
2814
  // ═══════════════════════════════════════════════════════════════
@@ -2314,6 +2850,10 @@ registry.set('/effort', effortCmd)
2314
2850
  registry.set('/tools', toolsCmd)
2315
2851
  registry.set('/skills', skillsCmd)
2316
2852
  registry.set('/reload-skills', reloadSkillsCmd)
2853
+ registry.set('/browse-skills', browseSkillsCmd)
2854
+ registry.set('/install-skill', installSkillCmd)
2855
+ registry.set('/remove-skill', removeSkillCmd)
2856
+ registry.set('/commands', commandsListCmd)
2317
2857
 
2318
2858
  // Workflow
2319
2859
  registry.set('/plan', planCmd)
@@ -2337,6 +2877,7 @@ registry.set('/upgrade', upgradeCmd)
2337
2877
  // Project
2338
2878
  registry.set('/init', initCmd)
2339
2879
  registry.set('/setup', setupCmd)
2880
+ registry.set('/recommend', recommendCmd)
2340
2881
  registry.set('/permissions', permissionsCmd)
2341
2882
  registry.set('/add-dir', addDirCmd)
2342
2883
  registry.set('/security', securityCmd)
@@ -2375,6 +2916,89 @@ export function getCommandNames(): string[] {
2375
2916
  return Array.from(registry.keys()).sort()
2376
2917
  }
2377
2918
 
2919
+ export interface CommandEntry {
2920
+ name: string
2921
+ description: string
2922
+ }
2923
+
2924
+ const COMMAND_DESCRIPTIONS: Record<string, string> = {
2925
+ '/help': 'Show help',
2926
+ '/commands': 'List all slash commands',
2927
+ '/version': 'Show version info',
2928
+ '/clear': 'Clear conversation',
2929
+ '/exit': 'Exit Mipham Code',
2930
+ '/quit': 'Exit Mipham Code',
2931
+ '/compact': 'Compact context window',
2932
+ '/context': 'Show context stats',
2933
+ '/status': 'Session and system status',
2934
+ '/cost': 'Token usage estimate',
2935
+ '/usage': 'Detailed usage dashboard',
2936
+ '/rename': 'Rename current session',
2937
+ '/goal': 'Set session goal',
2938
+ '/recap': 'Summarize session so far',
2939
+ '/export': 'Export conversation to file',
2940
+ '/doctor': 'System diagnostics',
2941
+ '/resume': 'List saved sessions',
2942
+ '/branch': 'Fork conversation',
2943
+ '/rewind': 'Undo last AI turn',
2944
+ '/undo': 'Same as /rewind',
2945
+ '/copy': 'Copy last response to clipboard',
2946
+ '/focus': 'Toggle focus view',
2947
+ '/pick': 'Open model picker',
2948
+ '/model': 'Show current model',
2949
+ '/models': 'List all available models',
2950
+ '/provider': 'Show current provider',
2951
+ '/providers': 'List configured providers',
2952
+ '/switch': 'Switch provider and model',
2953
+ '/config': 'View configuration',
2954
+ '/fast': 'Toggle fast mode',
2955
+ '/effort': 'Set reasoning effort',
2956
+ '/theme': 'Set terminal theme',
2957
+ '/tools': 'List available tools',
2958
+ '/skills': 'List loaded skills',
2959
+ '/reload-skills': 'Reload all skills',
2960
+ '/browse-skills': 'Browse community skill marketplace',
2961
+ '/install-skill': 'Install a skill by name or URL',
2962
+ '/remove-skill': 'Remove an installed skill',
2963
+ '/mcp': 'MCP server status',
2964
+ '/plan': 'Enter plan mode',
2965
+ '/no-plan': 'Exit plan mode',
2966
+ '/tdd': 'TDD mode',
2967
+ '/todos': 'Task management',
2968
+ '/tasks': 'Background tasks',
2969
+ '/review': 'Code review workflow',
2970
+ '/pr-comments': 'PR review summary',
2971
+ '/diff': 'Show git diff',
2972
+ '/workflows': 'List workflow scripts',
2973
+ '/loop': 'Run prompt on interval',
2974
+ '/init': 'Initialize .mipham config',
2975
+ '/setup': 'Guided project setup wizard',
2976
+ '/permissions': 'Show permission settings',
2977
+ '/add-dir': 'Add workspace directory',
2978
+ '/recommend': 'Analyze project + recommend skills & setup',
2979
+ '/security': 'Security review checklist',
2980
+ '/audit': 'Same as /security',
2981
+ '/ide': 'IDE integration guide',
2982
+ '/terminal-setup': 'Shell & terminal config',
2983
+ '/memory': 'Manage AI memories',
2984
+ '/release-notes': 'View version changelog',
2985
+ '/upgrade': 'Show upgrade instructions',
2986
+ '/login': 'Show API key status',
2987
+ '/logout': 'Clear credentials guide',
2988
+ '/feedback': 'Send feedback',
2989
+ '/agents': 'Agent view dashboard',
2990
+ '/bg': 'Run a background agent task',
2991
+ '/artifact': 'Manage artifacts',
2992
+ '/schedule': 'View scheduled tasks',
2993
+ }
2994
+
2995
+ export function getCommandList(): CommandEntry[] {
2996
+ return getCommandNames().map((name) => ({
2997
+ name,
2998
+ description: COMMAND_DESCRIPTIONS[name] ?? '',
2999
+ }))
3000
+ }
3001
+
2378
3002
  export function looksLikeSlashCommand(input: string): boolean {
2379
3003
  return input.trim().startsWith('/')
2380
3004
  }