@miphamai/cli 0.7.8 → 0.7.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/mipham.ts CHANGED
@@ -208,25 +208,52 @@ async function runUpdate(): Promise<boolean> {
208
208
  console.log(` Checking npm registry for latest version...`)
209
209
  console.log()
210
210
 
211
- // Check latest version from npm
211
+ // Check latest version from npm with retry + mirror fallback
212
212
  let latestVersion = ''
213
213
  try {
214
214
  const { execSync } = await import('node:child_process')
215
- const result = execSync(`npm view ${PACKAGE} version --json`, {
216
- encoding: 'utf-8',
217
- timeout: 10_000,
218
- stdio: ['pipe', 'pipe', 'pipe'],
219
- }).trim()
220
- latestVersion = result.replace(/"/g, '')
221
- } catch (err: unknown) {
222
- const msg = err instanceof Error ? err.message : String(err)
223
- console.log(`✗ Failed to check latest version: ${msg}`)
224
- console.log(` Check manually: https://www.npmjs.com/package/${PACKAGE}`)
215
+
216
+ // Registry fallback chain: npm default → npmmirror (China mirror)
217
+ const attempts: Array<{ label: string; registry: string; timeout: number }> = [
218
+ { label: 'npm registry', registry: 'https://registry.npmjs.org/', timeout: 10_000 },
219
+ { label: 'npm registry (retry)', registry: 'https://registry.npmjs.org/', timeout: 20_000 },
220
+ {
221
+ label: 'npmmirror (China mirror)',
222
+ registry: 'https://registry.npmmirror.com/',
223
+ timeout: 15_000,
224
+ },
225
+ ]
226
+
227
+ let lastError: Error | null = null
228
+
229
+ for (const attempt of attempts) {
230
+ try {
231
+ const result = execSync(
232
+ `npm view ${PACKAGE} version --json --registry=${attempt.registry}`,
233
+ { encoding: 'utf-8', timeout: attempt.timeout, stdio: ['pipe', 'pipe', 'pipe'] },
234
+ ).trim()
235
+ latestVersion = result.replace(/"/g, '')
236
+ if (latestVersion) break
237
+ } catch (err) {
238
+ lastError = err instanceof Error ? err : new Error(String(err))
239
+ // Continue to next attempt
240
+ }
241
+ }
242
+
243
+ if (!latestVersion && lastError) throw lastError
244
+ } catch (_err: unknown) {
245
+ console.log(`✗ Could not reach npm registry (network timeout)`)
246
+ console.log()
247
+ console.log(` Manual update:`)
248
+ console.log(` npm install -g ${PACKAGE}@latest`)
249
+ console.log()
250
+ console.log(` Or check: https://www.npmjs.com/package/${PACKAGE}`)
225
251
  process.exit(1)
226
252
  }
227
253
 
228
254
  if (!latestVersion) {
229
255
  console.log('✗ Could not determine latest version.')
256
+ console.log(` Check manually: https://www.npmjs.com/package/${PACKAGE}`)
230
257
  process.exit(1)
231
258
  }
232
259
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miphamai/cli",
3
- "version": "0.7.8",
3
+ "version": "0.7.10",
4
4
  "description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
5
5
  "keywords": [
6
6
  "ai",
@@ -16,6 +16,12 @@ const HOME = homedir()
16
16
  const MIPHAM_HOME = join(HOME, '.mipham')
17
17
  const CONFIG_PATH = join(MIPHAM_HOME, 'config.yml')
18
18
 
19
+ // Registry fallback chain: npm default → npmmirror (China mirror)
20
+ const REGISTRIES = [
21
+ { name: 'npm', url: 'https://registry.npmjs.org/' },
22
+ { name: 'npmmirror', url: 'https://registry.npmmirror.com/' },
23
+ ]
24
+
19
25
  export interface UpdateCheck {
20
26
  /** Current installed version */
21
27
  current: string
@@ -43,18 +49,49 @@ export function getCurrentVersion(): string {
43
49
  }
44
50
 
45
51
  /**
46
- * Fetch the latest version from the npm registry.
52
+ * Run `npm view` against a specific registry with a given timeout.
47
53
  * Returns the version string, or throws on failure.
48
54
  */
49
- function fetchLatestVersion(): string {
50
- const result = execSync(`npm view ${PACKAGE} version --json`, {
55
+ function tryRegistry(registry: string, timeoutMs: number): string {
56
+ const result = execSync(`npm view ${PACKAGE} version --json --registry=${registry}`, {
51
57
  encoding: 'utf-8',
52
- timeout: 30_000,
58
+ timeout: timeoutMs,
53
59
  stdio: ['pipe', 'pipe', 'pipe'],
54
60
  }).trim()
55
61
  return result.replace(/"/g, '')
56
62
  }
57
63
 
64
+ /**
65
+ * Fetch the latest version from the npm registry with retry + mirror fallback.
66
+ *
67
+ * Strategy:
68
+ * 1. Try npm default registry (10s timeout)
69
+ * 2. Retry npm default registry (20s timeout)
70
+ * 3. Fall back to npmmirror.com — China mirror (15s timeout)
71
+ *
72
+ * Returns the version string, or throws on all failures.
73
+ */
74
+ function fetchLatestVersion(): string {
75
+ const attempts: Array<{ registry: string; timeout: number }> = [
76
+ { registry: REGISTRIES[0]!.url, timeout: 10_000 },
77
+ { registry: REGISTRIES[0]!.url, timeout: 20_000 },
78
+ { registry: REGISTRIES[1]!.url, timeout: 15_000 },
79
+ ]
80
+
81
+ let lastError: Error | null = null
82
+
83
+ for (const attempt of attempts) {
84
+ try {
85
+ return tryRegistry(attempt.registry, attempt.timeout)
86
+ } catch (err) {
87
+ lastError = err instanceof Error ? err : new Error(String(err))
88
+ // Continue to next attempt
89
+ }
90
+ }
91
+
92
+ throw lastError ?? new Error('Failed to fetch latest version')
93
+ }
94
+
58
95
  /**
59
96
  * Check if an update is available by comparing the local version
60
97
  * against the npm registry.
@@ -122,8 +122,8 @@ const helpCmd: CommandHandler = (ctx) => {
122
122
  ── Workflow ────────────────────────
123
123
  /plan Enter plan mode (read-only)
124
124
  /no-plan Exit plan mode
125
- /tdd TDD mode [stub]
126
- /todos Task management
125
+ /tdd [goal] Test-Driven Development workflow
126
+ /todos [list|create] Task management
127
127
  /tasks Background tasks
128
128
  /review Code review workflow
129
129
  /pr-comments PR review summary
@@ -133,7 +133,7 @@ const helpCmd: CommandHandler = (ctx) => {
133
133
  /loop init Scaffold .mipham/ vault structure
134
134
  /batch Apply changes across files
135
135
  /hooks Manage lifecycle hook scripts
136
- /schedule View scheduled tasks [stub]
136
+ /schedule View scheduled tasks
137
137
 
138
138
  ── Git & GitHub ─────────────────────
139
139
  /commit Review staged changes + commit
@@ -402,25 +402,71 @@ const planCmd: CommandHandler = (_ctx) => ({
402
402
  `,
403
403
  })
404
404
 
405
- const tddCmd: CommandHandler = (_ctx) => ({
406
- content: stripIndent`
407
- ── TDD Mode ── [stub]
408
- Test-Driven Development workflow: RED → GREEN → REFACTOR.
409
- Full TDD integration coming in a future release.
410
- `,
411
- })
405
+ const tddCmd: CommandHandler = (_ctx, args) => {
406
+ const target = args.join(' ') || 'the current task'
407
+ return {
408
+ content: stripIndent`
409
+ ── TDD Mode ──
410
+ Starting Test-Driven Development workflow for: ${target}
412
411
 
413
- const todosCmd: CommandHandler = (_ctx) => ({
414
- content: stripIndent`
415
- ── Task Management ──
416
- Use the Task tool for structured task tracking:
417
- • Create: Task tool with action=create
418
- • List: Task tool with action=list
419
- • Update: Task tool with action=update
412
+ Cycle: 🔴 RED → 🟢 GREEN → 🔵 REFACTOR
413
+ RED: Write a failing test first
414
+ GREEN: Write minimal code to pass
415
+ REFACTOR: Clean up while keeping tests green
420
416
 
421
- Full /todos integration coming in a future release.
422
- `,
423
- })
417
+ The AI will guide you through each cycle.
418
+ `,
419
+ forwardToAI: `Follow the Test-Driven Development workflow for ${target}:
420
+ 1. RED — Write a failing test that defines the expected behavior. Use the project's test framework (Vitest/Jest/pytest). Show me the test code and confirm it fails.
421
+ 2. GREEN — Write the minimum code needed to make the test pass. Run the test to verify it passes.
422
+ 3. REFACTOR — Clean up both test and implementation code. Remove duplication, improve names, simplify. Keep tests green.
423
+ Repeat for each behavior. Do NOT write implementation before tests.`,
424
+ }
425
+ }
426
+
427
+ const todosCmd: CommandHandler = (_ctx, args) => {
428
+ const sub = args[0]
429
+ if (sub === 'create') {
430
+ const title = args.slice(1).join(' ')
431
+ if (!title.trim()) {
432
+ return {
433
+ content:
434
+ 'Usage: /todos create <task-title>\n\nExample: /todos create Add user authentication',
435
+ }
436
+ }
437
+ return {
438
+ content: `── Create Task ──\n\nCreating task: "${title.trim()}"\n\nPassing to AI for structured task creation with TaskCreate...`,
439
+ forwardToAI: `Create a new task using TaskCreate with subject "${title.trim()}". Set a clear description and activeForm.`,
440
+ }
441
+ }
442
+
443
+ if (sub === 'list' || !sub) {
444
+ return {
445
+ content: stripIndent`
446
+ ── Task Management ──
447
+ Fetching current task list...
448
+
449
+ Shortcuts:
450
+ /todos list Show all tasks
451
+ /todos create <title> Create a new task
452
+ `,
453
+ forwardToAI:
454
+ 'Use TaskList to show all current tasks. Present them in a clear summary grouped by status (pending/in_progress/completed). If there are no tasks, suggest creating one.',
455
+ }
456
+ }
457
+
458
+ return {
459
+ content: stripIndent`
460
+ ── Task Management ──
461
+ /todos list Show all tasks
462
+ /todos create <title> Create a new task
463
+
464
+ The AI manages task state via TaskCreate, TaskList, TaskUpdate, and TaskGet tools.
465
+ Tasks appear in the /tasks view and persist across the session.
466
+ `,
467
+ forwardToAI: 'Use TaskList to show all current tasks, then present them clearly.',
468
+ }
469
+ }
424
470
 
425
471
  // ═══════════════════════════════════════════════════════════════
426
472
  // Project
@@ -1205,16 +1251,17 @@ const scheduleCmd: CommandHandler = (_ctx) => ({
1205
1251
  content: stripIndent`
1206
1252
  ── Scheduled Tasks ──
1207
1253
 
1254
+ Fetching active scheduled tasks and loops...
1255
+
1208
1256
  Active loops are managed via ScheduleWakeup (in-session) and
1209
1257
  CronCreate (durable, survives restarts).
1210
1258
 
1211
1259
  Use /loop <interval> <prompt> to start a recurring task.
1212
1260
  Use CronList to view all scheduled jobs.
1213
1261
  Use CronDelete <id> to cancel a durable job.
1214
-
1215
- The ScheduleWakeup mechanism automatically re-schedules on
1216
- each wake, creating a self-perpetuating loop within the session.
1217
1262
  `,
1263
+ forwardToAI:
1264
+ 'Use CronList to show all currently scheduled cron jobs (both durable and session-only). Present them in a clear table with: name/ID, schedule (cron expression), type (recurring vs one-shot), and status. Also note any active ScheduleWakeup loops if visible in the current session. If no scheduled tasks exist, suggest: "No scheduled tasks. Use /loop <interval> <prompt> to create a recurring task."',
1218
1265
  })
1219
1266
 
1220
1267
  // ═══════════════════════════════════════════════════════════════
@@ -3580,7 +3627,17 @@ const registry = new Map<string, CommandHandler>()
3580
3627
 
3581
3628
  // Session
3582
3629
  registry.set('/help', helpCmd)
3583
- registry.set('/pick', () => ({ content: 'Opening model picker... (use Ctrl+P or /pick)' }))
3630
+ const pickCmd: CommandHandler = (ctx) => ({
3631
+ content: `── Model Picker ──
3632
+
3633
+ Use ↑↓ to navigate, Enter to select, Esc to close.
3634
+
3635
+ Current: ${ctx.providerId} / ${ctx.modelId}
3636
+ Available: ${ctx.config.providers.filter((p) => p.status === 'active').length} providers, ${ctx.config.providers.flatMap((p) => p.models.filter((m) => m.status === 'active')).length} models
3637
+
3638
+ Tip: Press Ctrl+P or type /pick from the chat input.`,
3639
+ })
3640
+ registry.set('/pick', pickCmd)
3584
3641
  registry.set('/version', versionCmd)
3585
3642
  registry.set('/clear', clearCmd)
3586
3643
  registry.set('/exit', exitCmd)
@@ -3608,6 +3665,7 @@ registry.set('/providers', providersCmd)
3608
3665
  registry.set('/config', configCmd)
3609
3666
  registry.set('/fast', fastCmd)
3610
3667
  registry.set('/effort', effortCmd)
3668
+ registry.set('/switch', switchCmd)
3611
3669
 
3612
3670
  // Tools & Skills
3613
3671
  registry.set('/tools', toolsCmd)
@@ -3759,8 +3817,8 @@ const COMMAND_DESCRIPTIONS: Record<string, string> = {
3759
3817
  '/plugin-disable': 'Disable an enabled plugin',
3760
3818
  '/plan': 'Enter plan mode',
3761
3819
  '/no-plan': 'Exit plan mode',
3762
- '/tdd': 'TDD mode',
3763
- '/todos': 'Task management',
3820
+ '/tdd': 'Test-Driven Development workflow (RED → GREEN → REFACTOR)',
3821
+ '/todos': 'Task management (list/create tasks)',
3764
3822
  '/tasks': 'Background tasks',
3765
3823
  '/review': 'Code review workflow',
3766
3824
  '/pr-comments': 'PR review summary',
@@ -3785,7 +3843,7 @@ const COMMAND_DESCRIPTIONS: Record<string, string> = {
3785
3843
  '/agents': 'Agent view dashboard',
3786
3844
  '/bg': 'Run a background agent task',
3787
3845
  '/artifact': 'Manage artifacts',
3788
- '/schedule': 'View scheduled tasks',
3846
+ '/schedule': 'View scheduled tasks and cron jobs',
3789
3847
  '/commit': 'Generate commit message and review staged changes',
3790
3848
  '/push': 'Push the current branch',
3791
3849
  '/pr': 'Create a pull request',