@miphamai/cli 0.81.6 → 0.81.7

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.
Files changed (51) hide show
  1. package/README.md +9 -9
  2. package/bin/daemon.ts +7 -32
  3. package/bin/mipham.ts +43 -29
  4. package/package.json +5 -2
  5. package/skills/standard/mipham-code-setup.SKILL.md +3 -3
  6. package/src/agent/sub-agent.ts +12 -1
  7. package/src/commands/project.ts +92 -12
  8. package/src/config/keys-manager.ts +3 -3
  9. package/src/config/loader.ts +82 -1
  10. package/src/core/context.ts +10 -2
  11. package/src/core/engine.ts +19 -4
  12. package/src/core/metrics.ts +8 -0
  13. package/src/core/paths.ts +79 -0
  14. package/src/core/permission-rules.ts +121 -13
  15. package/src/core/permission.ts +3 -0
  16. package/src/core/session-log.ts +11 -2
  17. package/src/daemon/engine-capabilities.ts +131 -0
  18. package/src/daemon/index.ts +4 -1
  19. package/src/daemon/launch.ts +287 -0
  20. package/src/daemon/remote-engine.ts +2 -0
  21. package/src/daemon/server.ts +9 -0
  22. package/src/daemon/session-worker.ts +7 -4
  23. package/src/i18n-core/locales/en-US.json +6 -7
  24. package/src/i18n-core/locales/zh-CN.json +6 -7
  25. package/src/index.tsx +79 -0
  26. package/src/mcp/client.ts +4 -2
  27. package/src/providers/anthropic.ts +2 -0
  28. package/src/shared/package-info.ts +1 -1
  29. package/src/shared/types.ts +15 -0
  30. package/src/skills/bundled-skills.ts +1 -1
  31. package/src/telemetry/consent.ts +209 -0
  32. package/src/telemetry/crash.ts +197 -0
  33. package/src/telemetry/endpoint.ts +82 -0
  34. package/src/telemetry/index.ts +153 -0
  35. package/src/telemetry/payload.ts +141 -0
  36. package/src/telemetry/queue.ts +95 -0
  37. package/src/telemetry/redact.ts +127 -0
  38. package/src/telemetry/transport.ts +81 -0
  39. package/src/tools/agent/workflow.ts +11 -4
  40. package/src/tools/exec/bash.ts +6 -4
  41. package/src/tools/exec/enter-worktree.ts +6 -5
  42. package/src/tools/exec/exit-worktree.ts +10 -5
  43. package/src/tools/exec/git.ts +18 -8
  44. package/src/tools/system/config.ts +3 -3
  45. package/src/ui/app.tsx +40 -11
  46. package/src/ui/commands.ts +159 -34
  47. package/src/workflow/primitives/agent.ts +4 -2
  48. package/src/core/task-runner-tasks.json +0 -14
  49. package/src/core/task-runner.ts +0 -163
  50. package/src/skills/mipham/runtime.ts +0 -66
  51. package/src/skills/standard/runtime.ts +0 -62
