@miphamai/cli 0.62.0 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ export interface McpConnectFailure {
2
+ name: string
3
+ reason: string
4
+ }
5
+
6
+ /**
7
+ * Format a startup notice for MCP servers that failed to connect, so the model
8
+ * knows their tools are unavailable (instead of concluding the tools don't
9
+ * exist). Empty string when nothing failed.
10
+ */
11
+ export function formatMcpConnectFailures(failures: McpConnectFailure[]): string {
12
+ if (failures.length === 0) return ''
13
+ const list = failures.map((f) => `- ${f.name}: ${f.reason}`).join('\n')
14
+ return `[MCP] These MCP servers failed to connect — their tools are unavailable:\n${list}`
15
+ }
@@ -26,7 +26,7 @@ export function readClaudeManifest(dir: string): ClaudeManifest | null {
26
26
  const path = join(dir, '.claude-plugin', 'plugin.json')
27
27
  if (!existsSync(path)) return null
28
28
  try {
29
- return JSON.parse(readFileSync(path, 'utf-8')) as ClaudeManifest
29
+ return JSON.parse(readFileSync(path, 'utf-8').replace(/^\uFEFF/, '')) as ClaudeManifest
30
30
  } catch {
31
31
  return null
32
32
  }
@@ -52,7 +52,7 @@ export function validatePlugin(dir: string): PluginValidation {
52
52
 
53
53
  const errors: string[] = []
54
54
  try {
55
- const raw = readFileSync(resolved.path, 'utf-8')
55
+ const raw = readFileSync(resolved.path, 'utf-8').replace(/^\uFEFF/, '')
56
56
  const manifest = JSON.parse(raw) as PluginManifest
57
57
 
58
58
  if (!manifest.name || !/^[a-z0-9-]+$/.test(manifest.name)) {
@@ -9,7 +9,7 @@
9
9
  export const PACKAGE_NAME = '@miphamai/cli' as const
10
10
 
11
11
  /** 当前发布版本 */
12
- export const PACKAGE_VERSION = '0.62.0' as const
12
+ export const PACKAGE_VERSION = '0.64.0' as const
13
13
 
14
14
  /** npm install 全局安装命令 */
15
15
  export const NPM_INSTALL_COMMAND = `npm install -g ${PACKAGE_NAME}` as const
@@ -158,6 +158,8 @@ export interface MiphamConfig {
158
158
  features?: Partial<FeatureFlags>
159
159
  /** Phase 10 CRSI feature flags. All default to true. */
160
160
  crsi?: Partial<CrsiConfig>
161
+ /** Ghost-text 自动补全(输入续写)。默认 enabled: true、debounceMs: 400。 */
162
+ autocomplete?: Partial<AutocompleteConfig>
161
163
  }
162
164
 
163
165
  export interface FeatureFlags {
@@ -176,6 +178,11 @@ export interface CrsiConfig {
176
178
  autoRuleManagement: boolean
177
179
  }
178
180
 
181
+ export interface AutocompleteConfig {
182
+ enabled: boolean
183
+ debounceMs: number
184
+ }
185
+
179
186
  export interface McpServerConfig {
180
187
  name: string
181
188
  /** stdio: executable to spawn (mutually exclusive with `url`). */
@@ -10,6 +10,7 @@ import { readFileSync, existsSync, copyFileSync, mkdirSync, chmodSync } from 'no
10
10
  import { join } from 'node:path'
11
11
  import { execSync } from 'node:child_process'
12
12
  import { homedir } from 'node:os'
13
+ import { PACKAGE_VERSION } from './package-info'
13
14
 
14
15
  const PACKAGE = '@miphamai/cli'
15
16
  const HOME = homedir()
@@ -190,6 +191,46 @@ export function getConfigPath(): string {
190
191
  return CONFIG_PATH
191
192
  }
192
193
 
194
+ /** 更新提示状态:有新版(未装)| 已装待重启。 */
195
+ export type UpdateStatus = { state: 'available' | 'installed'; latest: string }
196
+
197
+ /** registry /latest 端点(返回 {version}),npm → npmmirror 回退。 */
198
+ const LATEST_URLS = [
199
+ 'https://registry.npmjs.org/@miphamai%2fcli/latest',
200
+ 'https://registry.npmmirror.com/@miphamai%2fcli/latest',
201
+ ]
202
+
203
+ /** fetch 拉最新版本,npm → npmmirror 回退(超时 10s/个)。 */
204
+ async function fetchLatestVersionAsync(): Promise<string> {
205
+ let lastError: unknown = null
206
+ for (const url of LATEST_URLS) {
207
+ try {
208
+ const res = await fetch(url, { signal: AbortSignal.timeout(10_000) })
209
+ if (!res.ok) throw new Error(`HTTP ${res.status}`)
210
+ const data = (await res.json()) as { version?: string }
211
+ if (data.version) return data.version
212
+ throw new Error('no version in response')
213
+ } catch (err) {
214
+ lastError = err
215
+ }
216
+ }
217
+ throw lastError ?? new Error('Failed to fetch latest version')
218
+ }
219
+
220
+ /** 非阻塞版本检查。current 用 PACKAGE_VERSION(编译期常量,二进制下可靠)。离线/失败 → available: false。 */
221
+ export async function checkForUpdatesAsync(): Promise<UpdateCheck> {
222
+ const current: string = PACKAGE_VERSION
223
+ let latest: string = current
224
+ let available = false
225
+ try {
226
+ latest = await fetchLatestVersionAsync()
227
+ available = compareVersions(latest, current) > 0
228
+ } catch {
229
+ // offline → treat as up-to-date (don't alarm the user)
230
+ }
231
+ return { current, latest, available }
232
+ }
233
+
193
234
  /**
194
235
  * Compare two semver strings. Returns >0 if a > b, <0 if a < b, 0 if equal.
195
236
  */
@@ -19,6 +19,7 @@ export const BUNDLED_SKILLS: ReadonlyArray<BundledSkill> = [
19
19
  { type: 'standard', raw: "---\nname: memory\ndescription: Read and write persistent memory files for context retention across sessions — one fact per file with frontmatter\nversion: 2.0.0\n---\n\n# Memory Skill\n\nManage persistent memory stored as markdown files with YAML frontmatter.\n\n## File Format\n\nEach memory is one `.md` file under the `memory/` directory:\n\n```markdown\n---\nname: <kebab-case-slug>\ndescription: <one-line summary>\nmetadata:\n type: user | feedback | project | reference\n---\n\n<the fact body>\n\n**Why:** <rationale>\n**How to apply:** <practical guidance>\n```\n\n## File Path Conventions\n\n- Directory: `~/.mipham/memory/` (user-level) or `./.mipham/memory/` (project-level)\n- Filename: `<name-slug>.md` (lowercase, hyphens)\n- Index: `MEMORY.md` — one line per memory file, maintained automatically\n\n## Operations\n\n### List Memories\n\nScan `MEMORY.md` index for available memories. The index has one line per memory:\n\n```markdown\n- [Title](file.md) — brief hook\n```\n\n### Read Memory\n\nRead the full markdown file including frontmatter. Parse YAML frontmatter for metadata.\n\n### Write Memory\n\n1. Check for existing file with same `name:` slug — update if found\n2. Create new file if no match\n3. Add/update entry in `MEMORY.md` index\n4. Never write what the repo already records (code structure, git history, CLAUDE.md)\n\n### Delete Memory\n\nRemove the file and its index entry. Use when a memory is incorrect or superseded.\n\n## Best Practices\n\n- **One fact per file** — atomic, focused, easy to find\n- **Descriptive slugs** — `npm-publish-workflow` not `memory-1`\n- **Link related memories** — use `[[slug-name]]` wikilinks in body\n- **Check before writing** — search existing memories to avoid duplicates\n- **Types matter**: `user` (who), `feedback` (corrections), `project` (goals), `reference` (external)\n\n## Example\n\n```markdown\n---\nname: api-rate-limit\ndescription: OpenAI API has 500 RPM limit on our tier\nmetadata:\n type: reference\n---\n\nThe OpenAI API key for production has a hard 500 requests/minute limit.\nExceeding it returns HTTP 429 with a Retry-After header.\n\n**Why:** We hit this in production during peak usage\n**How to apply:** Use exponential backoff; batch requests where possible\n```\n" },
20
20
  { type: 'standard', raw: "---\nname: mipham-code-setup\ndescription: Install, configure, diagnose, and troubleshoot Mipham Code — the multi-model open-core intelligent coding terminal. Covers setup wizard, API keys, providers, models, skills, permissions, workspace trust, shell/IDE integration, and first-run onboarding.\nversion: 2.0.0\nuser-invocable: true\nallowed-tools:\n - Read\n - Write\n - Edit\n - Bash\n - Skill\n---\n\n# Mipham Code Setup — Executable Setup Workflow\n\n**Type**: Rigid — follow the decision tree exactly. Don't skip diagnostic phases.\n\n**Purpose**: Guide users from zero to fully configured Mipham Code. This skill is BOTH:\n\n1. A self-contained diagnostic + configuration workflow the AI can execute\n2. A reference for `/setup` command behavior and slash commands\n\n**Triggers**: \"setup mipham\", \"configure mipham\", \"install mipham code\", \"mipham not working\", \"mipham setup\", \"first time using mipham\", \"help me set up\", \"getting started\", `/setup`\n\n---\n\n## Phase 0: Environment Detection (ALWAYS RUN FIRST)\n\nBefore doing anything, run these diagnostic checks. Report results in a status table.\n\n### 0.1 — Detect Installation\n\n```bash\nwhich mipham 2>/dev/null\nmipham --version 2>/dev/null\nbun --version 2>/dev/null\nnode --version 2>/dev/null\n```\n\n### 0.2 — Detect Configuration\n\n```bash\nls -la .mipham/config.yml 2>/dev/null\nls -la ~/.mipham/config.yml 2>/dev/null\nls -la MIPHAM.md 2>/dev/null\nls -la CLAUDE.md 2>/dev/null\n```\n\n### 0.3 — Detect API Keys\n\n```bash\nenv | grep -E 'ANTHROPIC_API_KEY|OPENAI_API_KEY|DEEPSEEK_API_KEY|QWEN_API_KEY|DOUBAO_API_KEY|HUNYUAN_API_KEY|GEMINI_API_KEY' | cut -d= -f1\n```\n\n### 0.4 — Detect Skills & Permissions\n\n```bash\nls .mipham/skills/ 2>/dev/null\ncat .mipham/config.yml 2>/dev/null | grep -E 'permission|trust' || echo \"no config\"\n```\n\n### 0.5 — Detect Workspace Trust\n\n```bash\ncat ~/.mipham/trusted-workspaces.json 2>/dev/null || echo \"no trust store\"\n```\n\n### Status Report Format\n\nAfter detection, present results as:\n\n```\n── Mipham Code Status ──\n\nInstallation: [✅/⬜] mipham CLI [✅/⬜] Bun [✅/⬜] Node.js\nProject: [✅/⬜] .mipham/ [✅/⬜] config.yml [✅/⬜] MIPHAM.md\nUser Config: [✅/⬜] ~/.mipham/config.yml\nAPI Keys: [N] set (list names or \"none\")\nSkills: [N] installed\nPermissions: [mode] (default/acceptEdits/plan/bypassPermissions)\nTrust: [✅/⬜] workspace trusted\n```\n\nThen proceed to ONLY the phases where something is missing. Don't re-run already-configured steps unless asked.\n\n---\n\n## Phase 1: Installation\n\n**Trigger**: `mipham --version` fails.\n\n### Option A: Quick Install (recommended)\n\n```bash\ncurl -fsSL https://mipham.ai/install.sh | bash\n```\n\nThen restart the shell or run:\n\n```bash\nexport PATH=\"$HOME/.mipham/bin:$PATH\"\n```\n\n### Option B: npm Global Install\n\n```bash\nnpm install -g @miphamai/cli\nmipham\n```\n\n### Option C: From Source (developers)\n\n```bash\ngit clone https://github.com/One-Mipham/mipham-code\ncd mipham-code/apps/cli\nbun install && bun run bin/mipham\n```\n\n### ✅ Verification\n\n```bash\nmipham --version # Should print version ≥ 0.24.0\nmipham --help # Should print usage\n```\n\n---\n\n## Phase 2: Project Initialization\n\n**Trigger**: Missing `.mipham/` directory or `MIPHAM.md`.\n\n### 2.1 — Create .mipham/ directory\n\n```bash\nmkdir -p .mipham\n```\n\n### 2.2 — Create .mipham/config.yml\n\nWrite a minimal config. Ask the user which provider they want to use first, or pick a sensible default:\n\n```yaml\ndefaultProvider: anthropic\ndefaultModel: claude-sonnet-4-6\npermission: default\n```\n\n**Providers available** (alphabetical):\n\n| Provider | Type | Example Models |\n| --------- | ------------- | -------------------------------------- |\n| anthropic | Native SDK | Claude Haiku 4.5, Sonnet 4.6, Opus 4.8 |\n| deepseek | OpenAI Compat | V4 Flash, V4 Pro |\n| doubao | OpenAI Compat | Seed 1.6, Seed 2.0 |\n| gemini | OpenAI Compat | 3.0 Flash, 3.0 Pro, 2.5 Pro |\n| hunyuan | OpenAI Compat | Lite, TurboS, 2.0, T1 |\n| openai | OpenAI Compat | GPT-5.4 Mini, GPT-5.4, GPT-5.5, Codex |\n| qwen | OpenAI Compat | Qwen Plus, Qwen Max |\n\n### 2.3 — Create MIPHAM.md (optional but recommended)\n\nCreate `MIPHAM.md` in project root to define AI personality:\n\n```markdown\n# MIPHAM.md\n\n## Project Context\n\n- **Project**: [name]\n- **Language**: [zh-CN / en]\n- **Stack**: [TypeScript / Python / etc.]\n\n## Preferences\n\n- Code style: [e.g., functional, OOP]\n- Comment language: [e.g., English]\n- Test framework: [e.g., Vitest]\n```\n\n### ✅ Verification\n\n```bash\nls -la .mipham/config.yml MIPHAM.md\n```\n\n---\n\n## Phase 3: API Key Configuration\n\n**Trigger**: Missing API keys in environment.\n\n### 3.1 — Identify Required Providers\n\nAsk the user which providers they plan to use. For each, set the env var.\n\n### 3.2 — Set API Keys\n\n**Recommended: Environment variables** (not in config files — avoids accidental commits):\n\n```bash\nexport ANTHROPIC_API_KEY=\"sk-ant-...\"\nexport OPENAI_API_KEY=\"sk-...\"\nexport DEEPSEEK_API_KEY=\"sk-...\"\nexport QWEN_API_KEY=\"sk-...\"\nexport DOUBAO_API_KEY=\"...\"\nexport HUNYUAN_API_KEY=\"...\"\nexport GEMINI_API_KEY=\"...\"\n```\n\nAdd these to `~/.zshrc` or `~/.bashrc` for persistence:\n\n```bash\necho 'export ANTHROPIC_API_KEY=\"sk-ant-...\"' >> ~/.zshrc\nsource ~/.zshrc\n```\n\n**Alternative**: Store in `~/.mipham/config.yml`:\n\n```yaml\nproviders:\n - id: anthropic\n apiKey: $ANTHROPIC_API_KEY\n - id: openai\n apiKey: $OPENAI_API_KEY\n```\n\n### 3.3 — Verify Keys\n\n```bash\nenv | grep API_KEY\n```\n\n### ❗Security Rules\n\n- NEVER hardcode API keys in project config files (`.mipham/config.yml` in project root should use `$ENV_VAR` references, not raw keys)\n- NEVER commit API keys to git\n- Add to `.gitignore`: `.mipham/config.yml` (if it contains keys), `.env`, `*.pem`\n\n---\n\n## Phase 4: Provider & Model Configuration\n\n**Trigger**: Need to set default or enable/disable providers.\n\n### 4.1 — Set Default Provider & Model\n\nIn `.mipham/config.yml`:\n\n```yaml\ndefaultProvider: anthropic\ndefaultModel: claude-sonnet-4-6\n```\n\nOr use slash commands:\n\n```\n/model # Interactive model picker (Ctrl+P)\n/switch # Switch provider\n/providers # List all configured providers\n```\n\n### 4.2 — Enable/Disable Providers\n\n```yaml\nproviders:\n - id: anthropic\n status: active\n - id: openai\n status: active\n - id: deepseek\n status: disabled\n```\n\n### ✅ Verification\n\n```\n/model # Should show available models\n/providers # Should list active providers\n```\n\n---\n\n## Phase 5: Skills Installation\n\n**Trigger**: No or few skills installed.\n\n### 5.1 — Built-in Skills\n\nMipham Code ships with 17 built-in skills loaded automatically:\n\n- **Standard (14)**: code-review, compassionate-communication, doc-generator, github-ops, memory, mipham-code-setup, security-review, self-review, superpower, systematic-debugging, tdd, test-driven-development, web-access, web-search\n- **Mipham (3)**: om-artifact, om-model-optimize, om-security\n\n### 5.2 — Community Skills\n\nInstall from the community registry:\n\n```\n/setup 4 # Guided skill browser\n```\n\nOr directly:\n\n```bash\n# Skills are loaded from:\n# - apps/cli/skills/standard/ (built-in standard)\n# - apps/cli/skills/mipham/ (built-in mipham)\n# - ~/.mipham/skills/ (user-installed)\n# - .mipham/skills/ (project-local)\n```\n\n### 5.3 — Install Specific Skills\n\n```\n/skills install <name> # Install from registry\n/skills list # List available\n/skills search <query> # Search registry\n```\n\n### ✅ Verification\n\n```\n/skills list # Should show installed skills with counts\n```\n\n---\n\n## Phase 6: Permissions Configuration\n\n**Trigger**: Permission mode not configured or wrong for use case.\n\n### 6.1 — Permission Modes\n\n| Mode | Behavior | Use Case |\n| ------------------- | ------------------------------- | ----------------------------------- |\n| `default` | Prompt for each tool | Normal development (recommended) |\n| `acceptEdits` | Auto-allow edits, prompt others | Active coding sessions |\n| `plan` | Plan-only, no tool execution | Design & architecture work |\n| `bypassPermissions` | Skip all checks | ⚠️ Only for fully trusted codebases |\n\n### 6.2 — Configure\n\nIn `.mipham/config.yml`:\n\n```yaml\npermission: default\n```\n\nOr via slash command:\n\n```\n/permissions # View current settings\n/setup 5 # Permission setup wizard\n```\n\n### 6.3 — CI/CD Safety\n\nFor CI/CD environments, use the `default` mode (the daemon default): headless\nsessions never prompt, so `ask`-level tools (Bash/Write/Edit) are blocked rather\nthan auto-approved.\n\n### ✅ Verification\n\n```\n/permissions # Should show current mode\n```\n\n---\n\n## Phase 7: Workspace Trust\n\n**Trigger**: Untrusted workspace (prompted on startup in v0.24.3+).\n\n### 7.1 — Understanding Workspace Trust\n\nWorkspace trust is a security mechanism that prevents AI from operating in untrusted directories. Trust is **hierarchical**: trusting `/Users/me/Projects` implicitly trusts all subdirectories.\n\n### 7.2 — Trust a Workspace\n\n**Interactive**: Accept the trust prompt when launching Mipham Code in a new directory.\n\n**Manual**:\n\n```\n/trust # Show trust status\n/trust add <dir> # Trust a directory\n/trust remove <dir> # Revoke trust\n```\n\n### 7.3 — Trust Store\n\n```\n~/.mipham/trusted-workspaces.json\n```\n\n### 7.4 — Auto-Trust for Worktrees\n\nWhen using git worktrees, Mipham Code automatically trusts worktree directories if the parent workspace is already trusted (via `EnterWorktree`).\n\n### ✅ Verification\n\n```\n/trust # Should show \"✅ Yes\" for current directory\n```\n\n---\n\n## Phase 8: Shell & IDE Integration\n\n**Trigger**: Want terminal integration, aliases, or IDE plugins.\n\n### 8.1 — Shell Alias\n\nAdd to `~/.zshrc` or `~/.bashrc`:\n\n```bash\nalias mipham='cd ~/your-project && bun run ~/path/to/mipham-code/apps/cli/bin/mipham.ts'\n# Or if installed globally:\nalias mipham='mipham'\n```\n\n### 8.2 — VS Code Integration\n\nRun `/ide` to auto-generate `.vscode/` config files:\n\n- `settings.json` — terminal profile \"mipham\" using Bun\n- `keybindings.json` — Cmd+Esc to focus terminal, Cmd+Shift+M for new terminal\n- `extensions.json` — recommends `miphamai.mipham-code` extension\n\nTo use after generation:\n\n1. Restart VS Code (or Cmd+Shift+P → Reload Window)\n2. Open terminal: Ctrl+` or Cmd+Esc\n3. Select \"mipham\" profile from terminal dropdown\n\nInstall the VS Code extension:\n\n```bash\ncode --install-extension miphamai.mipham-code\n```\n\n### 8.3 — JetBrains Integration\n\nSettings → Tools → Terminal → Shell path → `bun run mipham`\n\n### 8.4 — Terminal Setup\n\n```\n/terminal-setup # Shell & terminal config wizard\n/setup 6 # Shell integration (part of full wizard)\n```\n\n### ✅ Verification\n\n```bash\nwhich mipham # Should resolve\n# In VS Code: Ctrl+` → select \"mipham\" profile\n```\n\n---\n\n## Phase 9: Full Verification\n\nRun after all configuration phases complete.\n\n### 9.1 — System Diagnostics\n\n```\n/doctor # System diagnostics check\n```\n\n### 9.2 — End-to-End Test\n\nStart a conversation and verify:\n\n1. Model responds (not stuck on \"connecting...\")\n2. File tools work: \"read CLAUDE.md\"\n3. Bash works: \"list files in current directory\"\n4. Skills load: `/skills list`\n\n### 9.3 — Common Issues & Fixes\n\n| Symptom | Diagnosis | Fix |\n| ------------------------- | -------------------------------------- | ------------------------------------------------ |\n| \"Provider not registered\" | Missing or invalid API key | `env \\| grep API_KEY`; check key format |\n| \"Model not found\" | Model ID mismatch or disabled provider | `/models` to list available; `/switch` to change |\n| Slow responses | Large model, network, or context full | `/fast on` or switch to Flash model; `/compact` |\n| Context full | Too many messages in history | `/compact` to compress; `/clear` to reset |\n| Permission denied | Tool blocked by permission mode | `/permissions` to check; adjust mode |\n| \"Workspace not trusted\" | New directory, not yet trusted | Accept startup prompt or run `/trust` |\n| MCP tools not available | Server not connected | `/mcp connect <name>` or check config |\n| Update not applying | Cached binary | `mipham update --force` then restart |\n| Config changes ignored | YAML syntax error | Validate with `mipham --check-config` |\n\n### 9.4 — Get Help\n\n```\n/help # Full command reference\n/setup # Re-run setup wizard\n/doctor # Run diagnostics\n```\n\nChat-based help: \"help me configure X\" or \"why isn't Y working?\"\n\n---\n\n## Quick Reference: Essential Slash Commands\n\n| Category | Command | Purpose |\n| ------------- | ----------------- | ------------------------------------------------------ |\n| **Setup** | `/setup` | Full 6-step setup wizard |\n| | `/setup 1` | Initialize project (.mipham/ + MIPHAM.md + config.yml) |\n| | `/setup 2` | Configure providers & API keys |\n| | `/setup 3` | Choose default model |\n| | `/setup 4` | Browse & install skills |\n| | `/setup 5` | Configure permissions |\n| | `/setup 6` | Shell & IDE integration |\n| **Diagnosis** | `/doctor` | System diagnostics |\n| | `/trust` | Workspace trust status |\n| | `/permissions` | Tool permission settings |\n| **Model** | `/model` | Interactive model picker (Ctrl+P) |\n| | `/switch` | Switch provider |\n| | `/models` | List available models |\n| **Session** | `/clear` | Reset conversation |\n| | `/compact` | Compress context |\n| | `/rename` | Rename session |\n| **Workflow** | `/plan` | Enter plan mode |\n| | `/review` | Code review |\n| | `/todos` | Task list |\n| **IDE** | `/ide` | Generate VS Code integration files |\n| | `/terminal-setup` | Shell & terminal config |\n| **Skills** | `/skills list` | List installed skills |\n| | `/skills search` | Search skill registry |\n| | `/skills install` | Install a skill |\n\n---\n\n## Post-Setup: What to Do Next\n\nAfter configuration is verified:\n\n1. **Initialize your project**: \"help me understand this codebase\"\n2. **Set up CLAUDE.md**: `/init` to generate project documentation for the AI\n3. **Install relevant skills**: `/setup 4` or `/skills search`\n4. **Configure MCP servers**: `/mcp connect` for external tool integration\n5. **Start coding**: Just start a conversation — the AI will use tools and skills automatically\n" },
21
21
  { type: 'standard', raw: "---\nname: research\ndescription: Deep research against primary sources, executed as a background agent. Collects findings into a single cited Markdown file. Use for investigation that requires reading official docs, source code, specs, or first-party APIs — not secondary summaries.\nversion: 1.0.0\nuser-invocable: true\nallowed-tools:\n - WebSearch\n - WebFetch\n - Agent\n - Bash\n - Write\n - Read\n---\n\n# Research — Background Deep Research\n\n融合 Mipham web-search v3.0(查询构建+验证)+ Matt Pocock research(后台代理+一手来源+Markdown 报告)。\n\n## When to Use\n\n- \"Research X for me\"\n- \"Find out everything about Y from primary sources\"\n- \"Investigate Z and write up findings\"\n- Any question where googling + reading multiple sources is the right answer\n\n## When NOT to Use\n\n- Quick fact lookup → use `/web-search` directly\n- Question answerable from code already in context\n- Pure logic/algorithmic question\n\n---\n\n## Phase 0: Route\n\n```\nResearch task is...\n├── Quick (1-2 sources, immediate answer)?\n│ └── → Use web-search skill directly (Phase 0-4)\n│\n├── Deep (multiple sources, needs synthesis)?\n│ └── → THIS SKILL — background agent\n│\n└── Login-walled / SPA-only sources?\n └── → web-access skill (ComputerUse browser)\n```\n\n---\n\n## Phase 1: Spin Up Background Agent\n\nLaunch a **background agent** to do the heavy reading, so you keep working while it researches.\n\nThe agent's instructions:\n\n```\nYou are a research agent. Your task:\n\n1. Investigate the question against PRIMARY SOURCES ONLY:\n - Official documentation (docs.*.com, *.org)\n - Source code repositories (GitHub, GitLab)\n - Technical specifications (RFCs, standards)\n - First-party API references\n - NOT: blog posts, Medium articles, forum threads, secondary summaries\n\n2. For every claim, follow it back to the source that owns it.\n If a secondary source makes a claim, find the primary source and cite that.\n\n3. Use WebSearch to find sources.\n Use WebFetch to deep-read promising pages.\n Cross-reference critical claims across 2+ independent primary sources.\n\n4. Write findings to a SINGLE Markdown file.\n - Cite every claim with its primary source URL\n - Distinguish between facts (needs citation) and reasoning (your own)\n - Flag outdated content (\"article from 2024, may be stale\")\n - Note if a source is official docs vs community\n\n5. Save the file where the repo already keeps such notes.\n Match existing conventions. If none exist, put it in docs/research/.\n```\n\n---\n\n## Phase 2: Report Format\n\nThe agent writes findings in this structure:\n\n```markdown\n# [Research Topic]\n\n**Date**: YYYY-MM-DD\n**Sources**: N primary, M cross-references\n\n## Key Findings\n\n- [Finding 1] — [Source](URL)\n- [Finding 2] — [Source](URL)\n\n## Detailed Analysis\n\n### [Subtopic A]\n\n[Claim and citation]\n\n### [Subtopic B]\n\n[Claim and citation]\n\n## Source Evaluation\n\n| Source | Type | Authority | Notes |\n| ----------- | ------------- | --------- | --------------------- |\n| [Name](URL) | Official docs | High | Current as of YYYY-MM |\n| [Name](URL) | Source code | High | Tag vX.Y.Z |\n\n## Open Questions\n\n- [Question 1]\n- [Question 2]\n\nSources:\n\n- [Title](URL) — brief note\n```\n\n---\n\n## Phase 3: Review\n\nWhen the background agent completes:\n\n1. Read the output file\n2. Spot-check: did it follow the chain back to primary sources?\n3. Flag any claims that need further verification\n4. Surface uncertainties to the user\n\n---\n\n## Research Quality Checklist\n\n- [ ] Every factual claim has a primary source citation\n- [ ] At least one critical claim is cross-referenced (2+ sources)\n- [ ] Source type is clearly identified (official docs / source code / spec / community)\n- [ ] Outdated content is flagged with publication year\n- [ ] Reasoning vs facts are clearly distinguished\n- [ ] File saved in repo-appropriate location\n" },
22
+ { type: 'standard', raw: "---\nname: safe-coding\ndescription: Safe coding rules for code generation — validate external/user input before use and throw RangeError on invalid input\nversion: 1.0.0\n---\n\n# Safe Coding\n\n处理外部/用户输入前必须校验:`null`、`undefined`、空字符串、格式非法时,抛出 `RangeError`,消息为 `'invalid input'`。\n" },
22
23
  { type: 'standard', raw: "---\nname: security-review\ndescription: Security audit skill — vulnerability scanning, OWASP Top 10, secrets detection, supply chain analysis, and compliance checking\nversion: 1.0.0\n---\n\n# Security Review\n\nComprehensive security audit for codebases. Covers vulnerability detection, compliance, and hardening recommendations.\n\n## Audit Checklist\n\n### 1. Secrets & Credentials\n\n- [ ] No hardcoded API keys, tokens, or passwords in source files\n- [ ] `.env` and `*.pem` files in `.gitignore`\n- [ ] API keys use environment variables or secret managers\n- [ ] No credentials in git history (check `git log -p`)\n- [ ] CI/CD secrets stored securely (not in workflow files)\n\n### 2. OWASP Top 10\n\n- [ ] **Injection**: SQL, NoSQL, OS command, LDAP injection points\n- [ ] **Broken Authentication**: Weak password policies, missing MFA\n- [ ] **Sensitive Data Exposure**: Unencrypted PII, missing TLS\n- [ ] **XXE**: XML external entity processing\n- [ ] **Broken Access Control**: Missing authorization checks\n- [ ] **Security Misconfiguration**: Default credentials, verbose errors\n- [ ] **XSS**: Reflected, stored, DOM-based cross-site scripting\n- [ ] **Insecure Deserialization**: Untrusted data deserialization\n- [ ] **Using Vulnerable Components**: Outdated dependencies with CVEs\n- [ ] **Insufficient Logging**: Missing audit trails for auth events\n\n### 3. Supply Chain\n\n- [ ] All dependencies have known licenses (no copyleft/GPL)\n- [ ] No dependencies with critical CVEs\n- [ ] Lock files committed (pnpm-lock.yaml, package-lock.json)\n- [ ] Dependency update policy in place\n- [ ] SBOM (Software Bill of Materials) available\n\n### 4. Network & API Security\n\n- [ ] TLS 1.3 enforced for all external communications\n- [ ] API endpoints have rate limiting\n- [ ] CORS configured with explicit origins (not `*`)\n- [ ] SSRF protections in place (URL validation, IP filtering)\n- [ ] WebSocket connections use WSS\n- [ ] GraphQL endpoints have query depth limits\n\n### 5. File System & Path Security\n\n- [ ] Path traversal protections (no `../../../etc/passwd`)\n- [ ] File upload validation (type, size, content inspection)\n- [ ] Symlink attacks prevented\n- [ ] Sensitive directories blocked (`/etc`, `/proc`, `/sys`)\n- [ ] Temporary files cleaned up after use\n\n### 6. Code-Level Security\n\n- [ ] No `eval()` or `Function()` with user input\n- [ ] No `child_process.exec()` with unsanitized input\n- [ ] Regex patterns safe from ReDoS\n- [ ] Prototype pollution prevented\n- [ ] No `dangerouslySetInnerHTML` without sanitization (React)\n- [ ] SQL queries use parameterized statements\n\n### 7. Authentication & Sessions\n\n- [ ] Passwords hashed with bcrypt/argon2 (not MD5/SHA1)\n- [ ] Session tokens use `httpOnly`, `secure`, `SameSite=Strict`\n- [ ] JWT tokens have reasonable expiration\n- [ ] Account lockout after failed attempts\n- [ ] Password reset tokens expire and are single-use\n\n### 8. Data Protection\n\n- [ ] PII data encrypted at rest (AES-256-GCM)\n- [ ] Data encrypted in transit (TLS 1.3)\n- [ ] Logs do not contain sensitive data\n- [ ] Database backups encrypted\n- [ ] Data retention policies defined\n\n### 9. Infrastructure\n\n- [ ] Infrastructure as Code (Terraform/Pulumi) used\n- [ ] Cloud resources not publicly exposed unless intended\n- [ ] Security groups / firewalls restrict inbound traffic\n- [ ] Container images scanned for vulnerabilities\n- [ ] Kubernetes pods run as non-root\n\n### 10. Logging & Monitoring\n\n- [ ] Authentication events logged\n- [ ] Failed access attempts logged and alerted\n- [ ] Structured logging format (JSON)\n- [ ] No PII in log messages\n- [ ] Alert thresholds configured for critical events\n\n## Report Format\n\n```\nSecurity Review Report\n======================\nDate: YYYY-MM-DD\nSeverity: Critical | High | Medium | Low\n\nFinding #N: [Title]\nSeverity: Critical/High/Medium/Low\nLocation: file:line\nDescription: [What was found]\nRisk: [What could happen]\nFix: [How to resolve]\n```\n\n## Compliance Standards\n\n- OWASP ASVS Level 2\n- PCI DSS (if handling payment data)\n- GDPR (if handling EU personal data)\n- SOC 2 Type II\n- ISO 27001\n" },
23
24
  { type: 'standard', raw: "---\nname: self-review\ndescription: Self-review of staged or recently changed code — reuse, simplification, efficiency, and architectural alignment\nversion: 2.0.0\n---\n\n# Self Review\n\nReview your own code changes before committing or merging. Focus on quality improvements, not bug hunting.\n\n## When to Run\n\n- Before committing changes\n- After completing a feature or fix\n- Before requesting a peer review\n- As the final step before merging\n\n## Review Passes\n\n### Pass 1: Reuse\n\n- Is there existing code that does the same thing?\n- Are there utility functions or shared libraries you missed?\n- Could this be solved with a standard library method?\n- Are you reimplementing something the framework provides?\n\n### Pass 2: Simplification\n\n- Can a complex function be split into smaller, named functions?\n- Are there unnecessary abstractions (interfaces with one impl, unused generics)?\n- Can nested conditionals be flattened with early returns?\n- Is there dead code, unused imports, or commented-out blocks?\n\n### Pass 3: Efficiency\n\n- Are you looping over data multiple times when once would suffice?\n- Are large objects being copied unnecessarily?\n- Could a synchronous operation be made async/non-blocking?\n- Are regex patterns compiled once or on every call?\n\n### Pass 4: Altitude (Architectural Alignment)\n\n- Does this code belong where it is?\n- Is it in the right layer (UI / business logic / data access)?\n- Does it follow existing patterns in the codebase?\n- Would a new developer understand where to find this?\n\n## Output\n\nAfter each pass, either:\n\n- Apply the improvement directly (for clear wins)\n- Note the observation with a recommendation (for trade-off decisions)\n\n## Anti-Patterns\n\n- ❌ Rewriting working code for style preference\n- ❌ Adding abstractions \"just in case\"\n- ❌ Changing code outside the scope of your changes\n- ❌ \"This could be a microservice\" — no it couldn't\n" },
24
25
  { type: 'standard', raw: "---\nname: superpower\ndescription: Skill discovery and invocation system — find and use skills before any response or action\nversion: 2.0.0\n---\n\n# Superpowers — Using Skills\n\n## The Rule\n\n**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means you should invoke it to check.\n\n## How to Access Skills\n\nUse the `Skill` tool to invoke skills by name. When you invoke a skill, its content is loaded — follow it directly.\n\n## Skill Discovery\n\n### Check Available Skills\n\nSkills are listed in `<system-reminder>` messages. Scan this list when receiving a task.\n\n### Matching Algorithm\n\n1. Parse the user's request for intent keywords\n2. Scan skill names and descriptions for matches\n3. If ANY skill matches at ≥1% probability → invoke it\n4. Multiple matches → invoke all that may apply\n5. Invoked skill doesn't fit → that's fine, don't use it\n\n### Priority Order\n\n1. **Process skills first** — brainstorming, systematic-debugging, tdd. These determine HOW to approach\n2. **Implementation skills second** — frontend-design, mcp-builder. These guide execution\n\n## Red Flags\n\nThese thoughts mean STOP — you're rationalizing:\n\n| Thought | Reality |\n| ----------------------------------- | ---------------------------------------------- |\n| \"This is just a simple question\" | Questions are tasks. Check skills. |\n| \"I need more context first\" | Skill check comes BEFORE clarifying questions. |\n| \"Let me explore the codebase first\" | Skills tell you HOW to explore. |\n| \"I remember this skill\" | Skills evolve. Read current version. |\n| \"The skill is overkill\" | Simple things become complex. Use it. |\n\n## Skill Types\n\n- **Rigid** (TDD, systematic-debugging): Follow exactly. Don't adapt away discipline.\n- **Flexible** (patterns): Adapt principles to context.\n\nThe skill itself tells you which type it is.\n\n## User Instructions\n\nInstructions say WHAT, not HOW. \"Add X\" or \"Fix Y\" doesn't mean skip workflows.\n" },
@@ -12,7 +12,7 @@ interface FrontmatterResult {
12
12
  content: string
13
13
  }
14
14
 
15
- function parseFrontmatter(raw: string): FrontmatterResult {
15
+ export function parseFrontmatter(raw: string): FrontmatterResult {
16
16
  // Strip a leading UTF-8 BOM — otherwise `^---` never matches and a
17
17
  // BOM-prefixed file is silently treated as body text (effectively ignored).
18
18
  const src = raw.replace(/^\uFEFF/, '')
@@ -25,7 +25,7 @@ interface AgentFooterProps {
25
25
  /** Tick counter for live elapsed-time re-renders. */
26
26
  tick: number
27
27
  /** Active foreground tool: shown as [ToolName detail...] before agent lines. */
28
- activeTool?: { name: string; detail: string } | null
28
+ activeTool?: { name: string; detail: string; startTime: number } | null
29
29
  /** Inline agent progress from tool_use streaming: ✻ Gerund… (elapsed · tokens). */
30
30
  agentProgress?: AgentProgress | null
31
31
  }
@@ -118,6 +118,10 @@ export function AgentFooter({ agents, tick, activeTool, agentProgress }: AgentFo
118
118
  {' '}[{activeTool!.name} {activeTool!.detail.slice(0, 60)}
119
119
  {animatedDots(tick)}]
120
120
  </Text>
121
+ <Text dimColor>
122
+ {' '}
123
+ {formatElapsed(Math.floor((Date.now() - activeTool!.startTime) / 1000))}
124
+ </Text>
121
125
  </Box>
122
126
  )}
123
127
 
package/src/ui/app.tsx CHANGED
@@ -1,11 +1,14 @@
1
1
  import React, { useState, useCallback, useEffect, useRef, useMemo } from 'react'
2
2
  import { Box, Text, useInput } from 'ink'
3
3
  import TextInput from 'ink-text-input'
4
+ import { execSync } from 'node:child_process'
4
5
  import { ErrorBoundary } from './error-boundary'
5
6
  import { formatThinking } from './thinking'
6
7
  import type { QueryEngine } from '../core/engine'
7
8
  import type { RemoteEngine } from '../daemon/remote-engine'
8
9
  import type { MiphamConfig } from '../shared/index.ts'
10
+ import type { Llm } from '../providers/llm'
11
+ import { AUTOCOMPLETE_MAX_CONTEXT, type RecentMessage } from '../core/autocomplete'
9
12
  import type { SkillsLoader } from '../skills/loader'
10
13
  import type { PluginManager } from '../plugin/plugin-manager'
11
14
  import { setPreference } from '../config/preferences'
@@ -24,6 +27,7 @@ import { InputBar } from './input'
24
27
  import { ModelPicker } from './picker'
25
28
  import { AgentFooter, type AgentEntry } from './agent-footer'
26
29
  import { GraftStatusLine } from './graft-status'
30
+ import { checkForUpdatesAsync, type UpdateStatus } from '../shared/update'
27
31
  import { collapseNoopTicks } from './loop-noop'
28
32
 
29
33
  /** Current context-window usage % — undefined when unknown (remote stub). */
@@ -164,6 +168,20 @@ export function App({
164
168
  [t],
165
169
  )
166
170
  const [messages, setMessages] = useState<ChatMessage[]>([])
171
+ const autocompleteLlm = useMemo<Llm | undefined>(() => {
172
+ // RemoteEngine(daemon 远程)无本地 LLM → 补全禁用;QueryEngine 取注入 LLM 或回退 registry。
173
+ if (!('getLlm' in engine)) return undefined
174
+ return engine.getLlm() ?? engine.getRegistry()
175
+ }, [engine])
176
+
177
+ const recentMessages = useMemo<RecentMessage[]>(
178
+ () =>
179
+ messages
180
+ .filter((m) => m.role === 'user' || m.role === 'assistant')
181
+ .slice(-AUTOCOMPLETE_MAX_CONTEXT)
182
+ .map((m) => ({ role: m.role as 'user' | 'assistant', content: m.content })),
183
+ [messages],
184
+ )
167
185
  const [isLoading, setIsLoading] = useState(false)
168
186
  /** Idle-drain tick — bumped by the engine's onEnqueue callback whenever the
169
187
  * ScheduleWakeup timer fires while the engine is idle. Drives the idle-drain
@@ -172,6 +190,30 @@ export function App({
172
190
  const [providerId, setProviderId] = useState(initialProvider || config.defaultProvider)
173
191
  const [modelId, setModelId] = useState(initialModel || config.defaultModel)
174
192
  const [pickerOpen, setPickerOpen] = useState(false)
193
+ const [updateStatus, setUpdateStatus] = useState<UpdateStatus | null>(null)
194
+ // Current git branch — read once at mount (not a git repo → null).
195
+ const [gitBranch] = useState<string | null>(() => {
196
+ try {
197
+ const b = execSync('git branch --show-current', { encoding: 'utf-8' }).trim()
198
+ return b || null
199
+ } catch {
200
+ return null
201
+ }
202
+ })
203
+
204
+ // 启动后台查新版(非阻塞;离线静默失败)
205
+ useEffect(() => {
206
+ let cancelled = false
207
+ checkForUpdatesAsync().then((update) => {
208
+ if (!cancelled && update.available) {
209
+ setUpdateStatus({ state: 'available', latest: update.latest })
210
+ }
211
+ })
212
+ return () => {
213
+ cancelled = true
214
+ }
215
+ }, [])
216
+
175
217
  const [agentViewOpen, setAgentViewOpen] = useState(false)
176
218
  const [apiKeyPrompt, setApiKeyPrompt] = useState<{
177
219
  providerId: string
@@ -211,9 +253,13 @@ export function App({
211
253
  const [runningAgents, setRunningAgents] = useState<Record<string, AgentEntry>>({})
212
254
  const [agentTick, setAgentTick] = useState(0)
213
255
  /** Active foreground tool indicator: [Bash command...], [Update file.ts...], etc. */
214
- const [activeTool, setActiveTool] = useState<{ name: string; detail: string } | null>(null)
256
+ const [activeTool, setActiveTool] = useState<{
257
+ name: string
258
+ detail: string
259
+ startTime: number
260
+ } | null>(null)
215
261
  // Refs for immediate state (bypasses React batching so footer renders between rapid chunks)
216
- const activeToolRef = useRef<{ name: string; detail: string } | null>(null)
262
+ const activeToolRef = useRef<{ name: string; detail: string; startTime: number } | null>(null)
217
263
  const agentProgressRef = useRef<AgentProgress | null>(null)
218
264
 
219
265
  // Tick timer for agent elapsed displays (re-renders every second while agents are running).
@@ -307,6 +353,7 @@ export function App({
307
353
  setFocusMode: (on: boolean) => setFocusMode(on),
308
354
  setGoal: (text: string) => setGoalText(text),
309
355
  setUltracodeMode: (on: boolean) => setUltracodeMode(on),
356
+ setUpdateStatus: (s: UpdateStatus) => setUpdateStatus(s),
310
357
  skillsLoader,
311
358
  pluginManager,
312
359
  t,
@@ -494,7 +541,7 @@ export function App({
494
541
  const detail = formatToolDetail(toolName, chunk.toolUse.input)
495
542
 
496
543
  // Show [ToolName detail...] activity indicator for ALL tools (via ref for immediate render)
497
- const toolEntry = { name: toolDisplayName(toolName), detail }
544
+ const toolEntry = { name: toolDisplayName(toolName), detail, startTime: Date.now() }
498
545
  activeToolRef.current = toolEntry
499
546
  setActiveTool(toolEntry)
500
547
  setAgentTick((t) => t + 1)
@@ -1089,6 +1136,13 @@ export function App({
1089
1136
  <InputBar
1090
1137
  onSubmit={handleSubmit}
1091
1138
  isLoading={isLoading}
1139
+ llm={autocompleteLlm}
1140
+ recentMessages={recentMessages}
1141
+ autocompleteEnabled={
1142
+ !process.env.MIPHAM_DISABLE_AUTOCOMPLETE &&
1143
+ (config.autocomplete?.enabled ?? true)
1144
+ }
1145
+ autocompleteDebounceMs={config.autocomplete?.debounceMs ?? 400}
1092
1146
  showCommandPicker={config.showCommandPicker ?? false}
1093
1147
  onTogglePicker={() => setPickerOpen((prev) => !prev)}
1094
1148
  onToggleFocus={() => setFocusMode((prev) => !prev)}
@@ -1149,6 +1203,17 @@ export function App({
1149
1203
  {/* graft status line — mirrors graft's own "◤ graft · …" bar */}
1150
1204
  <GraftStatusLine cwd={process.cwd()} ctxPct={contextUsagePct(engine)} />
1151
1205
 
1206
+ {/* Update notification — green, right-aligned, mirrors Claude Code's "Update installed · Restart to apply" */}
1207
+ {updateStatus && (
1208
+ <Box marginTop={1} flexDirection="row" justifyContent="flex-end" width="100%">
1209
+ <Text color="green">
1210
+ {updateStatus.state === 'installed'
1211
+ ? `✔ ${t('ui.status.update_installed_restart')}`
1212
+ : `✔ ${t('ui.status.update_available', { version: updateStatus.latest })}`}
1213
+ </Text>
1214
+ </Box>
1215
+ )}
1216
+
1152
1217
  {/* Status line — Claude Code style */}
1153
1218
  <Box marginTop={1} flexDirection="column">
1154
1219
  <Box flexDirection="row">
@@ -1166,6 +1231,9 @@ export function App({
1166
1231
  </Text>
1167
1232
  </Box>
1168
1233
  </Box>
1234
+
1235
+ {/* Git branch — dim, bottom-most, mirrors Claude Code's "⏺ main" */}
1236
+ {gitBranch && <Text dimColor>⏺ {gitBranch}</Text>}
1169
1237
  </>
1170
1238
  )}
1171
1239
  </Box>