@zhushanwen/pi-evolve-daily 0.1.2 → 0.1.4

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": "@zhushanwen/pi-evolve-daily",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Daily evolution data collector — runs Python analyzer on first session of the day.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -120,6 +120,37 @@ Use these to identify:
120
120
  - **Mixed-trigger skills** — both paths used; healthy signal
121
121
  - **Never-triggered skills** — candidates for removal or description improvement
122
122
 
123
+ #### 3e. New Dimension Analysis
124
+
125
+ 读取 daily-reports 中的新增字段,按优先级分析:
126
+
127
+ 1. **Tool Parameter Errors**(tool_error_stats)
128
+ - `param_error_rate > 25%` → 高优先级建议
129
+ - 某工具参数错误集中 → 针对性建议
130
+
131
+ 2. **Goal Task Quality**(goal_quality_stats)
132
+ - `task_completion_rate < 50%` → 任务拆分优化建议
133
+ - evidence 质量低 → Evidence 要求强化建议
134
+
135
+ 3. **Subagent Efficiency**(subagent_stats)
136
+ - `failure_rate > 20%` → task prompt 优化建议
137
+ - `retry_rate > 15%` → 任务拆分优化建议
138
+
139
+ 4. **Compact Efficiency**(compact_stats + context_stats)
140
+ - `compacts_per_session ≥ 3` → 上下文管理优化建议
141
+ - 上下文利用率持续偏高 → 工具输出优化建议
142
+
143
+ 5. **Workflow Efficiency**(workflow_stats)
144
+ - 某阶段耗时占比 > 50% → 流程优化建议
145
+ - gate 重试频繁 → gate 检查项优化建议
146
+
147
+ 6. **Todo Usage**(todo_stats)
148
+ - `abandon_rate > 25%` → Todo 使用模式优化建议
149
+
150
+ 每个维度的分析结果应作为 actionable issues 写入 feedback-records。
151
+ 对于高优先级发现(超过阈值的指标),生成 EvolutionSuggestion 对象并加入
152
+ 第 4 步的 suggestions 列表中。
153
+
123
154
  ### 4. Generate Suggestions
124
155
 
125
156
  For each actionable finding, create an EvolutionSuggestion object:
@@ -44,6 +44,58 @@ User says "/evolve-report", "evolve-report", "查看报告", "进化报告",
44
44
  - Token consumption (input/output)
45
45
  - Anomalies and signals
46
46
  - Improvement suggestions (if any in the report)
47
+ 4. **New Dimensions** — if present in the report, display additional sections:
48
+
49
+ #### Tool Parameter Errors (`tool_error_stats`)
50
+
51
+ Display when `tool_error_stats` field exists:
52
+ - Param error rate per tool (highlight tools with rate > 25%)
53
+ - Top parameter error patterns with occurrence counts
54
+ - Actionable issues from feedback-records (if any)
55
+
56
+ #### Goal Task Quality (`goal_quality_stats`)
57
+
58
+ Display when `goal_quality_stats` field exists:
59
+ - Task completion rate (highlight if < 50%)
60
+ - Average tasks per goal
61
+ - Evidence quality distribution (high/medium/low)
62
+ - Actionable issues: task splitting, evidence requirements
63
+
64
+ #### Subagent Efficiency (`subagent_stats`)
65
+
66
+ Display when `subagent_stats` field exists:
67
+ - Success / failure / retry counts
68
+ - Failure rate (highlight if > 20%)
69
+ - Retry rate (highlight if > 15%)
70
+ - Actionable issues: task prompt optimization, task splitting
71
+
72
+ #### Compact & Context Efficiency (`compact_stats` + `context_stats`)
73
+
74
+ Display when either field exists:
75
+ - Average compacts per session (highlight if ≥ 3)
76
+ - Context utilization trend (rising / stable / falling)
77
+ - Actionable issues: context management, tool output optimization
78
+
79
+ #### Workflow Efficiency (`workflow_stats`)
80
+
81
+ Display when `workflow_stats` field exists:
82
+ - Per-phase duration breakdown (highlight phase > 50% of total)
83
+ - Gate retry frequency per phase
84
+ - Actionable issues: workflow optimization, gate criteria tuning
85
+
86
+ #### Todo Usage (`todo_stats`)
87
+
88
+ Display when `todo_stats` field exists:
89
+ - Completion / abandon / in-progress counts
90
+ - Abandon rate (highlight if > 25%)
91
+ - Actionable issues: todo usage pattern optimization
92
+
93
+ **Formatting guidelines for new dimensions:**
94
+ - Use a consistent section header format: `### Dimension Name`
95
+ - Show metrics as key-value pairs or simple tables
96
+ - Prefix actionable issues with `⚠` marker
97
+ - If a dimension field is missing from the report, skip that section entirely
98
+ - Group all new dimension sections under a `## Extended Metrics` heading
47
99
 
