xcoder-dna 0.0.0-stage → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +66 -0
- package/README.md +87 -2
- package/dist/agents/ai-chat-debate.d.ts +36 -0
- package/dist/agents/ai-chat-debate.js +300 -0
- package/dist/agents/ai-chat-report-writer.d.ts +7 -0
- package/dist/agents/ai-chat-report-writer.js +100 -0
- package/dist/agents/duplicate-detector.d.ts +41 -0
- package/dist/agents/duplicate-detector.js +65 -0
- package/dist/agents/genesis-agent.d.ts +61 -0
- package/dist/agents/genesis-agent.js +340 -0
- package/dist/agents/samurai-agent.d.ts +53 -0
- package/dist/agents/samurai-agent.js +279 -0
- package/dist/agents/sub-agent.d.ts +56 -0
- package/dist/agents/sub-agent.js +353 -0
- package/dist/backlog/backlog-requirement-validator.d.ts +10 -0
- package/dist/backlog/backlog-requirement-validator.js +60 -0
- package/dist/backlog/backlog-store.d.ts +29 -0
- package/dist/backlog/backlog-store.js +148 -0
- package/dist/backlog/po-agent.d.ts +16 -0
- package/dist/backlog/po-agent.js +121 -0
- package/dist/backlog/types.d.ts +25 -0
- package/dist/backlog/types.js +4 -0
- package/dist/bench/run.d.ts +11 -0
- package/dist/bench/run.js +185 -0
- package/dist/bench/types.d.ts +50 -0
- package/dist/bench/types.js +1 -0
- package/dist/cli/swarm.d.ts +10 -0
- package/dist/cli/swarm.js +100 -0
- package/dist/cli/task.d.ts +14 -0
- package/dist/cli/task.js +121 -0
- package/dist/cli/wbc-arg.d.ts +6 -0
- package/dist/cli/wbc-arg.js +40 -0
- package/dist/cli/wbc.d.ts +45 -0
- package/dist/cli/wbc.js +98 -0
- package/dist/commands/license.d.ts +9 -0
- package/dist/commands/license.js +140 -0
- package/dist/commands/release.d.ts +44 -0
- package/dist/commands/release.js +180 -0
- package/dist/commands/test.d.ts +50 -0
- package/dist/commands/test.js +385 -0
- package/dist/config/loadConfig.d.ts +82 -0
- package/dist/config/loadConfig.js +240 -0
- package/dist/config/secure-mode.d.ts +29 -0
- package/dist/config/secure-mode.js +63 -0
- package/dist/config/user-config.d.ts +39 -0
- package/dist/config/user-config.js +202 -0
- package/dist/engine/reasoning-engine.d.ts +21 -0
- package/dist/engine/reasoning-engine.js +59 -0
- package/dist/events/bus.d.ts +22 -0
- package/dist/events/bus.js +18 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +77 -0
- package/dist/indexing/ignoreRules.d.ts +1 -0
- package/dist/indexing/ignoreRules.js +53 -0
- package/dist/instance-swarm/instance-swarm-orchestrator.d.ts +57 -0
- package/dist/instance-swarm/instance-swarm-orchestrator.js +204 -0
- package/dist/instance-swarm/protocol-name-resolver.d.ts +23 -0
- package/dist/instance-swarm/protocol-name-resolver.js +64 -0
- package/dist/license/gate.d.ts +31 -0
- package/dist/license/gate.js +188 -0
- package/dist/license/http-client.d.ts +43 -0
- package/dist/license/http-client.js +195 -0
- package/dist/license/manager.d.ts +39 -0
- package/dist/license/manager.js +175 -0
- package/dist/license/receipt.d.ts +27 -0
- package/dist/license/receipt.js +108 -0
- package/dist/license/state-codec.d.ts +72 -0
- package/dist/license/state-codec.js +218 -0
- package/dist/license/state-paths.d.ts +17 -0
- package/dist/license/state-paths.js +64 -0
- package/dist/license/store.d.ts +44 -0
- package/dist/license/store.js +223 -0
- package/dist/license/trust-anchor.d.ts +24 -0
- package/dist/license/trust-anchor.js +74 -0
- package/dist/license/types.d.ts +34 -0
- package/dist/license/types.js +19 -0
- package/dist/llm/budget.d.ts +23 -0
- package/dist/llm/budget.js +58 -0
- package/dist/llm/client.d.ts +53 -0
- package/dist/llm/client.js +122 -0
- package/dist/llm/factory.d.ts +14 -0
- package/dist/llm/factory.js +32 -0
- package/dist/orchestrator/defect-logger.d.ts +31 -0
- package/dist/orchestrator/defect-logger.js +101 -0
- package/dist/orchestrator/defect-manager.d.ts +118 -0
- package/dist/orchestrator/defect-manager.js +338 -0
- package/dist/orchestrator/defect-migration.d.ts +14 -0
- package/dist/orchestrator/defect-migration.js +174 -0
- package/dist/orchestrator/defect-store.d.ts +48 -0
- package/dist/orchestrator/defect-store.js +76 -0
- package/dist/orchestrator/healing.d.ts +14 -0
- package/dist/orchestrator/healing.js +11 -0
- package/dist/orchestrator/interactive-steering.d.ts +64 -0
- package/dist/orchestrator/interactive-steering.js +107 -0
- package/dist/orchestrator/interrupt-handler.d.ts +30 -0
- package/dist/orchestrator/interrupt-handler.js +70 -0
- package/dist/orchestrator/lesson-manager.d.ts +25 -0
- package/dist/orchestrator/lesson-manager.js +122 -0
- package/dist/orchestrator/progress-summary.d.ts +15 -0
- package/dist/orchestrator/progress-summary.js +106 -0
- package/dist/orchestrator/reasoning-score.d.ts +49 -0
- package/dist/orchestrator/reasoning-score.js +117 -0
- package/dist/orchestrator/swarm.d.ts +36 -0
- package/dist/orchestrator/swarm.js +77 -0
- package/dist/orchestrator/task-factory.d.ts +15 -0
- package/dist/orchestrator/task-factory.js +176 -0
- package/dist/orchestrator/validation-gate.d.ts +23 -0
- package/dist/orchestrator/validation-gate.js +155 -0
- package/dist/protocol/react-contract.d.ts +136 -0
- package/dist/protocol/react-contract.js +698 -0
- package/dist/protocol/wks-protocol-manager.d.ts +22 -0
- package/dist/protocol/wks-protocol-manager.js +72 -0
- package/dist/sdlc-runtime/conversation-runner.d.ts +8 -0
- package/dist/sdlc-runtime/conversation-runner.js +64 -0
- package/dist/sdlc-runtime/dag-utils.d.ts +15 -0
- package/dist/sdlc-runtime/dag-utils.js +67 -0
- package/dist/sdlc-runtime/defect-integration.d.ts +7 -0
- package/dist/sdlc-runtime/defect-integration.js +99 -0
- package/dist/sdlc-runtime/execution-loop.d.ts +38 -0
- package/dist/sdlc-runtime/execution-loop.js +274 -0
- package/dist/sdlc-runtime/lesson-recorder.d.ts +9 -0
- package/dist/sdlc-runtime/lesson-recorder.js +27 -0
- package/dist/sdlc-runtime/plan-initializer.d.ts +47 -0
- package/dist/sdlc-runtime/plan-initializer.js +135 -0
- package/dist/sdlc-runtime/progress-persister.d.ts +10 -0
- package/dist/sdlc-runtime/progress-persister.js +34 -0
- package/dist/sdlc-runtime/protocol-loader.d.ts +10 -0
- package/dist/sdlc-runtime/protocol-loader.js +21 -0
- package/dist/sdlc-runtime/reporting.d.ts +24 -0
- package/dist/sdlc-runtime/reporting.js +68 -0
- package/dist/sdlc-runtime/sdlc-orchestrator.d.ts +59 -0
- package/dist/sdlc-runtime/sdlc-orchestrator.js +170 -0
- package/dist/sdlc-runtime/sdlc-skill.d.ts +44 -0
- package/dist/sdlc-runtime/sdlc-skill.js +148 -0
- package/dist/sdlc-runtime/steering-handler.d.ts +14 -0
- package/dist/sdlc-runtime/steering-handler.js +51 -0
- package/dist/sdlc-runtime/trace-recorder.d.ts +10 -0
- package/dist/sdlc-runtime/trace-recorder.js +7 -0
- package/dist/security/index.d.ts +16 -0
- package/dist/security/index.js +12 -0
- package/dist/security/input-screen.d.ts +30 -0
- package/dist/security/input-screen.js +75 -0
- package/dist/security/tool-call-guard.d.ts +16 -0
- package/dist/security/tool-call-guard.js +163 -0
- package/dist/security/untrusted-content.d.ts +34 -0
- package/dist/security/untrusted-content.js +63 -0
- package/dist/skills/advanced-dfir/SKILL.md +85 -0
- package/dist/skills/advanced-pentest-lab/SKILL.md +88 -0
- package/dist/skills/analyst/SKILL.md +55 -0
- package/dist/skills/architect/SKILL.md +54 -0
- package/dist/skills/aws/SKILL.md +92 -0
- package/dist/skills/azure/SKILL.md +90 -0
- package/dist/skills/bylaw-context.d.ts +45 -0
- package/dist/skills/bylaw-context.js +61 -0
- package/dist/skills/bylaw-review.d.ts +20 -0
- package/dist/skills/bylaw-review.js +101 -0
- package/dist/skills/conversation/SKILL.md +29 -0
- package/dist/skills/design-patterns/SKILL.md +190 -0
- package/dist/skills/devops/SKILL.md +55 -0
- package/dist/skills/directives.d.ts +20 -0
- package/dist/skills/directives.js +313 -0
- package/dist/skills/docker/SKILL.md +76 -0
- package/dist/skills/docker-expert/SKILL.md +56 -0
- package/dist/skills/enterprise-systems-analyst/SKILL.md +83 -0
- package/dist/skills/enterprise-systems-analyst/references/crm.md +41 -0
- package/dist/skills/enterprise-systems-analyst/references/erp.md +40 -0
- package/dist/skills/enterprise-systems-analyst/references/financial-systems.md +35 -0
- package/dist/skills/enterprise-systems-analyst/references/hcm.md +38 -0
- package/dist/skills/enterprise-systems-analyst/references/mrp.md +37 -0
- package/dist/skills/enterprise-systems-analyst/references/scm.md +32 -0
- package/dist/skills/ethical-hacking-methodology/SKILL.md +119 -0
- package/dist/skills/externalSkillLoader.d.ts +25 -0
- package/dist/skills/externalSkillLoader.js +65 -0
- package/dist/skills/externalSkillRegistry.d.ts +33 -0
- package/dist/skills/externalSkillRegistry.js +94 -0
- package/dist/skills/filesystem-management/SKILL.md +61 -0
- package/dist/skills/first-run-seed.d.ts +26 -0
- package/dist/skills/first-run-seed.js +75 -0
- package/dist/skills/git-vcs/SKILL.md +84 -0
- package/dist/skills/kafka/SKILL.md +89 -0
- package/dist/skills/kubernetes/SKILL.md +93 -0
- package/dist/skills/kubernetes-expert/SKILL.md +54 -0
- package/dist/skills/openshift/SKILL.md +93 -0
- package/dist/skills/pentester/SKILL.md +59 -0
- package/dist/skills/performance-tester/SKILL.md +57 -0
- package/dist/skills/playwright-ui-tester/SKILL.md +67 -0
- package/dist/skills/programmer/SKILL.md +58 -0
- package/dist/skills/project-scaffolding/SKILL.md +133 -0
- package/dist/skills/qa-engineer/SKILL.md +91 -0
- package/dist/skills/rca/SKILL.md +58 -0
- package/dist/skills/redhat/SKILL.md +92 -0
- package/dist/skills/rosa/SKILL.md +80 -0
- package/dist/skills/scrum-framework/SKILL.md +74 -0
- package/dist/skills/scrum-framework/references/artifacts_and_quality.md +79 -0
- package/dist/skills/scrum-framework/references/roles_and_events.md +105 -0
- package/dist/skills/scrum-master-agent/SKILL.md +74 -0
- package/dist/skills/scrum-master-agent/references/behaviors.md +56 -0
- package/dist/skills/sdlc-skill.d.ts +1 -0
- package/dist/skills/sdlc-skill.js +4 -0
- package/dist/skills/secops/SKILL.md +52 -0
- package/dist/skills/secure-architect.bylaws.d.ts +68 -0
- package/dist/skills/secure-architect.bylaws.js +525 -0
- package/dist/skills/seed-skills.d.ts +48 -0
- package/dist/skills/seed-skills.js +213 -0
- package/dist/skills/skill-authoring/SKILL.md +123 -0
- package/dist/skills/skill-header.d.ts +51 -0
- package/dist/skills/skill-header.js +163 -0
- package/dist/skills/skill-path-guard.d.ts +41 -0
- package/dist/skills/skill-path-guard.js +95 -0
- package/dist/skills/skillRouter.d.ts +51 -0
- package/dist/skills/skillRouter.js +90 -0
- package/dist/skills/skills-root.d.ts +64 -0
- package/dist/skills/skills-root.js +168 -0
- package/dist/skills/software-architect/SKILL.md +77 -0
- package/dist/skills/software-engineer/SKILL.md +74 -0
- package/dist/skills/solid-principles/SKILL.md +94 -0
- package/dist/skills/task-planning/SKILL.md +115 -0
- package/dist/skills/tester/SKILL.md +54 -0
- package/dist/skills/ubuntu/SKILL.md +87 -0
- package/dist/skills/ui-ux-design/SKILL.md +114 -0
- package/dist/skills/workspace-context/SKILL.md +103 -0
- package/dist/sprint/controller.d.ts +37 -0
- package/dist/sprint/controller.js +220 -0
- package/dist/sprint/dag-builder.d.ts +14 -0
- package/dist/sprint/dag-builder.js +105 -0
- package/dist/sprint/reporter.d.ts +11 -0
- package/dist/sprint/reporter.js +97 -0
- package/dist/sprint/state-store.d.ts +10 -0
- package/dist/sprint/state-store.js +71 -0
- package/dist/sprint/types.d.ts +24 -0
- package/dist/sprint/types.js +4 -0
- package/dist/store/file-version-store.d.ts +24 -0
- package/dist/store/file-version-store.js +73 -0
- package/dist/store/task-store.d.ts +90 -0
- package/dist/store/task-store.js +166 -0
- package/dist/task-ingest/architect-detailer.d.ts +43 -0
- package/dist/task-ingest/architect-detailer.js +141 -0
- package/dist/task-ingest/detail-schema.d.ts +9 -0
- package/dist/task-ingest/detail-schema.js +195 -0
- package/dist/task-ingest/detailed-run.d.ts +36 -0
- package/dist/task-ingest/detailed-run.js +108 -0
- package/dist/task-ingest/extract-id.d.ts +9 -0
- package/dist/task-ingest/extract-id.js +24 -0
- package/dist/task-ingest/index.d.ts +23 -0
- package/dist/task-ingest/index.js +14 -0
- package/dist/task-ingest/ingest.d.ts +45 -0
- package/dist/task-ingest/ingest.js +163 -0
- package/dist/task-ingest/list-ingested.d.ts +13 -0
- package/dist/task-ingest/list-ingested.js +30 -0
- package/dist/task-ingest/pending-filter.d.ts +30 -0
- package/dist/task-ingest/pending-filter.js +59 -0
- package/dist/task-ingest/scanner.d.ts +56 -0
- package/dist/task-ingest/scanner.js +216 -0
- package/dist/task-ingest/source-reader.d.ts +25 -0
- package/dist/task-ingest/source-reader.js +93 -0
- package/dist/task-ingest/spec-builder.d.ts +34 -0
- package/dist/task-ingest/spec-builder.js +89 -0
- package/dist/task-ingest/wbs-extract.d.ts +50 -0
- package/dist/task-ingest/wbs-extract.js +226 -0
- package/dist/task-ingest/wbs-parser.d.ts +36 -0
- package/dist/task-ingest/wbs-parser.js +170 -0
- package/dist/tools/apiTestTool.d.ts +37 -0
- package/dist/tools/apiTestTool.js +128 -0
- package/dist/tools/ast-tools.d.ts +183 -0
- package/dist/tools/ast-tools.js +853 -0
- package/dist/tools/browse-tool.d.ts +105 -0
- package/dist/tools/browse-tool.js +394 -0
- package/dist/tools/credential.d.ts +17 -0
- package/dist/tools/credential.js +32 -0
- package/dist/tools/dependency-graph.d.ts +23 -0
- package/dist/tools/dependency-graph.js +164 -0
- package/dist/tools/edit-tool.d.ts +34 -0
- package/dist/tools/edit-tool.js +95 -0
- package/dist/tools/file-header-manager.d.ts +27 -0
- package/dist/tools/file-header-manager.js +153 -0
- package/dist/tools/fs-tools.d.ts +154 -0
- package/dist/tools/fs-tools.js +337 -0
- package/dist/tools/git-tools.d.ts +79 -0
- package/dist/tools/git-tools.js +348 -0
- package/dist/tools/github-tool.d.ts +48 -0
- package/dist/tools/github-tool.js +147 -0
- package/dist/tools/globTool.d.ts +20 -0
- package/dist/tools/globTool.js +34 -0
- package/dist/tools/grepTool.d.ts +19 -0
- package/dist/tools/grepTool.js +44 -0
- package/dist/tools/index-freshness.d.ts +58 -0
- package/dist/tools/index-freshness.js +190 -0
- package/dist/tools/index-glob.d.ts +48 -0
- package/dist/tools/index-glob.js +175 -0
- package/dist/tools/load-skill-tool.d.ts +15 -0
- package/dist/tools/load-skill-tool.js +114 -0
- package/dist/tools/patch-tools.d.ts +58 -0
- package/dist/tools/patch-tools.js +153 -0
- package/dist/tools/playwright-tool.d.ts +66 -0
- package/dist/tools/playwright-tool.js +121 -0
- package/dist/tools/project-detect.d.ts +20 -0
- package/dist/tools/project-detect.js +87 -0
- package/dist/tools/prompt-guidance.d.ts +22 -0
- package/dist/tools/prompt-guidance.js +23 -0
- package/dist/tools/scheduleTool.d.ts +19 -0
- package/dist/tools/scheduleTool.js +181 -0
- package/dist/tools/search-tools.d.ts +27 -0
- package/dist/tools/search-tools.js +78 -0
- package/dist/tools/sed-tool.d.ts +66 -0
- package/dist/tools/sed-tool.js +402 -0
- package/dist/tools/shell-result.d.ts +11 -0
- package/dist/tools/shell-result.js +1 -0
- package/dist/tools/shell-tool.d.ts +45 -0
- package/dist/tools/shell-tool.js +141 -0
- package/dist/tools/siteCrawlerTool.d.ts +32 -0
- package/dist/tools/siteCrawlerTool.js +205 -0
- package/dist/tools/tool-registry.d.ts +67 -0
- package/dist/tools/tool-registry.js +596 -0
- package/dist/tools/validate-tool.d.ts +23 -0
- package/dist/tools/validate-tool.js +201 -0
- package/dist/tools/workspace-index.d.ts +46 -0
- package/dist/tools/workspace-index.js +173 -0
- package/dist/types.d.ts +222 -0
- package/dist/types.js +7 -0
- package/dist/updater/index.d.ts +14 -0
- package/dist/updater/index.js +120 -0
- package/dist/util/banner.d.ts +25 -0
- package/dist/util/banner.js +140 -0
- package/dist/util/command-suggester.d.ts +6 -0
- package/dist/util/command-suggester.js +25 -0
- package/dist/util/confirm-interrupt.d.ts +10 -0
- package/dist/util/confirm-interrupt.js +31 -0
- package/dist/util/continue-trigger.d.ts +11 -0
- package/dist/util/continue-trigger.js +20 -0
- package/dist/util/env-info.d.ts +18 -0
- package/dist/util/env-info.js +37 -0
- package/dist/util/file-access-log.d.ts +35 -0
- package/dist/util/file-access-log.js +140 -0
- package/dist/util/footer-manager.d.ts +124 -0
- package/dist/util/footer-manager.js +195 -0
- package/dist/util/footer-renderer.d.ts +67 -0
- package/dist/util/footer-renderer.js +319 -0
- package/dist/util/footer-state.d.ts +34 -0
- package/dist/util/footer-state.js +18 -0
- package/dist/util/footer-text.d.ts +51 -0
- package/dist/util/footer-text.js +74 -0
- package/dist/util/ids.d.ts +1 -0
- package/dist/util/ids.js +4 -0
- package/dist/util/levenshtein.d.ts +6 -0
- package/dist/util/levenshtein.js +34 -0
- package/dist/util/logger.d.ts +10 -0
- package/dist/util/logger.js +41 -0
- package/dist/util/logs.d.ts +26 -0
- package/dist/util/logs.js +88 -0
- package/dist/util/output-handler.d.ts +89 -0
- package/dist/util/output-handler.js +193 -0
- package/dist/util/output-interceptor.d.ts +18 -0
- package/dist/util/output-interceptor.js +49 -0
- package/dist/util/output-paths.d.ts +43 -0
- package/dist/util/output-paths.js +121 -0
- package/dist/util/package-json.d.ts +6 -0
- package/dist/util/package-json.js +36 -0
- package/dist/util/process-kill.d.ts +27 -0
- package/dist/util/process-kill.js +148 -0
- package/dist/util/protocol-error-logger.d.ts +33 -0
- package/dist/util/protocol-error-logger.js +73 -0
- package/dist/util/redact.d.ts +25 -0
- package/dist/util/redact.js +103 -0
- package/dist/util/scroll-region-guard.d.ts +33 -0
- package/dist/util/scroll-region-guard.js +63 -0
- package/dist/util/spinner.d.ts +15 -0
- package/dist/util/spinner.js +49 -0
- package/dist/util/terminal-output.d.ts +34 -0
- package/dist/util/terminal-output.js +1 -0
- package/dist/util/terminal-silent.d.ts +6 -0
- package/dist/util/terminal-silent.js +20 -0
- package/dist/util/terminal.d.ts +56 -0
- package/dist/util/terminal.js +124 -0
- package/dist/util/text-sanitize.d.ts +8 -0
- package/dist/util/text-sanitize.js +45 -0
- package/dist/util/thinking-spinner.d.ts +27 -0
- package/dist/util/thinking-spinner.js +189 -0
- package/dist/util/tool-error-logger.d.ts +33 -0
- package/dist/util/tool-error-logger.js +77 -0
- package/dist/util/tty.d.ts +13 -0
- package/dist/util/tty.js +16 -0
- package/dist/util/xcoder-paths.d.ts +56 -0
- package/dist/util/xcoder-paths.js +63 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +4 -0
- package/dist/worker-agent/to-subtask-node.d.ts +35 -0
- package/dist/worker-agent/to-subtask-node.js +41 -0
- package/dist/worker-agent/worker-orchestrator.d.ts +104 -0
- package/dist/worker-agent/worker-orchestrator.js +201 -0
- package/package.json +39 -4
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readdirSync, existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { log } from "../util/logger.js";
|
|
5
|
+
import { createSpinner } from "../util/spinner.js";
|
|
6
|
+
import { trackPid, untrackPid } from "../util/process-kill.js";
|
|
7
|
+
// ── Predefined suites ──
|
|
8
|
+
const PREDEFINED_SUITES = [
|
|
9
|
+
{
|
|
10
|
+
name: "unit",
|
|
11
|
+
configPath: "vitest.config.ts",
|
|
12
|
+
npmScript: "npm test",
|
|
13
|
+
label: "Unit Tests",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "e2e",
|
|
17
|
+
configPath: "e2e/vitest.config.ts",
|
|
18
|
+
npmScript: "npm run test:e2e",
|
|
19
|
+
label: "E2E Tests",
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
// ── Per-folder suite discovery ──
|
|
23
|
+
/**
|
|
24
|
+
* Scan src/ directories for *.test.ts files, group by immediate parent
|
|
25
|
+
* directory, and produce one suite descriptor per folder that has tests.
|
|
26
|
+
* Skips folders with no tests. Uses the share vitest config, scoped
|
|
27
|
+
* via --include.
|
|
28
|
+
*/
|
|
29
|
+
export function generateFolderSuites(rootDir) {
|
|
30
|
+
const srcDir = path.join(rootDir, "src");
|
|
31
|
+
if (!existsSync(srcDir))
|
|
32
|
+
return [];
|
|
33
|
+
const folderMap = new Map();
|
|
34
|
+
function walk(dir) {
|
|
35
|
+
let entries;
|
|
36
|
+
try {
|
|
37
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const full = path.join(dir, entry.name);
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
walk(full);
|
|
46
|
+
}
|
|
47
|
+
else if (entry.isFile() && entry.name.endsWith(".test.ts")) {
|
|
48
|
+
// Determine the top-level folder under src/
|
|
49
|
+
const rel = path.relative(srcDir, full);
|
|
50
|
+
const topFolder = rel.split(path.sep)[0] || "root";
|
|
51
|
+
if (!folderMap.has(topFolder)) {
|
|
52
|
+
folderMap.set(topFolder, []);
|
|
53
|
+
}
|
|
54
|
+
folderMap.get(topFolder).push(rel);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
walk(srcDir);
|
|
59
|
+
// Build suite descriptors sorted by folder name.
|
|
60
|
+
const suites = [];
|
|
61
|
+
for (const [folder, files] of [...folderMap.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
62
|
+
// Build absolute paths from the src-relative file list.
|
|
63
|
+
const filePaths = files.map((f) => path.join("src", f));
|
|
64
|
+
suites.push({
|
|
65
|
+
name: `folder:${folder}`,
|
|
66
|
+
configPath: "vitest.config.ts",
|
|
67
|
+
npmScript: `npx vitest run --config vitest.config.ts ${filePaths.map((f) => `"${f}"`).join(" ")}`,
|
|
68
|
+
label: `src/${folder}`,
|
|
69
|
+
testFiles: filePaths,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return suites;
|
|
73
|
+
}
|
|
74
|
+
// ── Suite runner ──
|
|
75
|
+
/**
|
|
76
|
+
* Spawn vitest as a child process for a single suite, capture
|
|
77
|
+
* stdout/stderr, enforce a per-suite timeout, and return a
|
|
78
|
+
* structured SuiteResult.
|
|
79
|
+
*
|
|
80
|
+
* Uses vitest's built-in JSON reporter so we can parse test counts
|
|
81
|
+
* for the summary. Falls back to exit-code-only pass/fail if JSON
|
|
82
|
+
* parsing fails (vitest crash, old version, etc.).
|
|
83
|
+
*
|
|
84
|
+
* If suite has testFiles, they are passed as positional arguments.
|
|
85
|
+
*/
|
|
86
|
+
export function runSuite(suite, timeoutMs, capture) {
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
const start = Date.now();
|
|
89
|
+
// We always use --reporter=json so we can parse counts, but if
|
|
90
|
+
// the user wants live output we let stdout through _in addition_.
|
|
91
|
+
const args = [
|
|
92
|
+
"vitest",
|
|
93
|
+
"run",
|
|
94
|
+
"--config",
|
|
95
|
+
suite.configPath,
|
|
96
|
+
"--reporter=json",
|
|
97
|
+
];
|
|
98
|
+
// Add test file paths as positional arguments (vitest filter).
|
|
99
|
+
// Avoid globs — they don't expand on Windows with shell:true.
|
|
100
|
+
if (suite.testFiles && suite.testFiles.length > 0) {
|
|
101
|
+
for (const f of suite.testFiles) {
|
|
102
|
+
args.push(f);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const child = spawn("npx", args, {
|
|
106
|
+
cwd: process.cwd(),
|
|
107
|
+
shell: process.platform === "win32",
|
|
108
|
+
stdio: capture ? "pipe" : ["pipe", "inherit", "pipe"],
|
|
109
|
+
});
|
|
110
|
+
// Track PID for cleanup on interrupt.
|
|
111
|
+
if (child.pid) {
|
|
112
|
+
trackPid(child.pid);
|
|
113
|
+
}
|
|
114
|
+
let stdout = "";
|
|
115
|
+
let stderr = "";
|
|
116
|
+
if (child.stdout) {
|
|
117
|
+
child.stdout.setEncoding("utf8");
|
|
118
|
+
child.stdout.on("data", (chunk) => {
|
|
119
|
+
stdout += chunk;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (child.stderr) {
|
|
123
|
+
child.stderr.setEncoding("utf8");
|
|
124
|
+
child.stderr.on("data", (chunk) => {
|
|
125
|
+
stderr += chunk;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const timeout = setTimeout(() => {
|
|
129
|
+
if (child.pid)
|
|
130
|
+
untrackPid(child.pid);
|
|
131
|
+
child.kill("SIGKILL");
|
|
132
|
+
resolve({
|
|
133
|
+
suiteName: suite.name,
|
|
134
|
+
passed: false,
|
|
135
|
+
durationMs: Date.now() - start,
|
|
136
|
+
testCount: null,
|
|
137
|
+
passedCount: null,
|
|
138
|
+
failedCount: null,
|
|
139
|
+
exitCode: null,
|
|
140
|
+
error: `Suite timed out after ${timeoutMs}ms`,
|
|
141
|
+
});
|
|
142
|
+
}, timeoutMs);
|
|
143
|
+
child.on("close", (code) => {
|
|
144
|
+
clearTimeout(timeout);
|
|
145
|
+
if (child.pid)
|
|
146
|
+
untrackPid(child.pid);
|
|
147
|
+
const durationMs = Date.now() - start;
|
|
148
|
+
// Try to parse JSON reporter output for test counts.
|
|
149
|
+
let testCount = null;
|
|
150
|
+
let passedCount = null;
|
|
151
|
+
let failedCount = null;
|
|
152
|
+
try {
|
|
153
|
+
// vitest JSON reporter outputs one JSON object per test file on stdout.
|
|
154
|
+
// The last line should be a summary object with `numTotalTests` etc.
|
|
155
|
+
const lines = stdout.trim().split("\n");
|
|
156
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
157
|
+
const line = lines[i].trim();
|
|
158
|
+
if (!line)
|
|
159
|
+
continue;
|
|
160
|
+
try {
|
|
161
|
+
const parsed = JSON.parse(line);
|
|
162
|
+
if (parsed && typeof parsed.numTotalTests === "number") {
|
|
163
|
+
testCount = parsed.numTotalTests;
|
|
164
|
+
passedCount = parsed.numPassedTests ?? null;
|
|
165
|
+
failedCount = parsed.numFailedTests ?? null;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Not valid JSON — skip this line.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// JSON parsing failed entirely — fall back to exit-code-only.
|
|
176
|
+
}
|
|
177
|
+
if (testCount === null && !capture) {
|
|
178
|
+
log.dim(`Warning: could not parse test counts from vitest JSON output for ${suite.name}`);
|
|
179
|
+
}
|
|
180
|
+
const passed = code === 0;
|
|
181
|
+
resolve({
|
|
182
|
+
suiteName: suite.name,
|
|
183
|
+
passed,
|
|
184
|
+
durationMs,
|
|
185
|
+
testCount,
|
|
186
|
+
passedCount,
|
|
187
|
+
failedCount,
|
|
188
|
+
exitCode: code,
|
|
189
|
+
error: code !== 0 && stderr ? stderr.slice(0, 500) : null,
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
child.on("error", (err) => {
|
|
193
|
+
clearTimeout(timeout);
|
|
194
|
+
if (child.pid)
|
|
195
|
+
untrackPid(child.pid);
|
|
196
|
+
resolve({
|
|
197
|
+
suiteName: suite.name,
|
|
198
|
+
passed: false,
|
|
199
|
+
durationMs: Date.now() - start,
|
|
200
|
+
testCount: null,
|
|
201
|
+
passedCount: null,
|
|
202
|
+
failedCount: null,
|
|
203
|
+
exitCode: null,
|
|
204
|
+
error: err.message,
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
// ── Summary reporter ──
|
|
210
|
+
/**
|
|
211
|
+
* Accept an array of SuiteResults and print a color-coded,
|
|
212
|
+
* human-readable summary to stdout.
|
|
213
|
+
*/
|
|
214
|
+
export function printSummary(results, totalDurationMs) {
|
|
215
|
+
log.raw("");
|
|
216
|
+
log.raw("═══════════════════════════════════════");
|
|
217
|
+
log.raw(" Xcoder SDLC Test Summary");
|
|
218
|
+
log.raw("═══════════════════════════════════════");
|
|
219
|
+
log.raw("");
|
|
220
|
+
// Collect all known labels (including folder suites).
|
|
221
|
+
const allSuites = [...PREDEFINED_SUITES, ...generateFolderSuites(process.cwd())];
|
|
222
|
+
for (const r of results) {
|
|
223
|
+
const icon = r.passed ? "✓" : "✗";
|
|
224
|
+
const label = allSuites.find((s) => s.name === r.suiteName)?.label ?? r.suiteName;
|
|
225
|
+
if (r.passed) {
|
|
226
|
+
log.ok(`${icon} ${label}`);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
log.fail(`${icon} ${label}`);
|
|
230
|
+
}
|
|
231
|
+
if (r.testCount !== null) {
|
|
232
|
+
const p = r.passedCount ?? 0;
|
|
233
|
+
const f = r.failedCount ?? 0;
|
|
234
|
+
const t = r.testCount;
|
|
235
|
+
log.raw(` Tests: ${p} passed, ${f} failed, ${t} total`);
|
|
236
|
+
}
|
|
237
|
+
const seconds = (r.durationMs / 1000).toFixed(1);
|
|
238
|
+
log.raw(` Duration: ${seconds}s`);
|
|
239
|
+
if (r.error) {
|
|
240
|
+
log.dim(` Error: ${r.error.slice(0, 200)}`);
|
|
241
|
+
}
|
|
242
|
+
log.raw("");
|
|
243
|
+
}
|
|
244
|
+
const totalSeconds = (totalDurationMs / 1000).toFixed(1);
|
|
245
|
+
const passedCount = results.filter((r) => r.passed).length;
|
|
246
|
+
const totalCount = results.length;
|
|
247
|
+
if (passedCount === totalCount) {
|
|
248
|
+
log.ok(`All ${totalCount} suites passed. (${totalSeconds}s total)`);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const failedCount = totalCount - passedCount;
|
|
252
|
+
log.fail(`${failedCount}/${totalCount} suite${failedCount !== 1 ? "s" : ""} failed. (${totalSeconds}s total)`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// ── Command registration ──
|
|
256
|
+
/**
|
|
257
|
+
* Register the `xcoder test` subcommand.
|
|
258
|
+
*/
|
|
259
|
+
export function registerTestCommand(parent) {
|
|
260
|
+
parent
|
|
261
|
+
.command("test")
|
|
262
|
+
.description("Discover and execute project test suites (unit, e2e, per-folder). Delegates to vitest, captures output, and reports a unified summary.")
|
|
263
|
+
.option("--suite <unit|e2e|all|folder:name>", "filter suites to run (repeatable for multiple selections)", (val, prev) => [...prev, val], [])
|
|
264
|
+
.option("--folder <path>", "run only tests under a specific src/folder (repeatable, or use 'all' for every folder)", (val, prev) => [...prev, val], [])
|
|
265
|
+
.option("--timeout <ms>", "aggregate timeout in milliseconds", (val) => parseInt(val, 10), 300_000)
|
|
266
|
+
.option("--no-capture", "live-stream test output instead of capturing for summary")
|
|
267
|
+
.option("--regression", "run all per-folder suites sequentially (equivalent to --folder all)", false)
|
|
268
|
+
.action(async (opts) => {
|
|
269
|
+
const root = process.cwd();
|
|
270
|
+
// Build folder suites on demand.
|
|
271
|
+
const folderSuites = generateFolderSuites(root);
|
|
272
|
+
const allSuites = [...PREDEFINED_SUITES, ...folderSuites];
|
|
273
|
+
// Determine which suites to run.
|
|
274
|
+
let suites;
|
|
275
|
+
// --regression flag or --folder all → all folder suites only (not unit/e2e).
|
|
276
|
+
const folderFilter = opts.regression || opts.folder.includes("all");
|
|
277
|
+
const explicitFolders = opts.folder.filter((f) => f !== "all" && f.length > 0);
|
|
278
|
+
if (folderFilter) {
|
|
279
|
+
// Run all folder suites.
|
|
280
|
+
suites = [...folderSuites];
|
|
281
|
+
}
|
|
282
|
+
else if (explicitFolders.length > 0) {
|
|
283
|
+
// Run specific folders.
|
|
284
|
+
suites = folderSuites.filter((s) => {
|
|
285
|
+
const folderName = s.name.replace(/^folder:/, "");
|
|
286
|
+
return explicitFolders.some((f) => f === folderName || (s.testFiles && s.testFiles.some((tf) => tf.includes(f))));
|
|
287
|
+
});
|
|
288
|
+
if (suites.length === 0) {
|
|
289
|
+
log.fail(`No test files found in folders: ${explicitFolders.join(", ")}. Available: ${folderSuites.map((s) => s.name.replace(/^folder:/, "")).join(", ") || "(none)"}`);
|
|
290
|
+
process.exitCode = 2;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
else if (opts.suite.length === 0) {
|
|
295
|
+
// Default: run unit and e2e (backward compatible).
|
|
296
|
+
suites = [...PREDEFINED_SUITES];
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
// Filter by user selection, deduplicate.
|
|
300
|
+
const selected = new Set(opts.suite.flatMap((s) => s.split(",")).map((s) => s.trim().toLowerCase()));
|
|
301
|
+
if (selected.has("all")) {
|
|
302
|
+
suites = [...allSuites];
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
suites = allSuites.filter((s) => {
|
|
306
|
+
if (selected.has(s.name))
|
|
307
|
+
return true;
|
|
308
|
+
// Also check folder aliases: "folder:agents" or just "agents"
|
|
309
|
+
const folderName = s.name.replace(/^folder:/, "");
|
|
310
|
+
return selected.has(folderName);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (suites.length === 0) {
|
|
315
|
+
log.fail(`No matching test suites. Available: ${allSuites.map((s) => s.name).join(", ")}`);
|
|
316
|
+
process.exitCode = 2;
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
// Check that vitest is available.
|
|
320
|
+
const checkSpawn = spawn("npx", ["vitest", "--version"], {
|
|
321
|
+
cwd: root,
|
|
322
|
+
shell: process.platform === "win32",
|
|
323
|
+
stdio: "pipe",
|
|
324
|
+
});
|
|
325
|
+
let vitestOk = false;
|
|
326
|
+
try {
|
|
327
|
+
await new Promise((resolve, reject) => {
|
|
328
|
+
checkSpawn.on("close", (code) => {
|
|
329
|
+
vitestOk = code === 0;
|
|
330
|
+
resolve();
|
|
331
|
+
});
|
|
332
|
+
checkSpawn.on("error", () => reject(new Error("vitest not found")));
|
|
333
|
+
setTimeout(() => {
|
|
334
|
+
checkSpawn.kill();
|
|
335
|
+
resolve();
|
|
336
|
+
}, 10_000);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
vitestOk = false;
|
|
341
|
+
}
|
|
342
|
+
if (!vitestOk) {
|
|
343
|
+
log.fail("vitest not found. Ensure vitest is installed (npm install vitest) and available via npx.");
|
|
344
|
+
process.exitCode = 2;
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
// Run suites sequentially.
|
|
348
|
+
const results = [];
|
|
349
|
+
const suiteTimeout = Math.floor(opts.timeout / suites.length);
|
|
350
|
+
const overallStart = Date.now();
|
|
351
|
+
for (const suite of suites) {
|
|
352
|
+
const spinner = createSpinner(`Running ${suite.label}…`);
|
|
353
|
+
try {
|
|
354
|
+
const result = await runSuite(suite, suiteTimeout, opts.capture);
|
|
355
|
+
results.push(result);
|
|
356
|
+
if (result.passed) {
|
|
357
|
+
spinner.stop(`${suite.label}: passed`);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
spinner.fail(`${suite.label}: failed`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
catch (e) {
|
|
364
|
+
const errMsg = e.message;
|
|
365
|
+
spinner.fail(`${suite.label}: error — ${errMsg}`);
|
|
366
|
+
results.push({
|
|
367
|
+
suiteName: suite.name,
|
|
368
|
+
passed: false,
|
|
369
|
+
durationMs: Date.now() - overallStart,
|
|
370
|
+
testCount: null,
|
|
371
|
+
passedCount: null,
|
|
372
|
+
failedCount: null,
|
|
373
|
+
exitCode: null,
|
|
374
|
+
error: errMsg,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const totalDurationMs = Date.now() - overallStart;
|
|
379
|
+
printSummary(results, totalDurationMs);
|
|
380
|
+
const allPassed = results.every((r) => r.passed);
|
|
381
|
+
if (!allPassed) {
|
|
382
|
+
process.exitCode = 1;
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getAppDir } from "../skills/skills-root.js";
|
|
2
|
+
export { getAppDir };
|
|
3
|
+
export type LlmProvider = "deepseek" | "openai-compatible" | "anthropic";
|
|
4
|
+
export type AgentRole = "orchestrator" | "sub_agent" | "swarm_agent" | "instance_agent";
|
|
5
|
+
export interface LlmConfig {
|
|
6
|
+
provider: LlmProvider;
|
|
7
|
+
base_url?: string;
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
model: string;
|
|
10
|
+
api_key_env?: string;
|
|
11
|
+
max_tokens: number;
|
|
12
|
+
temperature: number;
|
|
13
|
+
thinking?: boolean;
|
|
14
|
+
reasoning_effort?: "high" | "max";
|
|
15
|
+
overrides?: Record<string, {
|
|
16
|
+
model?: string;
|
|
17
|
+
temperature?: number;
|
|
18
|
+
thinking?: boolean;
|
|
19
|
+
reasoning_effort?: "high" | "max";
|
|
20
|
+
}>;
|
|
21
|
+
fallback?: Partial<LlmConfig>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* YAML schema of `.xcoder/config/llm.yaml`. The `default` block is the
|
|
25
|
+
* global fallback; each role block (`orchestrator`, `sub_agent`,
|
|
26
|
+
* `swarm_agent`, `instance_agent`) overrides it for that agent role.
|
|
27
|
+
*/
|
|
28
|
+
export interface LlmConfigFile {
|
|
29
|
+
default?: LlmConfig;
|
|
30
|
+
orchestrator?: LlmConfig;
|
|
31
|
+
sub_agent?: LlmConfig;
|
|
32
|
+
swarm_agent?: LlmConfig;
|
|
33
|
+
instance_agent?: LlmConfig;
|
|
34
|
+
}
|
|
35
|
+
export declare const AGENT_ROLES: AgentRole[];
|
|
36
|
+
/**
|
|
37
|
+
* Returns a directory D such that `D/skills` is the resolved skills directory.
|
|
38
|
+
*
|
|
39
|
+
* @deprecated Use `resolveSkillsRoot()` from `src/skills/skills-root.ts` — it
|
|
40
|
+
* is the single source of truth and also tells you which candidate won and
|
|
41
|
+
* how many packs are actually usable. This wrapper is kept only so existing
|
|
42
|
+
* importers do not break.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveSkillsPath(): string;
|
|
45
|
+
export declare function resolveConfigPath(): string;
|
|
46
|
+
export declare function resolveAgentPath(): string;
|
|
47
|
+
/**
|
|
48
|
+
* Built-in DeepSeek default — used when no config file exists and as the
|
|
49
|
+
* fallback for every role. Reads XCODER_MODEL lazily so env changes between
|
|
50
|
+
* calls are honored.
|
|
51
|
+
*
|
|
52
|
+
* Provider `deepseek` is served through DeepSeek's Anthropic-compatible
|
|
53
|
+
* endpoint (`@anthropic-ai/sdk`, which appends `/v1/messages` to the base
|
|
54
|
+
* URL), so the canonical root is `https://api.deepseek.com/anthropic` —
|
|
55
|
+
* NOT the OpenAI-compatible `https://api.deepseek.com/v1`, which would
|
|
56
|
+
* produce the invalid `.../v1/v1/messages` request path.
|
|
57
|
+
*/
|
|
58
|
+
export declare function deepseekDefaultConfig(): LlmConfig;
|
|
59
|
+
/**
|
|
60
|
+
* Read `.xcoder/config/llm.yaml` and return the raw per-role schema.
|
|
61
|
+
* Merge happens in `resolveLlmConfig`, not here. A legacy file shaped as a
|
|
62
|
+
* single provider block is interpreted as the `default` block.
|
|
63
|
+
*/
|
|
64
|
+
export declare function loadLlmConfigFile(configPath?: string): LlmConfigFile;
|
|
65
|
+
/**
|
|
66
|
+
* Overlay `~/.xcoder/config.json` (provider / key / model / base_url) on a
|
|
67
|
+
* resolved block. Switching provider also resets base_url to that provider's
|
|
68
|
+
* default, so a key is never sent to the previous provider's host.
|
|
69
|
+
*/
|
|
70
|
+
export declare function applyUserLlmOverrides(cfg: LlmConfig): LlmConfig;
|
|
71
|
+
export declare function resolveLlmConfig(role: AgentRole, file?: LlmConfigFile): LlmConfig;
|
|
72
|
+
/**
|
|
73
|
+
* @deprecated Legacy loader — returns the merged `default` block. New code
|
|
74
|
+
* should use `loadLlmConfigFile()` + `resolveLlmConfig(role, file)`.
|
|
75
|
+
*/
|
|
76
|
+
export declare function loadLlmConfig(configPath?: string): LlmConfig;
|
|
77
|
+
export declare function resolveModelForSkill(config: LlmConfig, skillName?: string): {
|
|
78
|
+
model: string;
|
|
79
|
+
temperature: number;
|
|
80
|
+
thinking: boolean;
|
|
81
|
+
reasoningEffort: "high" | "max" | undefined;
|
|
82
|
+
};
|