@davidorex/pi-workflows 0.6.1 → 0.9.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.
Files changed (48) hide show
  1. package/README.md +1 -1
  2. package/agents/audit-finding-verifier.agent.yaml +40 -0
  3. package/agents/audit-results-router.agent.yaml +30 -0
  4. package/agents/gap-resolution-assessor.agent.yaml +40 -0
  5. package/dist/expression.d.ts.map +1 -1
  6. package/dist/expression.js +6 -0
  7. package/dist/expression.js.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/step-agent.js +1 -1
  11. package/dist/step-agent.js.map +1 -1
  12. package/dist/step-block.d.ts +18 -0
  13. package/dist/step-block.d.ts.map +1 -0
  14. package/dist/step-block.js +204 -0
  15. package/dist/step-block.js.map +1 -0
  16. package/dist/template-validation.d.ts +25 -0
  17. package/dist/template-validation.d.ts.map +1 -0
  18. package/dist/template-validation.js +341 -0
  19. package/dist/template-validation.js.map +1 -0
  20. package/dist/types.d.ts +31 -0
  21. package/dist/types.d.ts.map +1 -1
  22. package/dist/workflow-executor.d.ts.map +1 -1
  23. package/dist/workflow-executor.js +31 -5
  24. package/dist/workflow-executor.js.map +1 -1
  25. package/dist/workflow-sdk.d.ts +1 -0
  26. package/dist/workflow-sdk.d.ts.map +1 -1
  27. package/dist/workflow-sdk.js +7 -0
  28. package/dist/workflow-sdk.js.map +1 -1
  29. package/dist/workflow-spec.d.ts.map +1 -1
  30. package/dist/workflow-spec.js +106 -0
  31. package/dist/workflow-spec.js.map +1 -1
  32. package/package.json +7 -8
  33. package/schemas/audit-routing-manifest.schema.json +38 -0
  34. package/schemas/finding-verification.schema.json +34 -0
  35. package/schemas/resolution-assessment.schema.json +36 -0
  36. package/skills/pi-workflows/SKILL.md +1 -1
  37. package/skills/pi-workflows/references/bundled-resources.md +12 -3
  38. package/templates/audit-finding-verifier/task.md +59 -0
  39. package/templates/audit-results-router/task.md +37 -0
  40. package/templates/gap-resolution-assessor/task.md +48 -0
  41. package/templates/handoff-writer/task.md +16 -6
  42. package/templates/phase-author/task.md +8 -0
  43. package/workflows/create-handoff.workflow.yaml +56 -35
  44. package/workflows/create-phase.workflow.yaml +27 -32
  45. package/workflows/do-gap.workflow.yaml +47 -27
  46. package/workflows/fix-audit.workflow.yaml +89 -127
  47. package/workflows/gap-to-phase.workflow.yaml +47 -42
  48. package/workflows/plan-from-requirements.workflow.yaml +16 -27
@@ -0,0 +1,36 @@
1
+ {
2
+ "type": "object",
3
+ "required": ["resolved", "verdict", "resolution_summary", "evidence"],
4
+ "properties": {
5
+ "resolved": {
6
+ "type": "boolean",
7
+ "description": "true if the implementation addresses the gap's described root cause"
8
+ },
9
+ "verdict": {
10
+ "type": "string",
11
+ "enum": ["resolved", "partially_resolved", "not_resolved", "checks_failed"],
12
+ "description": "Categorical verdict"
13
+ },
14
+ "resolution_summary": {
15
+ "type": "string",
16
+ "description": "What was done and why it addresses (or fails to address) the gap — references specific changes"
17
+ },
18
+ "evidence": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "object",
22
+ "required": ["claim", "source"],
23
+ "properties": {
24
+ "claim": { "type": "string" },
25
+ "source": { "type": "string", "description": "File path, diff, or test output supporting the claim" }
26
+ }
27
+ },
28
+ "description": "Observable evidence supporting the verdict"
29
+ },
30
+ "unresolved_aspects": {
31
+ "type": "array",
32
+ "items": { "type": "string" },
33
+ "description": "Specific aspects of the gap that remain unaddressed (empty if fully resolved)"
34
+ }
35
+ }
36
+ }
@@ -78,7 +78,7 @@ Subcommands: `init`, `list`, `run`, `resume`, `validate`, `status`, `help`
78
78
  </keyboard_shortcuts>
