@makeitvisible/cli 0.1.0 → 0.2.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/index.d.ts CHANGED
@@ -1,66 +1,7 @@
1
+ import { A as ArtifactSource, a as ArtifactContext } from './types-BmNeVNFe.js';
2
+ export { b as Artifact, c as ArtifactStatus, C as CreateArtifactInput, d as CreateArtifactResponse } from './types-BmNeVNFe.js';
1
3
  import { ChatCompletionTool } from 'openai/resources/chat/completions';
2
4
 
3
- /**
4
- * Shared Types for Visible CLI
5
- *
6
- * Types related to artifacts generated from code analysis.
7
- * (Vendored from @visible/shared for standalone package)
8
- */
9
- /**
10
- * Represents an artifact generated from code analysis.
11
- * An artifact contains high-level descriptions and guidelines
12
- * that serve as input for final material generation.
13
- */
14
- interface Artifact {
15
- id: string;
16
- title: string;
17
- description: string;
18
- source: ArtifactSource;
19
- context: ArtifactContext;
20
- guidelines: string[];
21
- createdAt: Date;
22
- updatedAt: Date;
23
- }
24
- /**
25
- * The source of the artifact (PR, commit, code section, etc.)
26
- */
27
- interface ArtifactSource {
28
- type: "pr" | "commit" | "code-section" | "repository";
29
- reference: string;
30
- url?: string;
31
- }
32
- /**
33
- * Context extracted from the code analysis
34
- */
35
- interface ArtifactContext {
36
- summary: string;
37
- technicalDetails: string[];
38
- affectedComponents: string[];
39
- breakingChanges: boolean;
40
- keywords: string[];
41
- }
42
- /**
43
- * Status of an artifact
44
- */
45
- type ArtifactStatus = "draft" | "ready" | "generating" | "completed";
46
- /**
47
- * Input for creating a new artifact (from CLI)
48
- */
49
- interface CreateArtifactInput {
50
- title: string;
51
- description: string;
52
- source: ArtifactSource;
53
- context: ArtifactContext;
54
- guidelines: string[];
55
- }
56
- /**
57
- * Response after creating an artifact
58
- */
59
- interface CreateArtifactResponse {
60
- id: string;
61
- url: string;
62
- }
63
-
64
5
  /**
65
6
  * Git Analyzer
66
7
  *
@@ -73,12 +14,25 @@ interface CreateArtifactResponse {
73
14
  * - Identify changed files and their context
74
15
  */
75
16
 
17
+ /**
18
+ * Code snippet extracted from diff
19
+ */
20
+ interface DiffCodeSnippet {
21
+ file: string;
22
+ code: string;
23
+ startLine?: number;
24
+ language?: string;
25
+ description?: string;
26
+ changeType: "added" | "deleted" | "modified" | "context";
27
+ }
76
28
  interface AnalysisResult {
77
29
  title: string;
78
30
  description: string;
79
31
  source: ArtifactSource;
80
32
  context: ArtifactContext;
81
33
  guidelines: string[];
34
+ /** Code snippets extracted from the diff */
35
+ codeSnippets?: DiffCodeSnippet[];
82
36
  }
