@miphamai/cli 0.15.0 → 0.16.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.
@@ -10,6 +10,19 @@ import type { SkillsLoader } from '../skills/loader'
10
10
  import type { PluginManager } from '../plugin/plugin-manager'
11
11
  import { McpClient } from '../mcp/client'
12
12
  import { NPM_INSTALL_COMMAND, NPM_UPDATE_COMMAND, PACKAGE_VERSION } from '../shared/index.ts'
13
+ import { getPreference } from '../config/preferences'
14
+ import { stripIndent } from './strip-indent.js'
15
+ import {
16
+ initCmd,
17
+ setupCmd,
18
+ recommendCmd,
19
+ permissionsCmd,
20
+ addDirCmd,
21
+ securityCmd,
22
+ promptAuditCmd,
23
+ } from '../commands/project.js'
24
+ import { themeCmd, releaseNotesCmd, ideCmd, terminalSetupCmd } from '../commands/environment.js'
25
+ import { commitCmd, pushCmd, prCmd, issueCmd } from '../commands/git.js'
13
26
 
14
27
  export interface CommandContext {
15
28
  engine: QueryEngine
@@ -17,12 +30,14 @@ export interface CommandContext {
17
30
  providerId: string
18
31
  modelId: string
19
32
  version: string
33
+ sessionId: string
20
34
  // Callbacks for commands that mutate App state
21
35
  setSessionTitle: (title: string) => void
22
36
  setFastMode: (on: boolean) => void
23
37
  setEffort: (level: string) => void
24
38
  setFocusMode: (on: boolean) => void
25
39
  setGoal: (text: string) => void
40
+ setUltracodeMode: (on: boolean) => void
26
41
  skillsLoader?: SkillsLoader
27
42
  pluginManager?: PluginManager
28
43
  }
@@ -45,7 +60,7 @@ export interface CommandResult {
45
60
  forwardToAI?: string
46
61
  }
47
62
 
48
- type CommandHandler = (
63
+ export type CommandHandler = (
49
64
  ctx: CommandContext,
50
65
  args: string[],
51
66
  ) => CommandResult | Promise<CommandResult>
@@ -473,90 +488,6 @@ const todosCmd: CommandHandler = (_ctx, args) => {
473
488
  // Project
474
489
  // ═══════════════════════════════════════════════════════════════
475
490
 
476
- const initCmd: CommandHandler = async (ctx) => {
477
- const { existsSync, mkdirSync, writeFileSync } = await import('node:fs')
478
- const { join } = await import('node:path')
479
- const { homedir } = await import('node:os')
480
-
481
- const home = homedir()
482
- const userConfigPath = join(home, '.mipham', 'config.yml')
483
-
484
- // Generate user-friendly config if it doesn't exist yet
485
- if (!existsSync(userConfigPath)) {
486
- mkdirSync(join(home, '.mipham'), { recursive: true })
487
-
488
- const activeProviders = ctx.config.providers.filter((p) => p.status === 'active')
489
- const providerYaml = activeProviders
490
- .map((p) => {
491
- const tips: Record<string, string> = {
492
- anthropic: '# Get key: https://console.anthropic.com/',
493
- openai: '# Get key: https://platform.openai.com/api-keys',
494
- deepseek: '# Get key: https://platform.deepseek.com/api_keys',
495
- kimi: '# Get key: https://platform.moonshot.cn/',
496
- doubao: '# Get key: https://console.volcengine.com/ark',
497
- hunyuan: '# Get key: https://console.cloud.tencent.com/hunyuan',
498
- qwen: '# Get key: https://dashscope.console.aliyun.com/apiKey',
499
- google: '# Get key: https://aistudio.google.com/apikey',
500
- }
501
- const comment = tips[p.id] || ''
502
- const baseUrlLine = p.baseUrl ? `\n baseUrl: "${p.baseUrl}"` : ''
503
- return ` ${comment}
504
- - id: ${p.id}
505
- name: "${p.name}"${baseUrlLine}
506
- apiKey: "\${${p.id.toUpperCase()}_API_KEY}"`
507
- })
508
- .join('\n\n')
509
-
510
- const configContent = `# Mipham Code — User Configuration
511
- # Location: ~/.mipham/config.yml
512
- # Docs: https://mipham.ai/code/docs/config
513
- #
514
- # ═══ Quick Start ═══
515
- # 1. Replace the API key placeholders below with your real keys
516
- # 2. Save the file
517
- # 3. Run 'mipham' — it auto-detects configured providers
518
- #
519
- # ═══ Environment Variables (Alternative) ═══
520
- # Instead of editing this file, you can set env vars:
521
- # export ANTHROPIC_API_KEY="sk-ant-..."
522
- # export OPENAI_API_KEY="sk-..."
523
- # (The \${VAR} syntax below reads from environment variables)
524
-
525
- # ── Defaults ──
526
- defaultProvider: ${ctx.providerId}
527
- defaultModel: ${ctx.modelId}
528
- permission: ask
529
-
530
- # ── Providers (${activeProviders.length} pre-configured — just add your API keys) ──
531
- providers:
532
- ${providerYaml}
533
- `
534
-
535
- writeFileSync(userConfigPath, configContent, 'utf-8')
536
- return {
537
- content: `✅ Mipham Code initialized!
538
-
539
- Created: ~/.mipham/config.yml (${activeProviders.length} providers pre-configured)
540
-
541
- Next steps:
542
- 1. Edit ~/.mipham/config.yml — replace API key placeholders with your real keys
543
- 2. Run mipham to start
544
-
545
- Providers configured:
546
- ${activeProviders.map((p) => ` • ${p.name} — ${p.id.toUpperCase()}_API_KEY`).join('\n')}
547
-
548
- Tip: /setup for the full 6-step wizard.`,
549
- }
550
- }
551
-
552
- // Config already exists — show status
553
- return {
554
- content: `~/.mipham/config.yml already exists.
555
-
556
- Run /setup for the full wizard, or /config to view current settings.`,
557
- }
558
- }
559
-
560
491
  // ═══════════════════════════════════════════════════════════════
561
492
  // Phase 1 — New Session Commands
562
493
  // ═══════════════════════════════════════════════════════════════
@@ -799,7 +730,7 @@ const browseSkillsCmd: CommandHandler = async () => {
799
730
  return { content: lines.join('\n') }
800
731
  }
801
732
 
802
- const installSkillCmd: CommandHandler = async (_ctx, args) => {
733
+ const installSkillCmd: CommandHandler = async (ctx, args) => {
803
734
  const { installSkill, installSkillFromUrl } = await import('../skills/registry')
804
735
 
805
736
  const target = args[0]
@@ -807,12 +738,13 @@ const installSkillCmd: CommandHandler = async (_ctx, args) => {
807
738
  return { content: 'Usage: /install-skill <skill-name> or /install-skill <url>' }
808
739
  }
809
740
 
741
+ const marketplaceConfig = ctx.config.marketplace
810
742
  let result: { success: boolean; name: string; message: string }
811
743
 
812
744
  if (target.startsWith('http://') || target.startsWith('https://')) {
813
- result = installSkillFromUrl(target)
745
+ result = installSkillFromUrl(target, marketplaceConfig)
814
746
  } else {
815
- result = installSkill(target)
747
+ result = installSkill(target, marketplaceConfig)
816
748
  }
817
749
 
818
750
  return {
@@ -1162,6 +1094,52 @@ const focusCmd: CommandHandler = (ctx) => {
1162
1094
  }
1163
1095
  }
1164
1096
 
1097
+ // ═══════════════════════════════════════════════════════════════
1098
+ // Ultracode Mode — toggle multi-agent workflow orchestration
1099
+ // ═══════════════════════════════════════════════════════════════
1100
+
1101
+ const ultracodeCmd: CommandHandler = (ctx, args) => {
1102
+ const arg = args[0]?.toLowerCase()
1103
+ if (arg === 'on') {
1104
+ ctx.setUltracodeMode(true)
1105
+ return {
1106
+ content:
1107
+ '✓ Ultracode mode ON — multi-agent workflow orchestration enabled.\n\nThe AI will use Workflow tool to fan out, verify, and synthesize for all substantive tasks.',
1108
+ forwardToAI: stripIndent`
1109
+ Ultracode mode is now ACTIVE. For every substantive task:
1110
+ - Use the Workflow tool to orchestrate multi-agent execution
1111
+ - Default to pipeline() for multi-stage work — stages flow independently
1112
+ - Fan out independent work across parallel agents
1113
+ - Apply adversarial verification (verify with skeptics) to findings before accepting them
1114
+ - Synthesize results with a top-tier model pass
1115
+ - Prefer pipeline() over parallel() barriers — don't block stages unnecessarily
1116
+ - Use loopUntilConvergence when discovering unknown-size sets (bugs, issues, edge cases)
1117
+ - EDGE LOGIC IS FREE: flatten, dedupe, filter in plain JavaScript — NOT agent calls
1118
+ Keep responses concise — the workflow graph handles the depth.`,
1119
+ }
1120
+ } else if (arg === 'off') {
1121
+ ctx.setUltracodeMode(false)
1122
+ return {
1123
+ content: '✓ Ultracode mode OFF.',
1124
+ forwardToAI:
1125
+ 'Ultracode mode is now OFF. Revert to standard single-agent execution. Do NOT use Workflow tool unless explicitly asked.',
1126
+ }
1127
+ }
1128
+ return {
1129
+ content: stripIndent`
1130
+ Usage: /ultracode [on|off]
1131
+
1132
+ Ultracode mode enables multi-agent workflow orchestration for every substantive task.
1133
+ When ON, the AI uses the Workflow tool to decompose tasks, fan out parallel agents,
1134
+ verify findings adversarially, and synthesize results with a top-tier model.
1135
+
1136
+ Examples:
1137
+ /ultracode on — enable multi-agent mode
1138
+ /ultracode off — return to standard single-agent mode
1139
+ `,
1140
+ }
1141
+ }
1142
+
1165
1143
  // ═══════════════════════════════════════════════════════════════
1166
1144
  // Phase 1 — Workflow Commands
1167
1145
  // ═══════════════════════════════════════════════════════════════
@@ -1538,150 +1516,11 @@ const doctorCmd: CommandHandler = async (ctx) => {
1538
1516
  // GitHub & Git Workflow Commands (Claude Code parity)
1539
1517
  // ═══════════════════════════════════════════════════════════════
1540
1518
 
1541
- const commitCmd: CommandHandler = async () => {
1542
- try {
1543
- const { execSync } = await import('node:child_process')
1544
- const diff = execSync('git diff --cached --stat', { encoding: 'utf-8', timeout: 5000 }).trim()
1545
- const unstaged = execSync('git diff --name-only', { encoding: 'utf-8', timeout: 5000 }).trim()
1546
-
1547
- if (!diff) {
1548
- let msg = '── Git Commit ──\n\nNo staged changes found.\n\n'
1549
- if (unstaged) {
1550
- msg +=
1551
- 'Unstaged changes exist:\n' +
1552
- unstaged
1553
- .split('\n')
1554
- .map((f) => ' • ' + f)
1555
- .join('\n') +
1556
- '\n\n'
1557
- }
1558
- msg +=
1559
- 'Run: git add <files> to stage changes first.\nOr type: "commit these changes" for AI assistance.'
1560
- return { content: msg }
1561
- }
1562
-
1563
- return {
1564
- content: [
1565
- '── Git Commit ──',
1566
- '',
1567
- 'Staged changes:',
1568
- diff,
1569
- '',
1570
- 'To generate a commit message, type: "write a commit message for these changes"',
1571
- 'To commit: git commit -m "your message"',
1572
- '',
1573
- 'Tip: Use Conventional Commits format (feat:, fix:, chore:, docs:)',
1574
- ].join('\n'),
1575
- }
1576
- } catch {
1577
- return { content: '── Git Commit ──\n\nCould not run git. Are you in a git repository?' }
1578
- }
1579
- }
1580
-
1581
- const pushCmd: CommandHandler = async () => {
1582
- try {
1583
- const { execSync } = await import('node:child_process')
1584
- const branch = execSync('git branch --show-current', {
1585
- encoding: 'utf-8',
1586
- timeout: 3000,
1587
- }).trim()
1588
- const ahead = execSync(`git rev-list --count origin/${branch}..HEAD 2>/dev/null || echo 0`, {
1589
- encoding: 'utf-8',
1590
- timeout: 3000,
1591
- }).trim()
1592
-
1593
- return {
1594
- content: [
1595
- '── Git Push ──',
1596
- '',
1597
- `Branch: ${branch || '(unknown)'}`,
1598
- `Commits ahead of remote: ${ahead}`,
1599
- '',
1600
- ahead !== '0'
1601
- ? `Ready to push ${ahead} commit(s). Run: git push origin ${branch}`
1602
- : 'Nothing to push (up to date).',
1603
- '',
1604
- 'Or type: "push my changes" for AI assistance.',
1605
- ].join('\n'),
1606
- }
1607
- } catch {
1608
- return { content: '── Git Push ──\n\nCould not determine git status.' }
1609
- }
1610
- }
1611
-
1612
- const prCmd: CommandHandler = async () => {
1613
- try {
1614
- const { execSync } = await import('node:child_process')
1615
- const branch = execSync('git branch --show-current', {
1616
- encoding: 'utf-8',
1617
- timeout: 3000,
1618
- }).trim()
1619
- const mainBranch =
1620
- execSync('git remote show origin 2>/dev/null | grep "HEAD branch" | cut -d: -f2', {
1621
- encoding: 'utf-8',
1622
- timeout: 3000,
1623
- }).trim() || 'main'
1624
-
1625
- const diff = execSync(
1626
- `git diff --stat origin/${mainBranch}...HEAD 2>/dev/null || git diff --stat ${mainBranch}...HEAD 2>/dev/null || echo ""`,
1627
- { encoding: 'utf-8', timeout: 5000 },
1628
- ).trim()
1629
-
1630
- const commits = execSync(
1631
- `git log --oneline origin/${mainBranch}..HEAD 2>/dev/null || git log --oneline ${mainBranch}..HEAD 2>/dev/null || echo ""`,
1632
- { encoding: 'utf-8', timeout: 5000 },
1633
- ).trim()
1634
-
1635
- return {
1636
- content: [
1637
- '── Create Pull Request ──',
1638
- '',
1639
- `Branch: ${branch} → ${mainBranch}`,
1640
- '',
1641
- commits
1642
- ? `Commits:\n${commits
1643
- .split('\n')
1644
- .map((c) => ' ' + c)
1645
- .join('\n')}`
1646
- : 'No commits ahead.',
1647
- '',
1648
- diff ? `Changes:\n${diff}` : '',
1649
- '',
1650
- 'To create PR: type "create a pull request" for AI assistance.',
1651
- 'Or use: gh pr create --base ' + mainBranch + ' --head ' + branch,
1652
- ].join('\n'),
1653
- }
1654
- } catch {
1655
- return { content: '── Create Pull Request ──\n\nCould not determine PR context.' }
1656
- }
1657
- }
1658
-
1659
- const issueCmd: CommandHandler = async () => {
1660
- return {
1661
- content: [
1662
- '── GitHub Issues ──',
1663
- '',
1664
- 'To file an issue, type: "create a GitHub issue for [description]"',
1665
- '',
1666
- 'Quick commands:',
1667
- ' gh issue create --title "..." --body "..."',
1668
- ' gh issue list',
1669
- '',
1670
- 'Use /github-ops skill for advanced GitHub automation.',
1671
- ].join('\n'),
1672
- }
1673
- }
1674
-
1675
- // ═══════════════════════════════════════════════════════════════
1676
- // Code Quality — bridge slash commands to skills (Claude Code parity)
1677
- // ═══════════════════════════════════════════════════════════════
1678
-
1679
- /** Factory for git-diff-based commands that bridge to AI skills via forwardToAI. */
1680
1519
  function gitDiffBridgeCmd(opts: {
1681
1520
  label: string
1682
1521
  noChangesHint: string
1683
1522
  runningMsg: string
1684
- forwardToAI: string
1523
+ forwardToAI: string | (() => string)
1685
1524
  }): CommandHandler {
1686
1525
  return async () => {
1687
1526
  try {
@@ -1692,7 +1531,7 @@ function gitDiffBridgeCmd(opts: {
1692
1531
  }
1693
1532
  return {
1694
1533
  content: `─ ${opts.label} ─\n\n${opts.runningMsg}\n\nChanged files:\n${diff}`,
1695
- forwardToAI: opts.forwardToAI,
1534
+ forwardToAI: typeof opts.forwardToAI === 'function' ? opts.forwardToAI() : opts.forwardToAI,
1696
1535
  }
1697
1536
  } catch {
1698
1537
  return {
@@ -1708,8 +1547,8 @@ const codeReviewCmd = gitDiffBridgeCmd({
1708
1547
  'No uncommitted changes to review.\n\nTo review a specific file: /code-review path/to/file.ts',
1709
1548
  runningMsg:
1710
1549
  'Reviewing uncommitted changes with the code-review skill (7 dimensions: correctness, security, performance, code quality, architecture, testing, language-specific)...',
1711
- forwardToAI:
1712
- 'use the code-review skill to review all uncommitted changes. Check all 7 dimensions: correctness, security, performance, code quality, architecture & design, testing, and language-specific issues.',
1550
+ forwardToAI: () =>
1551
+ `use the code-review skill to review all uncommitted changes. Check all 7 dimensions: correctness, security, performance, code quality, architecture & design, testing, and language-specific issues. Use effort level: ${getPreference('lastCodeReviewEffort', 'high')}.`,
1713
1552
  })
1714
1553
 
1715
1554
  const simplifyCmd = gitDiffBridgeCmd({
@@ -1887,7 +1726,7 @@ const summaryCmd: CommandHandler = (ctx) => {
1887
1726
  }
1888
1727
  }
1889
1728
 
1890
- const cdCmd: CommandHandler = async (_ctx, args) => {
1729
+ const cdCmd: CommandHandler = async (ctx, args) => {
1891
1730
  const target = args[0]
1892
1731
  if (!target) {
1893
1732
  return {
@@ -1912,6 +1751,22 @@ const cdCmd: CommandHandler = async (_ctx, args) => {
1912
1751
 
1913
1752
  try {
1914
1753
  process.chdir(resolved)
1754
+
1755
+ // Persist cwd to active session (best-effort)
1756
+ try {
1757
+ const { SessionStore } = await import('../core/session-store')
1758
+ const saved = SessionStore.load(ctx.sessionId)
1759
+ if (saved) {
1760
+ SessionStore.save(ctx.sessionId, saved.messages, {
1761
+ provider: saved.metadata.provider,
1762
+ model: saved.metadata.model,
1763
+ cwd: resolved,
1764
+ })
1765
+ }
1766
+ } catch {
1767
+ /* session persistence is best-effort */
1768
+ }
1769
+
1915
1770
  return {
1916
1771
  content: [
1917
1772
  '── Directory Changed ──',
@@ -2039,48 +1894,10 @@ const exportCmd: CommandHandler = async (ctx) => {
2039
1894
  }
2040
1895
 
2041
1896
  // ═══════════════════════════════════════════════════════════════
2042
- // Review — code review workflow
1897
+ // Review — alias for /code-review (P1: 2026-08-06 polish)
2043
1898
  // ═══════════════════════════════════════════════════════════════
2044
1899
 
2045
- const reviewCmd: CommandHandler = async () => {
2046
- try {
2047
- const { execSync } = await import('node:child_process')
2048
- const diff = execSync('git diff --stat', { encoding: 'utf-8', timeout: 5000 }).trim()
2049
- const unstaged = execSync('git diff --name-only', { encoding: 'utf-8', timeout: 3000 }).trim()
2050
- const staged = execSync('git diff --cached --name-only', {
2051
- encoding: 'utf-8',
2052
- timeout: 3000,
2053
- }).trim()
2054
-
2055
- if (!diff) {
2056
- return {
2057
- content:
2058
- '─ Code Review ─\n\nNo uncommitted changes detected.\n\nUse /pr-comments for PR-level review, or make changes first.',
2059
- }
2060
- }
2061
-
2062
- const lines: string[] = ['─ Code Review ─', '', 'Uncommitted changes:', '', diff]
2063
-
2064
- if (staged) {
2065
- lines.push('')
2066
- lines.push('Staged files (ready for commit):')
2067
- for (const f of staged.split('\n')) lines.push(` ✓ ${f}`)
2068
- }
2069
- if (unstaged) {
2070
- lines.push('')
2071
- lines.push('Unstaged files (working directory):')
2072
- for (const f of unstaged.split('\n')) lines.push(` • ${f}`)
2073
- }
2074
-
2075
- lines.push('')
2076
- lines.push('To review with AI: type "review these changes" in chat.')
2077
- lines.push('To commit: git add -A && git commit -m "..."')
2078
-
2079
- return { content: lines.join('\n') }
2080
- } catch {
2081
- return { content: '─ Code Review ─\n\nCould not run git diff. Are you in a git repository?' }
2082
- }
2083
- }
1900
+ const reviewCmd = codeReviewCmd
2084
1901
 
2085
1902
  // ═══════════════════════════════════════════════════════════════
2086
1903
  // PR Comments
@@ -2519,1219 +2336,110 @@ const workflowAutoCmd: CommandHandler = async (_ctx, args) => {
2519
2336
  }
2520
2337
 
2521
2338
  // ═══════════════════════════════════════════════════════════════
2522
- // Permissions
2339
+ // /deep-research <topic> — multi-agent deep research pipeline
2523
2340
  // ═══════════════════════════════════════════════════════════════
2524
2341
 
2525
- const permissionsCmd: CommandHandler = (ctx) => {
2526
- const c = ctx.engine.getContext()
2527
- const msgs = c.getMessages()
2528
-
2529
- return {
2530
- content: `─ Permission Settings ─
2531
-
2532
- Mode: ${ctx.config.permission}
2533
- Messages: ${msgs.length} in context
2534
- Tools: ${ctx.engine.getTools().size} available
2535
-
2536
- Permission levels:
2537
- auto — run tools automatically without asking
2538
- ask — prompt before each tool execution (default)
2539
- bypass — skip all permission checks (use with caution)
2540
-
2541
- Change with /config permission <level>.
2542
-
2543
- Current directory permissions:
2544
- CWD: ${process.cwd()}
2545
-
2546
- To add a directory: use /add-dir (coming soon).
2547
- Tool execution is sandboxed to the project directory by default.`,
2548
- }
2549
- }
2550
-
2551
- // ═══════════════════════════════════════════════════════════════
2552
- // Recommend — analyze project and recommend setup (like Claude Code
2553
- // automation recommender)
2554
- // ═══════════════════════════════════════════════════════════════
2342
+ const deepResearchCmd: CommandHandler = (_ctx, args) => {
2343
+ const topic = args.join(' ').trim()
2344
+ if (!topic) {
2345
+ return {
2346
+ content: stripIndent`
2347
+ Usage: /deep-research <topic>
2555
2348
 
2556
- const recommendCmd: CommandHandler = async (ctx) => {
2557
- const { existsSync, readFileSync } = await import('node:fs')
2558
- const { join } = await import('node:path')
2559
- const { getAvailableSkills } = await import('../skills/registry')
2349
+ Conducts multi-agent deep research — 5-angle parallel search, adversarial verification,
2350
+ and top-tier synthesis into a well-cited report.
2560
2351
 
2561
- const cwd = process.cwd()
2562
- const lines: string[] = ['── Mipham Code: Setup Recommendations ──', '', `Project: ${cwd}`, '']
2563
-
2564
- // ── Detect project type ──
2565
- const hasPackageJson = existsSync(join(cwd, 'package.json'))
2566
- const hasTsConfig = existsSync(join(cwd, 'tsconfig.json'))
2567
- const hasPyProject = existsSync(join(cwd, 'pyproject.toml'))
2568
- const hasRequirements = existsSync(join(cwd, 'requirements.txt'))
2569
- const hasDockerfile = existsSync(join(cwd, 'Dockerfile'))
2570
- const hasGitHubActions = existsSync(join(cwd, '.github', 'workflows'))
2571
- const hasNextConfig =
2572
- existsSync(join(cwd, 'next.config.js')) || existsSync(join(cwd, 'next.config.ts'))
2573
- const hasVueConfig =
2574
- existsSync(join(cwd, 'vite.config.ts')) || existsSync(join(cwd, 'vite.config.js'))
2575
- const hasTailwind =
2576
- existsSync(join(cwd, 'tailwind.config.ts')) || existsSync(join(cwd, 'tailwind.config.js'))
2577
-
2578
- let pkgData: Record<string, unknown> = {}
2579
- if (hasPackageJson) {
2580
- try {
2581
- pkgData = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8'))
2582
- } catch {
2583
- /* ignore */
2352
+ Example:
2353
+ /deep-research impact of quantum computing on cryptography
2354
+ /deep-research latest advances in CRISPR gene editing
2355
+ `,
2584
2356
  }
2585
2357
  }
2586
- const deps = {
2587
- ...((pkgData.dependencies as Record<string, string>) || {}),
2588
- ...((pkgData.devDependencies as Record<string, string>) || {}),
2589
- }
2590
- const isTypeScript = hasTsConfig || 'typescript' in deps
2591
- const isReact = 'react' in deps || 'next' in deps
2592
- const isVue = 'vue' in deps
2593
- const isNode = hasPackageJson
2594
- const isPython = hasPyProject || hasRequirements
2595
- const isFastAPI = 'fastapi' in deps
2596
- const isNextJS = 'next' in deps
2597
- const isExpress = 'express' in deps || 'fastify' in deps
2598
- const isDocker = hasDockerfile
2599
-
2600
- // ── Detection summary ──
2601
- const tags: string[] = []
2602
- if (isTypeScript) tags.push('TypeScript')
2603
- if (isReact) tags.push('React')
2604
- if (isVue) tags.push('Vue')
2605
- if (isNextJS) tags.push('Next.js')
2606
- if (isExpress) tags.push('Node.js API')
2607
- if (isFastAPI) tags.push('FastAPI')
2608
- if (isPython) tags.push('Python')
2609
- if (isDocker) tags.push('Docker')
2610
- if (hasTailwind) tags.push('Tailwind CSS')
2611
- if (hasGitHubActions) tags.push('CI/CD')
2612
-
2613
- lines.push('── Detected Stack ──')
2614
- lines.push('')
2615
- if (tags.length > 0) {
2616
- lines.push(` ${tags.join(' · ')}`)
2617
- } else {
2618
- lines.push(' (generic project — no specific framework detected)')
2619
- }
2620
- lines.push('')
2621
-
2622
- // ── Skill recommendations ──
2623
- const communitySkills = getAvailableSkills()
2624
- const recommendedSkills: string[] = []
2625
-
2626
- // Always useful
2627
- recommendedSkills.push('code-review')
2628
- recommendedSkills.push('systematic-debugging')
2358
+ return {
2359
+ content: stripIndent`
2360
+ ── Deep Research ──
2629
2361
 
2630
- if (isTypeScript || isNode) {
2631
- recommendedSkills.push('github-ops')
2632
- }
2633
- if (isReact || isVue || isNextJS) {
2634
- recommendedSkills.push('frontend-design')
2635
- }
2636
- if (hasGitHubActions) {
2637
- recommendedSkills.push('security-review')
2638
- }
2639
- if (isPython) {
2640
- recommendedSkills.push('doc-generator')
2641
- }
2362
+ Topic: "${topic}"
2642
2363
 
2643
- // Filter to only those in the registry
2644
- const available = recommendedSkills.filter((name) => communitySkills.some((s) => s.name === name))
2364
+ Orchestrating multi-agent research pipeline:
2365
+ 1. Scope — 5 research angles
2366
+ 2. Research — parallel web searches
2367
+ 3. Reduce — deduplicate + merge
2368
+ 4. Verify — adversarial skeptic pass
2369
+ 5. Synthesize — cited report, top model
2645
2370
 
2646
- lines.push('── Recommended Skills ──')
2647
- lines.push('')
2648
- if (available.length > 0) {
2649
- for (const name of available) {
2650
- const entry = communitySkills.find((s) => s.name === name)!
2651
- lines.push(` /install-skill ${name.padEnd(26)} ${entry.description}`)
2652
- }
2653
- }
2654
- lines.push(' /browse-skills Browse all community skills')
2655
- lines.push('')
2371
+ This may take a few minutes...
2372
+ `,
2373
+ forwardToAI: stripIndent`
2374
+ Conduct deep research on: "${topic}"
2656
2375
 
2657
- // ── Provider recommendations ──
2658
- const activeProviders = ctx.config.providers.filter((p) => p.status === 'active')
2659
- const configured = activeProviders.filter((p) => p.apiKey && p.apiKey.trim() !== '')
2376
+ Use the Workflow tool to orchestrate a multi-agent research pipeline:
2660
2377
 
2661
- lines.push('── Provider Status ──')
2662
- lines.push('')
2663
- if (configured.length === 0) {
2664
- lines.push(' ⚠ No providers have API keys configured.')
2665
- lines.push(' Run /setup 2 to configure providers.')
2666
- lines.push('')
2667
- lines.push(' Recommended for this project:')
2668
- if (isTypeScript || isNode || isReact) {
2669
- lines.push(' • anthropic — Claude (code generation, review)')
2670
- lines.push(' • openai — GPT-5 (general purpose)')
2671
- }
2672
- if (isPython) {
2673
- lines.push(' • anthropic — Claude (data science, ML)')
2674
- }
2675
- } else {
2676
- lines.push(` ${configured.length}/${activeProviders.length} providers configured`)
2677
- for (const p of configured) {
2678
- lines.push(` ✅ ${p.id.padEnd(14)} ${p.name}`)
2679
- }
2680
- }
2681
- lines.push('')
2378
+ **Phase 1 — Scope**: Define 5 research angles covering: overview, technical-details,
2379
+ competitors/alternatives, criticism/limitations, future-trends.
2682
2380
 
2683
- // ── Config recommendations ──
2684
- lines.push('── Configuration Tips ──')
2685
- lines.push('')
2381
+ **Phase 2 — Research**: Fan out parallel web searches across all 5 angles. Each agent
2382
+ searches the web and returns sources with titles, URLs, and key points. Use schema
2383
+ validation to ensure structured output.
2686
2384
 
2687
- const hasProjectMipham = existsSync(join(cwd, '.mipham'))
2688
- if (!hasProjectMipham) {
2689
- lines.push(' /setup 1 Initialize .mipham/ + MIPHAM.md + config.yml')
2690
- }
2385
+ **Phase 3 — Reduce**: Deduplicate sources by URL. Merge overlapping findings. EDGE LOGIC
2386
+ IS FREE — use plain JavaScript, not agent calls.
2691
2387
 
2692
- const { listInstalledSkills } = await import('../skills/registry')
2693
- const installed = listInstalledSkills()
2694
- if (installed.length === 0 && available.length > 0) {
2695
- lines.push(' Tip: Install recommended skills above for better AI assistance')
2696
- }
2388
+ **Phase 4 — Verify**: Run adversarial verification (verify with mode: 'adversarial',
2389
+ skeptics: 2, threshold: 1) on each key claim. Discard claims that fail verification.
2697
2390
 
2698
- if (isDocker && !hasProjectMipham) {
2699
- lines.push(' Tip: Add .mipham/ to .dockerignore for smaller images')
2700
- }
2391
+ **Phase 5 — Synthesize**: Use the best available model to synthesize all verified findings
2392
+ into a comprehensive, well-cited research report. Include:
2393
+ - Executive summary
2394
+ - Key findings with source citations (title, URL)
2395
+ - Competing perspectives and disagreements
2396
+ - Gaps and unknowns
2397
+ - Recommendations for further reading
2701
2398
 
2702
- if (hasGitHubActions) {
2703
- lines.push(' /setup 5 Configure tool permissions for CI/CD safety')
2399
+ Output the final report as a structured markdown document with inline citations.
2400
+ `,
2704
2401
  }
2705
-
2706
- lines.push(' /setup Full setup wizard (6 steps)')
2707
- lines.push(' /help All commands')
2708
- lines.push('')
2709
-
2710
- return { content: lines.join('\n') }
2711
2402
  }
2712
2403
 
2713
2404
  // ═══════════════════════════════════════════════════════════════
2714
- // Setup — guided project initialization (mirrors Claude Code /setup)
2405
+ // Permissions
2715
2406
  // ═══════════════════════════════════════════════════════════════
2716
2407
 
2717
- const setupCmd: CommandHandler = async (ctx, args) => {
2718
- const step = args[0]
2719
-
2720
- // ── Step selection ──
2721
- if (step === '1' || step === 'init') {
2722
- return setupStep1(ctx)
2723
- }
2724
- if (step === '2' || step === 'providers') {
2725
- return setupStep2(ctx)
2726
- }
2727
- if (step === '3' || step === 'model') {
2728
- return setupStep3(ctx)
2729
- }
2730
- if (step === '4' || step === 'skills') {
2731
- return setupStep4(ctx)
2732
- }
2733
- if (step === '5' || step === 'permissions') {
2734
- return setupStep5(ctx)
2735
- }
2736
- if (step === '6' || step === 'shell') {
2737
- return setupStep6(ctx)
2738
- }
2739
-
2740
- // ── Check existing setup status ──
2741
- const { existsSync } = await import('node:fs')
2742
- const { join } = await import('node:path')
2743
- const cwd = process.cwd()
2744
- const home = process.env.HOME || '~'
2745
-
2746
- const hasProjectMipham = existsSync(join(cwd, 'MIPHAM.md'))
2747
- const hasProjectConfig = existsSync(join(cwd, '.mipham', 'config.yml'))
2748
- const hasUserConfig = existsSync(join(home, '.mipham', 'config.yml'))
2749
- const hasMiphamDir = existsSync(join(cwd, '.mipham'))
2750
-
2751
- const activeProviders = ctx.config.providers.filter((p) => p.status === 'active').length
2752
- const totalProviders = ctx.config.providers.length
2753
- const skills = ctx.skillsLoader?.list() || []
2754
- const standardSkills = skills.filter((s: { type: string }) => s.type === 'standard').length
2755
- const miphamSkills = skills.filter((s: { type: string }) => s.type === 'mipham').length
2756
-
2757
- const statusIcon = (ok: boolean) => (ok ? '✅' : '⬜')
2758
-
2759
- return {
2760
- content: `── Mipham Code Setup ──
2408
+ // ═══════════════════════════════════════════════════════════════
2409
+ // Recommend — analyze project and recommend setup (like Claude Code
2410
+ // automation recommender)
2411
+ // ═══════════════════════════════════════════════════════════════
2761
2412
 
2413
+ // ═══════════════════════════════════════════════════════════════
2414
+ // Setup — guided project initialization (mirrors Claude Code /setup)
2415
+ // ═══════════════════════════════════════════════════════════════
2762
2416
 
2763
- Project Status
2764
- ${statusIcon(hasMiphamDir)} .mipham/ directory ${hasMiphamDir ? '(config + metadata)' : '(not created)'}
2765
- ${statusIcon(hasProjectMipham)} MIPHAM.md ${hasProjectMipham ? '(project personality)' : '(not created)'}
2766
- ${statusIcon(hasProjectConfig)} Project config ${hasProjectConfig ? '~/.mipham/config.yml' : '(not created)'}
2767
- ${statusIcon(hasUserConfig)} User config ${hasUserConfig ? '~/.mipham/config.yml' : '(not created)'}
2417
+ // ═══════════════════════════════════════════════════════════════
2418
+ // Theme — display theme toggle
2419
+ // ═══════════════════════════════════════════════════════════════
2768
2420
 
2769
- Providers
2770
- ${activeProviders}/${totalProviders} active · Current: ${ctx.providerId}/${ctx.modelId}
2421
+ const mcpCmd: CommandHandler = (ctx) => {
2422
+ const configuredServers = ctx.config.skills?.mcpServers ?? []
2423
+ const liveConnections = McpClient.getInstance().listConnections()
2771
2424
 
2772
- Skills
2773
- ${skills.length} loaded (${standardSkills} standard + ${miphamSkills} mipham)
2425
+ const lines: string[] = ['── MCP Servers ──', '']
2774
2426
 
2775
- Permissions
2776
- Mode: ${ctx.config.permission} · Tools: ${ctx.engine.getTools().size}
2777
-
2778
-
2779
- ── Setup Steps ──
2780
-
2781
- 1. Initialize Project /setup 1 Create .mipham/ + MIPHAM.md + config.yml
2782
- 2. Configure Providers /setup 2 Set API keys, enable/disable providers
2783
- 3. Set Default Model /setup 3 Choose your preferred provider & model
2784
- 4. Install Skills /setup 4 Browse and install community skills
2785
- 5. Permissions Setup /setup 5 Configure tool access and security
2786
- 6. Shell Integration /setup 6 Add \`mipham\` to PATH, IDE setup
2787
-
2788
- Run: /setup <number> or chat: "help me set up Mipham Code"`,
2789
- }
2790
- }
2791
-
2792
- async function setupStep1(ctx: CommandContext): Promise<CommandResult> {
2793
- const { existsSync, mkdirSync, writeFileSync } = await import('node:fs')
2794
- const { join } = await import('node:path')
2795
- const cwd = process.cwd()
2796
-
2797
- const miphamDir = join(cwd, '.mipham')
2798
- const miphamPath = join(cwd, 'MIPHAM.md')
2799
- const configPath = join(miphamDir, 'config.yml')
2800
-
2801
- const created: string[] = []
2802
- const skipped: string[] = []
2803
-
2804
- // Create .mipham/ directory
2805
- if (!existsSync(miphamDir)) {
2806
- mkdirSync(miphamDir, { recursive: true })
2807
- created.push('.mipham/')
2808
- } else {
2809
- skipped.push('.mipham/ (already exists)')
2810
- }
2811
-
2812
- // Create project config if missing
2813
- if (!existsSync(configPath)) {
2814
- // Generate a user-friendly config with all providers pre-populated.
2815
- // Users just need to replace the API key placeholders with their real keys.
2816
- const activeProviders = ctx.config.providers.filter((p) => p.status === 'active')
2817
- const providerYaml = activeProviders
2818
- .map((p) => {
2819
- const comment =
2820
- p.id === 'anthropic'
2821
- ? '# Get key: https://console.anthropic.com/'
2822
- : p.id === 'openai'
2823
- ? '# Get key: https://platform.openai.com/api-keys'
2824
- : p.id === 'deepseek'
2825
- ? '# Get key: https://platform.deepseek.com/api_keys'
2826
- : p.id === 'kimi'
2827
- ? '# Get key: https://platform.moonshot.cn/'
2828
- : p.id === 'doubao'
2829
- ? '# Get key: https://console.volcengine.com/ark'
2830
- : p.id === 'hunyuan'
2831
- ? '# Get key: https://console.cloud.tencent.com/hunyuan'
2832
- : p.id === 'qwen'
2833
- ? '# Get key: https://dashscope.console.aliyun.com/apiKey'
2834
- : p.id === 'google'
2835
- ? '# Get key: https://aistudio.google.com/apikey'
2836
- : ''
2837
- const baseUrlLine = p.baseUrl ? `\n baseUrl: "${p.baseUrl}"` : ''
2838
- return ` ${comment}
2839
- - id: ${p.id}
2840
- name: "${p.name}"${baseUrlLine}
2841
- apiKey: "\${${p.id.toUpperCase()}_API_KEY}"`
2842
- })
2843
- .join('\n\n')
2844
-
2845
- const defaultConfig = `# Mipham Code — User Configuration
2846
- # Location: ~/.mipham/config.yml
2847
- # Docs: https://mipham.ai/code/docs/config
2848
- #
2849
- # ═══ Quick Start ═══
2850
- # 1. Set your API keys below (replace the placeholder values)
2851
- # 2. Save the file
2852
- # 3. Run 'mipham' — it auto-detects configured providers
2853
- #
2854
- # ═══ Environment Variables ═══
2855
- # Instead of editing this file, you can set env vars:
2856
- # export ANTHROPIC_API_KEY="sk-ant-..."
2857
- # export OPENAI_API_KEY="sk-..."
2858
- # (The \${VAR} syntax below reads from environment variables)
2859
-
2860
- # ── Defaults ──
2861
- defaultProvider: ${ctx.providerId}
2862
- defaultModel: ${ctx.modelId}
2863
- permission: ask
2864
-
2865
- # ── Providers (8 configured, just add your API keys) ──
2866
- providers:
2867
- ${providerYaml}
2868
- `
2869
- writeFileSync(configPath, defaultConfig, 'utf-8')
2870
- created.push('.mipham/config.yml')
2871
- } else {
2872
- skipped.push('.mipham/config.yml (already exists)')
2873
- }
2874
-
2875
- // Create MIPHAM.md if missing
2876
- if (!existsSync(miphamPath)) {
2877
- const projectName = cwd.split('/').pop() || 'my-project'
2878
- const defaultMipham = `---
2879
- model: mipham-code
2880
- version: 1.0.0
2881
- privacy: project
2882
- language: zh-CN
2883
- ---
2884
-
2885
- # MIPHAM.md — ${projectName}
2886
-
2887
- > 本文件定义 ${projectName} 项目中 AI 助手的交互人格和项目规范。
2888
- > 继承自 One Mipham Corporation 集团 MIPHAM.md。
2889
-
2890
- ---
2891
-
2892
- ## 项目概述
2893
-
2894
- [简要描述项目目的和定位]
2895
-
2896
- ## 技术栈
2897
-
2898
- [列出主要技术栈]
2899
-
2900
- ## 项目规范
2901
-
2902
- - [添加项目特有的编码规则]
2903
- - [添加团队约定]
2904
-
2905
- ## AI 交互偏好
2906
-
2907
- - 回复语言:[中文/英文]
2908
- - 代码风格:[偏好]
2909
- - 注释语言:[中文/英文]
2910
- `
2911
- writeFileSync(miphamPath, defaultMipham, 'utf-8')
2912
- created.push('MIPHAM.md')
2913
- } else {
2914
- skipped.push('MIPHAM.md (already exists)')
2915
- }
2916
-
2917
- const lines: string[] = ['── Step 1: Initialize Project ──', '']
2918
- if (created.length > 0) {
2919
- lines.push('Created:')
2920
- for (const c of created) lines.push(` ✅ ${c}`)
2921
- }
2922
- if (skipped.length > 0) {
2923
- lines.push('')
2924
- lines.push('Skipped (already configured):')
2925
- for (const s of skipped) lines.push(` ⏭ ${s}`)
2926
- }
2927
- lines.push('')
2928
- lines.push('Next: /setup 2 to configure providers')
2929
-
2930
- return { content: lines.join('\n') }
2931
- }
2932
-
2933
- async function setupStep2(ctx: CommandContext): Promise<CommandResult> {
2934
- const active = ctx.config.providers.filter((p) => p.status === 'active')
2935
- const upcoming = ctx.config.providers.filter((p) => p.status === 'upcoming')
2936
-
2937
- const lines: string[] = [
2938
- '── Step 2: Configure Providers ──',
2939
- '',
2940
- `Active providers (${active.length}):`,
2941
- ...active.map((p) => ` ✅ ${p.id.padEnd(14)} ${p.name.padEnd(20)} ${p.protocol}`),
2942
- '',
2943
- `Upcoming (${upcoming.length}):`,
2944
- ...upcoming.map((p) => ` 🔶 ${p.id.padEnd(14)} ${p.name.padEnd(20)} ${p.protocol}`),
2945
- '',
2946
- '── API Key Setup ──',
2947
- '',
2948
- 'Set API keys via environment variables or .mipham/config.yml:',
2949
- '',
2950
- ' export ANTHROPIC_API_KEY="sk-ant-..."',
2951
- ' export OPENAI_API_KEY="sk-..."',
2952
- ' export DEEPSEEK_API_KEY="sk-..."',
2953
- ' export QWEN_API_KEY="sk-..."',
2954
- ' export DOUBAO_API_KEY="..."',
2955
- ' export HUNYUAN_API_KEY="..."',
2956
- '',
2957
- 'Or add to ~/.mipham/config.yml:',
2958
- ' providers:',
2959
- ' - id: anthropic',
2960
- ' apiKey: $ANTHROPIC_API_KEY',
2961
- '',
2962
- 'Current: ' + ctx.providerId + ' / ' + ctx.modelId,
2963
- '',
2964
- 'Next: /setup 3 to choose default model',
2965
- ]
2966
-
2967
- return { content: lines.join('\n') }
2968
- }
2969
-
2970
- async function setupStep3(ctx: CommandContext): Promise<CommandResult> {
2971
- const activeProviders = ctx.config.providers.filter((p) => p.status === 'active')
2972
-
2973
- const lines: string[] = [
2974
- '── Step 3: Set Default Model ──',
2975
- '',
2976
- `Current: ${ctx.providerId} / ${ctx.modelId}`,
2977
- '',
2978
- 'Available providers & models:',
2979
- '',
2980
- ]
2981
-
2982
- for (const p of activeProviders) {
2983
- lines.push(` ${p.id}${p.id === ctx.providerId ? ' ← current' : ''}`)
2984
- for (const m of p.models.filter((m) => m.status === 'active')) {
2985
- const marker = m.id === ctx.modelId ? ' ★' : ' '
2986
- lines.push(
2987
- `${marker} ${m.id.padEnd(30)} ${m.contextWindow.toLocaleString()} ctx ${m.vision ? '🖼' : '📝'}`,
2988
- )
2989
- }
2990
- lines.push('')
2991
- }
2992
-
2993
- lines.push('To switch: /switch <provider> <model>')
2994
- lines.push('To make permanent: edit .mipham/config.yml → defaultProvider / defaultModel')
2995
- lines.push('')
2996
- lines.push('Next: /setup 4 to install skills')
2997
-
2998
- return { content: lines.join('\n') }
2999
- }
3000
-
3001
- async function setupStep4(ctx: CommandContext): Promise<CommandResult> {
3002
- const counts = ctx.skillsLoader?.countByType() ?? { standard: 0, mipham: 0, total: 0 }
3003
- const standardNames = ctx.skillsLoader?.getNamesByType('standard') ?? []
3004
- const miphamNames = ctx.skillsLoader?.getNamesByType('mipham') ?? []
3005
-
3006
- return {
3007
- content: `── Step 4: Install Skills ──
3008
-
3009
- Skills extend Mipham Code with specialized capabilities.
3010
-
3011
- Built-in skills (${counts.total} total):
3012
- Standard (${counts.standard}): ${standardNames.join(', ')}
3013
- Mipham (${counts.mipham}): ${miphamNames.join(', ')}
3014
-
3015
- Community skills:
3016
- Coming soon — the Mipham Code skills marketplace will let you
3017
- browse and install community-contributed skills.
3018
-
3019
- For now, add custom skills manually:
3020
- 1. Create a .SKILL.md file in .mipham/skills/
3021
- 2. Use /reload-skills to load it
3022
-
3023
- Skill file template:
3024
- ---
3025
- name: my-skill
3026
- description: What this skill does
3027
- version: 1.0.0
3028
- type: standard
3029
- ---
3030
- # My Skill
3031
- [instructions for the AI]
3032
-
3033
- Next: /setup 5 to configure permissions`,
3034
- }
3035
- }
3036
-
3037
- async function setupStep5(ctx: CommandContext): Promise<CommandResult> {
3038
- return {
3039
- content: `── Step 5: Permissions Setup ──
3040
-
3041
- Current mode: ${ctx.config.permission}
3042
-
3043
- Permission levels:
3044
- auto — Run tools automatically (suitable for sandboxed envs)
3045
- ask — Prompt before each tool execution (default, recommended)
3046
- bypass — Skip all checks (⚠ only for trusted codebases)
3047
-
3048
- Change with: /config permission <level>
3049
-
3050
- Available tools (${ctx.engine.getTools().size}):
3051
- File: read, write, edit, glob, grep
3052
- Exec: bash, git, task
3053
- Agent: agent, memory, plan, skill
3054
- Net: web-fetch, web-search
3055
- Sys: config, mcp
3056
-
3057
- Each tool category can be configured independently in .mipham/config.yml:
3058
- permissions:
3059
- file: ask
3060
- exec: ask
3061
- network: auto
3062
-
3063
- Next: /setup 6 for shell integration`,
3064
- }
3065
- }
3066
-
3067
- async function setupStep6(_ctx: CommandContext): Promise<CommandResult> {
3068
- return {
3069
- content: `── Step 6: Shell Integration ──
3070
-
3071
- Add Mipham Code to your shell:
3072
-
3073
- # Add to ~/.zshrc or ~/.bashrc
3074
- alias mipham='cd ~/your-project && bun run ~/path/to/mipham-code/apps/cli/bin/mipham.ts'
3075
-
3076
- # Or if installed globally:
3077
- alias mipham='mipham'
3078
-
3079
- IDE Integration:
3080
- VS Code — coming soon (extension marketplace)
3081
- JetBrains — coming soon (plugin)
3082
- Terminal — run \`mipham\` in any terminal
3083
-
3084
- Quick launch:
3085
- Ctrl+P Open model picker
3086
- /help Show all commands
3087
- Esc Exit
3088
-
3089
- ── Setup Complete! ──
3090
-
3091
- You're all set. Start a conversation:
3092
- "help me build a REST API"
3093
- "review my code"
3094
- "explain this project"
3095
-
3096
- For help at any time: /help`,
3097
- }
3098
- }
3099
-
3100
- // ═══════════════════════════════════════════════════════════════
3101
- // Theme — display theme toggle
3102
- // ═══════════════════════════════════════════════════════════════
3103
-
3104
- const themeCmd: CommandHandler = (_ctx, args) => {
3105
- const theme = args[0]?.toLowerCase()
3106
- const validThemes = ['dark', 'light', 'auto'] as const
3107
-
3108
- if (!theme || !validThemes.includes(theme as (typeof validThemes)[number])) {
3109
- return {
3110
- content: `── Theme ──
3111
-
3112
- Terminal color themes (set in .mipham/config.yml):
3113
-
3114
- theme: dark — dark background (default, recommended for terminals)
3115
- theme: light — light background
3116
- theme: auto — follow system preference
3117
-
3118
- Usage: /theme dark | light | auto
3119
-
3120
- Current: auto (follows terminal)`,
3121
- }
3122
- }
3123
-
3124
- return {
3125
- content: `✓ Theme set to "${theme}".
3126
-
3127
- Add to ~/.mipham/config.yml to persist:
3128
- theme: ${theme}
3129
-
3130
- Terminal themes affect syntax highlighting and UI accents.
3131
- Full theme customization is available in the Web UI at https://mipham.ai/code/dashboard`,
3132
- }
3133
- }
3134
-
3135
- // ═══════════════════════════════════════════════════════════════
3136
- // Add-Dir — add a directory to workspace permissions
3137
- // ═══════════════════════════════════════════════════════════════
3138
-
3139
- const addDirCmd: CommandHandler = async (_ctx, args) => {
3140
- const dir = args[0]
3141
- if (!dir) {
3142
- return {
3143
- content: `Usage: /add-dir <path>
3144
-
3145
- Add a directory to Mipham Code's allowed workspace paths.
3146
- This grants the AI permission to read/write files in that directory.
3147
-
3148
- Examples:
3149
- /add-dir ~/projects/my-api
3150
- /add-dir /usr/local/share/data
3151
-
3152
- Current allowed directories:
3153
- • ${process.cwd()} (project root, always allowed)
3154
-
3155
- Note: For security, tools like bash already respect .mipham/config.yml
3156
- permission boundaries. Adding directories here extends read/write access.`,
3157
- }
3158
- }
3159
-
3160
- const { existsSync } = await import('node:fs')
3161
- const { resolve } = await import('node:path')
3162
- const resolved = resolve(dir.replace(/^~/, process.env.HOME || '~'))
3163
-
3164
- if (!existsSync(resolved)) {
3165
- return { content: `✗ Directory not found: ${resolved}\n\nCheck the path and try again.` }
3166
- }
3167
-
3168
- return {
3169
- content: `✓ Directory registered: ${resolved}
3170
-
3171
- To persist across sessions, add to .mipham/config.yml:
3172
- workspace:
3173
- extraDirs:
3174
- - ${resolved}
3175
-
3176
- The AI can now access files in this directory.
3177
- Permission level is controlled by /config permission <level>.`,
3178
- }
3179
- }
3180
-
3181
- // ═══════════════════════════════════════════════════════════════
3182
- // Security / Audit — security review checklist
3183
- // ═══════════════════════════════════════════════════════════════
3184
-
3185
- const promptAuditCmd: CommandHandler = async () => {
3186
- const { existsSync, readFileSync, readdirSync, statSync } = await import('node:fs')
3187
- const { join, extname, basename } = await import('node:path')
3188
- const cwd = process.cwd()
3189
-
3190
- // Patterns that suggest prompts written for older/less-capable models
3191
- const AUDIT_RULES: Array<{
3192
- id: string
3193
- pattern: RegExp
3194
- severity: 'low' | 'medium' | 'high'
3195
- message: string
3196
- suggestion: string
3197
- }> = [
3198
- {
3199
- id: 'over-explaining',
3200
- pattern:
3201
- /you are (a|an) (helpful |friendly |knowledgeable )?(AI |language model |assistant)/i,
3202
- severity: 'low',
3203
- message: 'Self-identification preamble ("You are a...")',
3204
- suggestion: "Modern models don't need role preamble. Remove or shorten to 1 line.",
3205
- },
3206
- {
3207
- id: 'step-by-step',
3208
- pattern: /(think|reason|work) (step[ -]?by[ -]?step|through this|carefully about this)/i,
3209
- severity: 'low',
3210
- message: 'Step-by-step reasoning prompt',
3211
- suggestion: 'Reasoning models handle this natively. Remove for token savings.',
3212
- },
3213
- {
3214
- id: 'do-not-list',
3215
- pattern:
3216
- /(do not|never|you must not|don't|under no circumstances).*\n.*(do not|never|you must not|don't)/i,
3217
- severity: 'medium',
3218
- message: 'Multiple "do not" constraints',
3219
- suggestion: 'Modern models need fewer prohibitions. Consolidate to 1-2 key constraints.',
3220
- },
3221
- {
3222
- id: 'token-waste',
3223
- pattern:
3224
- /(remember|keep in mind|it is important to note|please note that|i want to emphasize)/i,
3225
- severity: 'low',
3226
- message: 'Filler emphasis phrases ("remember", "please note")',
3227
- suggestion: 'Remove filler. State the instruction directly.',
3228
- },
3229
- {
3230
- id: 'format-over-spec',
3231
- pattern: /(you must (always |only )?respond (in|with|using) (JSON|XML|YAML|markdown|HTML))/i,
3232
- severity: 'low',
3233
- message: 'Over-specified output format',
3234
- suggestion: 'Modern models follow format instructions with fewer words. Shorten.',
3235
- },
3236
- {
3237
- id: 'example-overload',
3238
- pattern: /((?:example|for instance|e\.g\.,).*\n){3,}/i,
3239
- severity: 'low',
3240
- message: 'Three or more examples in sequence',
3241
- suggestion: '1-2 examples suffice for modern models. Remove redundant ones.',
3242
- },
3243
- {
3244
- id: 'old-model-ref',
3245
- pattern: /(GPT-3|GPT-3\.5|Claude\s*[12][^3]|text-davinci|command-r|llama\s*2)/i,
3246
- severity: 'high',
3247
- message: 'Reference to older model',
3248
- suggestion: 'Update model references to current generation (Sonnet 5, Opus 5, GPT-5, etc.).',
3249
- },
3250
- {
3251
- id: 'long-preamble',
3252
- pattern: /^.{200,}$/m,
3253
- severity: 'medium',
3254
- message: 'Very long single-line instructions (200+ chars)',
3255
- suggestion: 'Break into bullet points. Modern models parse structured prompts better.',
3256
- },
3257
- {
3258
- id: 'verbose-constraint',
3259
- pattern:
3260
- /you (must|should|shall) (always |only |never )?(ensure|guarantee|verify|validate|confirm|cross-check|double-check)/i,
3261
- severity: 'medium',
3262
- message: 'Overly verbose constraint language',
3263
- suggestion: 'Replace "you must ensure that" with imperative: "Ensure".',
3264
- },
3265
- ]
3266
-
3267
- // Files to scan
3268
- const scanDirs = [
3269
- join(cwd, '.mipham', 'skills'),
3270
- join(cwd, '.mipham', 'rules'),
3271
- join(cwd, '.claude'),
3272
- ]
3273
- const skillFiles: string[] = []
3274
- // Also check standard project files
3275
- const projectFiles = [join(cwd, 'CLAUDE.md'), join(cwd, 'MIPHAM.md'), join(cwd, 'PRODUCT.md')]
3276
-
3277
- // Collect skill/rules files
3278
- for (const dir of scanDirs) {
3279
- try {
3280
- if (!existsSync(dir)) continue
3281
- const walk = (d: string) => {
3282
- const entries = readdirSync(d)
3283
- for (const e of entries) {
3284
- const fp = join(d, e)
3285
- const st = statSync(fp)
3286
- if (st.isDirectory()) {
3287
- walk(fp)
3288
- continue
3289
- }
3290
- if (['.md', '.SKILL.md', '.mipham-skill.md', '.txt'].some((ext) => fp.endsWith(ext))) {
3291
- skillFiles.push(fp)
3292
- }
3293
- }
3294
- }
3295
- walk(dir)
3296
- } catch {
3297
- /* skip */
3298
- }
3299
- }
3300
-
3301
- const allFiles = [...new Set([...projectFiles.filter((f) => existsSync(f)), ...skillFiles])]
3302
-
3303
- if (allFiles.length === 0) {
3304
- return {
3305
- content: [
3306
- '🔍 Prompt Audit',
3307
- '',
3308
- 'No prompt files found to audit.',
3309
- 'Place skills in .mipham/skills/ and rules in .mipham/rules/.',
3310
- ].join('\n'),
3311
- }
3312
- }
3313
-
3314
- // Scan each file
3315
- const results: Array<{
3316
- file: string
3317
- line: number
3318
- ruleId: string
3319
- severity: string
3320
- message: string
3321
- suggestion: string
3322
- }> = []
3323
-
3324
- for (const fp of allFiles) {
3325
- try {
3326
- const content = readFileSync(fp, 'utf-8')
3327
- const lines = content.split('\n')
3328
- for (const rule of AUDIT_RULES) {
3329
- // Multi-line pattern: check full content
3330
- if (rule.id === 'do-not-list' || rule.id === 'example-overload') {
3331
- if (rule.pattern.test(content)) {
3332
- // Find approx line
3333
- const match = rule.pattern.exec(content)
3334
- const idx = match ? content.slice(0, match.index).split('\n').length : 1
3335
- results.push({
3336
- file: fp.replace(cwd + '/', ''),
3337
- line: idx,
3338
- ruleId: rule.id,
3339
- severity: rule.severity,
3340
- message: rule.message,
3341
- suggestion: rule.suggestion,
3342
- })
3343
- }
3344
- continue
3345
- }
3346
-
3347
- // Line-by-line patterns
3348
- for (let i = 0; i < lines.length; i++) {
3349
- const line = lines[i]!
3350
- if (rule.pattern.test(line)) {
3351
- results.push({
3352
- file: fp.replace(cwd + '/', ''),
3353
- line: i + 1,
3354
- ruleId: rule.id,
3355
- severity: rule.severity,
3356
- message: rule.message,
3357
- suggestion: rule.suggestion,
3358
- })
3359
- }
3360
- }
3361
- }
3362
- } catch {
3363
- /* skip unreadable files */
3364
- }
3365
- }
3366
-
3367
- // Format output
3368
- const severityIcon: Record<string, string> = { high: '🔴', medium: '🟡', low: '🟢' }
3369
- const grouped: Record<string, typeof results> = {}
3370
- for (const r of results) {
3371
- const key = r.file
3372
- if (!grouped[key]) grouped[key] = []
3373
- grouped[key]!.push(r)
3374
- }
3375
-
3376
- const output: string[] = [
3377
- `🔍 Prompt Audit — ${allFiles.length} file(s) scanned, ${results.length} finding(s)`,
3378
- '',
3379
- ]
3380
-
3381
- if (results.length === 0) {
3382
- output.push('✅ All prompts look optimized for modern models. No issues found.')
3383
- } else {
3384
- for (const [file, items] of Object.entries(grouped)) {
3385
- output.push(`📄 ${file} (${items!.length} finding${items!.length > 1 ? 's' : ''})`)
3386
- for (const item of items!) {
3387
- output.push(
3388
- ` ${severityIcon[item.severity] || '⚪'} L${item.line}: [${item.ruleId}] ${item.message}`,
3389
- )
3390
- output.push(` → ${item.suggestion}`)
3391
- }
3392
- output.push('')
3393
- }
3394
- }
3395
-
3396
- output.push('─'.repeat(60))
3397
- output.push('Scanned: ' + allFiles.map((f) => basename(f)).join(', '))
3398
-
3399
- return { content: output.join('\n') }
3400
- }
3401
-
3402
- const securityCmd: CommandHandler = async () => {
3403
- const findings: string[] = []
3404
- const ok: string[] = []
3405
-
3406
- // Check for common security issues
3407
- const { existsSync, readFileSync } = await import('node:fs')
3408
- const { join } = await import('node:path')
3409
- const cwd = process.cwd()
3410
-
3411
- // 1. Check .gitignore for sensitive patterns
3412
- if (existsSync(join(cwd, '.gitignore'))) {
3413
- const gi = readFileSync(join(cwd, '.gitignore'), 'utf-8')
3414
- const hasEnv = gi.includes('.env')
3415
- const hasKeys = gi.includes('*.key') || gi.includes('*.pem')
3416
- if (hasEnv && hasKeys) {
3417
- ok.push('.gitignore covers .env + key files')
3418
- } else {
3419
- findings.push('Add .env, *.key, *.pem to .gitignore')
3420
- }
3421
- } else {
3422
- findings.push('No .gitignore found — create one with .env, node_modules, dist')
3423
- }
3424
-
3425
- // 2. Check for hardcoded secrets (quick grep for common patterns)
3426
- // ⚠ Security: results are redacted — only file:line locations are shown, never values
3427
- try {
3428
- const { execSync } = await import('node:child_process')
3429
- const secretPatterns = execSync(
3430
- `grep -rIn --include="*.ts" --include="*.js" --include="*.yml" --include="*.yaml" --include="*.json" -E "(API_KEY|SECRET|PASSWORD|TOKEN)\\s*=\\s*['\\\"][^$]" ${cwd} 2>/dev/null | grep -v node_modules | grep -v '.git/' | head -5 || echo ""`,
3431
- { encoding: 'utf-8', timeout: 5000 },
3432
- ).trim()
3433
- if (secretPatterns) {
3434
- // Redact the actual values — only show file:line locations
3435
- const redacted = secretPatterns
3436
- .split('\n')
3437
- .map((l) => {
3438
- const colonIdx = l.indexOf(':')
3439
- const secondColon = l.indexOf(':', colonIdx + 1)
3440
- if (secondColon > 0) {
3441
- return ' ' + l.slice(0, secondColon) + ' [VALUE REDACTED]'
3442
- }
3443
- return ' ' + l + ' [REDACTED]'
3444
- })
3445
- .join('\n')
3446
- findings.push(
3447
- `⚠ Hardcoded secrets detected (values redacted for security):\n${redacted}\n\n Replace with env vars: \${VAR_NAME} syntax`,
3448
- )
3449
- } else {
3450
- ok.push('No hardcoded secrets detected')
3451
- }
3452
- } catch {
3453
- // grep may fail if no matches — that's good
3454
- ok.push('No hardcoded secrets detected (quick scan)')
3455
- }
3456
-
3457
- // 3. Check for TLS in dependencies
3458
- if (existsSync(join(cwd, 'package.json'))) {
3459
- ok.push('package.json present — dependencies manageable')
3460
- }
3461
-
3462
- // 4. Check for license
3463
- if (existsSync(join(cwd, 'LICENSE'))) {
3464
- ok.push('LICENSE file present')
3465
- } else {
3466
- findings.push('No LICENSE file — add Apache 2.0 or appropriate license')
3467
- }
3468
-
3469
- // 5. CI/CD
3470
- if (existsSync(join(cwd, '.github', 'workflows'))) {
3471
- ok.push('CI/CD workflows configured')
3472
- } else {
3473
- findings.push('No CI/CD workflows found — add .github/workflows/')
3474
- }
3475
-
3476
- const lines: string[] = ['── Security Review ──', '', `Scanning: ${cwd}`, '']
3477
-
3478
- if (ok.length > 0) {
3479
- lines.push(`✅ Passed (${ok.length}):`)
3480
- for (const o of ok) lines.push(` • ${o}`)
3481
- }
3482
- if (findings.length > 0) {
3483
- lines.push('')
3484
- lines.push(`⚠ Findings (${findings.length}):`)
3485
- for (const f of findings) lines.push(` • ${f}`)
3486
- }
3487
- lines.push('')
3488
- lines.push('For a full audit: type "audit my project for security issues" in chat.')
3489
-
3490
- return { content: lines.join('\n') }
3491
- }
3492
-
3493
- // ═══════════════════════════════════════════════════════════════
3494
- // Release Notes — version changelog
3495
- // ═══════════════════════════════════════════════════════════════
3496
-
3497
- const releaseNotesCmd: CommandHandler = () => ({
3498
- content: `── Release Notes ──
3499
-
3500
- v0.1.2 (2026-06-09) — Current
3501
- • 48 slash commands (up from 20)
3502
- • /setup guided project initialization wizard
3503
- • Checkpoint/rewind mechanism
3504
- • Focus mode (last exchange view)
3505
- • 3-level MIPHAM.md architecture
3506
- • /doctor system diagnostics
3507
- • /export conversation to file
3508
- • /review and /pr-comments code review workflows
3509
- • /memory management
3510
- • /upgrade instructions
3511
- • Clipboard support (macOS/Windows)
3512
-
3513
- v0.1.1 (2026-06-02)
3514
- • Inline shared module for standalone builds
3515
- • Bin path fix for npm compatibility
3516
-
3517
- v0.1.0 (2026-06-01)
3518
- • Initial release
3519
- • Multi-model support (7 providers)
3520
- • 16 built-in tools
3521
- • 11 skills (9 standard + 2 mipham)
3522
- • Ctrl+P interactive model picker
3523
- • SSE streaming support
3524
-
3525
- Full changelog: https://mipham.ai/code/releases`,
3526
- })
3527
-
3528
- // ═══════════════════════════════════════════════════════════════
3529
- // IDE — IDE integration guide
3530
- // ═══════════════════════════════════════════════════════════════
3531
-
3532
- const ideCmd: CommandHandler = async (_ctx) => {
3533
- const { mkdirSync, writeFileSync, existsSync } = await import('node:fs')
3534
- const { join } = await import('node:path')
3535
-
3536
- const cwd = process.cwd()
3537
- const vscodeDir = join(cwd, '.vscode')
3538
- mkdirSync(vscodeDir, { recursive: true })
3539
-
3540
- const files: string[] = []
3541
-
3542
- // ── Detect bun path ──
3543
- let bunPath = '/opt/homebrew/bin/bun'
3544
- try {
3545
- const { execSync } = await import('node:child_process')
3546
- const detected = execSync('which bun 2>/dev/null || echo /opt/homebrew/bin/bun', {
3547
- encoding: 'utf-8',
3548
- }).trim()
3549
- if (detected) bunPath = detected
3550
- } catch {
3551
- // Use default
3552
- }
3553
-
3554
- // ── settings.json: terminal profile ──
3555
- const settingsPath = join(vscodeDir, 'settings.json')
3556
- const settings = {
3557
- 'terminal.integrated.profiles.osx': {
3558
- mipham: {
3559
- path: bunPath,
3560
- args: ['run', 'mipham'],
3561
- cwd: '${workspaceFolder}',
3562
- },
3563
- },
3564
- }
3565
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8')
3566
- files.push('.vscode/settings.json')
3567
-
3568
- // ── keybindings.json: Cmd+Esc launch ──
3569
- const keybindingsPath = join(vscodeDir, 'keybindings.json')
3570
- const keybindings = [
3571
- {
3572
- key: 'cmd+escape',
3573
- command: 'workbench.action.terminal.focus',
3574
- when: 'terminalProcessSupported',
3575
- },
3576
- {
3577
- key: 'cmd+shift+m',
3578
- command: 'workbench.action.terminal.new',
3579
- },
3580
- ]
3581
- writeFileSync(keybindingsPath, JSON.stringify(keybindings, null, 2) + '\n', 'utf-8')
3582
- files.push('.vscode/keybindings.json')
3583
-
3584
- // ── extensions.json: recommended ──
3585
- const extensionsPath = join(vscodeDir, 'extensions.json')
3586
- const extensions = {
3587
- recommendations: ['miphamai.mipham-code'],
3588
- }
3589
- writeFileSync(extensionsPath, JSON.stringify(extensions, null, 2) + '\n', 'utf-8')
3590
- files.push('.vscode/extensions.json')
3591
-
3592
- return {
3593
- content: [
3594
- '── VS Code Integration ──',
3595
- '',
3596
- `Generated in ${vscodeDir}:`,
3597
- ...files.map((f) => ` ✅ ${f}`),
3598
- '',
3599
- 'What was configured:',
3600
- ' • Terminal profile "mipham" — opens Mipham Code in integrated terminal',
3601
- ' • Keyboard shortcut Cmd+Esc — focus terminal',
3602
- ' • Cmd+Shift+M — new terminal',
3603
- '',
3604
- 'To use:',
3605
- ' 1. Restart VS Code (or reload window: Cmd+Shift+P → Reload Window)',
3606
- ' 2. Open terminal: Ctrl+` or Cmd+Esc',
3607
- ' 3. Select "mipham" profile from the terminal dropdown',
3608
- '',
3609
- 'Install the VS Code extension for full integration:',
3610
- ` code --install-extension miphamai.mipham-code`,
3611
- '',
3612
- 'JetBrains: Settings → Tools → Terminal → Shell path → bun run mipham',
3613
- ].join('\n'),
3614
- }
3615
- }
3616
-
3617
- // ═══════════════════════════════════════════════════════════════
3618
- // Terminal Setup — shell integration guide
3619
- // ═══════════════════════════════════════════════════════════════
3620
-
3621
- const terminalSetupCmd: CommandHandler = async () => {
3622
- const { writeFileSync, appendFileSync, existsSync, mkdirSync, readFileSync } =
3623
- await import('node:fs')
3624
- const { join } = await import('node:path')
3625
- const { homedir } = await import('node:os')
3626
-
3627
- const home = homedir()
3628
- const lines: string[] = ['── Terminal Setup ──', '']
3629
-
3630
- // ── 1. Generate standalone shell setup script ──
3631
- const miphamDir = join(home, '.mipham')
3632
- mkdirSync(miphamDir, { recursive: true })
3633
-
3634
- const shellScript = join(miphamDir, 'shell-setup.sh')
3635
- const shellContent = [
3636
- '#!/bin/bash',
3637
- '# Mipham Code — Shell Integration',
3638
- '# Source this file in your shell profile:',
3639
- '# source ~/.mipham/shell-setup.sh',
3640
- '',
3641
- `export MIPHAM_HOME="${home}/.mipham"`,
3642
- '',
3643
- '# Alias: launch Mipham Code in current directory',
3644
- `alias mipham='cd $(pwd) && ${NPM_INSTALL_COMMAND} > /dev/null 2>&1; mipham'`,
3645
- '',
3646
- '# Or use the local development version:',
3647
- '# alias mipham="bun run /path/to/mipham-code/apps/cli/bin/mipham"',
3648
- '',
3649
- '# Auto-detect provider from config',
3650
- 'if [ -f ~/.mipham/config.yml ]; then',
3651
- ' export MIPHAM_PROVIDER=$(grep "defaultProvider:" ~/.mipham/config.yml | awk "{print \$2}")',
3652
- 'fi',
3653
- ].join('\n')
3654
- writeFileSync(shellScript, shellContent + '\n', 'utf-8')
3655
- lines.push(` ✅ Generated: ${shellScript}`)
3656
-
3657
- // ── 2. Append to shell profile ──
3658
- const shell = process.env.SHELL || '/bin/zsh'
3659
- const profileName = shell.includes('zsh') ? '.zshrc' : '.bashrc'
3660
- const profilePath = join(home, profileName)
3661
- const sourceLine = `\n# Mipham Code shell integration\n[ -f ~/.mipham/shell-setup.sh ] && source ~/.mipham/shell-setup.sh\n`
3662
-
3663
- try {
3664
- const existing = existsSync(profilePath) ? readFileSync(profilePath, 'utf-8') : ''
3665
- if (existing.includes('shell-setup.sh')) {
3666
- lines.push(` ⏭ ${profileName} already has Mipham Code integration`)
3667
- } else {
3668
- appendFileSync(profilePath, sourceLine, 'utf-8')
3669
- lines.push(` ✅ Added to ~/${profileName}`)
3670
- }
3671
- } catch {
3672
- lines.push(` ⚠️ Could not update ~/${profileName}. Add manually:`)
3673
- lines.push(` echo '${sourceLine.trim()}' >> ~/${profileName}`)
3674
- }
3675
-
3676
- // ── 3. Global install check ──
3677
- lines.push('')
3678
- lines.push('── Installation ──')
3679
- try {
3680
- const { execSync } = await import('node:child_process')
3681
- const miphamPath = execSync('which mipham 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim()
3682
- if (miphamPath) {
3683
- lines.push(` ✅ mipham found at: ${miphamPath}`)
3684
- const version = execSync('mipham --version 2>/dev/null || echo "unknown"', {
3685
- encoding: 'utf-8',
3686
- }).trim()
3687
- lines.push(` 📦 Version: ${version}`)
3688
- } else {
3689
- lines.push(` ⚠️ mipham not in PATH. Install globally:`)
3690
- lines.push(` ${NPM_INSTALL_COMMAND}`)
3691
- lines.push(` or: curl -fsSL https://mipham.ai/install.sh | bash`)
3692
- }
3693
- } catch {
3694
- lines.push(` 💡 Install: ${NPM_INSTALL_COMMAND}`)
3695
- }
3696
-
3697
- // ── 4. Verify ──
3698
- lines.push('')
3699
- lines.push('── Next Steps ──')
3700
- lines.push(' 1. Restart your terminal or run: source ~/.mipham/shell-setup.sh')
3701
- lines.push(' 2. Run: mipham --version')
3702
- lines.push(' 3. Start coding: cd your-project && mipham')
3703
- lines.push('')
3704
- lines.push(`Works with: Zsh, Bash. Shell: ${shell}`)
3705
-
3706
- return { content: lines.join('\n') }
3707
- }
3708
-
3709
- // ═══════════════════════════════════════════════════════════════
3710
- // Phase 4 — MCP Server Management
3711
- // ═══════════════════════════════════════════════════════════════
3712
-
3713
- const mcpCmd: CommandHandler = (ctx) => {
3714
- const configuredServers = ctx.config.skills?.mcpServers ?? []
3715
- const liveConnections = McpClient.getInstance().listConnections()
3716
-
3717
- const lines: string[] = ['── MCP Servers ──', '']
3718
-
3719
- if (configuredServers.length > 0) {
3720
- lines.push(`Configured servers (${configuredServers.length}):`)
3721
- lines.push('')
3722
- for (const s of configuredServers) {
3723
- const live = liveConnections.find((c) => c.config.name === s.name)
3724
- const statusIcon = live
3725
- ? live.status === 'connected'
3726
- ? '🟢'
3727
- : live.status === 'connecting'
3728
- ? '🟡'
3729
- : live.status === 'error'
3730
- ? '🔴'
3731
- : '⚪'
3732
- : '⚪'
3733
- const statusLabel = live ? live.status : 'not started'
3734
- const envKeys = s.env ? Object.keys(s.env).join(', ') : '(none)'
2427
+ if (configuredServers.length > 0) {
2428
+ lines.push(`Configured servers (${configuredServers.length}):`)
2429
+ lines.push('')
2430
+ for (const s of configuredServers) {
2431
+ const live = liveConnections.find((c) => c.config.name === s.name)
2432
+ const statusIcon = live
2433
+ ? live.status === 'connected'
2434
+ ? '🟢'
2435
+ : live.status === 'connecting'
2436
+ ? '🟡'
2437
+ : live.status === 'error'
2438
+ ? '🔴'
2439
+ : '⚪'
2440
+ : '⚪'
2441
+ const statusLabel = live ? live.status : 'not started'
2442
+ const envKeys = s.env ? Object.keys(s.env).join(', ') : '(none)'
3735
2443
 
3736
2444
  lines.push(` ${statusIcon} ${s.name} [${statusLabel}]`)
3737
2445
  lines.push(` Command: ${s.command} ${s.args.join(' ')}`)
@@ -4290,6 +2998,7 @@ const commandsListCmd: CommandHandler = () => {
4290
2998
  '/undo': 'History',
4291
2999
  '/copy': 'History',
4292
3000
  '/focus': 'History',
3001
+ '/ultracode': 'Workflow',
4293
3002
  '/pick': 'Model & Provider',
4294
3003
  '/model': 'Model & Provider',
4295
3004
  '/models': 'Model & Provider',
@@ -4320,10 +3029,11 @@ const commandsListCmd: CommandHandler = () => {
4320
3029
  '/tdd': 'Workflow',
4321
3030
  '/todos': 'Workflow',
4322
3031
  '/tasks': 'Workflow',
4323
- '/review': 'Workflow',
3032
+ '/review': 'Code Quality',
4324
3033
  '/pr-comments': 'Workflow',
4325
3034
  '/diff': 'Workflow',
4326
3035
  '/workflows': 'Workflow',
3036
+ '/deep-research': 'Workflow',
4327
3037
  '/loop': 'Workflow',
4328
3038
  '/init': 'Project',
4329
3039
  '/setup': 'Project',
@@ -4420,6 +3130,7 @@ registry.set('/rewind', rewindCmd)
4420
3130
  registry.set('/undo', undoCmd)
4421
3131
  registry.set('/copy', copyCmd)
4422
3132
  registry.set('/focus', focusCmd)
3133
+ registry.set('/ultracode', ultracodeCmd)
4423
3134
 
4424
3135
  // Model & Provider
4425
3136
  registry.set('/model', modelCmd)
@@ -4456,6 +3167,7 @@ registry.set('/loop', loopCmd)
4456
3167
  registry.set('/no-plan', noPlanCmd)
4457
3168
  registry.set('/workflow', workflowAutoCmd)
4458
3169
  registry.set('/workflows', workflowsCmd)
3170
+ registry.set('/deep-research', deepResearchCmd)
4459
3171
  registry.set('/review', reviewCmd)
4460
3172
  registry.set('/pr-comments', prCommentsCmd)
4461
3173
 
@@ -4559,6 +3271,7 @@ const COMMAND_DESCRIPTIONS: Record<string, string> = {
4559
3271
  '/undo': 'Same as /rewind',
4560
3272
  '/copy': 'Copy last response to clipboard',
4561
3273
  '/focus': 'Toggle focus view',
3274
+ '/ultracode': 'Toggle multi-agent workflow orchestration mode',
4562
3275
  '/pick': 'Open model picker',
4563
3276
  '/model': 'Show current model',
4564
3277
  '/models': 'List all available models',
@@ -4587,10 +3300,12 @@ const COMMAND_DESCRIPTIONS: Record<string, string> = {
4587
3300
  '/tdd': 'Test-Driven Development workflow (RED → GREEN → REFACTOR)',
4588
3301
  '/todos': 'Task management (list/create tasks)',
4589
3302
  '/tasks': 'Background tasks',
4590
- '/review': 'Code review workflow',
3303
+ '/review': 'Review code for bugs, security, performance (alias for /code-review)',
4591
3304
  '/pr-comments': 'PR review summary',
4592
3305
  '/diff': 'Show git diff',
4593
3306
  '/workflows': 'List workflow scripts',
3307
+ '/deep-research':
3308
+ 'Deep research with multi-agent parallel search, verification, and cited synthesis',
4594
3309
  '/loop': 'Run prompt on interval',
4595
3310
  '/init': 'Initialize .mipham config',
4596
3311
  '/setup': 'Guided project setup wizard',
@@ -4648,22 +3363,6 @@ export function parseSlashCommand(input: string): { command: string; args: strin
4648
3363
  return { command, args }
4649
3364
  }
4650
3365
 
4651
- // ── Utility ──
4652
-
4653
- function stripIndent(strings: TemplateStringsArray, ...values: unknown[]): string {
4654
- let result = strings.reduce((acc, s, i) => acc + s + (values[i] ?? ''), '')
4655
- // Remove leading newline
4656
- result = result.replace(/^\n/, '')
4657
- // Find minimum indent
4658
- const match = result.match(/^( +)/m)
4659
- if (match?.[1]) {
4660
- const indent = match[1].length
4661
- result = result
4662
- .split('\n')
4663
- .map((line) => line.slice(indent))
4664
- .join('\n')
4665
- }
4666
- return result.trim()
4667
- }
3366
+ export { stripIndent } from './strip-indent.js'
4668
3367
 
4669
3368
  export { switchCmd as handleSwitch }