79
79
 
80
80
  <bundled_resources>
81
- 21 agents, 11 schemas, 14 workflows, 28 templates bundled.
81
+ 24 agents, 14 schemas, 14 workflows, 31 templates bundled.
82
82
  See references/bundled-resources.md for full inventory.
83
83
  </bundled_resources>
84
84
 
@@ -1,13 +1,16 @@
1
1
  # Bundled Resources
2
2
 
3
- ## agents/ (21 files)
3
+ ## agents/ (24 files)
4
4
 
5
5
  - `agents/architecture-designer.agent.yaml`
6
6
  - `agents/architecture-inferrer.agent.yaml`
7
+ - `agents/audit-finding-verifier.agent.yaml`
7
8
  - `agents/audit-fixer.agent.yaml`
9
+ - `agents/audit-results-router.agent.yaml`
8
10
  - `agents/code-explorer.agent.yaml`
9
11
  - `agents/decomposer.agent.yaml`
10
12
  - `agents/gap-identifier.agent.yaml`
13
+ - `agents/gap-resolution-assessor.agent.yaml`
11
14
  - `agents/handoff-writer.agent.yaml`
12
15
  - `agents/investigator.agent.yaml`
13
16
  - `agents/pattern-analyzer.agent.yaml`
@@ -24,16 +27,19 @@
24
27
  - `agents/synthesizer.agent.yaml`
25
28
  - `agents/verifier.agent.yaml`
26
29
 
27
- ## schemas/ (11 files)
30
+ ## schemas/ (14 files)
28
31
 
32
+ - `schemas/audit-routing-manifest.schema.json`
29
33
  - `schemas/decomposition-specs.schema.json`
30
34
  - `schemas/execution-results.schema.json`
35
+ - `schemas/finding-verification.schema.json`
31
36
  - `schemas/investigation-findings.schema.json`
32
37
  - `schemas/pattern-analysis.schema.json`
33
38
  - `schemas/phase.schema.json`
34
39
  - `schemas/plan-breakdown.schema.json`
35
40
  - `schemas/quality-analysis.schema.json`
36
41
  - `schemas/research-findings.schema.json`
42
+ - `schemas/resolution-assessment.schema.json`
37
43
  - `schemas/structure-analysis.schema.json`
38
44
  - `schemas/synthesis.schema.json`
39
45
  - `schemas/verifier-output.schema.json`
@@ -55,7 +61,7 @@
55
61
  - `workflows/self-implement.workflow.yaml`
56
62
  - `workflows/typed-analysis.workflow.yaml`
57
63
 
58
- ## templates/ (28 files)
64
+ ## templates/ (31 files)
59
65
 
60
66
  - `templates/analyzers/base-analyzer.md`
61
67
  - `templates/analyzers/macros.md`
@@ -67,11 +73,14 @@
67
73
  - `templates/analyzers/structure.md`
68
74
  - `templates/architecture-designer/task.md`
69
75
  - `templates/architecture-inferrer/task.md`
76
+ - `templates/audit-finding-verifier/task.md`
70
77
  - `templates/audit-fixer/task.md`
78
+ - `templates/audit-results-router/task.md`
71
79
  - `templates/decomposer/task.md`
72
80
  - `templates/explorer/system.md`
73
81
  - `templates/explorer/task.md`
74
82
  - `templates/gap-identifier/task.md`
