@markjaquith/agency 2.10.0 → 2.12.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 +36 -5
- package/cli.ts +32 -2
- package/index.ts +2 -0
- package/package.json +4 -1
- package/schemas/agency-graph-v1.schema.json +524 -0
- package/skills/agency/SKILL.md +6 -0
- package/src/cli-parser.test.ts +45 -0
- package/src/cli-parser.ts +60 -0
- package/src/cli.test.ts +83 -1
- package/src/commands/graph.ts +103 -0
- package/src/commands/read-only.test.ts +2 -0
- package/src/graph-schema.test.ts +68 -0
- package/src/graph-schema.ts +257 -0
- package/src/protocol.ts +5 -0
- package/src/readiness.test.ts +47 -0
- package/src/readiness.ts +58 -0
- package/src/services/ContextService.ts +6 -32
- package/src/services/GraphService.test.ts +331 -0
- package/src/services/GraphService.ts +860 -0
- package/src/services/PhaseService.ts +6 -0
- package/src/services/TaskPhaseService.test.ts +43 -0
- package/src/services/TaskService.ts +6 -0
- package/src/test-utils.ts +2 -0
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
formatMarkdownDocument,
|
|
16
16
|
parseFrontmatter,
|
|
17
17
|
} from "../workbase/frontmatter"
|
|
18
|
+
import { canTransitionStatus } from "../readiness"
|
|
18
19
|
|
|
19
20
|
class PhaseError extends Data.TaggedError("PhaseError")<{
|
|
20
21
|
readonly message: string
|
|
@@ -340,6 +341,11 @@ export class PhaseService extends Effect.Service<PhaseService>()(
|
|
|
340
341
|
const service = yield* PhaseService
|
|
341
342
|
const validStatus = yield* decodeStatus(status)
|
|
342
343
|
const record = yield* service.show(taskId, id, startPath)
|
|
344
|
+
if (!canTransitionStatus(record.data.status, validStatus)) {
|
|
345
|
+
return yield* new PhaseError({
|
|
346
|
+
message: `Cannot transition phase '${id}' from ${record.data.status} to ${validStatus}; reopen it first`,
|
|
347
|
+
})
|
|
348
|
+
}
|
|
343
349
|
const parsed = yield* parseFrontmatter(record.content, record.path)
|
|
344
350
|
const data = { ...record.data, status: validStatus }
|
|
345
351
|
const content = formatMarkdownDocument(data, parsed.body)
|
|
@@ -244,6 +244,22 @@ describe("task and phase services", () => {
|
|
|
244
244
|
expect(task.data.status).toBe("delegated")
|
|
245
245
|
expect(task.content).toContain("status: delegated")
|
|
246
246
|
expect(task.content).toContain("Describe the task outcome.")
|
|
247
|
+
await runTestEffect(
|
|
248
|
+
TaskService.pipe(
|
|
249
|
+
Effect.flatMap((service) =>
|
|
250
|
+
service.setStatus("single-status", "done", root),
|
|
251
|
+
),
|
|
252
|
+
),
|
|
253
|
+
)
|
|
254
|
+
await expect(
|
|
255
|
+
runTestEffect(
|
|
256
|
+
TaskService.pipe(
|
|
257
|
+
Effect.flatMap((service) =>
|
|
258
|
+
service.setStatus("single-status", "dropped", root),
|
|
259
|
+
),
|
|
260
|
+
),
|
|
261
|
+
),
|
|
262
|
+
).rejects.toThrow("reopen it first")
|
|
247
263
|
|
|
248
264
|
await runTestEffect(
|
|
249
265
|
TaskService.pipe(
|
|
@@ -283,6 +299,33 @@ describe("task and phase services", () => {
|
|
|
283
299
|
),
|
|
284
300
|
)
|
|
285
301
|
expect(phase.data.status).toBe("dropped")
|
|
302
|
+
await expect(
|
|
303
|
+
runTestEffect(
|
|
304
|
+
PhaseService.pipe(
|
|
305
|
+
Effect.flatMap((service) =>
|
|
306
|
+
service.setStatus("multi-status", "implementation", "done", root),
|
|
307
|
+
),
|
|
308
|
+
),
|
|
309
|
+
),
|
|
310
|
+
).rejects.toThrow("reopen it first")
|
|
311
|
+
await runTestEffect(
|
|
312
|
+
PhaseService.pipe(
|
|
313
|
+
Effect.flatMap((service) =>
|
|
314
|
+
service.setStatus("multi-status", "implementation", "open", root),
|
|
315
|
+
),
|
|
316
|
+
),
|
|
317
|
+
)
|
|
318
|
+
expect(
|
|
319
|
+
(
|
|
320
|
+
await runTestEffect(
|
|
321
|
+
PhaseService.pipe(
|
|
322
|
+
Effect.flatMap((service) =>
|
|
323
|
+
service.setStatus("multi-status", "implementation", "done", root),
|
|
324
|
+
),
|
|
325
|
+
),
|
|
326
|
+
)
|
|
327
|
+
).data.status,
|
|
328
|
+
).toBe("done")
|
|
286
329
|
await expect(
|
|
287
330
|
runTestEffect(
|
|
288
331
|
TaskService.pipe(
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
formatMarkdownDocument,
|
|
16
16
|
parseFrontmatter,
|
|
17
17
|
} from "../workbase/frontmatter"
|
|
18
|
+
import { canTransitionStatus } from "../readiness"
|
|
18
19
|
|
|
19
20
|
class TaskError extends Data.TaggedError("TaskError")<{
|
|
20
21
|
readonly message: string
|
|
@@ -212,6 +213,11 @@ export class TaskService extends Effect.Service<TaskService>()("TaskService", {
|
|
|
212
213
|
message: `Task '${id}' has multiple phases; set status on a phase instead`,
|
|
213
214
|
})
|
|
214
215
|
}
|
|
216
|
+
if (!canTransitionStatus(record.data.status, validStatus)) {
|
|
217
|
+
return yield* new TaskError({
|
|
218
|
+
message: `Cannot transition task '${id}' from ${record.data.status} to ${validStatus}; reopen it first`,
|
|
219
|
+
})
|
|
220
|
+
}
|
|
215
221
|
const parsed = yield* parseFrontmatter(record.content, record.path)
|
|
216
222
|
const data = { ...record.data, status: validStatus }
|
|
217
223
|
const content = formatMarkdownDocument(data, parsed.body)
|
package/src/test-utils.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { PullRequestService } from "./services/PullRequestService"
|
|
|
14
14
|
import { ArchiveService } from "./services/ArchiveService"
|
|
15
15
|
import { IntegrationService } from "./services/IntegrationService"
|
|
16
16
|
import { ContextService } from "./services/ContextService"
|
|
17
|
+
import { GraphService } from "./services/GraphService"
|
|
17
18
|
|
|
18
19
|
export const createTempDir = () => mkdtemp(join(tmpdir(), "agency-test-"))
|
|
19
20
|
|
|
@@ -32,6 +33,7 @@ const TestLayer = Layer.mergeAll(
|
|
|
32
33
|
ArchiveService.Default,
|
|
33
34
|
IntegrationService.Default,
|
|
34
35
|
ContextService.Default,
|
|
36
|
+
GraphService.Default,
|
|
35
37
|
)
|
|
36
38
|
|
|
37
39
|
export async function runTestEffect<A, E>(
|