@miphamai/cli 0.75.0 → 0.76.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miphamai/cli",
3
- "version": "0.75.0",
3
+ "version": "0.76.0",
4
4
  "description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
5
5
  "keywords": [
6
6
  "ai",
@@ -341,6 +341,7 @@ export function loadConfig(cwd: string = process.cwd()): MiphamConfig {
341
341
  skills: {
342
342
  paths: config.skills?.paths ?? [],
343
343
  mcpServers: [...existingServers, ...newFromJson],
344
+ reminder: config.skills?.reminder,
344
345
  },
345
346
  }
346
347
  }
@@ -145,6 +145,24 @@ function extractSubstitutions(command: string): string[] {
145
145
  return inners
146
146
  }
147
147
 
148
+ /**
149
+ * Flatten a command into every matchable sub-command: its shell segments plus
150
+ * the commands nested inside `$(...)`/backtick substitutions (recursively). So
151
+ * a `Bash(rm *)` deny rule also matches `REPORTTIME=$(rm -rf ~)` — zsh evaluates
152
+ * substitutions in REPORTTIME/REPORTMEMORY/DIRSTACKSIZE assignments immediately.
153
+ * Over-matching is the safe direction for a deny rule.
154
+ */
155
+ function flattenCommand(command: string): string[] {
156
+ const out: string[] = []
157
+ for (const seg of splitShellSegments(command)) {
158
+ out.push(seg)
159
+ for (const inner of extractSubstitutions(seg)) {
160
+ out.push(...flattenCommand(inner))
161
+ }
162
+ }
163
+ return out
164
+ }
165
+
148
166
  /**
149
167
  * Detect reader/writer commands at the front of each shell segment and recurse
150
168
  * into command substitutions, so `echo $(cat .git-credentials)` is caught.
@@ -215,10 +233,11 @@ export function matchBashRule(
215
233
  if (toolName !== baseTool!) return false
216
234
 
217
235
  // For Bash: match against the command string (any segment of a compound
218
- // command — `rm -rf /` buried in `foo && rm -rf /` still matches).
236
+ // command — `rm -rf /` buried in `foo && rm -rf /` still matches — or a
237
+ // `$(...)`/backtick substitution, so `Bash(rm *)` catches `x=$(rm -rf ~)`).
219
238
  if (baseTool === 'Bash') {
220
239
  const cmd = String(toolInput.command || '')
221
- return splitShellSegments(cmd).some((seg) => wildcardMatch(subPattern!, seg))
240
+ return flattenCommand(cmd).some((seg) => wildcardMatch(subPattern!, seg))
222
241
  }
223
242
 
224
243
  // For Write/Edit/Read: match against the file_path with path-glob semantics.
package/src/index.tsx CHANGED
@@ -410,7 +410,7 @@ export async function runApp(options: RunOptions): Promise<void> {
410
410
  if (context.getMessageCount() === 0) {
411
411
  const basePrompt = instructions.buildSystemPrompt(config.permission as string)
412
412
  const memoryReminder = loadSessionMemories(basePrompt)
413
- const skillsReminder = skillsLoader.buildSystemReminder()
413
+ const skillsReminder = skillsLoader.buildSystemReminder(5000, config.skills?.reminder ?? 'full')
414
414
 
415
415
  // Inject previous session summary for AI continuity
416
416
  let prompt = basePrompt
@@ -68,6 +68,15 @@ export const DEFAULT_PROVIDERS: ProviderConfig[] = [
68
68
  vision: true,
69
69
  status: 'active',
70
70
  },
71
+ {
72
+ id: 'claude-mythos-5',
73
+ name: 'Claude Mythos 5',
74
+ providerId: 'anthropic',
75
+ contextWindow: 1_000_000,
76
+ maxOutput: 128_000,
77
+ vision: true,
78
+ status: 'active',
79
+ },
71
80
  {
72
81
  id: 'claude-sonnet-5',
73
82
  name: 'Claude Sonnet 5',
@@ -349,6 +358,78 @@ export const DEFAULT_PROVIDERS: ProviderConfig[] = [
349
358
  },
350
359
  ],
351
360
  },
361
+ {
362
+ id: 'minimax',
363
+ name: 'MiniMax (国内)',
364
+ protocol: 'openai-compatible',
365
+ baseUrl: 'https://api.minimaxi.com/v1',
366
+ apiKey: '${MINIMAX_API_KEY}',
367
+ models: [
368
+ {
369
+ id: 'MiniMax-M2.7',
370
+ name: 'MiniMax M2.7',
371
+ providerId: 'minimax',
372
+ contextWindow: 204_800,
373
+ maxOutput: 64_000,
374
+ vision: false,
375
+ status: 'active',
376
+ },
377
+ {
378
+ id: 'MiniMax-M2',
379
+ name: 'MiniMax M2',
380
+ providerId: 'minimax',
381
+ contextWindow: 204_800,
382
+ maxOutput: 64_000,
383
+ vision: false,
384
+ status: 'active',
385
+ },
386
+ {
387
+ id: 'MiniMax-Text-01',
388
+ name: 'MiniMax Text 01',
389
+ providerId: 'minimax',
390
+ contextWindow: 1_000_000,
391
+ maxOutput: 64_000,
392
+ vision: false,
393
+ status: 'active',
394
+ },
395
+ ],
396
+ },
397
+ {
398
+ id: 'minimax-global',
399
+ name: 'MiniMax (国际)',
400
+ protocol: 'openai-compatible',
401
+ baseUrl: 'https://api.minimax.io/v1',
402
+ apiKey: '${MINIMAX_GLOBAL_API_KEY}',
403
+ models: [
404
+ {
405
+ id: 'MiniMax-M2.7',
406
+ name: 'MiniMax M2.7',
407
+ providerId: 'minimax-global',
408
+ contextWindow: 204_800,
409
+ maxOutput: 64_000,
410
+ vision: false,
411
+ status: 'active',
412
+ },
413
+ {
414
+ id: 'MiniMax-M2',
415
+ name: 'MiniMax M2',
416
+ providerId: 'minimax-global',
417
+ contextWindow: 204_800,
418
+ maxOutput: 64_000,
419
+ vision: false,
420
+ status: 'active',
421
+ },
422
+ {
423
+ id: 'MiniMax-Text-01',
424
+ name: 'MiniMax Text 01',
425
+ providerId: 'minimax-global',
426
+ contextWindow: 1_000_000,
427
+ maxOutput: 64_000,
428
+ vision: false,
429
+ status: 'active',
430
+ },
431
+ ],
432
+ },
352
433
  MIPHAM_PROVIDER,
353
434
  {
354
435
  id: 'ollama',
@@ -401,6 +482,15 @@ export const DEFAULT_PROVIDERS: ProviderConfig[] = [
401
482
  vision: false,
402
483
  status: 'active',
403
484
  },
485
+ {
486
+ id: 'gpt-6-astra',
487
+ name: 'GPT-6 Astra',
488
+ providerId: 'openai',
489
+ contextWindow: 1_050_000,
490
+ maxOutput: 128_000,
491
+ vision: true,
492
+ status: 'active',
493
+ },
404
494
  ],
405
495
  },
406
496
  {
@@ -9,7 +9,7 @@
9
9
  export const PACKAGE_NAME = '@miphamai/cli' as const
10
10
 
11
11
  /** 当前发布版本 */
