devworkspace 0.0.0-stage → 0.1.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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +127 -3
- package/dist/agents/claude/adapter.d.ts +10 -0
- package/dist/agents/claude/adapter.js +97 -0
- package/dist/agents/claude/adapter.js.map +1 -0
- package/dist/agents/claude/content.d.ts +7 -0
- package/dist/agents/claude/content.js +136 -0
- package/dist/agents/claude/content.js.map +1 -0
- package/dist/agents/types.d.ts +26 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/cli/args.d.ts +194 -0
- package/dist/cli/args.js +93 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/help.d.ts +10 -0
- package/dist/cli/help.js +169 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +19 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/render.d.ts +39 -0
- package/dist/cli/render.js +211 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/run.d.ts +10 -0
- package/dist/cli/run.js +329 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/config/schemas.d.ts +76 -0
- package/dist/config/schemas.js +83 -0
- package/dist/config/schemas.js.map +1 -0
- package/dist/config/validate.d.ts +22 -0
- package/dist/config/validate.js +76 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/core/migrations.d.ts +15 -0
- package/dist/core/migrations.js +34 -0
- package/dist/core/migrations.js.map +1 -0
- package/dist/core/version.d.ts +2 -0
- package/dist/core/version.js +12 -0
- package/dist/core/version.js.map +1 -0
- package/dist/core/versioned.d.ts +13 -0
- package/dist/core/versioned.js +16 -0
- package/dist/core/versioned.js.map +1 -0
- package/dist/core/workspace.d.ts +34 -0
- package/dist/core/workspace.js +92 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/decisions/decision.d.ts +31 -0
- package/dist/decisions/decision.js +123 -0
- package/dist/decisions/decision.js.map +1 -0
- package/dist/discovery/commands.d.ts +12 -0
- package/dist/discovery/commands.js +92 -0
- package/dist/discovery/commands.js.map +1 -0
- package/dist/discovery/detect.d.ts +72 -0
- package/dist/discovery/detect.js +421 -0
- package/dist/discovery/detect.js.map +1 -0
- package/dist/discovery/ignore.d.ts +6 -0
- package/dist/discovery/ignore.js +76 -0
- package/dist/discovery/ignore.js.map +1 -0
- package/dist/discovery/scan.d.ts +24 -0
- package/dist/discovery/scan.js +132 -0
- package/dist/discovery/scan.js.map +1 -0
- package/dist/errors/index.d.ts +25 -0
- package/dist/errors/index.js +39 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/git/git.d.ts +27 -0
- package/dist/git/git.js +97 -0
- package/dist/git/git.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/agent-skills.d.ts +10 -0
- package/dist/integrations/agent-skills.js +45 -0
- package/dist/integrations/agent-skills.js.map +1 -0
- package/dist/integrations/graphify.d.ts +7 -0
- package/dist/integrations/graphify.js +54 -0
- package/dist/integrations/graphify.js.map +1 -0
- package/dist/integrations/index.d.ts +6 -0
- package/dist/integrations/index.js +30 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/omniroute.d.ts +7 -0
- package/dist/integrations/omniroute.js +24 -0
- package/dist/integrations/omniroute.js.map +1 -0
- package/dist/integrations/ponytail.d.ts +10 -0
- package/dist/integrations/ponytail.js +58 -0
- package/dist/integrations/ponytail.js.map +1 -0
- package/dist/integrations/probe.d.ts +6 -0
- package/dist/integrations/probe.js +41 -0
- package/dist/integrations/probe.js.map +1 -0
- package/dist/integrations/types.d.ts +25 -0
- package/dist/integrations/types.js +2 -0
- package/dist/integrations/types.js.map +1 -0
- package/dist/knowledge/facts.d.ts +43 -0
- package/dist/knowledge/facts.js +195 -0
- package/dist/knowledge/facts.js.map +1 -0
- package/dist/portability/bundle.d.ts +22 -0
- package/dist/portability/bundle.js +102 -0
- package/dist/portability/bundle.js.map +1 -0
- package/dist/prompts/onboarding.d.ts +19 -0
- package/dist/prompts/onboarding.js +53 -0
- package/dist/prompts/onboarding.js.map +1 -0
- package/dist/prompts/refresh.d.ts +15 -0
- package/dist/prompts/refresh.js +33 -0
- package/dist/prompts/refresh.js.map +1 -0
- package/dist/prompts/requirements.d.ts +9 -0
- package/dist/prompts/requirements.js +34 -0
- package/dist/prompts/requirements.js.map +1 -0
- package/dist/security/files.d.ts +5 -0
- package/dist/security/files.js +44 -0
- package/dist/security/files.js.map +1 -0
- package/dist/security/redact.d.ts +13 -0
- package/dist/security/redact.js +43 -0
- package/dist/security/redact.js.map +1 -0
- package/dist/services/agent.d.ts +11 -0
- package/dist/services/agent.js +42 -0
- package/dist/services/agent.js.map +1 -0
- package/dist/services/analysis.d.ts +45 -0
- package/dist/services/analysis.js +151 -0
- package/dist/services/analysis.js.map +1 -0
- package/dist/services/common.d.ts +18 -0
- package/dist/services/common.js +18 -0
- package/dist/services/common.js.map +1 -0
- package/dist/services/doctor.d.ts +27 -0
- package/dist/services/doctor.js +223 -0
- package/dist/services/doctor.js.map +1 -0
- package/dist/services/init.d.ts +18 -0
- package/dist/services/init.js +135 -0
- package/dist/services/init.js.map +1 -0
- package/dist/services/knowledge.d.ts +40 -0
- package/dist/services/knowledge.js +72 -0
- package/dist/services/knowledge.js.map +1 -0
- package/dist/services/migrate.d.ts +16 -0
- package/dist/services/migrate.js +54 -0
- package/dist/services/migrate.js.map +1 -0
- package/dist/services/portability.d.ts +27 -0
- package/dist/services/portability.js +155 -0
- package/dist/services/portability.js.map +1 -0
- package/dist/services/records.d.ts +8 -0
- package/dist/services/records.js +26 -0
- package/dist/services/records.js.map +1 -0
- package/dist/services/refresh.d.ts +22 -0
- package/dist/services/refresh.js +64 -0
- package/dist/services/refresh.js.map +1 -0
- package/dist/services/status.d.ts +57 -0
- package/dist/services/status.js +111 -0
- package/dist/services/status.js.map +1 -0
- package/dist/services/tasks.d.ts +44 -0
- package/dist/services/tasks.js +136 -0
- package/dist/services/tasks.js.map +1 -0
- package/dist/sessions/session.d.ts +19 -0
- package/dist/sessions/session.js +53 -0
- package/dist/sessions/session.js.map +1 -0
- package/dist/tasks/routing.d.ts +27 -0
- package/dist/tasks/routing.js +206 -0
- package/dist/tasks/routing.js.map +1 -0
- package/dist/tasks/task.d.ts +90 -0
- package/dist/tasks/task.js +275 -0
- package/dist/tasks/task.js.map +1 -0
- package/dist/utils/cleanup.d.ts +3 -0
- package/dist/utils/cleanup.js +18 -0
- package/dist/utils/cleanup.js.map +1 -0
- package/dist/utils/fs.d.ts +17 -0
- package/dist/utils/fs.js +164 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/lock.d.ts +6 -0
- package/dist/utils/lock.js +77 -0
- package/dist/utils/lock.js.map +1 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.js +18 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +18 -0
- package/dist/utils/paths.js +95 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/proc.d.ts +22 -0
- package/dist/utils/proc.js +74 -0
- package/dist/utils/proc.js.map +1 -0
- package/docs/architecture.md +53 -0
- package/docs/benchmarking.md +33 -0
- package/docs/claude.md +46 -0
- package/docs/contributing.md +64 -0
- package/docs/getting-started.md +68 -0
- package/docs/integrations.md +51 -0
- package/docs/knowledge.md +66 -0
- package/docs/portability.md +40 -0
- package/docs/routing.md +51 -0
- package/docs/security.md +44 -0
- package/docs/testing.md +58 -0
- package/docs/troubleshooting.md +26 -0
- package/package.json +68 -4
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/** Single source of truth for routing. The generated Claude skill and docs/routing.md are rendered from this table. */
|
|
2
|
+
export const WORKFLOWS = {
|
|
3
|
+
QUESTION: {
|
|
4
|
+
type: 'QUESTION',
|
|
5
|
+
contextDepth: 5,
|
|
6
|
+
readOnly: true,
|
|
7
|
+
trackTask: false,
|
|
8
|
+
steps: [
|
|
9
|
+
'Read .devworkspace/context facts relevant to the topic (devworkspace context --topic <area>).',
|
|
10
|
+
'Use Graphify (if ready) or targeted search to locate the code; read only the files needed.',
|
|
11
|
+
'Answer with file references. State uncertainty explicitly.',
|
|
12
|
+
],
|
|
13
|
+
verification: 'None. Do not modify files or run tests.',
|
|
14
|
+
agentSkills: [],
|
|
15
|
+
ponytail: 'Not used.',
|
|
16
|
+
knowledgeUpdate: 'Only if you discovered a durable fact that is missing; record it with "devworkspace fact add".',
|
|
17
|
+
},
|
|
18
|
+
BUG: {
|
|
19
|
+
type: 'BUG',
|
|
20
|
+
contextDepth: 6,
|
|
21
|
+
readOnly: false,
|
|
22
|
+
trackTask: true,
|
|
23
|
+
steps: [
|
|
24
|
+
'Check git status; protect uncommitted user changes.',
|
|
25
|
+
'Investigate and reproduce where practical.',
|
|
26
|
+
'Write a regression test and confirm it fails.',
|
|
27
|
+
'Fix the cause, confirm the test passes.',
|
|
28
|
+
'Run the applicable regression suite, then typecheck/lint/build as required by the testing contract.',
|
|
29
|
+
'Review the diff; record results with "devworkspace task verify".',
|
|
30
|
+
],
|
|
31
|
+
verification: 'Regression test, applicable suite, typecheck, lint, build (whatever the testing contract marks required).',
|
|
32
|
+
agentSkills: ['/test', '/review'],
|
|
33
|
+
ponytail: 'Light review of the diff when review level is not "off".',
|
|
34
|
+
knowledgeUpdate: 'Only if the bug revealed a persistent constraint or convention.',
|
|
35
|
+
},
|
|
36
|
+
FEATURE: {
|
|
37
|
+
type: 'FEATURE',
|
|
38
|
+
contextDepth: 6,
|
|
39
|
+
readOnly: false,
|
|
40
|
+
trackTask: true,
|
|
41
|
+
steps: [
|
|
42
|
+
'Check git status. Read relevant facts and decisions.',
|
|
43
|
+
'Write acceptance criteria and a short plan (task update).',
|
|
44
|
+
'Write tests for the criteria, then implement.',
|
|
45
|
+
'Run targeted tests, then regression, then typecheck/lint/build.',
|
|
46
|
+
'Review the diff.',
|
|
47
|
+
'Update project knowledge for architecture, conventions or decisions that changed.',
|
|
48
|
+
],
|
|
49
|
+
verification: 'Targeted tests, regression, typecheck, lint, build, review.',
|
|
50
|
+
agentSkills: ['/spec', '/plan', '/build', '/test', '/review'],
|
|
51
|
+
ponytail: 'Review for unnecessary complexity before completion.',
|
|
52
|
+
knowledgeUpdate: 'Yes when the feature adds a module, integration, business rule or decision.',
|
|
53
|
+
},
|
|
54
|
+
REFACTOR: {
|
|
55
|
+
type: 'REFACTOR',
|
|
56
|
+
contextDepth: 6,
|
|
57
|
+
readOnly: false,
|
|
58
|
+
trackTask: true,
|
|
59
|
+
steps: [
|
|
60
|
+
'Check git status. Analyse architecture, decisions and dependents (Graphify).',
|
|
61
|
+
'Record a baseline test result (devworkspace task tests --phase baseline).',
|
|
62
|
+
'Plan, then refactor in small verified steps.',
|
|
63
|
+
'Run targeted verification and the full regression suite; compare against the baseline.',
|
|
64
|
+
'Review the diff.',
|
|
65
|
+
],
|
|
66
|
+
verification: 'Baseline vs after (no regressions), typecheck, lint, build, review.',
|
|
67
|
+
agentSkills: ['/plan', '/code-simplify', '/review'],
|
|
68
|
+
ponytail: 'Use for simplification review.',
|
|
69
|
+
knowledgeUpdate: 'Yes if structure or conventions changed.',
|
|
70
|
+
},
|
|
71
|
+
DOCUMENTATION: {
|
|
72
|
+
type: 'DOCUMENTATION',
|
|
73
|
+
contextDepth: 4,
|
|
74
|
+
readOnly: false,
|
|
75
|
+
trackTask: false,
|
|
76
|
+
steps: ['Read the code or facts being documented.', 'Edit documentation only.', 'Run documentation checks if the project has them (link or lint checks).'],
|
|
77
|
+
verification: 'Only checks that apply to documentation.',
|
|
78
|
+
agentSkills: [],
|
|
79
|
+
ponytail: 'Not used.',
|
|
80
|
+
knowledgeUpdate: 'Rarely.',
|
|
81
|
+
},
|
|
82
|
+
TESTING: {
|
|
83
|
+
type: 'TESTING',
|
|
84
|
+
contextDepth: 6,
|
|
85
|
+
readOnly: false,
|
|
86
|
+
trackTask: true,
|
|
87
|
+
steps: ['Check git status.', 'Find existing test conventions.', 'Add tests; confirm they fail for the right reason where applicable.', 'Run the affected suite plus typecheck and lint.'],
|
|
88
|
+
verification: 'Affected tests, typecheck, lint.',
|
|
89
|
+
agentSkills: ['/test'],
|
|
90
|
+
ponytail: 'Optional.',
|
|
91
|
+
knowledgeUpdate: 'If the testing strategy or commands changed.',
|
|
92
|
+
},
|
|
93
|
+
PERFORMANCE: {
|
|
94
|
+
type: 'PERFORMANCE',
|
|
95
|
+
contextDepth: 6,
|
|
96
|
+
readOnly: false,
|
|
97
|
+
trackTask: true,
|
|
98
|
+
steps: ['Measure first; record the baseline.', 'Change one thing at a time.', 'Re-measure and run the regression suite and build.', 'Review.'],
|
|
99
|
+
verification: 'Before/after measurement, regression suite, build.',
|
|
100
|
+
agentSkills: ['/webperf', '/review'],
|
|
101
|
+
ponytail: 'Review for added complexity.',
|
|
102
|
+
knowledgeUpdate: 'If a persistent constraint or decision emerged.',
|
|
103
|
+
},
|
|
104
|
+
SECURITY: {
|
|
105
|
+
type: 'SECURITY',
|
|
106
|
+
contextDepth: 6,
|
|
107
|
+
readOnly: false,
|
|
108
|
+
trackTask: true,
|
|
109
|
+
steps: ['Check git status.', 'Understand the threat and the affected paths.', 'Test that demonstrates the issue, then fix.', 'Regression suite, typecheck, lint, build.', 'Security-focused review. Never print secret values.'],
|
|
110
|
+
verification: 'Regression and exploit test, typecheck, lint, build, review.',
|
|
111
|
+
agentSkills: ['/test', '/review'],
|
|
112
|
+
ponytail: 'Use for bug and security review of the diff.',
|
|
113
|
+
knowledgeUpdate: 'Yes for new security conventions or decisions.',
|
|
114
|
+
},
|
|
115
|
+
ARCHITECTURE: {
|
|
116
|
+
type: 'ARCHITECTURE',
|
|
117
|
+
contextDepth: 6,
|
|
118
|
+
readOnly: false,
|
|
119
|
+
trackTask: true,
|
|
120
|
+
steps: ['Read facts, decisions and architecture notes; map structure with Graphify.', 'Present options and record the chosen one with "devworkspace decision add".', 'Plan and implement in verified steps.', 'Full verification and review.'],
|
|
121
|
+
verification: 'Baseline vs after, typecheck, lint, build, review.',
|
|
122
|
+
agentSkills: ['/spec', '/plan', '/build', '/review'],
|
|
123
|
+
ponytail: 'Review for unnecessary abstraction.',
|
|
124
|
+
knowledgeUpdate: 'Always: architecture notes and the decision record.',
|
|
125
|
+
},
|
|
126
|
+
RELEASE: {
|
|
127
|
+
type: 'RELEASE',
|
|
128
|
+
contextDepth: 6,
|
|
129
|
+
readOnly: false,
|
|
130
|
+
trackTask: true,
|
|
131
|
+
steps: ['Check git status and branch.', 'Run the full testing contract including integration/e2e where available.', 'Update changelog and version per project convention.', 'Do not publish or push without explicit user authorisation.'],
|
|
132
|
+
verification: 'Everything in the testing contract.',
|
|
133
|
+
agentSkills: ['/ship'],
|
|
134
|
+
ponytail: 'Optional final review.',
|
|
135
|
+
knowledgeUpdate: 'If release process facts changed.',
|
|
136
|
+
},
|
|
137
|
+
MAINTENANCE: {
|
|
138
|
+
type: 'MAINTENANCE',
|
|
139
|
+
contextDepth: 4,
|
|
140
|
+
readOnly: false,
|
|
141
|
+
trackTask: true,
|
|
142
|
+
steps: ['Check git status.', 'Make the change.', 'Run lint and build; run tests when behaviour could change.'],
|
|
143
|
+
verification: 'Lint, build, tests if behaviour could change.',
|
|
144
|
+
agentSkills: [],
|
|
145
|
+
ponytail: 'Optional.',
|
|
146
|
+
knowledgeUpdate: 'Only for changed commands or tooling.',
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
const OVERRIDE_PATTERNS = [
|
|
150
|
+
['no-modify', /\b(?:only|just)\s+(?:explain|describe|investigate|look|read|review)\b|\b(?:do\s*n[o']t|don'?t|never)\s+(?:modify|change|edit|touch)\b|\bread[- ]only\b|\bno\s+changes\b/i],
|
|
151
|
+
['plan-only', /\b(?:only|just)\s+(?:make\s+|write\s+|give\s+me\s+)?a\s+plan\b|\bplan\s+only\b/i],
|
|
152
|
+
['no-commit', /\b(?:do\s*n[o']t|don'?t|never)\s+commit\b|\bno\s+commits?\b/i],
|
|
153
|
+
['no-e2e', /\b(?:do\s*n[o']t|don'?t|skip|no)\s+(?:run\s+)?(?:the\s+)?(?:e2e|end[- ]to[- ]end)\b/i],
|
|
154
|
+
['no-tests', /\b(?:do\s*n[o']t|don'?t|skip)\s+(?:run\s+)?(?:any\s+|the\s+)?tests\b(?!\s+but)/i],
|
|
155
|
+
['no-fix', /\b(?:do\s*n[o']t|don'?t)\s+fix\b|\bwithout\s+fixing\b/i],
|
|
156
|
+
];
|
|
157
|
+
export function detectConstraints(text) {
|
|
158
|
+
return OVERRIDE_PATTERNS.filter(([, re]) => re.test(text)).map(([c]) => c);
|
|
159
|
+
}
|
|
160
|
+
const QUESTION_START = /^\s*(?:where|what|which|who|when|how\s+(?:does|do|is|are|can|should)|why|explain|describe|show\s+me|walk\s+me|is\s+there|are\s+there|can\s+you\s+(?:explain|tell|show)|tell\s+me|list|find)\b/i;
|
|
161
|
+
const IMPERATIVE_START = /^\s*(?:fix|add|implement|create|build|refactor|remove|delete|update|change|rename|write|make|migrate|upgrade|optimi[sz]e|release|publish)\b/i;
|
|
162
|
+
/** Heuristic classifier used to propose a task type. The agent may override it; ambiguous input defaults to the safest (read-only) type. */
|
|
163
|
+
export function classifyRequest(text) {
|
|
164
|
+
const constraints = detectConstraints(text);
|
|
165
|
+
// Negated phrases ("don't fix anything") describe limits, not the task itself.
|
|
166
|
+
const t = OVERRIDE_PATTERNS.reduce((acc, [, re]) => acc.replace(new RegExp(re.source, re.flags + 'g'), ' '), text).trim();
|
|
167
|
+
const make = (type, confidence) => ({
|
|
168
|
+
type,
|
|
169
|
+
confidence,
|
|
170
|
+
constraints,
|
|
171
|
+
readOnly: WORKFLOWS[type].readOnly || constraints.includes('no-modify') || constraints.includes('plan-only'),
|
|
172
|
+
});
|
|
173
|
+
if (QUESTION_START.test(t) && !IMPERATIVE_START.test(t))
|
|
174
|
+
return make('QUESTION', 'high');
|
|
175
|
+
if (/\b(?:add|write|improve|increase)\s+(?:\w+\s+)?(?:unit\s+|e2e\s+|integration\s+)?tests?\b|\btest coverage\b|^\s*run\s+(?:the\s+|all\s+)?(?:\w+\s+)?tests?\b/i.test(t))
|
|
176
|
+
return make('TESTING', 'high');
|
|
177
|
+
if (/\b(?:upgrade|bump|update\s+dependenc\w*)\b/i.test(t))
|
|
178
|
+
return make('MAINTENANCE', 'high');
|
|
179
|
+
if (/\b(?:fix|bug|broken|crash(?:es|ing)?|regression|not working|fails?|failing|exception|error)\b/i.test(t))
|
|
180
|
+
return make('BUG', 'high');
|
|
181
|
+
if (/\b(?:refactor|restructure|reorgani[sz]e|clean\s?up|simplify|de-?duplicate)\b/i.test(t))
|
|
182
|
+
return make('REFACTOR', 'high');
|
|
183
|
+
if (/\b(?:vulnerab\w*|security|csrf|xss|sql injection|sanitiz\w*|harden\w*|cve-\d+)\b/i.test(t))
|
|
184
|
+
return make('SECURITY', 'high');
|
|
185
|
+
if (/\b(?:performance|slow|latency|speed up|optimi[sz]e|throughput)\b/i.test(t))
|
|
186
|
+
return make('PERFORMANCE', 'high');
|
|
187
|
+
if (/\b(?:document|readme|docs|changelog|jsdoc|docstring)\b/i.test(t))
|
|
188
|
+
return make('DOCUMENTATION', 'high');
|
|
189
|
+
if (/\b(?:release|publish|deploy|ship|version bump)\b/i.test(t))
|
|
190
|
+
return make('RELEASE', 'high');
|
|
191
|
+
if (/\b(?:upgrade|bump|update\s+dependenc\w*|chore|lint(?:ing)?|format(?:ting)?)\b/i.test(t))
|
|
192
|
+
return make('MAINTENANCE', 'high');
|
|
193
|
+
if (/\b(?:redesign|re-?architect\w*|migrate\s+to|architecture)\b/i.test(t))
|
|
194
|
+
return make('ARCHITECTURE', 'high');
|
|
195
|
+
if (/\b(?:add|implement|create|build|support|introduce|enable|new)\b/i.test(t))
|
|
196
|
+
return make('FEATURE', 'high');
|
|
197
|
+
return make('QUESTION', 'low');
|
|
198
|
+
}
|
|
199
|
+
export function renderRoutingTable() {
|
|
200
|
+
const rows = Object.values(WORKFLOWS).map((w) => {
|
|
201
|
+
const flags = [w.readOnly ? 'read-only' : 'modifies code', w.trackTask ? 'tracked task' : 'no task file'].join(', ');
|
|
202
|
+
return `| ${w.type} | ${flags} | L${w.contextDepth} | ${w.verification} |`;
|
|
203
|
+
});
|
|
204
|
+
return ['| Type | Nature | Max context | Verification |', '| --- | --- | --- | --- |', ...rows].join('\n');
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.js","sourceRoot":"","sources":["../../src/tasks/routing.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TaskType } from './task.js';\n\nexport type Constraint = 'no-modify' | 'plan-only' | 'no-commit' | 'no-e2e' | 'no-tests' | 'no-fix';\n\nexport interface WorkflowSpec {\n type: TaskType;\n /** Highest context level normally needed (1 instructions … 6 tests/implementation). */\n contextDepth: 1 | 2 | 3 | 4 | 5 | 6;\n readOnly: boolean;\n trackTask: boolean;\n steps: string[];\n verification: string;\n agentSkills: string[];\n ponytail: string;\n knowledgeUpdate: string;\n}\n\n/** Single source of truth for routing. The generated Claude skill and docs/routing.md are rendered from this table. */\nexport const WORKFLOWS: Record<TaskType, WorkflowSpec> = {\n QUESTION: {\n type: 'QUESTION',\n contextDepth: 5,\n readOnly: true,\n trackTask: false,\n steps: [\n 'Read .devworkspace/context facts relevant to the topic (devworkspace context --topic <area>).',\n 'Use Graphify (if ready) or targeted search to locate the code; read only the files needed.',\n 'Answer with file references. State uncertainty explicitly.',\n ],\n verification: 'None. Do not modify files or run tests.',\n agentSkills: [],\n ponytail: 'Not used.',\n knowledgeUpdate: 'Only if you discovered a durable fact that is missing; record it with \"devworkspace fact add\".',\n },\n BUG: {\n type: 'BUG',\n contextDepth: 6,\n readOnly: false,\n trackTask: true,\n steps: [\n 'Check git status; protect uncommitted user changes.',\n 'Investigate and reproduce where practical.',\n 'Write a regression test and confirm it fails.',\n 'Fix the cause, confirm the test passes.',\n 'Run the applicable regression suite, then typecheck/lint/build as required by the testing contract.',\n 'Review the diff; record results with \"devworkspace task verify\".',\n ],\n verification: 'Regression test, applicable suite, typecheck, lint, build (whatever the testing contract marks required).',\n agentSkills: ['/test', '/review'],\n ponytail: 'Light review of the diff when review level is not \"off\".',\n knowledgeUpdate: 'Only if the bug revealed a persistent constraint or convention.',\n },\n FEATURE: {\n type: 'FEATURE',\n contextDepth: 6,\n readOnly: false,\n trackTask: true,\n steps: [\n 'Check git status. Read relevant facts and decisions.',\n 'Write acceptance criteria and a short plan (task update).',\n 'Write tests for the criteria, then implement.',\n 'Run targeted tests, then regression, then typecheck/lint/build.',\n 'Review the diff.',\n 'Update project knowledge for architecture, conventions or decisions that changed.',\n ],\n verification: 'Targeted tests, regression, typecheck, lint, build, review.',\n agentSkills: ['/spec', '/plan', '/build', '/test', '/review'],\n ponytail: 'Review for unnecessary complexity before completion.',\n knowledgeUpdate: 'Yes when the feature adds a module, integration, business rule or decision.',\n },\n REFACTOR: {\n type: 'REFACTOR',\n contextDepth: 6,\n readOnly: false,\n trackTask: true,\n steps: [\n 'Check git status. Analyse architecture, decisions and dependents (Graphify).',\n 'Record a baseline test result (devworkspace task tests --phase baseline).',\n 'Plan, then refactor in small verified steps.',\n 'Run targeted verification and the full regression suite; compare against the baseline.',\n 'Review the diff.',\n ],\n verification: 'Baseline vs after (no regressions), typecheck, lint, build, review.',\n agentSkills: ['/plan', '/code-simplify', '/review'],\n ponytail: 'Use for simplification review.',\n knowledgeUpdate: 'Yes if structure or conventions changed.',\n },\n DOCUMENTATION: {\n type: 'DOCUMENTATION',\n contextDepth: 4,\n readOnly: false,\n trackTask: false,\n steps: ['Read the code or facts being documented.', 'Edit documentation only.', 'Run documentation checks if the project has them (link or lint checks).'],\n verification: 'Only checks that apply to documentation.',\n agentSkills: [],\n ponytail: 'Not used.',\n knowledgeUpdate: 'Rarely.',\n },\n TESTING: {\n type: 'TESTING',\n contextDepth: 6,\n readOnly: false,\n trackTask: true,\n steps: ['Check git status.', 'Find existing test conventions.', 'Add tests; confirm they fail for the right reason where applicable.', 'Run the affected suite plus typecheck and lint.'],\n verification: 'Affected tests, typecheck, lint.',\n agentSkills: ['/test'],\n ponytail: 'Optional.',\n knowledgeUpdate: 'If the testing strategy or commands changed.',\n },\n PERFORMANCE: {\n type: 'PERFORMANCE',\n contextDepth: 6,\n readOnly: false,\n trackTask: true,\n steps: ['Measure first; record the baseline.', 'Change one thing at a time.', 'Re-measure and run the regression suite and build.', 'Review.'],\n verification: 'Before/after measurement, regression suite, build.',\n agentSkills: ['/webperf', '/review'],\n ponytail: 'Review for added complexity.',\n knowledgeUpdate: 'If a persistent constraint or decision emerged.',\n },\n SECURITY: {\n type: 'SECURITY',\n contextDepth: 6,\n readOnly: false,\n trackTask: true,\n steps: ['Check git status.', 'Understand the threat and the affected paths.', 'Test that demonstrates the issue, then fix.', 'Regression suite, typecheck, lint, build.', 'Security-focused review. Never print secret values.'],\n verification: 'Regression and exploit test, typecheck, lint, build, review.',\n agentSkills: ['/test', '/review'],\n ponytail: 'Use for bug and security review of the diff.',\n knowledgeUpdate: 'Yes for new security conventions or decisions.',\n },\n ARCHITECTURE: {\n type: 'ARCHITECTURE',\n contextDepth: 6,\n readOnly: false,\n trackTask: true,\n steps: ['Read facts, decisions and architecture notes; map structure with Graphify.', 'Present options and record the chosen one with \"devworkspace decision add\".', 'Plan and implement in verified steps.', 'Full verification and review.'],\n verification: 'Baseline vs after, typecheck, lint, build, review.',\n agentSkills: ['/spec', '/plan', '/build', '/review'],\n ponytail: 'Review for unnecessary abstraction.',\n knowledgeUpdate: 'Always: architecture notes and the decision record.',\n },\n RELEASE: {\n type: 'RELEASE',\n contextDepth: 6,\n readOnly: false,\n trackTask: true,\n steps: ['Check git status and branch.', 'Run the full testing contract including integration/e2e where available.', 'Update changelog and version per project convention.', 'Do not publish or push without explicit user authorisation.'],\n verification: 'Everything in the testing contract.',\n agentSkills: ['/ship'],\n ponytail: 'Optional final review.',\n knowledgeUpdate: 'If release process facts changed.',\n },\n MAINTENANCE: {\n type: 'MAINTENANCE',\n contextDepth: 4,\n readOnly: false,\n trackTask: true,\n steps: ['Check git status.', 'Make the change.', 'Run lint and build; run tests when behaviour could change.'],\n verification: 'Lint, build, tests if behaviour could change.',\n agentSkills: [],\n ponytail: 'Optional.',\n knowledgeUpdate: 'Only for changed commands or tooling.',\n },\n};\n\nconst OVERRIDE_PATTERNS: [Constraint, RegExp][] = [\n ['no-modify', /\\b(?:only|just)\\s+(?:explain|describe|investigate|look|read|review)\\b|\\b(?:do\\s*n[o']t|don'?t|never)\\s+(?:modify|change|edit|touch)\\b|\\bread[- ]only\\b|\\bno\\s+changes\\b/i],\n ['plan-only', /\\b(?:only|just)\\s+(?:make\\s+|write\\s+|give\\s+me\\s+)?a\\s+plan\\b|\\bplan\\s+only\\b/i],\n ['no-commit', /\\b(?:do\\s*n[o']t|don'?t|never)\\s+commit\\b|\\bno\\s+commits?\\b/i],\n ['no-e2e', /\\b(?:do\\s*n[o']t|don'?t|skip|no)\\s+(?:run\\s+)?(?:the\\s+)?(?:e2e|end[- ]to[- ]end)\\b/i],\n ['no-tests', /\\b(?:do\\s*n[o']t|don'?t|skip)\\s+(?:run\\s+)?(?:any\\s+|the\\s+)?tests\\b(?!\\s+but)/i],\n ['no-fix', /\\b(?:do\\s*n[o']t|don'?t)\\s+fix\\b|\\bwithout\\s+fixing\\b/i],\n];\n\nexport function detectConstraints(text: string): Constraint[] {\n return OVERRIDE_PATTERNS.filter(([, re]) => re.test(text)).map(([c]) => c);\n}\n\nexport interface Classification {\n type: TaskType;\n confidence: 'high' | 'low';\n constraints: Constraint[];\n /** True when the request must not change files (question type, or an explicit user override). */\n readOnly: boolean;\n}\n\nconst QUESTION_START = /^\\s*(?:where|what|which|who|when|how\\s+(?:does|do|is|are|can|should)|why|explain|describe|show\\s+me|walk\\s+me|is\\s+there|are\\s+there|can\\s+you\\s+(?:explain|tell|show)|tell\\s+me|list|find)\\b/i;\nconst IMPERATIVE_START = /^\\s*(?:fix|add|implement|create|build|refactor|remove|delete|update|change|rename|write|make|migrate|upgrade|optimi[sz]e|release|publish)\\b/i;\n\n/** Heuristic classifier used to propose a task type. The agent may override it; ambiguous input defaults to the safest (read-only) type. */\nexport function classifyRequest(text: string): Classification {\n const constraints = detectConstraints(text);\n // Negated phrases (\"don't fix anything\") describe limits, not the task itself.\n const t = OVERRIDE_PATTERNS.reduce((acc, [, re]) => acc.replace(new RegExp(re.source, re.flags + 'g'), ' '), text).trim();\n const make = (type: TaskType, confidence: 'high' | 'low'): Classification => ({\n type,\n confidence,\n constraints,\n readOnly: WORKFLOWS[type].readOnly || constraints.includes('no-modify') || constraints.includes('plan-only'),\n });\n if (QUESTION_START.test(t) && !IMPERATIVE_START.test(t)) return make('QUESTION', 'high');\n if (/\\b(?:add|write|improve|increase)\\s+(?:\\w+\\s+)?(?:unit\\s+|e2e\\s+|integration\\s+)?tests?\\b|\\btest coverage\\b|^\\s*run\\s+(?:the\\s+|all\\s+)?(?:\\w+\\s+)?tests?\\b/i.test(t)) return make('TESTING', 'high');\n if (/\\b(?:upgrade|bump|update\\s+dependenc\\w*)\\b/i.test(t)) return make('MAINTENANCE', 'high');\n if (/\\b(?:fix|bug|broken|crash(?:es|ing)?|regression|not working|fails?|failing|exception|error)\\b/i.test(t)) return make('BUG', 'high');\n if (/\\b(?:refactor|restructure|reorgani[sz]e|clean\\s?up|simplify|de-?duplicate)\\b/i.test(t)) return make('REFACTOR', 'high');\n if (/\\b(?:vulnerab\\w*|security|csrf|xss|sql injection|sanitiz\\w*|harden\\w*|cve-\\d+)\\b/i.test(t)) return make('SECURITY', 'high');\n if (/\\b(?:performance|slow|latency|speed up|optimi[sz]e|throughput)\\b/i.test(t)) return make('PERFORMANCE', 'high');\n if (/\\b(?:document|readme|docs|changelog|jsdoc|docstring)\\b/i.test(t)) return make('DOCUMENTATION', 'high');\n if (/\\b(?:release|publish|deploy|ship|version bump)\\b/i.test(t)) return make('RELEASE', 'high');\n if (/\\b(?:upgrade|bump|update\\s+dependenc\\w*|chore|lint(?:ing)?|format(?:ting)?)\\b/i.test(t)) return make('MAINTENANCE', 'high');\n if (/\\b(?:redesign|re-?architect\\w*|migrate\\s+to|architecture)\\b/i.test(t)) return make('ARCHITECTURE', 'high');\n if (/\\b(?:add|implement|create|build|support|introduce|enable|new)\\b/i.test(t)) return make('FEATURE', 'high');\n return make('QUESTION', 'low');\n}\n\nexport function renderRoutingTable(): string {\n const rows = (Object.values(WORKFLOWS) as WorkflowSpec[]).map((w) => {\n const flags = [w.readOnly ? 'read-only' : 'modifies code', w.trackTask ? 'tracked task' : 'no task file'].join(', ');\n return `| ${w.type} | ${flags} | L${w.contextDepth} | ${w.verification} |`;\n });\n return ['| Type | Nature | Max context | Verification |', '| --- | --- | --- | --- |', ...rows].join('\\n');\n}\n"]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type CommandEntry } from '../config/schemas.js';
|
|
2
|
+
import { type Ws } from '../core/workspace.js';
|
|
3
|
+
export declare const TASK_TYPES: readonly ["QUESTION", "BUG", "FEATURE", "REFACTOR", "DOCUMENTATION", "TESTING", "PERFORMANCE", "SECURITY", "ARCHITECTURE", "RELEASE", "MAINTENANCE"];
|
|
4
|
+
export type TaskType = (typeof TASK_TYPES)[number];
|
|
5
|
+
export declare const TASK_STATUSES: readonly ["IDEA", "SPECIFIED", "PLANNED", "IN_PROGRESS", "TESTING", "REVIEW", "BLOCKED", "FAILED", "COMPLETED"];
|
|
6
|
+
export type TaskStatus = (typeof TASK_STATUSES)[number];
|
|
7
|
+
export declare const VERIFY_STATUSES: readonly ["PENDING", "PASS", "FAIL", "SKIPPED"];
|
|
8
|
+
export type VerifyStatus = (typeof VERIFY_STATUSES)[number];
|
|
9
|
+
export interface VerificationItem {
|
|
10
|
+
check: string;
|
|
11
|
+
required: boolean;
|
|
12
|
+
status: VerifyStatus;
|
|
13
|
+
reason?: string;
|
|
14
|
+
detail?: string;
|
|
15
|
+
updatedAt?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface TestCounts {
|
|
18
|
+
passed: number;
|
|
19
|
+
failed: number;
|
|
20
|
+
}
|
|
21
|
+
export interface Attestations {
|
|
22
|
+
userWorkIntact: boolean;
|
|
23
|
+
knowledgeReviewed: boolean;
|
|
24
|
+
finalStateInspected: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface Task {
|
|
27
|
+
schemaVersion: number;
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
type: TaskType;
|
|
31
|
+
status: TaskStatus;
|
|
32
|
+
createdAt: string;
|
|
33
|
+
updatedAt: string;
|
|
34
|
+
description: string;
|
|
35
|
+
acceptanceCriteria: string[];
|
|
36
|
+
plan: string[];
|
|
37
|
+
affectedAreas: string[];
|
|
38
|
+
verification: VerificationItem[];
|
|
39
|
+
tests: {
|
|
40
|
+
baseline?: TestCounts;
|
|
41
|
+
after?: TestCounts;
|
|
42
|
+
};
|
|
43
|
+
attestations: Attestations;
|
|
44
|
+
decisions: string[];
|
|
45
|
+
blockers: string[];
|
|
46
|
+
constraints: string[];
|
|
47
|
+
completionSummary?: string;
|
|
48
|
+
history: {
|
|
49
|
+
at: string;
|
|
50
|
+
from: TaskStatus | null;
|
|
51
|
+
to: TaskStatus;
|
|
52
|
+
note?: string;
|
|
53
|
+
}[];
|
|
54
|
+
}
|
|
55
|
+
export declare function allowedTransitions(from: TaskStatus): TaskStatus[];
|
|
56
|
+
export declare function isCodeChanging(type: TaskType): boolean;
|
|
57
|
+
/** Verification a task of this type needs, given what the project actually offers. Unavailable checks are never demanded. */
|
|
58
|
+
export declare function requiredChecksFor(type: TaskType, contract: CommandEntry[], reviewLevel?: 'off' | 'light' | 'strict'): VerificationItem[];
|
|
59
|
+
export declare function newTaskId(now?: Date): string;
|
|
60
|
+
export interface NewTaskInput {
|
|
61
|
+
title: string;
|
|
62
|
+
type: TaskType;
|
|
63
|
+
description?: string;
|
|
64
|
+
verification: VerificationItem[];
|
|
65
|
+
constraints?: string[];
|
|
66
|
+
}
|
|
67
|
+
export declare function createTaskObject(input: NewTaskInput, now: string, id?: string): Task;
|
|
68
|
+
export declare function assertNoSecrets(task: Task): void;
|
|
69
|
+
export declare function validateTask(v: unknown, file?: string): Task;
|
|
70
|
+
export interface GateResult {
|
|
71
|
+
ok: boolean;
|
|
72
|
+
failures: string[];
|
|
73
|
+
warnings: string[];
|
|
74
|
+
}
|
|
75
|
+
/** The completion gate. A task is only COMPLETED when every applicable requirement holds. */
|
|
76
|
+
export declare function evaluateGate(task: Task): GateResult;
|
|
77
|
+
export declare function transitionTask(task: Task, to: TaskStatus, now: string, note?: string): GateResult | null;
|
|
78
|
+
export declare function nextRequiredStep(task: Task): string;
|
|
79
|
+
export declare function saveTask(ws: Ws, task: Task): Promise<void>;
|
|
80
|
+
export declare function loadTask(ws: Ws, idOrPrefix: string): Promise<Task>;
|
|
81
|
+
export interface TaskListing {
|
|
82
|
+
tasks: Task[];
|
|
83
|
+
corrupt: {
|
|
84
|
+
file: string;
|
|
85
|
+
error: string;
|
|
86
|
+
}[];
|
|
87
|
+
}
|
|
88
|
+
export declare function listTasks(ws: Ws, opts?: {
|
|
89
|
+
includeCompleted?: boolean;
|
|
90
|
+
}): Promise<TaskListing>;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DevWorkspaceError } from '../errors/index.js';
|
|
4
|
+
import { Check } from '../config/validate.js';
|
|
5
|
+
import { SCHEMA_VERSION } from '../config/schemas.js';
|
|
6
|
+
import { atomicWriteJson, ensureDir, pathExists, randomId } from '../utils/fs.js';
|
|
7
|
+
import { readVersioned } from '../core/versioned.js';
|
|
8
|
+
import { backupIfVersionChanges } from '../core/workspace.js';
|
|
9
|
+
import { containsSecret } from '../security/redact.js';
|
|
10
|
+
export const TASK_TYPES = [
|
|
11
|
+
'QUESTION',
|
|
12
|
+
'BUG',
|
|
13
|
+
'FEATURE',
|
|
14
|
+
'REFACTOR',
|
|
15
|
+
'DOCUMENTATION',
|
|
16
|
+
'TESTING',
|
|
17
|
+
'PERFORMANCE',
|
|
18
|
+
'SECURITY',
|
|
19
|
+
'ARCHITECTURE',
|
|
20
|
+
'RELEASE',
|
|
21
|
+
'MAINTENANCE',
|
|
22
|
+
];
|
|
23
|
+
export const TASK_STATUSES = ['IDEA', 'SPECIFIED', 'PLANNED', 'IN_PROGRESS', 'TESTING', 'REVIEW', 'BLOCKED', 'FAILED', 'COMPLETED'];
|
|
24
|
+
export const VERIFY_STATUSES = ['PENDING', 'PASS', 'FAIL', 'SKIPPED'];
|
|
25
|
+
const TRANSITIONS = {
|
|
26
|
+
IDEA: ['SPECIFIED', 'PLANNED', 'IN_PROGRESS', 'BLOCKED', 'FAILED'],
|
|
27
|
+
SPECIFIED: ['PLANNED', 'IN_PROGRESS', 'BLOCKED', 'IDEA'],
|
|
28
|
+
PLANNED: ['IN_PROGRESS', 'SPECIFIED', 'BLOCKED', 'IDEA'],
|
|
29
|
+
IN_PROGRESS: ['TESTING', 'REVIEW', 'COMPLETED', 'BLOCKED', 'FAILED', 'PLANNED'],
|
|
30
|
+
TESTING: ['IN_PROGRESS', 'REVIEW', 'COMPLETED', 'BLOCKED', 'FAILED'],
|
|
31
|
+
REVIEW: ['IN_PROGRESS', 'TESTING', 'COMPLETED', 'BLOCKED', 'FAILED'],
|
|
32
|
+
BLOCKED: ['IN_PROGRESS', 'PLANNED', 'SPECIFIED', 'IDEA', 'FAILED'],
|
|
33
|
+
FAILED: ['IN_PROGRESS', 'IDEA'],
|
|
34
|
+
COMPLETED: ['IN_PROGRESS'],
|
|
35
|
+
};
|
|
36
|
+
export function allowedTransitions(from) {
|
|
37
|
+
return TRANSITIONS[from];
|
|
38
|
+
}
|
|
39
|
+
const CODE_CHANGING = ['BUG', 'FEATURE', 'REFACTOR', 'PERFORMANCE', 'SECURITY', 'ARCHITECTURE', 'MAINTENANCE', 'TESTING', 'RELEASE'];
|
|
40
|
+
const CHECKS_BY_TYPE = {
|
|
41
|
+
QUESTION: [],
|
|
42
|
+
DOCUMENTATION: [],
|
|
43
|
+
BUG: ['unit', 'typecheck', 'lint', 'build'],
|
|
44
|
+
FEATURE: ['unit', 'typecheck', 'lint', 'build', 'review'],
|
|
45
|
+
REFACTOR: ['unit', 'typecheck', 'lint', 'build', 'review'],
|
|
46
|
+
TESTING: ['unit', 'typecheck', 'lint'],
|
|
47
|
+
PERFORMANCE: ['unit', 'build', 'review'],
|
|
48
|
+
SECURITY: ['unit', 'typecheck', 'lint', 'build', 'review'],
|
|
49
|
+
ARCHITECTURE: ['unit', 'typecheck', 'lint', 'build', 'review'],
|
|
50
|
+
RELEASE: ['unit', 'typecheck', 'lint', 'build', 'integration', 'e2e'],
|
|
51
|
+
MAINTENANCE: ['lint', 'build'],
|
|
52
|
+
};
|
|
53
|
+
export function isCodeChanging(type) {
|
|
54
|
+
return CODE_CHANGING.includes(type);
|
|
55
|
+
}
|
|
56
|
+
/** Verification a task of this type needs, given what the project actually offers. Unavailable checks are never demanded. */
|
|
57
|
+
export function requiredChecksFor(type, contract, reviewLevel = 'light') {
|
|
58
|
+
const available = new Set(contract.filter((c) => c.available).map((c) => c.id));
|
|
59
|
+
const items = [];
|
|
60
|
+
for (const check of CHECKS_BY_TYPE[type]) {
|
|
61
|
+
if (check === 'review') {
|
|
62
|
+
if (reviewLevel === 'off')
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
else if (!available.has(check))
|
|
66
|
+
continue;
|
|
67
|
+
items.push({ check, required: true, status: 'PENDING' });
|
|
68
|
+
}
|
|
69
|
+
return items;
|
|
70
|
+
}
|
|
71
|
+
export function newTaskId(now = new Date()) {
|
|
72
|
+
const d = now.toISOString().slice(0, 10).replace(/-/g, '');
|
|
73
|
+
return `T-${d}-${randomId(2)}`;
|
|
74
|
+
}
|
|
75
|
+
export function createTaskObject(input, now, id = newTaskId()) {
|
|
76
|
+
const title = input.title.trim().slice(0, 200);
|
|
77
|
+
if (!title)
|
|
78
|
+
throw new DevWorkspaceError('USAGE', 'Task title must not be empty.');
|
|
79
|
+
const task = {
|
|
80
|
+
schemaVersion: SCHEMA_VERSION,
|
|
81
|
+
id,
|
|
82
|
+
title,
|
|
83
|
+
type: input.type,
|
|
84
|
+
status: 'IDEA',
|
|
85
|
+
createdAt: now,
|
|
86
|
+
updatedAt: now,
|
|
87
|
+
description: (input.description ?? '').trim().slice(0, 4000),
|
|
88
|
+
acceptanceCriteria: [],
|
|
89
|
+
plan: [],
|
|
90
|
+
affectedAreas: [],
|
|
91
|
+
verification: input.verification,
|
|
92
|
+
tests: {},
|
|
93
|
+
attestations: { userWorkIntact: false, knowledgeReviewed: false, finalStateInspected: false },
|
|
94
|
+
decisions: [],
|
|
95
|
+
blockers: [],
|
|
96
|
+
constraints: input.constraints ?? [],
|
|
97
|
+
history: [{ at: now, from: null, to: 'IDEA' }],
|
|
98
|
+
};
|
|
99
|
+
assertNoSecrets(task);
|
|
100
|
+
return task;
|
|
101
|
+
}
|
|
102
|
+
export function assertNoSecrets(task) {
|
|
103
|
+
if (containsSecret(JSON.stringify(task))) {
|
|
104
|
+
throw new DevWorkspaceError('SECURITY', 'Task content looks like it contains a secret; refusing to store it.', {
|
|
105
|
+
hint: 'Remove credential values from titles, plans and notes.',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function validateTask(v, file = 'task') {
|
|
110
|
+
const c = new Check(file);
|
|
111
|
+
const o = c.obj(v);
|
|
112
|
+
c.at_('schemaVersion').int(o.schemaVersion, 1);
|
|
113
|
+
c.at_('id').str(o.id, { min: 3, max: 64 });
|
|
114
|
+
c.at_('title').str(o.title, { min: 1, max: 300 });
|
|
115
|
+
c.at_('type').oneOf(o.type, TASK_TYPES);
|
|
116
|
+
c.at_('status').oneOf(o.status, TASK_STATUSES);
|
|
117
|
+
c.at_('createdAt').iso(o.createdAt);
|
|
118
|
+
c.at_('updatedAt').iso(o.updatedAt);
|
|
119
|
+
for (const k of ['acceptanceCriteria', 'plan', 'affectedAreas', 'decisions', 'blockers', 'constraints'])
|
|
120
|
+
c.at_(k).strArr(o[k]);
|
|
121
|
+
c.at_('verification')
|
|
122
|
+
.arr(o.verification, 100)
|
|
123
|
+
.forEach((item, i) => {
|
|
124
|
+
const ic = c.at_(`verification.${i}`);
|
|
125
|
+
const io = ic.obj(item);
|
|
126
|
+
ic.at_('check').str(io.check, { max: 50 });
|
|
127
|
+
ic.at_('required').bool(io.required);
|
|
128
|
+
ic.at_('status').oneOf(io.status, VERIFY_STATUSES);
|
|
129
|
+
});
|
|
130
|
+
const a = c.at_('attestations').obj(o.attestations);
|
|
131
|
+
c.at_('attestations.userWorkIntact').bool(a.userWorkIntact);
|
|
132
|
+
c.at_('attestations.knowledgeReviewed').bool(a.knowledgeReviewed);
|
|
133
|
+
c.at_('attestations.finalStateInspected').bool(a.finalStateInspected);
|
|
134
|
+
c.at_('tests').obj(o.tests);
|
|
135
|
+
c.at_('history').arr(o.history, 500);
|
|
136
|
+
return o;
|
|
137
|
+
}
|
|
138
|
+
/** The completion gate. A task is only COMPLETED when every applicable requirement holds. */
|
|
139
|
+
export function evaluateGate(task) {
|
|
140
|
+
const failures = [];
|
|
141
|
+
const warnings = [];
|
|
142
|
+
for (const v of task.verification) {
|
|
143
|
+
if (!v.required)
|
|
144
|
+
continue;
|
|
145
|
+
if (v.status === 'FAIL')
|
|
146
|
+
failures.push(`${v.check}: FAIL`);
|
|
147
|
+
else if (v.status === 'PENDING')
|
|
148
|
+
failures.push(`${v.check}: not run yet`);
|
|
149
|
+
else if (v.status === 'SKIPPED') {
|
|
150
|
+
if (v.reason === 'user-override')
|
|
151
|
+
warnings.push(`${v.check}: skipped at the user's request; completion is less certain`);
|
|
152
|
+
else
|
|
153
|
+
failures.push(`${v.check}: skipped without a user override`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const { baseline, after } = task.tests;
|
|
157
|
+
if (baseline && !after)
|
|
158
|
+
failures.push('baseline test results recorded, but no results after the change');
|
|
159
|
+
if (baseline && after) {
|
|
160
|
+
if (after.failed > baseline.failed)
|
|
161
|
+
failures.push(`regression: failures rose from ${baseline.failed} to ${after.failed}`);
|
|
162
|
+
if (after.passed < baseline.passed)
|
|
163
|
+
failures.push(`regression: passing tests fell from ${baseline.passed} to ${after.passed}`);
|
|
164
|
+
}
|
|
165
|
+
if (isCodeChanging(task.type)) {
|
|
166
|
+
if (!task.attestations.userWorkIntact)
|
|
167
|
+
failures.push('not confirmed: no user work was overwritten');
|
|
168
|
+
if (!task.attestations.knowledgeReviewed)
|
|
169
|
+
failures.push('not confirmed: project knowledge was reviewed for updates');
|
|
170
|
+
if (!task.attestations.finalStateInspected)
|
|
171
|
+
failures.push('not confirmed: final repository state was inspected');
|
|
172
|
+
}
|
|
173
|
+
if ((isCodeChanging(task.type) || task.type === 'DOCUMENTATION') && !(task.completionSummary ?? '').trim()) {
|
|
174
|
+
failures.push('completion summary is missing');
|
|
175
|
+
}
|
|
176
|
+
return { ok: failures.length === 0, failures, warnings };
|
|
177
|
+
}
|
|
178
|
+
export function transitionTask(task, to, now, note) {
|
|
179
|
+
if (task.status === to)
|
|
180
|
+
return null;
|
|
181
|
+
if (!TRANSITIONS[task.status].includes(to)) {
|
|
182
|
+
throw new DevWorkspaceError('GATE', `Cannot move a task from ${task.status} to ${to}.`, {
|
|
183
|
+
hint: `Allowed from ${task.status}: ${TRANSITIONS[task.status].join(', ')}.`,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
let gate = null;
|
|
187
|
+
if (to === 'COMPLETED') {
|
|
188
|
+
gate = evaluateGate(task);
|
|
189
|
+
if (!gate.ok) {
|
|
190
|
+
throw new DevWorkspaceError('GATE', `Task ${task.id} cannot be completed:\n - ${gate.failures.join('\n - ')}`, {
|
|
191
|
+
hint: 'Resolve the items above (or mark the task BLOCKED/FAILED). A task is never complete while verification is failing.',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
task.history.push({ at: now, from: task.status, to, ...(note ? { note: note.slice(0, 300) } : {}) });
|
|
196
|
+
task.status = to;
|
|
197
|
+
task.updatedAt = now;
|
|
198
|
+
return gate;
|
|
199
|
+
}
|
|
200
|
+
export function nextRequiredStep(task) {
|
|
201
|
+
switch (task.status) {
|
|
202
|
+
case 'COMPLETED':
|
|
203
|
+
return 'None. The task is complete.';
|
|
204
|
+
case 'FAILED':
|
|
205
|
+
return 'Review the failure, then move the task back to IN_PROGRESS or close the idea.';
|
|
206
|
+
case 'BLOCKED':
|
|
207
|
+
return task.blockers.length > 0 ? `Resolve blocker: ${task.blockers[task.blockers.length - 1]}` : 'Identify and record what blocks this task.';
|
|
208
|
+
case 'IDEA':
|
|
209
|
+
return 'Clarify acceptance criteria, then plan.';
|
|
210
|
+
case 'SPECIFIED':
|
|
211
|
+
return 'Write a plan.';
|
|
212
|
+
case 'PLANNED':
|
|
213
|
+
return 'Start implementation (baseline verification first for meaningful changes).';
|
|
214
|
+
default: {
|
|
215
|
+
const pending = task.verification.filter((v) => v.required && (v.status === 'PENDING' || v.status === 'FAIL'));
|
|
216
|
+
if (pending.length > 0)
|
|
217
|
+
return `Run/fix verification: ${pending.map((p) => `${p.check} (${p.status.toLowerCase()})`).join(', ')}.`;
|
|
218
|
+
const g = evaluateGate(task);
|
|
219
|
+
return g.ok ? 'All gates pass. Mark the task COMPLETED.' : `Satisfy the completion gate: ${g.failures[0]}`;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// ---------- storage ----------
|
|
224
|
+
function taskFile(ws, task) {
|
|
225
|
+
return path.join(task.status === 'COMPLETED' ? ws.tasksCompleted : ws.tasksActive, `${task.id}.json`);
|
|
226
|
+
}
|
|
227
|
+
export async function saveTask(ws, task) {
|
|
228
|
+
validateTask(task);
|
|
229
|
+
assertNoSecrets(task);
|
|
230
|
+
const dest = taskFile(ws, task);
|
|
231
|
+
await backupIfVersionChanges(ws, dest, task.schemaVersion);
|
|
232
|
+
await ensureDir(path.dirname(dest));
|
|
233
|
+
await atomicWriteJson(dest, task);
|
|
234
|
+
const other = path.join(task.status === 'COMPLETED' ? ws.tasksActive : ws.tasksCompleted, `${task.id}.json`);
|
|
235
|
+
await fs.rm(other, { force: true });
|
|
236
|
+
}
|
|
237
|
+
export async function loadTask(ws, idOrPrefix) {
|
|
238
|
+
for (const dir of [ws.tasksActive, ws.tasksCompleted]) {
|
|
239
|
+
if (!(await pathExists(dir)))
|
|
240
|
+
continue;
|
|
241
|
+
const names = (await fs.readdir(dir)).filter((n) => n.endsWith('.json'));
|
|
242
|
+
const hits = names.filter((n) => n === `${idOrPrefix}.json` || n.startsWith(idOrPrefix));
|
|
243
|
+
if (hits.length > 1) {
|
|
244
|
+
const exact = hits.find((n) => n === `${idOrPrefix}.json`);
|
|
245
|
+
if (!exact)
|
|
246
|
+
throw new DevWorkspaceError('USAGE', `Task id "${idOrPrefix}" is ambiguous.`, { hint: 'Use more characters of the id.' });
|
|
247
|
+
hits.splice(0, hits.length, exact);
|
|
248
|
+
}
|
|
249
|
+
if (hits.length === 1) {
|
|
250
|
+
const file = path.join(dir, hits[0]);
|
|
251
|
+
return (await readVersioned(file, 'task', SCHEMA_VERSION, validateTask)).data;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
throw new DevWorkspaceError('NOT_FOUND', `No task matching "${idOrPrefix}".`, { hint: 'List tasks with "devworkspace task list --all".' });
|
|
255
|
+
}
|
|
256
|
+
export async function listTasks(ws, opts = {}) {
|
|
257
|
+
const tasks = [];
|
|
258
|
+
const corrupt = [];
|
|
259
|
+
const dirs = opts.includeCompleted ? [ws.tasksActive, ws.tasksCompleted] : [ws.tasksActive];
|
|
260
|
+
for (const dir of dirs) {
|
|
261
|
+
if (!(await pathExists(dir)))
|
|
262
|
+
continue;
|
|
263
|
+
for (const name of (await fs.readdir(dir)).filter((n) => n.endsWith('.json')).sort()) {
|
|
264
|
+
try {
|
|
265
|
+
tasks.push((await readVersioned(path.join(dir, name), 'task', SCHEMA_VERSION, validateTask)).data);
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
corrupt.push({ file: `${path.basename(dir)}/${name}`, error: e.message });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
tasks.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
273
|
+
return { tasks, corrupt };
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=task.js.map
|