@sean.holung/minicode 0.3.11 → 0.4.1
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 +5 -3
- package/dist/scripts/run-benchmarks.js +7 -1
- package/dist/src/benchmark/runner.js +66 -3
- package/dist/src/cli/benchmark-run.js +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/indexer/code-map.js +16 -1
- package/dist/src/serve/agent-bridge.js +1 -1
- package/dist/src/session/session-store.js +1 -1
- package/dist/src/shared/symbol-resolution.js +24 -3
- package/dist/src/tools/find-path.js +1 -1
- package/dist/src/tools/find-references.js +1 -1
- package/dist/src/tools/get-dependencies.js +1 -1
- package/dist/src/tools/post-edit-diagnostics.js +185 -0
- package/dist/src/tools/read-symbol.js +2 -2
- package/dist/src/tools/registry.js +18 -3
- package/dist/src/tools/search-code-map.js +101 -9
- package/dist/src/ui/cli-ink.js +1 -1
- package/dist/src/web/app.js +1 -1
- package/dist/src/web/favicon.ico +0 -0
- package/dist/src/web/favicon.svg +9 -0
- package/dist/src/web/index.html +2 -0
- package/dist/tests/agent.test.js +1 -1
- package/dist/tests/context-indicator.test.js +1 -1
- package/dist/tests/file-tools.test.js +2 -2
- package/dist/tests/focus-tracker.test.js +1 -1
- package/dist/tests/graph-onboarding.test.js +20 -0
- package/dist/tests/guardrails.test.js +1 -1
- package/dist/tests/indexer.test.js +59 -28
- package/dist/tests/model-client-openai.test.js +4 -3
- package/dist/tests/model-selection.test.js +1 -1
- package/dist/tests/package-metadata.test.js +9 -0
- package/dist/tests/python-plugin.test.js +3 -3
- package/dist/tests/read-symbol.test.js +84 -10
- package/dist/tests/reasoning-effort.test.js +1 -1
- package/dist/tests/search-code-map.test.js +132 -1
- package/dist/tests/serve.integration.test.js +1 -1
- package/dist/tests/session-store.test.js +1 -1
- package/dist/tests/session.test.js +1 -1
- package/dist/tests/symbol-resolution.test.js +57 -0
- package/dist/tests/system-prompt.test.js +1 -1
- package/dist/tests/tool-registry.test.js +1 -1
- package/node_modules/@sean.holung/minicode-sdk/LICENSE +201 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/README.md +43 -22
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/agent.d.ts +10 -1
- package/node_modules/@sean.holung/minicode-sdk/dist/src/agent/agent.d.ts.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/agent.js +33 -10
- package/node_modules/@sean.holung/minicode-sdk/dist/src/agent/agent.js.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/index.d.ts +1 -1
- package/node_modules/@sean.holung/minicode-sdk/dist/src/index.d.ts.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/index.js +1 -1
- package/node_modules/@sean.holung/minicode-sdk/dist/src/index.js.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/mcp/client-registry.js +1 -1
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/mcp/client-registry.js.map +1 -1
- package/node_modules/@sean.holung/minicode-sdk/dist/src/model/client.d.ts.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/model/client.js +73 -5
- package/node_modules/@sean.holung/minicode-sdk/dist/src/model/client.js.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/prompt/system-prompt.d.ts.map +1 -1
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/prompt/system-prompt.js +1 -1
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/prompt/system-prompt.js.map +1 -1
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file-replacers.d.ts +59 -0
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file-replacers.d.ts.map +1 -0
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file-replacers.js +392 -0
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file-replacers.js.map +1 -0
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file.d.ts +19 -0
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file.d.ts.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/edit-file.js +14 -25
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/edit-file.js.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/read-file.d.ts.map +1 -1
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/read-file.js +11 -5
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/read-file.js.map +1 -1
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/run-command.d.ts.map +1 -1
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/run-command.js +3 -0
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/run-command.js.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/search.d.ts.map +1 -1
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/search.js +52 -25
- package/node_modules/@sean.holung/minicode-sdk/dist/src/tools/search.js.map +1 -0
- package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/package.json +6 -2
- package/node_modules/minicode-plugin-python/dist/src/index.d.ts +1 -1
- package/node_modules/minicode-plugin-python/dist/src/index.d.ts.map +1 -1
- package/node_modules/minicode-plugin-python/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/minicode-plugin-python/package.json +2 -2
- package/package.json +4 -3
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/agent/agent.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/index.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/index.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/model/client.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/model/client.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/edit-file.d.ts +0 -13
- package/node_modules/@minicode/agent-sdk/dist/src/tools/edit-file.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/edit-file.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/run-command.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/src/tools/search.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.js +0 -569
- package/node_modules/@minicode/agent-sdk/dist/tests/agent.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.js +0 -131
- package/node_modules/@minicode/agent-sdk/dist/tests/file-tools.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.js +0 -54
- package/node_modules/@minicode/agent-sdk/dist/tests/guardrails.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.integration.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.integration.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.integration.test.js +0 -64
- package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.integration.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.test.js +0 -350
- package/node_modules/@minicode/agent-sdk/dist/tests/mcp-client.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-anthropic-structured-output.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-anthropic-structured-output.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-anthropic-structured-output.test.js +0 -211
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-anthropic-structured-output.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.js +0 -330
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-openai.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-structured-output.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-structured-output.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-structured-output.test.js +0 -171
- package/node_modules/@minicode/agent-sdk/dist/tests/model-client-structured-output.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/session.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/session.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/session.test.js +0 -226
- package/node_modules/@minicode/agent-sdk/dist/tests/session.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/structured-output.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/structured-output.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/structured-output.test.js +0 -212
- package/node_modules/@minicode/agent-sdk/dist/tests/structured-output.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/system-prompt.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/system-prompt.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/system-prompt.test.js +0 -76
- package/node_modules/@minicode/agent-sdk/dist/tests/system-prompt.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.d.ts +0 -3
- package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.js +0 -20
- package/node_modules/@minicode/agent-sdk/dist/tests/test-utils.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/tool-factory-options.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/tool-factory-options.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/tool-factory-options.test.js +0 -72
- package/node_modules/@minicode/agent-sdk/dist/tests/tool-factory-options.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/tool-registry.test.d.ts +0 -2
- package/node_modules/@minicode/agent-sdk/dist/tests/tool-registry.test.d.ts.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tests/tool-registry.test.js +0 -69
- package/node_modules/@minicode/agent-sdk/dist/tests/tool-registry.test.js.map +0 -1
- package/node_modules/@minicode/agent-sdk/dist/tsconfig.tsbuildinfo +0 -1
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/structured-output.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/structured-output.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/structured-output.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/structured-output.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/types.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/types.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/types.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/agent/types.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/focus-tracker.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/focus-tracker.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/focus-tracker.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/focus-tracker.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/types.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/types.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/types.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/indexer/types.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/mcp/client-registry.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/mcp/client-registry.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/model/client.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/prompt/system-prompt.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/safety/guardrails.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/safety/guardrails.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/safety/guardrails.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/safety/guardrails.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/session/session.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/session/session.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/session/session.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/session/session.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/helpers.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/helpers.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/helpers.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/helpers.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/list-files.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/list-files.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/list-files.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/list-files.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/read-file.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/registry.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/registry.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/registry.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/registry.js.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/run-command.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/search.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/write-file.d.ts +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/write-file.d.ts.map +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/write-file.js +0 -0
- /package/node_modules/{@minicode/agent-sdk → @sean.holung/minicode-sdk}/dist/src/tools/write-file.js.map +0 -0
|
@@ -1,13 +1,99 @@
|
|
|
1
|
-
import { expectNonEmptyString, expectOptionalNumber } from "@minicode
|
|
1
|
+
import { expectNonEmptyString, expectOptionalNumber } from "@sean.holung/minicode-sdk";
|
|
2
2
|
import { getSymbolDisplayName, getSymbolLookupNames } from "../indexer/symbol-names.js";
|
|
3
3
|
import { searchSymbols } from "../shared/symbol-search.js";
|
|
4
4
|
const DEFAULT_LIMIT = 30;
|
|
5
|
+
const DOC_SUMMARY_MAX_CHARS = 100;
|
|
6
|
+
/**
|
|
7
|
+
* Strip JSDoc/block-comment markers and return the first non-empty line of
|
|
8
|
+
* the comment, truncated.
|
|
9
|
+
*
|
|
10
|
+
* Surfacing this beside each match in `search_code_map` output is what
|
|
11
|
+
* addresses the disambiguation failure case from issue #184: when a search
|
|
12
|
+
* returns several similar-named symbols (e.g. `ToolRegistry` class +
|
|
13
|
+
* `createToolRegistry` wrapper + `ToolRegistry.createDefault` method), the
|
|
14
|
+
* model has no way to pick the right one from kind+path alone. The doc
|
|
15
|
+
* summary tells it which is which.
|
|
16
|
+
*/
|
|
17
|
+
export function summarizeDocComment(doc) {
|
|
18
|
+
if (!doc) {
|
|
19
|
+
return "";
|
|
20
|
+
}
|
|
21
|
+
// Strip leading /** and trailing */ — defense-in-depth; symbol indexing
|
|
22
|
+
// already cleans most of this.
|
|
23
|
+
const lines = doc
|
|
24
|
+
.replace(/^\s*\/\*\*?/, "")
|
|
25
|
+
.replace(/\*\/\s*$/, "")
|
|
26
|
+
// TS compiler can emit JSDoc with `\r` line separators (not `\n`),
|
|
27
|
+
// so we split on both. Without this, the entire comment becomes a
|
|
28
|
+
// single "line" and the truncation chops mid-paragraph rather than
|
|
29
|
+
// at the first description sentence.
|
|
30
|
+
.split(/\r\n|\r|\n/)
|
|
31
|
+
.map((line) => line.replace(/^\s*\*\s?/, "").trim())
|
|
32
|
+
.filter((line) => line.length > 0);
|
|
33
|
+
if (lines.length === 0) {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
const first = lines[0];
|
|
37
|
+
if (first.length <= DOC_SUMMARY_MAX_CHARS) {
|
|
38
|
+
return first;
|
|
39
|
+
}
|
|
40
|
+
return `${first.slice(0, DOC_SUMMARY_MAX_CHARS - 3)}...`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* When a search returns matches of multiple symbol kinds (class, function,
|
|
44
|
+
* method, …) for what is likely the same conceptual name, the model can
|
|
45
|
+
* confidently pick the wrong one — particularly for queries like
|
|
46
|
+
* `ToolRegistry` that resolve to a class, several class methods, and a
|
|
47
|
+
* standalone wrapper function. This is exactly the failure shape from
|
|
48
|
+
* issue #184. We surface the ambiguity so the model knows to refine
|
|
49
|
+
* rather than guess.
|
|
50
|
+
*
|
|
51
|
+
* The hint fires only on cross-kind ambiguity (class/interface/type AND
|
|
52
|
+
* function/method) — the typical "noun vs. verb" shape (`Foo` class vs.
|
|
53
|
+
* `createFoo()` factory). Methods alone alongside their class are
|
|
54
|
+
* structurally expected and not flagged.
|
|
55
|
+
*/
|
|
56
|
+
export function buildAmbiguityHint(matches) {
|
|
57
|
+
const kinds = new Set(matches.map((m) => m.kind));
|
|
58
|
+
if (kinds.size <= 1) {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
const hasNoun = kinds.has("class") || kinds.has("interface") || kinds.has("type");
|
|
62
|
+
// Standalone functions only — class methods alongside their class
|
|
63
|
+
// (e.g. `Widget` + `Widget.init`) are a structurally expected pairing,
|
|
64
|
+
// not a disambiguation problem. The model already understands that
|
|
65
|
+
// `Foo.method` is a member of `Foo`. The real confusion is between a
|
|
66
|
+
// type-name and a same-base-name standalone function (`Widget` class
|
|
67
|
+
// vs. `createWidget()` factory).
|
|
68
|
+
//
|
|
69
|
+
// Trade-off: this deliberately under-flags the rarer cross-class case
|
|
70
|
+
// (`class Widget` in one file plus an unrelated `widget()` method on
|
|
71
|
+
// some other class). Adding `method` to the verb set would close that
|
|
72
|
+
// gap but would also make the hint fire on every class-with-its-own-
|
|
73
|
+
// methods search, which is by far the more common shape — false
|
|
74
|
+
// positives there would be louder than the missed cross-class case.
|
|
75
|
+
const hasStandaloneVerb = kinds.has("function");
|
|
76
|
+
if (!hasNoun || !hasStandaloneVerb) {
|
|
77
|
+
return "";
|
|
78
|
+
}
|
|
79
|
+
return [
|
|
80
|
+
"Note: results span multiple symbol kinds. A class/interface/type and a function/method with similar",
|
|
81
|
+
"names usually have different responsibilities (e.g. `Foo` class vs. `createFoo()` factory). Read the",
|
|
82
|
+
"doc summaries below or call `read_symbol` on a specific candidate before assuming which one the",
|
|
83
|
+
"question is about.",
|
|
84
|
+
].join("\n");
|
|
85
|
+
}
|
|
86
|
+
function formatMatchLine(s) {
|
|
87
|
+
const main = `- ${getSymbolDisplayName(s)} (${s.kind}) — ${s.filePath}:${s.startLine} — qualified: ${s.qualifiedName}`;
|
|
88
|
+
const summary = summarizeDocComment(s.docComment);
|
|
89
|
+
return summary ? `${main}\n └─ ${summary}` : main;
|
|
90
|
+
}
|
|
5
91
|
export function createSearchCodeMapTool(projectIndex) {
|
|
6
92
|
return {
|
|
7
93
|
name: "search_code_map",
|
|
8
94
|
description: "Search the full project index for symbols by name or substring. " +
|
|
9
95
|
"Use when the code map is truncated and you need to find a symbol not listed. " +
|
|
10
|
-
"Returns disambiguated display names, qualified names, and
|
|
96
|
+
"Returns disambiguated display names, qualified names, file paths, and a one-line doc summary; use read_symbol with the result.",
|
|
11
97
|
inputSchema: {
|
|
12
98
|
type: "object",
|
|
13
99
|
properties: {
|
|
@@ -52,10 +138,10 @@ export function createSearchCodeMapTool(projectIndex) {
|
|
|
52
138
|
lookupNames: getSymbolLookupNames(sym),
|
|
53
139
|
})), pattern, { kind, limit, skip });
|
|
54
140
|
const shown = result.matches;
|
|
55
|
-
const lines = shown.map(
|
|
141
|
+
const lines = shown.map(formatMatchLine);
|
|
56
142
|
const remaining = result.total - skip - shown.length;
|
|
57
143
|
const footer = remaining > 0
|
|
58
|
-
? `\n... and ${remaining} more
|
|
144
|
+
? `\n... and ${remaining} more. Use skip: ${skip + limit}, limit: ${limit} for the next page, or refine the pattern to narrow.`
|
|
59
145
|
: "";
|
|
60
146
|
if (result.total === 0) {
|
|
61
147
|
return `No symbols matching "${pattern}"${kind ? ` (kind: ${kind})` : ""}. Try a shorter or different pattern.`;
|
|
@@ -70,12 +156,18 @@ export function createSearchCodeMapTool(projectIndex) {
|
|
|
70
156
|
footer,
|
|
71
157
|
].join("\n");
|
|
72
158
|
}
|
|
73
|
-
|
|
159
|
+
const ambiguityHint = buildAmbiguityHint(shown);
|
|
160
|
+
const sections = [
|
|
74
161
|
`# Symbols matching "${pattern}" (${result.total} total)`,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
162
|
+
];
|
|
163
|
+
if (ambiguityHint) {
|
|
164
|
+
sections.push("", ambiguityHint);
|
|
165
|
+
}
|
|
166
|
+
sections.push("", ...lines);
|
|
167
|
+
if (footer) {
|
|
168
|
+
sections.push(footer);
|
|
169
|
+
}
|
|
170
|
+
return sections.join("\n");
|
|
79
171
|
},
|
|
80
172
|
};
|
|
81
173
|
}
|
package/dist/src/ui/cli-ink.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
|
-
import { CodingAgent, createModelClient } from "@minicode
|
|
2
|
+
import { CodingAgent, createModelClient } from "@sean.holung/minicode-sdk";
|
|
3
3
|
import { loadAgentConfig, getConfigSetupMessage } from "../agent/config.js";
|
|
4
4
|
import { handleConfigSlashCommand } from "../cli/config-slash-command.js";
|
|
5
5
|
import { computeFileHashes, getWorkspaceCacheDir, loadIndex, saveIndex, } from "../indexer/cache.js";
|
package/dist/src/web/app.js
CHANGED
|
@@ -2580,7 +2580,7 @@ async function showDetail(node, detailEl) {
|
|
|
2580
2580
|
html += '<div class="detail-section" id="detail-refs"><div class="detail-section-title">References</div><div class="detail-section-list">Loading...</div></div>';
|
|
2581
2581
|
detailEl.innerHTML = '<div class="resize-handle"></div>' + html;
|
|
2582
2582
|
detailEl.classList.remove("hidden");
|
|
2583
|
-
syncDetailPanelLayout(
|
|
2583
|
+
syncDetailPanelLayout();
|
|
2584
2584
|
const handle = detailEl.querySelector(".resize-handle");
|
|
2585
2585
|
handle.addEventListener("mousedown", (e) => {
|
|
2586
2586
|
e.preventDefault();
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="64" height="64" rx="16" fill="#0f172a"/>
|
|
3
|
+
<path d="M18 19L44 22M18 19L20 45M44 22L46 46M20 45L46 46M18 19L46 46" stroke="#38bdf8" stroke-width="3" stroke-linecap="round" opacity="0.65"/>
|
|
4
|
+
<circle cx="18" cy="19" r="7" fill="#e2e8f0"/>
|
|
5
|
+
<circle cx="44" cy="22" r="6" fill="#22d3ee"/>
|
|
6
|
+
<circle cx="20" cy="45" r="6" fill="#22d3ee"/>
|
|
7
|
+
<circle cx="46" cy="46" r="7" fill="#e2e8f0"/>
|
|
8
|
+
<path d="M18 19L46 46" stroke="#f8fafc" stroke-width="2" stroke-linecap="round" opacity="0.35"/>
|
|
9
|
+
</svg>
|
package/dist/src/web/index.html
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>minicode</title>
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
8
|
+
<link rel="icon" sizes="any" href="/favicon.ico">
|
|
7
9
|
<link rel="stylesheet" href="style.css">
|
|
8
10
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
11
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
package/dist/tests/agent.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { test } from "node:test";
|
|
4
|
-
import { CodingAgent, ToolRegistry, } from "@minicode
|
|
4
|
+
import { CodingAgent, ToolRegistry, } from "@sean.holung/minicode-sdk";
|
|
5
5
|
import { buildProjectIndex } from "../src/indexer/project-index.js";
|
|
6
6
|
import { createTestAgentConfig } from "./test-utils.js";
|
|
7
7
|
class SequenceModelClient {
|
|
@@ -5,7 +5,7 @@ import { readFileSync } from "node:fs";
|
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { createRequestHandler } from "../src/serve/server.js";
|
|
7
7
|
import { AgentBridge } from "../src/serve/agent-bridge.js";
|
|
8
|
-
import { CodingAgent, ToolRegistry, } from "@minicode
|
|
8
|
+
import { CodingAgent, ToolRegistry, } from "@sean.holung/minicode-sdk";
|
|
9
9
|
import { UiStore } from "../src/ui/state/ui-store.js";
|
|
10
10
|
import { createTestAgentConfig } from "./test-utils.js";
|
|
11
11
|
const distWeb = join(import.meta.dirname, "..", "dist", "src", "web");
|
|
@@ -3,7 +3,7 @@ import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { test } from "node:test";
|
|
6
|
-
import { createEditFileTool, createReadFileTool, createRunCommandTool, createWriteFileTool } from "@minicode
|
|
6
|
+
import { createEditFileTool, createReadFileTool, createRunCommandTool, createWriteFileTool } from "@sean.holung/minicode-sdk";
|
|
7
7
|
import { buildProjectIndex } from "../src/indexer/project-index.js";
|
|
8
8
|
import { createToolRegistry } from "../src/tools/registry.js";
|
|
9
9
|
import { createTestAgentConfig } from "./test-utils.js";
|
|
@@ -33,7 +33,7 @@ test("edit_file fails when old_string is not unique", async () => {
|
|
|
33
33
|
path: "sample.txt",
|
|
34
34
|
old_string: "repeat",
|
|
35
35
|
new_string: "once",
|
|
36
|
-
}), /
|
|
36
|
+
}), /multiple matches/i);
|
|
37
37
|
const unchanged = await readFile(filePath, "utf8");
|
|
38
38
|
assert.equal(unchanged, "repeat repeat repeat");
|
|
39
39
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { FocusTracker } from "@minicode
|
|
3
|
+
import { FocusTracker } from "@sean.holung/minicode-sdk";
|
|
4
4
|
test("FocusTracker tracks added symbols", () => {
|
|
5
5
|
const tracker = new FocusTracker();
|
|
6
6
|
tracker.addSymbol("Foo");
|
|
@@ -3,6 +3,7 @@ import { test } from 'node:test';
|
|
|
3
3
|
import { readFileSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
const distWeb = join(import.meta.dirname, '..', 'dist', 'src', 'web');
|
|
6
|
+
const graphSource = join(import.meta.dirname, '..', 'src', 'web', 'graph.ts');
|
|
6
7
|
test('built CSS contains graph-onboarding styles', () => {
|
|
7
8
|
const css = readFileSync(join(distWeb, 'style.css'), 'utf-8');
|
|
8
9
|
assert.ok(css.includes('.graph-onboarding'), 'CSS should contain .graph-onboarding class');
|
|
@@ -23,11 +24,20 @@ test('built HTML contains #cy graph container', () => {
|
|
|
23
24
|
const html = readFileSync(join(distWeb, 'index.html'), 'utf-8');
|
|
24
25
|
assert.ok(html.includes('id="cy"'), 'HTML should contain the #cy graph container');
|
|
25
26
|
assert.ok(html.includes('id="graph-pane"'), 'HTML should contain the #graph-pane wrapper');
|
|
27
|
+
assert.ok(html.includes('href="/favicon.svg"'), 'HTML should link the app favicon');
|
|
28
|
+
assert.ok(html.includes('href="/favicon.ico"'), 'HTML should link the fallback ICO favicon');
|
|
26
29
|
assert.ok(html.includes('Search symbols or files...'), 'HTML should expose mixed symbol/file search');
|
|
27
30
|
assert.ok(html.includes('id="graph-refresh"'), 'HTML should expose a graph refresh button');
|
|
28
31
|
assert.ok(html.includes('id="file-preview-modal"'), 'HTML should contain the file preview modal shell');
|
|
29
32
|
assert.ok(html.includes('id="file-preview-code"'), 'HTML should contain the file preview code surface');
|
|
30
33
|
});
|
|
34
|
+
test('web build copies the favicon asset', () => {
|
|
35
|
+
const favicon = readFileSync(join(distWeb, 'favicon.svg'), 'utf-8');
|
|
36
|
+
const fallbackFavicon = readFileSync(join(distWeb, 'favicon.ico'));
|
|
37
|
+
assert.ok(favicon.includes('<svg'), 'favicon should be an SVG asset');
|
|
38
|
+
assert.ok(favicon.includes('#38bdf8'), 'favicon should use the minicode graph accent');
|
|
39
|
+
assert.ok(fallbackFavicon.length > 0, 'fallback favicon should be copied');
|
|
40
|
+
});
|
|
31
41
|
test('onboarding hint includes user-facing guidance text in built JS', () => {
|
|
32
42
|
const js = readFileSync(join(distWeb, 'app.js'), 'utf-8');
|
|
33
43
|
assert.ok(js.includes('Code dependency graph'), 'onboarding title should mention the code dependency graph');
|
|
@@ -58,6 +68,16 @@ test('built JS auto-opens symbol details for agent activity and graph search sel
|
|
|
58
68
|
assert.ok(js.includes('openDetail: true'), 'JS should request the detail panel when focusing symbols from agent activity or search');
|
|
59
69
|
assert.ok(js.includes('await showDetail(node, detailEl)'), 'JS should populate the symbol detail panel when focus requests it');
|
|
60
70
|
});
|
|
71
|
+
test('symbol selection keeps the current graph viewport instead of fitting the canvas', () => {
|
|
72
|
+
const source = readFileSync(graphSource, 'utf-8');
|
|
73
|
+
const showDetailStart = source.indexOf('async function showDetail');
|
|
74
|
+
const loadSourceStart = source.indexOf('async function loadSource');
|
|
75
|
+
assert.ok(showDetailStart >= 0, 'graph source should define showDetail');
|
|
76
|
+
assert.ok(loadSourceStart > showDetailStart, 'graph source should define loadSource after showDetail');
|
|
77
|
+
const showDetailBody = source.slice(showDetailStart, loadSourceStart);
|
|
78
|
+
assert.ok(showDetailBody.includes('syncDetailPanelLayout();'), 'showDetail should resize Cytoscape without changing pan/zoom');
|
|
79
|
+
assert.ok(!showDetailBody.includes('syncDetailPanelLayout({ fit: true })'), 'showDetail should not fit the graph when a symbol is selected');
|
|
80
|
+
});
|
|
61
81
|
test('built JS supports file search results and file-centered neighborhood rendering', () => {
|
|
62
82
|
const js = readFileSync(join(distWeb, 'app.js'), 'utf-8');
|
|
63
83
|
assert.ok(js.includes('focusFileInGraph'), 'JS should define a file-focused graph seeding helper');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { resolveWorkspacePath, validateCommand, validatePath, } from "@minicode
|
|
3
|
+
import { resolveWorkspacePath, validateCommand, validatePath, } from "@sean.holung/minicode-sdk";
|
|
4
4
|
test("validatePath allows files within workspace", () => {
|
|
5
5
|
const workspaceRoot = "/tmp/workspace";
|
|
6
6
|
assert.equal(validatePath("src/index.ts", workspaceRoot), true);
|
|
@@ -168,11 +168,29 @@ test("Code map handles empty symbols map", () => {
|
|
|
168
168
|
assert.ok(result.text.includes("# Project Code Map"));
|
|
169
169
|
assert.ok(result.text.length < 100);
|
|
170
170
|
});
|
|
171
|
+
test("Code map honors MINICODE_CODE_MAP_FORMAT=full opt-out", () => {
|
|
172
|
+
const symbols = typescriptPlugin.indexFile("sample.ts", SAMPLE_TS);
|
|
173
|
+
const byFile = new Map([["sample.ts", symbols]]);
|
|
174
|
+
const prev = process.env.MINICODE_CODE_MAP_FORMAT;
|
|
175
|
+
process.env.MINICODE_CODE_MAP_FORMAT = "full";
|
|
176
|
+
try {
|
|
177
|
+
const result = generateCodeMap(byFile);
|
|
178
|
+
// Full format prefixes each non-method symbol with the bare kind keyword
|
|
179
|
+
// (e.g. "class CodingAgent") and emits a separate signature line.
|
|
180
|
+
assert.ok(result.text.includes("class CodingAgent"), "full format prefixes class keyword");
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
if (prev === undefined)
|
|
184
|
+
delete process.env.MINICODE_CODE_MAP_FORMAT;
|
|
185
|
+
else
|
|
186
|
+
process.env.MINICODE_CODE_MAP_FORMAT = prev;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
171
189
|
test("Code map nests methods under class", () => {
|
|
172
190
|
const symbols = typescriptPlugin.indexFile("sample.ts", SAMPLE_TS);
|
|
173
191
|
const byFile = new Map([["sample.ts", symbols]]);
|
|
174
192
|
const result = generateCodeMap(byFile);
|
|
175
|
-
assert.ok(result.text.includes("class
|
|
193
|
+
assert.ok(result.text.includes("CodingAgent (class)"));
|
|
176
194
|
assert.ok(result.text.includes("runTurn"));
|
|
177
195
|
const runTurnLine = result.text.split("\n").find((l) => l.includes("runTurn"));
|
|
178
196
|
assert.ok(runTurnLine?.startsWith(" "), "method should be indented under class");
|
|
@@ -197,19 +215,32 @@ test("reindexFile updates symbols and code map after file change", async () => {
|
|
|
197
215
|
const updatedSym = index.getSymbol("greet");
|
|
198
216
|
assert.ok(updatedSym, "should still find greet");
|
|
199
217
|
assert.ok(updatedSym.signature.includes("title?: string"), "signature should reflect updated params");
|
|
200
|
-
|
|
201
|
-
|
|
218
|
+
// Verify the code map is regenerated after reindex. The default named
|
|
219
|
+
// format doesn't include signatures, so we opt into the full format
|
|
220
|
+
// here to assert the signature change flows through.
|
|
221
|
+
const prev = process.env.MINICODE_CODE_MAP_FORMAT;
|
|
222
|
+
process.env.MINICODE_CODE_MAP_FORMAT = "full";
|
|
223
|
+
try {
|
|
224
|
+
const codeMap = index.getCodeMap();
|
|
225
|
+
assert.ok(codeMap.text.includes("title?: string"), "code map should reflect new signature");
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
if (prev === undefined)
|
|
229
|
+
delete process.env.MINICODE_CODE_MAP_FORMAT;
|
|
230
|
+
else
|
|
231
|
+
process.env.MINICODE_CODE_MAP_FORMAT = prev;
|
|
232
|
+
}
|
|
202
233
|
});
|
|
203
234
|
test("buildProjectIndex preserves colliding top-level symbols with distinct display names", async () => {
|
|
204
235
|
const workspaceRoot = await mkdtemp(path.join(tmpdir(), "minicode-colliding-symbols-"));
|
|
205
236
|
const samplePath = path.join(workspaceRoot, "sample.ts");
|
|
206
|
-
const content = `export interface Employee {
|
|
207
|
-
id: string;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export class Employee {
|
|
211
|
-
constructor(public id: string) {}
|
|
212
|
-
}
|
|
237
|
+
const content = `export interface Employee {
|
|
238
|
+
id: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export class Employee {
|
|
242
|
+
constructor(public id: string) {}
|
|
243
|
+
}
|
|
213
244
|
`;
|
|
214
245
|
await writeFile(samplePath, content, "utf8");
|
|
215
246
|
const index = await buildProjectIndex(workspaceRoot);
|
|
@@ -227,8 +258,8 @@ export class Employee {
|
|
|
227
258
|
assert.equal(employeeClass.kind, "class");
|
|
228
259
|
assert.equal(employeeInterface.kind, "interface");
|
|
229
260
|
const codeMap = index.getCodeMap();
|
|
230
|
-
assert.ok(codeMap.text.includes("
|
|
231
|
-
assert.ok(codeMap.text.includes("
|
|
261
|
+
assert.ok(codeMap.text.includes("Employee (interface)"));
|
|
262
|
+
assert.ok(codeMap.text.includes("Employee (class)"));
|
|
232
263
|
const resolved = index.getSymbol("Employee");
|
|
233
264
|
assert.ok(resolved, "bare lookup should still resolve one of the colliding symbols");
|
|
234
265
|
assert.ok(resolved.displayName === "Employee (class)" || resolved.displayName === "Employee (interface)", "resolved symbol should use a disambiguated display name");
|
|
@@ -359,23 +390,23 @@ function beta() {}
|
|
|
359
390
|
assert.ok(callEdge, "should resolve dependencies even without cached AST");
|
|
360
391
|
});
|
|
361
392
|
test("resolveDependencies prefers same-file symbols over same-named helpers in other files", () => {
|
|
362
|
-
const projectIndexCode = `
|
|
363
|
-
async function collectSourceFiles(dir: string): Promise<void> {
|
|
364
|
-
await collectSourceFiles(dir + "/nested");
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
export async function buildProjectIndex(): Promise<void> {
|
|
368
|
-
await collectSourceFiles("src");
|
|
369
|
-
}
|
|
393
|
+
const projectIndexCode = `
|
|
394
|
+
async function collectSourceFiles(dir: string): Promise<void> {
|
|
395
|
+
await collectSourceFiles(dir + "/nested");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export async function buildProjectIndex(): Promise<void> {
|
|
399
|
+
await collectSourceFiles("src");
|
|
400
|
+
}
|
|
370
401
|
`;
|
|
371
|
-
const cacheCode = `
|
|
372
|
-
async function collectSourceFiles(dir: string): Promise<void> {
|
|
373
|
-
await collectSourceFiles(dir + "/cached");
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
export async function computeFileHashes(): Promise<void> {
|
|
377
|
-
await collectSourceFiles("src");
|
|
378
|
-
}
|
|
402
|
+
const cacheCode = `
|
|
403
|
+
async function collectSourceFiles(dir: string): Promise<void> {
|
|
404
|
+
await collectSourceFiles(dir + "/cached");
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export async function computeFileHashes(): Promise<void> {
|
|
408
|
+
await collectSourceFiles("src");
|
|
409
|
+
}
|
|
379
410
|
`;
|
|
380
411
|
const byFile = new Map([
|
|
381
412
|
["src/indexer/project-index.ts", typescriptPlugin.indexFile("src/indexer/project-index.ts", projectIndexCode)],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { test } from "node:test";
|
|
3
|
-
import { OpenAICompatibleModelClient, createModelClient, } from "@minicode
|
|
3
|
+
import { OpenAICompatibleModelClient, createModelClient, } from "@sean.holung/minicode-sdk";
|
|
4
4
|
import { createTestAgentConfig } from "./test-utils.js";
|
|
5
5
|
test("openai-compatible client sends tool schemas and parses tool calls", async () => {
|
|
6
6
|
let capturedUrl = "";
|
|
@@ -73,7 +73,7 @@ test("openai-compatible client sends tool schemas and parses tool calls", async
|
|
|
73
73
|
const tools = parsedBody.tools;
|
|
74
74
|
assert.equal(tools[0]?.type, "function");
|
|
75
75
|
});
|
|
76
|
-
test("openai-compatible client sends
|
|
76
|
+
test("openai-compatible client sends OpenRouter app attribution headers", async () => {
|
|
77
77
|
let capturedHeaders = {};
|
|
78
78
|
const fetchImpl = async (_input, init) => {
|
|
79
79
|
const rawHeaders = init?.headers;
|
|
@@ -100,7 +100,8 @@ test("openai-compatible client sends correct app URL in HTTP-Referer header", as
|
|
|
100
100
|
maxTokens: 64,
|
|
101
101
|
});
|
|
102
102
|
assert.equal(capturedHeaders["HTTP-Referer"], "https://minicode.seanholung.com", "HTTP-Referer should point to minicode.seanholung.com");
|
|
103
|
-
assert.equal(capturedHeaders["X-Title"], "minicode");
|
|
103
|
+
assert.equal(capturedHeaders["X-OpenRouter-Title"], "minicode");
|
|
104
|
+
assert.equal(capturedHeaders["X-OpenRouter-Categories"], "cli-agent,programming-app");
|
|
104
105
|
});
|
|
105
106
|
test("openai-compatible client repairs missing tool results before sending", async () => {
|
|
106
107
|
let capturedBody = "";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
|
-
import { OpenAICompatibleModelClient } from "@minicode
|
|
4
|
+
import { OpenAICompatibleModelClient } from "@sean.holung/minicode-sdk";
|
|
5
5
|
import { createRequestHandler } from "../src/serve/server.js";
|
|
6
6
|
import { AgentBridge } from "../src/serve/agent-bridge.js";
|
|
7
7
|
import { createTestAgentConfig } from "./test-utils.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
const packageJsonPath = join(import.meta.dirname, '..', 'package.json');
|
|
6
|
+
test('package metadata includes the minicode website for app attribution', () => {
|
|
7
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
8
|
+
assert.equal(pkg.homepage, 'https://minicode.seanholung.com');
|
|
9
|
+
});
|
|
@@ -37,9 +37,9 @@ test("buildProjectIndex indexes a Python workspace", async () => {
|
|
|
37
37
|
assert.ok(index.getSymbol("Foo.bar"));
|
|
38
38
|
assert.ok(index.getSymbol("baz"));
|
|
39
39
|
const codeMap = index.getCodeMap();
|
|
40
|
-
assert.ok(codeMap.text.includes("class
|
|
41
|
-
assert.ok(codeMap.text.includes("
|
|
42
|
-
assert.ok(codeMap.text.includes("
|
|
40
|
+
assert.ok(codeMap.text.includes("Foo (class)"));
|
|
41
|
+
assert.ok(codeMap.text.includes("bar"));
|
|
42
|
+
assert.ok(codeMap.text.includes("baz"));
|
|
43
43
|
});
|
|
44
44
|
test("verify-index-python fixture exercises the indexing pipeline", async () => {
|
|
45
45
|
const root = path.resolve(import.meta.dirname, "..");
|
|
@@ -27,6 +27,11 @@ test("read_symbol returns error for unknown symbol name", async () => {
|
|
|
27
27
|
const result = await tool.execute({ name: "NonExistentSymbol123" });
|
|
28
28
|
assert.ok(result.includes("not found"));
|
|
29
29
|
assert.ok(result.includes("search") || result.includes("read_file"));
|
|
30
|
+
// Miss path should preferentially nudge toward search_code_map (the
|
|
31
|
+
// graph-aware retry) before suggesting full-file reads — otherwise
|
|
32
|
+
// the agent abandons minicode's symbol-aware path the moment it
|
|
33
|
+
// hits a single miss.
|
|
34
|
+
assert.ok(result.includes("search_code_map"), `expected miss message to suggest search_code_map; got: ${result}`);
|
|
30
35
|
});
|
|
31
36
|
test("read_symbol with includeBody: false returns signature only", async () => {
|
|
32
37
|
const root = path.resolve(import.meta.dirname, "..");
|
|
@@ -66,6 +71,75 @@ test("read_symbol appears in tool registry schemas when projectIndex provided",
|
|
|
66
71
|
const props = readSymbol.input_schema.properties;
|
|
67
72
|
assert.ok(props && "name" in props);
|
|
68
73
|
});
|
|
74
|
+
test("MINICODE_TOOL_PROFILE=file-search-only omits the five graph-aware tools", async () => {
|
|
75
|
+
const root = path.resolve(import.meta.dirname, "..");
|
|
76
|
+
const config = createTestAgentConfig(root);
|
|
77
|
+
const projectIndex = await buildProjectIndex(root);
|
|
78
|
+
const original = process.env.MINICODE_TOOL_PROFILE;
|
|
79
|
+
process.env.MINICODE_TOOL_PROFILE = "file-search-only";
|
|
80
|
+
try {
|
|
81
|
+
const registry = createToolRegistry(config, projectIndex);
|
|
82
|
+
const schemas = registry.getToolSchemas();
|
|
83
|
+
const names = new Set(schemas.map((s) => s.name));
|
|
84
|
+
// Graph-aware tools must be absent in file-search-only mode.
|
|
85
|
+
for (const omitted of [
|
|
86
|
+
"read_symbol",
|
|
87
|
+
"find_references",
|
|
88
|
+
"get_dependencies",
|
|
89
|
+
"find_path",
|
|
90
|
+
"search_code_map",
|
|
91
|
+
]) {
|
|
92
|
+
assert.ok(!names.has(omitted), `expected ${omitted} to be absent`);
|
|
93
|
+
}
|
|
94
|
+
// Core file/search/run tools must still be present — that's the
|
|
95
|
+
// "search-only" half of the profile name.
|
|
96
|
+
for (const required of [
|
|
97
|
+
"read_file",
|
|
98
|
+
"write_file",
|
|
99
|
+
"edit_file",
|
|
100
|
+
"search",
|
|
101
|
+
"list_files",
|
|
102
|
+
"run_command",
|
|
103
|
+
]) {
|
|
104
|
+
assert.ok(names.has(required), `expected ${required} to be present`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
if (original === undefined) {
|
|
109
|
+
delete process.env.MINICODE_TOOL_PROFILE;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
process.env.MINICODE_TOOL_PROFILE = original;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
test("createToolRegistry includes graph-aware tools by default when projectIndex is provided", async () => {
|
|
117
|
+
const root = path.resolve(import.meta.dirname, "..");
|
|
118
|
+
const config = createTestAgentConfig(root);
|
|
119
|
+
const projectIndex = await buildProjectIndex(root);
|
|
120
|
+
// Must run with no MINICODE_TOOL_PROFILE set, otherwise the env from a
|
|
121
|
+
// host shell would mask the default behavior.
|
|
122
|
+
const original = process.env.MINICODE_TOOL_PROFILE;
|
|
123
|
+
delete process.env.MINICODE_TOOL_PROFILE;
|
|
124
|
+
try {
|
|
125
|
+
const registry = createToolRegistry(config, projectIndex);
|
|
126
|
+
const names = new Set(registry.getToolSchemas().map((s) => s.name));
|
|
127
|
+
for (const required of [
|
|
128
|
+
"read_symbol",
|
|
129
|
+
"find_references",
|
|
130
|
+
"get_dependencies",
|
|
131
|
+
"find_path",
|
|
132
|
+
"search_code_map",
|
|
133
|
+
]) {
|
|
134
|
+
assert.ok(names.has(required), `expected ${required} to be present by default`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
if (original !== undefined) {
|
|
139
|
+
process.env.MINICODE_TOOL_PROFILE = original;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
69
143
|
test("read_symbol includes Referenced Types section for createToolRegistry", async () => {
|
|
70
144
|
const root = path.resolve(import.meta.dirname, "..");
|
|
71
145
|
const config = createTestAgentConfig(root);
|
|
@@ -77,11 +151,11 @@ test("read_symbol includes Referenced Types section for createToolRegistry", asy
|
|
|
77
151
|
});
|
|
78
152
|
test("read_symbol returns disambiguation list for ambiguous bare symbol names", async () => {
|
|
79
153
|
const workspaceRoot = await mkdtemp(path.join(tmpdir(), "minicode-read-symbol-collisions-"));
|
|
80
|
-
await writeFile(path.join(workspaceRoot, "sample.ts"), `export type Review = { id: string };
|
|
81
|
-
|
|
82
|
-
export class Review {
|
|
83
|
-
constructor(public id: string) {}
|
|
84
|
-
}
|
|
154
|
+
await writeFile(path.join(workspaceRoot, "sample.ts"), `export type Review = { id: string };
|
|
155
|
+
|
|
156
|
+
export class Review {
|
|
157
|
+
constructor(public id: string) {}
|
|
158
|
+
}
|
|
85
159
|
`, "utf8");
|
|
86
160
|
const config = createTestAgentConfig(workspaceRoot);
|
|
87
161
|
const projectIndex = await buildProjectIndex(workspaceRoot);
|
|
@@ -95,11 +169,11 @@ export class Review {
|
|
|
95
169
|
});
|
|
96
170
|
test("read_symbol accepts qualified names for colliding symbols", async () => {
|
|
97
171
|
const workspaceRoot = await mkdtemp(path.join(tmpdir(), "minicode-read-symbol-qualified-"));
|
|
98
|
-
await writeFile(path.join(workspaceRoot, "sample.ts"), `export type Review = { id: string };
|
|
99
|
-
|
|
100
|
-
export class Review {
|
|
101
|
-
constructor(public id: string) {}
|
|
102
|
-
}
|
|
172
|
+
await writeFile(path.join(workspaceRoot, "sample.ts"), `export type Review = { id: string };
|
|
173
|
+
|
|
174
|
+
export class Review {
|
|
175
|
+
constructor(public id: string) {}
|
|
176
|
+
}
|
|
103
177
|
`, "utf8");
|
|
104
178
|
const config = createTestAgentConfig(workspaceRoot);
|
|
105
179
|
const projectIndex = await buildProjectIndex(workspaceRoot);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { test } from "node:test";
|
|
3
|
-
import { OpenAICompatibleModelClient, CodingAgent, ToolRegistry, } from "@minicode
|
|
3
|
+
import { OpenAICompatibleModelClient, CodingAgent, ToolRegistry, } from "@sean.holung/minicode-sdk";
|
|
4
4
|
import { createTestAgentConfig } from "./test-utils.js";
|
|
5
5
|
import { loadAgentConfig } from "../src/agent/config.js";
|
|
6
6
|
// ---------------------------------------------------------------------------
|