@yuaone/core 0.3.3 → 0.4.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/dist/agent-loop.d.ts +62 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +705 -18
- package/dist/agent-loop.js.map +1 -1
- package/dist/background-agent.d.ts +110 -0
- package/dist/background-agent.d.ts.map +1 -0
- package/dist/background-agent.js +255 -0
- package/dist/background-agent.js.map +1 -0
- package/dist/coding-standards.d.ts +45 -0
- package/dist/coding-standards.d.ts.map +1 -0
- package/dist/coding-standards.js +1152 -0
- package/dist/coding-standards.js.map +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -6
- package/dist/constants.js.map +1 -1
- package/dist/context-manager.d.ts +6 -0
- package/dist/context-manager.d.ts.map +1 -1
- package/dist/context-manager.js +23 -4
- package/dist/context-manager.js.map +1 -1
- package/dist/index.d.ts +28 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -2
- package/dist/index.js.map +1 -1
- package/dist/llm-client.d.ts +8 -3
- package/dist/llm-client.d.ts.map +1 -1
- package/dist/llm-client.js +64 -13
- package/dist/llm-client.js.map +1 -1
- package/dist/plugin-auto-loader.d.ts +108 -0
- package/dist/plugin-auto-loader.d.ts.map +1 -0
- package/dist/plugin-auto-loader.js +743 -0
- package/dist/plugin-auto-loader.js.map +1 -0
- package/dist/plugin-registry.d.ts +112 -0
- package/dist/plugin-registry.d.ts.map +1 -0
- package/dist/plugin-registry.js +319 -0
- package/dist/plugin-registry.js.map +1 -0
- package/dist/plugin-types.d.ts +388 -0
- package/dist/plugin-types.d.ts.map +1 -0
- package/dist/plugin-types.js +8 -0
- package/dist/plugin-types.js.map +1 -0
- package/dist/plugin-validator.d.ts +54 -0
- package/dist/plugin-validator.d.ts.map +1 -0
- package/dist/plugin-validator.js +129 -0
- package/dist/plugin-validator.js.map +1 -0
- package/dist/repo-knowledge-graph.d.ts +112 -0
- package/dist/repo-knowledge-graph.d.ts.map +1 -0
- package/dist/repo-knowledge-graph.js +561 -0
- package/dist/repo-knowledge-graph.js.map +1 -0
- package/dist/role-registry.js +1 -1
- package/dist/role-registry.js.map +1 -1
- package/dist/self-debug-loop.d.ts +257 -0
- package/dist/self-debug-loop.d.ts.map +1 -0
- package/dist/self-debug-loop.js +870 -0
- package/dist/self-debug-loop.js.map +1 -0
- package/dist/skill-learner.d.ts +136 -0
- package/dist/skill-learner.d.ts.map +1 -0
- package/dist/skill-learner.js +382 -0
- package/dist/skill-learner.js.map +1 -0
- package/dist/skill-loader.d.ts +90 -0
- package/dist/skill-loader.d.ts.map +1 -0
- package/dist/skill-loader.js +309 -0
- package/dist/skill-loader.js.map +1 -0
- package/dist/specialist-registry.d.ts +132 -0
- package/dist/specialist-registry.d.ts.map +1 -0
- package/dist/specialist-registry.js +413 -0
- package/dist/specialist-registry.js.map +1 -0
- package/dist/sub-agent-prompts.d.ts +45 -0
- package/dist/sub-agent-prompts.d.ts.map +1 -0
- package/dist/sub-agent-prompts.js +177 -0
- package/dist/sub-agent-prompts.js.map +1 -0
- package/dist/sub-agent-router.d.ts +75 -0
- package/dist/sub-agent-router.d.ts.map +1 -0
- package/dist/sub-agent-router.js +174 -0
- package/dist/sub-agent-router.js.map +1 -0
- package/dist/sub-agent.d.ts +48 -0
- package/dist/sub-agent.d.ts.map +1 -1
- package/dist/sub-agent.js +108 -5
- package/dist/sub-agent.js.map +1 -1
- package/dist/system-prompt.d.ts +26 -0
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +177 -7
- package/dist/system-prompt.js.map +1 -1
- package/dist/task-classifier.d.ts +25 -1
- package/dist/task-classifier.d.ts.map +1 -1
- package/dist/task-classifier.js +171 -1
- package/dist/task-classifier.js.map +1 -1
- package/dist/tool-planner.d.ts +160 -0
- package/dist/tool-planner.d.ts.map +1 -0
- package/dist/tool-planner.js +501 -0
- package/dist/tool-planner.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/plugins/git/patterns/branch-patterns.json +101 -0
- package/plugins/git/patterns/commit-patterns.json +186 -0
- package/plugins/git/plugin.yaml +128 -0
- package/plugins/git/skills/branch-strategy.md +172 -0
- package/plugins/git/skills/commit-conv.md +178 -0
- package/plugins/git/skills/conflict-resolve.md +159 -0
- package/plugins/git/skills/history-clean.md +199 -0
- package/plugins/git/skills/pr-review.md +196 -0
- package/plugins/git/strategies/conflict-resolve.json +244 -0
- package/plugins/git/strategies/release-flow.json +292 -0
- package/plugins/git/validators/rules.json +348 -0
- package/plugins/react/patterns/anti-patterns.json +88 -0
- package/plugins/react/patterns/components.json +80 -0
- package/plugins/react/patterns/hooks.json +72 -0
- package/plugins/react/plugin.yaml +229 -0
- package/plugins/react/skills/bugfix.md +208 -0
- package/plugins/react/skills/component-gen.md +206 -0
- package/plugins/react/skills/hook-extract.md +208 -0
- package/plugins/react/skills/ssr.md +256 -0
- package/plugins/react/skills/test.md +273 -0
- package/plugins/react/strategies/build-fix.json +43 -0
- package/plugins/react/strategies/hook-loop-fix.json +36 -0
- package/plugins/react/strategies/hydration-fix.json +42 -0
- package/plugins/react/validators/rules.json +92 -0
- package/plugins/typescript/patterns/best-practices.json +25 -0
- package/plugins/typescript/patterns/common-errors.json +32 -0
- package/plugins/typescript/plugin.yaml +74 -0
- package/plugins/typescript/skills/debug.md +23 -0
- package/plugins/typescript/skills/migration.md +24 -0
- package/plugins/typescript/skills/refactor.md +22 -0
- package/plugins/typescript/skills/strict-mode.md +23 -0
- package/plugins/typescript/strategies/strict-migration.json +37 -0
- package/plugins/typescript/strategies/type-error-fix.json +37 -0
- package/plugins/typescript/validators/rules.json +28 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module tool-planner
|
|
3
|
+
* @description Tool Planning Layer — Plans optimal tool sequences before execution.
|
|
4
|
+
*
|
|
5
|
+
* Adds explicit planning between reasoning and execution:
|
|
6
|
+
* reasoning → plan → tool sequence → execute → verify
|
|
7
|
+
*
|
|
8
|
+
* Benefits:
|
|
9
|
+
* - Fewer unnecessary tool calls
|
|
10
|
+
* - Better first-attempt quality
|
|
11
|
+
* - Adapts based on task type and repo memory
|
|
12
|
+
*/
|
|
13
|
+
/** Context provided when creating a plan */
|
|
14
|
+
export interface PlanContext {
|
|
15
|
+
/** User's original message / goal */
|
|
16
|
+
userMessage: string;
|
|
17
|
+
/** Known file paths relevant to the task */
|
|
18
|
+
knownFiles?: string[];
|
|
19
|
+
/** Project language / framework info */
|
|
20
|
+
language?: string;
|
|
21
|
+
/** Whether the project has tests */
|
|
22
|
+
hasTests?: boolean;
|
|
23
|
+
/** Whether the project has lint configured */
|
|
24
|
+
hasLint?: boolean;
|
|
25
|
+
/** Whether the project uses TypeScript */
|
|
26
|
+
usesTypeScript?: boolean;
|
|
27
|
+
/** Extra context hints from TaskClassifier */
|
|
28
|
+
contextHints?: string[];
|
|
29
|
+
}
|
|
30
|
+
/** Repo-specific profile used to adapt plans */
|
|
31
|
+
export interface RepoProfile {
|
|
32
|
+
/** Whether tsc --noEmit should always be run */
|
|
33
|
+
alwaysTypeCheck?: boolean;
|
|
34
|
+
/** Whether lint should always be run after edits */
|
|
35
|
+
alwaysLint?: boolean;
|
|
36
|
+
/** Whether tests should always be run after changes */
|
|
37
|
+
alwaysTest?: boolean;
|
|
38
|
+
/** Custom verification command (e.g., "pnpm build") */
|
|
39
|
+
verifyCommand?: string;
|
|
40
|
+
/** File patterns to always read first (e.g., CLAUDE.md) */
|
|
41
|
+
alwaysReadFiles?: string[];
|
|
42
|
+
/** Known flaky tools to avoid */
|
|
43
|
+
avoidTools?: string[];
|
|
44
|
+
}
|
|
45
|
+
/** A single step in a tool plan */
|
|
46
|
+
export interface ToolPlanStep {
|
|
47
|
+
/** Tool name */
|
|
48
|
+
tool: string;
|
|
49
|
+
/** Why this tool is needed */
|
|
50
|
+
purpose: string;
|
|
51
|
+
/** Expected input pattern */
|
|
52
|
+
expectedInput?: string;
|
|
53
|
+
/** What to do if this step fails */
|
|
54
|
+
fallback?: string;
|
|
55
|
+
/** Dependencies on previous steps (step indices) */
|
|
56
|
+
dependsOn?: number[];
|
|
57
|
+
/** Can be skipped if previous step covers it */
|
|
58
|
+
optional?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/** A complete tool execution plan */
|
|
61
|
+
export interface ToolPlan {
|
|
62
|
+
/** Planned tool sequence */
|
|
63
|
+
steps: ToolPlanStep[];
|
|
64
|
+
/** Estimated total tool calls */
|
|
65
|
+
estimatedCalls: number;
|
|
66
|
+
/** Reasoning for this plan */
|
|
67
|
+
reasoning: string;
|
|
68
|
+
/** Confidence in this plan (0–1) */
|
|
69
|
+
confidence: number;
|
|
70
|
+
}
|
|
71
|
+
/** Report on how well execution followed the plan */
|
|
72
|
+
export interface PlanComplianceReport {
|
|
73
|
+
/** Did execution follow the plan? */
|
|
74
|
+
compliant: boolean;
|
|
75
|
+
/** Steps that were executed as planned */
|
|
76
|
+
executedSteps: number[];
|
|
77
|
+
/** Steps that were skipped */
|
|
78
|
+
skippedSteps: number[];
|
|
79
|
+
/** Tools used that were not in the plan */
|
|
80
|
+
unplannedTools: string[];
|
|
81
|
+
/** Overall compliance ratio (0–1) */
|
|
82
|
+
complianceRatio: number;
|
|
83
|
+
/** Human-readable summary */
|
|
84
|
+
summary: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* ToolPlanner — Plans optimal tool sequences before execution.
|
|
88
|
+
*
|
|
89
|
+
* Uses pre-defined task sequences combined with repo-specific profiles
|
|
90
|
+
* to produce an efficient, dependency-ordered plan.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const planner = new ToolPlanner();
|
|
95
|
+
* const plan = planner.planForTask("debug", {
|
|
96
|
+
* userMessage: "Fix the type error in agent-loop.ts",
|
|
97
|
+
* usesTypeScript: true,
|
|
98
|
+
* });
|
|
99
|
+
* console.log(planner.formatPlanHint(plan));
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export declare class ToolPlanner {
|
|
103
|
+
/**
|
|
104
|
+
* Create a tool plan based on task classification.
|
|
105
|
+
*
|
|
106
|
+
* @param taskType - Task type from TaskClassifier (e.g., "debug", "feature")
|
|
107
|
+
* @param context - Optional planning context (user message, known files, etc.)
|
|
108
|
+
* @returns A ToolPlan with ordered steps, estimated calls, and confidence
|
|
109
|
+
*/
|
|
110
|
+
planForTask(taskType: string, context?: PlanContext): ToolPlan;
|
|
111
|
+
/**
|
|
112
|
+
* Adapt plan based on repo-specific memory.
|
|
113
|
+
* E.g., if repo always needs tsc check, add it.
|
|
114
|
+
*
|
|
115
|
+
* @param plan - The base plan to adapt
|
|
116
|
+
* @param repoProfile - Repo-specific profile
|
|
117
|
+
* @returns Adapted plan with extra steps as needed
|
|
118
|
+
*/
|
|
119
|
+
adaptPlan(plan: ToolPlan, repoProfile?: RepoProfile): ToolPlan;
|
|
120
|
+
/**
|
|
121
|
+
* Format plan as system prompt hint for the LLM.
|
|
122
|
+
*
|
|
123
|
+
* @param plan - The tool plan to format
|
|
124
|
+
* @returns Formatted string suitable for system prompt injection
|
|
125
|
+
*/
|
|
126
|
+
formatPlanHint(plan: ToolPlan): string;
|
|
127
|
+
/**
|
|
128
|
+
* Validate execution against plan (did we follow the plan?).
|
|
129
|
+
*
|
|
130
|
+
* @param plan - The original plan
|
|
131
|
+
* @param executedTools - List of tool names actually executed (in order)
|
|
132
|
+
* @returns Compliance report
|
|
133
|
+
*/
|
|
134
|
+
validateExecution(plan: ToolPlan, executedTools: string[]): PlanComplianceReport;
|
|
135
|
+
/**
|
|
136
|
+
* Get all available task types that have predefined sequences.
|
|
137
|
+
*/
|
|
138
|
+
getAvailableTaskTypes(): string[];
|
|
139
|
+
/**
|
|
140
|
+
* Get the raw step template for a task type (or undefined).
|
|
141
|
+
*/
|
|
142
|
+
getStepsForType(taskType: string): readonly ToolPlanStep[] | undefined;
|
|
143
|
+
/**
|
|
144
|
+
* Customize steps based on context.
|
|
145
|
+
*/
|
|
146
|
+
private customizeSteps;
|
|
147
|
+
/**
|
|
148
|
+
* Compute plan confidence based on task type and context completeness.
|
|
149
|
+
*/
|
|
150
|
+
private computeConfidence;
|
|
151
|
+
/**
|
|
152
|
+
* Build reasoning string for the plan.
|
|
153
|
+
*/
|
|
154
|
+
private buildReasoning;
|
|
155
|
+
/**
|
|
156
|
+
* Find the last index in an array matching a predicate.
|
|
157
|
+
*/
|
|
158
|
+
private findLastIndex;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=tool-planner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-planner.d.ts","sourceRoot":"","sources":["../src/tool-planner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module tool-planner
|
|
3
|
+
* @description Tool Planning Layer — Plans optimal tool sequences before execution.
|
|
4
|
+
*
|
|
5
|
+
* Adds explicit planning between reasoning and execution:
|
|
6
|
+
* reasoning → plan → tool sequence → execute → verify
|
|
7
|
+
*
|
|
8
|
+
* Benefits:
|
|
9
|
+
* - Fewer unnecessary tool calls
|
|
10
|
+
* - Better first-attempt quality
|
|
11
|
+
* - Adapts based on task type and repo memory
|
|
12
|
+
*/
|
|
13
|
+
// ─── Task Sequences ───
|
|
14
|
+
/** Pre-defined tool sequences by task type */
|
|
15
|
+
const TASK_SEQUENCES = {
|
|
16
|
+
debug: [
|
|
17
|
+
{ tool: "grep", purpose: "Find error source/pattern in codebase" },
|
|
18
|
+
{ tool: "file_read", purpose: "Read error source file + related files", dependsOn: [0] },
|
|
19
|
+
{ tool: "file_edit", purpose: "Apply fix based on analysis", dependsOn: [1] },
|
|
20
|
+
{ tool: "shell_exec", purpose: "Run tests to verify fix", dependsOn: [2] },
|
|
21
|
+
],
|
|
22
|
+
feature: [
|
|
23
|
+
{ tool: "file_read", purpose: "Understand existing code context" },
|
|
24
|
+
{ tool: "glob", purpose: "Find related files and patterns", optional: true },
|
|
25
|
+
{ tool: "file_write", purpose: "Create new files if needed" },
|
|
26
|
+
{ tool: "file_edit", purpose: "Modify existing files", dependsOn: [0] },
|
|
27
|
+
{ tool: "shell_exec", purpose: "Build + test", dependsOn: [3] },
|
|
28
|
+
],
|
|
29
|
+
refactor: [
|
|
30
|
+
{ tool: "grep", purpose: "Find all references to refactor target" },
|
|
31
|
+
{ tool: "file_read", purpose: "Read all affected files", dependsOn: [0] },
|
|
32
|
+
{ tool: "file_edit", purpose: "Apply refactoring changes", dependsOn: [1] },
|
|
33
|
+
{ tool: "shell_exec", purpose: "Type check (tsc --noEmit)", dependsOn: [2] },
|
|
34
|
+
{ tool: "shell_exec", purpose: "Run tests", dependsOn: [3] },
|
|
35
|
+
],
|
|
36
|
+
test: [
|
|
37
|
+
{ tool: "file_read", purpose: "Read source file to test" },
|
|
38
|
+
{ tool: "glob", purpose: "Find existing test files/patterns" },
|
|
39
|
+
{ tool: "file_write", purpose: "Create test file", dependsOn: [0, 1] },
|
|
40
|
+
{ tool: "shell_exec", purpose: "Run new tests", dependsOn: [2] },
|
|
41
|
+
],
|
|
42
|
+
security: [
|
|
43
|
+
{ tool: "grep", purpose: "Find vulnerability patterns in codebase" },
|
|
44
|
+
{ tool: "file_read", purpose: "Read potentially vulnerable code", dependsOn: [0] },
|
|
45
|
+
{ tool: "file_edit", purpose: "Apply security fix", dependsOn: [1] },
|
|
46
|
+
{ tool: "shell_exec", purpose: "Run security scan / tests", dependsOn: [2] },
|
|
47
|
+
{ tool: "grep", purpose: "Verify fix removed vulnerability pattern", dependsOn: [3] },
|
|
48
|
+
],
|
|
49
|
+
explain: [
|
|
50
|
+
{ tool: "glob", purpose: "Discover project structure" },
|
|
51
|
+
{ tool: "file_read", purpose: "Read target files", dependsOn: [0] },
|
|
52
|
+
{ tool: "grep", purpose: "Trace dependencies and references", dependsOn: [1], optional: true },
|
|
53
|
+
],
|
|
54
|
+
search: [
|
|
55
|
+
{ tool: "glob", purpose: "Find files matching pattern" },
|
|
56
|
+
{ tool: "grep", purpose: "Search file contents for pattern" },
|
|
57
|
+
{ tool: "file_read", purpose: "Read matched files for detail", dependsOn: [0, 1], optional: true },
|
|
58
|
+
],
|
|
59
|
+
config: [
|
|
60
|
+
{ tool: "file_read", purpose: "Read current config file" },
|
|
61
|
+
{ tool: "file_edit", purpose: "Modify configuration", dependsOn: [0] },
|
|
62
|
+
{ tool: "shell_exec", purpose: "Validate config (build/lint)", dependsOn: [1] },
|
|
63
|
+
],
|
|
64
|
+
deploy: [
|
|
65
|
+
{ tool: "shell_exec", purpose: "Check git status for uncommitted changes" },
|
|
66
|
+
{ tool: "shell_exec", purpose: "Run build to verify", dependsOn: [0] },
|
|
67
|
+
{ tool: "shell_exec", purpose: "Run tests", dependsOn: [1] },
|
|
68
|
+
{ tool: "shell_exec", purpose: "Execute deploy command", dependsOn: [2] },
|
|
69
|
+
],
|
|
70
|
+
design: [
|
|
71
|
+
{ tool: "file_read", purpose: "Read existing design tokens / component styles" },
|
|
72
|
+
{ tool: "glob", purpose: "Find related UI components", optional: true },
|
|
73
|
+
{ tool: "file_edit", purpose: "Update design tokens or styles", dependsOn: [0] },
|
|
74
|
+
{ tool: "file_write", purpose: "Create new components if needed", dependsOn: [0] },
|
|
75
|
+
{ tool: "shell_exec", purpose: "Build to verify no regressions", dependsOn: [2, 3] },
|
|
76
|
+
],
|
|
77
|
+
infra: [
|
|
78
|
+
{ tool: "file_read", purpose: "Read infrastructure config (docker, CI, etc.)" },
|
|
79
|
+
{ tool: "grep", purpose: "Find related infrastructure references", optional: true },
|
|
80
|
+
{ tool: "file_edit", purpose: "Modify infrastructure files", dependsOn: [0] },
|
|
81
|
+
{ tool: "file_write", purpose: "Create new infra files if needed", dependsOn: [0] },
|
|
82
|
+
{ tool: "shell_exec", purpose: "Validate config (dry-run, lint)", dependsOn: [2, 3] },
|
|
83
|
+
],
|
|
84
|
+
performance: [
|
|
85
|
+
{ tool: "grep", purpose: "Find performance bottleneck patterns" },
|
|
86
|
+
{ tool: "file_read", purpose: "Read hot-path code", dependsOn: [0] },
|
|
87
|
+
{ tool: "file_edit", purpose: "Apply optimization", dependsOn: [1] },
|
|
88
|
+
{ tool: "shell_exec", purpose: "Run benchmarks / profile", dependsOn: [2] },
|
|
89
|
+
{ tool: "shell_exec", purpose: "Run tests to ensure no regression", dependsOn: [3] },
|
|
90
|
+
],
|
|
91
|
+
migration: [
|
|
92
|
+
{ tool: "grep", purpose: "Find all references to old API/pattern" },
|
|
93
|
+
{ tool: "file_read", purpose: "Read affected files", dependsOn: [0] },
|
|
94
|
+
{ tool: "file_edit", purpose: "Apply migration changes", dependsOn: [1] },
|
|
95
|
+
{ tool: "shell_exec", purpose: "Type check across codebase", dependsOn: [2] },
|
|
96
|
+
{ tool: "shell_exec", purpose: "Run full test suite", dependsOn: [3] },
|
|
97
|
+
{ tool: "grep", purpose: "Verify no old pattern remains", dependsOn: [4] },
|
|
98
|
+
],
|
|
99
|
+
documentation: [
|
|
100
|
+
{ tool: "file_read", purpose: "Read source code to document" },
|
|
101
|
+
{ tool: "glob", purpose: "Find existing documentation files", optional: true },
|
|
102
|
+
{ tool: "file_edit", purpose: "Update existing documentation", dependsOn: [0, 1] },
|
|
103
|
+
{ tool: "file_write", purpose: "Create new documentation files", dependsOn: [0], optional: true },
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
// ─── ToolPlanner ───
|
|
107
|
+
/**
|
|
108
|
+
* ToolPlanner — Plans optimal tool sequences before execution.
|
|
109
|
+
*
|
|
110
|
+
* Uses pre-defined task sequences combined with repo-specific profiles
|
|
111
|
+
* to produce an efficient, dependency-ordered plan.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const planner = new ToolPlanner();
|
|
116
|
+
* const plan = planner.planForTask("debug", {
|
|
117
|
+
* userMessage: "Fix the type error in agent-loop.ts",
|
|
118
|
+
* usesTypeScript: true,
|
|
119
|
+
* });
|
|
120
|
+
* console.log(planner.formatPlanHint(plan));
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export class ToolPlanner {
|
|
124
|
+
/**
|
|
125
|
+
* Create a tool plan based on task classification.
|
|
126
|
+
*
|
|
127
|
+
* @param taskType - Task type from TaskClassifier (e.g., "debug", "feature")
|
|
128
|
+
* @param context - Optional planning context (user message, known files, etc.)
|
|
129
|
+
* @returns A ToolPlan with ordered steps, estimated calls, and confidence
|
|
130
|
+
*/
|
|
131
|
+
planForTask(taskType, context) {
|
|
132
|
+
const baseSteps = TASK_SEQUENCES[taskType];
|
|
133
|
+
if (!baseSteps) {
|
|
134
|
+
// Unknown task type — return a generic exploration plan
|
|
135
|
+
return {
|
|
136
|
+
steps: [
|
|
137
|
+
{ tool: "glob", purpose: "Discover project structure" },
|
|
138
|
+
{ tool: "file_read", purpose: "Read relevant files" },
|
|
139
|
+
{ tool: "grep", purpose: "Search for relevant patterns", optional: true },
|
|
140
|
+
],
|
|
141
|
+
estimatedCalls: 3,
|
|
142
|
+
reasoning: `Unknown task type "${taskType}" — using generic exploration plan.`,
|
|
143
|
+
confidence: 0.3,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// Deep-copy base steps so we don't mutate the template
|
|
147
|
+
let steps = baseSteps.map((s) => ({ ...s }));
|
|
148
|
+
// Customize based on context
|
|
149
|
+
if (context) {
|
|
150
|
+
steps = this.customizeSteps(steps, taskType, context);
|
|
151
|
+
}
|
|
152
|
+
// Compute confidence based on context availability
|
|
153
|
+
const confidence = this.computeConfidence(taskType, context);
|
|
154
|
+
// Build reasoning
|
|
155
|
+
const reasoning = this.buildReasoning(taskType, steps, context);
|
|
156
|
+
return {
|
|
157
|
+
steps,
|
|
158
|
+
estimatedCalls: steps.filter((s) => !s.optional).length,
|
|
159
|
+
reasoning,
|
|
160
|
+
confidence,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Adapt plan based on repo-specific memory.
|
|
165
|
+
* E.g., if repo always needs tsc check, add it.
|
|
166
|
+
*
|
|
167
|
+
* @param plan - The base plan to adapt
|
|
168
|
+
* @param repoProfile - Repo-specific profile
|
|
169
|
+
* @returns Adapted plan with extra steps as needed
|
|
170
|
+
*/
|
|
171
|
+
adaptPlan(plan, repoProfile) {
|
|
172
|
+
if (!repoProfile)
|
|
173
|
+
return plan;
|
|
174
|
+
const steps = [...plan.steps.map((s) => ({ ...s }))];
|
|
175
|
+
// Add always-read files as first step
|
|
176
|
+
if (repoProfile.alwaysReadFiles && repoProfile.alwaysReadFiles.length > 0) {
|
|
177
|
+
const hasInitialRead = steps.length > 0 && steps[0].tool === "file_read";
|
|
178
|
+
if (!hasInitialRead) {
|
|
179
|
+
steps.unshift({
|
|
180
|
+
tool: "file_read",
|
|
181
|
+
purpose: `Read required files: ${repoProfile.alwaysReadFiles.join(", ")}`,
|
|
182
|
+
expectedInput: repoProfile.alwaysReadFiles.join(", "),
|
|
183
|
+
});
|
|
184
|
+
// Shift all dependsOn indices by 1
|
|
185
|
+
for (let i = 1; i < steps.length; i++) {
|
|
186
|
+
if (steps[i].dependsOn) {
|
|
187
|
+
steps[i].dependsOn = steps[i].dependsOn.map((d) => d + 1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Add type check if always required and not present
|
|
193
|
+
if (repoProfile.alwaysTypeCheck) {
|
|
194
|
+
const hasTypeCheck = steps.some((s) => s.tool === "shell_exec" && s.purpose.toLowerCase().includes("type check"));
|
|
195
|
+
if (!hasTypeCheck) {
|
|
196
|
+
const lastEditIdx = this.findLastIndex(steps, (s) => s.tool === "file_edit" || s.tool === "file_write");
|
|
197
|
+
if (lastEditIdx >= 0) {
|
|
198
|
+
steps.splice(lastEditIdx + 1, 0, {
|
|
199
|
+
tool: "shell_exec",
|
|
200
|
+
purpose: "Type check (tsc --noEmit) — repo requires it",
|
|
201
|
+
dependsOn: [lastEditIdx],
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Add lint if always required and not present
|
|
207
|
+
if (repoProfile.alwaysLint) {
|
|
208
|
+
const hasLint = steps.some((s) => s.tool === "shell_exec" && s.purpose.toLowerCase().includes("lint"));
|
|
209
|
+
if (!hasLint) {
|
|
210
|
+
const lastStep = steps.length - 1;
|
|
211
|
+
steps.push({
|
|
212
|
+
tool: "shell_exec",
|
|
213
|
+
purpose: "Run lint — repo requires it",
|
|
214
|
+
dependsOn: [lastStep],
|
|
215
|
+
optional: true,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Add test if always required and not present
|
|
220
|
+
if (repoProfile.alwaysTest) {
|
|
221
|
+
const hasTest = steps.some((s) => s.tool === "shell_exec" && s.purpose.toLowerCase().includes("test"));
|
|
222
|
+
if (!hasTest) {
|
|
223
|
+
const lastStep = steps.length - 1;
|
|
224
|
+
steps.push({
|
|
225
|
+
tool: "shell_exec",
|
|
226
|
+
purpose: "Run tests — repo requires it",
|
|
227
|
+
dependsOn: [lastStep],
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Add custom verify command
|
|
232
|
+
if (repoProfile.verifyCommand) {
|
|
233
|
+
const hasVerify = steps.some((s) => s.expectedInput === repoProfile.verifyCommand);
|
|
234
|
+
if (!hasVerify) {
|
|
235
|
+
const lastStep = steps.length - 1;
|
|
236
|
+
steps.push({
|
|
237
|
+
tool: "shell_exec",
|
|
238
|
+
purpose: `Run repo verify: ${repoProfile.verifyCommand}`,
|
|
239
|
+
expectedInput: repoProfile.verifyCommand,
|
|
240
|
+
dependsOn: [lastStep],
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Remove avoided tools
|
|
245
|
+
if (repoProfile.avoidTools && repoProfile.avoidTools.length > 0) {
|
|
246
|
+
const avoid = new Set(repoProfile.avoidTools);
|
|
247
|
+
const filtered = steps.filter((s) => !avoid.has(s.tool));
|
|
248
|
+
return {
|
|
249
|
+
...plan,
|
|
250
|
+
steps: filtered,
|
|
251
|
+
estimatedCalls: filtered.filter((s) => !s.optional).length,
|
|
252
|
+
reasoning: plan.reasoning + ` (adapted for repo profile, avoided: ${repoProfile.avoidTools.join(", ")})`,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
...plan,
|
|
257
|
+
steps,
|
|
258
|
+
estimatedCalls: steps.filter((s) => !s.optional).length,
|
|
259
|
+
reasoning: plan.reasoning + " (adapted for repo profile)",
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Format plan as system prompt hint for the LLM.
|
|
264
|
+
*
|
|
265
|
+
* @param plan - The tool plan to format
|
|
266
|
+
* @returns Formatted string suitable for system prompt injection
|
|
267
|
+
*/
|
|
268
|
+
formatPlanHint(plan) {
|
|
269
|
+
const lines = [
|
|
270
|
+
"<tool-plan>",
|
|
271
|
+
`Confidence: ${(plan.confidence * 100).toFixed(0)}%`,
|
|
272
|
+
`Estimated tool calls: ${plan.estimatedCalls}`,
|
|
273
|
+
"",
|
|
274
|
+
"Planned steps:",
|
|
275
|
+
];
|
|
276
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
277
|
+
const step = plan.steps[i];
|
|
278
|
+
const prefix = step.optional ? " (optional) " : " ";
|
|
279
|
+
const deps = step.dependsOn?.length
|
|
280
|
+
? ` [after step ${step.dependsOn.map((d) => d + 1).join(", ")}]`
|
|
281
|
+
: "";
|
|
282
|
+
lines.push(`${prefix}${i + 1}. ${step.tool} — ${step.purpose}${deps}`);
|
|
283
|
+
if (step.fallback) {
|
|
284
|
+
lines.push(` Fallback: ${step.fallback}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
lines.push("");
|
|
288
|
+
lines.push(`Reasoning: ${plan.reasoning}`);
|
|
289
|
+
lines.push("</tool-plan>");
|
|
290
|
+
return lines.join("\n");
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Validate execution against plan (did we follow the plan?).
|
|
294
|
+
*
|
|
295
|
+
* @param plan - The original plan
|
|
296
|
+
* @param executedTools - List of tool names actually executed (in order)
|
|
297
|
+
* @returns Compliance report
|
|
298
|
+
*/
|
|
299
|
+
validateExecution(plan, executedTools) {
|
|
300
|
+
const executedSteps = [];
|
|
301
|
+
const skippedSteps = [];
|
|
302
|
+
const unplannedTools = [];
|
|
303
|
+
// Track which plan steps were matched
|
|
304
|
+
const matched = new Set();
|
|
305
|
+
const plannedTools = plan.steps.map((s) => s.tool);
|
|
306
|
+
// For each executed tool, try to match it to a plan step
|
|
307
|
+
let planCursor = 0;
|
|
308
|
+
for (const tool of executedTools) {
|
|
309
|
+
let found = false;
|
|
310
|
+
// Search forward from cursor for a matching plan step
|
|
311
|
+
for (let i = planCursor; i < plan.steps.length; i++) {
|
|
312
|
+
if (!matched.has(i) && plan.steps[i].tool === tool) {
|
|
313
|
+
matched.add(i);
|
|
314
|
+
executedSteps.push(i);
|
|
315
|
+
planCursor = i + 1;
|
|
316
|
+
found = true;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// If not found forward, search from beginning (out-of-order execution)
|
|
321
|
+
if (!found) {
|
|
322
|
+
for (let i = 0; i < planCursor; i++) {
|
|
323
|
+
if (!matched.has(i) && plan.steps[i].tool === tool) {
|
|
324
|
+
matched.add(i);
|
|
325
|
+
executedSteps.push(i);
|
|
326
|
+
found = true;
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (!found) {
|
|
332
|
+
// Tool not in plan
|
|
333
|
+
if (!plannedTools.includes(tool)) {
|
|
334
|
+
unplannedTools.push(tool);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Steps not matched are skipped
|
|
339
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
340
|
+
if (!matched.has(i)) {
|
|
341
|
+
skippedSteps.push(i);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Calculate compliance
|
|
345
|
+
const requiredSteps = plan.steps.filter((s) => !s.optional).length;
|
|
346
|
+
const requiredExecuted = executedSteps.filter((idx) => !plan.steps[idx].optional).length;
|
|
347
|
+
const complianceRatio = requiredSteps > 0 ? requiredExecuted / requiredSteps : 1;
|
|
348
|
+
const compliant = complianceRatio >= 0.7 && unplannedTools.length <= 2;
|
|
349
|
+
// Build summary
|
|
350
|
+
const summaryParts = [];
|
|
351
|
+
summaryParts.push(`${executedSteps.length}/${plan.steps.length} steps executed`);
|
|
352
|
+
if (skippedSteps.length > 0) {
|
|
353
|
+
const skippedNames = skippedSteps.map((i) => plan.steps[i].tool);
|
|
354
|
+
summaryParts.push(`skipped: ${skippedNames.join(", ")}`);
|
|
355
|
+
}
|
|
356
|
+
if (unplannedTools.length > 0) {
|
|
357
|
+
summaryParts.push(`unplanned: ${unplannedTools.join(", ")}`);
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
compliant,
|
|
361
|
+
executedSteps,
|
|
362
|
+
skippedSteps,
|
|
363
|
+
unplannedTools,
|
|
364
|
+
complianceRatio,
|
|
365
|
+
summary: summaryParts.join("; "),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Get all available task types that have predefined sequences.
|
|
370
|
+
*/
|
|
371
|
+
getAvailableTaskTypes() {
|
|
372
|
+
return Object.keys(TASK_SEQUENCES);
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Get the raw step template for a task type (or undefined).
|
|
376
|
+
*/
|
|
377
|
+
getStepsForType(taskType) {
|
|
378
|
+
const steps = TASK_SEQUENCES[taskType];
|
|
379
|
+
return steps ? steps.map((s) => ({ ...s })) : undefined;
|
|
380
|
+
}
|
|
381
|
+
// ─── Private ───
|
|
382
|
+
/**
|
|
383
|
+
* Customize steps based on context.
|
|
384
|
+
*/
|
|
385
|
+
customizeSteps(steps, taskType, context) {
|
|
386
|
+
const result = [...steps];
|
|
387
|
+
// If specific files are known, add expected input hints
|
|
388
|
+
if (context.knownFiles && context.knownFiles.length > 0) {
|
|
389
|
+
for (const step of result) {
|
|
390
|
+
if (step.tool === "file_read" && !step.expectedInput) {
|
|
391
|
+
step.expectedInput = context.knownFiles.join(", ");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// If project has no tests, make test steps optional
|
|
396
|
+
if (context.hasTests === false) {
|
|
397
|
+
for (const step of result) {
|
|
398
|
+
if (step.tool === "shell_exec" &&
|
|
399
|
+
step.purpose.toLowerCase().includes("test")) {
|
|
400
|
+
step.optional = true;
|
|
401
|
+
step.fallback = "Skip tests — project has no test setup";
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// If project uses TypeScript, add type check hint to shell_exec steps
|
|
406
|
+
if (context.usesTypeScript) {
|
|
407
|
+
const hasTypeCheck = result.some((s) => s.tool === "shell_exec" && s.purpose.toLowerCase().includes("type check"));
|
|
408
|
+
if (!hasTypeCheck && (taskType === "feature" || taskType === "refactor" || taskType === "migration")) {
|
|
409
|
+
const lastEditIdx = this.findLastIndex(result, (s) => s.tool === "file_edit" || s.tool === "file_write");
|
|
410
|
+
if (lastEditIdx >= 0) {
|
|
411
|
+
result.splice(lastEditIdx + 1, 0, {
|
|
412
|
+
tool: "shell_exec",
|
|
413
|
+
purpose: "Type check (tsc --noEmit)",
|
|
414
|
+
dependsOn: [lastEditIdx],
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Add fallback hints for key steps
|
|
420
|
+
for (const step of result) {
|
|
421
|
+
if (!step.fallback) {
|
|
422
|
+
if (step.tool === "grep") {
|
|
423
|
+
step.fallback = "Try glob or file_read with broader pattern";
|
|
424
|
+
}
|
|
425
|
+
else if (step.tool === "file_edit") {
|
|
426
|
+
step.fallback = "If edit fails, try file_write to replace entire file";
|
|
427
|
+
}
|
|
428
|
+
else if (step.tool === "shell_exec") {
|
|
429
|
+
step.fallback = "If command fails, check error output and retry with fix";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return result;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Compute plan confidence based on task type and context completeness.
|
|
437
|
+
*/
|
|
438
|
+
computeConfidence(taskType, context) {
|
|
439
|
+
let confidence = 0.6; // Base confidence for having a known task type
|
|
440
|
+
if (!context)
|
|
441
|
+
return confidence;
|
|
442
|
+
// Boost for known files
|
|
443
|
+
if (context.knownFiles && context.knownFiles.length > 0) {
|
|
444
|
+
confidence += 0.1;
|
|
445
|
+
}
|
|
446
|
+
// Boost for known language/framework
|
|
447
|
+
if (context.language) {
|
|
448
|
+
confidence += 0.05;
|
|
449
|
+
}
|
|
450
|
+
// Boost for test/lint awareness
|
|
451
|
+
if (context.hasTests !== undefined) {
|
|
452
|
+
confidence += 0.05;
|
|
453
|
+
}
|
|
454
|
+
if (context.hasLint !== undefined) {
|
|
455
|
+
confidence += 0.05;
|
|
456
|
+
}
|
|
457
|
+
// Boost for TypeScript projects (better tooling support)
|
|
458
|
+
if (context.usesTypeScript) {
|
|
459
|
+
confidence += 0.05;
|
|
460
|
+
}
|
|
461
|
+
// Context hints boost
|
|
462
|
+
if (context.contextHints && context.contextHints.length > 0) {
|
|
463
|
+
confidence += Math.min(context.contextHints.length * 0.03, 0.1);
|
|
464
|
+
}
|
|
465
|
+
// Task-specific confidence adjustments
|
|
466
|
+
const highConfidenceTasks = ["debug", "test", "search", "config"];
|
|
467
|
+
if (highConfidenceTasks.includes(taskType)) {
|
|
468
|
+
confidence += 0.05;
|
|
469
|
+
}
|
|
470
|
+
return Math.min(confidence, 0.95);
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Build reasoning string for the plan.
|
|
474
|
+
*/
|
|
475
|
+
buildReasoning(taskType, steps, context) {
|
|
476
|
+
const parts = [];
|
|
477
|
+
parts.push(`Task type: ${taskType}`);
|
|
478
|
+
parts.push(`${steps.length} steps planned (${steps.filter((s) => s.optional).length} optional)`);
|
|
479
|
+
if (context?.knownFiles?.length) {
|
|
480
|
+
parts.push(`targeting ${context.knownFiles.length} known file(s)`);
|
|
481
|
+
}
|
|
482
|
+
if (context?.usesTypeScript) {
|
|
483
|
+
parts.push("TypeScript project — includes type checking");
|
|
484
|
+
}
|
|
485
|
+
if (context?.hasTests === false) {
|
|
486
|
+
parts.push("no test setup detected — test steps optional");
|
|
487
|
+
}
|
|
488
|
+
return parts.join("; ") + ".";
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Find the last index in an array matching a predicate.
|
|
492
|
+
*/
|
|
493
|
+
findLastIndex(arr, predicate) {
|
|
494
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
495
|
+
if (predicate(arr[i]))
|
|
496
|
+
return i;
|
|
497
|
+
}
|
|
498
|
+
return -1;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
//# sourceMappingURL=tool-planner.js.map
|