@markjaquith/agency 2.30.1 → 2.31.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/README.md +6 -0
- package/package.json +1 -1
- package/src/services/EpicService.test.ts +3 -0
- package/src/services/EpicService.ts +2 -1
- package/src/services/PhaseService.ts +3 -2
- package/src/services/PullRequestService.test.ts +4 -1
- package/src/services/TaskPhaseService.test.ts +15 -2
- package/src/services/TaskService.ts +2 -1
- package/src/workbase/frontmatter.ts +17 -0
package/README.md
CHANGED
|
@@ -37,6 +37,12 @@ For development, run `bun link` from this repository.
|
|
|
37
37
|
Entity IDs come from directory names. Structured metadata lives in YAML 1.2
|
|
38
38
|
frontmatter; prose below it supplies human and agent context.
|
|
39
39
|
|
|
40
|
+
New epic, task, and phase documents use the same core prose sections:
|
|
41
|
+
`Outcome` states the intended result, `Plan` describes the current approach, and
|
|
42
|
+
`Important Decisions` preserves consequential choices and their rationale. These
|
|
43
|
+
sections are creation defaults rather than validation requirements, so existing
|
|
44
|
+
and customized documents remain valid.
|
|
45
|
+
|
|
40
46
|
## Workbase Layout
|
|
41
47
|
|
|
42
48
|
```text
|
package/package.json
CHANGED
|
@@ -34,6 +34,9 @@ describe("EpicService", () => {
|
|
|
34
34
|
|
|
35
35
|
expect(created.content).toContain("ticketUrl:")
|
|
36
36
|
expect(created.content).toContain("tasks: []")
|
|
37
|
+
expect(created.content).toContain(
|
|
38
|
+
"# Workspace Orchestration\n\n## Outcome\n\nDescribe the epic outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions\n\nRecord consequential decisions and their rationale.",
|
|
39
|
+
)
|
|
37
40
|
|
|
38
41
|
const records = await runTestEffect(
|
|
39
42
|
EpicService.pipe(Effect.flatMap((service) => service.list(root))),
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../workbase/schemas"
|
|
11
11
|
import {
|
|
12
12
|
formatMarkdownDocument,
|
|
13
|
+
formatWorkDocumentBody,
|
|
13
14
|
parseFrontmatter,
|
|
14
15
|
} from "../workbase/frontmatter"
|
|
15
16
|
import { documentRevision } from "../workbase/document-revision"
|
|
@@ -95,7 +96,7 @@ export class EpicService extends Effect.Service<EpicService>()("EpicService", {
|
|
|
95
96
|
.join(" ")
|
|
96
97
|
const content = formatMarkdownDocument(
|
|
97
98
|
data,
|
|
98
|
-
|
|
99
|
+
formatWorkDocumentBody(title, "epic"),
|
|
99
100
|
)
|
|
100
101
|
yield* fs.writeFile(path, content)
|
|
101
102
|
return {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "../workbase/schemas"
|
|
15
15
|
import {
|
|
16
16
|
formatMarkdownDocument,
|
|
17
|
+
formatWorkDocumentBody,
|
|
17
18
|
parseFrontmatter,
|
|
18
19
|
} from "../workbase/frontmatter"
|
|
19
20
|
import { canTransitionStatus } from "../readiness"
|
|
@@ -196,7 +197,7 @@ export class PhaseService extends Effect.Service<PhaseService>()(
|
|
|
196
197
|
.join(" ")
|
|
197
198
|
const content = formatMarkdownDocument(
|
|
198
199
|
data,
|
|
199
|
-
|
|
200
|
+
formatWorkDocumentBody(title, "phase"),
|
|
200
201
|
)
|
|
201
202
|
|
|
202
203
|
if (!isMultiPhase) {
|
|
@@ -246,7 +247,7 @@ export class PhaseService extends Effect.Service<PhaseService>()(
|
|
|
246
247
|
const firstPhasePath = join(firstDirectory, "PHASE.md")
|
|
247
248
|
const firstContent = formatMarkdownDocument(
|
|
248
249
|
firstData,
|
|
249
|
-
|
|
250
|
+
formatWorkDocumentBody(firstTitle, "phase"),
|
|
250
251
|
)
|
|
251
252
|
const steps: TransactionStep[] = []
|
|
252
253
|
if (yield* fs.isDirectory(oldCodePath)) {
|
|
@@ -220,7 +220,10 @@ process.exit(${exitCode})
|
|
|
220
220
|
const original = await Bun.file(taskPath).text()
|
|
221
221
|
await Bun.write(
|
|
222
222
|
taskPath,
|
|
223
|
-
original.replace(
|
|
223
|
+
original.replace(
|
|
224
|
+
"# Example\n\n## Outcome\n\nDescribe the task outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions\n\nRecord consequential decisions and their rationale.",
|
|
225
|
+
body,
|
|
226
|
+
),
|
|
224
227
|
)
|
|
225
228
|
const url = "https://github.com/example/agency/pull/42"
|
|
226
229
|
await writeFakeGh({ stdout: `${url}\n` })
|
|
@@ -33,7 +33,7 @@ describe("task and phase services", () => {
|
|
|
33
33
|
),
|
|
34
34
|
),
|
|
35
35
|
)
|
|
36
|
-
await runTestEffect(
|
|
36
|
+
const createdTask = await runTestEffect(
|
|
37
37
|
TaskService.pipe(
|
|
38
38
|
Effect.flatMap((service) =>
|
|
39
39
|
service.create(
|
|
@@ -51,6 +51,9 @@ describe("task and phase services", () => {
|
|
|
51
51
|
),
|
|
52
52
|
),
|
|
53
53
|
)
|
|
54
|
+
expect(createdTask.content).toContain(
|
|
55
|
+
"# Task One\n\n## Outcome\n\nDescribe the task outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions\n\nRecord consequential decisions and their rationale.",
|
|
56
|
+
)
|
|
54
57
|
|
|
55
58
|
const epic = await runTestEffect(
|
|
56
59
|
EpicService.pipe(
|
|
@@ -159,6 +162,14 @@ describe("task and phase services", () => {
|
|
|
159
162
|
),
|
|
160
163
|
)
|
|
161
164
|
expect(phases.map((phase) => phase.id)).toEqual(["first", "second"])
|
|
165
|
+
const firstPhase = await runTestEffect(
|
|
166
|
+
PhaseService.pipe(
|
|
167
|
+
Effect.flatMap((service) => service.show("multi", "first", root)),
|
|
168
|
+
),
|
|
169
|
+
)
|
|
170
|
+
expect(firstPhase.content).toContain(
|
|
171
|
+
"# First\n\n## Outcome\n\nDescribe the phase outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions\n\nRecord consequential decisions and their rationale.",
|
|
172
|
+
)
|
|
162
173
|
})
|
|
163
174
|
|
|
164
175
|
test("converts a single-phase task when the existing phase ID is provided", async () => {
|
|
@@ -243,7 +254,9 @@ describe("task and phase services", () => {
|
|
|
243
254
|
{ id: "extra", dependsOn: ["implementation"] },
|
|
244
255
|
],
|
|
245
256
|
})
|
|
246
|
-
expect(task.content).toContain(
|
|
257
|
+
expect(task.content).toContain(
|
|
258
|
+
"## Outcome\n\nDescribe the task outcome.\n\n## Plan\n\nDescribe the current approach.\n\n## Important Decisions",
|
|
259
|
+
)
|
|
247
260
|
|
|
248
261
|
const firstPhase = await runTestEffect(
|
|
249
262
|
PhaseService.pipe(
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "../workbase/schemas"
|
|
14
14
|
import {
|
|
15
15
|
formatMarkdownDocument,
|
|
16
|
+
formatWorkDocumentBody,
|
|
16
17
|
parseFrontmatter,
|
|
17
18
|
} from "../workbase/frontmatter"
|
|
18
19
|
import { canTransitionStatus } from "../readiness"
|
|
@@ -163,7 +164,7 @@ export class TaskService extends Effect.Service<TaskService>()("TaskService", {
|
|
|
163
164
|
.join(" ")
|
|
164
165
|
const content = formatMarkdownDocument(
|
|
165
166
|
data,
|
|
166
|
-
|
|
167
|
+
formatWorkDocumentBody(title, "task"),
|
|
167
168
|
)
|
|
168
169
|
const writes: {
|
|
169
170
|
path: string
|
|
@@ -81,3 +81,20 @@ export const parseFrontmatter = (content: string, path: string) =>
|
|
|
81
81
|
|
|
82
82
|
export const formatMarkdownDocument = (data: object, body: string) =>
|
|
83
83
|
`---\n${stringify(data, { lineWidth: 0 }).trimEnd()}\n---\n\n${body.trim()}\n`
|
|
84
|
+
|
|
85
|
+
export const formatWorkDocumentBody = (
|
|
86
|
+
title: string,
|
|
87
|
+
kind: "epic" | "task" | "phase",
|
|
88
|
+
) => `# ${title}
|
|
89
|
+
|
|
90
|
+
## Outcome
|
|
91
|
+
|
|
92
|
+
Describe the ${kind} outcome.
|
|
93
|
+
|
|
94
|
+
## Plan
|
|
95
|
+
|
|
96
|
+
Describe the current approach.
|
|
97
|
+
|
|
98
|
+
## Important Decisions
|
|
99
|
+
|
|
100
|
+
Record consequential decisions and their rationale.`
|