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,36 @@
|
|
|
1
|
+
import type { SubtaskNode, TaskRun } from "../types.js";
|
|
2
|
+
import type { ArchitectDetail } from "./architect-detailer.js";
|
|
3
|
+
import type { PendingItem } from "./pending-filter.js";
|
|
4
|
+
export interface DetailedRunInput {
|
|
5
|
+
item: PendingItem;
|
|
6
|
+
detail: ArchitectDetail;
|
|
7
|
+
source: {
|
|
8
|
+
relPathPosix: string;
|
|
9
|
+
sha256: string;
|
|
10
|
+
};
|
|
11
|
+
wbsTitle: string;
|
|
12
|
+
wbsGoal: string;
|
|
13
|
+
/** From C8, never from model output. */
|
|
14
|
+
taskId: string;
|
|
15
|
+
/** Supplied by C3 so this function stays pure. */
|
|
16
|
+
createdAt: string;
|
|
17
|
+
concurrency: number;
|
|
18
|
+
/** Default 2, same as `ingest.ts`. */
|
|
19
|
+
maxHealingAttempts?: number;
|
|
20
|
+
/** Rendered `[after: n]` hint, advisory only. */
|
|
21
|
+
sequencingNote?: string;
|
|
22
|
+
tokenUsage?: {
|
|
23
|
+
inputTokens: number;
|
|
24
|
+
outputTokens: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Deterministic node id: `<stage>-<first 6 hex of sha256(taskId + "#" + i)>`. */
|
|
28
|
+
export declare function detailedNodeId(stage: string, taskId: string, index: number): string;
|
|
29
|
+
/**
|
|
30
|
+
* Build the TaskSpec and the DAG. `dag[0].status === "ready"`, all others
|
|
31
|
+
* `"pending"` — exactly what `findResumableTasks()` accepts for an unstarted run.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildDetailedRun(input: DetailedRunInput): {
|
|
34
|
+
spec: TaskRun["spec"];
|
|
35
|
+
dag: SubtaskNode[];
|
|
36
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// xcoder:version 1 | xcoder:task task-7a4850 | xcoder:updated 2026-10-25T16:25:29.383Z | xcoder:subtask code-st-892a22
|
|
2
|
+
// C7 — PURE mapping from (pending item + architect detail) to a multi-node
|
|
3
|
+
// TaskRun body. Mirrors `buildIngestedRun` but produces one node per architect
|
|
4
|
+
// stage instead of one node per bullet.
|
|
5
|
+
//
|
|
6
|
+
// No I/O, no clock, no randomness, no LLM. Everything that varies (taskId,
|
|
7
|
+
// createdAt) is supplied by C3, so identical inputs produce byte-identical JSON.
|
|
8
|
+
//
|
|
9
|
+
// The enforceable meaning of "use the architect to define the content of each
|
|
10
|
+
// task": the architect contributes PROSE ONLY. Ids, roles, dependencies,
|
|
11
|
+
// statuses and the canonical deliverable/acceptance vocabulary are all computed
|
|
12
|
+
// here in code. A criterion produced from model text is always `type: "rubric"`,
|
|
13
|
+
// never `"command"` — no model string can ever become something that executes.
|
|
14
|
+
import crypto from "node:crypto";
|
|
15
|
+
import { getStageRole } from "../sdlc-runtime/sdlc-skill.js";
|
|
16
|
+
import { deliverableFor, acceptanceCriteriaFor } from "../orchestrator/task-factory.js";
|
|
17
|
+
/** Deterministic node id: `<stage>-<first 6 hex of sha256(taskId + "#" + i)>`. */
|
|
18
|
+
export function detailedNodeId(stage, taskId, index) {
|
|
19
|
+
const digest = crypto.createHash("sha256").update(`${taskId}#${index}`).digest("hex");
|
|
20
|
+
return `${stage}-${digest.slice(0, 6)}`;
|
|
21
|
+
}
|
|
22
|
+
/** One line, whitespace collapsed, length-capped. */
|
|
23
|
+
function oneLine(text, max) {
|
|
24
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
25
|
+
return flat.length > max ? `${flat.slice(0, max - 1).trimEnd()}…` : flat;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Canonical criteria for the stage, then the architect's rubrics.
|
|
29
|
+
*
|
|
30
|
+
* Canonical entries are preserved (description included) but any `command`-typed
|
|
31
|
+
* one is downgraded to a rubric that names the command as informational text:
|
|
32
|
+
* an extracted task is data on disk, and the design forbids a command-typed
|
|
33
|
+
* criterion anywhere in it. Model-supplied rubrics are ALWAYS `type: "rubric"`.
|
|
34
|
+
*/
|
|
35
|
+
function criteriaFrom(stage, rubrics) {
|
|
36
|
+
const canonical = acceptanceCriteriaFor(stage).map((c) => c.type === "command"
|
|
37
|
+
? {
|
|
38
|
+
id: c.id,
|
|
39
|
+
type: "rubric",
|
|
40
|
+
description: c.command ? `${c.description} (verify by running: ${c.command})` : c.description,
|
|
41
|
+
}
|
|
42
|
+
: { ...c });
|
|
43
|
+
const mapped = rubrics.map((description, i) => ({
|
|
44
|
+
id: `ac-${i + 1}`,
|
|
45
|
+
type: "rubric",
|
|
46
|
+
description,
|
|
47
|
+
}));
|
|
48
|
+
return [...canonical, ...mapped];
|
|
49
|
+
}
|
|
50
|
+
function finalNodeAppendix(detail) {
|
|
51
|
+
const parts = [];
|
|
52
|
+
parts.push(`Verification: ${oneLine(detail.verification, 600)}`);
|
|
53
|
+
if (detail.risks && detail.risks.length > 0) {
|
|
54
|
+
parts.push(`Risks: ${detail.risks.map((r) => oneLine(r, 400)).join("; ")}`);
|
|
55
|
+
}
|
|
56
|
+
return parts.join("\n");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build the TaskSpec and the DAG. `dag[0].status === "ready"`, all others
|
|
60
|
+
* `"pending"` — exactly what `findResumableTasks()` accepts for an unstarted run.
|
|
61
|
+
*/
|
|
62
|
+
export function buildDetailedRun(input) {
|
|
63
|
+
const { item, detail, source, taskId, createdAt, concurrency } = input;
|
|
64
|
+
const maxHealingAttempts = input.maxHealingAttempts ?? 2;
|
|
65
|
+
const spec = {
|
|
66
|
+
taskId,
|
|
67
|
+
goal: oneLine(detail.objective, 200),
|
|
68
|
+
// Deliberately EXACTLY freeText + sourcePath. code / design / url /
|
|
69
|
+
// defectDetail / credentialRef are never populated from file or model
|
|
70
|
+
// content — that is what would let a hostile document steer intake
|
|
71
|
+
// classification or a credential lookup.
|
|
72
|
+
inputs: { freeText: item.normalized, sourcePath: source.relPathPosix },
|
|
73
|
+
constraints: { maxHealingAttempts, concurrency },
|
|
74
|
+
createdAt,
|
|
75
|
+
provenance: {
|
|
76
|
+
sourcePath: source.relPathPosix,
|
|
77
|
+
sourceSha256: source.sha256,
|
|
78
|
+
ingestedAt: createdAt,
|
|
79
|
+
ingestVersion: 1,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
const dag = [];
|
|
83
|
+
const lastIndex = detail.stages.length - 1;
|
|
84
|
+
for (const [i, stageDetail] of detail.stages.entries()) {
|
|
85
|
+
const stage = stageDetail.stage;
|
|
86
|
+
const id = detailedNodeId(stage, taskId, i);
|
|
87
|
+
const goalLines = [`${oneLine(detail.objective, 200)}\nSource item: "${item.normalized}"`];
|
|
88
|
+
if (i === 0 && input.sequencingNote)
|
|
89
|
+
goalLines.push(`Sequencing hint: ${input.sequencingNote}`);
|
|
90
|
+
let deliverableSpec = `${deliverableFor(stage)}\n\n${oneLine(stageDetail.deliverable, 600)}`;
|
|
91
|
+
if (i === lastIndex)
|
|
92
|
+
deliverableSpec += `\n\n${finalNodeAppendix(detail)}`;
|
|
93
|
+
dag.push({
|
|
94
|
+
id,
|
|
95
|
+
stage,
|
|
96
|
+
role: getStageRole(stage, spec.inputs),
|
|
97
|
+
engine: "react-v1",
|
|
98
|
+
dependsOn: i === 0 ? [] : [dag[i - 1].id],
|
|
99
|
+
goal: goalLines.join("\n"),
|
|
100
|
+
upstreamContext: {},
|
|
101
|
+
deliverableSpec,
|
|
102
|
+
acceptanceCriteria: criteriaFrom(stage, stageDetail.acceptanceCriteria),
|
|
103
|
+
status: i === 0 ? "ready" : "pending",
|
|
104
|
+
healing: { attempts: 0, maxAttempts: maxHealingAttempts, log: [] },
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return { spec, dag };
|
|
108
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Version-salted like INGEST_VERSION_SALT so the scheme can evolve safely. */
|
|
2
|
+
export declare const EXTRACT_VERSION_SALT = "wbc-extract:v1";
|
|
3
|
+
/**
|
|
4
|
+
* task-<first N hex of sha256(salt \0 relPathPosix \0 normalizedItemText)>
|
|
5
|
+
*
|
|
6
|
+
* Deterministic across runs and machines; independent of line number, index,
|
|
7
|
+
* clock, and every byte of model output.
|
|
8
|
+
*/
|
|
9
|
+
export declare function deriveItemTaskId(relPathPosix: string, normalizedItemText: string, hexLength?: 8 | 12): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// xcoder:version 1 | xcoder:task task-7a4850 | xcoder:updated 2026-10-25T16:24:31.487Z | xcoder:subtask code-st-892a22
|
|
2
|
+
// C8 — item-scoped task id derivation for `--wbc` extraction.
|
|
3
|
+
//
|
|
4
|
+
// `deriveTaskId(relPathPosix)` in `ingest.ts` hashes ONLY the source path,
|
|
5
|
+
// because there one file means one task. Here one file means *n* tasks, so the
|
|
6
|
+
// id must also cover the item. The hash never sees model output, a line
|
|
7
|
+
// number, an index, or the clock — only the workspace-relative source path and
|
|
8
|
+
// the normalised item text (PRM-08: model output never determines a file name).
|
|
9
|
+
import crypto from "node:crypto";
|
|
10
|
+
/** Version-salted like INGEST_VERSION_SALT so the scheme can evolve safely. */
|
|
11
|
+
export const EXTRACT_VERSION_SALT = "wbc-extract:v1";
|
|
12
|
+
/**
|
|
13
|
+
* task-<first N hex of sha256(salt \0 relPathPosix \0 normalizedItemText)>
|
|
14
|
+
*
|
|
15
|
+
* Deterministic across runs and machines; independent of line number, index,
|
|
16
|
+
* clock, and every byte of model output.
|
|
17
|
+
*/
|
|
18
|
+
export function deriveItemTaskId(relPathPosix, normalizedItemText, hexLength = 8) {
|
|
19
|
+
const digest = crypto
|
|
20
|
+
.createHash("sha256")
|
|
21
|
+
.update(`${EXTRACT_VERSION_SALT}\u0000${relPathPosix}\u0000${normalizedItemText}`)
|
|
22
|
+
.digest("hex");
|
|
23
|
+
return `task-${digest.slice(0, hexLength)}`;
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { readSource, MAX_SOURCE_BYTES } from "./source-reader.js";
|
|
2
|
+
export type { SourceFile, ReadSourceResult, ReadSourceErrorCode } from "./source-reader.js";
|
|
3
|
+
export { parseWbs, ALLOWED_FRONT_MATTER_KEYS, MAX_ITEMS } from "./wbs-parser.js";
|
|
4
|
+
export type { ParsedWbs, WbsItem, ParseResult, ParseErrorCode } from "./wbs-parser.js";
|
|
5
|
+
export { buildIngestedRun, stageForItem, KEYWORD_STAGE_TABLE } from "./spec-builder.js";
|
|
6
|
+
export type { BuildInput } from "./spec-builder.js";
|
|
7
|
+
export { ingestTask, deriveTaskId, INGEST_VERSION_SALT } from "./ingest.js";
|
|
8
|
+
export type { IngestOptions, IngestResult, IngestErrorCode } from "./ingest.js";
|
|
9
|
+
export { listIngestedTasks } from "./list-ingested.js";
|
|
10
|
+
export type { IngestedTaskInfo } from "./list-ingested.js";
|
|
11
|
+
export { scanMarkdownTasks } from "./scanner.js";
|
|
12
|
+
export type { ScanOptions, ScanResult, ScanOutcome, ScanSkipReason } from "./scanner.js";
|
|
13
|
+
export { selectPendingItems, normalizeItemText } from "./pending-filter.js";
|
|
14
|
+
export type { PendingItem, Selection, SelectOptions } from "./pending-filter.js";
|
|
15
|
+
export { deriveItemTaskId, EXTRACT_VERSION_SALT } from "./extract-id.js";
|
|
16
|
+
export { createDeterministicDetailer, createLlmDetailer } from "./architect-detailer.js";
|
|
17
|
+
export type { ArchitectDetail, ArchitectDetailer, ArchitectRequest } from "./architect-detailer.js";
|
|
18
|
+
export { validateArchitectDetail } from "./detail-schema.js";
|
|
19
|
+
export type { DetailValidation } from "./detail-schema.js";
|
|
20
|
+
export { buildDetailedRun, detailedNodeId } from "./detailed-run.js";
|
|
21
|
+
export type { DetailedRunInput } from "./detailed-run.js";
|
|
22
|
+
export { extractWbsTasks } from "./wbs-extract.js";
|
|
23
|
+
export type { ExtractOptions, ExtractResult, ExtractErrorCode, ExtractedTask } from "./wbs-extract.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// xcoder:version 3 | xcoder:task task-7a4850 | xcoder:updated 2026-10-25T16:27:07.349Z | xcoder:subtask code-st-892a22
|
|
2
|
+
// Public surface of the task-ingestion module, consumed by the CLI.
|
|
3
|
+
export { readSource, MAX_SOURCE_BYTES } from "./source-reader.js";
|
|
4
|
+
export { parseWbs, ALLOWED_FRONT_MATTER_KEYS, MAX_ITEMS } from "./wbs-parser.js";
|
|
5
|
+
export { buildIngestedRun, stageForItem, KEYWORD_STAGE_TABLE } from "./spec-builder.js";
|
|
6
|
+
export { ingestTask, deriveTaskId, INGEST_VERSION_SALT } from "./ingest.js";
|
|
7
|
+
export { listIngestedTasks } from "./list-ingested.js";
|
|
8
|
+
export { scanMarkdownTasks } from "./scanner.js";
|
|
9
|
+
export { selectPendingItems, normalizeItemText } from "./pending-filter.js";
|
|
10
|
+
export { deriveItemTaskId, EXTRACT_VERSION_SALT } from "./extract-id.js";
|
|
11
|
+
export { createDeterministicDetailer, createLlmDetailer } from "./architect-detailer.js";
|
|
12
|
+
export { validateArchitectDetail } from "./detail-schema.js";
|
|
13
|
+
export { buildDetailedRun, detailedNodeId } from "./detailed-run.js";
|
|
14
|
+
export { extractWbsTasks } from "./wbs-extract.js";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TaskRun } from "../types.js";
|
|
2
|
+
export interface IngestOptions {
|
|
3
|
+
workspaceRoot: string;
|
|
4
|
+
/** User-supplied, workspace-relative or absolute-inside-workspace. */
|
|
5
|
+
from: string;
|
|
6
|
+
/** Explicit opt-in to replace a record that already has progress. */
|
|
7
|
+
force?: boolean;
|
|
8
|
+
/** Injected for tests. */
|
|
9
|
+
now?: () => Date;
|
|
10
|
+
/** Default concurrency (the CLI's `--concurrency`, default 3). */
|
|
11
|
+
concurrency?: number;
|
|
12
|
+
}
|
|
13
|
+
export type IngestErrorCode = "not-found" | "outside-workspace" | "too-large" | "not-a-file" | "bad-front-matter" | "no-items" | "too-many-items" | "has-progress" | "id-collision";
|
|
14
|
+
export type IngestResult = {
|
|
15
|
+
ok: true;
|
|
16
|
+
taskId: string;
|
|
17
|
+
created: boolean;
|
|
18
|
+
/** Absolute path of the written record. */
|
|
19
|
+
path: string;
|
|
20
|
+
/** Workspace-relative POSIX path of the source — what the user typed. */
|
|
21
|
+
sourcePath: string;
|
|
22
|
+
/** Stage of node 0, for the one-line summary. */
|
|
23
|
+
firstStage: string;
|
|
24
|
+
goal: string;
|
|
25
|
+
nodeCount: number;
|
|
26
|
+
nextCommand: string;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
exitCode: 1;
|
|
30
|
+
code: IngestErrorCode;
|
|
31
|
+
message: string;
|
|
32
|
+
};
|
|
33
|
+
export declare const INGEST_VERSION_SALT = "task-ingest:v1";
|
|
34
|
+
/** task- plus the first 8 (or 12, on collision) hex of the salted source path. */
|
|
35
|
+
export declare function deriveTaskId(relPathPosix: string, hexLength?: 8 | 12): string;
|
|
36
|
+
/**
|
|
37
|
+
* Final shape assertion (enforced in code, not in a prompt). Throws on a
|
|
38
|
+
* programming error — nothing is on disk yet at this point.
|
|
39
|
+
*/
|
|
40
|
+
export declare function assertValidRun(run: TaskRun): void;
|
|
41
|
+
/**
|
|
42
|
+
* Ingest one source file into a scannable `.xcoder/tasks/<taskId>.json`.
|
|
43
|
+
* Records intent only — it never runs the task and never spends tokens.
|
|
44
|
+
*/
|
|
45
|
+
export declare function ingestTask(opts: IngestOptions): Promise<IngestResult>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// xcoder:version 5 | xcoder:task task-7a4850 | xcoder:updated 2026-10-25T16:25:47.979Z | xcoder:subtask code-st-892a22
|
|
2
|
+
// C4 — orchestrate ingestion: derive id, enforce idempotency, persist.
|
|
3
|
+
//
|
|
4
|
+
// Failure model: every step can fail, none can half-write. The record only
|
|
5
|
+
// touches disk at the very end, via the existing atomic tmp+rename writer in
|
|
6
|
+
// `store/task-store.ts` — no new writer is introduced.
|
|
7
|
+
import crypto from "node:crypto";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { loadTaskRun, saveTaskRun } from "../store/task-store.js";
|
|
10
|
+
import { createXcoderPaths } from "../util/xcoder-paths.js";
|
|
11
|
+
import { readSource } from "./source-reader.js";
|
|
12
|
+
import { parseWbs } from "./wbs-parser.js";
|
|
13
|
+
import { buildIngestedRun } from "./spec-builder.js";
|
|
14
|
+
export const INGEST_VERSION_SALT = "task-ingest:v1";
|
|
15
|
+
/** task- plus the first 8 (or 12, on collision) hex of the salted source path. */
|
|
16
|
+
export function deriveTaskId(relPathPosix, hexLength = 8) {
|
|
17
|
+
const digest = crypto
|
|
18
|
+
.createHash("sha256")
|
|
19
|
+
.update(`${INGEST_VERSION_SALT}\u0000${relPathPosix}`)
|
|
20
|
+
.digest("hex");
|
|
21
|
+
return `task-${digest.slice(0, hexLength)}`;
|
|
22
|
+
}
|
|
23
|
+
const PROGRESS_STATUSES = new Set(["done", "in_progress", "validating", "healing"]);
|
|
24
|
+
function hasProgress(run) {
|
|
25
|
+
return run.dag.some((n) => PROGRESS_STATUSES.has(n.status));
|
|
26
|
+
}
|
|
27
|
+
const SDLC_STAGES = [
|
|
28
|
+
"requirements",
|
|
29
|
+
"design",
|
|
30
|
+
"code",
|
|
31
|
+
"refactor",
|
|
32
|
+
"test",
|
|
33
|
+
"ui_api_test",
|
|
34
|
+
"fix_defect",
|
|
35
|
+
"document",
|
|
36
|
+
"deploy",
|
|
37
|
+
"conversation",
|
|
38
|
+
"quality_gate",
|
|
39
|
+
];
|
|
40
|
+
const ROLES = [
|
|
41
|
+
"requirements-analyst",
|
|
42
|
+
"architect",
|
|
43
|
+
"programmer",
|
|
44
|
+
"refactorer",
|
|
45
|
+
"tester",
|
|
46
|
+
"ui-api-tester",
|
|
47
|
+
"technical-writer",
|
|
48
|
+
"devops",
|
|
49
|
+
"conversation",
|
|
50
|
+
"business-analyst",
|
|
51
|
+
"defect-manager",
|
|
52
|
+
"scrum-master",
|
|
53
|
+
"system-architect",
|
|
54
|
+
"security-lead",
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Final shape assertion (enforced in code, not in a prompt). Throws on a
|
|
58
|
+
* programming error — nothing is on disk yet at this point.
|
|
59
|
+
*/
|
|
60
|
+
export function assertValidRun(run) {
|
|
61
|
+
if (!run.spec.taskId)
|
|
62
|
+
throw new Error("ingest: taskId missing");
|
|
63
|
+
if (run.dag.length < 1)
|
|
64
|
+
throw new Error("ingest: dag must have at least one node");
|
|
65
|
+
for (const node of run.dag) {
|
|
66
|
+
for (const key of [
|
|
67
|
+
"id",
|
|
68
|
+
"stage",
|
|
69
|
+
"role",
|
|
70
|
+
"engine",
|
|
71
|
+
"dependsOn",
|
|
72
|
+
"goal",
|
|
73
|
+
"deliverableSpec",
|
|
74
|
+
"acceptanceCriteria",
|
|
75
|
+
"status",
|
|
76
|
+
]) {
|
|
77
|
+
if (node[key] === undefined)
|
|
78
|
+
throw new Error(`ingest: node ${node.id} is missing "${key}"`);
|
|
79
|
+
}
|
|
80
|
+
if (!SDLC_STAGES.includes(node.stage))
|
|
81
|
+
throw new Error(`ingest: node ${node.id} has invalid stage "${node.stage}"`);
|
|
82
|
+
if (!ROLES.includes(node.role))
|
|
83
|
+
throw new Error(`ingest: node ${node.id} has invalid role "${node.role}"`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function fail(code, message) {
|
|
87
|
+
return { ok: false, exitCode: 1, code, message };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Ingest one source file into a scannable `.xcoder/tasks/<taskId>.json`.
|
|
91
|
+
* Records intent only — it never runs the task and never spends tokens.
|
|
92
|
+
*/
|
|
93
|
+
export async function ingestTask(opts) {
|
|
94
|
+
const root = path.resolve(opts.workspaceRoot);
|
|
95
|
+
// 1. Read + confine.
|
|
96
|
+
const source = await readSource(root, opts.from);
|
|
97
|
+
if (!source.ok)
|
|
98
|
+
return fail(source.code, source.message);
|
|
99
|
+
const src = source.value;
|
|
100
|
+
// 2. Parse.
|
|
101
|
+
const fileStem = path.basename(src.relPathPosix, path.extname(src.relPathPosix));
|
|
102
|
+
const parsed = parseWbs(src.bytes.toString("utf8"), fileStem);
|
|
103
|
+
if (!parsed.ok)
|
|
104
|
+
return fail(parsed.code, parsed.message);
|
|
105
|
+
// 3. Derive the id, with a documented collision fallback.
|
|
106
|
+
let taskId = deriveTaskId(src.relPathPosix, 8);
|
|
107
|
+
const existingPrimary = await loadTaskRun(root, taskId);
|
|
108
|
+
if (existingPrimary && existingPrimary.spec.inputs.sourcePath !== src.relPathPosix) {
|
|
109
|
+
taskId = deriveTaskId(src.relPathPosix, 12);
|
|
110
|
+
}
|
|
111
|
+
// 4. Idempotency / progress guard.
|
|
112
|
+
const existing = await loadTaskRun(root, taskId);
|
|
113
|
+
let createdAt = (opts.now ?? (() => new Date()))().toISOString();
|
|
114
|
+
let created = true;
|
|
115
|
+
if (existing) {
|
|
116
|
+
if (existing.spec.inputs.sourcePath !== src.relPathPosix) {
|
|
117
|
+
return fail("id-collision", `derived id ${taskId} already belongs to a different source (${existing.spec.inputs.sourcePath ?? "<free-text>"})`);
|
|
118
|
+
}
|
|
119
|
+
if (hasProgress(existing) && !opts.force) {
|
|
120
|
+
return fail("has-progress", `${taskId} already has completed or in-flight subtasks — re-run with --force to replace it`);
|
|
121
|
+
}
|
|
122
|
+
created = false;
|
|
123
|
+
// Preserve the original timestamp so an unchanged source re-ingests to a
|
|
124
|
+
// byte-identical file.
|
|
125
|
+
if (existing.spec.createdAt)
|
|
126
|
+
createdAt = existing.spec.createdAt;
|
|
127
|
+
}
|
|
128
|
+
// 5. Pure build.
|
|
129
|
+
const { spec, dag } = buildIngestedRun({
|
|
130
|
+
parsed: parsed.value,
|
|
131
|
+
source: { relPathPosix: src.relPathPosix, sha256: src.sha256 },
|
|
132
|
+
taskId,
|
|
133
|
+
createdAt,
|
|
134
|
+
concurrency: opts.concurrency ?? 3,
|
|
135
|
+
});
|
|
136
|
+
const run = {
|
|
137
|
+
spec,
|
|
138
|
+
dag,
|
|
139
|
+
// `halted` is the only status `findResumableTasks()` accepts for a task
|
|
140
|
+
// that has not run yet; `running` would be reconciled away on next start
|
|
141
|
+
// and `done` would have nothing to resume.
|
|
142
|
+
status: "halted",
|
|
143
|
+
escalations: [],
|
|
144
|
+
orchestratorTrace: [],
|
|
145
|
+
finalDeliverable: `Ingested from ${src.relPathPosix} — ${dag.length} subtask${dag.length === 1 ? "" : "s"}, not yet started. Resume with: xcoder resume ${taskId}`,
|
|
146
|
+
totalTokenUsage: { inputTokens: 0, outputTokens: 0 },
|
|
147
|
+
};
|
|
148
|
+
// 6. Shape assertion — throws before anything is written.
|
|
149
|
+
assertValidRun(run);
|
|
150
|
+
// 7-8. Persist + report.
|
|
151
|
+
await saveTaskRun(root, run);
|
|
152
|
+
return {
|
|
153
|
+
ok: true,
|
|
154
|
+
taskId,
|
|
155
|
+
created,
|
|
156
|
+
path: createXcoderPaths(root).taskFile(taskId),
|
|
157
|
+
sourcePath: src.relPathPosix,
|
|
158
|
+
firstStage: dag[0].stage,
|
|
159
|
+
goal: spec.goal,
|
|
160
|
+
nodeCount: dag.length,
|
|
161
|
+
nextCommand: `xcoder resume ${taskId}`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface IngestedTaskInfo {
|
|
2
|
+
taskId: string;
|
|
3
|
+
status: string;
|
|
4
|
+
goal: string;
|
|
5
|
+
sourcePath: string | null;
|
|
6
|
+
nodeCount: number;
|
|
7
|
+
createdAt: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Every task run on disk, newest first. `sourcePath` is non-null only for
|
|
11
|
+
* records created by `task add --from`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function listIngestedTasks(root: string): Promise<IngestedTaskInfo[]>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// xcoder:version 1 | xcoder:task task-a91099 | xcoder:updated 2026-10-23T10:53:15.310Z | xcoder:subtask code-st-03748c
|
|
2
|
+
// Read-side helper for `xcoder task list`.
|
|
3
|
+
//
|
|
4
|
+
// Deliberately built on the existing `listTaskRuns()` + `loadTaskRun()` — the
|
|
5
|
+
// exact functions `status`/`incomplete`/`resume` use — so "what `task list`
|
|
6
|
+
// shows" and "what resume can pick up" can never disagree.
|
|
7
|
+
import { listTaskRuns, loadTaskRun } from "../store/task-store.js";
|
|
8
|
+
/**
|
|
9
|
+
* Every task run on disk, newest first. `sourcePath` is non-null only for
|
|
10
|
+
* records created by `task add --from`.
|
|
11
|
+
*/
|
|
12
|
+
export async function listIngestedTasks(root) {
|
|
13
|
+
const ids = await listTaskRuns(root);
|
|
14
|
+
const out = [];
|
|
15
|
+
for (const id of ids) {
|
|
16
|
+
const run = await loadTaskRun(root, id);
|
|
17
|
+
if (!run)
|
|
18
|
+
continue;
|
|
19
|
+
out.push({
|
|
20
|
+
taskId: id,
|
|
21
|
+
status: run.status,
|
|
22
|
+
goal: run.spec.goal ?? "",
|
|
23
|
+
sourcePath: run.spec.provenance?.sourcePath ?? run.spec.inputs.sourcePath ?? null,
|
|
24
|
+
nodeCount: run.dag?.length ?? 0,
|
|
25
|
+
createdAt: run.spec.createdAt ?? "",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
out.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ParsedWbs, WbsItem } from "./wbs-parser.js";
|
|
2
|
+
export interface PendingItem extends WbsItem {
|
|
3
|
+
/** Normalised, length-capped text — the ONLY text used for ids and prompts. */
|
|
4
|
+
normalized: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SelectOptions {
|
|
7
|
+
/** Upper bound on returned items, default MAX_ITEMS from wbs-parser. */
|
|
8
|
+
maxItems?: number;
|
|
9
|
+
/** Characters kept per item, default 400. Longer text is truncated, never rejected. */
|
|
10
|
+
maxChars?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface Selection {
|
|
13
|
+
/** Unchecked items only, in source order. `checked === true` is excluded. */
|
|
14
|
+
pending: PendingItem[];
|
|
15
|
+
/** Count only — checked item TEXT is never echoed, logged or prompted. */
|
|
16
|
+
checkedCount: number;
|
|
17
|
+
}
|
|
18
|
+
/** Default per-item character budget. */
|
|
19
|
+
export declare const DEFAULT_MAX_CHARS = 400;
|
|
20
|
+
/**
|
|
21
|
+
* Pure. Strip invisible/terminal-control code points, collapse all whitespace
|
|
22
|
+
* runs to a single space, trim, then truncate to `maxChars`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function normalizeItemText(text: string, maxChars?: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* Pure. Normalise every item, drop `checked === true`, and preserve source
|
|
27
|
+
* order. Checked items contribute to the count only — their text never leaves
|
|
28
|
+
* this function.
|
|
29
|
+
*/
|
|
30
|
+
export declare function selectPendingItems(parsed: ParsedWbs, opts?: SelectOptions): Selection;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// xcoder:version 1 | xcoder:task task-7a4850 | xcoder:updated 2026-10-25T16:24:39.159Z | xcoder:subtask code-st-892a22
|
|
2
|
+
// C4 — pure selector that turns a parsed WBS into the list of PENDING items.
|
|
3
|
+
//
|
|
4
|
+
// `WbsItem.checked` is already produced by the parser and was previously
|
|
5
|
+
// ignored by `buildIngestedRun`. Honouring it here — in one small pure function
|
|
6
|
+
// with its own tests — is cheaper and safer than threading a filter flag
|
|
7
|
+
// through the existing builder, which `task add --from` must keep calling
|
|
8
|
+
// unchanged.
|
|
9
|
+
//
|
|
10
|
+
// Normalisation is the security-relevant half: item text is untrusted (PRM-01)
|
|
11
|
+
// and is the ONLY text used for ids and prompts, so it is stripped of terminal
|
|
12
|
+
// controls, zero-width and bidi characters and length-capped before it can
|
|
13
|
+
// reach a hash or a model.
|
|
14
|
+
import { MAX_ITEMS } from "./wbs-parser.js";
|
|
15
|
+
/** Default per-item character budget. */
|
|
16
|
+
export const DEFAULT_MAX_CHARS = 400;
|
|
17
|
+
const ZERO_WIDTH = /[\u200b-\u200d\u2060\ufeff]/g;
|
|
18
|
+
const BIDI = /[\u202a-\u202e\u2066-\u2069]/g;
|
|
19
|
+
// All C0/C1 controls including tab/CR/LF — they are collapsed to a space below.
|
|
20
|
+
const CONTROLS = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
21
|
+
/**
|
|
22
|
+
* Pure. Strip invisible/terminal-control code points, collapse all whitespace
|
|
23
|
+
* runs to a single space, trim, then truncate to `maxChars`.
|
|
24
|
+
*/
|
|
25
|
+
export function normalizeItemText(text, maxChars = DEFAULT_MAX_CHARS) {
|
|
26
|
+
if (typeof text !== "string")
|
|
27
|
+
return "";
|
|
28
|
+
let out = text.replace(ZERO_WIDTH, "").replace(BIDI, "");
|
|
29
|
+
out = out.replace(CONTROLS, " ");
|
|
30
|
+
out = out.replace(/\s+/g, " ").trim();
|
|
31
|
+
const cap = Number.isFinite(maxChars) && maxChars > 0 ? Math.floor(maxChars) : DEFAULT_MAX_CHARS;
|
|
32
|
+
return out.length > cap ? out.slice(0, cap).trimEnd() : out;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Pure. Normalise every item, drop `checked === true`, and preserve source
|
|
36
|
+
* order. Checked items contribute to the count only — their text never leaves
|
|
37
|
+
* this function.
|
|
38
|
+
*/
|
|
39
|
+
export function selectPendingItems(parsed, opts = {}) {
|
|
40
|
+
const maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;
|
|
41
|
+
const maxItems = Number.isFinite(opts.maxItems) && opts.maxItems > 0
|
|
42
|
+
? Math.floor(opts.maxItems)
|
|
43
|
+
: MAX_ITEMS;
|
|
44
|
+
const pending = [];
|
|
45
|
+
let checkedCount = 0;
|
|
46
|
+
for (const item of parsed.items) {
|
|
47
|
+
if (item.checked) {
|
|
48
|
+
checkedCount += 1;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const normalized = normalizeItemText(item.text, maxChars);
|
|
52
|
+
if (normalized.length === 0)
|
|
53
|
+
continue;
|
|
54
|
+
if (pending.length >= maxItems)
|
|
55
|
+
continue;
|
|
56
|
+
pending.push({ ...item, normalized });
|
|
57
|
+
}
|
|
58
|
+
return { pending, checkedCount };
|
|
59
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Why a candidate `.md` was deliberately not turned into a task run. */
|
|
2
|
+
export type ScanSkipReason = "progress-summary" | "not-a-task" | "has-progress" | "id-collision" | "bad-source";
|
|
3
|
+
export interface ScanOutcome {
|
|
4
|
+
/** Workspace-relative POSIX path of the candidate. */
|
|
5
|
+
sourcePath: string;
|
|
6
|
+
/** Present once an id could be derived (created/unchanged and some skips). */
|
|
7
|
+
taskId?: string;
|
|
8
|
+
status: "created" | "unchanged" | "skipped" | "failed";
|
|
9
|
+
reason?: ScanSkipReason;
|
|
10
|
+
/** `IngestErrorCode` (or parse/read code) for skipped and failed outcomes. */
|
|
11
|
+
code?: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ScanResult {
|
|
15
|
+
/** Workspace-relative POSIX path of the directory that was scanned. */
|
|
16
|
+
dir: string;
|
|
17
|
+
/** Number of `.md` candidates considered (excludes ignored names). */
|
|
18
|
+
scanned: number;
|
|
19
|
+
created: ScanOutcome[];
|
|
20
|
+
unchanged: ScanOutcome[];
|
|
21
|
+
skipped: ScanOutcome[];
|
|
22
|
+
failed: ScanOutcome[];
|
|
23
|
+
/**
|
|
24
|
+
* Set when the directory itself could not be scanned (missing, not a
|
|
25
|
+
* directory, or outside the workspace). Per-file problems never land here —
|
|
26
|
+
* those are isolated into `failed`.
|
|
27
|
+
*/
|
|
28
|
+
dirError?: {
|
|
29
|
+
code: "not-found" | "not-a-directory" | "outside-workspace";
|
|
30
|
+
message: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface ScanOptions {
|
|
34
|
+
workspaceRoot: string;
|
|
35
|
+
/**
|
|
36
|
+
* Directory to scan, workspace-relative or absolute-inside-workspace.
|
|
37
|
+
* Defaults to the task store's directory (`<.xcoder>/tasks/`).
|
|
38
|
+
*/
|
|
39
|
+
dir?: string;
|
|
40
|
+
/** Report what would happen without writing anything. */
|
|
41
|
+
dryRun?: boolean;
|
|
42
|
+
/** Forwarded to `ingestTask` — never implied. */
|
|
43
|
+
force?: boolean;
|
|
44
|
+
/** Forwarded to `ingestTask`. */
|
|
45
|
+
concurrency?: number;
|
|
46
|
+
/** Injected for tests. */
|
|
47
|
+
now?: () => Date;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Scan `dir` (default: the task store directory) for `.md` task sources and
|
|
51
|
+
* ingest each one.
|
|
52
|
+
*
|
|
53
|
+
* Never throws for a single bad file — every per-file problem is isolated into
|
|
54
|
+
* `failed` so one unreadable source cannot abort the batch.
|
|
55
|
+
*/
|
|
56
|
+
export declare function scanMarkdownTasks(opts: ScanOptions): Promise<ScanResult>;
|