48
100
  ### List Reports
49
101
 
@@ -0,0 +1,43 @@
1
+ // packages/evolve-daily/src/detectors/compact.ts
2
+
3
+ import type { ProblemDefinition } from "../problems";
4
+
5
+ export interface CompactTrackedItem {
6
+ id: string;
7
+ problemId: "compact-frequency";
8
+ sessionId: string;
9
+ tokensBefore: number;
10
+ detected: boolean;
11
+ status: "pending" | "completed" | "error" | "dismissed";
12
+ detail?: string;
13
+ }
14
+
15
+ export function createCompactDetector(problem: ProblemDefinition) {
16
+ return {
17
+ problemId: problem.id,
18
+
19
+ /**
20
+ * 从 session_compact 事件创建 tracked item。
21
+ * compact 不通过通用的 tool_execution_end handler,
22
+ * 而是独立监听 pi.on("session_compact") 事件。
23
+ */
24
+ createItem(event: {
25
+ compactionEntry?: { tokensBefore?: number };
26
+ }): CompactTrackedItem {
27
+ return {
28
+ id: `compact-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
29
+ problemId: problem.id as "compact-frequency",
30
+ sessionId: "",
31
+ tokensBefore: event.compactionEntry?.tokensBefore ?? 0,
32
+ detected: true,
33
+ status: "pending",
34
+ };
35
+ },
36
+
37
+ steering(item: CompactTrackedItem): string {
38
+ return problem.detector.steering
39
+ .replace("{{id}}", item.id)
40
+ .replace("{{tokensBefore}}", String(item.tokensBefore));
41
+ },
42
+ };
43
+ }
@@ -0,0 +1,59 @@
1
+ // packages/evolve-daily/src/detectors/goal-quality.ts
2
+
3
+ import type { ProblemDefinition } from "../problems";
4
+
5
+ export interface GoalQualityTrackedItem {
6
+ id: string;
7
+ problemId: "goal-task-quality";
8
+ sessionId: string;
9
+ goalId: string;
10
+ taskCount: number;
11
+ completedCount: number;
12
+ cancelledCount: number;
13
+ taskCompletionRate: number;
14
+ taskCancelRate: number;
15
+ status: "pending" | "completed" | "error" | "dismissed";
16
+ detail?: string;
17
+ }
18
+
19
+ export function createGoalQualityDetector(problem: ProblemDefinition) {
20
+ return {
21
+ problemId: problem.id,
22
+ events: problem.detector.events,
23
+
24
+ match(event: { type: string; toolName?: string }): boolean {
25
+ if (event.type !== "tool_result") return false;
26
+ return event.toolName === "goal_manager";
27
+ },
28
+
29
+ createItem(event: {
30
+ type: string;
31
+ toolName?: string;
32
+ details?: { tasks?: Array<{ status: string }> };
33
+ }): GoalQualityTrackedItem {
34
+ const tasks = event.details?.tasks ?? [];
35
+ const completed = tasks.filter((t) => t.status === "completed").length;
36
+ const cancelled = tasks.filter((t) => t.status === "cancelled").length;
37
+ const total = tasks.length;
38
+
39
+ return {
40
+ id: `goal-quality-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
41
+ problemId: problem.id as "goal-task-quality",
42
+ sessionId: "",
43
+ goalId: "",
44
+ taskCount: total,
45
+ completedCount: completed,
46
+ cancelledCount: cancelled,
47
+ taskCompletionRate: total > 0 ? completed / total : 0,
48
+ taskCancelRate: total > 0 ? cancelled / total : 0,
49
+ status: "pending",
50
+ };
51
+ },
52
+
53
+ steering(item: GoalQualityTrackedItem): string {
54
+ return problem.detector.steering
55
+ .replace("{{id}}", item.id)
56
+ .replace("{{completionRate}}", String(item.taskCompletionRate));
57
+ },
58
+ };
59
+ }
@@ -0,0 +1,90 @@
1
+ // packages/evolve-daily/src/detectors/param-error.ts
2
+
3
+ import type { ProblemDefinition } from "../problems";
4
+
5
+ export interface ParamErrorTrackedItem {
6
+ id: string;
7
+ problemId: "tool-param-validation";
8
+ sessionId: string;
9
+ toolName: string;
10
+ errorType: "param" | "runtime" | "unclassified";
11
+ errorPreview: string;
12
+ status: "pending" | "completed" | "error" | "dismissed";
13
+ detail?: string;
14
+ }
15
+
16
+ const PARAM_ERROR_PATTERNS = [
17
+ /required.*parameter/i,
18
+ /missing.*argument/i,
19
+ /invalid.*type/i,
20
+ /schema.*validation/i,
21
+ /unexpected.*token/i,
22
+ /parameter.*missing/i,
23
+ /argument.*required/i,
24
+ /invalid.*argument/i,
25
+ /unknown.*parameter/i,
26
+ /missing.*required/i,
27
+ ];
28
+
29
+ const RUNTIME_ERROR_PATTERNS = [
30
+ /enoent/i,
31
+ /permission denied/i,
32
+ /non-zero exit/i,
33
+ /timeout/i,
34
+ /syntaxerror/i,
35
+ /typeerror/i,
36
+ /connection refused/i,
37
+ /out of memory/i,
38
+ /could not find the exact text/i,
39
+ /no such file/i,
40
+ ];
41
+
42
+ function classifyError(errorMessage: string): "param" | "runtime" | "unclassified" {
43
+ for (const pattern of PARAM_ERROR_PATTERNS) {
44
+ if (pattern.test(errorMessage)) return "param";
45
+ }
46
+ for (const pattern of RUNTIME_ERROR_PATTERNS) {
47
+ if (pattern.test(errorMessage)) return "runtime";
48
+ }
49
+ return "unclassified";
50
+ }
51
+
52
+ const TRACKED_TOOLS = new Set(["edit", "bash", "read", "write"]);
53
+
54
+ export function createParamErrorDetector(problem: ProblemDefinition) {
55
+ return {
56
+ problemId: problem.id,
57
+ events: problem.detector.events,
58
+
59
+ match(event: { type: string; toolName?: string; isError?: boolean }): boolean {
60
+ if (event.type !== "tool_result") return false;
61
+ if (event.isError !== true) return false;
62
+ return TRACKED_TOOLS.has(event.toolName ?? "");
63
+ },
64
+
65
+ createItem(event: {
66
+ type: string;
67
+ toolName?: string;
68
+ isError?: boolean;
69
+ content?: string;
70
+ }): ParamErrorTrackedItem {
71
+ const errorMessage = event.content ?? "";
72
+ return {
73
+ id: `param-error-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
74
+ problemId: problem.id as "tool-param-validation",
75
+ sessionId: "",
76
+ toolName: event.toolName ?? "unknown",
77
+ errorType: classifyError(errorMessage),
78
+ errorPreview: errorMessage.slice(0, 200),
79
+ status: "pending",
80
+ };
81
+ },
82
+
83
+ steering(item: ParamErrorTrackedItem): string {
84
+ return problem.detector.steering
85
+ .replace("{{id}}", item.id)
86
+ .replace("{{toolName}}", item.toolName)
87
+ .replace("{{errorPreview}}", item.errorPreview);
88
+ },
89
+ };
90
+ }
@@ -0,0 +1,65 @@
1
+ // packages/evolve-daily/src/detectors/subagent-result.ts
2
+
3
+ import type { ProblemDefinition } from "../problems";
4
+
5
+ export interface SubagentTrackedItem {
6
+ id: string;
7
+ problemId: "subagent-efficiency";
8
+ sessionId: string;
9
+ taskType: string;
10
+ isError: boolean;
11
+ resultLength: number;
12
+ status: "pending" | "completed" | "error" | "dismissed";
13
+ detail?: string;
14
+ }
15
+
16
+ const TASK_TYPE_PATTERNS: Record<string, RegExp> = {
17
+ code_review: /review|审查|检查/i,
18
+ implementation: /implement|实现|编写|创建/i,
19
+ testing: /test|测试|验证/i,
20
+ analysis: /analyze|分析|研究/i,
21
+ };
22
+
23
+ function classifyTaskType(taskPrompt: string): string {
24
+ for (const [type, pattern] of Object.entries(TASK_TYPE_PATTERNS)) {
25
+ if (pattern.test(taskPrompt)) return type;
26
+ }
27
+ return "unknown";
28
+ }
29
+
30
+ export function createSubagentDetector(problem: ProblemDefinition) {
31
+ return {
32
+ problemId: problem.id,
33
+ events: problem.detector.events,
34
+
35
+ match(event: { type: string; toolName?: string; isError?: boolean }): boolean {
36
+ if (event.type !== "tool_result") return false;
37
+ return event.toolName === "subagent";
38
+ },
39
+
40
+ createItem(event: {
41
+ type: string;
42
+ toolName?: string;
43
+ isError?: boolean;
44
+ content?: string;
45
+ taskPrompt?: string;
46
+ }): SubagentTrackedItem {
47
+ return {
48
+ id: `subagent-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
49
+ problemId: problem.id as "subagent-efficiency",
50
+ sessionId: "",
51
+ taskType: classifyTaskType(event.taskPrompt ?? ""),
52
+ isError: event.isError ?? false,
53
+ resultLength: event.content?.length ?? 0,
54
+ status: "pending",
55
+ };
56
+ },
57
+
58
+ steering(item: SubagentTrackedItem): string {
59
+ return problem.detector.steering
60
+ .replace("{{id}}", item.id)
61
+ .replace("{{exitCode}}", item.isError ? "error" : "0")
62
+ .replace("{{duration}}", "unknown");
63
+ },
64
+ };
65
+ }
package/src/index.ts CHANGED
@@ -1,18 +1,35 @@
1
+ // packages/evolve-daily/src/index.ts
2
+
1
3
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
4
  import { existsSync, unlinkSync } from "node:fs";
