xcoder-dna 0.0.0-stage → 1.3.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/LICENSE +66 -0
- package/README.md +87 -2
- package/dist/agents/ai-chat-debate.d.ts +36 -0
- package/dist/agents/ai-chat-debate.js +300 -0
- package/dist/agents/ai-chat-report-writer.d.ts +7 -0
- package/dist/agents/ai-chat-report-writer.js +100 -0
- package/dist/agents/duplicate-detector.d.ts +41 -0
- package/dist/agents/duplicate-detector.js +65 -0
- package/dist/agents/genesis-agent.d.ts +61 -0
- package/dist/agents/genesis-agent.js +340 -0
- package/dist/agents/samurai-agent.d.ts +53 -0
- package/dist/agents/samurai-agent.js +279 -0
- package/dist/agents/sub-agent.d.ts +56 -0
- package/dist/agents/sub-agent.js +353 -0
- package/dist/backlog/backlog-requirement-validator.d.ts +10 -0
- package/dist/backlog/backlog-requirement-validator.js +60 -0
- package/dist/backlog/backlog-store.d.ts +29 -0
- package/dist/backlog/backlog-store.js +148 -0
- package/dist/backlog/po-agent.d.ts +16 -0
- package/dist/backlog/po-agent.js +121 -0
- package/dist/backlog/types.d.ts +25 -0
- package/dist/backlog/types.js +4 -0
- package/dist/bench/run.d.ts +11 -0
- package/dist/bench/run.js +185 -0
- package/dist/bench/types.d.ts +50 -0
- package/dist/bench/types.js +1 -0
- package/dist/cli/swarm.d.ts +10 -0
- package/dist/cli/swarm.js +100 -0
- package/dist/cli/task.d.ts +14 -0
- package/dist/cli/task.js +121 -0
- package/dist/cli/wbc-arg.d.ts +6 -0
- package/dist/cli/wbc-arg.js +40 -0
- package/dist/cli/wbc.d.ts +45 -0
- package/dist/cli/wbc.js +98 -0
- package/dist/commands/license.d.ts +9 -0
- package/dist/commands/license.js +140 -0
- package/dist/commands/release.d.ts +44 -0
- package/dist/commands/release.js +180 -0
- package/dist/commands/test.d.ts +50 -0
- package/dist/commands/test.js +385 -0
- package/dist/config/loadConfig.d.ts +82 -0
- package/dist/config/loadConfig.js +240 -0
- package/dist/config/secure-mode.d.ts +29 -0
- package/dist/config/secure-mode.js +63 -0
- package/dist/config/user-config.d.ts +39 -0
- package/dist/config/user-config.js +202 -0
- package/dist/engine/reasoning-engine.d.ts +21 -0
- package/dist/engine/reasoning-engine.js +59 -0
- package/dist/events/bus.d.ts +22 -0
- package/dist/events/bus.js +18 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +77 -0
- package/dist/indexing/ignoreRules.d.ts +1 -0
- package/dist/indexing/ignoreRules.js +53 -0
- package/dist/instance-swarm/instance-swarm-orchestrator.d.ts +57 -0
- package/dist/instance-swarm/instance-swarm-orchestrator.js +204 -0
- package/dist/instance-swarm/protocol-name-resolver.d.ts +23 -0
- package/dist/instance-swarm/protocol-name-resolver.js +64 -0
- package/dist/license/gate.d.ts +31 -0
- package/dist/license/gate.js +188 -0
- package/dist/license/http-client.d.ts +43 -0
- package/dist/license/http-client.js +195 -0
- package/dist/license/manager.d.ts +39 -0
- package/dist/license/manager.js +175 -0
- package/dist/license/receipt.d.ts +27 -0
- package/dist/license/receipt.js +108 -0
- package/dist/license/state-codec.d.ts +72 -0
- package/dist/license/state-codec.js +218 -0
- package/dist/license/state-paths.d.ts +17 -0
- package/dist/license/state-paths.js +64 -0
- package/dist/license/store.d.ts +44 -0
- package/dist/license/store.js +223 -0
- package/dist/license/trust-anchor.d.ts +24 -0
- package/dist/license/trust-anchor.js +74 -0
- package/dist/license/types.d.ts +34 -0
- package/dist/license/types.js +19 -0
- package/dist/llm/budget.d.ts +23 -0
- package/dist/llm/budget.js +58 -0
- package/dist/llm/client.d.ts +53 -0
- package/dist/llm/client.js +122 -0
- package/dist/llm/factory.d.ts +14 -0
- package/dist/llm/factory.js +32 -0
- package/dist/orchestrator/defect-logger.d.ts +31 -0
- package/dist/orchestrator/defect-logger.js +101 -0
- package/dist/orchestrator/defect-manager.d.ts +118 -0
- package/dist/orchestrator/defect-manager.js +338 -0
- package/dist/orchestrator/defect-migration.d.ts +14 -0
- package/dist/orchestrator/defect-migration.js +174 -0
- package/dist/orchestrator/defect-store.d.ts +48 -0
- package/dist/orchestrator/defect-store.js +76 -0
- package/dist/orchestrator/healing.d.ts +14 -0
- package/dist/orchestrator/healing.js +11 -0
- package/dist/orchestrator/interactive-steering.d.ts +64 -0
- package/dist/orchestrator/interactive-steering.js +107 -0
- package/dist/orchestrator/interrupt-handler.d.ts +30 -0
- package/dist/orchestrator/interrupt-handler.js +70 -0
- package/dist/orchestrator/lesson-manager.d.ts +25 -0
- package/dist/orchestrator/lesson-manager.js +122 -0
- package/dist/orchestrator/progress-summary.d.ts +15 -0
- package/dist/orchestrator/progress-summary.js +106 -0
- package/dist/orchestrator/reasoning-score.d.ts +49 -0
- package/dist/orchestrator/reasoning-score.js +117 -0
- package/dist/orchestrator/swarm.d.ts +36 -0
- package/dist/orchestrator/swarm.js +77 -0
- package/dist/orchestrator/task-factory.d.ts +15 -0
- package/dist/orchestrator/task-factory.js +176 -0
- package/dist/orchestrator/validation-gate.d.ts +23 -0
- package/dist/orchestrator/validation-gate.js +155 -0
- package/dist/protocol/react-contract.d.ts +136 -0
- package/dist/protocol/react-contract.js +698 -0
- package/dist/protocol/wks-protocol-manager.d.ts +22 -0
- package/dist/protocol/wks-protocol-manager.js +72 -0
- package/dist/sdlc-runtime/conversation-runner.d.ts +8 -0
- package/dist/sdlc-runtime/conversation-runner.js +64 -0
- package/dist/sdlc-runtime/dag-utils.d.ts +15 -0
- package/dist/sdlc-runtime/dag-utils.js +67 -0
- package/dist/sdlc-runtime/defect-integration.d.ts +7 -0
- package/dist/sdlc-runtime/defect-integration.js +99 -0
- package/dist/sdlc-runtime/execution-loop.d.ts +38 -0
- package/dist/sdlc-runtime/execution-loop.js +274 -0
- package/dist/sdlc-runtime/lesson-recorder.d.ts +9 -0
- package/dist/sdlc-runtime/lesson-recorder.js +27 -0
- package/dist/sdlc-runtime/plan-initializer.d.ts +47 -0
- package/dist/sdlc-runtime/plan-initializer.js +135 -0
- package/dist/sdlc-runtime/progress-persister.d.ts +10 -0
- package/dist/sdlc-runtime/progress-persister.js +34 -0
- package/dist/sdlc-runtime/protocol-loader.d.ts +10 -0
- package/dist/sdlc-runtime/protocol-loader.js +21 -0
- package/dist/sdlc-runtime/reporting.d.ts +24 -0
- package/dist/sdlc-runtime/reporting.js +68 -0
- package/dist/sdlc-runtime/sdlc-orchestrator.d.ts +59 -0
- package/dist/sdlc-runtime/sdlc-orchestrator.js +170 -0
- package/dist/sdlc-runtime/sdlc-skill.d.ts +44 -0
- package/dist/sdlc-runtime/sdlc-skill.js +148 -0
- package/dist/sdlc-runtime/steering-handler.d.ts +14 -0
- package/dist/sdlc-runtime/steering-handler.js +51 -0
- package/dist/sdlc-runtime/trace-recorder.d.ts +10 -0
- package/dist/sdlc-runtime/trace-recorder.js +7 -0
- package/dist/security/index.d.ts +16 -0
- package/dist/security/index.js +12 -0
- package/dist/security/input-screen.d.ts +30 -0
- package/dist/security/input-screen.js +75 -0
- package/dist/security/tool-call-guard.d.ts +16 -0
- package/dist/security/tool-call-guard.js +163 -0
- package/dist/security/untrusted-content.d.ts +34 -0
- package/dist/security/untrusted-content.js +63 -0
- package/dist/skills/advanced-dfir/SKILL.md +85 -0
- package/dist/skills/advanced-pentest-lab/SKILL.md +88 -0
- package/dist/skills/analyst/SKILL.md +55 -0
- package/dist/skills/architect/SKILL.md +54 -0
- package/dist/skills/aws/SKILL.md +92 -0
- package/dist/skills/azure/SKILL.md +90 -0
- package/dist/skills/bylaw-context.d.ts +45 -0
- package/dist/skills/bylaw-context.js +61 -0
- package/dist/skills/bylaw-review.d.ts +20 -0
- package/dist/skills/bylaw-review.js +101 -0
- package/dist/skills/conversation/SKILL.md +29 -0
- package/dist/skills/design-patterns/SKILL.md +190 -0
- package/dist/skills/devops/SKILL.md +55 -0
- package/dist/skills/directives.d.ts +20 -0
- package/dist/skills/directives.js +313 -0
- package/dist/skills/docker/SKILL.md +76 -0
- package/dist/skills/docker-expert/SKILL.md +56 -0
- package/dist/skills/enterprise-systems-analyst/SKILL.md +83 -0
- package/dist/skills/enterprise-systems-analyst/references/crm.md +41 -0
- package/dist/skills/enterprise-systems-analyst/references/erp.md +40 -0
- package/dist/skills/enterprise-systems-analyst/references/financial-systems.md +35 -0
- package/dist/skills/enterprise-systems-analyst/references/hcm.md +38 -0
- package/dist/skills/enterprise-systems-analyst/references/mrp.md +37 -0
- package/dist/skills/enterprise-systems-analyst/references/scm.md +32 -0
- package/dist/skills/ethical-hacking-methodology/SKILL.md +119 -0
- package/dist/skills/externalSkillLoader.d.ts +25 -0
- package/dist/skills/externalSkillLoader.js +65 -0
- package/dist/skills/externalSkillRegistry.d.ts +33 -0
- package/dist/skills/externalSkillRegistry.js +94 -0
- package/dist/skills/filesystem-management/SKILL.md +61 -0
- package/dist/skills/first-run-seed.d.ts +26 -0
- package/dist/skills/first-run-seed.js +75 -0
- package/dist/skills/git-vcs/SKILL.md +84 -0
- package/dist/skills/kafka/SKILL.md +89 -0
- package/dist/skills/kubernetes/SKILL.md +93 -0
- package/dist/skills/kubernetes-expert/SKILL.md +54 -0
- package/dist/skills/openshift/SKILL.md +93 -0
- package/dist/skills/pentester/SKILL.md +59 -0
- package/dist/skills/performance-tester/SKILL.md +57 -0
- package/dist/skills/playwright-ui-tester/SKILL.md +67 -0
- package/dist/skills/programmer/SKILL.md +58 -0
- package/dist/skills/project-scaffolding/SKILL.md +133 -0
- package/dist/skills/qa-engineer/SKILL.md +91 -0
- package/dist/skills/rca/SKILL.md +58 -0
- package/dist/skills/redhat/SKILL.md +92 -0
- package/dist/skills/rosa/SKILL.md +80 -0
- package/dist/skills/scrum-framework/SKILL.md +74 -0
- package/dist/skills/scrum-framework/references/artifacts_and_quality.md +79 -0
- package/dist/skills/scrum-framework/references/roles_and_events.md +105 -0
- package/dist/skills/scrum-master-agent/SKILL.md +74 -0
- package/dist/skills/scrum-master-agent/references/behaviors.md +56 -0
- package/dist/skills/sdlc-skill.d.ts +1 -0
- package/dist/skills/sdlc-skill.js +4 -0
- package/dist/skills/secops/SKILL.md +52 -0
- package/dist/skills/secure-architect.bylaws.d.ts +68 -0
- package/dist/skills/secure-architect.bylaws.js +525 -0
- package/dist/skills/seed-skills.d.ts +48 -0
- package/dist/skills/seed-skills.js +213 -0
- package/dist/skills/skill-authoring/SKILL.md +123 -0
- package/dist/skills/skill-header.d.ts +51 -0
- package/dist/skills/skill-header.js +163 -0
- package/dist/skills/skill-path-guard.d.ts +41 -0
- package/dist/skills/skill-path-guard.js +95 -0
- package/dist/skills/skillRouter.d.ts +51 -0
- package/dist/skills/skillRouter.js +90 -0
- package/dist/skills/skills-root.d.ts +64 -0
- package/dist/skills/skills-root.js +168 -0
- package/dist/skills/software-architect/SKILL.md +77 -0
- package/dist/skills/software-engineer/SKILL.md +74 -0
- package/dist/skills/solid-principles/SKILL.md +94 -0
- package/dist/skills/task-planning/SKILL.md +115 -0
- package/dist/skills/tester/SKILL.md +54 -0
- package/dist/skills/ubuntu/SKILL.md +87 -0
- package/dist/skills/ui-ux-design/SKILL.md +114 -0
- package/dist/skills/workspace-context/SKILL.md +103 -0
- package/dist/sprint/controller.d.ts +37 -0
- package/dist/sprint/controller.js +220 -0
- package/dist/sprint/dag-builder.d.ts +14 -0
- package/dist/sprint/dag-builder.js +105 -0
- package/dist/sprint/reporter.d.ts +11 -0
- package/dist/sprint/reporter.js +97 -0
- package/dist/sprint/state-store.d.ts +10 -0
- package/dist/sprint/state-store.js +71 -0
- package/dist/sprint/types.d.ts +24 -0
- package/dist/sprint/types.js +4 -0
- package/dist/store/file-version-store.d.ts +24 -0
- package/dist/store/file-version-store.js +73 -0
- package/dist/store/task-store.d.ts +90 -0
- package/dist/store/task-store.js +166 -0
- package/dist/task-ingest/architect-detailer.d.ts +43 -0
- package/dist/task-ingest/architect-detailer.js +141 -0
- package/dist/task-ingest/detail-schema.d.ts +9 -0
- package/dist/task-ingest/detail-schema.js +195 -0
- package/dist/task-ingest/detailed-run.d.ts +36 -0
- package/dist/task-ingest/detailed-run.js +108 -0
- package/dist/task-ingest/extract-id.d.ts +9 -0
- package/dist/task-ingest/extract-id.js +24 -0
- package/dist/task-ingest/index.d.ts +23 -0
- package/dist/task-ingest/index.js +14 -0
- package/dist/task-ingest/ingest.d.ts +45 -0
- package/dist/task-ingest/ingest.js +163 -0
- package/dist/task-ingest/list-ingested.d.ts +13 -0
- package/dist/task-ingest/list-ingested.js +30 -0
- package/dist/task-ingest/pending-filter.d.ts +30 -0
- package/dist/task-ingest/pending-filter.js +59 -0
- package/dist/task-ingest/scanner.d.ts +56 -0
- package/dist/task-ingest/scanner.js +216 -0
- package/dist/task-ingest/source-reader.d.ts +25 -0
- package/dist/task-ingest/source-reader.js +93 -0
- package/dist/task-ingest/spec-builder.d.ts +34 -0
- package/dist/task-ingest/spec-builder.js +89 -0
- package/dist/task-ingest/wbs-extract.d.ts +50 -0
- package/dist/task-ingest/wbs-extract.js +226 -0
- package/dist/task-ingest/wbs-parser.d.ts +36 -0
- package/dist/task-ingest/wbs-parser.js +170 -0
- package/dist/tools/apiTestTool.d.ts +37 -0
- package/dist/tools/apiTestTool.js +128 -0
- package/dist/tools/ast-tools.d.ts +183 -0
- package/dist/tools/ast-tools.js +853 -0
- package/dist/tools/browse-tool.d.ts +105 -0
- package/dist/tools/browse-tool.js +394 -0
- package/dist/tools/credential.d.ts +17 -0
- package/dist/tools/credential.js +32 -0
- package/dist/tools/dependency-graph.d.ts +23 -0
- package/dist/tools/dependency-graph.js +164 -0
- package/dist/tools/edit-tool.d.ts +34 -0
- package/dist/tools/edit-tool.js +95 -0
- package/dist/tools/file-header-manager.d.ts +27 -0
- package/dist/tools/file-header-manager.js +153 -0
- package/dist/tools/fs-tools.d.ts +154 -0
- package/dist/tools/fs-tools.js +337 -0
- package/dist/tools/git-tools.d.ts +79 -0
- package/dist/tools/git-tools.js +348 -0
- package/dist/tools/github-tool.d.ts +48 -0
- package/dist/tools/github-tool.js +147 -0
- package/dist/tools/globTool.d.ts +20 -0
- package/dist/tools/globTool.js +34 -0
- package/dist/tools/grepTool.d.ts +19 -0
- package/dist/tools/grepTool.js +44 -0
- package/dist/tools/index-freshness.d.ts +58 -0
- package/dist/tools/index-freshness.js +190 -0
- package/dist/tools/index-glob.d.ts +48 -0
- package/dist/tools/index-glob.js +175 -0
- package/dist/tools/load-skill-tool.d.ts +15 -0
- package/dist/tools/load-skill-tool.js +114 -0
- package/dist/tools/patch-tools.d.ts +58 -0
- package/dist/tools/patch-tools.js +153 -0
- package/dist/tools/playwright-tool.d.ts +66 -0
- package/dist/tools/playwright-tool.js +121 -0
- package/dist/tools/project-detect.d.ts +20 -0
- package/dist/tools/project-detect.js +87 -0
- package/dist/tools/prompt-guidance.d.ts +22 -0
- package/dist/tools/prompt-guidance.js +23 -0
- package/dist/tools/scheduleTool.d.ts +19 -0
- package/dist/tools/scheduleTool.js +181 -0
- package/dist/tools/search-tools.d.ts +27 -0
- package/dist/tools/search-tools.js +78 -0
- package/dist/tools/sed-tool.d.ts +66 -0
- package/dist/tools/sed-tool.js +402 -0
- package/dist/tools/shell-result.d.ts +11 -0
- package/dist/tools/shell-result.js +1 -0
- package/dist/tools/shell-tool.d.ts +45 -0
- package/dist/tools/shell-tool.js +141 -0
- package/dist/tools/siteCrawlerTool.d.ts +32 -0
- package/dist/tools/siteCrawlerTool.js +205 -0
- package/dist/tools/tool-registry.d.ts +67 -0
- package/dist/tools/tool-registry.js +596 -0
- package/dist/tools/validate-tool.d.ts +23 -0
- package/dist/tools/validate-tool.js +201 -0
- package/dist/tools/workspace-index.d.ts +46 -0
- package/dist/tools/workspace-index.js +173 -0
- package/dist/types.d.ts +222 -0
- package/dist/types.js +7 -0
- package/dist/updater/index.d.ts +14 -0
- package/dist/updater/index.js +120 -0
- package/dist/util/banner.d.ts +25 -0
- package/dist/util/banner.js +140 -0
- package/dist/util/command-suggester.d.ts +6 -0
- package/dist/util/command-suggester.js +25 -0
- package/dist/util/confirm-interrupt.d.ts +10 -0
- package/dist/util/confirm-interrupt.js +31 -0
- package/dist/util/continue-trigger.d.ts +11 -0
- package/dist/util/continue-trigger.js +20 -0
- package/dist/util/env-info.d.ts +18 -0
- package/dist/util/env-info.js +37 -0
- package/dist/util/file-access-log.d.ts +35 -0
- package/dist/util/file-access-log.js +140 -0
- package/dist/util/footer-manager.d.ts +124 -0
- package/dist/util/footer-manager.js +195 -0
- package/dist/util/footer-renderer.d.ts +67 -0
- package/dist/util/footer-renderer.js +319 -0
- package/dist/util/footer-state.d.ts +34 -0
- package/dist/util/footer-state.js +18 -0
- package/dist/util/footer-text.d.ts +51 -0
- package/dist/util/footer-text.js +74 -0
- package/dist/util/ids.d.ts +1 -0
- package/dist/util/ids.js +4 -0
- package/dist/util/levenshtein.d.ts +6 -0
- package/dist/util/levenshtein.js +34 -0
- package/dist/util/logger.d.ts +10 -0
- package/dist/util/logger.js +41 -0
- package/dist/util/logs.d.ts +26 -0
- package/dist/util/logs.js +88 -0
- package/dist/util/output-handler.d.ts +89 -0
- package/dist/util/output-handler.js +193 -0
- package/dist/util/output-interceptor.d.ts +18 -0
- package/dist/util/output-interceptor.js +49 -0
- package/dist/util/output-paths.d.ts +43 -0
- package/dist/util/output-paths.js +121 -0
- package/dist/util/package-json.d.ts +6 -0
- package/dist/util/package-json.js +36 -0
- package/dist/util/process-kill.d.ts +27 -0
- package/dist/util/process-kill.js +148 -0
- package/dist/util/protocol-error-logger.d.ts +33 -0
- package/dist/util/protocol-error-logger.js +73 -0
- package/dist/util/redact.d.ts +25 -0
- package/dist/util/redact.js +103 -0
- package/dist/util/scroll-region-guard.d.ts +33 -0
- package/dist/util/scroll-region-guard.js +63 -0
- package/dist/util/spinner.d.ts +15 -0
- package/dist/util/spinner.js +49 -0
- package/dist/util/terminal-output.d.ts +34 -0
- package/dist/util/terminal-output.js +1 -0
- package/dist/util/terminal-silent.d.ts +6 -0
- package/dist/util/terminal-silent.js +20 -0
- package/dist/util/terminal.d.ts +56 -0
- package/dist/util/terminal.js +124 -0
- package/dist/util/text-sanitize.d.ts +8 -0
- package/dist/util/text-sanitize.js +45 -0
- package/dist/util/thinking-spinner.d.ts +27 -0
- package/dist/util/thinking-spinner.js +189 -0
- package/dist/util/tool-error-logger.d.ts +33 -0
- package/dist/util/tool-error-logger.js +77 -0
- package/dist/util/tty.d.ts +13 -0
- package/dist/util/tty.js +16 -0
- package/dist/util/xcoder-paths.d.ts +56 -0
- package/dist/util/xcoder-paths.js +63 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +4 -0
- package/dist/worker-agent/to-subtask-node.d.ts +35 -0
- package/dist/worker-agent/to-subtask-node.js +41 -0
- package/dist/worker-agent/worker-orchestrator.d.ts +104 -0
- package/dist/worker-agent/worker-orchestrator.js +201 -0
- package/package.json +39 -4
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
// xcoder:version 10 | xcoder:task task-e6370a | xcoder:updated 2026-08-12T16:48:04.548Z | xcoder:subtask code-st-cd4444
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { buildIndex, findInIndex, getOrBuildIndex, upsertEntry, toPosixPath, } from "./workspace-index.js";
|
|
5
|
+
import { getCommentSyntax, injectVersionHeader, } from "./file-header-manager.js";
|
|
6
|
+
import { incrementFileVersion } from "../store/file-version-store.js";
|
|
7
|
+
import { hashFile } from "./patch-tools.js";
|
|
8
|
+
export class WorkspaceEscapeError extends Error {
|
|
9
|
+
}
|
|
10
|
+
/** Every path a tool touches is resolved and pinned inside the workspace root. No `..` escapes. */
|
|
11
|
+
function resolveInWorkspace(root, relPath) {
|
|
12
|
+
const full = path.resolve(root, relPath);
|
|
13
|
+
const normalizedRoot = path.resolve(root) + path.sep;
|
|
14
|
+
if (full + path.sep !== normalizedRoot && !full.startsWith(normalizedRoot)) {
|
|
15
|
+
throw new WorkspaceEscapeError(`path "${relPath}" resolves outside the workspace root — refusing.`);
|
|
16
|
+
}
|
|
17
|
+
return full;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The workspace index is the source of truth for "what files exist". If a
|
|
21
|
+
* requested path isn't in the index, this triggers exactly one automatic
|
|
22
|
+
* reindex and retries — covering the common case (a file appeared via some
|
|
23
|
+
* process outside Xcoder SDLC, or the index is simply stale). If it's still not
|
|
24
|
+
* found after that, the caller gets a typed `file_not_found`, not a crash.
|
|
25
|
+
*
|
|
26
|
+
* `startLine`/`endLine` (1-indexed, inclusive) let a large file be read in
|
|
27
|
+
* slices instead of pulling the whole thing into context at once — pair
|
|
28
|
+
* this with `edit_file` for surgical changes to files too big to safely
|
|
29
|
+
* rewrite in full via `write_file`.
|
|
30
|
+
*/
|
|
31
|
+
export async function readFileTool(root, relPath, range) {
|
|
32
|
+
let full;
|
|
33
|
+
try {
|
|
34
|
+
full = resolveInWorkspace(root, relPath);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
return { ok: false, error: e.message, code: "workspace_escape" };
|
|
38
|
+
}
|
|
39
|
+
let index = await getOrBuildIndex(root);
|
|
40
|
+
if (!findInIndex(index, relPath)) {
|
|
41
|
+
index = await buildIndex(root); // one automatic reindex
|
|
42
|
+
if (!findInIndex(index, relPath)) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
code: "file_not_found",
|
|
46
|
+
error: `"${relPath}" is not in the workspace index even after a reindex. ` +
|
|
47
|
+
`Either the path is wrong, or the file genuinely doesn't exist yet — ` +
|
|
48
|
+
`consider whether this subtask should create it instead.`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const raw = await fs.readFile(full, "utf8");
|
|
54
|
+
if (!range || (range.startLine === undefined && range.endLine === undefined)) {
|
|
55
|
+
return { ok: true, content: raw, relPath };
|
|
56
|
+
}
|
|
57
|
+
const lines = raw.split("\n");
|
|
58
|
+
const start = Math.max(1, range.startLine ?? 1);
|
|
59
|
+
const end = Math.min(lines.length, range.endLine ?? lines.length);
|
|
60
|
+
const slice = lines.slice(start - 1, end).join("\n");
|
|
61
|
+
const readSha1 = await hashFile(full);
|
|
62
|
+
return { ok: true, content: slice, relPath, totalLines: lines.length, readSha1 };
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
return { ok: false, error: e.message, code: "read_error" };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* write_file gate (AC-15): refuse whole-file rewrites above this many lines
|
|
70
|
+
* unless the caller explicitly opts out with `force: true`. The refusal is
|
|
71
|
+
* a steering signal — point the model at the surgical alternatives rather
|
|
72
|
+
* than silently accepting a 900-line reproduce-the-whole-file risk.
|
|
73
|
+
*/
|
|
74
|
+
export const WRITE_FILE_LINE_CEILING = 200;
|
|
75
|
+
export async function writeFileTool(root, relPath, content, ctx, opts) {
|
|
76
|
+
const lineCount = content.split("\n").length;
|
|
77
|
+
if (lineCount > WRITE_FILE_LINE_CEILING && !opts?.force) {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
code: "file_too_large",
|
|
81
|
+
error: `content is ${lineCount} lines > ${WRITE_FILE_LINE_CEILING} ceiling. If this is a genuine full rewrite add force: true; otherwise prefer edit_file, line_patch, or update_function.`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
let full;
|
|
85
|
+
try {
|
|
86
|
+
full = resolveInWorkspace(root, relPath);
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
return { ok: false, error: e.message, code: "workspace_escape" };
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
// Header injection (when context is available and file type supports it)
|
|
93
|
+
let finalContent = content;
|
|
94
|
+
if (ctx) {
|
|
95
|
+
const syntax = getCommentSyntax(relPath);
|
|
96
|
+
if (syntax) {
|
|
97
|
+
const meta = await incrementFileVersion(root, relPath, ctx.taskId, ctx.subtaskId);
|
|
98
|
+
const injected = injectVersionHeader(finalContent, syntax, meta);
|
|
99
|
+
finalContent = injected.content;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
103
|
+
await fs.writeFile(full, finalContent, "utf8");
|
|
104
|
+
await upsertEntry(root, path.relative(root, full)); // keep the index fresh incrementally
|
|
105
|
+
return { ok: true, relPath, bytesWritten: Buffer.byteLength(content, "utf8") };
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
return { ok: false, error: e.message, code: "write_error" };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export async function listDirTool(index, relDir) {
|
|
112
|
+
const normalizedDir = toPosixPath(relDir);
|
|
113
|
+
const prefix = normalizedDir === "" || normalizedDir === "." ? "" : normalizedDir.replace(/\/?$/, "/");
|
|
114
|
+
const entries = index.entries
|
|
115
|
+
.filter((e) => e.relPath.startsWith(prefix))
|
|
116
|
+
.map((e) => e.relPath);
|
|
117
|
+
return { ok: true, entries };
|
|
118
|
+
}
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Efficient-filesystem read tools (read_outline / read_multiple_files /
|
|
121
|
+
// read_full_file) + the shared truncation helper.
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
/**
|
|
124
|
+
* Maximum characters for a single observation returned to the LLM. Every
|
|
125
|
+
* growable new-tool output routes through `truncateOutput` — with an
|
|
126
|
+
* actionable note naming that tool's narrowing parameter, never a silent
|
|
127
|
+
* cutoff (AC-18).
|
|
128
|
+
*/
|
|
129
|
+
export const MAX_OBSERVATION_CHARS = 4000;
|
|
130
|
+
export function truncateOutput(text, maxChars = MAX_OBSERVATION_CHARS) {
|
|
131
|
+
if (text.length <= maxChars) {
|
|
132
|
+
return { text, truncated: false, note: "" };
|
|
133
|
+
}
|
|
134
|
+
const head = text.slice(0, Math.max(0, maxChars - 120));
|
|
135
|
+
return {
|
|
136
|
+
text: `${head}\n… [truncated: ${text.length} chars, showing first ${maxChars}]`,
|
|
137
|
+
truncated: true,
|
|
138
|
+
note: `output was truncated from ${text.length} chars to ${maxChars}. Narrow the result with that tool's range/path/maxCount parameters before retrying.`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const CODE_EXT_LANG = {
|
|
142
|
+
".ts": "typescript",
|
|
143
|
+
".tsx": "typescript",
|
|
144
|
+
".mts": "typescript",
|
|
145
|
+
".cts": "typescript",
|
|
146
|
+
".js": "javascript",
|
|
147
|
+
".jsx": "javascript",
|
|
148
|
+
".mjs": "javascript",
|
|
149
|
+
".cjs": "javascript",
|
|
150
|
+
};
|
|
151
|
+
async function readFileRaw(root, relPath) {
|
|
152
|
+
let full;
|
|
153
|
+
try {
|
|
154
|
+
full = resolveInWorkspace(root, relPath);
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
return { ok: false, error: e.message, code: "workspace_escape" };
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
const content = await fs.readFile(full, "utf8");
|
|
161
|
+
return { ok: true, full, content };
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return { ok: false, code: "file_not_found", error: `"${relPath}" doesn't exist or can't be read.` };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Structural skeleton read: imports, symbol signatures, docstrings, bodies
|
|
169
|
+
* collapsed. The default first read for files over ~150 lines — far cheaper
|
|
170
|
+
* than pulling the whole file into context. TS/JS uses the scanner in
|
|
171
|
+
* ast-tools; other text files get a structural fallback (markdown headings,
|
|
172
|
+
* `---` separators, first non-empty lines). Binary → unsupported_language.
|
|
173
|
+
*/
|
|
174
|
+
export async function readOutlineTool(root, relPath) {
|
|
175
|
+
const raw = await readFileRaw(root, relPath);
|
|
176
|
+
if (!raw.ok)
|
|
177
|
+
return raw;
|
|
178
|
+
const ext = path.extname(raw.full).toLowerCase();
|
|
179
|
+
const codeLang = CODE_EXT_LANG[ext];
|
|
180
|
+
const lineCount = raw.content.split("\n").length;
|
|
181
|
+
// Binary / unsupported detection: NUL byte or unknown extension.
|
|
182
|
+
if (raw.content.includes("\u0000")) {
|
|
183
|
+
return {
|
|
184
|
+
ok: false,
|
|
185
|
+
code: "unsupported_language",
|
|
186
|
+
error: `"${relPath}" appears to be a binary file. Use ` + "`read_file`" + ` with a line range for text, or run_command if it needs a specific tool.`,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (codeLang) {
|
|
190
|
+
const { scanFile } = await import("./ast-tools.js");
|
|
191
|
+
const scan = scanFile(raw.content, codeLang);
|
|
192
|
+
if (!("ok" in scan) && !scan.parseError) {
|
|
193
|
+
const symbols = [];
|
|
194
|
+
const lines = raw.content.split("\n");
|
|
195
|
+
const outKind = {
|
|
196
|
+
function: "function",
|
|
197
|
+
class: "class",
|
|
198
|
+
interface: "interface",
|
|
199
|
+
type: "type",
|
|
200
|
+
const: "const",
|
|
201
|
+
let: "const",
|
|
202
|
+
var: "const",
|
|
203
|
+
};
|
|
204
|
+
for (const d of scan.declarations) {
|
|
205
|
+
if (!outKind[d.kind])
|
|
206
|
+
continue;
|
|
207
|
+
symbols.push({
|
|
208
|
+
kind: outKind[d.kind],
|
|
209
|
+
name: d.name,
|
|
210
|
+
startLine: d.startLine,
|
|
211
|
+
endLine: d.endLine,
|
|
212
|
+
signature: (lines[d.startLine - 1] ?? "").trim().slice(0, 160),
|
|
213
|
+
doc: lines[d.startLine - 2]?.trim().startsWith("//") ? lines[d.startLine - 2].trim() : undefined,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// Collapse bodies: keep the skeleton, not the full content.
|
|
217
|
+
let body = raw.content;
|
|
218
|
+
if (lineCount > 150) {
|
|
219
|
+
body = lines
|
|
220
|
+
.map((line, i) => (scan.declarations.some((d) => i + 1 >= d.startLine && i + 1 <= Math.min(d.endLine, d.startLine + 2)) ? line : line.length > 80 ? line.slice(0, 80) + " …" : line))
|
|
221
|
+
.join("\n");
|
|
222
|
+
body = "… (outline collapsed: " + lineCount + " lines total; use read_file with a range for full sections) …\n" + body;
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
ok: true,
|
|
226
|
+
relPath,
|
|
227
|
+
language: codeLang,
|
|
228
|
+
lineCount,
|
|
229
|
+
imports: scan.imports.slice(0, 40),
|
|
230
|
+
symbols,
|
|
231
|
+
body,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// Scanner failed — fall through to the structural text fallback.
|
|
235
|
+
}
|
|
236
|
+
// Structural text fallback for non-code files / scan failures.
|
|
237
|
+
const lines = raw.content.split("\n");
|
|
238
|
+
const head = [];
|
|
239
|
+
for (const line of lines) {
|
|
240
|
+
if (head.length >= 60)
|
|
241
|
+
break;
|
|
242
|
+
const trimmed = line.trim();
|
|
243
|
+
if (trimmed === "")
|
|
244
|
+
continue;
|
|
245
|
+
if (/^(#{1,6}\s|[-*]\s|\d+\.\s|---)/.test(trimmed))
|
|
246
|
+
head.push(trimmed.slice(0, 120));
|
|
247
|
+
else if (head.length < 20)
|
|
248
|
+
head.push(trimmed.slice(0, 120));
|
|
249
|
+
}
|
|
250
|
+
const body = head.length < lineCount ? `… (${lineCount} lines total — use read_file with a range to see more) …\n` + head.join("\n") : head.join("\n");
|
|
251
|
+
return {
|
|
252
|
+
ok: true,
|
|
253
|
+
relPath,
|
|
254
|
+
language: "text",
|
|
255
|
+
lineCount,
|
|
256
|
+
imports: [],
|
|
257
|
+
symbols: [],
|
|
258
|
+
body,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Batched, labeled section reads in ONE call — the replacement for N
|
|
263
|
+
* separate read_file calls when building cross-file understanding. Parallel
|
|
264
|
+
* (bounded to 8), each section labeled `--- path (lines a-b of N) ---`.
|
|
265
|
+
* Over maxTotalChars (default 12000) tail sections are dropped with an
|
|
266
|
+
* actionable note; never silently swallowed.
|
|
267
|
+
*/
|
|
268
|
+
export async function readMultipleFilesTool(root, input) {
|
|
269
|
+
if (!Array.isArray(input.files) || input.files.length === 0) {
|
|
270
|
+
return { ok: false, code: "invalid_files", error: "read_multiple_files requires a non-empty files[] array of { path, startLine?, endLine? }." };
|
|
271
|
+
}
|
|
272
|
+
const maxChars = input.maxTotalChars ?? 12_000;
|
|
273
|
+
const results = await Promise.all(input.files.slice(0, 8).map(async (f) => {
|
|
274
|
+
const read = await readFileTool(root, f.path, { startLine: f.startLine, endLine: f.endLine });
|
|
275
|
+
return { f, read };
|
|
276
|
+
}));
|
|
277
|
+
const sections = [];
|
|
278
|
+
let totalChars = 0;
|
|
279
|
+
let dropped = 0;
|
|
280
|
+
let truncated = false;
|
|
281
|
+
for (const { f, read } of results) {
|
|
282
|
+
if (!read.ok) {
|
|
283
|
+
sections.push({ label: `--- ${f.path} (ERROR) ---`, content: read.error, totalLines: 0, sliced: false });
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const lines = read.content.split("\n");
|
|
287
|
+
const posixPath = toPosixPath(f.path);
|
|
288
|
+
const label = `--- ${posixPath} (lines ${f.startLine ?? 1}-${f.endLine ?? lines.length} of ${read.totalLines ?? lines.length}) ---`;
|
|
289
|
+
const sectionText = `${label}\n${read.content}`;
|
|
290
|
+
if (ceilBudgetExceeded(totalChars, sectionText.length, maxChars)) {
|
|
291
|
+
truncated = true;
|
|
292
|
+
dropped += 1;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
sections.push({
|
|
296
|
+
label,
|
|
297
|
+
content: read.content,
|
|
298
|
+
totalLines: read.totalLines ?? lines.length,
|
|
299
|
+
sliced: !!(f.startLine !== undefined || f.endLine !== undefined),
|
|
300
|
+
});
|
|
301
|
+
totalChars += sectionText.length;
|
|
302
|
+
}
|
|
303
|
+
const overBudget = input.files.length > 8;
|
|
304
|
+
return {
|
|
305
|
+
ok: true,
|
|
306
|
+
sections: truncated || overBudget ? sections : sections.slice(0, Math.max(1, sections.length)),
|
|
307
|
+
totalChars,
|
|
308
|
+
dropped: dropped + (overBudget ? input.files.length - 8 : 0),
|
|
309
|
+
note: truncated || overBudget
|
|
310
|
+
? `kept ${sections.length} of ${input.files.length} sections under the ${maxChars}-char maxTotalChars budget${overBudget ? " (max 8 files per call)" : ""}. Narrow each file with smaller startLine/endLine ranges or split the batch.`
|
|
311
|
+
: undefined,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function ceilBudgetExceeded(current, add, max) {
|
|
315
|
+
return current + add > max;
|
|
316
|
+
}
|
|
317
|
+
/** read_full_file refuses whole-file reads above this line count unless allowLarge is set. */
|
|
318
|
+
export const FULL_FILE_LINE_CEILING = 500;
|
|
319
|
+
/**
|
|
320
|
+
* Gated whole-file read: refused above 500 lines unless `allowLarge: true`
|
|
321
|
+
* explicitly opts in. The refusal names the cheaper alternative
|
|
322
|
+
* (read_outline / read_file with a range) instead of silently auto-chunking.
|
|
323
|
+
*/
|
|
324
|
+
export async function readFullFileTool(root, relPath, allowLarge = false) {
|
|
325
|
+
const raw = await readFileRaw(root, relPath);
|
|
326
|
+
if (!raw.ok)
|
|
327
|
+
return raw;
|
|
328
|
+
const lineCount = raw.content.split("\n").length;
|
|
329
|
+
if (lineCount > FULL_FILE_LINE_CEILING && !allowLarge) {
|
|
330
|
+
return {
|
|
331
|
+
ok: false,
|
|
332
|
+
code: "file_too_large",
|
|
333
|
+
error: `read_full_file refused: "${relPath}" is ${lineCount} lines > ${FULL_FILE_LINE_CEILING} ceiling. Use read_outline for the skeleton, read_file with a line range for sections, or pass allowLarge: true if you genuinely need the whole file.`,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
return { ok: true, relPath, lineCount, content: raw.content };
|
|
337
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export interface GitDiffInput {
|
|
2
|
+
ref?: string;
|
|
3
|
+
path?: string;
|
|
4
|
+
rawDiff?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export type GitDiffResult = {
|
|
7
|
+
ok: true;
|
|
8
|
+
summary: string;
|
|
9
|
+
entries: Array<{
|
|
10
|
+
file: string;
|
|
11
|
+
added: number;
|
|
12
|
+
removed: number;
|
|
13
|
+
status: "added" | "modified" | "deleted" | "renamed";
|
|
14
|
+
}>;
|
|
15
|
+
hasDiff: boolean;
|
|
16
|
+
statsOnly: boolean;
|
|
17
|
+
rawDiff?: string;
|
|
18
|
+
note?: string;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
code: "not_a_git_repo" | "git_error";
|
|
22
|
+
error: string;
|
|
23
|
+
stderr?: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Low-token diff summary by default (`git diff --stat`). Pass `rawDiff: true`
|
|
27
|
+
* to get the full unified diff (still capped by truncation). Summary-first is
|
|
28
|
+
* the cheapest-first default (T5).
|
|
29
|
+
*/
|
|
30
|
+
export declare function gitDiffTool(root: string, input?: GitDiffInput): Promise<GitDiffResult>;
|
|
31
|
+
export interface GitLogInput {
|
|
32
|
+
path?: string;
|
|
33
|
+
maxCount?: number;
|
|
34
|
+
}
|
|
35
|
+
export type GitLogResult = {
|
|
36
|
+
ok: true;
|
|
37
|
+
commits: Array<{
|
|
38
|
+
hash: string;
|
|
39
|
+
shortHash: string;
|
|
40
|
+
author: string;
|
|
41
|
+
date: string;
|
|
42
|
+
subject: string;
|
|
43
|
+
}>;
|
|
44
|
+
truncated: boolean;
|
|
45
|
+
truncationNote?: string;
|
|
46
|
+
} | {
|
|
47
|
+
ok: false;
|
|
48
|
+
code: "not_a_git_repo" | "git_error";
|
|
49
|
+
error: string;
|
|
50
|
+
stderr?: string;
|
|
51
|
+
};
|
|
52
|
+
export declare function gitLogTool(root: string, input: GitLogInput): Promise<GitLogResult>;
|
|
53
|
+
export interface ApplyUnifiedDiffInput {
|
|
54
|
+
path: string;
|
|
55
|
+
diff: string;
|
|
56
|
+
}
|
|
57
|
+
export type ApplyUnifiedDiffResult = {
|
|
58
|
+
ok: true;
|
|
59
|
+
relPath: string;
|
|
60
|
+
hunksApplied: number;
|
|
61
|
+
linesChanged: number;
|
|
62
|
+
sha1After: string;
|
|
63
|
+
} | {
|
|
64
|
+
ok: false;
|
|
65
|
+
error: string;
|
|
66
|
+
code: "diff_check_failed" | "diff_apply_failed" | "file_not_found" | "workspace_escape" | "not_a_git_repo" | "no_diff_hunks";
|
|
67
|
+
checkOutput?: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Applies a unified diff to a single file. Inside a git repo this uses
|
|
71
|
+
* `git apply --check` (zero-writes on failure, full check output returned)
|
|
72
|
+
* then `git apply`. Outside a repo it falls back to a small JS hunk parser
|
|
73
|
+
* that splices the same way `line_patch` does (cross-platform — no `patch`
|
|
74
|
+
* binary dependency).
|
|
75
|
+
*/
|
|
76
|
+
export declare function applyUnifiedDiffTool(root: string, input: ApplyUnifiedDiffInput, ctx?: {
|
|
77
|
+
taskId: string;
|
|
78
|
+
subtaskId: string;
|
|
79
|
+
}): Promise<ApplyUnifiedDiffResult>;
|