xcoder-dna 0.0.0-stage → 1.3.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/LICENSE +21 -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 +38 -3
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// xcoder:version 2 | xcoder:task task-ebf4f7 | xcoder:updated 2026-10-17T12:16:50.555Z | xcoder:subtask test-st-f454da
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { redactSecrets } from "./redact.js";
|
|
5
|
+
export { redactSecrets, REDACTED } from "./redact.js";
|
|
6
|
+
const MAX_BYTES = 1_000_000; // 1MB, per the brief
|
|
7
|
+
function logDir(root, taskId) {
|
|
8
|
+
return path.join(root, ".xcoder", "logs", taskId);
|
|
9
|
+
}
|
|
10
|
+
function logFile(root, taskId, name) {
|
|
11
|
+
return path.join(logDir(root, taskId), name);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Single-generation size-based rollover: once appending would push a log
|
|
15
|
+
* file past 1MB, the current file is renamed to `<name>.1` (clobbering
|
|
16
|
+
* whatever was there before) and a fresh file is started. Simple and
|
|
17
|
+
* bounded — never accumulates unbounded history, never grows past ~2MB
|
|
18
|
+
* per log per task.
|
|
19
|
+
*/
|
|
20
|
+
async function appendWithRollover(filePath, line) {
|
|
21
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
22
|
+
const entry = line.endsWith("\n") ? line : line + "\n";
|
|
23
|
+
let currentSize = 0;
|
|
24
|
+
try {
|
|
25
|
+
currentSize = (await fs.stat(filePath)).size;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
currentSize = 0;
|
|
29
|
+
}
|
|
30
|
+
if (currentSize + Buffer.byteLength(entry, "utf8") > MAX_BYTES) {
|
|
31
|
+
const rolled = filePath.replace(/(\.[^.]+)$/, ".1$1");
|
|
32
|
+
try {
|
|
33
|
+
await fs.rm(rolled, { force: true });
|
|
34
|
+
await fs.rename(filePath, rolled);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// best-effort; if the rename fails just keep appending to the current file
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
await fs.appendFile(filePath, entry, "utf8");
|
|
41
|
+
}
|
|
42
|
+
function ts() {
|
|
43
|
+
return new Date().toISOString();
|
|
44
|
+
}
|
|
45
|
+
/** Reason (Thought) → Action → Observation trace, one line per step, human-readable. */
|
|
46
|
+
export async function appendThink(root, taskId, subtaskId, line) {
|
|
47
|
+
await appendWithRollover(logFile(root, taskId, "think.log"), `[${ts()}] [${subtaskId}] ${line}`);
|
|
48
|
+
}
|
|
49
|
+
/** System-level errors: crashes, tool exceptions, anything that would otherwise vanish silently. */
|
|
50
|
+
export async function appendSys(root, taskId, line) {
|
|
51
|
+
await appendWithRollover(logFile(root, taskId, "sys.log"), `[${ts()}] ${line}`);
|
|
52
|
+
}
|
|
53
|
+
/** Raw LLM request/response pairs, one JSON line each. */
|
|
54
|
+
export async function appendLlm(root, taskId, subtaskId, entry) {
|
|
55
|
+
const stopSeg = entry.stopReason ? ` [stop=${entry.stopReason}]` : "";
|
|
56
|
+
// C7: redact before the line reaches disk — a key pasted into a prompt or
|
|
57
|
+
// echoed by a model must never land in llm.log.
|
|
58
|
+
const { text: safeContent } = redactSecrets(entry.content);
|
|
59
|
+
await appendWithRollover(logFile(root, taskId, "llm.log"), `[${ts()}] [${subtaskId}]${stopSeg} [${entry.direction}] ${safeContent.replace(/\n/g, "\\n")}`);
|
|
60
|
+
}
|
|
61
|
+
/** Token usage per LLM call, plus running totals — one line per call. */
|
|
62
|
+
export async function appendToken(root, taskId, subtaskId, usage) {
|
|
63
|
+
await appendWithRollover(logFile(root, taskId, "token.log"), `[${ts()}] [${subtaskId}] in=${usage.inputTokens} out=${usage.outputTokens} ` +
|
|
64
|
+
`(task-cumulative in=${usage.cumulativeInput} out=${usage.cumulativeOutput})`);
|
|
65
|
+
}
|
|
66
|
+
/** Reasoning-effectiveness score per subtask attempt, with its full breakdown — see orchestrator/reasoning-score.ts for what this is and isn't. */
|
|
67
|
+
export async function appendScore(root, taskId, subtaskId, score) {
|
|
68
|
+
await appendWithRollover(logFile(root, taskId, "score.log"), `[${ts()}] [${subtaskId}] score=${score.score}/100\n ` + score.breakdown.join("\n "));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Every file (or shell command) any tool touched: one line per access,
|
|
72
|
+
* `[ts] [subtask] tool=… op=… ok=… path=…` (or `cmd=…` for shell commands).
|
|
73
|
+
* Same 1 MB single-generation rollover (`files.log` → `files.1.log`) as the
|
|
74
|
+
* other logs. Writes are serialised so parallel agents can't race the
|
|
75
|
+
* stat/rename in `appendWithRollover`.
|
|
76
|
+
*/
|
|
77
|
+
let filesChain = Promise.resolve();
|
|
78
|
+
export function appendFiles(root, taskId, subtaskId, line) {
|
|
79
|
+
filesChain = filesChain
|
|
80
|
+
.then(() => appendWithRollover(logFile(root, taskId, "files.log"), `[${ts()}] [${subtaskId}] ${line}`))
|
|
81
|
+
.catch(() => {
|
|
82
|
+
// logging must never break a tool call
|
|
83
|
+
});
|
|
84
|
+
return filesChain;
|
|
85
|
+
}
|
|
86
|
+
export function taskLogDir(root, taskId) {
|
|
87
|
+
return logDir(root, taskId);
|
|
88
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OutputHandler — centralized output sink for ReAct signals.
|
|
3
|
+
*
|
|
4
|
+
* Provides flag-gated terminal output and optional SQLite persistence
|
|
5
|
+
* for Thought, Reason, Action, Observation, and raw/error signals.
|
|
6
|
+
*
|
|
7
|
+
* Singleton pattern: use getOutputHandler() to obtain the instance.
|
|
8
|
+
* Call reset() in tests to clear the singleton between test cases.
|
|
9
|
+
*
|
|
10
|
+
* SQLite support is via sql.js (wasm-based, zero native deps) as an
|
|
11
|
+
* optional dependency. It is lazy-loaded on first logToSqlite:true call
|
|
12
|
+
* and falls back gracefully if the package is not installed.
|
|
13
|
+
*/
|
|
14
|
+
export interface OutputHandlerOptions {
|
|
15
|
+
/** Whether to print signals to stdout (default: true). */
|
|
16
|
+
printToTerminal: boolean;
|
|
17
|
+
/** Whether to persist signals to SQLite (default: false). */
|
|
18
|
+
logToSqlite?: boolean;
|
|
19
|
+
/** Path to the SQLite database file (default: ".xcoder/output.db"). */
|
|
20
|
+
dbPath?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Obtain the singleton OutputHandler instance.
|
|
24
|
+
* On first call, the instance is created with the provided options.
|
|
25
|
+
* Subsequent calls return the same instance (options are ignored).
|
|
26
|
+
*/
|
|
27
|
+
export declare function getOutputHandler(opts?: OutputHandlerOptions): OutputHandler;
|
|
28
|
+
/**
|
|
29
|
+
* Reset the singleton instance — for test use only.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resetOutputHandler(): void;
|
|
32
|
+
export declare class OutputHandler {
|
|
33
|
+
private printToTerminal;
|
|
34
|
+
private logToSqlite;
|
|
35
|
+
private dbPath;
|
|
36
|
+
private db;
|
|
37
|
+
private initPromise;
|
|
38
|
+
constructor(opts: OutputHandlerOptions);
|
|
39
|
+
/**
|
|
40
|
+
* Lazy-initialize the SQLite database.
|
|
41
|
+
* Uses sql.js (wasm-based, zero native deps).
|
|
42
|
+
* Falls back gracefully if sql.js is not installed.
|
|
43
|
+
*/
|
|
44
|
+
private ensureDb;
|
|
45
|
+
private _initDb;
|
|
46
|
+
/**
|
|
47
|
+
* Emit a thought signal.
|
|
48
|
+
*/
|
|
49
|
+
thought(msg: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Emit a reasoning step.
|
|
52
|
+
*/
|
|
53
|
+
reason(msg: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Emit an action invocation.
|
|
56
|
+
*/
|
|
57
|
+
action(tool: string, input: unknown): void;
|
|
58
|
+
/**
|
|
59
|
+
* Emit a tool observation.
|
|
60
|
+
*/
|
|
61
|
+
observation(msg: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Generic fallback output.
|
|
64
|
+
*/
|
|
65
|
+
raw(msg: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Emit an error signal.
|
|
68
|
+
*/
|
|
69
|
+
error(msg: string): void;
|
|
70
|
+
/**
|
|
71
|
+
* Core emit logic — prints to terminal if flag is on,
|
|
72
|
+
* and persists to SQLite if enabled.
|
|
73
|
+
*/
|
|
74
|
+
private emit;
|
|
75
|
+
/**
|
|
76
|
+
* Print a formatted signal to stdout.
|
|
77
|
+
*/
|
|
78
|
+
private printSignal;
|
|
79
|
+
/**
|
|
80
|
+
* Persist a signal to the SQLite database.
|
|
81
|
+
* Fire-and-forget — does not throw on failure.
|
|
82
|
+
*/
|
|
83
|
+
private persistSignal;
|
|
84
|
+
/**
|
|
85
|
+
* Close the SQLite database connection.
|
|
86
|
+
* Call on process exit or when the handler is no longer needed.
|
|
87
|
+
*/
|
|
88
|
+
close(): void;
|
|
89
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// xcoder:version 10 | xcoder:task task-7f2823 | xcoder:updated 2026-08-03T09:31:58.357Z | xcoder:subtask code-st-e8e2bf
|
|
2
|
+
/**
|
|
3
|
+
* OutputHandler — centralized output sink for ReAct signals.
|
|
4
|
+
*
|
|
5
|
+
* Provides flag-gated terminal output and optional SQLite persistence
|
|
6
|
+
* for Thought, Reason, Action, Observation, and raw/error signals.
|
|
7
|
+
*
|
|
8
|
+
* Singleton pattern: use getOutputHandler() to obtain the instance.
|
|
9
|
+
* Call reset() in tests to clear the singleton between test cases.
|
|
10
|
+
*
|
|
11
|
+
* SQLite support is via sql.js (wasm-based, zero native deps) as an
|
|
12
|
+
* optional dependency. It is lazy-loaded on first logToSqlite:true call
|
|
13
|
+
* and falls back gracefully if the package is not installed.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Mutable singleton reference — reset between tests via reset().
|
|
17
|
+
*/
|
|
18
|
+
let _instance = null;
|
|
19
|
+
/**
|
|
20
|
+
* Obtain the singleton OutputHandler instance.
|
|
21
|
+
* On first call, the instance is created with the provided options.
|
|
22
|
+
* Subsequent calls return the same instance (options are ignored).
|
|
23
|
+
*/
|
|
24
|
+
export function getOutputHandler(opts) {
|
|
25
|
+
if (!_instance) {
|
|
26
|
+
_instance = new OutputHandler(opts ?? { printToTerminal: true });
|
|
27
|
+
}
|
|
28
|
+
return _instance;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Reset the singleton instance — for test use only.
|
|
32
|
+
*/
|
|
33
|
+
export function resetOutputHandler() {
|
|
34
|
+
if (_instance) {
|
|
35
|
+
_instance.close();
|
|
36
|
+
_instance = null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export class OutputHandler {
|
|
40
|
+
printToTerminal;
|
|
41
|
+
logToSqlite;
|
|
42
|
+
dbPath;
|
|
43
|
+
db = null; // sql.js Database | null
|
|
44
|
+
initPromise = null;
|
|
45
|
+
constructor(opts) {
|
|
46
|
+
this.printToTerminal = opts.printToTerminal;
|
|
47
|
+
this.logToSqlite = opts.logToSqlite ?? false;
|
|
48
|
+
this.dbPath = opts.dbPath ?? ".xcoder/output.db";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Lazy-initialize the SQLite database.
|
|
52
|
+
* Uses sql.js (wasm-based, zero native deps).
|
|
53
|
+
* Falls back gracefully if sql.js is not installed.
|
|
54
|
+
*/
|
|
55
|
+
async ensureDb() {
|
|
56
|
+
if (this.db !== null)
|
|
57
|
+
return this.db;
|
|
58
|
+
if (!this.initPromise) {
|
|
59
|
+
this.initPromise = this._initDb();
|
|
60
|
+
}
|
|
61
|
+
const db = await this.initPromise;
|
|
62
|
+
this.db = db;
|
|
63
|
+
this.initPromise = null;
|
|
64
|
+
return db;
|
|
65
|
+
}
|
|
66
|
+
async _initDb() {
|
|
67
|
+
try {
|
|
68
|
+
// Dynamic import — sql.js is an optionalDependency.
|
|
69
|
+
// Falls back gracefully if not installed.
|
|
70
|
+
const initSqlJs = (await import("sql.js")).default;
|
|
71
|
+
const SQL = await initSqlJs();
|
|
72
|
+
const db = new SQL.Database();
|
|
73
|
+
// Create schema
|
|
74
|
+
db.run(`
|
|
75
|
+
CREATE TABLE IF NOT EXISTS output_log (
|
|
76
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
77
|
+
ts TEXT NOT NULL DEFAULT (datetime('now')),
|
|
78
|
+
level TEXT NOT NULL CHECK(level IN ('thought','reason','action','observation','raw','error')),
|
|
79
|
+
content TEXT NOT NULL,
|
|
80
|
+
metadata TEXT
|
|
81
|
+
)
|
|
82
|
+
`);
|
|
83
|
+
return db;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
// sql.js not installed — emit a warning once and fall back
|
|
87
|
+
console.warn("[output-handler] sql.js not available — SQLite logging disabled. " +
|
|
88
|
+
"Install with: npm install sql.js");
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Emit a thought signal.
|
|
94
|
+
*/
|
|
95
|
+
thought(msg) {
|
|
96
|
+
this.emit("thought", msg);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Emit a reasoning step.
|
|
100
|
+
*/
|
|
101
|
+
reason(msg) {
|
|
102
|
+
this.emit("reason", msg);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Emit an action invocation.
|
|
106
|
+
*/
|
|
107
|
+
action(tool, input) {
|
|
108
|
+
const meta = input !== undefined
|
|
109
|
+
? JSON.stringify(typeof input === "string" ? input.slice(0, 200) : input).slice(0, 500)
|
|
110
|
+
: undefined;
|
|
111
|
+
this.emit("action", tool, meta);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Emit a tool observation.
|
|
115
|
+
*/
|
|
116
|
+
observation(msg) {
|
|
117
|
+
this.emit("observation", msg);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Generic fallback output.
|
|
121
|
+
*/
|
|
122
|
+
raw(msg) {
|
|
123
|
+
this.emit("raw", msg);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Emit an error signal.
|
|
127
|
+
*/
|
|
128
|
+
error(msg) {
|
|
129
|
+
this.emit("error", msg);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Core emit logic — prints to terminal if flag is on,
|
|
133
|
+
* and persists to SQLite if enabled.
|
|
134
|
+
*/
|
|
135
|
+
emit(level, content, metadata) {
|
|
136
|
+
if (this.printToTerminal) {
|
|
137
|
+
this.printSignal(level, content, metadata);
|
|
138
|
+
}
|
|
139
|
+
if (this.logToSqlite) {
|
|
140
|
+
this.persistSignal(level, content, metadata);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Print a formatted signal to stdout.
|
|
145
|
+
*/
|
|
146
|
+
printSignal(level, content, metadata) {
|
|
147
|
+
const now = new Date().toISOString();
|
|
148
|
+
const metaStr = metadata ? ` | ${metadata}` : "";
|
|
149
|
+
console.log(`[${now}] [${level.toUpperCase()}] ${content}${metaStr}`);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Persist a signal to the SQLite database.
|
|
153
|
+
* Fire-and-forget — does not throw on failure.
|
|
154
|
+
*/
|
|
155
|
+
persistSignal(level, content, metadata) {
|
|
156
|
+
this.ensureDb()
|
|
157
|
+
.then((db) => {
|
|
158
|
+
if (!db)
|
|
159
|
+
return;
|
|
160
|
+
const stmt = db.run.bind(db);
|
|
161
|
+
// Build INSERT via raw SQL to avoid prepared-statement API differences
|
|
162
|
+
const safeContent = content.replace(/'/g, "''");
|
|
163
|
+
const safeMeta = metadata ? metadata.replace(/'/g, "''") : "";
|
|
164
|
+
const sql = safeMeta
|
|
165
|
+
? `INSERT INTO output_log (level, content, metadata) VALUES ('${level}', '${safeContent}', '${safeMeta}')`
|
|
166
|
+
: `INSERT INTO output_log (level, content) VALUES ('${level}', '${safeContent}')`;
|
|
167
|
+
try {
|
|
168
|
+
db.run(sql);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// Silently ignore DB write errors — log persistence is best-effort
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
.catch(() => {
|
|
175
|
+
// Silently ignore — sql.js load failed (already warned)
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Close the SQLite database connection.
|
|
180
|
+
* Call on process exit or when the handler is no longer needed.
|
|
181
|
+
*/
|
|
182
|
+
close() {
|
|
183
|
+
if (this.db) {
|
|
184
|
+
try {
|
|
185
|
+
this.db.close();
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Ignore
|
|
189
|
+
}
|
|
190
|
+
this.db = null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ScrollRegionGuard } from "./scroll-region-guard.js";
|
|
2
|
+
/**
|
|
3
|
+
* OutputInterceptor — monkey-patches `process.stdout.write` during the
|
|
4
|
+
* footer's active lifetime to route ALL writes through the ScrollRegionGuard.
|
|
5
|
+
*
|
|
6
|
+
* This is the "nuclear option" that guarantees no write can break the
|
|
7
|
+
* scroll region, even from third-party code or libraries we don't control
|
|
8
|
+
* (ora, clack, etc.).
|
|
9
|
+
*
|
|
10
|
+
* Activated via XCODER_INTERCEPT_STDOUT=1 feature flag. Not enabled by
|
|
11
|
+
* default because monkey-patching process.stdout.write is fragile and
|
|
12
|
+
* can interfere with cursor-positioning ANSI sequences that clack/ora
|
|
13
|
+
* rely on.
|
|
14
|
+
*
|
|
15
|
+
* @param guard - The ScrollRegionGuard to route writes through.
|
|
16
|
+
* @returns A restore function that undoes the monkey-patch.
|
|
17
|
+
*/
|
|
18
|
+
export declare function interceptStdout(guard: ScrollRegionGuard): () => void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OutputInterceptor — monkey-patches `process.stdout.write` during the
|
|
3
|
+
* footer's active lifetime to route ALL writes through the ScrollRegionGuard.
|
|
4
|
+
*
|
|
5
|
+
* This is the "nuclear option" that guarantees no write can break the
|
|
6
|
+
* scroll region, even from third-party code or libraries we don't control
|
|
7
|
+
* (ora, clack, etc.).
|
|
8
|
+
*
|
|
9
|
+
* Activated via XCODER_INTERCEPT_STDOUT=1 feature flag. Not enabled by
|
|
10
|
+
* default because monkey-patching process.stdout.write is fragile and
|
|
11
|
+
* can interfere with cursor-positioning ANSI sequences that clack/ora
|
|
12
|
+
* rely on.
|
|
13
|
+
*
|
|
14
|
+
* @param guard - The ScrollRegionGuard to route writes through.
|
|
15
|
+
* @returns A restore function that undoes the monkey-patch.
|
|
16
|
+
*/
|
|
17
|
+
export function interceptStdout(guard) {
|
|
18
|
+
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
19
|
+
// Prevent double-patching
|
|
20
|
+
if (process.stdout.write._intercepted) {
|
|
21
|
+
return () => { };
|
|
22
|
+
}
|
|
23
|
+
process.stdout.write._intercepted = true;
|
|
24
|
+
process.stdout.write = function (chunk, encodingOrCb, cb) {
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString();
|
|
27
|
+
// Resolve callback arguments
|
|
28
|
+
let callback;
|
|
29
|
+
let encoding;
|
|
30
|
+
if (typeof encodingOrCb === "function") {
|
|
31
|
+
callback = encodingOrCb;
|
|
32
|
+
}
|
|
33
|
+
else if (encodingOrCb) {
|
|
34
|
+
encoding = encodingOrCb;
|
|
35
|
+
callback = cb;
|
|
36
|
+
}
|
|
37
|
+
// Guard: write safely through the scroll region
|
|
38
|
+
guard.write(text);
|
|
39
|
+
// Fire callback if provided
|
|
40
|
+
if (callback) {
|
|
41
|
+
callback();
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
};
|
|
45
|
+
return () => {
|
|
46
|
+
process.stdout.write._intercepted = false;
|
|
47
|
+
process.stdout.write = originalWrite;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface OutputPathsConfig {
|
|
2
|
+
/** Base directory for all reports. Defaults to ".reports". */
|
|
3
|
+
reportsDir?: string;
|
|
4
|
+
/** Base directory for all temporary files. Defaults to ".tmp". */
|
|
5
|
+
tmpDir?: string;
|
|
6
|
+
/** Working directory root. Defaults to process.cwd(). */
|
|
7
|
+
cwd?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface OutputPaths {
|
|
10
|
+
readonly reportsDir: string;
|
|
11
|
+
readonly tmpDir: string;
|
|
12
|
+
/** Sprint gate rejection + retro reports → <reportsDir>/sprint/ */
|
|
13
|
+
sprintReportsDir(): string;
|
|
14
|
+
/** AI chat debate reports → <reportsDir>/ai-chat/ */
|
|
15
|
+
aiChatReportsDir(): string;
|
|
16
|
+
/** Bench reports → <reportsDir>/bench/ */
|
|
17
|
+
benchReportsDir(): string;
|
|
18
|
+
/** Regression output → <reportsDir>/regression/ */
|
|
19
|
+
regressionDir(): string;
|
|
20
|
+
/** Instance swarm output → <reportsDir>/instance-swarms/ */
|
|
21
|
+
instanceSwarmDir(instanceId: string): string;
|
|
22
|
+
/** Atomic-write staging → <tmpDir>/atomic/ */
|
|
23
|
+
atomicDir(): string;
|
|
24
|
+
/** Protocol error log path → <reportsDir>/protocol_error.log */
|
|
25
|
+
protocolErrorLogPath(): string;
|
|
26
|
+
/** Bench report file path → <reportsDir>/bench/report-<ts>.json */
|
|
27
|
+
benchReportPath(ts?: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Ensures .gitignore contains entries for .reports/ and .tmp/.
|
|
30
|
+
* Lazily called on first write — fire-and-forget, never throws.
|
|
31
|
+
*/
|
|
32
|
+
ensureGitignore(): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns the singleton OutputPaths instance. On first call, the optional
|
|
36
|
+
* config is applied; subsequent calls ignore the config (singleton pattern).
|
|
37
|
+
*/
|
|
38
|
+
export declare function getOutputPaths(config?: OutputPathsConfig): OutputPaths;
|
|
39
|
+
/**
|
|
40
|
+
* Resets the singleton. Primarily for test isolation — call in afterEach.
|
|
41
|
+
* After reset, the next getOutputPaths() call creates a fresh instance.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resetOutputPaths(): void;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// xcoder:version 1 | xcoder:task swarm-swarm-is-b22287 | xcoder:updated 2026-08-04T12:11:06.168Z | xcoder:subtask swarm-swarm-is-b22287/1
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Singleton state
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
let _instance = null;
|
|
8
|
+
let _config;
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Constants
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const GITIGNORE_ENTRIES = [
|
|
13
|
+
"# Xcoder SDLC generated artifacts (managed by src/util/output-paths.ts)",
|
|
14
|
+
".reports/",
|
|
15
|
+
".tmp/",
|
|
16
|
+
".xcoder/",
|
|
17
|
+
"!.xcoder/blueprint.md",
|
|
18
|
+
];
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Implementation
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
class OutputPathsImpl {
|
|
23
|
+
reportsDir;
|
|
24
|
+
tmpDir;
|
|
25
|
+
cwd;
|
|
26
|
+
_gitignoreEnsured = false;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.cwd = config?.cwd ?? process.cwd();
|
|
29
|
+
this.reportsDir = path.resolve(this.cwd, config?.reportsDir ?? ".reports");
|
|
30
|
+
this.tmpDir = path.resolve(this.cwd, config?.tmpDir ?? ".tmp");
|
|
31
|
+
}
|
|
32
|
+
sprintReportsDir() {
|
|
33
|
+
return path.join(this.reportsDir, "sprint");
|
|
34
|
+
}
|
|
35
|
+
aiChatReportsDir() {
|
|
36
|
+
return path.join(this.reportsDir, "ai-chat");
|
|
37
|
+
}
|
|
38
|
+
benchReportsDir() {
|
|
39
|
+
return path.join(this.reportsDir, "bench");
|
|
40
|
+
}
|
|
41
|
+
regressionDir() {
|
|
42
|
+
return path.join(this.reportsDir, "regression");
|
|
43
|
+
}
|
|
44
|
+
instanceSwarmDir(instanceId) {
|
|
45
|
+
return path.join(this.reportsDir, "instance-swarms", instanceId);
|
|
46
|
+
}
|
|
47
|
+
atomicDir() {
|
|
48
|
+
return path.join(this.tmpDir, "atomic");
|
|
49
|
+
}
|
|
50
|
+
protocolErrorLogPath() {
|
|
51
|
+
return path.join(this.reportsDir, "protocol_error.log");
|
|
52
|
+
}
|
|
53
|
+
benchReportPath(ts) {
|
|
54
|
+
const timestamp = ts ?? new Date().toISOString().replace(/[:.]/g, "-");
|
|
55
|
+
return path.join(this.reportsDir, "bench", `report-${timestamp}.json`);
|
|
56
|
+
}
|
|
57
|
+
ensureGitignore() {
|
|
58
|
+
if (this._gitignoreEnsured)
|
|
59
|
+
return;
|
|
60
|
+
this._gitignoreEnsured = true;
|
|
61
|
+
try {
|
|
62
|
+
const gitignorePath = path.join(this.cwd, ".gitignore");
|
|
63
|
+
let existing = "";
|
|
64
|
+
let existingExists = false;
|
|
65
|
+
try {
|
|
66
|
+
existing = fs.readFileSync(gitignorePath, "utf-8");
|
|
67
|
+
existingExists = true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// File doesn't exist — that's fine, we'll create it.
|
|
71
|
+
}
|
|
72
|
+
const existingLines = existingExists
|
|
73
|
+
? existing.split("\n")
|
|
74
|
+
: [];
|
|
75
|
+
// Determine which entries are already present (exact match).
|
|
76
|
+
const missingEntries = GITIGNORE_ENTRIES.filter((entry) => {
|
|
77
|
+
// For the comment line, check if ANY comment about xcoder-managed is present.
|
|
78
|
+
if (entry.startsWith("# Xcoder SDLC")) {
|
|
79
|
+
return !existingLines.some((line) => line.startsWith("# Xcoder SDLC generated artifacts"));
|
|
80
|
+
}
|
|
81
|
+
// For dir entries, check exact match.
|
|
82
|
+
return !existingLines.some((line) => line.trim() === entry);
|
|
83
|
+
});
|
|
84
|
+
if (missingEntries.length === 0)
|
|
85
|
+
return;
|
|
86
|
+
const block = [
|
|
87
|
+
...(existingExists && existing.length > 0 && !existing.endsWith("\n")
|
|
88
|
+
? [""]
|
|
89
|
+
: []),
|
|
90
|
+
...missingEntries,
|
|
91
|
+
"",
|
|
92
|
+
].join("\n");
|
|
93
|
+
fs.appendFileSync(gitignorePath, block, "utf-8");
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Silently swallow — gitignore management is best-effort.
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Public API
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Returns the singleton OutputPaths instance. On first call, the optional
|
|
105
|
+
* config is applied; subsequent calls ignore the config (singleton pattern).
|
|
106
|
+
*/
|
|
107
|
+
export function getOutputPaths(config) {
|
|
108
|
+
if (!_instance) {
|
|
109
|
+
_config = config;
|
|
110
|
+
_instance = new OutputPathsImpl(config);
|
|
111
|
+
}
|
|
112
|
+
return _instance;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Resets the singleton. Primarily for test isolation — call in afterEach.
|
|
116
|
+
* After reset, the next getOutputPaths() call creates a fresh instance.
|
|
117
|
+
*/
|
|
118
|
+
export function resetOutputPaths() {
|
|
119
|
+
_instance = null;
|
|
120
|
+
_config = undefined;
|
|
121
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read package.json from workspace root if present, extract name/version/
|
|
3
|
+
* scripts/dependencies, and format as a context block injected into the
|
|
4
|
+
* system prompt. Non-fatal if absent.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readPackageJsonContext(root: string): Promise<string | null>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Read package.json from workspace root if present, extract name/version/
|
|
5
|
+
* scripts/dependencies, and format as a context block injected into the
|
|
6
|
+
* system prompt. Non-fatal if absent.
|
|
7
|
+
*/
|
|
8
|
+
export async function readPackageJsonContext(root) {
|
|
9
|
+
try {
|
|
10
|
+
const raw = await fs.readFile(path.join(root, "package.json"), "utf8");
|
|
11
|
+
const pkg = JSON.parse(raw);
|
|
12
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13
|
+
const depNames = Object.keys(deps).sort();
|
|
14
|
+
const depLines = depNames.length
|
|
15
|
+
? depNames.map((n) => ` ${n}: ${deps[n]}`).join("\n")
|
|
16
|
+
: " (none)";
|
|
17
|
+
const scriptLines = pkg.scripts
|
|
18
|
+
? Object.entries(pkg.scripts)
|
|
19
|
+
.map(([k, v]) => ` ${k}: ${v}`)
|
|
20
|
+
.join("\n")
|
|
21
|
+
: " (none)";
|
|
22
|
+
return `
|
|
23
|
+
## Project package.json
|
|
24
|
+
- name: ${pkg.name ?? "(not set)"}
|
|
25
|
+
- version: ${pkg.version ?? "(not set)"}
|
|
26
|
+
- description: ${pkg.description ?? "(not set)"}
|
|
27
|
+
- scripts:
|
|
28
|
+
${scriptLines}
|
|
29
|
+
- dependencies:
|
|
30
|
+
${depLines}
|
|
31
|
+
`.trim();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kill a process and all of its descendants.
|
|
3
|
+
*
|
|
4
|
+
* @param rootPid - The PID of the root process to kill.
|
|
5
|
+
* @param signal - 'SIGTERM' for graceful, 'SIGKILL' for force.
|
|
6
|
+
* @returns Promise that resolves when all descendants are confirmed dead or after 3s timeout.
|
|
7
|
+
*/
|
|
8
|
+
export declare function killProcessTree(rootPid: number, signal?: "SIGTERM" | "SIGKILL"): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* A Set of tracked child process PIDs. Used by the signal handler to
|
|
11
|
+
* kill any surviving child processes on forced exit.
|
|
12
|
+
*/
|
|
13
|
+
export declare const trackedPids: Set<number>;
|
|
14
|
+
/**
|
|
15
|
+
* Register a pid for cleanup tracking. Safe to call multiple times
|
|
16
|
+
* (Set deduplicates).
|
|
17
|
+
*/
|
|
18
|
+
export declare function trackPid(pid: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Remove a pid from tracking (called when process exits normally).
|
|
21
|
+
*/
|
|
22
|
+
export declare function untrackPid(pid: number): void;
|
|
23
|
+
/**
|
|
24
|
+
* Kill all tracked PIDs and clear the set. Used in process 'exit'
|
|
25
|
+
* handler as a safety net to prevent orphaned processes.
|
|
26
|
+
*/
|
|
27
|
+
export declare function killTrackedPids(): Promise<void>;
|