83
+ - `templates/gap-resolution-assessor/task.md`
75
84
  - `templates/handoff-writer/task.md`
76
85
  - `templates/investigator/task.md`
77
86
  - `templates/phase-author/task.md`
@@ -0,0 +1,59 @@
1
+ Verify whether the following audit findings have been resolved.
2
+
3
+ ## Audit Findings
4
+
5
+ {% for finding in audit.findings %}
6
+ ### Finding: {{ finding.id }}
7
+
8
+ **Description**: {{ finding.description }}
9
+ **Severity**: {{ finding.severity | default("unspecified") }}
10
+ **Category**: {{ finding.category | default("unspecified") }}
11
+ **Principle**: {{ finding.principle | default("unspecified") }}
12
+
13
+ **Locations**:
14
+ {% for loc in finding.locations %}
15
+ - `{{ loc.file }}` {% if loc.line %}line {{ loc.line }}{% endif %} {% if loc.description %} — {{ loc.description }}{% endif %}
16
+ {% endfor %}
17
+
18
+ {% if finding.fix %}
19
+ **Fix suggestion**: {{ finding.fix.suggestion | default("none") }}
20
+ {% endif %}
21
+
22
+ {% if finding.resolution and finding.resolution.status == 'passed' %}
23
+ *Previously marked as resolved — re-verify.*
24
+ {% endif %}
25
+ ---
26
+ {% endfor %}
27
+
28
+ ## Implementation Results
29
+
30
+ {% if implementation_results is iterable and implementation_results is not string %}
31
+ {% for result in implementation_results %}
32
+ - {{ result.spec_name | default(result.name | default("task")) }}: {{ result.status | default("unknown") }}
33
+ {% endfor %}
34
+ {% else %}
35
+ ```json
36
+ {{ implementation_results | dump(2) }}
37
+ ```
38
+ {% endif %}
39
+
40
+ {% if conformance_reference %}
41
+ ## Conformance Reference
42
+
43
+ ```json
44
+ {{ conformance_reference | dump(2) }}
45
+ ```
46
+ {% endif %}
47
+
48
+ ## Instructions
49
+
50
+ For EACH finding above:
51
+
52
+ 1. Read the code at the referenced location(s)
53
+ 2. Determine if the finding's described issue is addressed
54
+ 3. Record your verdict: `passed`, `failed`, or `needs_inspect`
55
+ 4. Provide specific evidence (code snippet, file:line reference)
56
+
57
+ Do NOT use grep exit codes as evidence. Read the code and assess whether the issue described in the finding is genuinely resolved.
58
+
59
+ Produce JSON output with a `findings` array containing one entry per finding, plus summary statistics.
@@ -0,0 +1,37 @@
1
+ Review the audit fix results and produce a routing manifest.
2
+
3
+ ## Implementation Results
4
+
5
+ {% if implementation_results is iterable and implementation_results is not string %}
6
+ {% for result in implementation_results %}
7
+ ### Task: {{ result.spec_name | default(result.name | default("unnamed")) }}
8
+
9
+ - Status: {{ result.status | default("unknown") }}
10
+ {% if result.decisions %}
11
+ - Decisions: {{ result.decisions | length }}
12
+ {% endif %}
13
+ {% if result.issues %}
14
+ - Issues flagged: {{ result.issues | length }}
15
+ {% endif %}
16
+ {% endfor %}
17
+ {% else %}
18
+ ```json
19
+ {{ implementation_results | dump(2) }}
20
+ ```
21
+ {% endif %}
22
+
23
+ ## Verification Results
24
+
25
+ ```json
26
+ {{ verification | dump(2) }}
27
+ ```
28
+
29
+ ## Instructions
30
+
31
+ Produce a routing manifest with:
32
+
33
+ 1. **decisions** — validated decisions from implementation results (with complete id, description, rationale fields)
34
+ 2. **new_gaps** — genuine issues that should be tracked (with stable id, description, status: "open", category, priority)
35
+ 3. **summary** — accurate summary of what was completed, what failed, what needs inspection
36
+
37
+ Only include items that are well-formed and represent genuine project artifacts. Do not route items that are missing required fields or describe hypothetical concerns.
@@ -0,0 +1,48 @@
1
+ Assess whether the implementation resolves this gap.
2
+
3
+ ## Gap
4
+
5
+ **ID**: {{ gap.id }}
6
+ **Description**: {{ gap.description }}
7
+ **Category**: {{ gap.category | default("unspecified") }}
8
+ **Priority**: {{ gap.priority | default("unspecified") }}
9
+
10
+ ## Investigation Findings
11
+
12
+ ```json
13
+ {{ investigation | dump(2) }}
14
+ ```
15
+
16
+ ## Implementation Results
17
+
18
+ {% if implementation_results is iterable and implementation_results is not string %}
19
+ {% for result in implementation_results %}
20
+ ### Spec: {{ result.spec_name | default("unnamed") }}
21
+ - Status: {{ result.status | default("unknown") }}
22
+ - Files changed: {{ result.files_changed | default([]) | join(", ") }}
23
+ {% if result.commit_hash %}- Commit: {{ result.commit_hash }}{% endif %}
24
+ {% endfor %}
25
+ {% else %}
26
+ ```json
27
+ {{ implementation_results | dump(2) }}
28
+ ```
29
+ {% endif %}
30
+
31
+ ## Check Results
32
+
33
+ - Status: {{ check_results.status }}
34
+ {% if check_results.errors | length > 0 %}
35
+ - Errors:
36
+ {% for err in check_results.errors %}
37
+ - {{ err }}
38
+ {% endfor %}
39
+ {% endif %}
40
+
41
+ ## Instructions
42
+
43
+ 1. Read the gap description above. Understand what root cause it identifies.
44
+ 2. Examine the implementation results — what files were changed and what was the stated work.
45
+ 3. Run `git diff HEAD~{{ implementation_results | length | default(1) }}` (or appropriate range) to see actual code changes.
46
+ 4. Judge: do the code changes address the root cause described in the gap?
47
+ 5. If tests failed or lint errors exist, the gap is NOT resolved regardless of code quality.
48
+ 6. Produce your assessment as JSON.
@@ -2,27 +2,37 @@ Capture a handoff snapshot for the project at `{{ path }}`.
2
2
 
