clink-cli 0.2.1__py3-none-any.whl
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.
- clink/__init__.py +19 -0
- clink/agents/__init__.py +30 -0
- clink/agents/base.py +70 -0
- clink/agents/claude.py +7 -0
- clink/agents/codex.py +7 -0
- clink/agents/gemini.py +7 -0
- clink/allowed_roots.py +356 -0
- clink/backend.py +765 -0
- clink/cli.py +594 -0
- clink/constants.py +78 -0
- clink/contracts.py +167 -0
- clink/files.py +70 -0
- clink/generation.py +274 -0
- clink/generation_capabilities.py +207 -0
- clink/generation_contracts.py +214 -0
- clink/generation_policy.py +174 -0
- clink/models.py +243 -0
- clink/native.py +409 -0
- clink/native_adapters.py +244 -0
- clink/native_contracts.py +68 -0
- clink/native_generation.py +421 -0
- clink/native_generation_claude_policy.py +59 -0
- clink/native_generation_contracts.py +264 -0
- clink/native_generation_families.py +218 -0
- clink/native_generation_family_policy.py +14 -0
- clink/native_generation_files.py +153 -0
- clink/native_generation_policy.py +82 -0
- clink/native_policy.py +86 -0
- clink/parsers/__init__.py +34 -0
- clink/parsers/agy.py +53 -0
- clink/parsers/agy_native.py +161 -0
- clink/parsers/agy_native_generation.py +189 -0
- clink/parsers/base.py +209 -0
- clink/parsers/claude.py +104 -0
- clink/parsers/claude_native.py +113 -0
- clink/parsers/claude_native_generation.py +346 -0
- clink/parsers/codex.py +172 -0
- clink/parsers/codex_generation.py +212 -0
- clink/parsers/codex_native.py +236 -0
- clink/parsers/codex_native_generation.py +367 -0
- clink/parsers/gemini.py +110 -0
- clink/parsers/grok.py +63 -0
- clink/parsers/grok_native.py +91 -0
- clink/parsers/grok_native_generation.py +157 -0
- clink/policy.py +180 -0
- clink/registry.py +544 -0
- clink/resources/CODEX-LICENSE +201 -0
- clink/resources/CODEX-NOTICE +6 -0
- clink/resources/PROVENANCE.md +16 -0
- clink/resources/codex-0.166.0-models-direct.json +1284 -0
- clink/resources/codex-0.166.0-models-generation.json +1269 -0
- clink/resources/native-agy-1.4.12-admission.json +746 -0
- clink/resources/native-claude-2.1.304-admission.json +99 -0
- clink/resources/native-generation-claude-2.1.304-admission.json +277 -0
- clink/resources/native-generation-codex-0.166.0-admission.json +309 -0
- clink/resources/native-generation-grok-1.0.70-admission.json +329 -0
- clink/resources/native-grok-1.0.70-admission.json +644 -0
- clink/runner.py +303 -0
- clink/setup.py +399 -0
- clink/tasks.py +695 -0
- clink_cli-0.2.1.dist-info/METADATA +225 -0
- clink_cli-0.2.1.dist-info/RECORD +141 -0
- clink_cli-0.2.1.dist-info/WHEEL +5 -0
- clink_cli-0.2.1.dist-info/entry_points.txt +3 -0
- clink_cli-0.2.1.dist-info/licenses/LICENSE +197 -0
- clink_cli-0.2.1.dist-info/licenses/clink/resources/CODEX-LICENSE +201 -0
- clink_cli-0.2.1.dist-info/licenses/clink/resources/CODEX-NOTICE +6 -0
- clink_cli-0.2.1.dist-info/top_level.txt +7 -0
- conf/__init__.py +1 -0
- conf/cli_clients/agy.json +40 -0
- conf/cli_clients/claude.json +39 -0
- conf/cli_clients/codex.json +40 -0
- conf/cli_clients/gemini.json +23 -0
- conf/cli_clients/grok.json +40 -0
- config.py +40 -0
- server.py +896 -0
- systemprompts/__init__.py +35 -0
- systemprompts/analyze_prompt.py +87 -0
- systemprompts/chat_prompt.py +57 -0
- systemprompts/clink/codex_codereviewer.txt +8 -0
- systemprompts/clink/default.txt +8 -0
- systemprompts/clink/default_codereviewer.txt +8 -0
- systemprompts/clink/default_planner.txt +7 -0
- systemprompts/clink/native_codereviewer.txt +1 -0
- systemprompts/clink/native_default.txt +1 -0
- systemprompts/clink/native_planner.txt +1 -0
- systemprompts/codereview_prompt.py +91 -0
- systemprompts/consensus_prompt.py +120 -0
- systemprompts/debug_prompt.py +150 -0
- systemprompts/docgen_prompt.py +252 -0
- systemprompts/generate_code_prompt.py +181 -0
- systemprompts/planner_prompt.py +127 -0
- systemprompts/precommit_prompt.py +179 -0
- systemprompts/refactor_prompt.py +323 -0
- systemprompts/secaudit_prompt.py +420 -0
- systemprompts/testgen_prompt.py +133 -0
- systemprompts/thinkdeep_prompt.py +55 -0
- systemprompts/tracer_prompt.py +145 -0
- tools/__init__.py +43 -0
- tools/analyze.py +583 -0
- tools/apilookup.py +97 -0
- tools/challenge.py +198 -0
- tools/chat.py +187 -0
- tools/clink.py +130 -0
- tools/codereview.py +724 -0
- tools/consensus.py +790 -0
- tools/debug.py +573 -0
- tools/docgen.py +577 -0
- tools/listmodels.py +48 -0
- tools/models.py +373 -0
- tools/planner.py +533 -0
- tools/precommit.py +776 -0
- tools/refactor.py +645 -0
- tools/secaudit.py +685 -0
- tools/shared/__init__.py +19 -0
- tools/shared/base_models.py +167 -0
- tools/shared/base_tool.py +659 -0
- tools/shared/exceptions.py +20 -0
- tools/shared/schema_builders.py +159 -0
- tools/simple/__init__.py +18 -0
- tools/simple/base.py +539 -0
- tools/testgen.py +513 -0
- tools/thinkdeep.py +586 -0
- tools/tracer.py +763 -0
- tools/version.py +53 -0
- tools/workflow/__init__.py +22 -0
- tools/workflow/base.py +448 -0
- tools/workflow/schema_builders.py +166 -0
- tools/workflow/workflow_mixin.py +1195 -0
- utils/__init__.py +21 -0
- utils/call_state.py +349 -0
- utils/client_info.py +293 -0
- utils/consensus_manifest.py +131 -0
- utils/conversation_memory.py +582 -0
- utils/env.py +111 -0
- utils/file_types.py +271 -0
- utils/file_utils.py +803 -0
- utils/image_utils.py +94 -0
- utils/security_config.py +163 -0
- utils/storage_backend.py +177 -0
- utils/token_utils.py +54 -0
tools/analyze.py
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AnalyzeWorkflow tool - Step-by-step code analysis with systematic investigation
|
|
3
|
+
|
|
4
|
+
This tool provides a structured workflow for comprehensive code and file analysis.
|
|
5
|
+
It guides the CLI agent through systematic investigation steps with forced pauses between each step
|
|
6
|
+
to ensure thorough code examination, pattern identification, and architectural assessment before proceeding.
|
|
7
|
+
The tool supports complex analysis scenarios including architectural review, performance analysis,
|
|
8
|
+
security assessment, and maintainability evaluation.
|
|
9
|
+
|
|
10
|
+
Key features:
|
|
11
|
+
- Step-by-step analysis workflow with progress tracking
|
|
12
|
+
- Context-aware file embedding (references during investigation, full content for analysis)
|
|
13
|
+
- Automatic pattern and insight tracking with categorization
|
|
14
|
+
- Expert analysis integration with external models
|
|
15
|
+
- Support for focused analysis (architecture, performance, security, quality)
|
|
16
|
+
- Confidence-based workflow optimization
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional
|
|
21
|
+
|
|
22
|
+
from pydantic import Field, model_validator
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from tools.models import ToolModelCategory
|
|
26
|
+
|
|
27
|
+
from config import TEMPERATURE_ANALYTICAL
|
|
28
|
+
from systemprompts import ANALYZE_PROMPT
|
|
29
|
+
from tools.shared.base_models import WorkflowRequest
|
|
30
|
+
|
|
31
|
+
from .workflow.base import WorkflowTool
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
# Tool-specific field descriptions for analyze workflow
|
|
36
|
+
ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS = {
|
|
37
|
+
"step": (
|
|
38
|
+
"The analysis plan. Step 1: State your strategy, including how you will map the codebase structure, "
|
|
39
|
+
"understand business logic, and assess code quality, performance implications, and architectural patterns. "
|
|
40
|
+
"Later steps: Report findings and adapt the approach as new insights emerge."
|
|
41
|
+
),
|
|
42
|
+
"step_number": (
|
|
43
|
+
"The index of the current step in the analysis sequence, beginning at 1. Each step should build upon or "
|
|
44
|
+
"revise the previous one."
|
|
45
|
+
),
|
|
46
|
+
"total_steps": (
|
|
47
|
+
"Your current estimate for how many steps will be needed to complete the analysis. "
|
|
48
|
+
"Adjust as new findings emerge."
|
|
49
|
+
),
|
|
50
|
+
"next_step_required": (
|
|
51
|
+
"Set to true if you plan to continue the investigation with another step. False means you believe the "
|
|
52
|
+
"analysis is complete and ready for expert validation."
|
|
53
|
+
),
|
|
54
|
+
"findings": (
|
|
55
|
+
"Summary of discoveries from this step, including architectural patterns, tech stack assessment, scalability characteristics, "
|
|
56
|
+
"performance implications, maintainability factors, and strategic improvement opportunities. "
|
|
57
|
+
"IMPORTANT: Document both strengths (good patterns, solid architecture) and concerns (tech debt, overengineering, unnecessary complexity). "
|
|
58
|
+
"In later steps, confirm or update past findings with additional evidence."
|
|
59
|
+
),
|
|
60
|
+
"files_checked": (
|
|
61
|
+
"List all files examined (absolute paths). Include even ruled-out files to track exploration path."
|
|
62
|
+
),
|
|
63
|
+
"relevant_files": (
|
|
64
|
+
"Subset of files_checked directly relevant to analysis findings (absolute paths). Include files with "
|
|
65
|
+
"significant patterns, architectural decisions, or strategic improvement opportunities."
|
|
66
|
+
),
|
|
67
|
+
"relevant_context": (
|
|
68
|
+
"List methods/functions central to analysis findings, in 'ClassName.methodName' or 'functionName' format. "
|
|
69
|
+
"Prioritize those demonstrating key patterns, architectural decisions, or improvement opportunities."
|
|
70
|
+
),
|
|
71
|
+
"images": (
|
|
72
|
+
"Optional absolute paths to architecture diagrams or visual references that help with analysis context."
|
|
73
|
+
),
|
|
74
|
+
"confidence": (
|
|
75
|
+
"Your confidence in the analysis: exploring, low, medium, high, very_high, almost_certain, or certain. "
|
|
76
|
+
"'certain' indicates the analysis is complete and ready for validation."
|
|
77
|
+
),
|
|
78
|
+
"analysis_type": "Type of analysis to perform (architecture, performance, security, quality, general)",
|
|
79
|
+
"output_format": "How to format the output (summary, detailed, actionable)",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class AnalyzeWorkflowRequest(WorkflowRequest):
|
|
84
|
+
"""Request model for analyze workflow investigation steps"""
|
|
85
|
+
|
|
86
|
+
# Required fields for each investigation step
|
|
87
|
+
step: str = Field(..., description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["step"])
|
|
88
|
+
step_number: int = Field(..., description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["step_number"])
|
|
89
|
+
total_steps: int = Field(..., description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["total_steps"])
|
|
90
|
+
next_step_required: bool = Field(..., description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["next_step_required"])
|
|
91
|
+
|
|
92
|
+
# Investigation tracking fields
|
|
93
|
+
findings: str = Field(..., description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["findings"])
|
|
94
|
+
files_checked: list[str] = Field(
|
|
95
|
+
default_factory=list, description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["files_checked"]
|
|
96
|
+
)
|
|
97
|
+
relevant_files: list[str] = Field(
|
|
98
|
+
default_factory=list, description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["relevant_files"]
|
|
99
|
+
)
|
|
100
|
+
relevant_context: list[str] = Field(
|
|
101
|
+
default_factory=list, description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["relevant_context"]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Issues found during analysis (structured with severity)
|
|
105
|
+
issues_found: list[dict] = Field(
|
|
106
|
+
default_factory=list,
|
|
107
|
+
description="Issues or concerns identified during analysis, each with severity level (critical, high, medium, low)",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Optional images for visual context
|
|
111
|
+
images: Optional[list[str]] = Field(default=None, description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["images"])
|
|
112
|
+
|
|
113
|
+
# Analyze-specific fields (only used in step 1 to initialize)
|
|
114
|
+
# Note: Use relevant_files field instead of files for consistency across workflow tools
|
|
115
|
+
analysis_type: Optional[Literal["architecture", "performance", "security", "quality", "general"]] = Field(
|
|
116
|
+
"general", description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["analysis_type"]
|
|
117
|
+
)
|
|
118
|
+
output_format: Optional[Literal["summary", "detailed", "actionable"]] = Field(
|
|
119
|
+
"detailed", description=ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["output_format"]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Keep thinking_mode from original analyze tool; temperature is inherited from WorkflowRequest
|
|
123
|
+
|
|
124
|
+
@model_validator(mode="after")
|
|
125
|
+
def validate_step_one_requirements(self):
|
|
126
|
+
"""Ensure step 1 has required relevant_files."""
|
|
127
|
+
if self.step_number == 1:
|
|
128
|
+
if not self.relevant_files:
|
|
129
|
+
raise ValueError("Step 1 requires 'relevant_files' field to specify files or directories to analyze")
|
|
130
|
+
return self
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class AnalyzeTool(WorkflowTool):
|
|
134
|
+
"""
|
|
135
|
+
Analyze workflow tool for step-by-step code analysis and expert validation.
|
|
136
|
+
|
|
137
|
+
This tool implements a structured analysis workflow that guides users through
|
|
138
|
+
methodical investigation steps, ensuring thorough code examination, pattern identification,
|
|
139
|
+
and architectural assessment before reaching conclusions. It supports complex analysis scenarios
|
|
140
|
+
including architectural review, performance analysis, security assessment, and maintainability evaluation.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
def __init__(self):
|
|
144
|
+
super().__init__()
|
|
145
|
+
self.initial_request = None
|
|
146
|
+
self.analysis_config = {}
|
|
147
|
+
|
|
148
|
+
def get_name(self) -> str:
|
|
149
|
+
return "analyze"
|
|
150
|
+
|
|
151
|
+
def get_description(self) -> str:
|
|
152
|
+
return (
|
|
153
|
+
"Performs comprehensive code analysis with systematic investigation and expert validation. "
|
|
154
|
+
"Use for architecture, performance, maintainability, and pattern analysis. "
|
|
155
|
+
"Guides through structured code review and strategic planning."
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def get_system_prompt(self) -> str:
|
|
159
|
+
return ANALYZE_PROMPT
|
|
160
|
+
|
|
161
|
+
def get_default_temperature(self) -> float:
|
|
162
|
+
return TEMPERATURE_ANALYTICAL
|
|
163
|
+
|
|
164
|
+
def get_model_category(self) -> "ToolModelCategory":
|
|
165
|
+
"""Analyze workflow requires thorough analysis and reasoning"""
|
|
166
|
+
from tools.models import ToolModelCategory
|
|
167
|
+
|
|
168
|
+
return ToolModelCategory.EXTENDED_REASONING
|
|
169
|
+
|
|
170
|
+
def get_workflow_request_model(self):
|
|
171
|
+
"""Return the analyze workflow-specific request model."""
|
|
172
|
+
return AnalyzeWorkflowRequest
|
|
173
|
+
|
|
174
|
+
def get_input_schema(self) -> dict[str, Any]:
|
|
175
|
+
"""Generate input schema using WorkflowSchemaBuilder with analyze-specific overrides."""
|
|
176
|
+
from .workflow.schema_builders import WorkflowSchemaBuilder
|
|
177
|
+
|
|
178
|
+
# Fields to exclude from analyze workflow (inherited from WorkflowRequest but not used)
|
|
179
|
+
excluded_fields = {"hypothesis", "confidence"}
|
|
180
|
+
|
|
181
|
+
# Analyze workflow-specific field overrides
|
|
182
|
+
analyze_field_overrides = {
|
|
183
|
+
"step": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["step"],
|
|
186
|
+
},
|
|
187
|
+
"step_number": {
|
|
188
|
+
"type": "integer",
|
|
189
|
+
"minimum": 1,
|
|
190
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["step_number"],
|
|
191
|
+
},
|
|
192
|
+
"total_steps": {
|
|
193
|
+
"type": "integer",
|
|
194
|
+
"minimum": 1,
|
|
195
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["total_steps"],
|
|
196
|
+
},
|
|
197
|
+
"next_step_required": {
|
|
198
|
+
"type": "boolean",
|
|
199
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["next_step_required"],
|
|
200
|
+
},
|
|
201
|
+
"findings": {
|
|
202
|
+
"type": "string",
|
|
203
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["findings"],
|
|
204
|
+
},
|
|
205
|
+
"files_checked": {
|
|
206
|
+
"type": "array",
|
|
207
|
+
"items": {"type": "string"},
|
|
208
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["files_checked"],
|
|
209
|
+
},
|
|
210
|
+
"relevant_files": {
|
|
211
|
+
"type": "array",
|
|
212
|
+
"items": {"type": "string"},
|
|
213
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["relevant_files"],
|
|
214
|
+
},
|
|
215
|
+
"confidence": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"enum": ["exploring", "low", "medium", "high", "very_high", "almost_certain", "certain"],
|
|
218
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["confidence"],
|
|
219
|
+
},
|
|
220
|
+
"images": {
|
|
221
|
+
"type": "array",
|
|
222
|
+
"items": {"type": "string"},
|
|
223
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["images"],
|
|
224
|
+
},
|
|
225
|
+
"issues_found": {
|
|
226
|
+
"type": "array",
|
|
227
|
+
"items": {"type": "object"},
|
|
228
|
+
"description": "Issues or concerns identified during analysis, each with severity level (critical, high, medium, low)",
|
|
229
|
+
},
|
|
230
|
+
"analysis_type": {
|
|
231
|
+
"type": "string",
|
|
232
|
+
"enum": ["architecture", "performance", "security", "quality", "general"],
|
|
233
|
+
"default": "general",
|
|
234
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["analysis_type"],
|
|
235
|
+
},
|
|
236
|
+
"output_format": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"enum": ["summary", "detailed", "actionable"],
|
|
239
|
+
"default": "detailed",
|
|
240
|
+
"description": ANALYZE_WORKFLOW_FIELD_DESCRIPTIONS["output_format"],
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# Use WorkflowSchemaBuilder with analyze-specific tool fields
|
|
245
|
+
return WorkflowSchemaBuilder.build_schema(
|
|
246
|
+
tool_specific_fields=analyze_field_overrides,
|
|
247
|
+
model_field_schema=self.get_model_field_schema(),
|
|
248
|
+
auto_mode=self.is_effective_auto_mode(),
|
|
249
|
+
tool_name=self.get_name(),
|
|
250
|
+
excluded_workflow_fields=list(excluded_fields),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def get_required_actions(
|
|
254
|
+
self, step_number: int, confidence: str, findings: str, total_steps: int, request=None
|
|
255
|
+
) -> list[str]:
|
|
256
|
+
"""Define required actions for each investigation phase."""
|
|
257
|
+
if step_number == 1:
|
|
258
|
+
# Initial analysis investigation tasks
|
|
259
|
+
return [
|
|
260
|
+
"Read and understand the code files specified for analysis",
|
|
261
|
+
"Map the tech stack, frameworks, and overall architecture",
|
|
262
|
+
"Identify the main components, modules, and their relationships",
|
|
263
|
+
"Understand the business logic and intended functionality",
|
|
264
|
+
"Examine architectural patterns and design decisions used",
|
|
265
|
+
"Look for strengths, risks, and strategic improvement areas",
|
|
266
|
+
]
|
|
267
|
+
elif step_number < total_steps:
|
|
268
|
+
# Need deeper investigation
|
|
269
|
+
return [
|
|
270
|
+
"Examine specific architectural patterns and design decisions in detail",
|
|
271
|
+
"Analyze scalability characteristics and performance implications",
|
|
272
|
+
"Assess maintainability factors: module cohesion, coupling, tech debt",
|
|
273
|
+
"Identify security posture and potential systemic vulnerabilities",
|
|
274
|
+
"Look for overengineering, unnecessary complexity, or missing abstractions",
|
|
275
|
+
"Evaluate how well the architecture serves business and scaling goals",
|
|
276
|
+
]
|
|
277
|
+
else:
|
|
278
|
+
# Close to completion - need final verification
|
|
279
|
+
return [
|
|
280
|
+
"Verify all significant architectural insights have been documented",
|
|
281
|
+
"Confirm strategic improvement opportunities are comprehensively captured",
|
|
282
|
+
"Ensure both strengths and risks are properly identified with evidence",
|
|
283
|
+
"Validate that findings align with the analysis type and goals specified",
|
|
284
|
+
"Check that recommendations are actionable and proportional to the codebase",
|
|
285
|
+
"Confirm the analysis provides clear guidance for strategic decisions",
|
|
286
|
+
]
|
|
287
|
+
|
|
288
|
+
def should_call_expert_analysis(self, consolidated_findings, request=None) -> bool:
|
|
289
|
+
"""
|
|
290
|
+
Always call expert analysis for comprehensive validation.
|
|
291
|
+
|
|
292
|
+
Analysis benefits from a second opinion to ensure completeness.
|
|
293
|
+
"""
|
|
294
|
+
# Check if user explicitly requested to skip assistant model
|
|
295
|
+
if request and not self.get_request_use_assistant_model(request):
|
|
296
|
+
return False
|
|
297
|
+
|
|
298
|
+
# For analysis, we always want expert validation if we have any meaningful data
|
|
299
|
+
return len(consolidated_findings.relevant_files) > 0 or len(consolidated_findings.findings) >= 1
|
|
300
|
+
|
|
301
|
+
def prepare_expert_analysis_context(self, consolidated_findings) -> str:
|
|
302
|
+
"""Prepare context for external model call for final analysis validation."""
|
|
303
|
+
context_parts = [
|
|
304
|
+
f"=== ANALYSIS REQUEST ===\\n{self.get_expert_initial_request(self.initial_request, 'Code analysis workflow initiated')}\\n=== END REQUEST ==="
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
# Add investigation summary
|
|
308
|
+
investigation_summary = self._build_analysis_summary(consolidated_findings)
|
|
309
|
+
context_parts.append(
|
|
310
|
+
f"\\n=== AGENT'S ANALYSIS INVESTIGATION ===\\n{investigation_summary}\\n=== END INVESTIGATION ==="
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
# Add analysis configuration context if available
|
|
314
|
+
if self.analysis_config:
|
|
315
|
+
config_text = "\\n".join(f"- {key}: {value}" for key, value in self.analysis_config.items() if value)
|
|
316
|
+
context_parts.append(f"\\n=== ANALYSIS CONFIGURATION ===\\n{config_text}\\n=== END CONFIGURATION ===")
|
|
317
|
+
|
|
318
|
+
# Add relevant code elements if available
|
|
319
|
+
if consolidated_findings.relevant_context:
|
|
320
|
+
methods_text = "\\n".join(f"- {method}" for method in consolidated_findings.relevant_context)
|
|
321
|
+
context_parts.append(f"\\n=== RELEVANT CODE ELEMENTS ===\\n{methods_text}\\n=== END CODE ELEMENTS ===")
|
|
322
|
+
|
|
323
|
+
# Add assessment evolution if available
|
|
324
|
+
if consolidated_findings.hypotheses:
|
|
325
|
+
assessments_text = "\\n".join(
|
|
326
|
+
f"Step {h['step']}: {h['hypothesis']}" for h in consolidated_findings.hypotheses
|
|
327
|
+
)
|
|
328
|
+
context_parts.append(f"\\n=== ASSESSMENT EVOLUTION ===\\n{assessments_text}\\n=== END ASSESSMENTS ===")
|
|
329
|
+
|
|
330
|
+
# Add images if available
|
|
331
|
+
if consolidated_findings.images:
|
|
332
|
+
images_text = "\\n".join(f"- {img}" for img in consolidated_findings.images)
|
|
333
|
+
context_parts.append(
|
|
334
|
+
f"\\n=== VISUAL ANALYSIS INFORMATION ===\\n{images_text}\\n=== END VISUAL INFORMATION ==="
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
return "\\n".join(context_parts)
|
|
338
|
+
|
|
339
|
+
def _build_analysis_summary(self, consolidated_findings) -> str:
|
|
340
|
+
"""Prepare a comprehensive summary of the analysis investigation."""
|
|
341
|
+
summary_parts = [
|
|
342
|
+
"=== SYSTEMATIC ANALYSIS INVESTIGATION SUMMARY ===",
|
|
343
|
+
f"Total steps: {len(consolidated_findings.findings)}",
|
|
344
|
+
f"Files examined: {len(consolidated_findings.files_checked)}",
|
|
345
|
+
f"Relevant files identified: {len(consolidated_findings.relevant_files)}",
|
|
346
|
+
f"Code elements analyzed: {len(consolidated_findings.relevant_context)}",
|
|
347
|
+
"",
|
|
348
|
+
"=== INVESTIGATION PROGRESSION ===",
|
|
349
|
+
]
|
|
350
|
+
|
|
351
|
+
for finding in consolidated_findings.findings:
|
|
352
|
+
summary_parts.append(finding)
|
|
353
|
+
|
|
354
|
+
return "\\n".join(summary_parts)
|
|
355
|
+
|
|
356
|
+
def should_include_files_in_expert_prompt(self) -> bool:
|
|
357
|
+
"""Include files in expert analysis for comprehensive validation."""
|
|
358
|
+
return True
|
|
359
|
+
|
|
360
|
+
def should_embed_system_prompt(self) -> bool:
|
|
361
|
+
"""Embed system prompt in expert analysis for proper context."""
|
|
362
|
+
return True
|
|
363
|
+
|
|
364
|
+
def get_expert_thinking_mode(self) -> str:
|
|
365
|
+
"""Use high thinking mode for thorough analysis."""
|
|
366
|
+
return "high"
|
|
367
|
+
|
|
368
|
+
def get_expert_analysis_instruction(self) -> str:
|
|
369
|
+
"""Get specific instruction for analysis expert validation."""
|
|
370
|
+
return (
|
|
371
|
+
"Please provide comprehensive analysis validation based on the investigation findings. "
|
|
372
|
+
"Focus on identifying any remaining architectural insights, validating the completeness of the analysis, "
|
|
373
|
+
"and providing final strategic recommendations following the structured format specified in the system prompt."
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
# Hook method overrides for analyze-specific behavior
|
|
377
|
+
|
|
378
|
+
def prepare_step_data(self, request) -> dict:
|
|
379
|
+
"""
|
|
380
|
+
Map analyze-specific fields for internal processing.
|
|
381
|
+
"""
|
|
382
|
+
step_data = {
|
|
383
|
+
"step": request.step,
|
|
384
|
+
"step_number": request.step_number,
|
|
385
|
+
"findings": request.findings,
|
|
386
|
+
"files_checked": request.files_checked,
|
|
387
|
+
"relevant_files": request.relevant_files,
|
|
388
|
+
"relevant_context": request.relevant_context,
|
|
389
|
+
"issues_found": request.issues_found, # Analyze workflow uses issues_found for structured problem tracking
|
|
390
|
+
"confidence": "medium", # Fixed value for workflow compatibility
|
|
391
|
+
"hypothesis": request.findings, # Map findings to hypothesis for compatibility
|
|
392
|
+
"images": request.images or [],
|
|
393
|
+
}
|
|
394
|
+
return step_data
|
|
395
|
+
|
|
396
|
+
def should_skip_expert_analysis(self, request, consolidated_findings) -> bool:
|
|
397
|
+
"""
|
|
398
|
+
Analyze workflow always uses expert analysis for comprehensive validation.
|
|
399
|
+
|
|
400
|
+
Analysis benefits from a second opinion to ensure completeness and catch
|
|
401
|
+
any missed insights or alternative perspectives.
|
|
402
|
+
"""
|
|
403
|
+
return False
|
|
404
|
+
|
|
405
|
+
def store_initial_issue(self, step_description: str):
|
|
406
|
+
"""Store initial request for expert analysis."""
|
|
407
|
+
self.initial_request = step_description
|
|
408
|
+
|
|
409
|
+
# Override inheritance hooks for analyze-specific behavior
|
|
410
|
+
|
|
411
|
+
def get_completion_status(self) -> str:
|
|
412
|
+
"""Analyze tools use analysis-specific status."""
|
|
413
|
+
return "analysis_complete_ready_for_implementation"
|
|
414
|
+
|
|
415
|
+
def get_completion_data_key(self) -> str:
|
|
416
|
+
"""Analyze uses 'complete_analysis' key."""
|
|
417
|
+
return "complete_analysis"
|
|
418
|
+
|
|
419
|
+
def get_final_analysis_from_request(self, request):
|
|
420
|
+
"""Analyze tools use 'findings' field."""
|
|
421
|
+
return request.findings
|
|
422
|
+
|
|
423
|
+
def get_confidence_level(self, request) -> str:
|
|
424
|
+
"""Analyze tools use fixed confidence for consistency."""
|
|
425
|
+
return "medium"
|
|
426
|
+
|
|
427
|
+
def get_completion_message(self) -> str:
|
|
428
|
+
"""Analyze-specific completion message."""
|
|
429
|
+
return (
|
|
430
|
+
"Analysis complete. You have identified all significant patterns, "
|
|
431
|
+
"architectural insights, and strategic opportunities. MANDATORY: Present the user with the complete "
|
|
432
|
+
"analysis results organized by strategic impact, and IMMEDIATELY proceed with implementing the "
|
|
433
|
+
"highest priority recommendations or provide specific guidance for improvements. Focus on actionable "
|
|
434
|
+
"strategic insights."
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
def get_skip_reason(self) -> str:
|
|
438
|
+
"""Analyze-specific skip reason."""
|
|
439
|
+
return "Completed comprehensive analysis locally"
|
|
440
|
+
|
|
441
|
+
def get_skip_expert_analysis_status(self) -> str:
|
|
442
|
+
"""Analyze-specific expert analysis skip status."""
|
|
443
|
+
return "skipped_due_to_complete_analysis"
|
|
444
|
+
|
|
445
|
+
def prepare_work_summary(self) -> str:
|
|
446
|
+
"""Analyze-specific work summary."""
|
|
447
|
+
return self._build_analysis_summary(self.consolidated_findings)
|
|
448
|
+
|
|
449
|
+
def get_completion_next_steps_message(self, expert_analysis_used: bool = False) -> str:
|
|
450
|
+
"""
|
|
451
|
+
Analyze-specific completion message.
|
|
452
|
+
"""
|
|
453
|
+
base_message = (
|
|
454
|
+
"ANALYSIS IS COMPLETE. You MUST now summarize and present ALL analysis findings organized by "
|
|
455
|
+
"strategic impact (Critical → High → Medium → Low), specific architectural insights with code references, "
|
|
456
|
+
"and exact recommendations for improvement. Clearly prioritize the top 3 strategic opportunities that need "
|
|
457
|
+
"immediate attention. Provide concrete, actionable guidance for each finding—make it easy for a developer "
|
|
458
|
+
"to understand exactly what strategic improvements to implement and how to approach them."
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
# Add expert analysis guidance only when expert analysis was actually used
|
|
462
|
+
if expert_analysis_used:
|
|
463
|
+
expert_guidance = self.get_expert_analysis_guidance()
|
|
464
|
+
if expert_guidance:
|
|
465
|
+
return f"{base_message}\n\n{expert_guidance}"
|
|
466
|
+
|
|
467
|
+
return base_message
|
|
468
|
+
|
|
469
|
+
def get_expert_analysis_guidance(self) -> str:
|
|
470
|
+
"""
|
|
471
|
+
Provide specific guidance for handling expert analysis in code analysis.
|
|
472
|
+
"""
|
|
473
|
+
return (
|
|
474
|
+
"IMPORTANT: Analysis from an assistant model has been provided above. You MUST thoughtfully evaluate and validate "
|
|
475
|
+
"the expert insights rather than treating them as definitive conclusions. Cross-reference the expert "
|
|
476
|
+
"analysis with your own systematic investigation, verify that architectural recommendations are "
|
|
477
|
+
"appropriate for this codebase's scale and context, and ensure suggested improvements align with "
|
|
478
|
+
"the project's goals and constraints. Present a comprehensive synthesis that combines your detailed "
|
|
479
|
+
"analysis with validated expert perspectives, clearly distinguishing between patterns you've "
|
|
480
|
+
"independently identified and additional strategic insights from expert validation."
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
def get_step_guidance_message(self, request) -> str:
|
|
484
|
+
"""
|
|
485
|
+
Analyze-specific step guidance with detailed investigation instructions.
|
|
486
|
+
"""
|
|
487
|
+
step_guidance = self.get_analyze_step_guidance(request.step_number, request)
|
|
488
|
+
return step_guidance["next_steps"]
|
|
489
|
+
|
|
490
|
+
def get_analyze_step_guidance(self, step_number: int, request) -> dict[str, Any]:
|
|
491
|
+
"""
|
|
492
|
+
Provide step-specific guidance for analyze workflow.
|
|
493
|
+
"""
|
|
494
|
+
# Generate the next steps instruction based on required actions
|
|
495
|
+
required_actions = self.get_required_actions(step_number, "medium", request.findings, request.total_steps)
|
|
496
|
+
|
|
497
|
+
if step_number == 1:
|
|
498
|
+
next_steps = (
|
|
499
|
+
f"MANDATORY: DO NOT call the {self.get_name()} tool again immediately. You MUST first examine "
|
|
500
|
+
f"the code files thoroughly using appropriate tools. CRITICAL AWARENESS: You need to understand "
|
|
501
|
+
f"the architectural patterns, assess scalability and performance characteristics, identify strategic "
|
|
502
|
+
f"improvement areas, and look for systemic risks, overengineering, and missing abstractions. "
|
|
503
|
+
f"Use file reading tools, code analysis, and systematic examination to gather comprehensive information. "
|
|
504
|
+
f"Only call {self.get_name()} again AFTER completing your investigation. When you call "
|
|
505
|
+
f"{self.get_name()} next time, use step_number: {step_number + 1} and report specific "
|
|
506
|
+
f"files examined, architectural insights found, and strategic assessment discoveries."
|
|
507
|
+
)
|
|
508
|
+
elif step_number < request.total_steps:
|
|
509
|
+
next_steps = (
|
|
510
|
+
f"STOP! Do NOT call {self.get_name()} again yet. Based on your findings, you've identified areas that need "
|
|
511
|
+
f"deeper analysis. MANDATORY ACTIONS before calling {self.get_name()} step {step_number + 1}:\\n"
|
|
512
|
+
+ "\\n".join(f"{i+1}. {action}" for i, action in enumerate(required_actions))
|
|
513
|
+
+ f"\\n\\nOnly call {self.get_name()} again with step_number: {step_number + 1} AFTER "
|
|
514
|
+
+ "completing these analysis tasks."
|
|
515
|
+
)
|
|
516
|
+
else:
|
|
517
|
+
next_steps = (
|
|
518
|
+
f"WAIT! Your analysis needs final verification. DO NOT call {self.get_name()} immediately. REQUIRED ACTIONS:\\n"
|
|
519
|
+
+ "\\n".join(f"{i+1}. {action}" for i, action in enumerate(required_actions))
|
|
520
|
+
+ f"\\n\\nREMEMBER: Ensure you have identified all significant architectural insights and strategic "
|
|
521
|
+
f"opportunities across all areas. Document findings with specific file references and "
|
|
522
|
+
f"code examples where applicable, then call {self.get_name()} with step_number: {step_number + 1}."
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
return {"next_steps": next_steps}
|
|
526
|
+
|
|
527
|
+
def customize_workflow_response(self, response_data: dict, request) -> dict:
|
|
528
|
+
"""
|
|
529
|
+
Customize response to match analyze workflow format.
|
|
530
|
+
"""
|
|
531
|
+
# Store initial request on first step
|
|
532
|
+
if request.step_number == 1:
|
|
533
|
+
self.initial_request = request.step
|
|
534
|
+
# Store analysis configuration for expert analysis
|
|
535
|
+
if request.relevant_files:
|
|
536
|
+
self.analysis_config = {
|
|
537
|
+
"relevant_files": request.relevant_files,
|
|
538
|
+
"analysis_type": request.analysis_type,
|
|
539
|
+
"output_format": request.output_format,
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
# Convert generic status names to analyze-specific ones
|
|
543
|
+
tool_name = self.get_name()
|
|
544
|
+
status_mapping = {
|
|
545
|
+
f"{tool_name}_in_progress": "analysis_in_progress",
|
|
546
|
+
f"pause_for_{tool_name}": "pause_for_analysis",
|
|
547
|
+
f"{tool_name}_required": "analysis_required",
|
|
548
|
+
f"{tool_name}_complete": "analysis_complete",
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if response_data["status"] in status_mapping:
|
|
552
|
+
response_data["status"] = status_mapping[response_data["status"]]
|
|
553
|
+
|
|
554
|
+
# Rename status field to match analyze workflow
|
|
555
|
+
if f"{tool_name}_status" in response_data:
|
|
556
|
+
response_data["analysis_status"] = response_data.pop(f"{tool_name}_status")
|
|
557
|
+
# Add analyze-specific status fields
|
|
558
|
+
response_data["analysis_status"]["insights_by_severity"] = {}
|
|
559
|
+
for insight in self.consolidated_findings.issues_found:
|
|
560
|
+
severity = insight.get("severity", "unknown")
|
|
561
|
+
if severity not in response_data["analysis_status"]["insights_by_severity"]:
|
|
562
|
+
response_data["analysis_status"]["insights_by_severity"][severity] = 0
|
|
563
|
+
response_data["analysis_status"]["insights_by_severity"][severity] += 1
|
|
564
|
+
response_data["analysis_status"]["analysis_confidence"] = self.get_request_confidence(request)
|
|
565
|
+
|
|
566
|
+
# Map complete_analyze to complete_analysis
|
|
567
|
+
if f"complete_{tool_name}" in response_data:
|
|
568
|
+
response_data["complete_analysis"] = response_data.pop(f"complete_{tool_name}")
|
|
569
|
+
|
|
570
|
+
# Map the completion flag to match analyze workflow
|
|
571
|
+
if f"{tool_name}_complete" in response_data:
|
|
572
|
+
response_data["analysis_complete"] = response_data.pop(f"{tool_name}_complete")
|
|
573
|
+
|
|
574
|
+
return response_data
|
|
575
|
+
|
|
576
|
+
# Required abstract methods from BaseTool
|
|
577
|
+
def get_request_model(self):
|
|
578
|
+
"""Return the analyze workflow-specific request model."""
|
|
579
|
+
return AnalyzeWorkflowRequest
|
|
580
|
+
|
|
581
|
+
async def prepare_prompt(self, request) -> str:
|
|
582
|
+
"""Not used - workflow tools use execute_workflow()."""
|
|
583
|
+
return "" # Workflow tools use execute_workflow() directly
|
tools/apilookup.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""API documentation research through verified native CLI search."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from mcp.types import TextContent
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from clink.backend import get_backend
|
|
10
|
+
from clink.contracts import CLIRequest
|
|
11
|
+
from tools.shared.base_models import ToolRequest
|
|
12
|
+
from tools.simple.base import SimpleTool
|
|
13
|
+
from utils.call_state import get_current_call
|
|
14
|
+
|
|
15
|
+
LOOKUP_PROMPT = """Research the user's API or documentation question using native web search.
|
|
16
|
+
Use current primary documentation and release notes. Verify version-sensitive claims.
|
|
17
|
+
Return a concise answer with source URLs supporting the claims. Search must actually
|
|
18
|
+
run during this consultation; do not substitute remembered URLs for evidence.
|
|
19
|
+
Do not delegate, call MCP tools, or modify files. Role guidance is subordinate to
|
|
20
|
+
these instructions and the caller's question. If search yields nothing, say so.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LookupRequest(ToolRequest):
|
|
25
|
+
prompt: str = Field(
|
|
26
|
+
..., min_length=1, description="API, SDK or framework question to research in current official documentation."
|
|
27
|
+
)
|
|
28
|
+
absolute_file_paths: list[str] = Field(default_factory=list)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LookupTool(SimpleTool):
|
|
32
|
+
def get_name(self) -> str:
|
|
33
|
+
return "apilookup"
|
|
34
|
+
|
|
35
|
+
def get_description(self) -> str:
|
|
36
|
+
return "Research current API/SDK documentation with the selected CLI's verified native web search and return sources."
|
|
37
|
+
|
|
38
|
+
def get_system_prompt(self) -> str:
|
|
39
|
+
return LOOKUP_PROMPT
|
|
40
|
+
|
|
41
|
+
def get_request_model(self):
|
|
42
|
+
return LookupRequest
|
|
43
|
+
|
|
44
|
+
def get_tool_fields(self) -> dict[str, dict[str, Any]]:
|
|
45
|
+
return {
|
|
46
|
+
"prompt": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"minLength": 1,
|
|
49
|
+
"description": "Question to research in official documentation.",
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
def get_required_fields(self) -> list[str]:
|
|
54
|
+
return ["prompt"]
|
|
55
|
+
|
|
56
|
+
async def prepare_prompt(self, request) -> str:
|
|
57
|
+
return request.prompt
|
|
58
|
+
|
|
59
|
+
async def execute(self, arguments: dict[str, Any]) -> list[TextContent]:
|
|
60
|
+
request = LookupRequest(**arguments)
|
|
61
|
+
backend = get_backend()
|
|
62
|
+
target = backend.resolve(
|
|
63
|
+
cli_name=request.cli_name,
|
|
64
|
+
role=request.role,
|
|
65
|
+
model=request.model,
|
|
66
|
+
temperature=request.temperature,
|
|
67
|
+
thinking_mode=request.thinking_mode,
|
|
68
|
+
images=tuple(request.images or []),
|
|
69
|
+
)
|
|
70
|
+
files = backend.validate_files(target, request.absolute_file_paths, request.images or [], tool_name="apilookup")
|
|
71
|
+
call = get_current_call()
|
|
72
|
+
options = {}
|
|
73
|
+
if call:
|
|
74
|
+
options = {"request_id": call.request_id, "admitted_at": call.admitted_at, "deadline": call.deadline}
|
|
75
|
+
result = await backend.run(
|
|
76
|
+
CLIRequest(
|
|
77
|
+
prompt=request.prompt,
|
|
78
|
+
target=target,
|
|
79
|
+
system_prompt=LOOKUP_PROMPT,
|
|
80
|
+
tool_name="apilookup",
|
|
81
|
+
require_search=True,
|
|
82
|
+
files=tuple(files),
|
|
83
|
+
images=tuple(request.images or []),
|
|
84
|
+
history_turns=tuple(arguments.get("_replay_history_turns", ())),
|
|
85
|
+
history_omitted_turns=arguments.get("_replay_omitted_turns", 0),
|
|
86
|
+
**options,
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
payload = {
|
|
90
|
+
"status": result.status,
|
|
91
|
+
"content": result.content,
|
|
92
|
+
"sources": result.metadata.get("source_urls", []),
|
|
93
|
+
"metadata": result.to_metadata(),
|
|
94
|
+
}
|
|
95
|
+
if call:
|
|
96
|
+
payload["continuation_id"] = call.continuation_id
|
|
97
|
+
return [TextContent(type="text", text=json.dumps(payload, ensure_ascii=False))]
|