@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 +38 -11
- package/package.json +1 -1
- package/src/shared/update.ts +41 -4
- package/src/ui/commands.ts +85 -27
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
package/src/shared/update.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
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:
|
|
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.
|
package/src/ui/commands.ts
CHANGED
|
@@ -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
|
|
126
|
-
/todos
|
|
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
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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': '
|
|
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',
|