3
3
  ## Current Project State
4
4
 
5
- {% if project_state.project is defined %}
5
+ {% if project_state.blocks_status %}
6
+ ### Block Availability
7
+
8
+ | Block | Available |
9
+ |-------|-----------|
10
+ {% for name, available in project_state.blocks_status %}
11
+ | {{ name }} | {{ "yes" if available else "NO — absent" }} |
12
+ {% endfor %}
13
+ {% endif %}
14
+
15
+ {% if project_state.project %}
6
16
  ### Project
7
17
  **{{ project_state.project.name | default("unnamed") }}** — {{ project_state.project.description | default("no description") }}
8
18
  Status: {{ project_state.project.status | default("unknown") }}
9
19
  {% endif %}
10
20
 
11
- {% if project_state.phases is defined and project_state.phases | length > 0 %}
21
+ {% if project_state.phases and project_state.phases | length > 0 %}
12
22
  ### Phases
13
23
  {% for phase in project_state.phases %}
14
24
  - Phase {{ phase.number | default(loop.index) }}: {{ phase.name | default("unnamed") }} ({{ phase.status | default("unknown") }})
15
25
  {% endfor %}
16
26
  {% endif %}
17
27
 
18
- {% if project_state.blockSummaries is defined %}
28
+ {% if project_state.blockSummaries %}
19
29
  ### Block Summaries
20
30
  {% for block in project_state.blockSummaries %}
21
31
  - **{{ block.name }}**: {{ block.count | default("?") }} entries