3
5
  import { homedir } from "node:os";
4
6
  import { dirname, join } from "node:path";
5
7
  import { fileURLToPath } from "node:url";
6
8
 
9
+ import { PROBLEM_REGISTRY } from "./problems";
10
+ import { createCompactDetector } from "./detectors/compact";
11
+ import { createSubagentDetector } from "./detectors/subagent-result";
12
+ import { createParamErrorDetector } from "./detectors/param-error";
13
+ import { createGoalQualityDetector } from "./detectors/goal-quality";
14
+ import { createTracker } from "./trackers/core";
15
+ import { skillExecutionConfig } from "./trackers/skill-execution";
16
+
7
17
  // 资源文件(Python 脚本)相对于扩展目录自身定位,不依赖外部绝对路径
8
18
  const EXT_DIR = dirname(fileURLToPath(import.meta.url)); // src/
9
- const ANALYZER_PATH = join(EXT_DIR, "..", "scripts", "analyze.py");
19
+ const ANALYZER_PATH = join(EXT_DIR, "..", "analyzer", "analyze.py");
10
20
 
11
21
  // 运行时数据目录使用 Pi 平台约定路径(homedir + .pi/agent/)
12
- // 这是运行时产出数据,不是扩展自带的资源,用平台约定路径是合理的
13
22
  const REPORTS_DIR = join(homedir(), ".pi", "agent", "evolution-data", "daily-reports");
