@sean.holung/minicode 0.3.2 → 0.3.3
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/README.md +48 -43
- package/dist/scripts/run-benchmarks.js +147 -0
- package/dist/src/agent/config.js +149 -40
- package/dist/src/agent/editable-config.js +314 -0
- package/dist/src/analysis/structural-analysis.js +379 -0
- package/dist/src/benchmark/evaluator.js +79 -0
- package/dist/src/benchmark/index.js +4 -0
- package/dist/src/benchmark/reporter.js +177 -0
- package/dist/src/benchmark/runner.js +100 -0
- package/dist/src/benchmark/task-loader.js +78 -0
- package/dist/src/benchmark/types.js +5 -0
- package/dist/src/cli/args.js +10 -0
- package/dist/src/cli/config-slash-command.js +135 -0
- package/dist/src/cli/plugin-install.js +69 -0
- package/dist/src/index.js +76 -6
- package/dist/src/indexer/cache.js +6 -4
- package/dist/src/indexer/code-map.js +41 -13
- package/dist/src/indexer/plugins/typescript.js +70 -23
- package/dist/src/indexer/project-index.js +175 -36
- package/dist/src/indexer/symbol-names.js +92 -0
- package/dist/src/model-utils.js +18 -0
- package/dist/src/serve/agent-bridge.js +203 -24
- package/dist/src/serve/mcp-server.js +405 -0
- package/dist/src/serve/server.js +165 -10
- package/dist/src/serve/websocket.js +8 -0
- package/dist/src/shared/graph-styles.js +119 -0
- package/dist/src/tools/find-path.js +75 -0
- package/dist/src/tools/find-references.js +7 -2
- package/dist/src/tools/get-dependencies.js +3 -2
- package/dist/src/tools/read-symbol.js +12 -5
- package/dist/src/tools/registry.js +3 -1
- package/dist/src/tools/search-code-map.js +4 -2
- package/dist/src/ui/app.js +1 -1
- package/dist/src/ui/cli-ink.js +79 -4
- package/dist/src/ui/components/header-bar.js +6 -2
- package/dist/src/ui/state/ui-store.js +5 -0
- package/dist/src/web/app.js +1124 -176
- package/dist/src/web/index.html +113 -3
- package/dist/src/web/style.css +973 -55
- package/dist/tests/agent.test.js +31 -0
- package/dist/tests/analysis-helpers.test.js +89 -0
- package/dist/tests/analysis-ui.test.js +29 -0
- package/dist/tests/benchmark-harness.test.js +527 -0
- package/dist/tests/config-api.test.js +143 -0
- package/dist/tests/config-integration.test.js +751 -0
- package/dist/tests/config-slash-command.test.js +106 -0
- package/dist/tests/config.test.js +42 -1
- package/dist/tests/context-indicator.test.js +220 -0
- package/dist/tests/editable-config.test.js +109 -0
- package/dist/tests/find-path.test.js +183 -0
- package/dist/tests/focus-tracker.test.js +62 -0
- package/dist/tests/graph-onboarding.test.js +55 -0
- package/dist/tests/graph-styles.test.js +65 -0
- package/dist/tests/indexer.test.js +137 -0
- package/dist/tests/mcp-and-plugin.test.js +186 -0
- package/dist/tests/model-client-openai.test.js +29 -0
- package/dist/tests/model-selection.test.js +136 -0
- package/dist/tests/model-utils.test.js +22 -0
- package/dist/tests/reasoning-effort.test.js +264 -0
- package/dist/tests/run-benchmarks.test.js +161 -0
- package/dist/tests/search-code-map.test.js +18 -0
- package/dist/tests/serve.integration.test.js +218 -2
- package/dist/tests/session-ui.test.js +21 -0
- package/dist/tests/session.test.js +50 -0
- package/dist/tests/settings-ui.test.js +30 -0
- package/dist/tests/structural-analysis.test.js +218 -0
- package/node_modules/@minicode/agent-sdk/README.md +80 -51
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.d.ts +16 -5
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.js +51 -33
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/agent/types.d.ts +14 -0
- package/node_modules/@minicode/agent-sdk/dist/src/agent/types.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/index.d.ts +3 -2
- package/node_modules/@minicode/agent-sdk/dist/src/index.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/index.js +2 -0
- package/node_modules/@minicode/agent-sdk/dist/src/index.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/indexer/focus-tracker.d.ts +35 -0
- package/node_modules/@minicode/agent-sdk/dist/src/indexer/focus-tracker.d.ts.map +1 -0
- package/node_modules/@minicode/agent-sdk/dist/src/indexer/focus-tracker.js +64 -0
- package/node_modules/@minicode/agent-sdk/dist/src/indexer/focus-tracker.js.map +1 -0
- package/node_modules/@minicode/agent-sdk/dist/src/indexer/types.d.ts +7 -0
- package/node_modules/@minicode/agent-sdk/dist/src/indexer/types.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/model/client.d.ts +5 -1
- package/node_modules/@minicode/agent-sdk/dist/src/model/client.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/model/client.js +83 -11
- package/node_modules/@minicode/agent-sdk/dist/src/model/client.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/safety/guardrails.d.ts +1 -0
- package/node_modules/@minicode/agent-sdk/dist/src/safety/guardrails.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/safety/guardrails.js +8 -1
- package/node_modules/@minicode/agent-sdk/dist/src/safety/guardrails.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/session/session.d.ts.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/src/session/session.js +4 -1
- package/node_modules/@minicode/agent-sdk/dist/src/session/session.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.js +3 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.js +8 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.js.map +1 -1
- package/node_modules/@minicode/agent-sdk/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -5
- package/plugin/.claude-plugin/plugin.json +12 -0
- package/plugin/.mcp.json +8 -0
- package/plugin/CLAUDE.md +26 -0
- package/plugin/skills/analyze/SKILL.md +12 -0
- package/plugin/skills/focus/SKILL.md +20 -0
- package/plugin/skills/graph/SKILL.md +13 -0
- package/plugin/skills/symbols/SKILL.md +13 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// graph-styles.ts — Pure style definitions for the Cytoscape graph.
|
|
2
|
+
// Extracted so they can be tested without browser globals.
|
|
3
|
+
export const KIND_COLORS = {
|
|
4
|
+
function: { border: '#7aa2f7', bg: 'rgba(122,162,247,0.15)' },
|
|
5
|
+
class: { border: '#bb9af7', bg: 'rgba(187,154,247,0.15)' },
|
|
6
|
+
interface: { border: '#2ac3de', bg: 'rgba(42,195,222,0.15)' },
|
|
7
|
+
type: { border: '#e0af68', bg: 'rgba(224,175,104,0.15)' },
|
|
8
|
+
variable: { border: '#9ece6a', bg: 'rgba(158,206,106,0.15)' },
|
|
9
|
+
method: { border: '#7dcfff', bg: 'rgba(125,207,255,0.15)' },
|
|
10
|
+
};
|
|
11
|
+
export const EDGE_STYLES = {
|
|
12
|
+
calls: { lineStyle: 'solid', opacity: 0.5, color: '#565f89', width: 1 },
|
|
13
|
+
imports: { lineStyle: 'dashed', opacity: 0.4, color: '#565f89', width: 1 },
|
|
14
|
+
extends: { lineStyle: 'solid', opacity: 0.7, color: '#bb9af7', width: 2 },
|
|
15
|
+
implements: { lineStyle: 'dashed', opacity: 0.6, color: '#2ac3de', width: 1.5 },
|
|
16
|
+
references: { lineStyle: 'dotted', opacity: 0.3, color: '#565f89', width: 1 },
|
|
17
|
+
};
|
|
18
|
+
export function buildStylesheet() {
|
|
19
|
+
const styles = [
|
|
20
|
+
{
|
|
21
|
+
selector: 'node',
|
|
22
|
+
style: {
|
|
23
|
+
'label': 'data(label)',
|
|
24
|
+
'font-size': 11,
|
|
25
|
+
'color': '#c0caf5',
|
|
26
|
+
'text-valign': 'bottom',
|
|
27
|
+
'text-halign': 'center',
|
|
28
|
+
'text-margin-y': 5,
|
|
29
|
+
'width': 20,
|
|
30
|
+
'height': 20,
|
|
31
|
+
'shape': 'roundrectangle',
|
|
32
|
+
'border-width': 1.5,
|
|
33
|
+
'border-color': '#565f89',
|
|
34
|
+
'background-color': 'rgba(34,35,54,0.8)',
|
|
35
|
+
'font-family': "'JetBrains Mono', monospace",
|
|
36
|
+
'text-wrap': 'none',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
selector: 'edge',
|
|
41
|
+
style: {
|
|
42
|
+
'width': 1,
|
|
43
|
+
'line-color': '#565f89',
|
|
44
|
+
'target-arrow-color': '#565f89',
|
|
45
|
+
'target-arrow-shape': 'triangle',
|
|
46
|
+
'arrow-scale': 0.6,
|
|
47
|
+
'curve-style': 'bezier',
|
|
48
|
+
'opacity': 0.4,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
for (const [kind, colors] of Object.entries(KIND_COLORS)) {
|
|
53
|
+
styles.push({
|
|
54
|
+
selector: `node.${kind}`,
|
|
55
|
+
style: {
|
|
56
|
+
'color': colors.border,
|
|
57
|
+
'border-color': colors.border,
|
|
58
|
+
'background-color': colors.bg,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
for (const [kind, s] of Object.entries(EDGE_STYLES)) {
|
|
63
|
+
styles.push({
|
|
64
|
+
selector: `edge.${kind}`,
|
|
65
|
+
style: {
|
|
66
|
+
'line-style': s.lineStyle,
|
|
67
|
+
'line-color': s.color,
|
|
68
|
+
'target-arrow-color': s.color,
|
|
69
|
+
'opacity': s.opacity,
|
|
70
|
+
'width': s.width,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
styles.push({ selector: 'node.pinned', style: { 'border-color': '#e0af68', 'border-width': 3 } });
|
|
75
|
+
styles.push({ selector: 'node.faded', style: { 'opacity': 0.15 } });
|
|
76
|
+
styles.push({ selector: 'edge.faded', style: { 'opacity': 0.05 } });
|
|
77
|
+
styles.push({ selector: 'node.highlighted', style: { 'border-width': 2.5, 'z-index': 10 } });
|
|
78
|
+
styles.push({ selector: 'edge.highlighted', style: { 'opacity': 0.9, 'width': 2, 'z-index': 10 } });
|
|
79
|
+
styles.push({
|
|
80
|
+
selector: 'node.analysis-flagged',
|
|
81
|
+
style: {
|
|
82
|
+
'border-style': 'double',
|
|
83
|
+
'border-width': 3,
|
|
84
|
+
'overlay-color': '#7dcfff',
|
|
85
|
+
'overlay-opacity': 0.05,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
styles.push({
|
|
89
|
+
selector: 'node.analysis-selected',
|
|
90
|
+
style: {
|
|
91
|
+
'border-color': '#f7768e',
|
|
92
|
+
'border-width': 4,
|
|
93
|
+
'background-color': 'rgba(247,118,142,0.18)',
|
|
94
|
+
'z-index': 30,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
styles.push({
|
|
98
|
+
selector: 'edge.analysis-flagged',
|
|
99
|
+
style: {
|
|
100
|
+
'opacity': 0.55,
|
|
101
|
+
'width': 1.75,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
styles.push({
|
|
105
|
+
selector: 'edge.analysis-selected',
|
|
106
|
+
style: {
|
|
107
|
+
'line-color': '#f7768e',
|
|
108
|
+
'target-arrow-color': '#f7768e',
|
|
109
|
+
'opacity': 0.95,
|
|
110
|
+
'width': 3,
|
|
111
|
+
'z-index': 30,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
styles.push({ selector: 'node.agent-pulse', style: { 'border-color': '#ff9e64', 'border-width': 4, 'background-color': 'rgba(255,158,100,0.25)' } });
|
|
115
|
+
styles.push({ selector: 'node.search-match', style: { 'border-color': '#e0af68', 'border-width': 2.5 } });
|
|
116
|
+
styles.push({ selector: 'node.expanded', style: { 'border-width': 2.5 } });
|
|
117
|
+
styles.push({ selector: 'node.hover-target', style: { 'border-width': 3, 'width': 24, 'height': 24, 'overlay-color': '#c0caf5', 'overlay-opacity': 0.08, 'z-index': 20 } });
|
|
118
|
+
return styles;
|
|
119
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { expectNonEmptyString, expectOptionalNumber } from "@minicode/agent-sdk";
|
|
2
|
+
import { getSymbolDisplayName } from "../indexer/symbol-names.js";
|
|
3
|
+
export function createFindPathTool(projectIndex) {
|
|
4
|
+
return {
|
|
5
|
+
name: "find_path",
|
|
6
|
+
description: "Find the path between two symbols in the dependency graph, or trace a symbol back to its entry point. Use to understand execution flow and how a function gets called.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
from: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Symbol name or qualified name to start from.",
|
|
13
|
+
},
|
|
14
|
+
to: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Symbol name or qualified name to reach. If omitted, traces back to entry point(s).",
|
|
17
|
+
},
|
|
18
|
+
max_depth: {
|
|
19
|
+
type: "number",
|
|
20
|
+
description: "Maximum search depth. Default 10 for path finding, 20 for entry point tracing.",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ["from"],
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
},
|
|
26
|
+
execute: async (input) => {
|
|
27
|
+
const from = expectNonEmptyString(input, "from");
|
|
28
|
+
const to = typeof input.to === "string" && input.to.length > 0 ? input.to : undefined;
|
|
29
|
+
const maxDepth = expectOptionalNumber(input, "max_depth");
|
|
30
|
+
const fromSymbol = projectIndex.getSymbol(from);
|
|
31
|
+
if (!fromSymbol) {
|
|
32
|
+
return `Symbol "${from}" not found in the project index.`;
|
|
33
|
+
}
|
|
34
|
+
if (to) {
|
|
35
|
+
// Path between two symbols
|
|
36
|
+
const toSymbol = projectIndex.getSymbol(to);
|
|
37
|
+
if (!toSymbol) {
|
|
38
|
+
return `Symbol "${to}" not found in the project index.`;
|
|
39
|
+
}
|
|
40
|
+
const path = projectIndex.findPath(from, to, maxDepth ?? 10);
|
|
41
|
+
if (path.length === 0) {
|
|
42
|
+
return `No path found between "${from}" and "${to}".`;
|
|
43
|
+
}
|
|
44
|
+
const lines = path.map((s, i) => {
|
|
45
|
+
const arrow = i < path.length - 1 ? " ->" : "";
|
|
46
|
+
return `${i + 1}. [${s.kind}] ${getSymbolDisplayName(s)} (${s.filePath}:${s.startLine})${arrow}`;
|
|
47
|
+
});
|
|
48
|
+
return [
|
|
49
|
+
`# Path from ${getSymbolDisplayName(fromSymbol)} to ${getSymbolDisplayName(toSymbol)} (${path.length} symbols)`,
|
|
50
|
+
"",
|
|
51
|
+
...lines,
|
|
52
|
+
].join("\n");
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Trace to entry point(s)
|
|
56
|
+
const paths = projectIndex.findPathToEntryPoint(from, maxDepth ?? 20);
|
|
57
|
+
if (paths.length === 0) {
|
|
58
|
+
return `No entry point paths found for "${from}". It may itself be an entry point.`;
|
|
59
|
+
}
|
|
60
|
+
const sections = paths.map((p, pi) => {
|
|
61
|
+
const lines = p.map((s, i) => {
|
|
62
|
+
const arrow = i < p.length - 1 ? " ->" : "";
|
|
63
|
+
return ` ${i + 1}. [${s.kind}] ${getSymbolDisplayName(s)} (${s.filePath}:${s.startLine})${arrow}`;
|
|
64
|
+
});
|
|
65
|
+
return [`## Path ${pi + 1}`, ...lines].join("\n");
|
|
66
|
+
});
|
|
67
|
+
return [
|
|
68
|
+
`# Entry point paths for ${getSymbolDisplayName(fromSymbol)} (${paths.length} path${paths.length === 1 ? "" : "s"})`,
|
|
69
|
+
"",
|
|
70
|
+
...sections,
|
|
71
|
+
].join("\n\n");
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectNonEmptyString, expectOptionalNumber } from "@minicode/agent-sdk";
|
|
2
|
+
import { getSymbolDisplayName } from "../indexer/symbol-names.js";
|
|
2
3
|
const DEFAULT_LIMIT = 50;
|
|
3
4
|
export function createFindReferencesTool(projectIndex) {
|
|
4
5
|
return {
|
|
@@ -36,13 +37,17 @@ export function createFindReferencesTool(projectIndex) {
|
|
|
36
37
|
return `No references found for "${name}".`;
|
|
37
38
|
}
|
|
38
39
|
const shown = refs.slice(skip, skip + limit);
|
|
39
|
-
const lines = shown.map((e) =>
|
|
40
|
+
const lines = shown.map((e) => {
|
|
41
|
+
const fromSymbol = projectIndex.getSymbol(e.from);
|
|
42
|
+
const label = fromSymbol ? getSymbolDisplayName(fromSymbol) : e.from;
|
|
43
|
+
return `- ${label} (${e.kind})`;
|
|
44
|
+
});
|
|
40
45
|
const remaining = refs.length - skip - shown.length;
|
|
41
46
|
const footer = remaining > 0
|
|
42
47
|
? `\n... and ${remaining} more (use skip: ${skip + limit}, limit: ${limit} for next page)`
|
|
43
48
|
: "";
|
|
44
49
|
return [
|
|
45
|
-
`# References to ${symbol
|
|
50
|
+
`# References to ${getSymbolDisplayName(symbol)} (${refs.length} total)`,
|
|
46
51
|
"",
|
|
47
52
|
...lines,
|
|
48
53
|
footer,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectNonEmptyString, expectOptionalNumber } from "@minicode/agent-sdk";
|
|
2
|
+
import { getSymbolDisplayName } from "../indexer/symbol-names.js";
|
|
2
3
|
export function createGetDependenciesTool(projectIndex) {
|
|
3
4
|
return {
|
|
4
5
|
name: "get_dependencies",
|
|
@@ -38,7 +39,7 @@ export function createGetDependenciesTool(projectIndex) {
|
|
|
38
39
|
const cone = projectIndex.getDependencyCone(name, depth);
|
|
39
40
|
const shown = cone.slice(skip, skip + limit);
|
|
40
41
|
const lines = shown.map((s) => {
|
|
41
|
-
const header = `${s.kind} ${s
|
|
42
|
+
const header = `${s.kind} ${getSymbolDisplayName(s)}`;
|
|
42
43
|
return `${header}\n ${s.signature}`;
|
|
43
44
|
});
|
|
44
45
|
const remaining = cone.length - skip - shown.length;
|
|
@@ -46,7 +47,7 @@ export function createGetDependenciesTool(projectIndex) {
|
|
|
46
47
|
? `\n\n... and ${remaining} more (use skip: ${skip + limit}, limit: ${limit} for next page)`
|
|
47
48
|
: "";
|
|
48
49
|
return [
|
|
49
|
-
`# Dependencies of ${symbol
|
|
50
|
+
`# Dependencies of ${getSymbolDisplayName(symbol)} (depth ${depth}, ${cone.length} total)`,
|
|
50
51
|
"",
|
|
51
52
|
...lines,
|
|
52
53
|
footer,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile, stat } from "node:fs/promises";
|
|
2
2
|
import { resolveWorkspacePath, validateFileReadSize, expectNonEmptyString, expectOptionalBoolean, } from "@minicode/agent-sdk";
|
|
3
|
+
import { getSymbolDisplayName } from "../indexer/symbol-names.js";
|
|
3
4
|
const LEADING_CONTEXT_LINES = 3;
|
|
4
5
|
export function createReadSymbolTool(config, projectIndex) {
|
|
5
6
|
return {
|
|
@@ -45,7 +46,7 @@ export function createReadSymbolTool(config, projectIndex) {
|
|
|
45
46
|
.map((line, i) => `${startLine + i}|${line}`)
|
|
46
47
|
.join("\n");
|
|
47
48
|
const parts = [
|
|
48
|
-
`# ${symbol
|
|
49
|
+
`# ${getSymbolDisplayName(symbol)} (${symbol.kind})`,
|
|
49
50
|
`File: ${symbol.filePath}`,
|
|
50
51
|
`Lines: ${symbol.startLine}-${symbol.endLine}`,
|
|
51
52
|
"",
|
|
@@ -57,14 +58,20 @@ export function createReadSymbolTool(config, projectIndex) {
|
|
|
57
58
|
const usedBy = projectIndex.dependencyEdges
|
|
58
59
|
.filter((e) => e.to === symbol.qualifiedName || e.to === symbol.name)
|
|
59
60
|
.slice(0, 5)
|
|
60
|
-
.map((e) =>
|
|
61
|
+
.map((e) => {
|
|
62
|
+
const ref = projectIndex.getSymbol(e.from);
|
|
63
|
+
return ref ? getSymbolDisplayName(ref) : e.from;
|
|
64
|
+
});
|
|
61
65
|
if (usedBy.length > 0) {
|
|
62
66
|
parts.push("", "## Used by", "", usedBy.map((s) => `- ${s}`).join("\n"));
|
|
63
67
|
}
|
|
64
68
|
const calls = projectIndex.dependencyEdges
|
|
65
69
|
.filter((e) => e.from === symbol.qualifiedName || e.from === symbol.name)
|
|
66
70
|
.slice(0, 5)
|
|
67
|
-
.map((e) =>
|
|
71
|
+
.map((e) => {
|
|
72
|
+
const dep = projectIndex.getSymbol(e.to);
|
|
73
|
+
return dep ? getSymbolDisplayName(dep) : e.to;
|
|
74
|
+
});
|
|
68
75
|
if (calls.length > 0) {
|
|
69
76
|
parts.push("", "## Calls", "", calls.map((s) => `- ${s}`).join("\n"));
|
|
70
77
|
}
|
|
@@ -74,13 +81,13 @@ export function createReadSymbolTool(config, projectIndex) {
|
|
|
74
81
|
if (typeRefs.length > 0) {
|
|
75
82
|
parts.push("", "## Referenced Types", "");
|
|
76
83
|
for (const ref of typeRefs) {
|
|
77
|
-
parts.push(`### ${ref
|
|
84
|
+
parts.push(`### ${getSymbolDisplayName(ref)}`, ref.signature, "");
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
87
|
return parts.join("\n");
|
|
81
88
|
}
|
|
82
89
|
const sigParts = [
|
|
83
|
-
`# ${symbol
|
|
90
|
+
`# ${getSymbolDisplayName(symbol)} (${symbol.kind})`,
|
|
84
91
|
`File: ${symbol.filePath}`,
|
|
85
92
|
`Lines: ${symbol.startLine}-${symbol.endLine}`,
|
|
86
93
|
"",
|
|
@@ -2,6 +2,7 @@ import { ToolRegistry, createReadFileTool, createWriteFileTool, createEditFileTo
|
|
|
2
2
|
import { createFindReferencesTool } from "./find-references.js";
|
|
3
3
|
import { createGetDependenciesTool } from "./get-dependencies.js";
|
|
4
4
|
import { createReadSymbolTool } from "./read-symbol.js";
|
|
5
|
+
import { createFindPathTool } from "./find-path.js";
|
|
5
6
|
import { createSearchCodeMapTool } from "./search-code-map.js";
|
|
6
7
|
export { ToolRegistry };
|
|
7
8
|
/**
|
|
@@ -27,7 +28,8 @@ export function createToolRegistry(config, projectIndex) {
|
|
|
27
28
|
tools.splice(1, 0, createReadSymbolTool(config, projectIndex));
|
|
28
29
|
tools.splice(2, 0, createFindReferencesTool(projectIndex));
|
|
29
30
|
tools.splice(3, 0, createGetDependenciesTool(projectIndex));
|
|
30
|
-
tools.splice(4, 0,
|
|
31
|
+
tools.splice(4, 0, createFindPathTool(projectIndex));
|
|
32
|
+
tools.splice(5, 0, createSearchCodeMapTool(projectIndex));
|
|
31
33
|
}
|
|
32
34
|
return new ToolRegistry(tools);
|
|
33
35
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectNonEmptyString, expectOptionalNumber } from "@minicode/agent-sdk";
|
|
2
|
+
import { getSymbolDisplayName, getSymbolLookupNames } from "../indexer/symbol-names.js";
|
|
2
3
|
const DEFAULT_LIMIT = 30;
|
|
3
4
|
function matchesPattern(text, pattern) {
|
|
4
5
|
const lowerText = text.toLowerCase();
|
|
@@ -44,7 +45,8 @@ export function createSearchCodeMapTool(projectIndex) {
|
|
|
44
45
|
const skip = Math.max(0, expectOptionalNumber(input, "skip") ?? 0);
|
|
45
46
|
const symbols = [...projectIndex.symbols.values()];
|
|
46
47
|
const matches = symbols.filter((sym) => {
|
|
47
|
-
|
|
48
|
+
const lookupNames = getSymbolLookupNames(sym);
|
|
49
|
+
if (!lookupNames.some((candidate) => matchesPattern(candidate, pattern))) {
|
|
48
50
|
return false;
|
|
49
51
|
}
|
|
50
52
|
if (kind && sym.kind !== kind) {
|
|
@@ -53,7 +55,7 @@ export function createSearchCodeMapTool(projectIndex) {
|
|
|
53
55
|
return true;
|
|
54
56
|
});
|
|
55
57
|
const shown = matches.slice(skip, skip + limit);
|
|
56
|
-
const lines = shown.map((s) => `- ${s
|
|
58
|
+
const lines = shown.map((s) => `- ${getSymbolDisplayName(s)} (${s.kind}) — ${s.filePath}`);
|
|
57
59
|
const remaining = matches.length - skip - shown.length;
|
|
58
60
|
const footer = remaining > 0
|
|
59
61
|
? `\n... and ${remaining} more (use skip: ${skip + limit}, limit: ${limit} for next page)`
|
package/dist/src/ui/app.js
CHANGED
|
@@ -16,7 +16,7 @@ function AppInner({ store, onRunTurn, onCtrlC }) {
|
|
|
16
16
|
onRunTurn(input);
|
|
17
17
|
}, [onRunTurn]);
|
|
18
18
|
const disabled = state.phase !== "idle" && state.phase !== "loading";
|
|
19
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ActivityPane, { items: state.items }), _jsx(HeaderBar, { model: state.model, step: state.step, maxSteps: state.maxSteps, inputTokens: state.inputTokens, outputTokens: state.outputTokens, workspaceRoot: state.workspaceRoot, indexStatus: state.indexStatus }), _jsx(InputComposer, { onSubmit: handleSubmit, disabled: disabled, ...(onCtrlC && { onCtrlC }) }), state.errorMessage && (_jsx(Box, { paddingX: 1, children: _jsx(Box, { borderStyle: "single", borderColor: "red", paddingX: 1, children: _jsxs(Text, { color: "red", children: ["Error: ", state.errorMessage] }) }) }))] }));
|
|
19
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ActivityPane, { items: state.items }), _jsx(HeaderBar, { model: state.model, step: state.step, maxSteps: state.maxSteps, inputTokens: state.inputTokens, outputTokens: state.outputTokens, contextTokens: state.contextTokens, maxContextTokens: state.maxContextTokens, workspaceRoot: state.workspaceRoot, indexStatus: state.indexStatus }), _jsx(InputComposer, { onSubmit: handleSubmit, disabled: disabled, ...(onCtrlC && { onCtrlC }) }), state.errorMessage && (_jsx(Box, { paddingX: 1, children: _jsx(Box, { borderStyle: "single", borderColor: "red", paddingX: 1, children: _jsxs(Text, { color: "red", children: ["Error: ", state.errorMessage] }) }) }))] }));
|
|
20
20
|
}
|
|
21
21
|
export function runInkApp(store, onRunTurn, onCtrlC) {
|
|
22
22
|
process.stdout.write("\x1b[2J\x1b[H");
|
package/dist/src/ui/cli-ink.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import { CodingAgent, createModelClient } from "@minicode/agent-sdk";
|
|
3
|
-
import {
|
|
3
|
+
import { loadAgentConfig, getConfigSetupMessage } from "../agent/config.js";
|
|
4
|
+
import { handleConfigSlashCommand } from "../cli/config-slash-command.js";
|
|
4
5
|
import { computeFileHashes, getWorkspaceCacheDir, loadIndex, saveIndex, } from "../indexer/cache.js";
|
|
5
6
|
import { buildProjectIndex } from "../indexer/project-index.js";
|
|
7
|
+
import { sortModelsAlphabetically } from "../model-utils.js";
|
|
6
8
|
import { listSessions, loadSession, loadSessionByLabel, saveSession, } from "../session/session-store.js";
|
|
7
9
|
import { createToolRegistry } from "../tools/registry.js";
|
|
8
10
|
import { UiStore } from "./state/ui-store.js";
|
|
@@ -11,6 +13,11 @@ export async function runInkCli(verbose, initialTask) {
|
|
|
11
13
|
const store = new UiStore();
|
|
12
14
|
store.setPhase("loading");
|
|
13
15
|
const config = await loadAgentConfig();
|
|
16
|
+
const setupMessage = getConfigSetupMessage(config);
|
|
17
|
+
if (setupMessage) {
|
|
18
|
+
console.error(setupMessage);
|
|
19
|
+
process.exit(2);
|
|
20
|
+
}
|
|
14
21
|
const modelClient = createModelClient(config);
|
|
15
22
|
let projectIndex;
|
|
16
23
|
let indexStatus = "building...";
|
|
@@ -38,6 +45,7 @@ export async function runInkCli(verbose, initialTask) {
|
|
|
38
45
|
maxSteps: config.maxSteps,
|
|
39
46
|
indexStatus,
|
|
40
47
|
});
|
|
48
|
+
store.setContextStatus(0, config.maxContextTokens);
|
|
41
49
|
store.setPhase("idle");
|
|
42
50
|
const toolRegistry = createToolRegistry(config, projectIndex);
|
|
43
51
|
function createUiUpdateHandler() {
|
|
@@ -74,6 +82,9 @@ export async function runInkCli(verbose, initialTask) {
|
|
|
74
82
|
});
|
|
75
83
|
store.setPhase("model_wait");
|
|
76
84
|
break;
|
|
85
|
+
case "context_status":
|
|
86
|
+
store.setContextStatus(event.contextTokens, event.maxContextTokens);
|
|
87
|
+
break;
|
|
77
88
|
}
|
|
78
89
|
};
|
|
79
90
|
}
|
|
@@ -122,14 +133,15 @@ export async function runInkCli(verbose, initialTask) {
|
|
|
122
133
|
if (trimmed === "/help") {
|
|
123
134
|
store.addItem({
|
|
124
135
|
type: "system",
|
|
125
|
-
content: 'Commands: "/help", "/config", "/compact", "/save [label]", "/load [label]", "/sessions", "/exit".',
|
|
136
|
+
content: 'Commands: "/help", "/config [keys|get|set|unset]", "/compact", "/reasoning [level]", "/models", "/model [name]", "/save [label]", "/load [label]", "/sessions", "/exit".',
|
|
126
137
|
});
|
|
127
138
|
return;
|
|
128
139
|
}
|
|
129
|
-
|
|
140
|
+
const configCommand = await handleConfigSlashCommand(trimmed, { config });
|
|
141
|
+
if (configCommand.handled) {
|
|
130
142
|
store.addItem({
|
|
131
143
|
type: "system",
|
|
132
|
-
content:
|
|
144
|
+
content: configCommand.message ?? "",
|
|
133
145
|
});
|
|
134
146
|
return;
|
|
135
147
|
}
|
|
@@ -153,6 +165,69 @@ export async function runInkCli(verbose, initialTask) {
|
|
|
153
165
|
}
|
|
154
166
|
return;
|
|
155
167
|
}
|
|
168
|
+
if (trimmed === "/reasoning" || trimmed.startsWith("/reasoning ")) {
|
|
169
|
+
const VALID_LEVELS = ["xhigh", "high", "medium", "low", "minimal", "none"];
|
|
170
|
+
const arg = trimmed.slice("/reasoning".length).trim().toLowerCase();
|
|
171
|
+
if (arg.length === 0) {
|
|
172
|
+
const current = agent.getReasoningEffort() ?? "(unset)";
|
|
173
|
+
store.addItem({
|
|
174
|
+
type: "system",
|
|
175
|
+
content: `Current reasoning effort: ${current}\nValid levels: ${VALID_LEVELS.join(", ")}, off`,
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (arg === "off") {
|
|
180
|
+
agent.setReasoningEffort(undefined);
|
|
181
|
+
store.addItem({ type: "system", content: "Reasoning effort disabled." });
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (VALID_LEVELS.includes(arg)) {
|
|
185
|
+
agent.setReasoningEffort(arg);
|
|
186
|
+
store.addItem({ type: "system", content: `Reasoning effort set to: ${arg}` });
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
store.addItem({
|
|
190
|
+
type: "system",
|
|
191
|
+
content: `Invalid reasoning level "${arg}". Valid levels: ${VALID_LEVELS.join(", ")}, off`,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (trimmed === "/models") {
|
|
197
|
+
if (modelClient.listModels) {
|
|
198
|
+
store.addItem({ type: "system", content: "Fetching models..." });
|
|
199
|
+
const models = sortModelsAlphabetically(await modelClient.listModels());
|
|
200
|
+
if (models.length === 0) {
|
|
201
|
+
store.addItem({ type: "system", content: "No models found (provider may not support listing)." });
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
const lines = models.map((m) => {
|
|
205
|
+
const marker = m.id === config.model ? " (active)" : "";
|
|
206
|
+
return ` ${m.id}${marker}`;
|
|
207
|
+
});
|
|
208
|
+
store.addItem({ type: "system", content: `Available models (${models.length}):\n${lines.join("\n")}` });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
store.addItem({ type: "system", content: "Current provider does not support listing models." });
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (trimmed.startsWith("/model ")) {
|
|
217
|
+
const newModel = trimmed.slice("/model ".length).trim();
|
|
218
|
+
if (newModel.length === 0) {
|
|
219
|
+
store.addItem({ type: "system", content: `Current model: ${config.model}` });
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
config.model = newModel;
|
|
223
|
+
store.setConfig({ model: newModel, workspaceRoot: config.workspaceRoot, maxSteps: config.maxSteps, indexStatus: "" });
|
|
224
|
+
store.addItem({ type: "system", content: `Model switched to: ${newModel}` });
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (trimmed === "/model") {
|
|
228
|
+
store.addItem({ type: "system", content: `Current model: ${config.model}` });
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
156
231
|
if (trimmed === "/save" || trimmed.startsWith("/save ")) {
|
|
157
232
|
const label = trimmed.slice("/save".length).trim() || undefined;
|
|
158
233
|
try {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { c } from "../theme.js";
|
|
4
|
-
export function HeaderBar({ model, step, maxSteps, inputTokens, outputTokens, workspaceRoot, indexStatus, }) {
|
|
5
|
-
|
|
4
|
+
export function HeaderBar({ model, step, maxSteps, inputTokens, outputTokens, contextTokens, maxContextTokens, workspaceRoot, indexStatus, }) {
|
|
5
|
+
const contextPct = maxContextTokens > 0
|
|
6
|
+
? Math.min(100, Math.round((contextTokens / maxContextTokens) * 100))
|
|
7
|
+
: 0;
|
|
8
|
+
const contextColor = contextPct >= 80 ? c.red : contextPct >= 60 ? c.yellow : c.green;
|
|
9
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: c.cyan("minicode") }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [c.dim("model:"), " "] }), _jsx(Text, { children: model || "—" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [c.dim("steps:"), " "] }), _jsxs(Text, { children: [step, "/", maxSteps] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [c.dim("tokens:"), " "] }), _jsxs(Text, { children: [inputTokens, " in / ", outputTokens, " out"] })] }), _jsxs(Box, { children: [_jsxs(Text, { children: [c.dim("cwd:"), " "] }), _jsx(Text, { children: workspaceRoot || "—" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [c.dim("index:"), " "] }), _jsx(Text, { children: indexStatus || "—" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [c.dim("context:"), " "] }), _jsx(Text, { children: contextColor(`${contextPct}%`) }), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: ["(~", contextTokens.toLocaleString(), "/", maxContextTokens.toLocaleString(), ")"] })] })] }));
|
|
6
10
|
}
|
|
@@ -4,6 +4,8 @@ const DEFAULT_STATE = {
|
|
|
4
4
|
maxSteps: 50,
|
|
5
5
|
inputTokens: 0,
|
|
6
6
|
outputTokens: 0,
|
|
7
|
+
contextTokens: 0,
|
|
8
|
+
maxContextTokens: 0,
|
|
7
9
|
model: "",
|
|
8
10
|
workspaceRoot: "",
|
|
9
11
|
indexStatus: "",
|
|
@@ -43,6 +45,9 @@ export class UiStore {
|
|
|
43
45
|
setTokenUsage(inputTokens, outputTokens) {
|
|
44
46
|
this.update({ inputTokens, outputTokens });
|
|
45
47
|
}
|
|
48
|
+
setContextStatus(contextTokens, maxContextTokens) {
|
|
49
|
+
this.update({ contextTokens, maxContextTokens });
|
|
50
|
+
}
|
|
46
51
|
addItem(item) {
|
|
47
52
|
this.update({
|
|
48
53
|
items: [...this.state.items, item],
|