salmon-loop 0.3.2 → 0.5.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/cli/authorization/non-interactive.js +9 -13
- package/dist/cli/authorization/provider.js +2 -10
- package/dist/cli/chat.js +12 -6
- package/dist/cli/commands/allowlist.js +1 -1
- package/dist/cli/commands/chat.js +13 -13
- package/dist/cli/commands/config.js +2 -2
- package/dist/cli/commands/mode.js +2 -2
- package/dist/cli/commands/parallel.js +1 -1
- package/dist/cli/commands/run/handler.js +9 -4
- package/dist/cli/commands/run/loop-params.js +2 -0
- package/dist/cli/commands/run/parse-options.js +14 -26
- package/dist/cli/commands/run/runtime-llm.js +15 -12
- package/dist/cli/commands/run/runtime-options.js +3 -1
- package/dist/cli/config.js +0 -8
- package/dist/cli/headless/openai-responses-canonical-applier.js +1 -7
- package/dist/cli/locales/en.js +2 -2
- package/dist/cli/reporters/standard.js +12 -3
- package/dist/cli/reporters/stream-json.js +2 -1
- package/dist/cli/slash/runtime.js +2 -2
- package/dist/cli/ui/hooks/useLoopEvents.js +1 -1
- package/dist/cli/ui/hooks/useLoopState.js +1 -1
- package/dist/core/adapters/fs/file-adapter.js +3 -1
- package/dist/core/adapters/git/git-adapter.js +6 -3
- package/dist/core/adapters/git/git-runner.js +5 -2
- package/dist/core/adapters/git/lock-manager.js +7 -4
- package/dist/core/ast/parser.js +18 -9
- package/dist/core/checkpoint-domain/manifest-store.js +21 -13
- package/dist/core/checkpoint-domain/service.js +3 -1
- package/dist/core/config/limits.js +1 -1
- package/dist/core/config/model-pricing.js +61 -0
- package/dist/core/config/schema.js +738 -0
- package/dist/core/config/validate.js +11 -922
- package/dist/core/context/ast/skeleton-extractor.js +225 -0
- package/dist/core/context/ast/source-outline.js +24 -1
- package/dist/core/context/budget/dynamic-adjuster.js +20 -5
- package/dist/core/context/builder.js +7 -3
- package/dist/core/context/cache/store-factory.js +3 -1
- package/dist/core/context/dependencies.js +2 -1
- package/dist/core/context/effectiveness/persistence.js +50 -0
- package/dist/core/context/effectiveness/tracker.js +24 -0
- package/dist/core/context/gatherers/architecture-gatherer.js +2 -1
- package/dist/core/context/gatherers/artifact-gatherer.js +7 -4
- package/dist/core/context/gatherers/ast-gatherer.js +34 -40
- package/dist/core/context/gatherers/ghost-dependency-gatherer.js +0 -1
- package/dist/core/context/gatherers/git-history-gatherer.js +3 -1
- package/dist/core/context/gatherers/knowledge-gatherer.js +21 -2
- package/dist/core/context/gatherers/metadata-gatherer.js +12 -7
- package/dist/core/context/gatherers/ripgrep-gatherer.js +6 -3
- package/dist/core/context/service.js +12 -2
- package/dist/core/context/steps/context-gather.js +14 -3
- package/dist/core/context/steps/context-targets.js +1 -0
- package/dist/core/context/targeting/target-resolver.js +29 -11
- package/dist/core/context/token/cache.js +5 -2
- package/dist/core/context/token/encoding-registry.js +7 -6
- package/dist/core/context/truncation/strategies/json.js +5 -2
- package/dist/core/context/truncation/type-detector.js +3 -1
- package/dist/core/extensions/index.js +48 -3
- package/dist/core/extensions/load.js +3 -2
- package/dist/core/extensions/merge.js +5 -1
- package/dist/core/extensions/paths.js +8 -2
- package/dist/core/extensions/schemas.js +21 -0
- package/dist/core/facades/cli-authorization-provider.js +1 -0
- package/dist/core/facades/cli-command-chat.js +2 -0
- package/dist/core/facades/cli-run-handler.js +1 -0
- package/dist/core/facades/cli-utils-serialize.js +2 -0
- package/dist/core/feedback/parsers.js +290 -1
- package/dist/core/grizzco/dsl/llm-strategy.js +4 -3
- package/dist/core/grizzco/engine/observability/loop-telemetry.js +5 -2
- package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +30 -13
- package/dist/core/grizzco/engine/pipeline/pipeline.js +149 -240
- package/dist/core/grizzco/engine/transaction/attempt-failure.js +49 -24
- package/dist/core/grizzco/engine/transaction/authorization-summary.js +2 -1
- package/dist/core/grizzco/engine/transaction/transaction-runner.js +40 -34
- package/dist/core/grizzco/execution/RejectionManager.js +7 -5
- package/dist/core/grizzco/runtime/apply-back-runtime.js +5 -2
- package/dist/core/grizzco/services/implementations/default/GitConfigService.js +2 -1
- package/dist/core/grizzco/services/registry.js +18 -0
- package/dist/core/grizzco/steps/audit.js +20 -10
- package/dist/core/grizzco/steps/autopilot.js +21 -32
- package/dist/core/grizzco/steps/display-report.js +4 -11
- package/dist/core/grizzco/steps/explore.js +14 -4
- package/dist/core/grizzco/steps/generateReview.js +3 -1
- package/dist/core/grizzco/steps/patch/prompt-input.js +4 -1
- package/dist/core/grizzco/steps/patch.js +1 -0
- package/dist/core/grizzco/steps/plan.js +58 -49
- package/dist/core/grizzco/steps/research.js +3 -1
- package/dist/core/grizzco/steps/tool-runtime.js +3 -0
- package/dist/core/grizzco/steps/verify.js +7 -1
- package/dist/core/grizzco/validation/AstValidationService.js +3 -1
- package/dist/core/grizzco/workers/strata-sync-worker.js +2 -1
- package/dist/core/history/input-history.js +3 -1
- package/dist/core/intent/chat-intent.js +3 -1
- package/dist/core/llm/ai-sdk/message-mapper.js +37 -26
- package/dist/core/llm/ai-sdk/request-params.js +2 -6
- package/dist/core/llm/ai-sdk/result-mapper.js +14 -8
- package/dist/core/llm/ai-sdk/retry-classifier.js +17 -7
- package/dist/core/llm/ai-sdk/retry-executor.js +1 -1
- package/dist/core/llm/contracts/repair.js +16 -8
- package/dist/core/llm/errors.js +18 -14
- package/dist/core/llm/output-policy.js +8 -0
- package/dist/core/llm/redact.js +1 -3
- package/dist/core/llm/retry-utils.js +8 -2
- package/dist/core/llm/stream-utils.js +5 -3
- package/dist/core/llm/sub-agent-factory.js +51 -0
- package/dist/core/llm/tool-calling-stub.js +48 -0
- package/dist/core/llm/utils.js +17 -6
- package/dist/core/mcp/bridge/prompt-command-provider.js +4 -3
- package/dist/core/mcp/bridge/resource-context-provider.js +3 -1
- package/dist/core/mcp/bridge/tool-bridge.js +5 -14
- package/dist/core/mcp/catalog/discovery.js +3 -1
- package/dist/core/mcp/client/connection-manager.js +7 -4
- package/dist/core/mcp/client/transport-factory.js +7 -3
- package/dist/core/mcp/host/sampling-provider.js +1 -1
- package/dist/core/mcp/schema/json-schema-to-zod.js +2 -1
- package/dist/core/memory/relevant-retrieval.js +6 -4
- package/dist/core/observability/audit-file.js +2 -1
- package/dist/core/observability/audit-trail.js +3 -1
- package/dist/core/observability/authorization-decisions.js +13 -12
- package/dist/core/observability/error-mapping.js +2 -1
- package/dist/core/observability/logger.js +2 -1
- package/dist/core/observability/monitor.js +24 -0
- package/dist/core/observability/run-outcome-reporter.js +1 -0
- package/dist/core/observability/token-usage.js +5 -4
- package/dist/core/permission-gate/default-gate.js +5 -8
- package/dist/core/plan/storage.js +7 -4
- package/dist/core/plugin/loader.js +8 -5
- package/dist/core/prompts/registry.js +12 -30
- package/dist/core/prompts/runtime.js +3 -1
- package/dist/core/prompts/templates/system/autopilot_system.hbs +28 -4
- package/dist/core/protocols/a2a/sdk/executor.js +3 -1
- package/dist/core/protocols/a2a/sdk/server.js +5 -4
- package/dist/core/protocols/acp/acp-command-runner.js +7 -6
- package/dist/core/protocols/acp/acp-session-persistence.js +13 -10
- package/dist/core/protocols/acp/formal-agent.js +13 -6
- package/dist/core/protocols/acp/permission-provider.js +3 -2
- package/dist/core/protocols/acp/stdio-server.js +6 -6
- package/dist/core/reflection/engine.js +114 -14
- package/dist/core/runtime/agent-server-runtime.js +3 -2
- package/dist/core/runtime/batch-runner.js +81 -0
- package/dist/core/runtime/initialize.js +71 -6
- package/dist/core/runtime/loop-finalize.js +3 -0
- package/dist/core/runtime/loop-session-runner.js +5 -0
- package/dist/core/runtime/loop.js +4 -0
- package/dist/core/runtime/paths.js +9 -6
- package/dist/core/runtime/spawn-interactive.js +5 -4
- package/dist/core/security/redaction.js +3 -2
- package/dist/core/session/compaction/index.js +4 -3
- package/dist/core/session/compression.js +3 -1
- package/dist/core/session/manager.js +26 -38
- package/dist/core/session/pruning-strategy.js +2 -1
- package/dist/core/session/token-tracker.js +27 -9
- package/dist/core/skills/parser.js +3 -2
- package/dist/core/skills/permissions.js +2 -2
- package/dist/core/skills/runtime/MicroTaskRunner.js +1 -1
- package/dist/core/skills/runtime/SkillRunner.js +5 -2
- package/dist/core/slash/steps/slash-execute.js +7 -5
- package/dist/core/slash/strategy.js +1 -1
- package/dist/core/strata/checkpoint/manager.js +16 -10
- package/dist/core/strata/checkpoint/snapshot-create.js +5 -4
- package/dist/core/strata/checkpoint/snapshot-write-tree.js +7 -3
- package/dist/core/strata/engine/shadow-merge-engine.js +4 -2
- package/dist/core/strata/interaction/file-system-provider.js +2 -1
- package/dist/core/strata/layers/file-state-resolver.js +9 -7
- package/dist/core/strata/layers/immutable-git-layer.js +3 -1
- package/dist/core/strata/layers/shadow-driver/readonly-lock.js +8 -6
- package/dist/core/strata/layers/shadow-driver/shadow-driver.js +2 -1
- package/dist/core/strata/layers/worktree.js +9 -10
- package/dist/core/strata/runtime/environment.js +2 -1
- package/dist/core/strata/runtime/synchronizer.js +28 -26
- package/dist/core/streaming/canonical/parts-from-llm-stream-chunk.js +1 -11
- package/dist/core/structured-output/json-extract.js +3 -1
- package/dist/core/structured-output/json-schema-validator.js +1 -13
- package/dist/core/sub-agent/artifacts/store.js +2 -1
- package/dist/core/sub-agent/context-snapshot.js +12 -6
- package/dist/core/sub-agent/controller.js +70 -1
- package/dist/core/sub-agent/core/loop.js +25 -3
- package/dist/core/sub-agent/core/manager.js +343 -117
- package/dist/core/sub-agent/registry-defaults.js +12 -0
- package/dist/core/sub-agent/registry.js +8 -0
- package/dist/core/sub-agent/summary.js +96 -0
- package/dist/core/sub-agent/team.js +98 -0
- package/dist/core/sub-agent/tools/task-await.js +109 -0
- package/dist/core/sub-agent/tools/task-spawn.js +52 -7
- package/dist/core/sub-agent/tools/team.js +92 -0
- package/dist/core/sub-agent/types.js +11 -2
- package/dist/core/target-runtime/profile.js +3 -1
- package/dist/core/tools/audit.js +3 -2
- package/dist/core/tools/budget.js +7 -12
- package/dist/core/tools/builtin/ast.js +144 -0
- package/dist/core/tools/builtin/code-search/backends/powershell.js +3 -1
- package/dist/core/tools/builtin/code-search/backends/rg.js +3 -1
- package/dist/core/tools/builtin/code-search/executor.js +46 -43
- package/dist/core/tools/builtin/code-search/parse/plain-grep.js +3 -1
- package/dist/core/tools/builtin/code-search/parse/rg-json.js +3 -1
- package/dist/core/tools/builtin/fs.js +90 -7
- package/dist/core/tools/builtin/git.js +242 -0
- package/dist/core/tools/builtin/glob.js +79 -0
- package/dist/core/tools/builtin/index.js +53 -111
- package/dist/core/tools/builtin/interaction.js +13 -15
- package/dist/core/tools/builtin/knowledge.js +146 -4
- package/dist/core/tools/builtin/proposal.js +14 -3
- package/dist/core/tools/builtin/verify.js +35 -3
- package/dist/core/tools/capability/executor.js +5 -5
- package/dist/core/tools/headless-payload.js +1 -3
- package/dist/core/tools/mapper.js +8 -42
- package/dist/core/tools/parallel/persistence.js +17 -5
- package/dist/core/tools/parallel/scheduler.js +23 -21
- package/dist/core/tools/permissions/permission-rules.js +69 -115
- package/dist/core/tools/plugins/loader.js +4 -3
- package/dist/core/tools/router.js +112 -58
- package/dist/core/tools/session.js +64 -102
- package/dist/core/tools/streaming/ToolCallAccumulator.js +1 -3
- package/dist/core/tools/tool-visibility.js +2 -1
- package/dist/core/tools/types.js +10 -0
- package/dist/core/types/batch.js +2 -0
- package/dist/core/utils/error.js +79 -0
- package/dist/core/utils/sanitizer.js +5 -2
- package/dist/core/utils/serialize.js +66 -0
- package/dist/core/utils/zod.js +29 -0
- package/dist/core/verification/detect-runner.js +86 -0
- package/dist/core/verification/runner.js +76 -0
- package/dist/core/version.js +3 -1
- package/dist/core/workspace/capabilities.js +3 -2
- package/dist/integrations/langfuse/litellm-langfuse-outcome-reporter.js +9 -8
- package/dist/languages/python/index.js +154 -0
- package/dist/locales/en.js +8 -1
- package/package.json +2 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test runner detection and JSON flag injection.
|
|
3
|
+
*
|
|
4
|
+
* Detects which test runner a command invokes and, when the runner
|
|
5
|
+
* supports structured JSON output, rewrites the command to emit JSON
|
|
6
|
+
* so downstream parsers can consume machine-readable data instead of
|
|
7
|
+
* regex-matching human-friendly text.
|
|
8
|
+
*/
|
|
9
|
+
// ── Detection ────────────────────────────────────────────────────────────────
|
|
10
|
+
/** Heuristic detection from the raw command string. */
|
|
11
|
+
export function detectRunner(command) {
|
|
12
|
+
const cmd = command.toLowerCase();
|
|
13
|
+
// Order matters: more specific patterns first
|
|
14
|
+
if (/\bvitest\b/.test(cmd))
|
|
15
|
+
return 'vitest';
|
|
16
|
+
if (/\bjest\b/.test(cmd))
|
|
17
|
+
return 'jest';
|
|
18
|
+
if (/\bpytest\b/.test(cmd) || /\bpy\.test\b/.test(cmd))
|
|
19
|
+
return 'pytest';
|
|
20
|
+
if (/\btsc\b/.test(cmd))
|
|
21
|
+
return 'tsc';
|
|
22
|
+
if (/\beslint\b/.test(cmd))
|
|
23
|
+
return 'eslint';
|
|
24
|
+
if (/\bbun\s+test\b/.test(cmd))
|
|
25
|
+
return 'bun';
|
|
26
|
+
if (/\bgo\s+test\b/.test(cmd))
|
|
27
|
+
return 'go';
|
|
28
|
+
// npm/pnpm/yarn script proxies — try to infer from script name
|
|
29
|
+
if (/\bnpm\s+run\s+/.test(cmd) || /\bpnpm\s+/.test(cmd) || /\byarn\s+/.test(cmd)) {
|
|
30
|
+
if (/test:unit|test:e2e|test:integration|test:full/.test(cmd))
|
|
31
|
+
return 'unknown';
|
|
32
|
+
if (/\btest\b/.test(cmd))
|
|
33
|
+
return 'unknown'; // could be anything
|
|
34
|
+
}
|
|
35
|
+
return 'unknown';
|
|
36
|
+
}
|
|
37
|
+
// ── JSON flag injection ──────────────────────────────────────────────────────
|
|
38
|
+
/** Returns true when the runner supports structured JSON output. */
|
|
39
|
+
export function supportsJsonOutput(runner) {
|
|
40
|
+
return (runner === 'jest' ||
|
|
41
|
+
runner === 'vitest' ||
|
|
42
|
+
runner === 'eslint' ||
|
|
43
|
+
runner === 'bun' ||
|
|
44
|
+
runner === 'go');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Rewrite the command to emit structured output.
|
|
48
|
+
*
|
|
49
|
+
* Only modifies commands for runners that support JSON.
|
|
50
|
+
* Returns the original command unchanged when the runner
|
|
51
|
+
* has no JSON mode (pytest, tsc) or is unknown.
|
|
52
|
+
*/
|
|
53
|
+
export function injectJsonFlags(command, runner) {
|
|
54
|
+
switch (runner) {
|
|
55
|
+
case 'jest':
|
|
56
|
+
// jest --json --outputFile=/dev/null would suppress file write;
|
|
57
|
+
// but --json alone prints to stdout which is what we want.
|
|
58
|
+
// Avoid duplicating --json if already present.
|
|
59
|
+
if (command.includes('--json'))
|
|
60
|
+
return command;
|
|
61
|
+
return `${command} --json`;
|
|
62
|
+
case 'vitest':
|
|
63
|
+
// vitest --reporter=json --run (--run prevents watch mode)
|
|
64
|
+
if (command.includes('--reporter=json') || command.includes('--reporter json'))
|
|
65
|
+
return command;
|
|
66
|
+
return `${command} --reporter=json --run`;
|
|
67
|
+
case 'eslint':
|
|
68
|
+
// eslint --format json
|
|
69
|
+
if (command.includes('--format json') || command.includes('--format=json'))
|
|
70
|
+
return command;
|
|
71
|
+
return `${command} --format json`;
|
|
72
|
+
case 'bun':
|
|
73
|
+
// bun test --json (outputs NDJSON to stdout)
|
|
74
|
+
if (command.includes('--json'))
|
|
75
|
+
return command;
|
|
76
|
+
return `${command} --json`;
|
|
77
|
+
case 'go':
|
|
78
|
+
// go test -json
|
|
79
|
+
if (command.includes('-json'))
|
|
80
|
+
return command;
|
|
81
|
+
return `${command} -json`;
|
|
82
|
+
default:
|
|
83
|
+
return command;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=detect-runner.js.map
|
|
@@ -90,6 +90,82 @@ export function isRetryable(error) {
|
|
|
90
90
|
return false;
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Extract test summary counts from runner output.
|
|
95
|
+
* Supports jest/vitest, pytest, bun test, and go test formats.
|
|
96
|
+
*/
|
|
97
|
+
export function parseTestSummary(output) {
|
|
98
|
+
const lower = output.toLowerCase();
|
|
99
|
+
let passed = 0;
|
|
100
|
+
let failed = 0;
|
|
101
|
+
let skipped = 0;
|
|
102
|
+
let found = false;
|
|
103
|
+
// jest/vitest: "Tests: 2 failed, 5 passed, 7 total"
|
|
104
|
+
const jestMatch = lower.match(/tests:\s+(?:(\d+)\s+failed,\s*)?(?:(\d+)\s+passed,\s*)?(\d+)\s+total/);
|
|
105
|
+
if (jestMatch) {
|
|
106
|
+
failed = parseInt(jestMatch[1] || '0');
|
|
107
|
+
passed = parseInt(jestMatch[2] || '0');
|
|
108
|
+
const total = parseInt(jestMatch[3]);
|
|
109
|
+
skipped = total - passed - failed;
|
|
110
|
+
found = true;
|
|
111
|
+
}
|
|
112
|
+
// jest/vitest: "Test Suites: 1 failed, 3 passed, 4 total"
|
|
113
|
+
if (!found) {
|
|
114
|
+
const suiteMatch = lower.match(/test suites:\s+(?:(\d+)\s+failed,\s*)?(?:(\d+)\s+passed,\s*)?(\d+)\s+total/);
|
|
115
|
+
if (suiteMatch) {
|
|
116
|
+
failed = parseInt(suiteMatch[1] || '0');
|
|
117
|
+
passed = parseInt(suiteMatch[2] || '0');
|
|
118
|
+
const total = parseInt(suiteMatch[3]);
|
|
119
|
+
skipped = total - passed - failed;
|
|
120
|
+
found = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// pytest: "2 failed, 5 passed"
|
|
124
|
+
if (!found) {
|
|
125
|
+
const pytestMatch = lower.match(/(?:(\d+)\s+failed,?\s*)?(?:(\d+)\s+passed,?\s*)?(?:(\d+)\s+skipped)?/);
|
|
126
|
+
if (pytestMatch && (pytestMatch[1] || pytestMatch[2])) {
|
|
127
|
+
failed = parseInt(pytestMatch[1] || '0');
|
|
128
|
+
passed = parseInt(pytestMatch[2] || '0');
|
|
129
|
+
skipped = parseInt(pytestMatch[3] || '0');
|
|
130
|
+
found = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// bun test: "5 pass | 1 fail"
|
|
134
|
+
if (!found) {
|
|
135
|
+
const bunMatch = lower.match(/(\d+)\s+pass\s*\|\s*(\d+)\s+fail/);
|
|
136
|
+
if (bunMatch) {
|
|
137
|
+
passed = parseInt(bunMatch[1]);
|
|
138
|
+
failed = parseInt(bunMatch[2]);
|
|
139
|
+
found = true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// go test: "ok" / "FAIL" with counts
|
|
143
|
+
if (!found) {
|
|
144
|
+
const goMatch = lower.match(/(\d+)\s+of\s+(\d+)\s+tests?\s+passed/);
|
|
145
|
+
if (goMatch) {
|
|
146
|
+
passed = parseInt(goMatch[1]);
|
|
147
|
+
const total = parseInt(goMatch[2]);
|
|
148
|
+
failed = total - passed;
|
|
149
|
+
found = true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Generic: "N passed" / "N failed" / "N skipped"
|
|
153
|
+
if (!found) {
|
|
154
|
+
const passMatch = lower.match(/(\d+)\s+pass(?:ed|ing)/);
|
|
155
|
+
const failMatch = lower.match(/(\d+)\s+fail(?:ed|ing)/);
|
|
156
|
+
const skipMatch = lower.match(/(\d+)\s+skip(?:ped)/);
|
|
157
|
+
if (passMatch || failMatch) {
|
|
158
|
+
passed = passMatch ? parseInt(passMatch[1]) : 0;
|
|
159
|
+
failed = failMatch ? parseInt(failMatch[1]) : 0;
|
|
160
|
+
skipped = skipMatch ? parseInt(skipMatch[1]) : 0;
|
|
161
|
+
found = true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!found)
|
|
165
|
+
return undefined;
|
|
166
|
+
const total = passed + failed + Math.max(skipped, 0);
|
|
167
|
+
return { total, passed, failed, skipped: Math.max(skipped, 0) };
|
|
168
|
+
}
|
|
93
169
|
export async function runCommand(repoPath, command, timeoutMs, env, signal) {
|
|
94
170
|
const shell = getPlatformShellInvocation(command);
|
|
95
171
|
let output = '';
|
package/dist/core/version.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
|
+
import { getLogger } from './observability/logger.js';
|
|
2
3
|
const require = createRequire(import.meta.url);
|
|
3
4
|
function readPackageVersion() {
|
|
4
5
|
try {
|
|
@@ -7,8 +8,9 @@ function readPackageVersion() {
|
|
|
7
8
|
return pkg.version;
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
|
-
catch {
|
|
11
|
+
catch (error) {
|
|
11
12
|
// Fall back for non-package runtime embeddings.
|
|
13
|
+
getLogger().debug(`[Version] Failed to read package.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
12
14
|
}
|
|
13
15
|
return '0.0.0';
|
|
14
16
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { access, constants } from '../adapters/fs/node-fs.js';
|
|
2
2
|
import { GitAdapter } from '../adapters/git/git-adapter.js';
|
|
3
3
|
import { LIMITS } from '../config/limits.js';
|
|
4
|
+
import { errorMessage } from '../utils/error.js';
|
|
4
5
|
const PROBE_LIMITS = { maxStdoutBytes: 4_096, maxStderrChars: 4_096 };
|
|
5
6
|
function gitFailureReason(result) {
|
|
6
7
|
if (result.error?.code === 'ENOENT')
|
|
@@ -23,7 +24,7 @@ async function detectFileSystemCapability(workspacePath) {
|
|
|
23
24
|
return {
|
|
24
25
|
readable: false,
|
|
25
26
|
writable: false,
|
|
26
|
-
reason:
|
|
27
|
+
reason: errorMessage(error),
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
try {
|
|
@@ -34,7 +35,7 @@ async function detectFileSystemCapability(workspacePath) {
|
|
|
34
35
|
return {
|
|
35
36
|
readable: true,
|
|
36
37
|
writable: false,
|
|
37
|
-
reason:
|
|
38
|
+
reason: errorMessage(error),
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from '../../core/adapters/fs/node-fs.js';
|
|
2
2
|
import { recordAuditEvent } from '../../core/observability/audit-trail.js';
|
|
3
3
|
import { getLogger } from '../../core/observability/logger.js';
|
|
4
|
+
import { isRecord } from '../../core/utils/serialize.js';
|
|
4
5
|
import { text } from '../../locales/index.js';
|
|
5
6
|
function nowIso() {
|
|
6
7
|
return new Date().toISOString();
|
|
@@ -43,7 +44,7 @@ function buildPhaseDurations(traces) {
|
|
|
43
44
|
return undefined;
|
|
44
45
|
const out = {};
|
|
45
46
|
for (const t of traces) {
|
|
46
|
-
if (!t
|
|
47
|
+
if (!isRecord(t))
|
|
47
48
|
continue;
|
|
48
49
|
const name = t.name;
|
|
49
50
|
const duration = t.duration;
|
|
@@ -69,13 +70,13 @@ async function tryReadAuditJson(auditPath) {
|
|
|
69
70
|
}
|
|
70
71
|
function extractNetworkErrorCode(error) {
|
|
71
72
|
const allow = (value) => typeof value === 'string' && /^[A-Z0-9_]{2,32}$/.test(value) ? value : undefined;
|
|
72
|
-
if (error
|
|
73
|
-
const
|
|
74
|
-
return (allow(
|
|
75
|
-
allow(
|
|
76
|
-
allow(
|
|
77
|
-
allow(
|
|
78
|
-
allow(
|
|
73
|
+
if (isRecord(error)) {
|
|
74
|
+
const cause = isRecord(error.cause) ? error.cause : undefined;
|
|
75
|
+
return (allow(error.code) ||
|
|
76
|
+
allow(cause?.code) ||
|
|
77
|
+
allow(cause?.errno) ||
|
|
78
|
+
allow(error.errno) ||
|
|
79
|
+
allow(cause?.name));
|
|
79
80
|
}
|
|
80
81
|
return undefined;
|
|
81
82
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { syncFs as fs } from '../../core/adapters/fs/node-fs.js';
|
|
5
|
+
import { ErrorType } from '../../core/types/index.js';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const queries = {
|
|
9
|
+
definitions: `
|
|
10
|
+
(function_definition name: (identifier) @name) @def
|
|
11
|
+
(class_definition name: (identifier) @name) @def
|
|
12
|
+
(decorated_definition
|
|
13
|
+
definition: (function_definition name: (identifier) @name)) @def
|
|
14
|
+
(decorated_definition
|
|
15
|
+
definition: (class_definition name: (identifier) @name)) @def
|
|
16
|
+
`,
|
|
17
|
+
references: `
|
|
18
|
+
(call function: (identifier) @name) @ref
|
|
19
|
+
(attribute object: (identifier) @name) @ref
|
|
20
|
+
`,
|
|
21
|
+
};
|
|
22
|
+
export const pythonPlugin = {
|
|
23
|
+
meta: {
|
|
24
|
+
id: 'python',
|
|
25
|
+
name: 'Python',
|
|
26
|
+
extensions: ['.py', '.pyw', '.pyi'],
|
|
27
|
+
capabilities: {
|
|
28
|
+
levels: {
|
|
29
|
+
l1Parsing: true,
|
|
30
|
+
l2Symbols: true,
|
|
31
|
+
l3Flow: true,
|
|
32
|
+
},
|
|
33
|
+
ast: {
|
|
34
|
+
strictValidation: true,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
detection: {
|
|
39
|
+
matches: async (repoPath) => {
|
|
40
|
+
const markers = ['pyproject.toml', 'setup.py', 'requirements.txt', 'Pipfile'];
|
|
41
|
+
return markers.some((m) => fs.existsSync(path.join(repoPath, m)));
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
parsing: {
|
|
45
|
+
getTreeSitterWasm: async () => {
|
|
46
|
+
const searchPaths = [path.resolve(moduleDir, '../../../bin', 'tree-sitter-python.wasm')];
|
|
47
|
+
try {
|
|
48
|
+
const pkgPath = path.dirname(require.resolve('tree-sitter-python/package.json'));
|
|
49
|
+
searchPaths.push(path.join(pkgPath, 'tree-sitter-python.wasm'));
|
|
50
|
+
}
|
|
51
|
+
catch (_e) {
|
|
52
|
+
// ignore
|
|
53
|
+
}
|
|
54
|
+
for (const p of searchPaths) {
|
|
55
|
+
if (fs.existsSync(p)) {
|
|
56
|
+
return p;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return searchPaths[0];
|
|
60
|
+
},
|
|
61
|
+
queries,
|
|
62
|
+
queryPack: {
|
|
63
|
+
version: '1.0.0',
|
|
64
|
+
symbols: {
|
|
65
|
+
calls: `
|
|
66
|
+
(call function: (identifier) @callee)
|
|
67
|
+
(call function: (attribute attribute: (identifier) @callee))
|
|
68
|
+
`,
|
|
69
|
+
},
|
|
70
|
+
flow: {
|
|
71
|
+
control: `
|
|
72
|
+
(if_statement) @branch
|
|
73
|
+
(elif_clause) @branch
|
|
74
|
+
(for_statement) @loop
|
|
75
|
+
(while_statement) @loop
|
|
76
|
+
(await) @async
|
|
77
|
+
`,
|
|
78
|
+
exceptions: `
|
|
79
|
+
(try_statement) @trycatch
|
|
80
|
+
(raise_statement) @throw
|
|
81
|
+
(except_clause) @catch
|
|
82
|
+
`,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
dependency: {
|
|
87
|
+
extractImports: (content) => {
|
|
88
|
+
const dependencies = [];
|
|
89
|
+
// from .foo import bar / from ..foo import bar
|
|
90
|
+
const fromPattern = /from\s+(\.\.+[.\w/]*)\s+import/g;
|
|
91
|
+
// import .foo (rare but valid in some contexts)
|
|
92
|
+
const importPattern = /(?:^|\s)import\s+(\.\.+[.\w/]*)/gm;
|
|
93
|
+
let match;
|
|
94
|
+
while ((match = fromPattern.exec(content)) !== null) {
|
|
95
|
+
if (match[1])
|
|
96
|
+
dependencies.push(match[1]);
|
|
97
|
+
}
|
|
98
|
+
while ((match = importPattern.exec(content)) !== null) {
|
|
99
|
+
if (match[1])
|
|
100
|
+
dependencies.push(match[1]);
|
|
101
|
+
}
|
|
102
|
+
return dependencies;
|
|
103
|
+
},
|
|
104
|
+
resolvePath: (_basePath, importPath) => {
|
|
105
|
+
if (!importPath.endsWith('.py') && !importPath.endsWith('.pyi')) {
|
|
106
|
+
return importPath + '.py';
|
|
107
|
+
}
|
|
108
|
+
return importPath;
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
diagnostics: {
|
|
112
|
+
classifyError: (output) => {
|
|
113
|
+
const lower = output.toLowerCase();
|
|
114
|
+
// Dependency errors
|
|
115
|
+
if (lower.includes('modulenotfounderror') ||
|
|
116
|
+
lower.includes('importerror') ||
|
|
117
|
+
lower.includes('no module named') ||
|
|
118
|
+
lower.includes('pip install')) {
|
|
119
|
+
return ErrorType.DEPENDENCY_ERROR;
|
|
120
|
+
}
|
|
121
|
+
// Compilation / syntax errors
|
|
122
|
+
if (lower.includes('syntaxerror') ||
|
|
123
|
+
lower.includes('indentationerror') ||
|
|
124
|
+
lower.includes('taberror') ||
|
|
125
|
+
lower.includes('failed to compile') ||
|
|
126
|
+
lower.includes('py_compile')) {
|
|
127
|
+
return ErrorType.COMPILATION;
|
|
128
|
+
}
|
|
129
|
+
// Test errors
|
|
130
|
+
if (lower.includes('pytest') ||
|
|
131
|
+
lower.includes('unittest') ||
|
|
132
|
+
lower.includes('assertionerror') ||
|
|
133
|
+
lower.includes('assert ') ||
|
|
134
|
+
lower.includes('test failed') ||
|
|
135
|
+
lower.includes('tests failed') ||
|
|
136
|
+
lower.includes('failed tests') ||
|
|
137
|
+
lower.includes(' ERRORS') ||
|
|
138
|
+
lower.includes(' FAILURES')) {
|
|
139
|
+
return ErrorType.TEST;
|
|
140
|
+
}
|
|
141
|
+
// Lint errors
|
|
142
|
+
if (lower.includes('pylint') ||
|
|
143
|
+
lower.includes('flake8') ||
|
|
144
|
+
lower.includes('mypy') ||
|
|
145
|
+
lower.includes('ruff') ||
|
|
146
|
+
lower.includes('pycodestyle') ||
|
|
147
|
+
lower.includes('pyflakes')) {
|
|
148
|
+
return ErrorType.LINT;
|
|
149
|
+
}
|
|
150
|
+
return undefined;
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=index.js.map
|
package/dist/locales/en.js
CHANGED
|
@@ -576,11 +576,17 @@ Please return the patch in PURE unified diff format:`;
|
|
|
576
576
|
fsListDirectoryDescription: 'List directory entries (files and subdirectories) under a repository path',
|
|
577
577
|
fsListFilesDescription: 'List files (excluding subdirectories) under a repository path',
|
|
578
578
|
fsWriteFileDescription: 'Write a UTF-8 text file atomically (slash-only)',
|
|
579
|
+
fsEditFileDescription: 'Replace exact occurrences of a string in a file (slash-only). old_string must appear exactly once unless replace_all is set.',
|
|
579
580
|
fsCreateDirectoryDescription: 'Create a directory under the repository root (slash-only)',
|
|
580
581
|
fsDeleteFileDescription: 'Delete a file under the repository root (slash-only)',
|
|
581
582
|
gitStatusDescription: 'Show the working tree status',
|
|
582
583
|
gitCatDescription: 'Read file content from a specific git revision',
|
|
584
|
+
gitBlameDescription: 'Show line-by-line authorship for a file',
|
|
585
|
+
gitLogDescription: 'Show structured commit history',
|
|
586
|
+
gitShowDescription: 'Show commit details and diff',
|
|
587
|
+
globFindDescription: 'Find files matching a glob pattern (respects .gitignore)',
|
|
583
588
|
codeAstDescription: 'Query AST definitions and references for symbols',
|
|
589
|
+
codeFindReferencesDescription: 'Find all references to a symbol across the codebase using ripgrep pre-filter + tree-sitter precise matching',
|
|
584
590
|
testRunDescription: 'Run verification command (test/lint/build) and classify errors',
|
|
585
591
|
shellExecDescription: 'Execute a shell command in an isolated workspace (slash-only)',
|
|
586
592
|
artifactReadDescription: 'Read salmonloop (s8p) artifacts by handle',
|
|
@@ -770,7 +776,8 @@ Please return the patch in PURE unified diff format:`;
|
|
|
770
776
|
missionFailedWithReason: (reason) => `Smallfry mission failed: ${reason}`,
|
|
771
777
|
},
|
|
772
778
|
ui: {
|
|
773
|
-
spawnToolDescription: 'Delegate a concrete sub-task to a specialized sub-agent. This is not a no-argument action: always provide agent_ref and task. Use agent_ref="explorer" for read-only investigation, "reviewer" for audit, "surgeon" for implementation proposals, or "cleaner" for lint/format cleanup. Keep task self-contained with relevant files and the exact deliverable. Omit session_target unless shared context is explicitly required.',
|
|
779
|
+
spawnToolDescription: 'Delegate a concrete sub-task to a specialized sub-agent. This is not a no-argument action: always provide agent_ref and task. Use agent_ref="explorer" for read-only investigation, "reviewer" for audit, "surgeon" for implementation proposals, or "cleaner" for lint/format cleanup. Keep task self-contained with relevant files and the exact deliverable. Omit session_target unless shared context is explicitly required. Set async=true to get a handle immediately and use agent_await to collect the result later.',
|
|
780
|
+
awaitToolDescription: 'Wait for an async sub-agent (spawned with agent_dispatch async=true) to complete and return its result. Pass the agentId from the dispatch handle.',
|
|
774
781
|
progressTitle: (id) => `[Smallfry: ${id}]`,
|
|
775
782
|
},
|
|
776
783
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "salmon-loop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A chat-first coding agent CLI for safe, reviewable repository changes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -154,6 +154,7 @@
|
|
|
154
154
|
"react": "^19.2.4",
|
|
155
155
|
"tiktoken": "^1.0.22",
|
|
156
156
|
"tree-sitter-javascript": "^0.25.0",
|
|
157
|
+
"tree-sitter-python": "^0.25.0",
|
|
157
158
|
"web-tree-sitter": "0.26.3",
|
|
158
159
|
"yaml": "^2.8.2",
|
|
159
160
|
"zod": "^4.3.6",
|