14
23
 
24
+ /** tool_result 事件中匹配的工具结果 detector */
25
+ interface ToolResultDetector {
26
+ problemId: string;
27
+ match(event: Record<string, unknown>): boolean;
28
+ createItem(event: Record<string, unknown>): { id: string; problemId: string; status: string; detail?: string };
29
+ }
30
+
15
31
  export default function evolveDailyExtension(pi: ExtensionAPI) {
32
+ // ── L1: session_start 时调用 Python analyzer ──
16
33
  pi.on("session_start", async () => {
17
34
  const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
18
35
  const reportPath = join(REPORTS_DIR, `${today}.json`);
@@ -22,13 +39,90 @@ export default function evolveDailyExtension(pi: ExtensionAPI) {
22
39
  try {
23
40
  await pi.exec(
24
41
  "python3",
25
- [ANALYZER_PATH, "--since", "1d", "--format", "json", "--output", reportPath],
42
+ [
43
+ ANALYZER_PATH,
44
+ "--since",
45
+ "1d",
46
+ "--format",
47
+ "json",
48
+ "--output",
49
+ reportPath,
50
+ ],
26
51
  { timeout: 30_000 }
27
52
  );
28
53
  } catch (e) {
29
54
  // Clean up partial output if analyzer failed mid-write
30
- try { unlinkSync(reportPath); } catch { /* already gone */ }
55
+ try {
56
+ unlinkSync(reportPath);
57
+ } catch {
58
+ /* already gone */
59
+ }
31
60
  console.error("[evolve-daily] analyzer failed:", e);
32
61
  }
33
62
  });
63
+
64
+ // ── L2c: Tracker 主动追踪(createTracker 在闭包内调用) ──
65
+ createTracker(pi, skillExecutionConfig);
66
+
67
+ // ── L2a: Compact 实时追踪 — 监听 session_compact 事件 ──
68
+ const compactDetector = createCompactDetector(
69
+ PROBLEM_REGISTRY.find((p) => p.id === "compact-frequency")!
70
+ );
71
+
72
+ pi.on("session_compact", async (event: Record<string, unknown>) => {
73
+ try {
74
+ const item = compactDetector.createItem(event);
75
+ pi.appendEntry("evolve-feedback", {
76
+ problemId: item.problemId,
77
+ itemId: item.id,
78
+ status: item.status,
79
+ detail: item.detail ?? null,
80
+ timestamp: new Date().toISOString(),
81
+ });
82
+ } catch (e) {
83
+ console.error(
84
+ `[evolve-daily] compact detector error:`,
85
+ e
86
+ );
87
+ }
88
+ });
89
+
90
+ // ── L2b: 工具结果实时追踪 — 监听 tool_result 事件 ──
91
+ // subagent/param-error/goal-quality detectors 检查 event.type === "tool_result"
92
+ const toolDetectors: ToolResultDetector[] = [
93
+ createSubagentDetector(
94
+ PROBLEM_REGISTRY.find((p) => p.id === "subagent-efficiency")!
95
+ ),
96
+ createParamErrorDetector(
97
+ PROBLEM_REGISTRY.find((p) => p.id === "tool-param-validation")!
98
+ ),
99
+ createGoalQualityDetector(
100
+ PROBLEM_REGISTRY.find((p) => p.id === "goal-task-quality")!
101
+ ),
102
+ ];
103
+
104
+ pi.on(
105
+ "tool_result",
106
+ async (event: Record<string, unknown>, _ctx?: unknown) => {
107
+ for (const detector of toolDetectors) {
108
+ try {
109
+ if (detector.match(event)) {
110
+ const item = detector.createItem(event);
111
+ pi.appendEntry("evolve-feedback", {
112
+ problemId: item.problemId,
113
+ itemId: item.id,
114
+ status: item.status,
115
+ detail: item.detail ?? null,
116
+ timestamp: new Date().toISOString(),
117
+ });
118
+ }
119
+ } catch (e) {
120
+ console.error(
121
+ `[evolve-daily] detector ${detector.problemId} error:`,
122
+ e
123
+ );
124
+ }
125
+ }
126
+ }
127
+ );
34
128
  }