@mjasnikovs/pi-task 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/shared/child-output.d.ts +21 -0
- package/dist/shared/child-output.js +40 -0
- package/dist/shared/child-process.d.ts +71 -0
- package/dist/shared/child-process.js +190 -0
- package/dist/shared/pi-invocation.d.ts +7 -0
- package/dist/shared/pi-invocation.js +24 -0
- package/dist/task/child-runner.d.ts +66 -0
- package/dist/task/child-runner.js +157 -0
- package/dist/task/enrichment.d.ts +12 -0
- package/dist/task/enrichment.js +82 -0
- package/dist/task/failure-classifier.d.ts +15 -0
- package/dist/task/failure-classifier.js +63 -0
- package/dist/task/file-inventory.d.ts +9 -0
- package/dist/task/file-inventory.js +44 -0
- package/dist/task/loop-detector.d.ts +32 -0
- package/dist/task/loop-detector.js +46 -0
- package/dist/task/orchestrator.d.ts +54 -0
- package/dist/task/orchestrator.js +387 -0
- package/dist/task/parsers.d.ts +32 -0
- package/dist/task/parsers.js +172 -0
- package/dist/task/phases.d.ts +56 -0
- package/dist/task/phases.js +477 -0
- package/dist/task/prompts.d.ts +21 -0
- package/dist/task/prompts.js +346 -0
- package/dist/task/service-blocks.d.ts +3 -0
- package/dist/task/service-blocks.js +10 -0
- package/dist/task/task-file.d.ts +14 -0
- package/dist/task/task-file.js +15 -0
- package/dist/task/task-io.d.ts +19 -0
- package/dist/task/task-io.js +78 -0
- package/dist/task/task-parsers.d.ts +12 -0
- package/dist/task/task-parsers.js +75 -0
- package/dist/task/task-types.d.ts +21 -0
- package/dist/task/task-types.js +18 -0
- package/dist/task/timings.d.ts +18 -0
- package/dist/task/timings.js +36 -0
- package/dist/task/widget.d.ts +39 -0
- package/dist/task/widget.js +122 -0
- package/dist/workers/brave-search.d.ts +17 -0
- package/dist/workers/brave-search.js +77 -0
- package/dist/workers/docs-cache.d.ts +16 -0
- package/dist/workers/docs-cache.js +66 -0
- package/dist/workers/docs-core.d.ts +86 -0
- package/dist/workers/docs-core.js +329 -0
- package/dist/workers/docs-index.d.ts +9 -0
- package/dist/workers/docs-index.js +200 -0
- package/dist/workers/docs-resolve.d.ts +12 -0
- package/dist/workers/docs-resolve.js +126 -0
- package/dist/workers/docs-retrieve.d.ts +15 -0
- package/dist/workers/docs-retrieve.js +91 -0
- package/dist/workers/fetch-core.d.ts +35 -0
- package/dist/workers/fetch-core.js +91 -0
- package/dist/workers/html-clean.d.ts +17 -0
- package/dist/workers/html-clean.js +142 -0
- package/dist/workers/index.d.ts +2 -0
- package/dist/workers/index.js +10 -0
- package/dist/workers/npm-version.d.ts +32 -0
- package/dist/workers/npm-version.js +102 -0
- package/dist/workers/pi-worker-core.d.ts +28 -0
- package/dist/workers/pi-worker-core.js +29 -0
- package/dist/workers/pi-worker-docs.d.ts +16 -0
- package/dist/workers/pi-worker-docs.js +143 -0
- package/dist/workers/pi-worker-fetch.d.ts +20 -0
- package/dist/workers/pi-worker-fetch.js +72 -0
- package/dist/workers/pi-worker-search.d.ts +7 -0
- package/dist/workers/pi-worker-search.js +55 -0
- package/dist/workers/pi-worker.d.ts +10 -0
- package/dist/workers/pi-worker.js +61 -0
- package/dist/workers/search-core.d.ts +19 -0
- package/dist/workers/search-core.js +35 -0
- package/dist/workers/shared.d.ts +3 -0
- package/dist/workers/shared.js +4 -0
- package/package.json +50 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt templates for every phase of the pi-task pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Each template is a pure function: inputs → prompt string. No I/O, no side
|
|
5
|
+
* effects, trivially testable.
|
|
6
|
+
*/
|
|
7
|
+
export const MAX_GRILL_QUESTIONS = 10;
|
|
8
|
+
const REFINE_PROMPT = (raw) => `You receive a user's task description for an AI coding agent. Rewrite it to be unambiguous and actionable.
|
|
9
|
+
|
|
10
|
+
Output structure (four sections, exact headings, in this order):
|
|
11
|
+
|
|
12
|
+
GOAL
|
|
13
|
+
One paragraph. What done looks like, in the user's domain language.
|
|
14
|
+
|
|
15
|
+
CONSTRAINTS
|
|
16
|
+
Bullet list. What must not change, what semantics to preserve, what the agent should avoid touching.
|
|
17
|
+
|
|
18
|
+
KNOWN-UNKNOWNS
|
|
19
|
+
Bullet list. Questions worth asking the user before implementing, inferred from gaps or ambiguities in the raw prompt.
|
|
20
|
+
|
|
21
|
+
EXTERNAL-DEPENDENCIES
|
|
22
|
+
Bullet list. Third-party APIs, SDKs, services, protocols, or cloud products the task touches. One bullet per dependency. Format each bullet as:
|
|
23
|
+
- <name> <one-line search-friendly phrase optimized for current-state web search>
|
|
24
|
+
Two or more spaces separate name from phrase.
|
|
25
|
+
Do NOT list npm packages here — they flow through the existing backtick-package path. If the task mentions a service only by its npm SDK name (e.g. \`@twurple/api\`), still list the underlying service (Twitch) here.
|
|
26
|
+
If the task is purely local with no third-party services, leave this section header in place with zero bullets.
|
|
27
|
+
|
|
28
|
+
Rules:
|
|
29
|
+
- Fix spelling and grammar; output in English regardless of input language.
|
|
30
|
+
- Preserve every concrete identifier verbatim (paths, function names, ports, env vars, file:line refs).
|
|
31
|
+
- Do not invent requirements not implied by the input.
|
|
32
|
+
- Do not output any preamble, commentary, or markdown headings beyond the four sections above.
|
|
33
|
+
|
|
34
|
+
Task: ${raw}`;
|
|
35
|
+
// ─── Research fan-out prompts ─────────────────────────────────────────────────
|
|
36
|
+
const RESEARCH_READ_ONLY_CONSTRAINT = `IMPORTANT: You are ONLY allowed to READ. Do NOT create, modify, or delete any files. Use the read, grep, find, and ls tools to inspect the repo.`;
|
|
37
|
+
// Shared guard for every research worker. Open-ended tasks ("analyze the code",
|
|
38
|
+
// "how would you improve X", "write a report") tempt a worker into producing the
|
|
39
|
+
// deliverable itself — e.g. writing the whole code-review report in the CONTEXT
|
|
40
|
+
// section, which then runs for many minutes, gets truncated, and poisons every
|
|
41
|
+
// downstream phase. Research only gathers INPUTS for a later spec; it must never
|
|
42
|
+
// be the deliverable. This also pins the output format (no preamble, no fences,
|
|
43
|
+
// no repeated header) that large/open-ended tasks otherwise drift away from.
|
|
44
|
+
const RESEARCH_INPUTS_NOT_DELIVERABLE = `CRITICAL — you are gathering INPUTS for a later spec, NOT performing the task. Even if the task asks you to analyze, review, audit, report, plan, design, or write code, you must NOT produce that deliverable here. Do not write the report/analysis/plan/code. Your entire job is to emit the one structured section described below, which feeds a separate phase that writes the spec. Surveying the repo so that section is accurate is right; producing the task's output is wrong and wastes the run.
|
|
45
|
+
|
|
46
|
+
OUTPUT DISCIPLINE — strict: emit ONLY the raw section lines described below. No preamble (never "I've read the codebase…", never "Here is the … section"), no closing remarks, no Markdown headings, no code fences (no \`\`\`), and do NOT repeat the section name as a header. The first character of your output is the first entry of the list.`;
|
|
47
|
+
const RESEARCH_FILES_PROMPT = (refined) => `You are doing targeted research for an AI coding agent. Use the read, grep, find, and ls tools to locate every path on disk the agent will read, edit, or reference for the following task. This includes source code AND configuration, schemas, fixtures — any file the agent needs to know exists.
|
|
48
|
+
|
|
49
|
+
FILES owns paths. APIS owns symbols. Do not omit a path because it "feels like config" — if the agent will touch or read it, list it here.
|
|
50
|
+
|
|
51
|
+
When a task operates across a whole directory tree (e.g. lint, typecheck, build, format, test-all), list the root directory entry (\`src/ one-line purpose\`) instead of enumerating every file under it. Enumerate individual files only when they need to be singled out — modified specifically, called out by name in the task, or distinct from their siblings in some material way.
|
|
52
|
+
|
|
53
|
+
RELEVANCE — read carefully: list ONLY paths this specific task touches — the files the agent will read, edit, or must be aware of to complete THIS task. Do NOT inventory the whole repo or list files just because they exist. A file the agent will never open does not belong here. Aim for the smallest sufficient set: include every path the task genuinely reaches and nothing more. There is no fixed limit — a broad task may need many entries, a narrow one only a few. Right-size to the task, not to a number, and collapse directories to their root entry where the task spans a whole tree.
|
|
54
|
+
|
|
55
|
+
${RESEARCH_INPUTS_NOT_DELIVERABLE}
|
|
56
|
+
|
|
57
|
+
${RESEARCH_READ_ONLY_CONSTRAINT}
|
|
58
|
+
|
|
59
|
+
Output ONLY the content of a FILES section — one entry per line, format:
|
|
60
|
+
<path>[:<line>] <one-line purpose>
|
|
61
|
+
|
|
62
|
+
No section header. No other sections. No preamble.
|
|
63
|
+
|
|
64
|
+
Task:
|
|
65
|
+
${refined}`;
|
|
66
|
+
const RESEARCH_APIS_PROMPT = (refined) => `You are doing targeted research for an AI coding agent. Use the read, grep, find, and ls tools to identify the commands, functions, types, and interfaces the agent will use for the following task.
|
|
67
|
+
|
|
68
|
+
APIS owns symbols and commands BY NAME ONLY. Do NOT include any file path or path fragment — no \`package.json\`, no \`./src/foo.ts\`, no \`package.json#scripts.lint\`. If the symbol is a script defined in package.json, write the invocation (\`npm run lint\`), not its location. If the symbol is a config file, it does not belong in APIS at all — it belongs in FILES.
|
|
69
|
+
|
|
70
|
+
RELEVANCE — read carefully: list ONLY the symbols the agent will call, implement, modify, or directly depend on for THIS task. Do NOT enumerate the project's entire public surface or dump every exported function in a touched file. A symbol unrelated to the task does not belong here just because it sits in the same module. Keep the smallest sufficient set: include every symbol the task actually exercises and nothing more. There is no fixed limit — list as many as the task truly needs and no padding beyond that.
|
|
71
|
+
|
|
72
|
+
${RESEARCH_INPUTS_NOT_DELIVERABLE}
|
|
73
|
+
|
|
74
|
+
${RESEARCH_READ_ONLY_CONSTRAINT}
|
|
75
|
+
|
|
76
|
+
Output ONLY the content of an APIS section — one entry per line, format:
|
|
77
|
+
<name> <one-line signature or use>
|
|
78
|
+
|
|
79
|
+
No section header. No other sections. No preamble.
|
|
80
|
+
|
|
81
|
+
Task:
|
|
82
|
+
${refined}`;
|
|
83
|
+
const RESEARCH_CONTEXT_PROMPT = (refined) => `You are doing targeted research for an AI coding agent. Use the read, grep, find, and ls tools to gather background knowledge and architectural context the agent will need for the following task.
|
|
84
|
+
|
|
85
|
+
RELEVANCE — read carefully: keep it tight. Each bullet must be an architectural fact that changes HOW the agent implements THIS task — a constraint, a non-obvious data flow, a gotcha, a hidden coupling. No general project tour, no restating the task, no facts the agent would not act on. If a bullet would not change a single implementation decision, drop it. There is no fixed bullet count — include every fact that bears on the task and no filler; fewer sharp bullets beat many shallow ones. If the task is itself an analysis or review, these bullets capture facts that analysis will rely on — they are NOT the analysis; do not write findings or recommendations here.
|
|
86
|
+
|
|
87
|
+
${RESEARCH_INPUTS_NOT_DELIVERABLE}
|
|
88
|
+
|
|
89
|
+
${RESEARCH_READ_ONLY_CONSTRAINT}
|
|
90
|
+
|
|
91
|
+
LIVE-DATA RULE:
|
|
92
|
+
- If EXTERNAL CONTEXT contains an "### npm: <pkg>" block, those version numbers are LIVE registry data. Cite them verbatim if you mention versions at all.
|
|
93
|
+
- If EXTERNAL CONTEXT contains a "### service: <name>" block, those search results are LIVE web data and are authoritative over training data for that service's current API surface, deprecation status, and replacement systems. Do not contradict them from memory. If you must cite a version, status, or API name for that service, take it from the block.
|
|
94
|
+
- If EXTERNAL CONTEXT contains a "### freshness-check skipped" block, you have no current data for the listed services. Do NOT claim their current state from memory; say "current state not verified" and recommend the user verify before implementation.
|
|
95
|
+
- Do NOT write bullets like "X is the latest stable" or "version Y is current" from memory — your training data goes stale. Either quote from EXTERNAL CONTEXT or omit the claim entirely.
|
|
96
|
+
|
|
97
|
+
Output ONLY the content of a CONTEXT section — bullet list, one bullet per line, format:
|
|
98
|
+
- <bullet>
|
|
99
|
+
|
|
100
|
+
No section header. No other sections. No preamble.
|
|
101
|
+
|
|
102
|
+
Task:
|
|
103
|
+
${refined}`;
|
|
104
|
+
const RESEARCH_TOOLING_PROMPT = (refined) => `You are doing targeted research for an AI coding agent. Inspect the repo to identify the verification tools (lint, typecheck, test, build, e2e, container, dev-server) the project actually has.
|
|
105
|
+
|
|
106
|
+
${RESEARCH_INPUTS_NOT_DELIVERABLE}
|
|
107
|
+
|
|
108
|
+
${RESEARCH_READ_ONLY_CONSTRAINT}
|
|
109
|
+
|
|
110
|
+
Look at package.json scripts, Makefile, pyproject.toml, go.mod, Dockerfile, docker-compose.y*ml, playwright.config.*, .eslintrc*, tsconfig.json, etc. Use exact commands, not guesses. If a tool isn't present in the repo, omit it — don't invent.
|
|
111
|
+
|
|
112
|
+
Output ONLY the content of a TOOLING section — one entry per line, format:
|
|
113
|
+
<category> <exact command to invoke>
|
|
114
|
+
|
|
115
|
+
Categories: lint, typecheck, test, build, e2e/browser, container, dev-server
|
|
116
|
+
|
|
117
|
+
No section header. No other sections. No preamble. May be empty if no verification tools are found.
|
|
118
|
+
|
|
119
|
+
Task:
|
|
120
|
+
${refined}`;
|
|
121
|
+
const GRILL_GEN_PROMPT = (refined, research) => `You are preparing clarifying questions for the user, based on a refined task description and the research that follows.
|
|
122
|
+
|
|
123
|
+
Start from the KNOWN-UNKNOWNS bullets in the task. Add any new ambiguity surfaced by the research. Drop any unknowns the research already resolved.
|
|
124
|
+
|
|
125
|
+
SCOPE RULES — read carefully:
|
|
126
|
+
- Questions must clarify the EXISTING scope. Do NOT propose new deliverables, enhancements, modernizations, or "while I'm here" cleanups.
|
|
127
|
+
- Forbidden patterns: "should I also…", "should we modernize…", "do you want me to update X while I'm at it…", "should I integrate Y…", "would you like guidance on Z…".
|
|
128
|
+
- Allowed patterns: "by 'X' do you mean A or B?", "should failure mode Y be treated as Z?", "which of <files matching the task> applies here?".
|
|
129
|
+
- If the refined task + research leave no genuine ambiguity, output zero questions. Zero questions is a valid and preferred outcome. Do not pad.
|
|
130
|
+
|
|
131
|
+
Output format — read carefully:
|
|
132
|
+
- If you have questions: emit them as a plain numbered list, one per line, at most ${MAX_GRILL_QUESTIONS}, no preamble.
|
|
133
|
+
- If you have zero questions: emit the single literal token NONE on its own line. Do NOT emit empty output — an empty response is treated as a crash, not as "no questions". The NONE sentinel is the only way to signal an intentional empty list.
|
|
134
|
+
|
|
135
|
+
Refined task:
|
|
136
|
+
${refined}
|
|
137
|
+
|
|
138
|
+
Research:
|
|
139
|
+
${research}`;
|
|
140
|
+
const GRILL_AUTO_ANSWER_PROMPT = (refined, research, question) => `You are pre-answering a clarifying question for an AI coding task. You have the refined task and the research notes. You can also use the read tool to open any file mentioned in the research (e.g. package.json) if it helps you answer.
|
|
141
|
+
|
|
142
|
+
Your job is to produce a recommended default answer. If the default is one the user would almost certainly accept, you tag it ANSWER and we skip the user entirely. Otherwise you tag it UNKNOWN and we show the suggestion in the input box for the user to confirm or override.
|
|
143
|
+
|
|
144
|
+
YOU MUST PROPOSE A DEFAULT, no matter what. NEVER refuse. NEVER leave the answer empty.
|
|
145
|
+
|
|
146
|
+
LIVE-DATA RULE — read carefully:
|
|
147
|
+
- If EXTERNAL CONTEXT contains an "### npm: <pkg>" block, those version numbers are LIVE registry data and are MORE RECENT than anything you remember from training. Use them as the source of truth.
|
|
148
|
+
- For any question about "latest", "current", "newest", or "which version" of an npm package, you MUST cite the version from the "### npm: <pkg>" block in EXTERNAL CONTEXT if one is present. Do NOT contradict it with a remembered version.
|
|
149
|
+
- If EXTERNAL CONTEXT contains a "### service: <name>" block, those search results are LIVE web data and are authoritative over training data for that service's current API surface, deprecation status, and replacement systems. For any question about that service's API, status, or replacement, cite from the block; do not contradict it from memory.
|
|
150
|
+
- If EXTERNAL CONTEXT contains a "### freshness-check skipped" block, you have no current data for the listed services. If the question is about one of them, tag UNKNOWN and say the current state needs user verification — do NOT answer from memory.
|
|
151
|
+
- If no "### npm: <pkg>" block is present and the question is about latest/current versions, tag UNKNOWN — do not invent a version from training data, since that data goes stale within months.
|
|
152
|
+
|
|
153
|
+
Use the REVERSIBILITY TEST to choose the tag:
|
|
154
|
+
|
|
155
|
+
ANSWER: accepting your default is cheap to undo — output style, reporting
|
|
156
|
+
format, treat-as-error policy, summary vs full output, scope when
|
|
157
|
+
obviously implied, recommended convention for a typical project.
|
|
158
|
+
If the user would only "fix" your default by editing prose in the
|
|
159
|
+
task file, it's ANSWER.
|
|
160
|
+
|
|
161
|
+
UNKNOWN: accepting your default would do work that is costly to reverse —
|
|
162
|
+
file mutations, destructive operations, irreversible writes, tool
|
|
163
|
+
or dependency choices, format/structure decisions that change
|
|
164
|
+
downstream artifacts, anything that touches state outside the
|
|
165
|
+
task file. This INCLUDES choosing the implementation approach,
|
|
166
|
+
algorithm, or strategy — *how* the task is solved, not just
|
|
167
|
+
whether (e.g. "extract the value with a post-processing regex" vs
|
|
168
|
+
"rewrite the system prompt", "add a fallback step" vs "swap the
|
|
169
|
+
model", "parse manually" vs "use a library"). Approach decisions
|
|
170
|
+
shape the entire spec and are expensive to unwind once the agent
|
|
171
|
+
builds on them, so the user must vet the strategy: tag UNKNOWN and
|
|
172
|
+
surface your recommended approach as the default.
|
|
173
|
+
|
|
174
|
+
Output format — ONE LINE only, no preamble, no markdown:
|
|
175
|
+
ANSWER: <one-line answer>
|
|
176
|
+
UNKNOWN: <one-line default>
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
ANSWER: report a summary with counts and representative examples ← reporting style is cheap to undo
|
|
180
|
+
ANSWER: treat all warnings and errors as genuine issues, do not ignore ← policy is cheap to undo
|
|
181
|
+
ANSWER: run the read-only check variant (prettier --check, eslint, tsc) ← read-only side, safer default; flip later if wanted
|
|
182
|
+
UNKNOWN: use npm ← package manager choice is costly to reverse mid-task
|
|
183
|
+
UNKNOWN: write output to ./report.md ← creates a file; user may want a different path or no file
|
|
184
|
+
UNKNOWN: extract the value with a post-processing regex step ← picks the implementation approach; user must vet the strategy
|
|
185
|
+
|
|
186
|
+
Examples of FORBIDDEN outputs:
|
|
187
|
+
UNKNOWN:
|
|
188
|
+
UNKNOWN: it depends
|
|
189
|
+
(empty)
|
|
190
|
+
I think the user should decide.
|
|
191
|
+
|
|
192
|
+
Refined task:
|
|
193
|
+
${refined}
|
|
194
|
+
|
|
195
|
+
Research:
|
|
196
|
+
${research}
|
|
197
|
+
|
|
198
|
+
Question: ${question}`;
|
|
199
|
+
function composeRetryEmphasis(problem) {
|
|
200
|
+
if (problem === 'spec does not start with GOAL'
|
|
201
|
+
|| problem === 'spec starts with a markdown fence'
|
|
202
|
+
|| problem === 'spec is wrapped in a cat heredoc') {
|
|
203
|
+
return '\nPREVIOUS ATTEMPT VIOLATED THESE RULES. The very first characters of your output MUST be the letters G-O-A-L. Not a backtick, not `cat`, not a heredoc — the literal word GOAL.\n';
|
|
204
|
+
}
|
|
205
|
+
if (problem.startsWith('spec missing required section:')) {
|
|
206
|
+
const section = problem.replace('spec missing required section: ', '');
|
|
207
|
+
return `\nPREVIOUS ATTEMPT was missing the ${section} section. All four sections are required and must be non-empty: GOAL, CONSTRAINTS, ACCEPTANCE, VERIFY.\n`;
|
|
208
|
+
}
|
|
209
|
+
return `\nPREVIOUS ATTEMPT was invalid (${problem}). Ensure all four sections are present and the output starts with the literal word GOAL.\n`;
|
|
210
|
+
}
|
|
211
|
+
const COMPOSE_PROMPT = (refined, research, qa, retryProblem) => `You are composing the final implementation spec for an AI coding agent. Combine the refined task, the research, and the user's Q&A answers into one spec.
|
|
212
|
+
|
|
213
|
+
CRITICAL FORMAT RULES (read first):
|
|
214
|
+
- Output the spec as plain markdown text. Do NOT wrap your entire output in a code block, shell fence, or heredoc. Do NOT prefix with \`\`\`sh / \`\`\`bash. Do NOT use \`cat << EOF > file\` patterns. Your response begins literally with "GOAL" on the first line.
|
|
215
|
+
- The ONLY fenced code block in your output is the one immediately following \`VERIFY:\` — and that fence must be \`\`\`sh, not \`\`\`bash, not anything else.
|
|
216
|
+
- No preamble, no commentary, no trailing summary.
|
|
217
|
+
${retryProblem ? composeRetryEmphasis(retryProblem) : ''}
|
|
218
|
+
Output exactly four top-level sections in this order. Every section must be present and non-empty.
|
|
219
|
+
|
|
220
|
+
GOAL
|
|
221
|
+
<one paragraph>
|
|
222
|
+
|
|
223
|
+
CONSTRAINTS
|
|
224
|
+
- <bullet>
|
|
225
|
+
- …
|
|
226
|
+
|
|
227
|
+
ACCEPTANCE
|
|
228
|
+
- <human-readable success criterion>
|
|
229
|
+
- …
|
|
230
|
+
|
|
231
|
+
VERIFY:
|
|
232
|
+
\`\`\`sh
|
|
233
|
+
<runnable shell command 1>
|
|
234
|
+
<runnable shell command 2>
|
|
235
|
+
\`\`\`
|
|
236
|
+
|
|
237
|
+
VERIFY must contain real, runnable commands the receiving agent can execute via \`bash -c\`. No placeholders. No "TODO". No "your test here".
|
|
238
|
+
|
|
239
|
+
VERIFY must exercise the surface area the task actually touches. Draw VERIFY commands only from VERIFIED-TOOLING (the pre-validated subset of the research TOOLING section) — do not invent tools the repo does not have. Apply these rules:
|
|
240
|
+
- HTML / CSS / client-side JS / UI changes → MUST include a browser-driving check. If the repo has playwright, use it (e.g. \`npx playwright test\`). If not, at minimum start the dev server and curl the affected route to confirm it serves 200 and the expected markup. A bare "open the page" instruction is not acceptable — it must be a shell command.
|
|
241
|
+
- Dockerfile / docker-compose changes → MUST include a real build (\`docker build …\` or \`docker compose build\`) and, where feasible, a smoke run that proves the container starts (e.g. \`docker run --rm <img> <cmd>\` or \`docker compose up -d && docker compose ps\`).
|
|
242
|
+
- TypeScript / JavaScript source changes → MUST include the project's typecheck, lint, and test commands when those scripts exist in TOOLING. Include build only if the change could affect the build output.
|
|
243
|
+
- Python / Go / Rust / other source changes → MUST include the language's standard verification from TOOLING (e.g. \`pytest\`, \`go test ./...\`, \`cargo test\`) plus lint/typecheck if configured.
|
|
244
|
+
- Config / infra-only changes with no executable verification → state that explicitly with a single command that re-reads or validates the config (e.g. \`docker compose config\`, \`nginx -t\`, \`yamllint file.yml\`). Never leave VERIFY with only \`true\` or \`echo ok\`.
|
|
245
|
+
|
|
246
|
+
If TOOLING is empty for a category the change clearly touches, still include the best-effort standard command for that ecosystem (e.g. \`npx tsc --noEmit\` for a TS repo with no script) and note that the receiving agent may need to install it.
|
|
247
|
+
|
|
248
|
+
Refined task:
|
|
249
|
+
${refined}
|
|
250
|
+
|
|
251
|
+
Research:
|
|
252
|
+
${research}
|
|
253
|
+
|
|
254
|
+
User Q&A:
|
|
255
|
+
${qa}`;
|
|
256
|
+
// Fast triage pass run before the (expensive) full rewrite. It produces either
|
|
257
|
+
// the single token CLEAN — meaning the compose draft needs no rewrite — or a
|
|
258
|
+
// short defect list. When CLEAN, the orchestrator returns the draft unchanged
|
|
259
|
+
// and skips the rewrite entirely; otherwise the defects are fed into
|
|
260
|
+
// CRITIQUE_PROMPT as a focus list so the rewrite targets real problems instead
|
|
261
|
+
// of re-deriving them from scratch.
|
|
262
|
+
const CRITIQUE_TRIAGE_PROMPT = (spec, refined, qa) => `You are triaging an implementation spec for an AI coding agent. Decide whether it needs a rewrite. Do NOT rewrite it — only judge it.
|
|
263
|
+
|
|
264
|
+
The refined task and the user's Q&A below are GROUND TRUTH. Judge the spec against them. Look for SUBSTANTIVE defects only:
|
|
265
|
+
- ambiguity that would let the agent build the wrong thing
|
|
266
|
+
- acceptance criteria that are vague, unmeasurable, or missing
|
|
267
|
+
- a VERIFY block that is missing, unrunnable, full of placeholders, or does not exercise the surface the task touches
|
|
268
|
+
- scope drift: requirements, files, or deliverables not implied by the refined task or Q&A
|
|
269
|
+
- a dropped or weakened CONSTRAINT from the refined task
|
|
270
|
+
|
|
271
|
+
Do NOT flag cosmetic wording, style, or anything you would change only to "polish" prose. The bar is: would this defect change what the agent builds or whether the work can be verified?
|
|
272
|
+
|
|
273
|
+
Output format — read carefully:
|
|
274
|
+
- If the spec has NO substantive defects, output the single literal token CLEAN on its own line. Nothing else.
|
|
275
|
+
- Otherwise output a short plain list, one defect per line, naming the section and the problem (e.g. "ACCEPTANCE: criterion 3 is unmeasurable — 'works well' has no check"). No rewrite, no preamble, no fixed spec.
|
|
276
|
+
|
|
277
|
+
Refined task (ground truth):
|
|
278
|
+
${refined}
|
|
279
|
+
|
|
280
|
+
User Q&A (ground truth):
|
|
281
|
+
${qa}
|
|
282
|
+
|
|
283
|
+
Spec to triage:
|
|
284
|
+
${spec}`;
|
|
285
|
+
const CRITIQUE_PROMPT = (spec, refined, qa, addVerifyEmphasis, triageDefects = null) => `You are reviewing the implementation spec below for ambiguity, weak acceptance criteria, and missing or unrunnable VERIFY commands.
|
|
286
|
+
|
|
287
|
+
CRITICAL FORMAT RULES (read first):
|
|
288
|
+
- Output the rewritten spec as plain markdown. Do NOT wrap your entire output in a code block, shell fence, or heredoc. Do NOT prefix with \`\`\`sh / \`\`\`bash. Do NOT use \`cat << EOF > file\` patterns. Your response begins literally with "GOAL" on the first line.
|
|
289
|
+
- The ONLY fenced code block in your output is the one immediately following \`VERIFY:\` — and that fence must be \`\`\`sh.
|
|
290
|
+
- No separate critique section, no preamble, no trailing summary — just the rewritten spec.
|
|
291
|
+
|
|
292
|
+
SCOPE RULES (equally critical — do not break these):
|
|
293
|
+
- The refined task and the user's Q&A below are GROUND TRUTH. The rewritten spec must stay faithful to them.
|
|
294
|
+
- Do NOT introduce new requirements, deliverables, files, scripts, hooks, configs, or acceptance criteria that are not explicitly implied by the refined task or the Q&A.
|
|
295
|
+
- Do NOT broaden scope. If the refined task says "run X and report", do not turn it into "build a toolchain around X with hooks, docs, and reports".
|
|
296
|
+
- CONSTRAINTS from the refined task MUST be preserved in spirit. Do not silently drop or weaken them.
|
|
297
|
+
- If the spec below is malformed, empty, or wrapped in a heredoc, reconstruct it from the refined task and Q&A — not from your own invention.
|
|
298
|
+
- Your job is to tighten language, sharpen acceptance criteria, and ensure VERIFY is runnable. Not to redesign the task.
|
|
299
|
+
|
|
300
|
+
Rewrite the spec in the same four-section format (GOAL, CONSTRAINTS, ACCEPTANCE, VERIFY). Fix any issues you find within the scope rules above.
|
|
301
|
+
|
|
302
|
+
VERIFY QUALITY CHECK (apply during the rewrite):
|
|
303
|
+
- VERIFY must exercise the surface the task touches, using tools the repo actually has (see research notes).
|
|
304
|
+
- Frontend / HTML / CSS / UI tasks → must include a browser-driving step (playwright if available; otherwise a dev-server + curl smoke test). Reject bare "open browser and check" instructions.
|
|
305
|
+
- Dockerfile / compose tasks → must include a real \`docker build\` (or \`docker compose build\`) and, where feasible, a container smoke run.
|
|
306
|
+
- Source-code tasks → must include the project's typecheck, lint, and tests when those exist. Do not drop them to "simplify".
|
|
307
|
+
- If the existing VERIFY is missing or too thin for the change being described, expand it using commands consistent with the research notes. Do not invent tooling that isn't present.
|
|
308
|
+
- Never accept \`true\`, \`echo ok\`, or other no-op commands as VERIFY content.
|
|
309
|
+
|
|
310
|
+
${addVerifyEmphasis ? 'REQUIRED: The output MUST include a VERIFY: section followed by a ```sh fenced block of runnable shell commands. The previous attempt was missing this.' : ''}
|
|
311
|
+
${triageDefects ?
|
|
312
|
+
`FOCUS — a triage pass already found these specific defects. Fix every one of them in your rewrite (without breaking the scope rules above):\n${triageDefects}\n`
|
|
313
|
+
: ''}
|
|
314
|
+
Refined task (ground truth):
|
|
315
|
+
${refined}
|
|
316
|
+
|
|
317
|
+
User Q&A (ground truth):
|
|
318
|
+
${qa}
|
|
319
|
+
|
|
320
|
+
Spec to rewrite:
|
|
321
|
+
${spec}`;
|
|
322
|
+
const VERIFY_TOOLING_PROMPT = (tooling) => `You receive a TOOLING list of candidate verification commands for an AI coding task.
|
|
323
|
+
|
|
324
|
+
YOU MAY ONLY READ. Do NOT execute any of the listed commands, not even with --help or --dry-run. Use ls/cat/grep/find/which/command -v (the BUILTIN command -v, NOT executing the candidate binary) to inspect static evidence:
|
|
325
|
+
- package.json scripts
|
|
326
|
+
- Makefile targets
|
|
327
|
+
- the presence of config files (tsconfig.json, playwright.config.*, .eslintrc*, etc.)
|
|
328
|
+
- binaries inside node_modules/.bin/
|
|
329
|
+
- system binaries in PATH (via command -v)
|
|
330
|
+
|
|
331
|
+
Output exactly two sections:
|
|
332
|
+
|
|
333
|
+
VERIFIED
|
|
334
|
+
<command> <one-line evidence: where it was found>
|
|
335
|
+
...
|
|
336
|
+
|
|
337
|
+
REJECTED
|
|
338
|
+
<command> <one-line reason it can't be confirmed>
|
|
339
|
+
...
|
|
340
|
+
|
|
341
|
+
Do not add other sections, preamble, or commentary.
|
|
342
|
+
|
|
343
|
+
TOOLING (one command per line):
|
|
344
|
+
${tooling}`;
|
|
345
|
+
// ─── Exports ─────────────────────────────────────────────────────────────────
|
|
346
|
+
export { REFINE_PROMPT, RESEARCH_FILES_PROMPT, RESEARCH_APIS_PROMPT, RESEARCH_CONTEXT_PROMPT, RESEARCH_TOOLING_PROMPT, RESEARCH_READ_ONLY_CONSTRAINT, GRILL_GEN_PROMPT, GRILL_AUTO_ANSWER_PROMPT, COMPOSE_PROMPT, CRITIQUE_PROMPT, CRITIQUE_TRIAGE_PROMPT, VERIFY_TOOLING_PROMPT, composeRetryEmphasis };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function formatServiceBlock(name, fullQuery, results) {
|
|
2
|
+
const header = `### service: ${name}\nQuery: ${fullQuery}`;
|
|
3
|
+
if (results.length === 0)
|
|
4
|
+
return header;
|
|
5
|
+
const bullets = results.map(r => `- **${r.title}** — ${r.url}\n ${r.description}`).join('\n');
|
|
6
|
+
return `${header}\n${bullets}`;
|
|
7
|
+
}
|
|
8
|
+
export function formatFreshnessSkippedBlock(names) {
|
|
9
|
+
return `### freshness-check skipped\nCould not verify external services (BRAVE_SEARCH_API_KEY not set):\n${names.map(n => `- ${n}`).join('\n')}`;
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task file — barrel re-export for backward compatibility.
|
|
3
|
+
*
|
|
4
|
+
* All existing import sites continue to work unchanged.
|
|
5
|
+
*
|
|
6
|
+
* @deprecated Import from the specific modules:
|
|
7
|
+
* - Types & constants: task-types.ts
|
|
8
|
+
* - Parsing & formatting: task-parsers.ts
|
|
9
|
+
* - File I/O: task-io.ts
|
|
10
|
+
*/
|
|
11
|
+
export type { TaskState, PhaseName, TaskFrontMatter } from './task-types.js';
|
|
12
|
+
export { PHASE_ORDER, PHASE_INDEX, TASKS_DIR_NAME, RESUMABLE_STATES } from './task-types.js';
|
|
13
|
+
export { emitFrontMatter, parseFrontMatter, sectionRegex, extractSection, normaliseTaskId } from './task-parsers.js';
|
|
14
|
+
export { tasksDir, taskFilePath, ensureTasksDir, allocateTaskId, readTaskFile, writeTaskFile, updateTaskFrontMatter, readSection, setTaskSection } from './task-io.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task file — barrel re-export for backward compatibility.
|
|
3
|
+
*
|
|
4
|
+
* All existing import sites continue to work unchanged.
|
|
5
|
+
*
|
|
6
|
+
* @deprecated Import from the specific modules:
|
|
7
|
+
* - Types & constants: task-types.ts
|
|
8
|
+
* - Parsing & formatting: task-parsers.ts
|
|
9
|
+
* - File I/O: task-io.ts
|
|
10
|
+
*/
|
|
11
|
+
export { PHASE_ORDER, PHASE_INDEX, TASKS_DIR_NAME, RESUMABLE_STATES } from './task-types.js';
|
|
12
|
+
// Parsing & formatting
|
|
13
|
+
export { emitFrontMatter, parseFrontMatter, sectionRegex, extractSection, normaliseTaskId } from './task-parsers.js';
|
|
14
|
+
// File I/O
|
|
15
|
+
export { tasksDir, taskFilePath, ensureTasksDir, allocateTaskId, readTaskFile, writeTaskFile, updateTaskFrontMatter, readSection, setTaskSection } from './task-io.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task file I/O.
|
|
3
|
+
*
|
|
4
|
+
* File read/write operations for the .pi-tasks directory. Depends on
|
|
5
|
+
* task-types.ts (types, constants) and task-parsers.ts (parsing/formatting).
|
|
6
|
+
*/
|
|
7
|
+
import { type TaskFrontMatter } from './task-types.js';
|
|
8
|
+
export declare function tasksDir(cwd: string): string;
|
|
9
|
+
export declare function taskFilePath(cwd: string, id: string): string;
|
|
10
|
+
export declare function ensureTasksDir(cwd: string): Promise<void>;
|
|
11
|
+
export declare function allocateTaskId(cwd: string): Promise<string>;
|
|
12
|
+
export declare function readTaskFile(cwd: string, id: string): Promise<{
|
|
13
|
+
frontMatter: TaskFrontMatter;
|
|
14
|
+
body: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function writeTaskFile(cwd: string, fm: TaskFrontMatter, body: string): Promise<void>;
|
|
17
|
+
export declare function updateTaskFrontMatter(cwd: string, id: string, patch: Partial<TaskFrontMatter>): Promise<void>;
|
|
18
|
+
export declare function readSection(cwd: string, id: string, heading: string): Promise<string | null>;
|
|
19
|
+
export declare function setTaskSection(cwd: string, id: string, heading: string, content: string): Promise<void>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task file I/O.
|
|
3
|
+
*
|
|
4
|
+
* File read/write operations for the .pi-tasks directory. Depends on
|
|
5
|
+
* task-types.ts (types, constants) and task-parsers.ts (parsing/formatting).
|
|
6
|
+
*/
|
|
7
|
+
import * as fsp from 'node:fs/promises';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { TASKS_DIR_NAME } from './task-types.js';
|
|
10
|
+
import { emitFrontMatter, parseFrontMatter, sectionRegex } from './task-parsers.js';
|
|
11
|
+
// ─── Directory & path helpers ────────────────────────────────────────────────
|
|
12
|
+
export function tasksDir(cwd) {
|
|
13
|
+
return path.join(cwd, TASKS_DIR_NAME);
|
|
14
|
+
}
|
|
15
|
+
export function taskFilePath(cwd, id) {
|
|
16
|
+
return path.join(tasksDir(cwd), `${id}.md`);
|
|
17
|
+
}
|
|
18
|
+
export async function ensureTasksDir(cwd) {
|
|
19
|
+
await fsp.mkdir(tasksDir(cwd), { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
export async function allocateTaskId(cwd) {
|
|
22
|
+
await ensureTasksDir(cwd);
|
|
23
|
+
const entries = await fsp.readdir(tasksDir(cwd));
|
|
24
|
+
let max = 0;
|
|
25
|
+
for (const e of entries) {
|
|
26
|
+
const m = /^TASK_(\d{4,})\.md$/.exec(e);
|
|
27
|
+
if (m) {
|
|
28
|
+
const n = parseInt(m[1], 10);
|
|
29
|
+
if (n > max)
|
|
30
|
+
max = n;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return `TASK_${String(max + 1).padStart(4, '0')}`;
|
|
34
|
+
}
|
|
35
|
+
// ─── File read/write ─────────────────────────────────────────────────────────
|
|
36
|
+
export async function readTaskFile(cwd, id) {
|
|
37
|
+
const raw = await fsp.readFile(taskFilePath(cwd, id), 'utf8');
|
|
38
|
+
const fm = parseFrontMatter(raw);
|
|
39
|
+
if (!fm)
|
|
40
|
+
throw new Error(`malformed front matter in ${id}.md`);
|
|
41
|
+
const body = raw.replace(/^---\n[\s\S]*?\n---\n?/, '');
|
|
42
|
+
return { frontMatter: fm, body };
|
|
43
|
+
}
|
|
44
|
+
export async function writeTaskFile(cwd, fm, body) {
|
|
45
|
+
await ensureTasksDir(cwd);
|
|
46
|
+
const content = `${emitFrontMatter(fm)}\n${body}`;
|
|
47
|
+
await fsp.writeFile(taskFilePath(cwd, fm.id), content, 'utf8');
|
|
48
|
+
}
|
|
49
|
+
export async function updateTaskFrontMatter(cwd, id, patch) {
|
|
50
|
+
const { frontMatter, body } = await readTaskFile(cwd, id);
|
|
51
|
+
const next = {
|
|
52
|
+
...frontMatter,
|
|
53
|
+
...patch,
|
|
54
|
+
updated_at: new Date().toISOString()
|
|
55
|
+
};
|
|
56
|
+
await writeTaskFile(cwd, next, body);
|
|
57
|
+
}
|
|
58
|
+
// ─── Section read/write (append if absent, rewrite if present) ───────────────
|
|
59
|
+
export async function readSection(cwd, id, heading) {
|
|
60
|
+
const { body } = await readTaskFile(cwd, id);
|
|
61
|
+
const m = sectionRegex(heading).exec(body);
|
|
62
|
+
return m ? m[2].trim() : null;
|
|
63
|
+
}
|
|
64
|
+
export async function setTaskSection(cwd, id, heading, content) {
|
|
65
|
+
const { frontMatter, body } = await readTaskFile(cwd, id);
|
|
66
|
+
const re = sectionRegex(heading);
|
|
67
|
+
let next;
|
|
68
|
+
if (re.test(body)) {
|
|
69
|
+
next = body.replace(re, `$1\n${content.trim()}\n\n`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const sep = body.endsWith('\n\n') ? ''
|
|
73
|
+
: body.endsWith('\n') ? '\n'
|
|
74
|
+
: '\n\n';
|
|
75
|
+
next = `${body}${sep}## ${heading}\n\n${content.trim()}\n`;
|
|
76
|
+
}
|
|
77
|
+
await writeTaskFile(cwd, { ...frontMatter, updated_at: new Date().toISOString() }, next);
|
|
78
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task file parsing and formatting.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for parsing YAML front matter, extracting sections, and
|
|
5
|
+
* normalising task IDs. No I/O.
|
|
6
|
+
*/
|
|
7
|
+
import { type TaskFrontMatter } from './task-types.js';
|
|
8
|
+
export declare function emitFrontMatter(fm: TaskFrontMatter): string;
|
|
9
|
+
export declare function parseFrontMatter(content: string): TaskFrontMatter | null;
|
|
10
|
+
export declare function sectionRegex(heading: string): RegExp;
|
|
11
|
+
export declare function extractSection(body: string, heading: string): string | null;
|
|
12
|
+
export declare function normaliseTaskId(input: string): string;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task file parsing and formatting.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for parsing YAML front matter, extracting sections, and
|
|
5
|
+
* normalising task IDs. No I/O.
|
|
6
|
+
*/
|
|
7
|
+
import { PHASE_INDEX } from './task-types.js';
|
|
8
|
+
const FRONT_MATTER_KEYS = [
|
|
9
|
+
'id',
|
|
10
|
+
'state',
|
|
11
|
+
'phase',
|
|
12
|
+
'created_at',
|
|
13
|
+
'updated_at',
|
|
14
|
+
'title',
|
|
15
|
+
'reason'
|
|
16
|
+
];
|
|
17
|
+
// ─── Front matter ────────────────────────────────────────────────────────────
|
|
18
|
+
export function emitFrontMatter(fm) {
|
|
19
|
+
const lines = ['---'];
|
|
20
|
+
for (const k of FRONT_MATTER_KEYS) {
|
|
21
|
+
const v = fm[k];
|
|
22
|
+
if (v === undefined || v === '') {
|
|
23
|
+
if (k === 'reason')
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
lines.push(`${k}: ${typeof v === 'string' ? v : String(v)}`);
|
|
27
|
+
}
|
|
28
|
+
lines.push('---');
|
|
29
|
+
return lines.join('\n');
|
|
30
|
+
}
|
|
31
|
+
export function parseFrontMatter(content) {
|
|
32
|
+
const m = /^---\n([\s\S]*?)\n---\n?/.exec(content);
|
|
33
|
+
if (!m)
|
|
34
|
+
return null;
|
|
35
|
+
const obj = {};
|
|
36
|
+
for (const line of m[1].split('\n')) {
|
|
37
|
+
const kv = /^([a-z_]+):\s*(.*)$/.exec(line);
|
|
38
|
+
if (kv)
|
|
39
|
+
obj[kv[1]] = kv[2];
|
|
40
|
+
}
|
|
41
|
+
if (!obj.id || !obj.state || !obj.phase || !obj.created_at)
|
|
42
|
+
return null;
|
|
43
|
+
if (PHASE_INDEX[obj.phase] === undefined) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
id: obj.id,
|
|
48
|
+
state: obj.state,
|
|
49
|
+
phase: obj.phase,
|
|
50
|
+
created_at: obj.created_at,
|
|
51
|
+
updated_at: obj.updated_at ?? obj.created_at,
|
|
52
|
+
title: obj.title ?? '',
|
|
53
|
+
reason: obj.reason || undefined
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// ─── Section helpers ─────────────────────────────────────────────────────────
|
|
57
|
+
function escapeRegex(s) {
|
|
58
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
59
|
+
}
|
|
60
|
+
export function sectionRegex(heading) {
|
|
61
|
+
return new RegExp(`(^## ${escapeRegex(heading)}\\s*\\n)([\\s\\S]*?)(?=^## |$(?![\\s\\S]))`, 'm');
|
|
62
|
+
}
|
|
63
|
+
export function extractSection(body, heading) {
|
|
64
|
+
const m = sectionRegex(heading).exec(body);
|
|
65
|
+
return m ? m[2].trim() : null;
|
|
66
|
+
}
|
|
67
|
+
// ─── Task ID helpers ─────────────────────────────────────────────────────────
|
|
68
|
+
export function normaliseTaskId(input) {
|
|
69
|
+
const trimmed = input.trim();
|
|
70
|
+
if (/^TASK_\d{4,}$/.test(trimmed))
|
|
71
|
+
return trimmed;
|
|
72
|
+
if (/^\d+$/.test(trimmed))
|
|
73
|
+
return `TASK_${trimmed.padStart(4, '0')}`;
|
|
74
|
+
return trimmed;
|
|
75
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task file types and constants.
|
|
3
|
+
*
|
|
4
|
+
* Declares the core types used across the pi-task pipeline: task states,
|
|
5
|
+
* phase names, front matter, and ordering constants.
|
|
6
|
+
*/
|
|
7
|
+
export type TaskState = 'pending' | 'in_progress' | 'completed' | 'failed' | 'cancelled';
|
|
8
|
+
export type PhaseName = 'refine' | 'research' | 'grill' | 'compose' | 'critique' | 'done';
|
|
9
|
+
export interface TaskFrontMatter {
|
|
10
|
+
id: string;
|
|
11
|
+
state: TaskState;
|
|
12
|
+
phase: PhaseName;
|
|
13
|
+
created_at: string;
|
|
14
|
+
updated_at: string;
|
|
15
|
+
title: string;
|
|
16
|
+
reason?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const PHASE_ORDER: PhaseName[];
|
|
19
|
+
export declare const PHASE_INDEX: Record<PhaseName, number>;
|
|
20
|
+
export declare const TASKS_DIR_NAME = ".pi-tasks";
|
|
21
|
+
export declare const RESUMABLE_STATES: TaskState[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task file types and constants.
|
|
3
|
+
*
|
|
4
|
+
* Declares the core types used across the pi-task pipeline: task states,
|
|
5
|
+
* phase names, front matter, and ordering constants.
|
|
6
|
+
*/
|
|
7
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
8
|
+
export const PHASE_ORDER = ['refine', 'research', 'grill', 'compose', 'critique'];
|
|
9
|
+
export const PHASE_INDEX = {
|
|
10
|
+
refine: 0,
|
|
11
|
+
research: 1,
|
|
12
|
+
grill: 2,
|
|
13
|
+
compose: 3,
|
|
14
|
+
critique: 4,
|
|
15
|
+
done: 5
|
|
16
|
+
};
|
|
17
|
+
export const TASKS_DIR_NAME = '.pi-tasks';
|
|
18
|
+
export const RESUMABLE_STATES = ['in_progress', 'pending', 'cancelled', 'failed'];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase timing data — captures how long each pipeline phase took so we can
|
|
3
|
+
* spot regressions and target future speed improvements.
|
|
4
|
+
*
|
|
5
|
+
* Top-level entries are the five phases (refine, research, grill, compose,
|
|
6
|
+
* critique). Each phase may attach optional sub-step children (e.g. research
|
|
7
|
+
* → workers + verify-tooling, grill → gen + auto-answers + user input).
|
|
8
|
+
*
|
|
9
|
+
* `formatTimings` produces the human-readable block we write to the
|
|
10
|
+
* `## phase timings` section of the TASK_NNNN.md file.
|
|
11
|
+
*/
|
|
12
|
+
export interface TimingEntry {
|
|
13
|
+
label: string;
|
|
14
|
+
ms: number;
|
|
15
|
+
children: TimingEntry[];
|
|
16
|
+
}
|
|
17
|
+
export declare function formatMs(ms: number): string;
|
|
18
|
+
export declare function formatTimings(entries: ReadonlyArray<TimingEntry>): string;
|