22
32
  {% endfor %}
23
33
  {% endif %}
24
34
 
25
- {% if project_state.gaps is defined %}
35
+ {% if project_state.gaps %}
26
36
  ### Open Gaps
27
37
  {% for gap in project_state.gaps %}
28
38
  {% if gap.status == "open" %}
@@ -31,14 +41,14 @@ Status: {{ project_state.project.status | default("unknown") }}
31
41
  {% endfor %}
32
42
  {% endif %}
33
43
 
34
- {% if project_state.decisions is defined %}
44
+ {% if project_state.decisions %}
35
45
  ### Recent Decisions
36
46
  {% for decision in project_state.decisions %}
37
47
  - {{ decision.id | default("?") }}: {{ decision.description | default(decision.title | default("no description")) }}
38
48
  {% endfor %}
39
49
  {% endif %}
40
50
 
41
- {% if project_state.recentCommits is defined %}
51
+ {% if project_state.recentCommits %}
42
52
  ### Recent Commits
43
53
  {% for commit in project_state.recentCommits %}
44
54
  - `{{ commit.sha | default("?") | truncate(7, true, "") }}` {{ commit.message | default("no message") }}
@@ -8,6 +8,7 @@
8
8
  - Phase {{ phase.number }}: {{ phase.name }} ({{ phase.status }})
9
9
  {% endfor %}
10
10
 
11
+ {% if architecture and architecture.modules %}
11
12
  ## Current Architecture
12
13
 
13
14
  {% for module in architecture.modules %}
@@ -20,13 +21,17 @@
20
21
  {{ loop.index }}. **{{ stage.stage }}**: {{ stage.description }}
21
22
  {% endfor %}
22
23
  {% endif %}
24
+ {% endif %}
23
25
 
26
+ {% if conventions and conventions.rules %}
24
27
  ## Conventions
25
28
 
26
29
  {% for rule in conventions.rules %}
27
30
  - {{ rule.id }}: {{ rule.description }} ({{ rule.enforcement }})
28
31
  {% endfor %}
32
+ {% endif %}
29
33
 
34
+ {% if gaps %}
30
35
  ## Current Gaps
31
36
 
32
37
  {% for gap in gaps %}
@@ -34,13 +39,16 @@
34
39
  - [{{ gap.priority }}] {{ gap.id }}: {{ gap.description }}
35
40
  {% endif %}
36
41
  {% endfor %}
42
+ {% endif %}
37
43
 
44
+ {% if inventory %}
38
45
  ## Current Inventory
39
46
 
40
47
  - Step types: {{ inventory.step_types | length }}
41
48
  - Agent specs: {{ inventory.agent_specs | length }}
42
49
  - Schemas: {{ inventory.schemas | length }}
43
50
  - Tests: {{ inventory.test_count }}
51
+ {% endif %}
44
52
 
45
53
  ## Instructions
46
54
 
@@ -3,52 +3,73 @@ description: Capture current work context for session resume
3
3
  version: "1"
4
4
 
5
5
  steps:
6
- load-state:
7
- command: |
8
- node -e "
9
- const fs = require('fs');
10
- const path = require('path');
11
- const projectDir = '.project';
12
- const state = {};
13
-
14
- // Read project identity
15
- try { state.project = JSON.parse(fs.readFileSync(path.join(projectDir, 'project.json'), 'utf-8')); } catch {}
6
+ load-blocks:
7
+ block:
8
+ read: [project, gaps, decisions, tasks]
9
+ optional: [project, gaps, decisions, tasks]
10
+ output:
11
+ format: json
16
12
 
17
- // Read phase files
18
- try {
19
- const phasesDir = path.join(projectDir, 'phases');
20
- state.phases = fs.readdirSync(phasesDir).filter(f => f.endsWith('.json')).map(f => {
21
- try { return JSON.parse(fs.readFileSync(path.join(phasesDir, f), 'utf-8')); } catch { return null; }
22
- }).filter(Boolean);
23
- } catch { state.phases = []; }
13
+ load-phases:
14
+ block:
15
+ readDir: phases
16
+ output:
17
+ format: json
24
18
 