83
37
  /**
84
38
  * Analyze a git diff range and produce an artifact
@@ -228,6 +182,7 @@ interface DetectiveOptions {
228
182
  projectRoot: string;
229
183
  verbose?: boolean;
230
184
  maxIterations?: number;
185
+ maxToolCalls?: number;
231
186
  onToolCall?: (toolName: string, args: unknown) => void;
232
187
  onToolResult?: (toolName: string, result: ToolResult) => void;
233
188
  onThinking?: (thought: string) => void;
@@ -289,4 +244,4 @@ declare function investigateFeature(query: string, options: Omit<DetectiveOption
289
244
  projectRoot?: string;
290
245
  }): Promise<InvestigationResult>;
291
246
 
292
- export { AGENT_TOOLS, type AnalysisResult, type Artifact, type ArtifactContext, type ArtifactSource, type ArtifactStatus, type ContextInput, type CreateArtifactInput, type CreateArtifactResponse, DetectiveAgent, type DetectiveOptions, type InvestigationResult, type PROptions, type ToolContext, type ToolName, type ToolResult, analyzeDiff, analyzePR, artifactGenerator, detectBreakingChangesFromDiff, executeTool, extractContextFromPaths, findDefinition, findReferences, generateTechnicalSummary, getFileStructure, investigateFeature, listDirectory, mergeContexts, readFile, searchFiles, searchTypes };
247
+ export { AGENT_TOOLS, type AnalysisResult, ArtifactContext, ArtifactSource, type ContextInput, DetectiveAgent, type DetectiveOptions, type DiffCodeSnippet, type InvestigationResult, type PROptions, type ToolContext, type ToolName, type ToolResult, analyzeDiff, analyzePR, artifactGenerator, detectBreakingChangesFromDiff, executeTool, extractContextFromPaths, findDefinition, findReferences, generateTechnicalSummary, getFileStructure, investigateFeature, listDirectory, mergeContexts, readFile, searchFiles, searchTypes };
package/dist/index.js CHANGED
@@ -63,6 +63,92 @@ function getCommits(range) {
63
63
  function getDiffContent(range) {
64
64
  return git(`diff ${range}`);
65
65
  }
66
+ function extractCodeSnippets(diffContent, maxSnippets = 6) {
67
+ const snippets = [];
68
+ const lines = diffContent.split("\n");
69
+ let currentFile = "";
70
+ let currentHunk = [];
71
+ let currentStartLine = 0;
72
+ let inHunk = false;
73
+ for (const line of lines) {
74
+ if (line.startsWith("diff --git")) {
75
+ if (currentHunk.length > 0 && currentFile) {
76
+ snippets.push(createSnippetFromHunk(currentFile, currentHunk, currentStartLine));
77
+ if (snippets.length >= maxSnippets) break;
78
+ }
79
+ currentHunk = [];
80
+ inHunk = false;
81
+ continue;
82
+ }
83
+ if (line.startsWith("+++ b/")) {
84
+ currentFile = line.slice(6);
85
+ continue;
86
+ }
87
+ if (line.startsWith("@@")) {
88
+ if (currentHunk.length > 0 && currentFile) {
89
+ snippets.push(createSnippetFromHunk(currentFile, currentHunk, currentStartLine));
90
+ if (snippets.length >= maxSnippets) break;
91
+ }
92
+ const match = line.match(/@@ -\d+(?:,\d+)? \+(\d+)/);
93
+ currentStartLine = match ? parseInt(match[1], 10) : 1;
94
+ currentHunk = [];
95
+ inHunk = true;
96
+ continue;
97
+ }
98
+ if (inHunk && currentHunk.length < 15) {
99
+ currentHunk.push(line);
100
+ }
101
+ }
102
+ if (currentHunk.length > 0 && currentFile && snippets.length < maxSnippets) {
103
+ snippets.push(createSnippetFromHunk(currentFile, currentHunk, currentStartLine));
104
+ }
105
+ return snippets;
106
+ }
107
+ function createSnippetFromHunk(file, hunkLines, startLine) {
108
+ const hasAdditions = hunkLines.some((l) => l.startsWith("+"));
109
+ const hasDeletions = hunkLines.some((l) => l.startsWith("-"));
110
+ let changeType;
111
+ if (hasAdditions && hasDeletions) {
112
+ changeType = "modified";
113
+ } else if (hasAdditions) {
114
+ changeType = "added";
115
+ } else if (hasDeletions) {
116
+ changeType = "deleted";
117
+ } else {
118
+ changeType = "context";
119
+ }
120
+ const code = hunkLines.map((line) => {
121
+ if (line.startsWith("+") || line.startsWith("-") || line.startsWith(" ")) {
122
+ return line.slice(1);
123
+ }
124
+ return line;
125
+ }).join("\n");
126
+ const ext = file.split(".").pop()?.toLowerCase() || "";
127
+ const langMap = {
128
+ ts: "typescript",
129
+ tsx: "typescript",
130
+ js: "javascript",
131
+ jsx: "javascript",
132
+ vue: "vue",
133
+ py: "python",
134
+ rb: "ruby",
135
+ go: "go",
136
+ rs: "rust",
137
+ java: "java",
138
+ css: "css",
139
+ scss: "scss",
140
+ html: "html",
141
+ json: "json"
142
+ };
143
+ return {
144
+ file,
145
+ code,
146
+ startLine,
147
+ language: langMap[ext] || "text",
148
+ description: `${changeType === "added" ? "Added" : changeType === "deleted" ? "Removed" : "Changed"} in ${file}`,
149
+ changeType
150
+ };
151
+ }
66
152
  function detectBreakingChanges(commits, diffContent) {
67
153
  const breakingPatterns = [
68
154
  /BREAKING CHANGE/i,
@@ -213,12 +299,14 @@ async function analyzeDiff(range) {
213
299
  breakingChanges,
214
300
  keywords
215
301
  };
302
+ const codeSnippets = extractCodeSnippets(diffContent);
216
303
  return {
217
304
  title,
218
305
  description,
219
306
  source,
220
307
  context,
221
- guidelines
308
+ guidelines,
309
+ codeSnippets
222
310
  };
223
311
  }
224
312
  async function analyzePR(options) {
@@ -1167,6 +1255,14 @@ Your mission is to investigate a codebase based on a user's query and produce a
1167
1255
 
1168
1256
  6. **Document Technical Details**: Note any important patterns, validations, error handling, or edge cases.
1169
1257
 
1258
+ ## Efficiency Rules (Important)
1259
+
1260
+ - Be efficient: use as few tools as possible (aim for 8-12 tool calls).
1261
+ - Prefer targeted \`search_files\` in content or filename over broad directory listing.
1262
+ - Read only the most relevant files; avoid reading the same file multiple times.
1263
+ - If you have enough information to explain the feature, **complete the investigation** even if some sections are partial.
1264
+ - Use empty arrays for unknown sections rather than continuing to search.
1265
+
1170
1266
  ## Tool Usage Tips
1171
1267
 
1172
1268
  - Use \`search_files\` with searchIn="filename" first for broad discovery
@@ -1210,6 +1306,7 @@ var DetectiveAgent = class {
1210
1306
  this.context = { projectRoot: options.projectRoot };
1211
1307
  this.options = {
1212
1308
  maxIterations: 25,
1309
+ maxToolCalls: 18,
1213
1310
  ...options
1214
1311
  };
1215
1312
  this.messages = [{ role: "system", content: SYSTEM_PROMPT }];
@@ -1228,14 +1325,23 @@ Use the available tools to explore the codebase, understand the feature, and the
1228
1325
  });
1229
1326
  let iterations = 0;
1230
1327
  const maxIterations = this.options.maxIterations;
1328
+ const maxToolCalls = this.options.maxToolCalls;
1231
1329
  while (iterations < maxIterations) {
1232
1330
  iterations++;
1233
1331
  try {
1332
+ const shouldForceCompletion = this.toolCallCount >= maxToolCalls || iterations === maxIterations;
1333
+ if (shouldForceCompletion) {
1334
+ this.messages.push({
1335
+ role: "user",
1336
+ content: "Finalize the investigation now using ONLY the information already gathered. Do not call any tools except complete_investigation. Use empty arrays for any section you could not verify."
1337
+ });
1338
+ }
1339
+ const tools = shouldForceCompletion ? AGENT_TOOLS.filter((tool) => tool.function.name === "complete_investigation") : AGENT_TOOLS;
1234
1340
  const response = await this.openai.chat.completions.create({
1235
- model: "gpt-4o",
1341
+ model: "gpt-5.2",
1236
1342
  messages: this.messages,
1237
- tools: AGENT_TOOLS,
1238
- tool_choice: "auto",
1343
+ tools,
1344
+ tool_choice: shouldForceCompletion ? "required" : "auto",
1239
1345
  temperature: 0.1
1240
1346
  });
1241
1347
  const message = response.choices[0].message;
@@ -1243,15 +1349,41 @@ Use the available tools to explore the codebase, understand the feature, and the
1243
1349
  if (message.content && this.options.onThinking) {
1244
1350
  this.options.onThinking(message.content);
1245
1351
  }
1246
- if (!message.tool_calls || message.tool_calls.length === 0) {
1247
- return {
1248
- success: false,
1249
- error: "Agent finished without completing investigation",
1250
- toolCalls: this.toolCallCount
1251
- };
1352
+ let toolCalls = message.tool_calls;
1353
+ if (!toolCalls || toolCalls.length === 0) {
1354
+ if (!shouldForceCompletion) {
1355
+ this.messages.push({
1356
+ role: "user",
1357
+ content: "Complete the investigation now using the current context. Call complete_investigation with best-effort details and empty arrays where needed."
1358
+ });
1359
+ const forced = await this.openai.chat.completions.create({
1360
+ model: "gpt-5.2",
1361
+ messages: this.messages,
1362
+ tools: AGENT_TOOLS.filter((tool) => tool.function.name === "complete_investigation"),
1363
+ tool_choice: "required",
1364
+ temperature: 0.1
1365
+ });
1366
+ const forcedMessage = forced.choices[0].message;
1367
+ this.messages.push(forcedMessage);
1368
+ if (forcedMessage.tool_calls && forcedMessage.tool_calls.length > 0) {
1369
+ toolCalls = forcedMessage.tool_calls;
1370
+ } else {
1371
+ return {
1372
+ success: false,
1373
+ error: forcedMessage.content ? `Agent finished without tool calls: ${forcedMessage.content}` : "Agent finished without completing investigation",
1374
+ toolCalls: this.toolCallCount
1375
+ };
1376
+ }
1377
+ } else {
1378
+ return {
1379
+ success: false,
1380
+ error: message.content ? `Agent finished without tool calls: ${message.content}` : "Agent finished without completing investigation",
1381
+ toolCalls: this.toolCallCount
1382
+ };
1383
+ }
1252
1384
  }
1253
1385
  const toolResults = [];
1254
- for (const toolCall of message.tool_calls) {
1386
+ for (const toolCall of toolCalls ?? []) {
1255
1387
  const toolName = toolCall.function.name;
1256
1388
  let args;
1257
1389
  try {