12
- export const PACKAGE_VERSION = '0.75.0' as const
12
+ export const PACKAGE_VERSION = '0.76.0' as const
13
13
 
14
14
  /** npm install 全局安装命令 */
15
15
  export const NPM_INSTALL_COMMAND = `npm install -g ${PACKAGE_NAME}` as const
@@ -147,7 +147,12 @@ export interface MiphamConfig {
147
147
  /** When false, disable the slash-command picker auto-popup on `/`. Default false. */
148
148
  showCommandPicker?: boolean
149
149
  providers: ProviderConfig[]
150
- skills?: { paths: string[]; mcpServers: McpServerConfig[] }
150
+ skills?: {
151
+ paths: string[]
152
+ mcpServers: McpServerConfig[]
153
+ /** Startup skill-list budget: full (default) | compact (one-line desc) | off. */
154
+ reminder?: 'full' | 'compact' | 'off'
155
+ }
151
156
  marketplace?: {
152
157
  /** If set, only allow installs from matching repos (e.g. ["One-Mipham/*"]) */
153
158
  strictKnownMarketplaces?: string[]
@@ -204,8 +204,15 @@ export class SkillsLoader implements Skills {
204
204
  * the Skill tool. A single full listing keeps the whole catalog discoverable:
205
205
  * at session start there is no query to match against, so a keyword "recall"
206
206
  * would silently hide most skills. Capped at `maxTokens` to stay bounded.
207
+ *
208
+ * `mode` controls the startup token budget:
209
+ * - `full` (default): name + full description
210
+ * - `compact`: name + description collapsed to one short line
211
+ * - `off`: no reminder at all
207
212
  */
208
- buildSystemReminder(maxTokens: number = 5000): string {
213
+ buildSystemReminder(maxTokens: number = 5000, mode: 'full' | 'compact' | 'off' = 'full'): string {
214
+ if (mode === 'off') return ''
215
+
209
216
  const selected = this.list().filter((s) => !s.disableModelInvocation)
210
217
 
211
218
  if (selected.length === 0) return ''
@@ -218,7 +225,8 @@ export class SkillsLoader implements Skills {
218
225
  let tokenBudget = 0
219
226
  for (const skill of selected) {
220
227
  const safeDesc = sanitizeSkillDescription(skill.description, skill.type)
221
- const entry = `- ${skill.name}: ${safeDesc}`
228
+ const desc = mode === 'compact' ? truncateSkillDescription(safeDesc) : safeDesc
229
+ const entry = `- ${skill.name}: ${desc}`
222
230
  const entryTokens = Math.ceil(entry.length / 4) + 1 // rough estimate
223
231
  if (tokenBudget + entryTokens > maxTokens) break
224
232
 
@@ -242,3 +250,13 @@ export class SkillsLoader implements Skills {
242
250
  return base.replace(/\.(SKILL|mipham-skill)\.md$/i, '')
243
251
  }
244
252
  }
253
+
254
+ const COMPACT_DESC_MAX_CHARS = 80
255
+
256
+ /** Compact reminder: collapse a description to a single short line. */
257
+ function truncateSkillDescription(desc: string): string {
258
+ const firstLine = desc.split('\n')[0]?.trim() ?? ''
259
+ return firstLine.length > COMPACT_DESC_MAX_CHARS
260
+ ? firstLine.slice(0, COMPACT_DESC_MAX_CHARS) + '…'
261
+ : firstLine
262
+ }