25
- // Read block summaries
26
- try {
27
- state.gaps = JSON.parse(fs.readFileSync(path.join(projectDir, 'gaps.json'), 'utf-8'));
28
- } catch {}
29
- try {
30
- state.decisions = JSON.parse(fs.readFileSync(path.join(projectDir, 'decisions.json'), 'utf-8'));
31
- } catch {}
19
+ load-git:
20
+ command: |
21
+ node -e "
22
+ const { execSync } = require('child_process');
23
+ const git = {};
32
24
  try {
33
- state.tasks = JSON.parse(fs.readFileSync(path.join(projectDir, 'tasks.json'), 'utf-8'));
34
- } catch {}
35
-
36
- // Recent git activity
25
+ git.recent_commits = execSync('git log --oneline -10', { encoding: 'utf-8' }).trim().split('\n');
26
+ } catch (e) {
27
+ git.recent_commits = [];
28
+ git.commits_error = e.message || 'git log failed';
29
+ }
37
30
  try {
38
- const { execSync } = require('child_process');
39
- state.recent_commits = execSync('git log --oneline -10', { encoding: 'utf-8' }).trim().split('\n');
40
- state.changed_files = execSync('git diff --name-only HEAD~3 2>/dev/null || echo \"\"', { encoding: 'utf-8' }).trim().split('\n').filter(Boolean);
41
- } catch { state.recent_commits = []; state.changed_files = []; }
42
-
43
- console.log(JSON.stringify(state));
31
+ git.changed_files = execSync('git diff --name-only HEAD~3 2>/dev/null || echo \"\"', { encoding: 'utf-8' }).trim().split('\n').filter(Boolean);
32
+ } catch (e) {
33
+ git.changed_files = [];
34
+ git.files_error = e.message || 'git diff failed';
35
+ }
36
+ console.log(JSON.stringify(git));
44
37
  "
45
38
  output:
46
39
  format: json
47
40
 
41
+ assemble-state:
42
+ command: |
43
+ node -e "
44
+ const blocks = JSON.parse(process.argv[1]);
45
+ const phases = JSON.parse(process.argv[2]);
46
+ const git = JSON.parse(process.argv[3]);
47
+ console.log(JSON.stringify({
48
+ project: blocks.project,
49
+ phases: phases,
50
+ gaps: blocks.gaps,
51
+ decisions: blocks.decisions,
52
+ tasks: blocks.tasks,
53
+ recentCommits: git.recent_commits || [],
54
+ changedFiles: git.changed_files || [],
55
+ blocks_status: {
56
+ project: blocks.project != null,
57
+ gaps: blocks.gaps != null,
58
+ decisions: blocks.decisions != null,
59
+ tasks: blocks.tasks != null,
60
+ phases: phases.length > 0,
61
+ git_commits: !git.commits_error,
62
+ git_files: !git.files_error
63
+ }
64
+ }));
65
+ " '${{ steps.load-blocks.output | json }}' '${{ steps.load-phases.output | json }}' '${{ steps.load-git.output | json }}'
66
+ output:
67
+ format: json
68
+
48
69
  capture:
49
70
  agent: handoff-writer
50
71
  input:
51
- project_state: ${{ steps.load-state.output }}
72
+ project_state: ${{ steps.assemble-state.output }}
52
73
  path: "."
53
74
  output:
54
75
  format: json
@@ -11,28 +11,16 @@ input:
11
11
  description: Unstructured intent from conversation — what needs to be built and why
12
12
 
13
13
  steps:
14
+ load-phases:
15
+ block:
16
+ readDir: phases
17
+ output:
18
+ format: json
19
+
14
20
  load-context:
15
- command: |
16
- node --experimental-strip-types -e "
17
- import fs from 'fs';
18
- import path from 'path';
19
- const phasesDir = '.project/phases';
20
- let phases = [];
21
- try {
22
- phases = fs.readdirSync(phasesDir)
23
- .filter(f => f.endsWith('.json'))
24
- .map(f => JSON.parse(fs.readFileSync(path.join(phasesDir, f), 'utf8')));
25
- } catch {}
26
- let arch = {};
27
- try { arch = JSON.parse(fs.readFileSync('.project/architecture.json', 'utf8')); } catch {}
28
- let conv = {};
29
- try { conv = JSON.parse(fs.readFileSync('.project/conventions.json', 'utf8')); } catch {}
30
- let gapsData = { gaps: [] };
31
- try { gapsData = JSON.parse(fs.readFileSync('.project/gaps.json', 'utf8')); } catch {}
32
- let inv = {};
33
- try { inv = JSON.parse(fs.readFileSync('.project/inventory.json', 'utf8')); } catch {}
34
- console.log(JSON.stringify({ phases, architecture: arch, conventions: conv, gaps: gapsData.gaps, inventory: inv }));
35
- "
21
+ block:
22
+ read: [architecture, conventions, gaps, inventory]
23
+ optional: [architecture, conventions, gaps, inventory]
36
24
  output:
37
25
  format: json
38
26
 
@@ -40,32 +28,39 @@ steps:
40
28
  agent: phase-author
41
29
  input:
42
30
  intent: "${{ input.intent }}"
43
- phases: "${{ steps.load-context.output.phases }}"
31
+ phases: "${{ steps.load-phases.output }}"
44
32
  architecture: "${{ steps.load-context.output.architecture }}"
45
33
  conventions: "${{ steps.load-context.output.conventions }}"
46
- gaps: "${{ steps.load-context.output.gaps }}"
34
+ gaps: "${{ steps.load-context.output.gaps.gaps }}"
47
35
  inventory: "${{ steps.load-context.output.inventory }}"
48
36
  output:
49
37
  format: json
50
38
  schema: block:phase
51
39
 
52
- write-phase:
40
+ compute-phase-path:
53
41
  command: |
54
- node --experimental-strip-types -e "
55
- import fs from 'fs';
42
+ node -e "
56
43
  const phase = JSON.parse(process.argv[1]);
57
- const filename = '.project/phases/' + String(phase.number).padStart(2, '0') + '-' + phase.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-+$/, '') + '.json';
58
- fs.mkdirSync('.project/phases', { recursive: true });
59
- fs.writeFileSync(filename, JSON.stringify(phase, null, 2) + '\n');
60
- console.log(JSON.stringify({ path: filename, phase_number: phase.number, spec_count: phase.specs.length }));
44
+ const num = String(phase.number).padStart(2, '0');
45
+ const name = phase.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-+$/g, '');
46
+ console.log(JSON.stringify({ path: 'phases/' + num + '-' + name }));
61
47
  " '${{ steps.author.output | json }}'
62
48
  output:
63
49
  format: json
64
50
 
51
+ write-phase:
52
+ block:
53
+ write:
54
+ name: phase
55
+ data: "${{ steps.author.output }}"
56
+ path: "${{ steps.compute-phase-path.output.path }}"
57
+ output:
58
+ format: json
59
+
65
60
  completion:
66
61
  message: |
67
- Phase ${{ steps.write-phase.output.phase_number }} created at ${{ steps.write-phase.output.path }}
68
- Contains ${{ steps.write-phase.output.spec_count }} specs.
62
+ Phase ${{ steps.author.output.number }} created at ${{ steps.write-phase.output.path }}
63
+ Contains ${{ steps.author.output.specs | length }} specs.
69
64
  include:
70
65
  - steps.author.output.success_criteria
71
66
  - steps.author.output.specs