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,120 @@
|
|
|
1
|
+
/** Auto-Update Module — §3.5 of design-desktop-installer.md */
|
|
2
|
+
import { createWriteStream, createReadStream } from "node:fs";
|
|
3
|
+
import { unlink, mkdir } from "node:fs/promises";
|
|
4
|
+
import { pipeline } from "node:stream/promises";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
function getPlatformExtension() {
|
|
10
|
+
switch (process.platform) {
|
|
11
|
+
case "win32": return "exe";
|
|
12
|
+
case "darwin": return "dmg";
|
|
13
|
+
case "linux": return "deb";
|
|
14
|
+
default: return "tar.gz";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function getPlatformSuffix() {
|
|
18
|
+
const pmap = { win32: "win", darwin: "macos", linux: "linux" };
|
|
19
|
+
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
20
|
+
return `${pmap[process.platform] || process.platform}-${arch}`;
|
|
21
|
+
}
|
|
22
|
+
function semverCompare(a, b) {
|
|
23
|
+
const pa = a.replace(/^v/, "").split(".").map(Number);
|
|
24
|
+
const pb = b.replace(/^v/, "").split(".").map(Number);
|
|
25
|
+
for (let i = 0; i < 3; i++) {
|
|
26
|
+
const da = pa[i] || 0;
|
|
27
|
+
const db = pb[i] || 0;
|
|
28
|
+
if (da !== db)
|
|
29
|
+
return da - db;
|
|
30
|
+
}
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
async function sha256stream(filePath) {
|
|
34
|
+
const hash = createHash("sha256");
|
|
35
|
+
const stream = createReadStream(filePath);
|
|
36
|
+
for await (const chunk of stream) {
|
|
37
|
+
hash.update(chunk);
|
|
38
|
+
}
|
|
39
|
+
return hash.digest("hex");
|
|
40
|
+
}
|
|
41
|
+
function runProcess(cmd, args) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const proc = spawn(cmd, args, { stdio: "inherit" });
|
|
44
|
+
proc.on("close", (code) => {
|
|
45
|
+
code === 0 ? resolve() : reject(new Error(`${cmd} exited ${code}`));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export async function checkForUpdate(opts) {
|
|
50
|
+
const defaultValue = {
|
|
51
|
+
updateAvailable: false, latestVersion: null, downloadUrl: null, releaseNotes: null,
|
|
52
|
+
};
|
|
53
|
+
try {
|
|
54
|
+
const url = `https://api.github.com/repos/${opts.repo}/releases/latest`;
|
|
55
|
+
const response = await fetch(url, {
|
|
56
|
+
headers: {
|
|
57
|
+
Accept: "application/vnd.github+json",
|
|
58
|
+
"User-Agent": `xcoder-updater/${opts.currentVersion}`,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
if (!response.ok)
|
|
62
|
+
return defaultValue;
|
|
63
|
+
const release = (await response.json());
|
|
64
|
+
const latestVersion = release.tag_name.replace(/^v/, "");
|
|
65
|
+
if (semverCompare(latestVersion, opts.currentVersion) <= 0) {
|
|
66
|
+
return { ...defaultValue, latestVersion };
|
|
67
|
+
}
|
|
68
|
+
const platformSuffix = getPlatformSuffix();
|
|
69
|
+
const ext = getPlatformExtension();
|
|
70
|
+
const assetPattern = `xcoder-${latestVersion}-${platformSuffix}.${ext}`;
|
|
71
|
+
const asset = release.assets.find((a) => a.name === assetPattern);
|
|
72
|
+
if (!asset) {
|
|
73
|
+
return { ...defaultValue, latestVersion, releaseNotes: release.body };
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
updateAvailable: true,
|
|
77
|
+
latestVersion,
|
|
78
|
+
downloadUrl: asset.browser_download_url,
|
|
79
|
+
releaseNotes: release.body,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return defaultValue;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function downloadAndApplyUpdate(downloadUrl, sha256Url) {
|
|
87
|
+
const tmpDir = path.join(os.tmpdir(), "xcoder-update");
|
|
88
|
+
await mkdir(tmpDir, { recursive: true });
|
|
89
|
+
const ext = getPlatformExtension();
|
|
90
|
+
const installerPath = path.join(tmpDir, `xcoder-update.${ext}`);
|
|
91
|
+
try {
|
|
92
|
+
const response = await fetch(downloadUrl);
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`Download failed: ${response.status}`);
|
|
95
|
+
}
|
|
96
|
+
const fileStream = createWriteStream(installerPath);
|
|
97
|
+
await pipeline(response.body, fileStream);
|
|
98
|
+
const shaResponse = await fetch(sha256Url);
|
|
99
|
+
if (shaResponse.ok) {
|
|
100
|
+
const expectedHash = (await shaResponse.text()).trim().split(/\s+/)[0];
|
|
101
|
+
const actualHash = await sha256stream(installerPath);
|
|
102
|
+
if (expectedHash !== actualHash) {
|
|
103
|
+
throw new Error(`SHA256 mismatch`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (process.platform === "win32") {
|
|
107
|
+
await runProcess(installerPath, ["/S"]);
|
|
108
|
+
}
|
|
109
|
+
else if (process.platform === "darwin") {
|
|
110
|
+
console.log(`Update downloaded to ${installerPath}`);
|
|
111
|
+
console.log(`Please open the DMG and copy Xcoder SDLC.app to /Applications.`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
await runProcess("sudo", ["dpkg", "-i", installerPath]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await unlink(installerPath).catch(() => { });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write the full banner to stdout as one buffered frame.
|
|
3
|
+
*
|
|
4
|
+
* Unconditional — performs no gating (use maybePrintBanner for that). The
|
|
5
|
+
* content is a compile-time constant: no env var, path, model name, session id
|
|
6
|
+
* or other runtime data is ever interpolated, so terminal escape injection via
|
|
7
|
+
* this surface is structurally impossible.
|
|
8
|
+
*/
|
|
9
|
+
export declare function printBanner(): void;
|
|
10
|
+
/**
|
|
11
|
+
* Whether the banner may be shown for this invocation.
|
|
12
|
+
*
|
|
13
|
+
* Pure predicate — no writes, no memoisation, safe to call repeatedly. Gate
|
|
14
|
+
* order is fixed and first hit wins; every ambiguity resolves to "no banner"
|
|
15
|
+
* because a false positive corrupts piped or machine-parseable output while a
|
|
16
|
+
* false negative costs one cosmetic line.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isBannerEnabled(argv?: readonly string[]): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Gated banner emission, at most once per process.
|
|
21
|
+
*
|
|
22
|
+
* @param argv token list inspected for suppressing flags; defaults to process.argv.
|
|
23
|
+
* @returns true if this call wrote a banner; false if suppressed or already printed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function maybePrintBanner(argv?: readonly string[]): boolean;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// xcoder:version 2 | xcoder:task task-46e4c0 | xcoder:updated 2026-10-25T16:36:43.632Z | xcoder:subtask code-st-1329b0
|
|
2
|
+
// banner.ts — the Xcoder SDLC ASCII banner.
|
|
3
|
+
//
|
|
4
|
+
// Render manually with: npx tsx src/util/banner.ts
|
|
5
|
+
//
|
|
6
|
+
// This module is side-effect free at import time. Entry points call
|
|
7
|
+
// maybePrintBanner() so the banner is gated (interactive stdout, --json,
|
|
8
|
+
// --help/--version, --no-color, XCODER_NO_BANNER) and emitted at most once
|
|
9
|
+
// per process.
|
|
10
|
+
import { pathToFileURL } from "node:url";
|
|
11
|
+
import { isInteractiveStdout } from "./tty.js";
|
|
12
|
+
const FONT = {
|
|
13
|
+
X: ["██╗ ██╗", "╚██╗██╔╝", " ╚███╔╝ ", " ██╔██╗ ", "██╔╝ ██╗", "╚═╝ ╚═╝"],
|
|
14
|
+
C: [" ██████╗", "██╔════╝", "██║ ", "██║ ", "╚██████╗", " ╚═════╝"],
|
|
15
|
+
O: [" ██████╗ ", "██╔═══██╗", "██║ ██║", "██║ ██║", "╚██████╔╝", " ╚═════╝ "],
|
|
16
|
+
D: ["██████╗ ", "██╔══██╗", "██║ ██║", "██║ ██║", "██████╔╝", "╚═════╝ "],
|
|
17
|
+
E: ["███████╗", "██╔════╝", "█████╗ ", "██╔══╝ ", "███████╗", "╚══════╝"],
|
|
18
|
+
R: ["██████╗ ", "██╔══██╗", "██████╔╝", "██╔══██╗", "██║ ██║", "╚═╝ ╚═╝"],
|
|
19
|
+
S: ["███████╗", "██╔════╝", "███████╗", "╚════██║", "███████║", "╚══════╝"],
|
|
20
|
+
L: ["██╗ ", "██║ ", "██║ ", "██║ ", "███████╗", "╚══════╝"],
|
|
21
|
+
" ": [" ", " ", " ", " ", " ", " "],
|
|
22
|
+
};
|
|
23
|
+
const RESET = "\x1b[0m";
|
|
24
|
+
const BOLD = "\x1b[1m";
|
|
25
|
+
const DIM = "\x1b[2m";
|
|
26
|
+
const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
|
|
27
|
+
// Linear gradient between two RGB colors
|
|
28
|
+
function lerp(a, b, t) {
|
|
29
|
+
return a.map((v, i) => Math.round(v + (b[i] - v) * t));
|
|
30
|
+
}
|
|
31
|
+
function render(text) {
|
|
32
|
+
const rows = Array(6).fill("");
|
|
33
|
+
for (const ch of text.toUpperCase()) {
|
|
34
|
+
const glyph = FONT[ch] ?? FONT[" "];
|
|
35
|
+
const width = Math.max(...glyph.map((l) => l.length));
|
|
36
|
+
glyph.forEach((line, i) => (rows[i] += line.padEnd(width)));
|
|
37
|
+
}
|
|
38
|
+
return rows;
|
|
39
|
+
}
|
|
40
|
+
function gradient(line, from, to) {
|
|
41
|
+
const chars = [...line];
|
|
42
|
+
return (chars
|
|
43
|
+
.map((c, i) => {
|
|
44
|
+
if (c === " ")
|
|
45
|
+
return c;
|
|
46
|
+
const [r, g, b] = lerp(from, to, i / Math.max(chars.length - 1, 1));
|
|
47
|
+
return `${rgb(r, g, b)}${c}`;
|
|
48
|
+
})
|
|
49
|
+
.join("") + RESET);
|
|
50
|
+
}
|
|
51
|
+
function center(text, width) {
|
|
52
|
+
const visible = text.replace(/\x1b\[[0-9;]*m/g, "");
|
|
53
|
+
const pad = Math.max(0, Math.floor((width - visible.length) / 2));
|
|
54
|
+
return " ".repeat(pad) + text;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Write the full banner to stdout as one buffered frame.
|
|
58
|
+
*
|
|
59
|
+
* Unconditional — performs no gating (use maybePrintBanner for that). The
|
|
60
|
+
* content is a compile-time constant: no env var, path, model name, session id
|
|
61
|
+
* or other runtime data is ever interpolated, so terminal escape injection via
|
|
62
|
+
* this surface is structurally impossible.
|
|
63
|
+
*/
|
|
64
|
+
export function printBanner() {
|
|
65
|
+
const cyan = [0, 229, 255];
|
|
66
|
+
const violet = [168, 85, 247];
|
|
67
|
+
const xcoder = render("XCODER");
|
|
68
|
+
const sdlc = render("SDLC");
|
|
69
|
+
const width = Math.max(...xcoder.map((l) => l.length), ...sdlc.map((l) => l.length));
|
|
70
|
+
const rule = rgb(...violet) + "═".repeat(width) + RESET;
|
|
71
|
+
const lines = ["", rule, ""];
|
|
72
|
+
for (const line of xcoder)
|
|
73
|
+
lines.push(gradient(line, cyan, violet));
|
|
74
|
+
lines.push("");
|
|
75
|
+
for (const line of sdlc)
|
|
76
|
+
lines.push(gradient(line, violet, cyan));
|
|
77
|
+
lines.push("", rule);
|
|
78
|
+
lines.push(center(`${BOLD}${rgb(0, 229, 255)}By Neural Node Labs${RESET}`, width));
|
|
79
|
+
lines.push(center(`${DIM}Sir John Nueva${RESET}`, width));
|
|
80
|
+
lines.push("");
|
|
81
|
+
// One write: cannot interleave with an ora spinner and is trivially testable.
|
|
82
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Exact-token match (`--flag` or `--flag=value`), so a positional value that
|
|
86
|
+
* happens to contain a flag spelling cannot suppress the banner.
|
|
87
|
+
*/
|
|
88
|
+
function hasFlag(argv, ...names) {
|
|
89
|
+
return argv.some((arg) => names.includes(arg.split("=", 1)[0]));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Whether the banner may be shown for this invocation.
|
|
93
|
+
*
|
|
94
|
+
* Pure predicate — no writes, no memoisation, safe to call repeatedly. Gate
|
|
95
|
+
* order is fixed and first hit wins; every ambiguity resolves to "no banner"
|
|
96
|
+
* because a false positive corrupts piped or machine-parseable output while a
|
|
97
|
+
* false negative costs one cosmetic line.
|
|
98
|
+
*/
|
|
99
|
+
export function isBannerEnabled(argv = process.argv) {
|
|
100
|
+
// 1. Explicit operator opt-out wins over everything. Unset, empty, "0" and
|
|
101
|
+
// "false" (case-insensitive) mean "not opted out"; any other value means
|
|
102
|
+
// disabled — an operator should not have to guess a spelling.
|
|
103
|
+
const optOut = process.env.XCODER_NO_BANNER;
|
|
104
|
+
if (optOut !== undefined) {
|
|
105
|
+
const value = optOut.toLowerCase();
|
|
106
|
+
if (value !== "" && value !== "0" && value !== "false")
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
// 2. Machine-readable output must stay parseable (worker writes JSON to stdout).
|
|
110
|
+
if (hasFlag(argv, "--json"))
|
|
111
|
+
return false;
|
|
112
|
+
// 3. Help and version text is asserted verbatim.
|
|
113
|
+
if (hasFlag(argv, "--help", "-h", "--version", "-V"))
|
|
114
|
+
return false;
|
|
115
|
+
// 4. Read argv because `--no-color` only lands in process.env after parse.
|
|
116
|
+
if (hasFlag(argv, "--no-color"))
|
|
117
|
+
return false;
|
|
118
|
+
// 5. Pipes, redirection, CI and NO_COLOR.
|
|
119
|
+
return isInteractiveStdout();
|
|
120
|
+
}
|
|
121
|
+
let alreadyPrinted = false;
|
|
122
|
+
/**
|
|
123
|
+
* Gated banner emission, at most once per process.
|
|
124
|
+
*
|
|
125
|
+
* @param argv token list inspected for suppressing flags; defaults to process.argv.
|
|
126
|
+
* @returns true if this call wrote a banner; false if suppressed or already printed.
|
|
127
|
+
*/
|
|
128
|
+
export function maybePrintBanner(argv = process.argv) {
|
|
129
|
+
if (alreadyPrinted)
|
|
130
|
+
return false;
|
|
131
|
+
if (!isBannerEnabled(argv))
|
|
132
|
+
return false;
|
|
133
|
+
printBanner();
|
|
134
|
+
alreadyPrinted = true;
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
// ESM self-run guard: `npx tsx src/util/banner.ts` must still render.
|
|
138
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
139
|
+
printBanner();
|
|
140
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given an unknown command name and a list of known commands, return up to
|
|
3
|
+
* `maxSuggestions` (default 3) closest matches sorted by Levenshtein distance
|
|
4
|
+
* then alphabetically, filtered to a maximum distance of `threshold` (default 3).
|
|
5
|
+
*/
|
|
6
|
+
export declare function suggestCommands(unknown: string, known: string[], maxSuggestions?: number, threshold?: number): string[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// xcoder:version 1 | xcoder:task task-c776f2 | xcoder:updated 2026-08-03T19:28:41.385Z | xcoder:subtask code-st-5c254e
|
|
2
|
+
import { levenshtein } from "./levenshtein.js";
|
|
3
|
+
/**
|
|
4
|
+
* Given an unknown command name and a list of known commands, return up to
|
|
5
|
+
* `maxSuggestions` (default 3) closest matches sorted by Levenshtein distance
|
|
6
|
+
* then alphabetically, filtered to a maximum distance of `threshold` (default 3).
|
|
7
|
+
*/
|
|
8
|
+
export function suggestCommands(unknown, known, maxSuggestions = 3, threshold = 3) {
|
|
9
|
+
const target = unknown.toLowerCase();
|
|
10
|
+
const candidates = [];
|
|
11
|
+
for (const cmd of known) {
|
|
12
|
+
const distance = levenshtein(target, cmd);
|
|
13
|
+
if (distance <= threshold) {
|
|
14
|
+
candidates.push({ name: cmd, distance });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Sort by distance ascending, then alphabetically by command name
|
|
18
|
+
candidates.sort((a, b) => {
|
|
19
|
+
if (a.distance !== b.distance) {
|
|
20
|
+
return a.distance - b.distance;
|
|
21
|
+
}
|
|
22
|
+
return a.name.localeCompare(b.name);
|
|
23
|
+
});
|
|
24
|
+
return candidates.slice(0, maxSuggestions).map((c) => c.name);
|
|
25
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display a Yes/No confirmation dialog using @clack/prompts.
|
|
3
|
+
*
|
|
4
|
+
* Callers are responsible for toggling raw mode on/off around the
|
|
5
|
+
* call — this utility assumes stdin is in cooked mode when called
|
|
6
|
+
* (Tradeoff 4: caller-owned lifecycle for clear ownership).
|
|
7
|
+
*
|
|
8
|
+
* @returns true if the user confirms Yes, false otherwise (including on cancel or error).
|
|
9
|
+
*/
|
|
10
|
+
export declare function confirmInterrupt(): Promise<boolean>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// xcoder:version 3 | xcoder:task task-0684a6 | xcoder:updated 2026-08-03T10:25:48.381Z | xcoder:subtask test-st-5ed700
|
|
2
|
+
import * as clack from "@clack/prompts";
|
|
3
|
+
/**
|
|
4
|
+
* Display a Yes/No confirmation dialog using @clack/prompts.
|
|
5
|
+
*
|
|
6
|
+
* Callers are responsible for toggling raw mode on/off around the
|
|
7
|
+
* call — this utility assumes stdin is in cooked mode when called
|
|
8
|
+
* (Tradeoff 4: caller-owned lifecycle for clear ownership).
|
|
9
|
+
*
|
|
10
|
+
* @returns true if the user confirms Yes, false otherwise (including on cancel or error).
|
|
11
|
+
*/
|
|
12
|
+
export async function confirmInterrupt() {
|
|
13
|
+
try {
|
|
14
|
+
const answer = await clack.confirm({
|
|
15
|
+
message: "Do you want to interrupt the current run?",
|
|
16
|
+
active: "Yes",
|
|
17
|
+
inactive: "No",
|
|
18
|
+
initialValue: false,
|
|
19
|
+
});
|
|
20
|
+
// clack.confirm returns boolean | symbol (for cancel)
|
|
21
|
+
if (clack.isCancel(answer)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
// Coerce to strict boolean: true on Yes, false on anything else (No, null, undefined, etc.)
|
|
25
|
+
return Boolean(answer);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// If the prompt fails for any reason (e.g. non-TTY), default to not interrupting.
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matches the plain-language way people actually ask to keep going:
|
|
3
|
+
* "continue", "resume", "keep going", "carry on" — with or without more
|
|
4
|
+
* words after it ("continue on the UI", "continue write code"). This is
|
|
5
|
+
* what makes typing "continue" resume the actual unfinished task instead
|
|
6
|
+
* of Task Factory building a brand-new DAG from scratch and re-deriving
|
|
7
|
+
* requirements/design it already has — see `SdlcOrchestrator.resume()`.
|
|
8
|
+
* A referenced document supplies new task context and must go through
|
|
9
|
+
* normal task planning, even when the goal starts with "continue".
|
|
10
|
+
*/
|
|
11
|
+
export declare function isContinueTrigger(taskText: string): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matches the plain-language way people actually ask to keep going:
|
|
3
|
+
* "continue", "resume", "keep going", "carry on" — with or without more
|
|
4
|
+
* words after it ("continue on the UI", "continue write code"). This is
|
|
5
|
+
* what makes typing "continue" resume the actual unfinished task instead
|
|
6
|
+
* of Task Factory building a brand-new DAG from scratch and re-deriving
|
|
7
|
+
* requirements/design it already has — see `SdlcOrchestrator.resume()`.
|
|
8
|
+
* A referenced document supplies new task context and must go through
|
|
9
|
+
* normal task planning, even when the goal starts with "continue".
|
|
10
|
+
*/
|
|
11
|
+
export function isContinueTrigger(taskText) {
|
|
12
|
+
const text = taskText.trim();
|
|
13
|
+
if (!/^(continue|resume|keep going|carry on)\b/i.test(text))
|
|
14
|
+
return false;
|
|
15
|
+
// Anchor each whitespace-delimited token so a long non-path token is
|
|
16
|
+
// scanned once rather than retried at every character. Accept closing
|
|
17
|
+
// Markdown/quotation punctuation, but not suffixes such as .md.backup.
|
|
18
|
+
const referencesDocument = text.split(/\s+/).some((token) => /^\S+\.(?:md|mdx|txt|rst|pdf|docx?|ya?ml|json)["'`\])}>,;:.!?]*$/i.test(token));
|
|
19
|
+
return !referencesDocument;
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface EnvironmentInfo {
|
|
2
|
+
platform: NodeJS.Platform;
|
|
3
|
+
isWindows: boolean;
|
|
4
|
+
shellHint: string;
|
|
5
|
+
pathSep: string;
|
|
6
|
+
release: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function getEnvironmentInfo(): EnvironmentInfo;
|
|
9
|
+
/**
|
|
10
|
+
* Injected into every Sub Agent's system prompt. This exists because a
|
|
11
|
+
* model that assumes `ls`, `grep`, `rm -rf`, `cat`, or unix path
|
|
12
|
+
* separators will happily emit a `run_command` action that fails outright
|
|
13
|
+
* on native Windows — and with no isolation around that failure, it looked
|
|
14
|
+
* like the agent "couldn't refactor" when it was actually just running the
|
|
15
|
+
* wrong shell dialect. Telling it up front, plus giving it glob/grep/edit
|
|
16
|
+
* tools that work identically on every platform, closes that gap.
|
|
17
|
+
*/
|
|
18
|
+
export declare function environmentPromptBlock(env: EnvironmentInfo): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
export function getEnvironmentInfo() {
|
|
3
|
+
const platform = process.platform;
|
|
4
|
+
const isWindows = platform === "win32";
|
|
5
|
+
return {
|
|
6
|
+
platform,
|
|
7
|
+
isWindows,
|
|
8
|
+
pathSep: isWindows ? "\\" : "/",
|
|
9
|
+
release: os.release(),
|
|
10
|
+
shellHint: isWindows
|
|
11
|
+
? "PowerShell or cmd.exe (unless run under WSL/Git Bash, in which case a real unix shell is available)"
|
|
12
|
+
: "a POSIX shell (bash/zsh/sh)",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Injected into every Sub Agent's system prompt. This exists because a
|
|
17
|
+
* model that assumes `ls`, `grep`, `rm -rf`, `cat`, or unix path
|
|
18
|
+
* separators will happily emit a `run_command` action that fails outright
|
|
19
|
+
* on native Windows — and with no isolation around that failure, it looked
|
|
20
|
+
* like the agent "couldn't refactor" when it was actually just running the
|
|
21
|
+
* wrong shell dialect. Telling it up front, plus giving it glob/grep/edit
|
|
22
|
+
* tools that work identically on every platform, closes that gap.
|
|
23
|
+
*/
|
|
24
|
+
export function environmentPromptBlock(env) {
|
|
25
|
+
const warning = env.isWindows
|
|
26
|
+
? `You are running on Windows (${env.release}), most likely under ${env.shellHint}. ` +
|
|
27
|
+
`Unix-only commands (\`ls\`, \`grep\`, \`cat\`, \`rm -rf\`, \`&&\` chaining in cmd.exe, \`/\`-only paths) ` +
|
|
28
|
+
`may not exist or may behave differently here. Prefer the \`glob\`, \`grep\`, \`read_file\`, \`edit_file\`, ` +
|
|
29
|
+
`and \`write_file\` tools over shell equivalents for anything file-related — they work identically on every ` +
|
|
30
|
+
`platform. Only reach for \`run_command\` for things that genuinely need a real process (build/test/lint/` +
|
|
31
|
+
`type-check commands, git, package managers), and use PowerShell-compatible syntax when you do.`
|
|
32
|
+
: `You are running on ${env.platform} under ${env.shellHint}. Standard unix commands are available via ` +
|
|
33
|
+
`\`run_command\`, but still prefer \`glob\`/\`grep\`/\`read_file\`/\`edit_file\` over shelling out to ` +
|
|
34
|
+
`\`find\`/\`grep\`/\`cat\`/\`sed\` for file operations — they're faster, and their output is structured ` +
|
|
35
|
+
`instead of raw text you'd have to re-parse.`;
|
|
36
|
+
return `## Runtime environment\n${warning}`;
|
|
37
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type FileOp = "read" | "write" | "search" | "list" | "index" | "exec" | "git" | "skill" | "lesson";
|
|
2
|
+
export interface FileAccess {
|
|
3
|
+
op: FileOp;
|
|
4
|
+
/** Workspace-relative path / glob (as the agent supplied it), or the command line. */
|
|
5
|
+
target: string;
|
|
6
|
+
kind: "path" | "glob" | "dir" | "cmd";
|
|
7
|
+
}
|
|
8
|
+
/** Strip obvious secrets before a command line is written to disk. */
|
|
9
|
+
export declare function redactSecrets(text: string): string;
|
|
10
|
+
/** Pure: map a tool name + input to the files/commands it touches. Never throws. */
|
|
11
|
+
export declare function extractFileAccess(toolName: string, input: unknown): FileAccess[];
|
|
12
|
+
/**
|
|
13
|
+
* Record one line per task run describing the index freshness gate.
|
|
14
|
+
*
|
|
15
|
+
* Reuses the existing `index` FileOp and the same log sink as every other
|
|
16
|
+
* tool call — the gate is observability, not a new datastore.
|
|
17
|
+
*/
|
|
18
|
+
export declare function logIndexRefresh(root: string, result: {
|
|
19
|
+
action: string;
|
|
20
|
+
reason: string;
|
|
21
|
+
counts?: {
|
|
22
|
+
added: number;
|
|
23
|
+
removed: number;
|
|
24
|
+
changed: number;
|
|
25
|
+
};
|
|
26
|
+
durationMs?: number;
|
|
27
|
+
}, ctx?: {
|
|
28
|
+
taskId: string;
|
|
29
|
+
subtaskId: string;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
/** Best-effort: write one `files.log` line per file/command the call touched. Never throws. */
|
|
32
|
+
export declare function logFileAccess(root: string, toolName: string, input: unknown, ok: boolean, ctx?: {
|
|
33
|
+
taskId: string;
|
|
34
|
+
subtaskId: string;
|
|
35
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// xcoder:version 1 | xcoder:task task-e16f85 | xcoder:updated 2026-10-30T18:14:32.237Z | xcoder:subtask code-st-791be4
|
|
2
|
+
// Describes which files / commands a tool call touches, for `files.log`.
|
|
3
|
+
import { appendFiles } from "./logs.js";
|
|
4
|
+
const MAX_CMD = 2000;
|
|
5
|
+
/** Strip obvious secrets before a command line is written to disk. */
|
|
6
|
+
export function redactSecrets(text) {
|
|
7
|
+
return text
|
|
8
|
+
.replace(/\b(?:sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g, "[REDACTED]")
|
|
9
|
+
.replace(/(:\/\/[^\s:@/]+:)[^\s@/]+@/g, "$1[REDACTED]@")
|
|
10
|
+
.replace(/(authorization:\s*(?:bearer|basic)\s+)\S+/gi, "$1[REDACTED]")
|
|
11
|
+
.replace(/\b([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)=)\S+/g, "$1[REDACTED]");
|
|
12
|
+
}
|
|
13
|
+
const str = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
|
|
14
|
+
/** Pure: map a tool name + input to the files/commands it touches. Never throws. */
|
|
15
|
+
export function extractFileAccess(toolName, input) {
|
|
16
|
+
const i = (input && typeof input === "object" ? input : {});
|
|
17
|
+
const out = [];
|
|
18
|
+
const path = (op, v, kind = "path") => {
|
|
19
|
+
const t = str(v);
|
|
20
|
+
if (t)
|
|
21
|
+
out.push({ op, target: t, kind });
|
|
22
|
+
};
|
|
23
|
+
switch (toolName) {
|
|
24
|
+
case "read_file":
|
|
25
|
+
case "read_outline":
|
|
26
|
+
case "read_full_file":
|
|
27
|
+
case "get_dependency_graph":
|
|
28
|
+
case "validate_file":
|
|
29
|
+
path("read", i.path);
|
|
30
|
+
break;
|
|
31
|
+
case "read_multiple_files":
|
|
32
|
+
if (Array.isArray(i.files))
|
|
33
|
+
for (const f of i.files)
|
|
34
|
+
path("read", f?.path);
|
|
35
|
+
break;
|
|
36
|
+
case "write_file":
|
|
37
|
+
case "edit_file":
|
|
38
|
+
case "line_patch":
|
|
39
|
+
case "apply_unified_diff":
|
|
40
|
+
case "update_function":
|
|
41
|
+
case "rename_symbol":
|
|
42
|
+
path("write", i.path);
|
|
43
|
+
break;
|
|
44
|
+
case "sed_tool":
|
|
45
|
+
path("write", i.path);
|
|
46
|
+
path("write", i.glob, "glob");
|
|
47
|
+
break;
|
|
48
|
+
case "list_dir":
|
|
49
|
+
path("list", i.dir, "dir");
|
|
50
|
+
break;
|
|
51
|
+
case "glob":
|
|
52
|
+
path("search", i.pattern, "glob");
|
|
53
|
+
break;
|
|
54
|
+
case "grep":
|
|
55
|
+
path("search", i.pathGlob ?? "**/*", "glob");
|
|
56
|
+
break;
|
|
57
|
+
case "search_ast":
|
|
58
|
+
path("search", i.pathGlob ?? "**/*", "glob");
|
|
59
|
+
break;
|
|
60
|
+
case "search_workspace":
|
|
61
|
+
case "reindex_workspace":
|
|
62
|
+
out.push({ op: "index", target: ".", kind: "dir" });
|
|
63
|
+
break;
|
|
64
|
+
case "git_diff":
|
|
65
|
+
case "git_log":
|
|
66
|
+
out.push({ op: "git", target: str(i.path) ?? ".", kind: "path" });
|
|
67
|
+
break;
|
|
68
|
+
case "github_clone":
|
|
69
|
+
out.push({ op: "write", target: str(i.dir) ?? ".", kind: "dir" });
|
|
70
|
+
break;
|
|
71
|
+
case "github_commit":
|
|
72
|
+
case "github_push":
|
|
73
|
+
out.push({ op: "git", target: str(i.cwd) ?? ".", kind: "dir" });
|
|
74
|
+
break;
|
|
75
|
+
case "load_skill":
|
|
76
|
+
out.push({ op: "skill", target: str(i.role) ?? str(i.keyword) ?? "*", kind: "path" });
|
|
77
|
+
break;
|
|
78
|
+
case "record_lesson":
|
|
79
|
+
out.push({ op: "lesson", target: ".xcoder/lesson.md", kind: "path" });
|
|
80
|
+
break;
|
|
81
|
+
case "browse":
|
|
82
|
+
if (Array.isArray(i.steps)) {
|
|
83
|
+
for (const st of i.steps) {
|
|
84
|
+
if (st?.op === "screenshot")
|
|
85
|
+
out.push({ op: "write", target: str(st.path) ?? ".xcoder/screenshots", kind: str(st.path) ? "path" : "dir" });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
case "run_command":
|
|
90
|
+
path("exec", i.command, "cmd");
|
|
91
|
+
break;
|
|
92
|
+
case "schedule":
|
|
93
|
+
if (str(i.command))
|
|
94
|
+
path("exec", i.command, "cmd");
|
|
95
|
+
break;
|
|
96
|
+
default:
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
function oneLine(s) {
|
|
102
|
+
return s.replace(/\r?\n/g, "\\n");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Record one line per task run describing the index freshness gate.
|
|
106
|
+
*
|
|
107
|
+
* Reuses the existing `index` FileOp and the same log sink as every other
|
|
108
|
+
* tool call — the gate is observability, not a new datastore.
|
|
109
|
+
*/
|
|
110
|
+
export async function logIndexRefresh(root, result, ctx) {
|
|
111
|
+
try {
|
|
112
|
+
const taskId = ctx?.taskId ?? "session";
|
|
113
|
+
const subtaskId = ctx?.subtaskId ?? "-";
|
|
114
|
+
const c = result.counts ?? { added: 0, removed: 0, changed: 0 };
|
|
115
|
+
const line = [
|
|
116
|
+
"tool=workspace_index op=index ok=true",
|
|
117
|
+
`dir=.,action=${result.action},reason=${result.reason}`,
|
|
118
|
+
`added=${c.added},removed=${c.removed},changed=${c.changed}`,
|
|
119
|
+
`durationMs=${result.durationMs ?? 0}`,
|
|
120
|
+
].join(" ");
|
|
121
|
+
await appendFiles(root, taskId, subtaskId, oneLine(redactSecrets(line)));
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// logging must never affect the task
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** Best-effort: write one `files.log` line per file/command the call touched. Never throws. */
|
|
128
|
+
export async function logFileAccess(root, toolName, input, ok, ctx) {
|
|
129
|
+
try {
|
|
130
|
+
const taskId = ctx?.taskId ?? "session";
|
|
131
|
+
const subtaskId = ctx?.subtaskId ?? "-";
|
|
132
|
+
for (const a of extractFileAccess(toolName, input)) {
|
|
133
|
+
const field = a.kind === "cmd" ? `cmd=${oneLine(redactSecrets(a.target)).slice(0, MAX_CMD)}` : `${a.kind}=${oneLine(a.target)}`;
|
|
134
|
+
await appendFiles(root, taskId, subtaskId, `tool=${toolName} op=${a.op} ok=${ok} ${field}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// logging must never affect the tool call
|
|
139
|
+
}
|
|
140
|
+
}
|