@@ -1,163 +0,0 @@
1
- // apps/cli/src/core/task-runner.ts
2
- // CRSI 端到端任务运行器(C-MVP)——行为效果度量基建。
3
- import { existsSync, readFileSync, mkdirSync, rmSync } from 'node:fs'
4
- import { join } from 'node:path'
5
- import tasksFile from './task-runner-tasks.json' with { type: 'json' }
6
- import { QueryEngine } from './engine'
7
- import { ContextManager } from './context'
8
- import { PermissionSystem } from './permission'
9
- import { ProviderRegistry } from '../providers/registry'
10
- import type { Llm } from '../providers/llm'
11
- import { createToolRegistry } from '../tools'
12
- import type { PermissionLevel } from '../shared'
13
-
14
- export type RunnerGroundTruth = { kind: 'file-contains'; file: string; contains: string[] }
15
-
16
- export interface RunnerTask {
17
- id: string
18
- instruction: string
19
- groundTruth: RunnerGroundTruth
20
- }
21
-
22
- export function loadRunnerTasks(): RunnerTask[] {
23
- return tasksFile.tasks as unknown as RunnerTask[]
24
- }
25
-
26
- export function judgeTask(task: RunnerTask, taskDir: string): { passed: boolean; detail?: string } {
27
- if (task.groundTruth.kind !== 'file-contains') {
28
- return { passed: false, detail: `unsupported groundTruth kind: ${task.groundTruth.kind}` }
29
- }
30
- const filePath = join(taskDir, task.groundTruth.file)
31
- if (!existsSync(filePath)) {
32
- return { passed: false, detail: `file not found: ${task.groundTruth.file}` }
33
- }
34
- const content = readFileSync(filePath, 'utf-8')
35
- for (const needle of task.groundTruth.contains) {
36
- if (!content.includes(needle)) {
37
- return { passed: false, detail: `missing substring: ${needle}` }
38
- }
39
- }
40
- return { passed: true }
41
- }
42
-
43
- export interface TaskRunResult {
44
- taskId: string
45
- passed: boolean
46
- detail?: string
47
- }
48
-
49
- const TASK_DIR_PLACEHOLDER = '<taskDir>'
50
-
51
- function buildEngine(llm: Llm, permission: PermissionLevel, systemPrompt?: string): QueryEngine {
52
- const registry = new ProviderRegistry([], 'test', 'test-model')
53
- // 注册一个永不 chat 的占位 provider——llm 被 setLlm 覆盖,但 process() 内部
54
- // 多处调用 registry.getActive().config.id 记录 provider id,必须能取到。
55
- registry.register('test', {
56
- config: { id: 'test', name: 'Test', protocol: 'openai-compatible', apiKey: 'key', models: [] },
57
- chat: async function* () {
58
- yield { type: 'stop' }
59
- },
60
- listModels: async () => [],
61
- healthCheck: async () => true,
62
- })
63
- const context = new ContextManager({ maxTokens: 100_000, compactionThreshold: 0.9 })
64
- if (systemPrompt !== undefined) context.setSystemPrompt(systemPrompt)
65
- const tools = createToolRegistry()
66
- const engine = new QueryEngine(registry, context, tools, new PermissionSystem(permission))
67
- engine.setLlm(llm)
68
- return engine
69
- }
70
-
71
- export async function runTask(
72
- task: RunnerTask,
73
- llm: Llm,
74
- opts: { taskDir?: string; permission?: PermissionLevel; systemPrompt?: string } = {},
75
- ): Promise<TaskRunResult> {
76
- const taskDir = opts.taskDir ?? join(process.cwd(), '.mipham', 'task-runner')
77
- const permission = opts.permission ?? 'bypassPermissions'
78
-
79
- rmSync(taskDir, { recursive: true, force: true })
80
- mkdirSync(taskDir, { recursive: true })
81
-
82
- const instruction = task.instruction.replaceAll(TASK_DIR_PLACEHOLDER, taskDir)
83
- const engine = buildEngine(llm, permission, opts.systemPrompt)
84
-
85
- for await (const _ of engine.process(instruction)) {
86
- /* drain agentic loop */
87
- }
88
-
89
- const verdict = judgeTask(task, taskDir)
90
- return { taskId: task.id, passed: verdict.passed, detail: verdict.detail }
91
- }
92
-
93
- export interface TaskRunStats {
94
- taskId: string
95
- samples: number
96
- passed: number
97
- /** 0-1 */
98
- passRate: number
99
- }
100
-
101
- export async function runTaskN(
102
- task: RunnerTask,
103
- llm: Llm,
104
- n: number,
105
- opts: { taskDir?: string; permission?: PermissionLevel; systemPrompt?: string } = {},
106
- ): Promise<TaskRunStats> {
107
- let passed = 0
108
- for (let i = 0; i < n; i++) {
109
- const result = await runTask(task, llm, opts)
110
- if (result.passed) passed++
111
- }
112
- return { taskId: task.id, samples: n, passed, passRate: n > 0 ? passed / n : 0 }
113
- }
114
-
115
- export interface RunComparison {
116
- baseline: TaskRunStats
117
- candidate: TaskRunStats
118
- /** 弱判:candidate 不退化(不低于 baseline 且至少 1 次成功) */
119
- notDegraded: boolean
120
- /** candidate 严格更好(通过率更高) */
121
- improved: boolean
122
- }
123
-
124
- export function isNotDegraded(baseline: TaskRunStats, candidate: TaskRunStats): boolean {
125
- return candidate.passRate >= baseline.passRate && candidate.passed >= 1
126
- }
127
-
128
- export function isImproved(baseline: TaskRunStats, candidate: TaskRunStats): boolean {
129
- return candidate.passRate > baseline.passRate
130
- }
131
-
132
- export function compareRuns(baseline: TaskRunStats, candidate: TaskRunStats): RunComparison {
133
- return {
134
- baseline,
135
- candidate,
136
- notDegraded: isNotDegraded(baseline, candidate),
137
- improved: isImproved(baseline, candidate),
138
- }
139
- }
140
-
141
- export async function runBeforeAfter(
142
- task: RunnerTask,
143
- llm: Llm,
144
- n: number,
145
- opts: {
146
- beforePrompt?: string
147
- afterPrompt?: string
148
- taskDir?: string
149
- permission?: PermissionLevel
150
- } = {},
151
- ): Promise<RunComparison> {
152
- const baseline = await runTaskN(task, llm, n, {
153
- taskDir: opts.taskDir,
154
- permission: opts.permission,
155
- systemPrompt: opts.beforePrompt,
156
- })
157
- const candidate = await runTaskN(task, llm, n, {
158
- taskDir: opts.taskDir,
159
- permission: opts.permission,
160
- systemPrompt: opts.afterPrompt,
161
- })
162
- return compareRuns(baseline, candidate)
163
- }
@@ -1,66 +0,0 @@
1
- import type { SkillDefinition } from '../../shared/index.ts'
2
-
3
- export interface MiphamRuntimeContext {
4
- skill: SkillDefinition
5
- cwd: string
6
- sessionId: string
7
- modelId: string
8
- providerId: string
9
- }
10
-
11
- /**
12
- * Mipham exclusive skill runtime.
13
- * Mipham skills use `.mipham-skill.md` extension and have access to
14
- * Mipham-specific features: model optimization, security features, etc.
15
- */
16
- export class MiphamRuntime {
17
- private context: MiphamRuntimeContext
18
-
19
- constructor(context: MiphamRuntimeContext) {
20
- this.context = context
21
- }
22
-
23
- getSkill(): SkillDefinition {
24
- return this.context.skill
25
- }
26
-
27
- getPrompts(): Record<string, string> {
28
- return this.context.skill.prompts || {}
29
- }
30
-
31
- getTools(): SkillDefinition['tools'] {
32
- return this.context.skill.tools || []
33
- }
34
-
35
- getHooks(): SkillDefinition['hooks'] {
36
- return this.context.skill.hooks || []
37
- }
38
-
39
- /**
40
- * Execute a named prompt with Mipham-specific context.
41
- */
42
- async executePrompt(name: string, variables?: Record<string, string>): Promise<string> {
43
- const prompt = this.context.skill.prompts?.[name]
44
- if (!prompt) {
45
- throw new Error(`Prompt "${name}" not found in skill "${this.context.skill.name}"`)
46
- }
47
-
48
- let result = prompt
49
- const allVars: Record<string, string> = {
50
- provider: this.context.providerId,
51
- model: this.context.modelId,
52
- session: this.context.sessionId,
53
- cwd: this.context.cwd,
54
- ...variables,
55
- }
56
-
57
- // Single-pass substitution: replacement values are NOT re-scanned for more
58
- // template markers, so a variable containing `${key}` can't be re-expanded
59
- // into another variable's value.
60
- result = result.replace(/\$\{([^}]+)\}/g, (match, key: string) =>
61
- Object.prototype.hasOwnProperty.call(allVars, key) ? allVars[key]! : match,
62
- )
63
-
64
- return result
65
- }
66
- }
@@ -1,62 +0,0 @@
1
- import type { SkillDefinition } from '../../shared/index.ts'
2
-
3
- export interface StandardRuntimeContext {
4
- skill: SkillDefinition
5
- cwd: string
6
- sessionId: string
7
- }
8
-
9
- /**
10
- * Standard skill runtime — executes SKILL.md definitions.
11
- * Standard skills follow the open-source SKILL.md specification.
12
- */
13
- export class StandardRuntime {
14
- private context: StandardRuntimeContext
15
-
16
- constructor(context: StandardRuntimeContext) {
17
- this.context = context
18
- }
19
-
20
- getSkill(): SkillDefinition {
21
- return this.context.skill
22
- }
23
-
24
- getPrompts(): Record<string, string> {
25
- return this.context.skill.prompts || {}
26
- }
27
-
28
- getPrompt(name: string): string | undefined {
29
- return this.context.skill.prompts?.[name]
30
- }
31
-
32
- getTools(): SkillDefinition['tools'] {
33
- return this.context.skill.tools || []
34
- }
35
-
36
- getHooks(): SkillDefinition['hooks'] {
37
- return this.context.skill.hooks || []
38
- }
39
-
40
- /**
41
- * Execute a named prompt from the skill.
42
- * Returns the prompt text with any variable substitution applied.
43
- */
44
- async executePrompt(name: string, variables?: Record<string, string>): Promise<string> {
45
- const prompt = this.getPrompt(name)
46
- if (!prompt) {
47
- throw new Error(`Prompt "${name}" not found in skill "${this.context.skill.name}"`)
48
- }
49
-
50
- let result = prompt
51
- if (variables) {
52
- // Single-pass substitution: replacement values are NOT re-scanned for
53
- // more template markers, so an argument containing `${key}` can't be
54
- // re-expanded into another variable's value.
55
- result = result.replace(/\$\{([^}]+)\}/g, (match, key: string) =>
56
- Object.prototype.hasOwnProperty.call(variables, key) ? variables[key]! : match,
57
- )
58
- }
59
-
60
- return result
61
- }
62
- }