@trailstep/create-flows 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/LICENSE +176 -0
- package/README.md +32 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +823 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
https://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# @trailstep/create-flows
|
|
2
|
+
|
|
3
|
+
`@trailstep/create-flows` is a public package of reusable, general-purpose TrailStep workflows.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @trailstep/create-flows @trailstep/authoring
|
|
9
|
+
pnpm add -D @trailstep/cli
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Workflows
|
|
13
|
+
|
|
14
|
+
- `takeItAway`: turns an existing conversation or feature request into reviewed planning and story-by-story delivery.
|
|
15
|
+
- `grillItAway`: starts with an interactive conversation and then runs the same implementation pipeline.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"conversation": "We need a workflow that exports widget reports..."
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
trailstep add @trailstep/create-flows#takeItAway
|
|
27
|
+
trailstep @trailstep/create-flows#takeItAway --input-file feature-request.json
|
|
28
|
+
trailstep @trailstep/create-flows#grillItAway
|
|
29
|
+
trailstep retry @trailstep/create-flows#takeItAway <runName>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Generated run artifacts live under `.trailstep/runs`; inspect them, but do not edit them to recover workflow state.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as _trailstep_authoring from '@trailstep/authoring';
|
|
2
|
+
|
|
3
|
+
interface TakeItAwayOutput extends Record<string, unknown> {
|
|
4
|
+
readonly status: "implemented";
|
|
5
|
+
readonly featureDocPath: string;
|
|
6
|
+
readonly implementationDocPath: string;
|
|
7
|
+
readonly storyCount: number;
|
|
8
|
+
readonly completedStories: readonly string[];
|
|
9
|
+
readonly summary: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare const grillItAway: _trailstep_authoring.DefinedWorkflow<Record<string, never>, TakeItAwayOutput>;
|
|
13
|
+
|
|
14
|
+
interface TakeItAwayInput extends Record<string, unknown> {
|
|
15
|
+
readonly conversation: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare const takeItAway: _trailstep_authoring.DefinedWorkflow<TakeItAwayInput, TakeItAwayOutput>;
|
|
19
|
+
|
|
20
|
+
export { grillItAway, takeItAway };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,823 @@
|
|
|
1
|
+
// src/grill-it-away/workflow.ts
|
|
2
|
+
import { defineWorkflow, shape as shape2 } from "@trailstep/authoring";
|
|
3
|
+
|
|
4
|
+
// src/feature-implementation/shared/output-schema.ts
|
|
5
|
+
import { jsonSchema } from "@trailstep/authoring";
|
|
6
|
+
var takeItAwayOutput = jsonSchema({
|
|
7
|
+
type: "object",
|
|
8
|
+
properties: {
|
|
9
|
+
status: { type: "string", const: "implemented" },
|
|
10
|
+
featureDocPath: { type: "string" },
|
|
11
|
+
implementationDocPath: { type: "string" },
|
|
12
|
+
storyCount: { type: "number" },
|
|
13
|
+
completedStories: { type: "array", items: { type: "string" } },
|
|
14
|
+
summary: { type: "string" }
|
|
15
|
+
},
|
|
16
|
+
required: [
|
|
17
|
+
"status",
|
|
18
|
+
"featureDocPath",
|
|
19
|
+
"implementationDocPath",
|
|
20
|
+
"storyCount",
|
|
21
|
+
"completedStories",
|
|
22
|
+
"summary"
|
|
23
|
+
],
|
|
24
|
+
additionalProperties: false
|
|
25
|
+
});
|
|
26
|
+
function extractStoryTitle(storyContent, fallbackIndex) {
|
|
27
|
+
const heading = /^#{1,6}\s+(.+)$/m.exec(storyContent);
|
|
28
|
+
return heading?.[1]?.trim() || `Story ${fallbackIndex}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/grill-it-away/grill/step.ts
|
|
32
|
+
import { step as step8 } from "@trailstep/authoring";
|
|
33
|
+
|
|
34
|
+
// src/feature-implementation/create-feature-doc/step.ts
|
|
35
|
+
import { Document as Document2, state as state5, step as step7 } from "@trailstep/authoring";
|
|
36
|
+
|
|
37
|
+
// src/feature-implementation/create-or-improve-implementation-doc/step.ts
|
|
38
|
+
import { Document, state as state4, step as step6 } from "@trailstep/authoring";
|
|
39
|
+
|
|
40
|
+
// src/feature-implementation/review-implementation-doc/step.ts
|
|
41
|
+
import { fail as fail5, step as step5 } from "@trailstep/authoring";
|
|
42
|
+
|
|
43
|
+
// src/feature-implementation/shared/constants.ts
|
|
44
|
+
var STORY_BOUNDARY = "<!-- trailstep-story-boundary -->";
|
|
45
|
+
var STORY_CONTEXT_START = "<context>";
|
|
46
|
+
var STORY_CONTEXT_END = "</context>";
|
|
47
|
+
var REVIEW_PASS_THRESHOLD = 4;
|
|
48
|
+
var MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS = 5;
|
|
49
|
+
var MAX_STORY_REVIEW_ATTEMPTS = 3;
|
|
50
|
+
|
|
51
|
+
// src/feature-implementation/shared/review-schema.ts
|
|
52
|
+
import { jsonSchema as jsonSchema2 } from "@trailstep/authoring";
|
|
53
|
+
var RATING_SCHEMA = { type: "integer", minimum: 1, maximum: 5 };
|
|
54
|
+
var reviewOutput = jsonSchema2({
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
score: RATING_SCHEMA,
|
|
58
|
+
summary: { type: "string" },
|
|
59
|
+
methodologyRatings: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
tdd: RATING_SCHEMA,
|
|
63
|
+
verticalSlicing: RATING_SCHEMA,
|
|
64
|
+
tracerBullet: RATING_SCHEMA,
|
|
65
|
+
dependencies: RATING_SCHEMA,
|
|
66
|
+
architecture: RATING_SCHEMA
|
|
67
|
+
},
|
|
68
|
+
required: ["tdd", "verticalSlicing", "tracerBullet", "dependencies", "architecture"],
|
|
69
|
+
additionalProperties: false
|
|
70
|
+
},
|
|
71
|
+
requiredImprovements: { type: "array", items: { type: "string" } }
|
|
72
|
+
},
|
|
73
|
+
required: ["score", "summary", "methodologyRatings", "requiredImprovements"],
|
|
74
|
+
additionalProperties: false
|
|
75
|
+
});
|
|
76
|
+
function reviewPasses(review) {
|
|
77
|
+
return review.score >= REVIEW_PASS_THRESHOLD;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/feature-implementation/split-implementation-stories/step.ts
|
|
81
|
+
import { document, fail as fail4, state as state3, step as step4 } from "@trailstep/authoring";
|
|
82
|
+
|
|
83
|
+
// src/feature-implementation/implement-story/step.ts
|
|
84
|
+
import { fail as fail3, step as step3 } from "@trailstep/authoring";
|
|
85
|
+
|
|
86
|
+
// src/feature-implementation/review-story-implementation/step.ts
|
|
87
|
+
import { fail as fail2, step as step2 } from "@trailstep/authoring";
|
|
88
|
+
|
|
89
|
+
// src/feature-implementation/commit-reviewed-story/step.ts
|
|
90
|
+
import { done, fail, state as state2, step } from "@trailstep/authoring";
|
|
91
|
+
|
|
92
|
+
// src/feature-implementation/shared/story-state.ts
|
|
93
|
+
import { execFile } from "child_process";
|
|
94
|
+
import { promisify } from "util";
|
|
95
|
+
import { state } from "@trailstep/authoring";
|
|
96
|
+
var execFileAsync = promisify(execFile);
|
|
97
|
+
var STORY_STATE_KEYS = {
|
|
98
|
+
activeStory: "activeStory",
|
|
99
|
+
activeStoryStartCommit: "activeStoryStartCommit",
|
|
100
|
+
completedStories: "completedStories",
|
|
101
|
+
storyQueue: "storyQueue"
|
|
102
|
+
};
|
|
103
|
+
async function runGit(args, cwd) {
|
|
104
|
+
try {
|
|
105
|
+
const { stdout } = await execFileAsync("git", [...args], {
|
|
106
|
+
cwd,
|
|
107
|
+
maxBuffer: 1024 * 1024 * 10
|
|
108
|
+
});
|
|
109
|
+
return { ok: true, stdout: stdout.trimEnd() };
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
112
|
+
return { ok: false, error: message };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function recordActiveStoryStartCommit() {
|
|
116
|
+
const existing = await state.get(
|
|
117
|
+
STORY_STATE_KEYS.activeStoryStartCommit
|
|
118
|
+
);
|
|
119
|
+
if (existing?.commit || existing?.warning) {
|
|
120
|
+
return existing;
|
|
121
|
+
}
|
|
122
|
+
const cwd = state.cwd;
|
|
123
|
+
if (!cwd) {
|
|
124
|
+
const missingCwd = {
|
|
125
|
+
warning: "Workflow cwd is unavailable; the story reviewer cannot verify committed story changes from a durable git baseline."
|
|
126
|
+
};
|
|
127
|
+
await state.set(STORY_STATE_KEYS.activeStoryStartCommit, missingCwd);
|
|
128
|
+
return missingCwd;
|
|
129
|
+
}
|
|
130
|
+
const result = await runGit(["rev-parse", "HEAD"], cwd);
|
|
131
|
+
if (!result.ok || result.stdout.length === 0) {
|
|
132
|
+
const missingGitBaseline = {
|
|
133
|
+
warning: `Unable to record story start commit with \`git rev-parse HEAD\`: ${result.ok ? "no commit was returned" : result.error}`
|
|
134
|
+
};
|
|
135
|
+
await state.set(STORY_STATE_KEYS.activeStoryStartCommit, missingGitBaseline);
|
|
136
|
+
return missingGitBaseline;
|
|
137
|
+
}
|
|
138
|
+
const baseline = { commit: result.stdout };
|
|
139
|
+
await state.set(STORY_STATE_KEYS.activeStoryStartCommit, baseline);
|
|
140
|
+
return baseline;
|
|
141
|
+
}
|
|
142
|
+
async function resetActiveStoryStartCommit() {
|
|
143
|
+
await state.set(STORY_STATE_KEYS.activeStoryStartCommit, null);
|
|
144
|
+
}
|
|
145
|
+
async function loadStoryReviewGitContext() {
|
|
146
|
+
const baseline = await state.get(
|
|
147
|
+
STORY_STATE_KEYS.activeStoryStartCommit
|
|
148
|
+
);
|
|
149
|
+
const warnings = [];
|
|
150
|
+
if (baseline?.warning) {
|
|
151
|
+
warnings.push(baseline.warning);
|
|
152
|
+
}
|
|
153
|
+
const cwd = state.cwd;
|
|
154
|
+
if (!cwd) {
|
|
155
|
+
warnings.push(
|
|
156
|
+
"Workflow cwd is unavailable; reviewer must treat committed story-change context as missing."
|
|
157
|
+
);
|
|
158
|
+
return { storyStartCommit: baseline?.commit, warnings };
|
|
159
|
+
}
|
|
160
|
+
const status = await runGit(["status", "--short"], cwd);
|
|
161
|
+
if (!status.ok) {
|
|
162
|
+
warnings.push(`Unable to inspect \`git status --short\`: ${status.error}`);
|
|
163
|
+
}
|
|
164
|
+
const uncommittedDiff = await runGit(["diff"], cwd);
|
|
165
|
+
if (!uncommittedDiff.ok) {
|
|
166
|
+
warnings.push(
|
|
167
|
+
`Unable to inspect uncommitted changes with \`git diff\`: ${uncommittedDiff.error}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
const storyStartCommit = baseline?.commit;
|
|
171
|
+
let committedDiff;
|
|
172
|
+
if (!storyStartCommit) {
|
|
173
|
+
warnings.push(
|
|
174
|
+
"No durable story start commit is recorded; reviewer must not silently review only the current working tree diff."
|
|
175
|
+
);
|
|
176
|
+
} else {
|
|
177
|
+
const baselineReachable = await runGit(["cat-file", "-e", `${storyStartCommit}^{commit}`], cwd);
|
|
178
|
+
if (!baselineReachable.ok) {
|
|
179
|
+
warnings.push(
|
|
180
|
+
`Recorded story start commit \`${storyStartCommit}\` is invalid or unreachable: ${baselineReachable.error}`
|
|
181
|
+
);
|
|
182
|
+
} else {
|
|
183
|
+
const committed = await runGit(["diff", `${storyStartCommit}..HEAD`], cwd);
|
|
184
|
+
if (!committed.ok) {
|
|
185
|
+
warnings.push(
|
|
186
|
+
`Unable to inspect committed story changes with \`git diff ${storyStartCommit}..HEAD\`: ${committed.error}`
|
|
187
|
+
);
|
|
188
|
+
} else {
|
|
189
|
+
committedDiff = committed.stdout;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
storyStartCommit,
|
|
195
|
+
statusShort: status.ok ? status.stdout : void 0,
|
|
196
|
+
committedDiff,
|
|
197
|
+
uncommittedDiff: uncommittedDiff.ok ? uncommittedDiff.stdout : void 0,
|
|
198
|
+
warnings
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/feature-implementation/commit-reviewed-story/run-git.ts
|
|
203
|
+
import { execFile as execFile2 } from "child_process";
|
|
204
|
+
import { promisify as promisify2 } from "util";
|
|
205
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
206
|
+
async function runGit2(args, cwd) {
|
|
207
|
+
try {
|
|
208
|
+
const { stdout } = await execFileAsync2("git", [...args], {
|
|
209
|
+
cwd,
|
|
210
|
+
maxBuffer: 1024 * 1024 * 10
|
|
211
|
+
});
|
|
212
|
+
return { ok: true, stdout: stdout.trimEnd() };
|
|
213
|
+
} catch (error) {
|
|
214
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
215
|
+
return { ok: false, error: message };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/feature-implementation/commit-reviewed-story/step.ts
|
|
220
|
+
var commitReviewedStoryStep = step({ id: "commit-reviewed-story" }).do(
|
|
221
|
+
async (input) => {
|
|
222
|
+
const activeStory = await state2.get(STORY_STATE_KEYS.activeStory) ?? input.currentStory;
|
|
223
|
+
if (storyAutoCommitEnabled()) {
|
|
224
|
+
const commitResult = await commitReviewedStoryChanges(
|
|
225
|
+
activeStory,
|
|
226
|
+
input.implementationSummary
|
|
227
|
+
);
|
|
228
|
+
if (!commitResult.ok) {
|
|
229
|
+
return fail({
|
|
230
|
+
code: commitResult.code,
|
|
231
|
+
message: commitResult.message,
|
|
232
|
+
details: commitResult.details
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return completeReviewedStory(activeStory);
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
function storyAutoCommitEnabled() {
|
|
240
|
+
const mode = process.env.TRAILSTEP_STORY_COMMIT_MODE;
|
|
241
|
+
return ["1", "true", "enabled", "worktree"].includes(mode?.toLowerCase() ?? "");
|
|
242
|
+
}
|
|
243
|
+
async function commitReviewedStoryChanges(activeStory, implementationSummary) {
|
|
244
|
+
const cwd = state2.cwd;
|
|
245
|
+
if (!cwd) {
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
code: "story_commit_missing_cwd",
|
|
249
|
+
message: "Cannot commit reviewed story because the TrailStep workflow cwd is unavailable.",
|
|
250
|
+
details: { storyPath: activeStory.path }
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const insideWorkTree = await runGit2(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
254
|
+
if (!insideWorkTree.ok || insideWorkTree.stdout !== "true") {
|
|
255
|
+
return {
|
|
256
|
+
ok: false,
|
|
257
|
+
code: "story_commit_not_git_worktree",
|
|
258
|
+
message: "Cannot commit reviewed story because the workflow is not running inside a git worktree.",
|
|
259
|
+
details: {
|
|
260
|
+
storyPath: activeStory.path,
|
|
261
|
+
gitError: insideWorkTree.ok ? void 0 : insideWorkTree.error
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
const add = await runGit2(["add", "-A"], cwd);
|
|
266
|
+
if (!add.ok) {
|
|
267
|
+
return {
|
|
268
|
+
ok: false,
|
|
269
|
+
code: "story_commit_stage_failed",
|
|
270
|
+
message: "Cannot commit reviewed story because `git add -A` failed.",
|
|
271
|
+
details: { storyPath: activeStory.path, gitError: add.error }
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
const staged = await runGit2(["diff", "--cached", "--name-only"], cwd);
|
|
275
|
+
if (!staged.ok) {
|
|
276
|
+
return {
|
|
277
|
+
ok: false,
|
|
278
|
+
code: "story_commit_status_failed",
|
|
279
|
+
message: "Cannot commit reviewed story because staged changes could not be inspected.",
|
|
280
|
+
details: { storyPath: activeStory.path, gitError: staged.error }
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
if (staged.stdout.trim().length === 0) {
|
|
284
|
+
const baseline = await state2.get(
|
|
285
|
+
STORY_STATE_KEYS.activeStoryStartCommit
|
|
286
|
+
);
|
|
287
|
+
const head = await runGit2(["rev-parse", "HEAD"], cwd);
|
|
288
|
+
if (baseline?.commit && head.ok && head.stdout !== baseline.commit) {
|
|
289
|
+
return { ok: true };
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
ok: false,
|
|
293
|
+
code: "story_commit_empty",
|
|
294
|
+
message: "The story passed review, but there are no staged or already-committed changes since the story baseline to commit.",
|
|
295
|
+
details: {
|
|
296
|
+
storyPath: activeStory.path,
|
|
297
|
+
storyStartCommit: baseline?.commit,
|
|
298
|
+
head: head.ok ? head.stdout : void 0,
|
|
299
|
+
gitError: head.ok ? void 0 : head.error
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const storyTitle = extractStoryTitle(activeStory.content, 1);
|
|
304
|
+
const commit = await runGit2(
|
|
305
|
+
[
|
|
306
|
+
"commit",
|
|
307
|
+
"-m",
|
|
308
|
+
truncateCommitSubject(`trailstep: ${storyTitle}`),
|
|
309
|
+
"-m",
|
|
310
|
+
commitBody(activeStory, implementationSummary, staged.stdout)
|
|
311
|
+
],
|
|
312
|
+
cwd
|
|
313
|
+
);
|
|
314
|
+
if (!commit.ok) {
|
|
315
|
+
return {
|
|
316
|
+
ok: false,
|
|
317
|
+
code: "story_commit_failed",
|
|
318
|
+
message: "The story passed review, but TrailStep could not create the story commit.",
|
|
319
|
+
details: { storyPath: activeStory.path, gitError: commit.error }
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
return { ok: true };
|
|
323
|
+
}
|
|
324
|
+
async function completeReviewedStory(activeStory) {
|
|
325
|
+
const completed = await state2.get(STORY_STATE_KEYS.completedStories) ?? [];
|
|
326
|
+
const updatedCompleted = [
|
|
327
|
+
...completed,
|
|
328
|
+
extractStoryTitle(activeStory.content, completed.length + 1)
|
|
329
|
+
];
|
|
330
|
+
await state2.set(STORY_STATE_KEYS.completedStories, updatedCompleted);
|
|
331
|
+
const storyQueue = await state2.get(STORY_STATE_KEYS.storyQueue) ?? [];
|
|
332
|
+
const [nextStory, ...remaining] = storyQueue;
|
|
333
|
+
if (!nextStory) {
|
|
334
|
+
await state2.set(STORY_STATE_KEYS.activeStory, null);
|
|
335
|
+
await resetActiveStoryStartCommit();
|
|
336
|
+
const featureDoc = await state2.get("featureDoc");
|
|
337
|
+
const implementationDoc = await state2.get("implementationDoc");
|
|
338
|
+
const output = {
|
|
339
|
+
status: "implemented",
|
|
340
|
+
featureDocPath: featureDoc?.path ?? "",
|
|
341
|
+
implementationDocPath: implementationDoc?.path ?? "",
|
|
342
|
+
storyCount: updatedCompleted.length,
|
|
343
|
+
completedStories: updatedCompleted,
|
|
344
|
+
summary: `Implemented and reviewed ${updatedCompleted.length} ${updatedCompleted.length === 1 ? "story" : "stories"}.`
|
|
345
|
+
};
|
|
346
|
+
return done(output);
|
|
347
|
+
}
|
|
348
|
+
await state2.set(STORY_STATE_KEYS.storyQueue, remaining);
|
|
349
|
+
await state2.set(STORY_STATE_KEYS.activeStory, nextStory);
|
|
350
|
+
await resetActiveStoryStartCommit();
|
|
351
|
+
await recordActiveStoryStartCommit();
|
|
352
|
+
return implementStoryStep({ currentStory: nextStory, attempt: 1 });
|
|
353
|
+
}
|
|
354
|
+
function truncateCommitSubject(subject) {
|
|
355
|
+
return subject.length <= 72 ? subject : `${subject.slice(0, 69)}...`;
|
|
356
|
+
}
|
|
357
|
+
function commitBody(activeStory, implementationSummary, stagedFiles) {
|
|
358
|
+
return [
|
|
359
|
+
"Committed automatically by TrailStep after a passing story review.",
|
|
360
|
+
"",
|
|
361
|
+
`Story artifact: ${activeStory.path}`,
|
|
362
|
+
"",
|
|
363
|
+
"Implementer summary:",
|
|
364
|
+
implementationSummary?.trim() || "Not provided.",
|
|
365
|
+
"",
|
|
366
|
+
"Staged files:",
|
|
367
|
+
stagedFiles.split("\n").filter((file) => file.length > 0).map((file) => `- ${file}`).join("\n")
|
|
368
|
+
].join("\n");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/feature-implementation/review-story-implementation/prompt.ts
|
|
372
|
+
import { loadFragments, promptSections, section } from "@trailstep/authoring";
|
|
373
|
+
var fragments = loadFragments(import.meta.dirname, {
|
|
374
|
+
methodology: "../shared/feature-methodology.md",
|
|
375
|
+
storyContract: "../shared/story-implementation-contract.md"
|
|
376
|
+
});
|
|
377
|
+
function reviewStoryImplementationPrompt({
|
|
378
|
+
input
|
|
379
|
+
}) {
|
|
380
|
+
const committedDiffCommand = input.gitContext.storyStartCommit ? `git diff ${input.gitContext.storyStartCommit}..HEAD` : "git diff <missing-story-start-commit>..HEAD";
|
|
381
|
+
return promptSections(
|
|
382
|
+
fragments.methodology,
|
|
383
|
+
fragments.storyContract,
|
|
384
|
+
section("Story", input.currentStory.content),
|
|
385
|
+
section("Implementer summary", input.implementationSummary ?? "Not provided."),
|
|
386
|
+
section(
|
|
387
|
+
"Story review git baseline",
|
|
388
|
+
[
|
|
389
|
+
`Recorded story start commit: ${input.gitContext.storyStartCommit ?? "MISSING"}`,
|
|
390
|
+
`Inspect committed story changes with: \`${committedDiffCommand}\``,
|
|
391
|
+
"Inspect uncommitted working tree changes with: `git diff`",
|
|
392
|
+
"Inspect status with: `git status --short`",
|
|
393
|
+
input.gitContext.warnings.length > 0 ? [
|
|
394
|
+
"",
|
|
395
|
+
"Warnings / blocked context:",
|
|
396
|
+
...input.gitContext.warnings.map((warning) => `- ${warning}`)
|
|
397
|
+
].join("\n") : ""
|
|
398
|
+
].filter((line) => line.length > 0).join("\n")
|
|
399
|
+
),
|
|
400
|
+
section(
|
|
401
|
+
"Committed story changes (`git diff <storyStartCommit>..HEAD`)",
|
|
402
|
+
input.gitContext.committedDiff ?? "Not available."
|
|
403
|
+
),
|
|
404
|
+
section(
|
|
405
|
+
"Uncommitted working tree changes (`git diff`)",
|
|
406
|
+
input.gitContext.uncommittedDiff ?? "Not available."
|
|
407
|
+
),
|
|
408
|
+
section(
|
|
409
|
+
"Working tree status (`git status --short`)",
|
|
410
|
+
input.gitContext.statusShort ?? "Not available."
|
|
411
|
+
),
|
|
412
|
+
section(
|
|
413
|
+
"Task",
|
|
414
|
+
"Review only the active story slice represented by the recorded story start commit through HEAD, plus the current uncommitted working tree changes, against the story above and the implementer summary. Prefer the supplied diff/status sections; if you need to inspect locally, use read-only commands only (`git status --short`, the listed `git diff <storyStartCommit>..HEAD`, and `git diff`) \u2014 do not run tests, edit code, revert files, clean files, or try to isolate the diff by changing the repository. If unrelated dirty files, a missing/invalid baseline, or ambiguous context prevents a trustworthy review, say so in the structured review and require the implementer/workflow to fix the isolation; never remove changes yourself. Respond only with the structured review."
|
|
415
|
+
)
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/feature-implementation/review-story-implementation/step.ts
|
|
420
|
+
var reviewStoryImplementationStep = step2({ id: "review-story-implementation" }).prompt(reviewStoryImplementationPrompt, {
|
|
421
|
+
agent: "reviewer",
|
|
422
|
+
output: reviewOutput
|
|
423
|
+
}).do(async (review, input) => {
|
|
424
|
+
if (!reviewPasses(review)) {
|
|
425
|
+
if (input.attempt >= MAX_STORY_REVIEW_ATTEMPTS) {
|
|
426
|
+
return fail2({
|
|
427
|
+
code: "story_review_exhausted",
|
|
428
|
+
message: `Story failed review ${MAX_STORY_REVIEW_ATTEMPTS} times in a row (last score ${review.score}/5).`,
|
|
429
|
+
details: { review, storyPath: input.currentStory.path }
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return implementStoryStep({
|
|
433
|
+
currentStory: input.currentStory,
|
|
434
|
+
previousStoryReview: review,
|
|
435
|
+
attempt: input.attempt + 1
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
return commitReviewedStoryStep({
|
|
439
|
+
currentStory: input.currentStory,
|
|
440
|
+
implementationSummary: input.implementationSummary
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// src/feature-implementation/implement-story/prompt.ts
|
|
445
|
+
import {
|
|
446
|
+
jsonSchema as jsonSchema3,
|
|
447
|
+
list,
|
|
448
|
+
loadFragments as loadFragments2,
|
|
449
|
+
promptSections as promptSections2,
|
|
450
|
+
section as section2
|
|
451
|
+
} from "@trailstep/authoring";
|
|
452
|
+
var fragments2 = loadFragments2(import.meta.dirname, {
|
|
453
|
+
methodology: "../shared/feature-methodology.md",
|
|
454
|
+
architectureGuidance: "../shared/project-architecture-guidance.md",
|
|
455
|
+
storyContract: "../shared/story-implementation-contract.md"
|
|
456
|
+
});
|
|
457
|
+
var implementStoryOutput = jsonSchema3({
|
|
458
|
+
type: "object",
|
|
459
|
+
properties: {
|
|
460
|
+
blocked: { type: "boolean" },
|
|
461
|
+
blockedReason: { type: "string" },
|
|
462
|
+
summary: {
|
|
463
|
+
type: "string",
|
|
464
|
+
description: "Files changed, commands run, and their results."
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
required: ["blocked", "summary"],
|
|
468
|
+
additionalProperties: false
|
|
469
|
+
});
|
|
470
|
+
function implementStoryPrompt({ input }) {
|
|
471
|
+
const taskBody = input.previousStoryReview === void 0 ? "Implement this story." : promptSections2(
|
|
472
|
+
`This is retry attempt ${input.attempt} for this story. A previous review scored the implementation ${input.previousStoryReview.score}/5: ${input.previousStoryReview.summary}`,
|
|
473
|
+
`Required improvements to address, without discarding what already works:
|
|
474
|
+
|
|
475
|
+
${list(input.previousStoryReview.requiredImprovements)}`
|
|
476
|
+
);
|
|
477
|
+
return promptSections2(
|
|
478
|
+
fragments2.methodology,
|
|
479
|
+
fragments2.architectureGuidance,
|
|
480
|
+
fragments2.storyContract,
|
|
481
|
+
section2("Story", input.currentStory.content),
|
|
482
|
+
section2(
|
|
483
|
+
"Task",
|
|
484
|
+
`${taskBody}
|
|
485
|
+
|
|
486
|
+
Set \`blocked: true\` with a clear \`blockedReason\` instead of guessing or pretending the story is complete. Otherwise set \`blocked: false\` and use \`summary\` to report files changed, commands run, and their results.`
|
|
487
|
+
)
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/feature-implementation/implement-story/step.ts
|
|
492
|
+
var implementStoryStep = step3({ id: "implement-story" }).prompt(implementStoryPrompt, {
|
|
493
|
+
agent: "implementer",
|
|
494
|
+
output: implementStoryOutput
|
|
495
|
+
}).do(async (promptOutput, input) => {
|
|
496
|
+
if (promptOutput.blocked) {
|
|
497
|
+
return fail3({
|
|
498
|
+
code: "story_blocked",
|
|
499
|
+
message: promptOutput.blockedReason ?? "Story implementation reported a blocked state.",
|
|
500
|
+
details: { storyPath: input.currentStory.path }
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
return reviewStoryImplementationStep({
|
|
504
|
+
currentStory: input.currentStory,
|
|
505
|
+
attempt: input.attempt,
|
|
506
|
+
implementationSummary: promptOutput.summary,
|
|
507
|
+
gitContext: await loadStoryReviewGitContext()
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// src/feature-implementation/split-implementation-stories/step.ts
|
|
512
|
+
var splitImplementationStoriesStep = step4({ id: "split-implementation-stories" }).do(
|
|
513
|
+
async ({ implementationDoc }) => {
|
|
514
|
+
const contextStartCount = countOccurrences(implementationDoc.content, STORY_CONTEXT_START);
|
|
515
|
+
const contextEndCount = countOccurrences(implementationDoc.content, STORY_CONTEXT_END);
|
|
516
|
+
if (contextStartCount !== contextEndCount) {
|
|
517
|
+
return fail4({
|
|
518
|
+
code: "unbalanced_story_context",
|
|
519
|
+
message: `Found ${contextStartCount} \`${STORY_CONTEXT_START}\` marker(s) and ${contextEndCount} \`${STORY_CONTEXT_END}\` marker(s) in implementation-doc.md. Context blocks must be balanced.`,
|
|
520
|
+
details: { implementationDocPath: implementationDoc.path }
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
const contextMatches = Array.from(implementationDoc.content.matchAll(storyContextPattern()));
|
|
524
|
+
if (contextMatches.length !== contextStartCount) {
|
|
525
|
+
return fail4({
|
|
526
|
+
code: "invalid_story_context_order",
|
|
527
|
+
message: `Every \`${STORY_CONTEXT_START}\` marker in implementation-doc.md must be followed by a matching \`${STORY_CONTEXT_END}\` marker.`,
|
|
528
|
+
details: { implementationDocPath: implementationDoc.path }
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
const contextBlocks = contextMatches.map(([, context]) => context?.trim() ?? "").filter((context) => context.length > 0);
|
|
532
|
+
const sharedContext = contextBlocks.join("\n\n---\n\n");
|
|
533
|
+
const contentWithoutContextBlocks = implementationDoc.content.replace(
|
|
534
|
+
storyContextPattern(),
|
|
535
|
+
""
|
|
536
|
+
);
|
|
537
|
+
const chunks = contentWithoutContextBlocks.split(STORY_BOUNDARY).slice(1).map((chunk) => chunk.trim()).filter((chunk) => chunk.length > 0).map((chunk) => prependSharedContext(chunk, sharedContext));
|
|
538
|
+
if (chunks.length === 0) {
|
|
539
|
+
return fail4({
|
|
540
|
+
code: "no_stories_found",
|
|
541
|
+
message: `No \`${STORY_BOUNDARY}\`-delimited stories found in implementation-doc.md.`,
|
|
542
|
+
details: { implementationDocPath: implementationDoc.path }
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
const storyDocs = [];
|
|
546
|
+
for (const chunk of chunks) {
|
|
547
|
+
storyDocs.push(await document(chunk));
|
|
548
|
+
}
|
|
549
|
+
const [firstStory, ...remaining] = storyDocs;
|
|
550
|
+
await state3.set(STORY_STATE_KEYS.storyQueue, remaining);
|
|
551
|
+
await state3.set(STORY_STATE_KEYS.completedStories, []);
|
|
552
|
+
await state3.set(STORY_STATE_KEYS.activeStory, firstStory);
|
|
553
|
+
await recordActiveStoryStartCommit();
|
|
554
|
+
return implementStoryStep({ currentStory: firstStory, attempt: 1 });
|
|
555
|
+
}
|
|
556
|
+
);
|
|
557
|
+
function countOccurrences(value, needle) {
|
|
558
|
+
if (needle.length === 0) {
|
|
559
|
+
return 0;
|
|
560
|
+
}
|
|
561
|
+
let count = 0;
|
|
562
|
+
let offset = 0;
|
|
563
|
+
while (true) {
|
|
564
|
+
const index = value.indexOf(needle, offset);
|
|
565
|
+
if (index === -1) {
|
|
566
|
+
return count;
|
|
567
|
+
}
|
|
568
|
+
count += 1;
|
|
569
|
+
offset = index + needle.length;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function storyContextPattern() {
|
|
573
|
+
return new RegExp(
|
|
574
|
+
`${escapeRegExp(STORY_CONTEXT_START)}([\\s\\S]*?)${escapeRegExp(STORY_CONTEXT_END)}`,
|
|
575
|
+
"g"
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
function prependSharedContext(story, sharedContext) {
|
|
579
|
+
if (sharedContext.length === 0) {
|
|
580
|
+
return story;
|
|
581
|
+
}
|
|
582
|
+
return ["## Shared implementation context", sharedContext, "## Active story", story].join("\n\n");
|
|
583
|
+
}
|
|
584
|
+
function escapeRegExp(value) {
|
|
585
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/feature-implementation/review-implementation-doc/prompt.ts
|
|
589
|
+
import { list as list2, loadFragments as loadFragments3, promptSections as promptSections3, section as section3 } from "@trailstep/authoring";
|
|
590
|
+
var fragments3 = loadFragments3(import.meta.dirname, {
|
|
591
|
+
methodology: "../shared/feature-methodology.md",
|
|
592
|
+
implementationDocFormat: "../shared/implementation-doc-format.md"
|
|
593
|
+
});
|
|
594
|
+
function reviewImplementationDocPrompt({
|
|
595
|
+
input
|
|
596
|
+
}) {
|
|
597
|
+
const reviewCriteria = list2([
|
|
598
|
+
"traceability to the feature doc",
|
|
599
|
+
"strict behavioral-red TDD story design",
|
|
600
|
+
"vertical slices rather than horizontal tasks",
|
|
601
|
+
"a clear tracer-bullet strategy",
|
|
602
|
+
"hard dependency labels and topological ordering",
|
|
603
|
+
"acceptance criteria and validation commands",
|
|
604
|
+
"story instructions self-contained enough for an implementer who will only see one story plus the prepended shared context at a time",
|
|
605
|
+
"correct use of balanced `<context>` ... `</context>` blocks for shared implementer context",
|
|
606
|
+
"correct use of the `<!-- trailstep-story-boundary -->` marker between stories, and that no story-critical detail lives only in non-context overview text above the first marker"
|
|
607
|
+
]);
|
|
608
|
+
return promptSections3(
|
|
609
|
+
fragments3.methodology,
|
|
610
|
+
fragments3.implementationDocFormat,
|
|
611
|
+
section3("Feature doc", input.featureDoc.content),
|
|
612
|
+
section3("Implementation doc under review", input.implementationDoc.content),
|
|
613
|
+
section3(
|
|
614
|
+
"Task",
|
|
615
|
+
`Critically review the implementation doc above against the feature doc and the methodology. Review for:
|
|
616
|
+
|
|
617
|
+
${reviewCriteria}
|
|
618
|
+
|
|
619
|
+
Do not edit the implementation doc. Respond only with the structured review.`
|
|
620
|
+
)
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// src/feature-implementation/review-implementation-doc/step.ts
|
|
625
|
+
var reviewImplementationDocStep = step5({ id: "review-implementation-doc" }).prompt(reviewImplementationDocPrompt, {
|
|
626
|
+
agent: "reviewer",
|
|
627
|
+
output: reviewOutput
|
|
628
|
+
}).do((review, input) => {
|
|
629
|
+
if (reviewPasses(review)) {
|
|
630
|
+
return splitImplementationStoriesStep({ implementationDoc: input.implementationDoc });
|
|
631
|
+
}
|
|
632
|
+
if (input.attempt >= MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS) {
|
|
633
|
+
return fail5({
|
|
634
|
+
code: "implementation_doc_review_exhausted",
|
|
635
|
+
message: `implementation-doc.md failed review ${MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS} times in a row (last score ${review.score}/5).`,
|
|
636
|
+
details: { review, implementationDocPath: input.implementationDoc.path }
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
return createOrImproveImplementationDocStep({
|
|
640
|
+
featureDoc: input.featureDoc,
|
|
641
|
+
previousReview: review,
|
|
642
|
+
attempt: input.attempt + 1
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// src/feature-implementation/create-or-improve-implementation-doc/prompt.ts
|
|
647
|
+
import { list as list3, loadFragments as loadFragments4, promptSections as promptSections4, section as section4 } from "@trailstep/authoring";
|
|
648
|
+
var fragments4 = loadFragments4(import.meta.dirname, {
|
|
649
|
+
methodology: "../shared/feature-methodology.md",
|
|
650
|
+
architectureGuidance: "../shared/project-architecture-guidance.md",
|
|
651
|
+
implementationDocFormat: "../shared/implementation-doc-format.md"
|
|
652
|
+
});
|
|
653
|
+
function createOrImproveImplementationDocPrompt({
|
|
654
|
+
input
|
|
655
|
+
}) {
|
|
656
|
+
const taskBody = input.previousReview === void 0 ? "Create `implementation-doc.md` from the feature doc above, following the format exactly enough that stories can later be split mechanically on the `<!-- trailstep-story-boundary -->` marker and shared context can be prepended from balanced `<context>` blocks." : promptSections4(
|
|
657
|
+
`This is improvement attempt ${input.attempt}. A previous review scored this plan ${input.previousReview.score}/5: ${input.previousReview.summary}`,
|
|
658
|
+
`Required improvements to address, without discarding what already works:
|
|
659
|
+
|
|
660
|
+
${list3(input.previousReview.requiredImprovements)}`
|
|
661
|
+
);
|
|
662
|
+
return promptSections4(
|
|
663
|
+
fragments4.methodology,
|
|
664
|
+
fragments4.architectureGuidance,
|
|
665
|
+
fragments4.implementationDocFormat,
|
|
666
|
+
section4("Feature doc", input.featureDoc.content),
|
|
667
|
+
section4(
|
|
668
|
+
"Task",
|
|
669
|
+
`${taskBody}
|
|
670
|
+
|
|
671
|
+
Design stories around strict behavioral-red TDD, vertical slicing, tracer-bullet methodology, and hard dependency labels only. Topologically order stories by dependency. Put shared implementation details in balanced \`<context>\` ... \`</context>\` blocks so split-stories prepends them to every story; repeat details inside individual stories when only selected stories need them. Write every story as implementation-ready instructions for an implementer who will only see that story plus the prepended context.`
|
|
672
|
+
)
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/feature-implementation/create-or-improve-implementation-doc/step.ts
|
|
677
|
+
var createOrImproveImplementationDocStep = step6({
|
|
678
|
+
id: "create-or-improve-implementation-doc"
|
|
679
|
+
}).prompt(createOrImproveImplementationDocPrompt, {
|
|
680
|
+
agent: "planner",
|
|
681
|
+
output: Document
|
|
682
|
+
}).do(async (implementationDoc, input) => {
|
|
683
|
+
await state4.set("implementationDoc", implementationDoc);
|
|
684
|
+
return reviewImplementationDocStep({
|
|
685
|
+
featureDoc: input.featureDoc,
|
|
686
|
+
implementationDoc,
|
|
687
|
+
attempt: input.attempt
|
|
688
|
+
});
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// src/feature-implementation/create-feature-doc/prompt.ts
|
|
692
|
+
import { loadFragments as loadFragments5, promptSections as promptSections5, section as section5 } from "@trailstep/authoring";
|
|
693
|
+
var fragments5 = loadFragments5(import.meta.dirname, {
|
|
694
|
+
methodology: "../shared/feature-methodology.md",
|
|
695
|
+
featureDocFormat: "../shared/feature-doc-format.md"
|
|
696
|
+
});
|
|
697
|
+
function createFeatureDocPrompt({ input }) {
|
|
698
|
+
return promptSections5(
|
|
699
|
+
fragments5.methodology,
|
|
700
|
+
fragments5.featureDocFormat,
|
|
701
|
+
section5("Conversation / feature request", input.conversation),
|
|
702
|
+
section5(
|
|
703
|
+
"Task",
|
|
704
|
+
'Write `feature-doc.md` following the format above, based on the conversation/request. Make it detailed enough that another agent can plan implementation without reading the original conversation. Preserve uncertainty explicitly in "Open Questions and Assumptions" \u2014 do not invent missing product decisions.'
|
|
705
|
+
)
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/feature-implementation/create-feature-doc/step.ts
|
|
710
|
+
var createFeatureDocStep = step7({ id: "create-feature-doc" }).prompt(createFeatureDocPrompt, {
|
|
711
|
+
agent: "featureWriter",
|
|
712
|
+
output: Document2
|
|
713
|
+
}).do(async (featureDoc) => {
|
|
714
|
+
await state5.set("featureDoc", featureDoc);
|
|
715
|
+
return createOrImproveImplementationDocStep({ featureDoc, attempt: 1 });
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
// src/feature-implementation/shared/input-schema.ts
|
|
719
|
+
import { shape } from "@trailstep/authoring";
|
|
720
|
+
var takeItAwayInput = shape({ conversation: "string" });
|
|
721
|
+
|
|
722
|
+
// src/grill-it-away/grill/prompt.ts
|
|
723
|
+
import { promptSections as promptSections6, section as section6 } from "@trailstep/authoring";
|
|
724
|
+
function grillPrompt() {
|
|
725
|
+
return promptSections6(
|
|
726
|
+
section6(
|
|
727
|
+
"Role",
|
|
728
|
+
"You are the interactive front door for starting a new TrailStep feature. Open by asking the user what they want to build."
|
|
729
|
+
),
|
|
730
|
+
section6(
|
|
731
|
+
"Task",
|
|
732
|
+
"Keep asking follow-up questions until you are confident you understand the feature request well enough to hand off to a planning agent. There is no fixed checklist of required topics \u2014 use your judgment, and do not end the conversation prematurely just to finish quickly."
|
|
733
|
+
),
|
|
734
|
+
section6(
|
|
735
|
+
"Output",
|
|
736
|
+
"Your structured output's `conversation` field must contain the full raw transcript of the conversation so far, not a summary. Preserve as much of the actual back-and-forth as possible."
|
|
737
|
+
)
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/grill-it-away/grill/step.ts
|
|
742
|
+
var grillStep = step8({ id: "grill" }).prompt(grillPrompt, {
|
|
743
|
+
agent: "grillingAgent",
|
|
744
|
+
mode: "interactive",
|
|
745
|
+
output: takeItAwayInput
|
|
746
|
+
}).do((output) => createFeatureDocStep(output));
|
|
747
|
+
|
|
748
|
+
// src/grill-it-away/workflow.ts
|
|
749
|
+
var grillItAway = defineWorkflow({
|
|
750
|
+
id: "grill-it-away",
|
|
751
|
+
description: "Interactively grills the user until it understands the requested feature.",
|
|
752
|
+
inputShape: shape2({}),
|
|
753
|
+
outputShape: takeItAwayOutput,
|
|
754
|
+
agents: {
|
|
755
|
+
grillingAgent: {
|
|
756
|
+
size: "medium",
|
|
757
|
+
thinking: "medium",
|
|
758
|
+
description: "Grills the user until it understands the requested feature."
|
|
759
|
+
},
|
|
760
|
+
featureWriter: {
|
|
761
|
+
size: "medium",
|
|
762
|
+
thinking: "medium",
|
|
763
|
+
description: "Turns a conversation/feature request into a standalone feature doc."
|
|
764
|
+
},
|
|
765
|
+
planner: {
|
|
766
|
+
size: "large",
|
|
767
|
+
thinking: "high",
|
|
768
|
+
description: "Creates or improves the implementation doc: architecture-aware, TDD/vertical-slice/tracer-bullet story design."
|
|
769
|
+
},
|
|
770
|
+
reviewer: {
|
|
771
|
+
size: "large",
|
|
772
|
+
thinking: "high",
|
|
773
|
+
description: "Reviews isolated implementation docs and story diffs against methodology."
|
|
774
|
+
},
|
|
775
|
+
implementer: {
|
|
776
|
+
size: "medium",
|
|
777
|
+
thinking: "medium",
|
|
778
|
+
description: "Implements one story at a time using strict behavioral-red TDD."
|
|
779
|
+
}
|
|
780
|
+
},
|
|
781
|
+
start() {
|
|
782
|
+
return grillStep();
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
// src/take-it-away/workflow.ts
|
|
787
|
+
import { defineWorkflow as defineWorkflow2 } from "@trailstep/authoring";
|
|
788
|
+
var takeItAway = defineWorkflow2({
|
|
789
|
+
id: "take-it-away",
|
|
790
|
+
description: "Turns an already-organic conversation/feature request into a reviewed implementation plan, then implements it story by story.",
|
|
791
|
+
inputShape: takeItAwayInput,
|
|
792
|
+
outputShape: takeItAwayOutput,
|
|
793
|
+
agents: {
|
|
794
|
+
featureWriter: {
|
|
795
|
+
size: "medium",
|
|
796
|
+
thinking: "medium",
|
|
797
|
+
description: "Turns a conversation/feature request into a standalone feature doc."
|
|
798
|
+
},
|
|
799
|
+
planner: {
|
|
800
|
+
size: "large",
|
|
801
|
+
thinking: "high",
|
|
802
|
+
description: "Creates or improves the implementation doc: architecture-aware, TDD/vertical-slice/tracer-bullet story design."
|
|
803
|
+
},
|
|
804
|
+
reviewer: {
|
|
805
|
+
size: "large",
|
|
806
|
+
thinking: "high",
|
|
807
|
+
description: "Reviews isolated implementation docs and story diffs against methodology."
|
|
808
|
+
},
|
|
809
|
+
implementer: {
|
|
810
|
+
size: "medium",
|
|
811
|
+
thinking: "medium",
|
|
812
|
+
description: "Implements one story at a time using strict behavioral-red TDD."
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
start(input) {
|
|
816
|
+
return createFeatureDocStep(input);
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
export {
|
|
820
|
+
grillItAway,
|
|
821
|
+
takeItAway
|
|
822
|
+
};
|
|
823
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/grill-it-away/workflow.ts","../src/feature-implementation/shared/output-schema.ts","../src/grill-it-away/grill/step.ts","../src/feature-implementation/create-feature-doc/step.ts","../src/feature-implementation/create-or-improve-implementation-doc/step.ts","../src/feature-implementation/review-implementation-doc/step.ts","../src/feature-implementation/shared/constants.ts","../src/feature-implementation/shared/review-schema.ts","../src/feature-implementation/split-implementation-stories/step.ts","../src/feature-implementation/implement-story/step.ts","../src/feature-implementation/review-story-implementation/step.ts","../src/feature-implementation/commit-reviewed-story/step.ts","../src/feature-implementation/shared/story-state.ts","../src/feature-implementation/commit-reviewed-story/run-git.ts","../src/feature-implementation/review-story-implementation/prompt.ts","../src/feature-implementation/implement-story/prompt.ts","../src/feature-implementation/review-implementation-doc/prompt.ts","../src/feature-implementation/create-or-improve-implementation-doc/prompt.ts","../src/feature-implementation/create-feature-doc/prompt.ts","../src/feature-implementation/shared/input-schema.ts","../src/grill-it-away/grill/prompt.ts","../src/take-it-away/workflow.ts"],"sourcesContent":["import { defineWorkflow, shape } from \"@trailstep/authoring\";\nimport { takeItAwayOutput } from \"../feature-implementation/shared/output-schema.js\";\nimport { grillStep } from \"./grill/step.js\";\n\nexport const grillItAway = defineWorkflow({\n id: \"grill-it-away\",\n description: \"Interactively grills the user until it understands the requested feature.\",\n inputShape: shape<Record<string, never>>({}),\n outputShape: takeItAwayOutput,\n agents: {\n grillingAgent: {\n size: \"medium\",\n thinking: \"medium\",\n description: \"Grills the user until it understands the requested feature.\",\n },\n featureWriter: {\n size: \"medium\",\n thinking: \"medium\",\n description: \"Turns a conversation/feature request into a standalone feature doc.\",\n },\n planner: {\n size: \"large\",\n thinking: \"high\",\n description:\n \"Creates or improves the implementation doc: architecture-aware, TDD/vertical-slice/tracer-bullet story design.\",\n },\n reviewer: {\n size: \"large\",\n thinking: \"high\",\n description: \"Reviews isolated implementation docs and story diffs against methodology.\",\n },\n implementer: {\n size: \"medium\",\n thinking: \"medium\",\n description: \"Implements one story at a time using strict behavioral-red TDD.\",\n },\n },\n start() {\n return grillStep();\n },\n});\n","import { jsonSchema } from \"@trailstep/authoring\";\n\nexport interface TakeItAwayOutput extends Record<string, unknown> {\n readonly status: \"implemented\";\n readonly featureDocPath: string;\n readonly implementationDocPath: string;\n readonly storyCount: number;\n readonly completedStories: readonly string[];\n readonly summary: string;\n}\n\nexport const takeItAwayOutput = jsonSchema<TakeItAwayOutput>({\n type: \"object\",\n properties: {\n status: { type: \"string\", const: \"implemented\" },\n featureDocPath: { type: \"string\" },\n implementationDocPath: { type: \"string\" },\n storyCount: { type: \"number\" },\n completedStories: { type: \"array\", items: { type: \"string\" } },\n summary: { type: \"string\" },\n },\n required: [\n \"status\",\n \"featureDocPath\",\n \"implementationDocPath\",\n \"storyCount\",\n \"completedStories\",\n \"summary\",\n ],\n additionalProperties: false,\n});\n\nexport function extractStoryTitle(storyContent: string, fallbackIndex: number): string {\n const heading = /^#{1,6}\\s+(.+)$/m.exec(storyContent);\n return heading?.[1]?.trim() || `Story ${fallbackIndex}`;\n}\n","import { step } from \"@trailstep/authoring\";\nimport { createFeatureDocStep } from \"../../feature-implementation/create-feature-doc/step.js\";\nimport { takeItAwayInput } from \"../../feature-implementation/shared/input-schema.js\";\nimport { grillPrompt } from \"./prompt.js\";\n\nexport const grillStep = step({ id: \"grill\" })\n .prompt(grillPrompt, {\n agent: \"grillingAgent\",\n mode: \"interactive\",\n output: takeItAwayInput,\n })\n .do((output) => createFeatureDocStep(output));\n","import { Document, state, step } from \"@trailstep/authoring\";\nimport { createOrImproveImplementationDocStep } from \"../create-or-improve-implementation-doc/step.js\";\nimport { createFeatureDocPrompt } from \"./prompt.js\";\n\nexport const createFeatureDocStep = step({ id: \"create-feature-doc\" })\n .prompt(createFeatureDocPrompt, {\n agent: \"featureWriter\",\n output: Document,\n })\n .do(async (featureDoc) => {\n await state.set(\"featureDoc\", featureDoc);\n return createOrImproveImplementationDocStep({ featureDoc, attempt: 1 });\n });\n","import { Document, state, step } from \"@trailstep/authoring\";\nimport { reviewImplementationDocStep } from \"../review-implementation-doc/step.js\";\nimport { createOrImproveImplementationDocPrompt } from \"./prompt.js\";\n\nexport const createOrImproveImplementationDocStep = step({\n id: \"create-or-improve-implementation-doc\",\n})\n .prompt(createOrImproveImplementationDocPrompt, {\n agent: \"planner\",\n output: Document,\n })\n .do(async (implementationDoc, input) => {\n await state.set(\"implementationDoc\", implementationDoc);\n return reviewImplementationDocStep({\n featureDoc: input.featureDoc,\n implementationDoc,\n attempt: input.attempt,\n });\n });\n","import { fail, step } from \"@trailstep/authoring\";\nimport { createOrImproveImplementationDocStep } from \"../create-or-improve-implementation-doc/step.js\";\nimport { MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS } from \"../shared/constants.js\";\nimport type { ReviewResult } from \"../shared/review-schema.js\";\nimport { reviewOutput, reviewPasses } from \"../shared/review-schema.js\";\nimport { splitImplementationStoriesStep } from \"../split-implementation-stories/step.js\";\nimport { type ReviewImplementationDocInput, reviewImplementationDocPrompt } from \"./prompt.js\";\n\nexport const reviewImplementationDocStep = step({ id: \"review-implementation-doc\" })\n .prompt<ReviewImplementationDocInput, ReviewResult>(reviewImplementationDocPrompt, {\n agent: \"reviewer\",\n output: reviewOutput,\n })\n .do((review, input) => {\n if (reviewPasses(review)) {\n return splitImplementationStoriesStep({ implementationDoc: input.implementationDoc });\n }\n\n if (input.attempt >= MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS) {\n return fail({\n code: \"implementation_doc_review_exhausted\",\n message: `implementation-doc.md failed review ${MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS} times in a row (last score ${review.score}/5).`,\n details: { review, implementationDocPath: input.implementationDoc.path },\n });\n }\n\n return createOrImproveImplementationDocStep({\n featureDoc: input.featureDoc,\n previousReview: review,\n attempt: input.attempt + 1,\n });\n });\n","export const STORY_BOUNDARY = \"<!-- trailstep-story-boundary -->\";\n\nexport const STORY_CONTEXT_START = \"<context>\";\n\nexport const STORY_CONTEXT_END = \"</context>\";\n\nexport const REVIEW_PASS_THRESHOLD = 4;\n\nexport const MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS = 5;\n\nexport const MAX_STORY_REVIEW_ATTEMPTS = 3;\n","import { jsonSchema } from \"@trailstep/authoring\";\n\nimport { REVIEW_PASS_THRESHOLD } from \"./constants.js\";\n\nexport interface MethodologyRatings extends Record<string, unknown> {\n readonly tdd: number;\n readonly verticalSlicing: number;\n readonly tracerBullet: number;\n readonly dependencies: number;\n readonly architecture: number;\n}\n\nexport interface ReviewResult extends Record<string, unknown> {\n readonly score: number;\n readonly summary: string;\n readonly methodologyRatings: MethodologyRatings;\n readonly requiredImprovements: readonly string[];\n}\n\nconst RATING_SCHEMA = { type: \"integer\", minimum: 1, maximum: 5 } as const;\n\nexport const reviewOutput = jsonSchema<ReviewResult>({\n type: \"object\",\n properties: {\n score: RATING_SCHEMA,\n summary: { type: \"string\" },\n methodologyRatings: {\n type: \"object\",\n properties: {\n tdd: RATING_SCHEMA,\n verticalSlicing: RATING_SCHEMA,\n tracerBullet: RATING_SCHEMA,\n dependencies: RATING_SCHEMA,\n architecture: RATING_SCHEMA,\n },\n required: [\"tdd\", \"verticalSlicing\", \"tracerBullet\", \"dependencies\", \"architecture\"],\n additionalProperties: false,\n },\n requiredImprovements: { type: \"array\", items: { type: \"string\" } },\n },\n required: [\"score\", \"summary\", \"methodologyRatings\", \"requiredImprovements\"],\n additionalProperties: false,\n});\n\nexport function reviewPasses(review: ReviewResult): boolean {\n return review.score >= REVIEW_PASS_THRESHOLD;\n}\n","import { type Document, document, fail, state, step } from \"@trailstep/authoring\";\nimport type { ContinuationResult } from \"@trailstep/core\";\nimport { implementStoryStep } from \"../implement-story/step.js\";\nimport { STORY_BOUNDARY, STORY_CONTEXT_END, STORY_CONTEXT_START } from \"../shared/constants.js\";\nimport { recordActiveStoryStartCommit, STORY_STATE_KEYS } from \"../shared/story-state.js\";\n\nexport interface SplitImplementationStoriesInput extends Record<string, unknown> {\n readonly implementationDoc: Document;\n}\n\nexport const splitImplementationStoriesStep = step({ id: \"split-implementation-stories\" }).do(\n async ({ implementationDoc }: SplitImplementationStoriesInput): Promise<ContinuationResult> => {\n const contextStartCount = countOccurrences(implementationDoc.content, STORY_CONTEXT_START);\n const contextEndCount = countOccurrences(implementationDoc.content, STORY_CONTEXT_END);\n\n if (contextStartCount !== contextEndCount) {\n return fail({\n code: \"unbalanced_story_context\",\n message: `Found ${contextStartCount} \\`${STORY_CONTEXT_START}\\` marker(s) and ${contextEndCount} \\`${STORY_CONTEXT_END}\\` marker(s) in implementation-doc.md. Context blocks must be balanced.`,\n details: { implementationDocPath: implementationDoc.path },\n });\n }\n\n const contextMatches = Array.from(implementationDoc.content.matchAll(storyContextPattern()));\n if (contextMatches.length !== contextStartCount) {\n return fail({\n code: \"invalid_story_context_order\",\n message: `Every \\`${STORY_CONTEXT_START}\\` marker in implementation-doc.md must be followed by a matching \\`${STORY_CONTEXT_END}\\` marker.`,\n details: { implementationDocPath: implementationDoc.path },\n });\n }\n\n const contextBlocks = contextMatches\n .map(([, context]) => context?.trim() ?? \"\")\n .filter((context) => context.length > 0);\n const sharedContext = contextBlocks.join(\"\\n\\n---\\n\\n\");\n const contentWithoutContextBlocks = implementationDoc.content.replace(\n storyContextPattern(),\n \"\",\n );\n\n const chunks = contentWithoutContextBlocks\n .split(STORY_BOUNDARY)\n .slice(1) // drop overview-only text before the first boundary; <context> blocks are prepended below\n .map((chunk) => chunk.trim())\n .filter((chunk) => chunk.length > 0)\n .map((chunk) => prependSharedContext(chunk, sharedContext));\n\n if (chunks.length === 0) {\n return fail({\n code: \"no_stories_found\",\n message: `No \\`${STORY_BOUNDARY}\\`-delimited stories found in implementation-doc.md.`,\n details: { implementationDocPath: implementationDoc.path },\n });\n }\n\n const storyDocs: Document[] = [];\n for (const chunk of chunks) {\n storyDocs.push(await document(chunk));\n }\n\n // storyDocs.length === chunks.length, already guarded above to be > 0.\n const [firstStory, ...remaining] = storyDocs as [Document, ...Document[]];\n await state.set(STORY_STATE_KEYS.storyQueue, remaining);\n await state.set(STORY_STATE_KEYS.completedStories, []);\n await state.set(STORY_STATE_KEYS.activeStory, firstStory);\n await recordActiveStoryStartCommit();\n\n return implementStoryStep({ currentStory: firstStory, attempt: 1 });\n },\n);\n\nfunction countOccurrences(value: string, needle: string): number {\n if (needle.length === 0) {\n return 0;\n }\n\n let count = 0;\n let offset = 0;\n while (true) {\n const index = value.indexOf(needle, offset);\n if (index === -1) {\n return count;\n }\n\n count += 1;\n offset = index + needle.length;\n }\n}\n\nfunction storyContextPattern(): RegExp {\n return new RegExp(\n `${escapeRegExp(STORY_CONTEXT_START)}([\\\\s\\\\S]*?)${escapeRegExp(STORY_CONTEXT_END)}`,\n \"g\",\n );\n}\n\nfunction prependSharedContext(story: string, sharedContext: string): string {\n if (sharedContext.length === 0) {\n return story;\n }\n\n return [\"## Shared implementation context\", sharedContext, \"## Active story\", story].join(\"\\n\\n\");\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { fail, step } from \"@trailstep/authoring\";\nimport { reviewStoryImplementationStep } from \"../review-story-implementation/step.js\";\nimport { loadStoryReviewGitContext } from \"../shared/story-state.js\";\nimport {\n type ImplementStoryInput,\n type ImplementStoryOutput,\n implementStoryOutput,\n implementStoryPrompt,\n} from \"./prompt.js\";\n\nexport const implementStoryStep = step({ id: \"implement-story\" })\n .prompt<ImplementStoryInput, ImplementStoryOutput>(implementStoryPrompt, {\n agent: \"implementer\",\n output: implementStoryOutput,\n })\n .do(async (promptOutput, input) => {\n if (promptOutput.blocked) {\n return fail({\n code: \"story_blocked\",\n message: promptOutput.blockedReason ?? \"Story implementation reported a blocked state.\",\n details: { storyPath: input.currentStory.path },\n });\n }\n\n return reviewStoryImplementationStep({\n currentStory: input.currentStory,\n attempt: input.attempt,\n implementationSummary: promptOutput.summary,\n gitContext: await loadStoryReviewGitContext(),\n });\n });\n","import { fail, step } from \"@trailstep/authoring\";\nimport { commitReviewedStoryStep } from \"../commit-reviewed-story/step.js\";\nimport { implementStoryStep } from \"../implement-story/step.js\";\nimport { MAX_STORY_REVIEW_ATTEMPTS } from \"../shared/constants.js\";\nimport type { ReviewResult } from \"../shared/review-schema.js\";\nimport { reviewOutput, reviewPasses } from \"../shared/review-schema.js\";\nimport { type ReviewStoryImplementationInput, reviewStoryImplementationPrompt } from \"./prompt.js\";\n\nexport const reviewStoryImplementationStep = step({ id: \"review-story-implementation\" })\n .prompt<ReviewStoryImplementationInput, ReviewResult>(reviewStoryImplementationPrompt, {\n agent: \"reviewer\",\n output: reviewOutput,\n })\n .do(async (review, input) => {\n if (!reviewPasses(review)) {\n if (input.attempt >= MAX_STORY_REVIEW_ATTEMPTS) {\n return fail({\n code: \"story_review_exhausted\",\n message: `Story failed review ${MAX_STORY_REVIEW_ATTEMPTS} times in a row (last score ${review.score}/5).`,\n details: { review, storyPath: input.currentStory.path },\n });\n }\n\n return implementStoryStep({\n currentStory: input.currentStory,\n previousStoryReview: review,\n attempt: input.attempt + 1,\n });\n }\n\n return commitReviewedStoryStep({\n currentStory: input.currentStory,\n implementationSummary: input.implementationSummary,\n });\n });\n","import type { Document } from \"@trailstep/authoring\";\nimport { done, fail, state, step } from \"@trailstep/authoring\";\nimport type { ContinuationResult } from \"@trailstep/core\";\nimport { implementStoryStep } from \"../implement-story/step.js\";\nimport { extractStoryTitle, type TakeItAwayOutput } from \"../shared/output-schema.js\";\nimport {\n type ActiveStoryStartCommit,\n recordActiveStoryStartCommit,\n resetActiveStoryStartCommit,\n STORY_STATE_KEYS,\n} from \"../shared/story-state.js\";\nimport { runGit } from \"./run-git.js\";\n\nexport interface CommitReviewedStoryInput extends Record<string, unknown> {\n readonly currentStory: Document;\n readonly implementationSummary?: string;\n}\n\nexport const commitReviewedStoryStep = step({ id: \"commit-reviewed-story\" }).do(\n async (input: CommitReviewedStoryInput): Promise<ContinuationResult> => {\n const activeStory =\n (await state.get<Document | null>(STORY_STATE_KEYS.activeStory)) ?? input.currentStory;\n\n if (storyAutoCommitEnabled()) {\n const commitResult = await commitReviewedStoryChanges(\n activeStory,\n input.implementationSummary,\n );\n if (!commitResult.ok) {\n return fail({\n code: commitResult.code,\n message: commitResult.message,\n details: commitResult.details,\n });\n }\n }\n\n return completeReviewedStory(activeStory);\n },\n);\n\nfunction storyAutoCommitEnabled(): boolean {\n const mode = process.env.TRAILSTEP_STORY_COMMIT_MODE;\n return [\"1\", \"true\", \"enabled\", \"worktree\"].includes(mode?.toLowerCase() ?? \"\");\n}\n\nasync function commitReviewedStoryChanges(\n activeStory: Document,\n implementationSummary: string | undefined,\n): Promise<\n | { readonly ok: true }\n | {\n readonly ok: false;\n readonly code: string;\n readonly message: string;\n readonly details: Record<string, unknown>;\n }\n> {\n const cwd = state.cwd;\n if (!cwd) {\n return {\n ok: false,\n code: \"story_commit_missing_cwd\",\n message: \"Cannot commit reviewed story because the TrailStep workflow cwd is unavailable.\",\n details: { storyPath: activeStory.path },\n };\n }\n\n const insideWorkTree = await runGit([\"rev-parse\", \"--is-inside-work-tree\"], cwd);\n if (!insideWorkTree.ok || insideWorkTree.stdout !== \"true\") {\n return {\n ok: false,\n code: \"story_commit_not_git_worktree\",\n message:\n \"Cannot commit reviewed story because the workflow is not running inside a git worktree.\",\n details: {\n storyPath: activeStory.path,\n gitError: insideWorkTree.ok ? undefined : insideWorkTree.error,\n },\n };\n }\n\n const add = await runGit([\"add\", \"-A\"], cwd);\n if (!add.ok) {\n return {\n ok: false,\n code: \"story_commit_stage_failed\",\n message: \"Cannot commit reviewed story because `git add -A` failed.\",\n details: { storyPath: activeStory.path, gitError: add.error },\n };\n }\n\n const staged = await runGit([\"diff\", \"--cached\", \"--name-only\"], cwd);\n if (!staged.ok) {\n return {\n ok: false,\n code: \"story_commit_status_failed\",\n message: \"Cannot commit reviewed story because staged changes could not be inspected.\",\n details: { storyPath: activeStory.path, gitError: staged.error },\n };\n }\n\n if (staged.stdout.trim().length === 0) {\n const baseline = await state.get<ActiveStoryStartCommit | null>(\n STORY_STATE_KEYS.activeStoryStartCommit,\n );\n const head = await runGit([\"rev-parse\", \"HEAD\"], cwd);\n if (baseline?.commit && head.ok && head.stdout !== baseline.commit) {\n return { ok: true };\n }\n\n return {\n ok: false,\n code: \"story_commit_empty\",\n message:\n \"The story passed review, but there are no staged or already-committed changes since the story baseline to commit.\",\n details: {\n storyPath: activeStory.path,\n storyStartCommit: baseline?.commit,\n head: head.ok ? head.stdout : undefined,\n gitError: head.ok ? undefined : head.error,\n },\n };\n }\n\n const storyTitle = extractStoryTitle(activeStory.content, 1);\n const commit = await runGit(\n [\n \"commit\",\n \"-m\",\n truncateCommitSubject(`trailstep: ${storyTitle}`),\n \"-m\",\n commitBody(activeStory, implementationSummary, staged.stdout),\n ],\n cwd,\n );\n\n if (!commit.ok) {\n return {\n ok: false,\n code: \"story_commit_failed\",\n message: \"The story passed review, but TrailStep could not create the story commit.\",\n details: { storyPath: activeStory.path, gitError: commit.error },\n };\n }\n\n return { ok: true };\n}\n\nasync function completeReviewedStory(activeStory: Document): Promise<ContinuationResult> {\n const completed = (await state.get<string[]>(STORY_STATE_KEYS.completedStories)) ?? [];\n const updatedCompleted = [\n ...completed,\n extractStoryTitle(activeStory.content, completed.length + 1),\n ];\n await state.set(STORY_STATE_KEYS.completedStories, updatedCompleted);\n\n const storyQueue = (await state.get<Document[]>(STORY_STATE_KEYS.storyQueue)) ?? [];\n const [nextStory, ...remaining] = storyQueue;\n\n if (!nextStory) {\n await state.set(STORY_STATE_KEYS.activeStory, null);\n await resetActiveStoryStartCommit();\n const featureDoc = await state.get<Document>(\"featureDoc\");\n const implementationDoc = await state.get<Document>(\"implementationDoc\");\n const output: TakeItAwayOutput = {\n status: \"implemented\",\n featureDocPath: featureDoc?.path ?? \"\",\n implementationDocPath: implementationDoc?.path ?? \"\",\n storyCount: updatedCompleted.length,\n completedStories: updatedCompleted,\n summary: `Implemented and reviewed ${updatedCompleted.length} ${updatedCompleted.length === 1 ? \"story\" : \"stories\"}.`,\n };\n return done(output);\n }\n\n await state.set(STORY_STATE_KEYS.storyQueue, remaining);\n await state.set(STORY_STATE_KEYS.activeStory, nextStory);\n await resetActiveStoryStartCommit();\n await recordActiveStoryStartCommit();\n return implementStoryStep({ currentStory: nextStory, attempt: 1 });\n}\n\nfunction truncateCommitSubject(subject: string): string {\n return subject.length <= 72 ? subject : `${subject.slice(0, 69)}...`;\n}\n\nfunction commitBody(\n activeStory: Document,\n implementationSummary: string | undefined,\n stagedFiles: string,\n): string {\n return [\n \"Committed automatically by TrailStep after a passing story review.\",\n \"\",\n `Story artifact: ${activeStory.path}`,\n \"\",\n \"Implementer summary:\",\n implementationSummary?.trim() || \"Not provided.\",\n \"\",\n \"Staged files:\",\n stagedFiles\n .split(\"\\n\")\n .filter((file) => file.length > 0)\n .map((file) => `- ${file}`)\n .join(\"\\n\"),\n ].join(\"\\n\");\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { state } from \"@trailstep/authoring\";\n\nconst execFileAsync = promisify(execFile);\n\nexport const STORY_STATE_KEYS = {\n activeStory: \"activeStory\",\n activeStoryStartCommit: \"activeStoryStartCommit\",\n completedStories: \"completedStories\",\n storyQueue: \"storyQueue\",\n} as const;\n\nexport interface ActiveStoryStartCommit {\n readonly commit?: string;\n readonly warning?: string;\n}\n\nexport interface StoryReviewGitContext {\n readonly storyStartCommit?: string;\n readonly statusShort?: string;\n readonly committedDiff?: string;\n readonly uncommittedDiff?: string;\n readonly warnings: readonly string[];\n}\n\nasync function runGit(\n args: readonly string[],\n cwd: string,\n): Promise<\n { readonly ok: true; readonly stdout: string } | { readonly ok: false; readonly error: string }\n> {\n try {\n const { stdout } = await execFileAsync(\"git\", [...args], {\n cwd,\n maxBuffer: 1024 * 1024 * 10,\n });\n return { ok: true, stdout: stdout.trimEnd() };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { ok: false, error: message };\n }\n}\n\nexport async function recordActiveStoryStartCommit(): Promise<ActiveStoryStartCommit> {\n const existing = await state.get<ActiveStoryStartCommit | null>(\n STORY_STATE_KEYS.activeStoryStartCommit,\n );\n if (existing?.commit || existing?.warning) {\n return existing;\n }\n\n const cwd = state.cwd;\n if (!cwd) {\n const missingCwd = {\n warning:\n \"Workflow cwd is unavailable; the story reviewer cannot verify committed story changes from a durable git baseline.\",\n };\n await state.set(STORY_STATE_KEYS.activeStoryStartCommit, missingCwd);\n return missingCwd;\n }\n\n const result = await runGit([\"rev-parse\", \"HEAD\"], cwd);\n if (!result.ok || result.stdout.length === 0) {\n const missingGitBaseline = {\n warning: `Unable to record story start commit with \\`git rev-parse HEAD\\`: ${result.ok ? \"no commit was returned\" : result.error}`,\n };\n await state.set(STORY_STATE_KEYS.activeStoryStartCommit, missingGitBaseline);\n return missingGitBaseline;\n }\n\n const baseline = { commit: result.stdout };\n await state.set(STORY_STATE_KEYS.activeStoryStartCommit, baseline);\n return baseline;\n}\n\nexport async function resetActiveStoryStartCommit(): Promise<void> {\n await state.set(STORY_STATE_KEYS.activeStoryStartCommit, null);\n}\n\nexport async function loadStoryReviewGitContext(): Promise<StoryReviewGitContext> {\n const baseline = await state.get<ActiveStoryStartCommit | null>(\n STORY_STATE_KEYS.activeStoryStartCommit,\n );\n const warnings: string[] = [];\n\n if (baseline?.warning) {\n warnings.push(baseline.warning);\n }\n\n const cwd = state.cwd;\n if (!cwd) {\n warnings.push(\n \"Workflow cwd is unavailable; reviewer must treat committed story-change context as missing.\",\n );\n return { storyStartCommit: baseline?.commit, warnings };\n }\n\n const status = await runGit([\"status\", \"--short\"], cwd);\n if (!status.ok) {\n warnings.push(`Unable to inspect \\`git status --short\\`: ${status.error}`);\n }\n\n const uncommittedDiff = await runGit([\"diff\"], cwd);\n if (!uncommittedDiff.ok) {\n warnings.push(\n `Unable to inspect uncommitted changes with \\`git diff\\`: ${uncommittedDiff.error}`,\n );\n }\n\n const storyStartCommit = baseline?.commit;\n let committedDiff: string | undefined;\n if (!storyStartCommit) {\n warnings.push(\n \"No durable story start commit is recorded; reviewer must not silently review only the current working tree diff.\",\n );\n } else {\n const baselineReachable = await runGit([\"cat-file\", \"-e\", `${storyStartCommit}^{commit}`], cwd);\n if (!baselineReachable.ok) {\n warnings.push(\n `Recorded story start commit \\`${storyStartCommit}\\` is invalid or unreachable: ${baselineReachable.error}`,\n );\n } else {\n const committed = await runGit([\"diff\", `${storyStartCommit}..HEAD`], cwd);\n if (!committed.ok) {\n warnings.push(\n `Unable to inspect committed story changes with \\`git diff ${storyStartCommit}..HEAD\\`: ${committed.error}`,\n );\n } else {\n committedDiff = committed.stdout;\n }\n }\n }\n\n return {\n storyStartCommit,\n statusShort: status.ok ? status.stdout : undefined,\n committedDiff,\n uncommittedDiff: uncommittedDiff.ok ? uncommittedDiff.stdout : undefined,\n warnings,\n };\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nexport type GitResult =\n | { readonly ok: true; readonly stdout: string }\n | { readonly ok: false; readonly error: string };\n\nexport async function runGit(args: readonly string[], cwd: string): Promise<GitResult> {\n try {\n const { stdout } = await execFileAsync(\"git\", [...args], {\n cwd,\n maxBuffer: 1024 * 1024 * 10,\n });\n return { ok: true, stdout: stdout.trimEnd() };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { ok: false, error: message };\n }\n}\n","import type { Document } from \"@trailstep/authoring\";\nimport { loadFragments, promptSections, section } from \"@trailstep/authoring\";\nimport type { StoryReviewGitContext } from \"../shared/story-state.js\";\n\nconst fragments = loadFragments(import.meta.dirname, {\n methodology: \"../shared/feature-methodology.md\",\n storyContract: \"../shared/story-implementation-contract.md\",\n});\n\nexport interface ReviewStoryImplementationInput extends Record<string, unknown> {\n readonly currentStory: Document;\n readonly attempt: number;\n readonly implementationSummary?: string;\n readonly gitContext: StoryReviewGitContext;\n}\n\nexport function reviewStoryImplementationPrompt({\n input,\n}: {\n readonly input: ReviewStoryImplementationInput;\n}): string {\n const committedDiffCommand = input.gitContext.storyStartCommit\n ? `git diff ${input.gitContext.storyStartCommit}..HEAD`\n : \"git diff <missing-story-start-commit>..HEAD\";\n\n return promptSections(\n fragments.methodology,\n fragments.storyContract,\n section(\"Story\", input.currentStory.content),\n section(\"Implementer summary\", input.implementationSummary ?? \"Not provided.\"),\n section(\n \"Story review git baseline\",\n [\n `Recorded story start commit: ${input.gitContext.storyStartCommit ?? \"MISSING\"}`,\n `Inspect committed story changes with: \\`${committedDiffCommand}\\``,\n \"Inspect uncommitted working tree changes with: `git diff`\",\n \"Inspect status with: `git status --short`\",\n input.gitContext.warnings.length > 0\n ? [\n \"\",\n \"Warnings / blocked context:\",\n ...input.gitContext.warnings.map((warning) => `- ${warning}`),\n ].join(\"\\n\")\n : \"\",\n ]\n .filter((line) => line.length > 0)\n .join(\"\\n\"),\n ),\n section(\n \"Committed story changes (`git diff <storyStartCommit>..HEAD`)\",\n input.gitContext.committedDiff ?? \"Not available.\",\n ),\n section(\n \"Uncommitted working tree changes (`git diff`)\",\n input.gitContext.uncommittedDiff ?? \"Not available.\",\n ),\n section(\n \"Working tree status (`git status --short`)\",\n input.gitContext.statusShort ?? \"Not available.\",\n ),\n section(\n \"Task\",\n \"Review only the active story slice represented by the recorded story start commit through HEAD, plus the current uncommitted working tree changes, against the story above and the implementer summary. Prefer the supplied diff/status sections; if you need to inspect locally, use read-only commands only (`git status --short`, the listed `git diff <storyStartCommit>..HEAD`, and `git diff`) — do not run tests, edit code, revert files, clean files, or try to isolate the diff by changing the repository. If unrelated dirty files, a missing/invalid baseline, or ambiguous context prevents a trustworthy review, say so in the structured review and require the implementer/workflow to fix the isolation; never remove changes yourself. Respond only with the structured review.\",\n ),\n );\n}\n","import {\n type Document,\n jsonSchema,\n list,\n loadFragments,\n promptSections,\n section,\n} from \"@trailstep/authoring\";\nimport type { ReviewResult } from \"../shared/review-schema.js\";\n\nconst fragments = loadFragments(import.meta.dirname, {\n methodology: \"../shared/feature-methodology.md\",\n architectureGuidance: \"../shared/project-architecture-guidance.md\",\n storyContract: \"../shared/story-implementation-contract.md\",\n});\n\nexport interface ImplementStoryInput extends Record<string, unknown> {\n readonly currentStory: Document;\n readonly attempt: number;\n readonly previousStoryReview?: ReviewResult;\n}\n\nexport interface ImplementStoryOutput extends Record<string, unknown> {\n readonly blocked: boolean;\n readonly blockedReason?: string;\n readonly summary: string;\n}\n\nexport const implementStoryOutput = jsonSchema<ImplementStoryOutput>({\n type: \"object\",\n properties: {\n blocked: { type: \"boolean\" },\n blockedReason: { type: \"string\" },\n summary: {\n type: \"string\",\n description: \"Files changed, commands run, and their results.\",\n },\n },\n required: [\"blocked\", \"summary\"],\n additionalProperties: false,\n});\n\nexport function implementStoryPrompt({ input }: { readonly input: ImplementStoryInput }): string {\n const taskBody =\n input.previousStoryReview === undefined\n ? \"Implement this story.\"\n : promptSections(\n `This is retry attempt ${input.attempt} for this story. A previous review scored the implementation ${input.previousStoryReview.score}/5: ${input.previousStoryReview.summary}`,\n `Required improvements to address, without discarding what already works:\\n\\n${list(input.previousStoryReview.requiredImprovements)}`,\n );\n\n return promptSections(\n fragments.methodology,\n fragments.architectureGuidance,\n fragments.storyContract,\n section(\"Story\", input.currentStory.content),\n section(\n \"Task\",\n `${taskBody}\\n\\nSet \\`blocked: true\\` with a clear \\`blockedReason\\` instead of guessing or pretending the story is complete. Otherwise set \\`blocked: false\\` and use \\`summary\\` to report files changed, commands run, and their results.`,\n ),\n );\n}\n","import type { Document } from \"@trailstep/authoring\";\nimport { list, loadFragments, promptSections, section } from \"@trailstep/authoring\";\n\nconst fragments = loadFragments(import.meta.dirname, {\n methodology: \"../shared/feature-methodology.md\",\n implementationDocFormat: \"../shared/implementation-doc-format.md\",\n});\n\nexport interface ReviewImplementationDocInput extends Record<string, unknown> {\n readonly featureDoc: Document;\n readonly implementationDoc: Document;\n readonly attempt: number;\n}\n\nexport function reviewImplementationDocPrompt({\n input,\n}: {\n readonly input: ReviewImplementationDocInput;\n}): string {\n const reviewCriteria = list([\n \"traceability to the feature doc\",\n \"strict behavioral-red TDD story design\",\n \"vertical slices rather than horizontal tasks\",\n \"a clear tracer-bullet strategy\",\n \"hard dependency labels and topological ordering\",\n \"acceptance criteria and validation commands\",\n \"story instructions self-contained enough for an implementer who will only see one story plus the prepended shared context at a time\",\n \"correct use of balanced `<context>` ... `</context>` blocks for shared implementer context\",\n \"correct use of the `<!-- trailstep-story-boundary -->` marker between stories, and that no story-critical detail lives only in non-context overview text above the first marker\",\n ]);\n\n return promptSections(\n fragments.methodology,\n fragments.implementationDocFormat,\n section(\"Feature doc\", input.featureDoc.content),\n section(\"Implementation doc under review\", input.implementationDoc.content),\n section(\n \"Task\",\n `Critically review the implementation doc above against the feature doc and the methodology. Review for:\\n\\n${reviewCriteria}\\n\\nDo not edit the implementation doc. Respond only with the structured review.`,\n ),\n );\n}\n","import type { Document } from \"@trailstep/authoring\";\nimport { list, loadFragments, promptSections, section } from \"@trailstep/authoring\";\nimport type { ReviewResult } from \"../shared/review-schema.js\";\n\nconst fragments = loadFragments(import.meta.dirname, {\n methodology: \"../shared/feature-methodology.md\",\n architectureGuidance: \"../shared/project-architecture-guidance.md\",\n implementationDocFormat: \"../shared/implementation-doc-format.md\",\n});\n\nexport interface CreateOrImproveImplementationDocInput extends Record<string, unknown> {\n readonly featureDoc: Document;\n readonly previousReview?: ReviewResult;\n readonly attempt: number;\n}\n\nexport function createOrImproveImplementationDocPrompt({\n input,\n}: {\n readonly input: CreateOrImproveImplementationDocInput;\n}): string {\n const taskBody =\n input.previousReview === undefined\n ? \"Create `implementation-doc.md` from the feature doc above, following the format exactly enough that stories can later be split mechanically on the `<!-- trailstep-story-boundary -->` marker and shared context can be prepended from balanced `<context>` blocks.\"\n : promptSections(\n `This is improvement attempt ${input.attempt}. A previous review scored this plan ${input.previousReview.score}/5: ${input.previousReview.summary}`,\n `Required improvements to address, without discarding what already works:\\n\\n${list(input.previousReview.requiredImprovements)}`,\n );\n\n return promptSections(\n fragments.methodology,\n fragments.architectureGuidance,\n fragments.implementationDocFormat,\n section(\"Feature doc\", input.featureDoc.content),\n section(\n \"Task\",\n `${taskBody}\\n\\nDesign stories around strict behavioral-red TDD, vertical slicing, tracer-bullet methodology, and hard dependency labels only. Topologically order stories by dependency. Put shared implementation details in balanced \\`<context>\\` ... \\`</context>\\` blocks so split-stories prepends them to every story; repeat details inside individual stories when only selected stories need them. Write every story as implementation-ready instructions for an implementer who will only see that story plus the prepended context.`,\n ),\n );\n}\n","import { loadFragments, promptSections, section } from \"@trailstep/authoring\";\nimport type { TakeItAwayInput } from \"../shared/input-schema.js\";\n\nconst fragments = loadFragments(import.meta.dirname, {\n methodology: \"../shared/feature-methodology.md\",\n featureDocFormat: \"../shared/feature-doc-format.md\",\n});\n\nexport function createFeatureDocPrompt({ input }: { readonly input: TakeItAwayInput }): string {\n return promptSections(\n fragments.methodology,\n fragments.featureDocFormat,\n section(\"Conversation / feature request\", input.conversation),\n section(\n \"Task\",\n 'Write `feature-doc.md` following the format above, based on the conversation/request. Make it detailed enough that another agent can plan implementation without reading the original conversation. Preserve uncertainty explicitly in \"Open Questions and Assumptions\" — do not invent missing product decisions.',\n ),\n );\n}\n","import { shape } from \"@trailstep/authoring\";\n\nexport interface TakeItAwayInput extends Record<string, unknown> {\n readonly conversation: string;\n}\n\nexport const takeItAwayInput = shape<TakeItAwayInput>({ conversation: \"string\" });\n","import { promptSections, section } from \"@trailstep/authoring\";\n\nexport function grillPrompt(): string {\n return promptSections(\n section(\n \"Role\",\n \"You are the interactive front door for starting a new TrailStep feature. Open by asking the user what they want to build.\",\n ),\n section(\n \"Task\",\n \"Keep asking follow-up questions until you are confident you understand the feature request well enough to hand off to a planning agent. There is no fixed checklist of required topics — use your judgment, and do not end the conversation prematurely just to finish quickly.\",\n ),\n section(\n \"Output\",\n \"Your structured output's `conversation` field must contain the full raw transcript of the conversation so far, not a summary. Preserve as much of the actual back-and-forth as possible.\",\n ),\n );\n}\n","import { defineWorkflow } from \"@trailstep/authoring\";\nimport { createFeatureDocStep } from \"../feature-implementation/create-feature-doc/step.js\";\nimport { takeItAwayInput } from \"../feature-implementation/shared/input-schema.js\";\nimport { takeItAwayOutput } from \"../feature-implementation/shared/output-schema.js\";\n\nexport type { TakeItAwayInput } from \"../feature-implementation/shared/input-schema.js\";\n\nexport const takeItAway = defineWorkflow({\n id: \"take-it-away\",\n description:\n \"Turns an already-organic conversation/feature request into a reviewed implementation plan, then implements it story by story.\",\n inputShape: takeItAwayInput,\n outputShape: takeItAwayOutput,\n agents: {\n featureWriter: {\n size: \"medium\",\n thinking: \"medium\",\n description: \"Turns a conversation/feature request into a standalone feature doc.\",\n },\n planner: {\n size: \"large\",\n thinking: \"high\",\n description:\n \"Creates or improves the implementation doc: architecture-aware, TDD/vertical-slice/tracer-bullet story design.\",\n },\n reviewer: {\n size: \"large\",\n thinking: \"high\",\n description: \"Reviews isolated implementation docs and story diffs against methodology.\",\n },\n implementer: {\n size: \"medium\",\n thinking: \"medium\",\n description: \"Implements one story at a time using strict behavioral-red TDD.\",\n },\n },\n start(input) {\n return createFeatureDocStep(input);\n },\n});\n"],"mappings":"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","names":["shape","step","Document","state","step","state","step","fail","step","jsonSchema","jsonSchema","fail","state","step","fail","step","fail","step","state","execFile","promisify","execFileAsync","runGit","state","runGit","step","fail","jsonSchema","loadFragments","promptSections","section","fragments","step","fail","step","fail","state","list","loadFragments","promptSections","section","fragments","step","fail","list","loadFragments","promptSections","section","fragments","step","state","loadFragments","promptSections","section","fragments","step","Document","state","promptSections","section","step","shape","defineWorkflow","defineWorkflow"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trailstep/create-flows",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Public reusable general-purpose TrailStep workflows.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+ssh://git@github.com/chily-john/trailstep.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/chily-john/trailstep/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/chily-john/trailstep#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"trailstep-workflow"
|
|
17
|
+
],
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@trailstep/authoring": "0.1.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@biomejs/biome": "^2.1.1",
|
|
31
|
+
"@types/node": "^26.1.1",
|
|
32
|
+
"tsup": "^8.5.0",
|
|
33
|
+
"typescript": "^5.8.3",
|
|
34
|
+
"vitest": "^3.2.4",
|
|
35
|
+
"@trailstep/core": "0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@trailstep/authoring": "^0.1.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup src/index.ts --format esm --dts --sourcemap --clean",
|
|
50
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"lint": "biome check src"
|
|
53
|
+
}
|
|
54
|
+
}
|