@markjaquith/agency 1.11.0 → 2.0.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 +302 -113
- package/cli.ts +123 -415
- package/index.ts +1 -1
- package/package.json +12 -17
- package/skills/agency/SKILL.md +258 -0
- package/src/cli.test.ts +290 -0
- package/src/commands/description.test.ts +103 -0
- package/src/commands/epic.test.ts +103 -0
- package/src/commands/epic.ts +101 -0
- package/src/commands/init.test.ts +42 -290
- package/src/commands/init.ts +15 -123
- package/src/commands/phase.ts +120 -0
- package/src/commands/pr.test.ts +19 -170
- package/src/commands/pr.ts +23 -179
- package/src/commands/repo.test.ts +80 -0
- package/src/commands/repo.ts +83 -0
- package/src/commands/status.test.ts +35 -477
- package/src/commands/status.ts +24 -213
- package/src/commands/task-phase.test.ts +206 -0
- package/src/commands/task.ts +83 -1285
- package/src/commands/validate.test.ts +91 -0
- package/src/commands/validate.ts +50 -0
- package/src/commands/work.test.ts +172 -419
- package/src/commands/work.ts +47 -166
- package/src/services/EpicService.test.ts +71 -0
- package/src/services/EpicService.ts +135 -0
- package/src/services/FileSystemService.ts +61 -92
- package/src/services/PhaseService.ts +323 -0
- package/src/services/PullRequestService.test.ts +386 -0
- package/src/services/PullRequestService.ts +143 -0
- package/src/services/RepositoryService.test.ts +106 -0
- package/src/services/RepositoryService.ts +159 -0
- package/src/services/TaskPhaseService.test.ts +217 -0
- package/src/services/TaskService.ts +192 -0
- package/src/services/WorkbaseService.test.ts +446 -0
- package/src/services/WorkbaseService.ts +493 -0
- package/src/services/WorktreeService.test.ts +599 -0
- package/src/services/WorktreeService.ts +357 -0
- package/src/test-utils.ts +41 -438
- package/src/types.ts +1 -215
- package/src/utils/command.ts +1 -5
- package/src/utils/effect.test.ts +24 -0
- package/src/utils/effect.ts +3 -273
- package/src/utils/process.test.ts +0 -15
- package/src/utils/process.ts +1 -60
- package/src/workbase/frontmatter.test.ts +50 -0
- package/src/workbase/frontmatter.ts +77 -0
- package/src/workbase/repository-reference.test.ts +25 -0
- package/src/workbase/repository-reference.ts +18 -0
- package/src/workbase/schemas.test.ts +146 -0
- package/src/workbase/schemas.ts +78 -0
- package/src/workbase/worktree-command.test.ts +61 -0
- package/src/workbase/worktree-command.ts +52 -0
- package/src/commands/base.test.ts +0 -198
- package/src/commands/base.ts +0 -198
- package/src/commands/clean.test.ts +0 -299
- package/src/commands/clean.ts +0 -274
- package/src/commands/completions.test.ts +0 -51
- package/src/commands/completions.ts +0 -308
- package/src/commands/emit.integration.test.ts +0 -549
- package/src/commands/emit.test.ts +0 -576
- package/src/commands/emit.ts +0 -700
- package/src/commands/emitted.test.ts +0 -226
- package/src/commands/emitted.ts +0 -57
- package/src/commands/loop.test.ts +0 -621
- package/src/commands/loop.ts +0 -406
- package/src/commands/merge.integration.test.ts +0 -195
- package/src/commands/merge.test.ts +0 -246
- package/src/commands/merge.ts +0 -244
- package/src/commands/pull.test.ts +0 -395
- package/src/commands/pull.ts +0 -205
- package/src/commands/push.test.ts +0 -518
- package/src/commands/push.ts +0 -434
- package/src/commands/rebase.test.ts +0 -521
- package/src/commands/rebase.ts +0 -232
- package/src/commands/save.test.ts +0 -247
- package/src/commands/save.ts +0 -162
- package/src/commands/source.test.ts +0 -195
- package/src/commands/source.ts +0 -72
- package/src/commands/switch.test.ts +0 -194
- package/src/commands/switch.ts +0 -84
- package/src/commands/task-branching.test.ts +0 -633
- package/src/commands/task-continue.test.ts +0 -311
- package/src/commands/task-edit.test.ts +0 -235
- package/src/commands/task-main.test.ts +0 -1066
- package/src/commands/task-squash.test.ts +0 -266
- package/src/commands/tasks.test.ts +0 -391
- package/src/commands/tasks.ts +0 -161
- package/src/commands/template-delete.test.ts +0 -178
- package/src/commands/template-delete.ts +0 -98
- package/src/commands/template-list.test.ts +0 -135
- package/src/commands/template-list.ts +0 -87
- package/src/commands/template-view.test.ts +0 -158
- package/src/commands/template-view.ts +0 -86
- package/src/commands/template.test.ts +0 -32
- package/src/commands/template.ts +0 -96
- package/src/commands/use.test.ts +0 -87
- package/src/commands/use.ts +0 -97
- package/src/constants.ts +0 -10
- package/src/errors.ts +0 -17
- package/src/schemas.ts +0 -33
- package/src/services/AgencyMetadataService.ts +0 -311
- package/src/services/ClaudeService.test.ts +0 -184
- package/src/services/ClaudeService.ts +0 -91
- package/src/services/ConfigService.ts +0 -115
- package/src/services/FilterRepoService.ts +0 -164
- package/src/services/FormatterService.test.ts +0 -432
- package/src/services/FormatterService.ts +0 -219
- package/src/services/GitService.ts +0 -1183
- package/src/services/MockFilterRepoService.ts +0 -140
- package/src/services/OpencodeService.ts +0 -263
- package/src/services/PromptService.ts +0 -183
- package/src/services/TemplateService.ts +0 -81
- package/src/types/native-exec.d.ts +0 -8
- package/src/utils/colors.ts +0 -178
- package/src/utils/git-path.ts +0 -6
- package/src/utils/glob.test.ts +0 -161
- package/src/utils/glob.ts +0 -78
- package/src/utils/paths.ts +0 -51
- package/src/utils/pr-branch.test.ts +0 -376
- package/src/utils/pr-branch.ts +0 -508
- package/src/utils/spinner.test.ts +0 -45
- package/src/utils/spinner.ts +0 -96
- package/src/utils/task-parser.test.ts +0 -172
- package/src/utils/task-parser.ts +0 -93
- package/templates/AGENCY.md +0 -22
- package/templates/AGENTS.md +0 -11
- package/templates/CLAUDE.md +0 -3
- package/templates/TASK.md +0 -5
- package/templates/opencode.json +0 -4
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test"
|
|
2
|
-
import {
|
|
3
|
-
parseTaskItems,
|
|
4
|
-
countTasks,
|
|
5
|
-
areAllTasksComplete,
|
|
6
|
-
extractCompletionPromise,
|
|
7
|
-
validateCompletion,
|
|
8
|
-
} from "./task-parser"
|
|
9
|
-
|
|
10
|
-
describe("task-parser", () => {
|
|
11
|
-
describe("parseTaskItems", () => {
|
|
12
|
-
it("should parse task items correctly", () => {
|
|
13
|
-
const taskContent = `
|
|
14
|
-
# Tasks
|
|
15
|
-
|
|
16
|
-
- [ ] First task
|
|
17
|
-
- [x] Second task
|
|
18
|
-
- [ ] Third task
|
|
19
|
-
`
|
|
20
|
-
|
|
21
|
-
const items = parseTaskItems(taskContent)
|
|
22
|
-
expect(items).toHaveLength(3)
|
|
23
|
-
expect(items[0]!.isComplete).toBe(false)
|
|
24
|
-
expect(items[0]!.text).toBe("First task")
|
|
25
|
-
expect(items[1]!.isComplete).toBe(true)
|
|
26
|
-
expect(items[1]!.text).toBe("Second task")
|
|
27
|
-
expect(items[2]!.isComplete).toBe(false)
|
|
28
|
-
expect(items[2]!.text).toBe("Third task")
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it("should handle empty task list", () => {
|
|
32
|
-
const taskContent = `
|
|
33
|
-
# Tasks
|
|
34
|
-
|
|
35
|
-
No task items here
|
|
36
|
-
`
|
|
37
|
-
|
|
38
|
-
const items = parseTaskItems(taskContent)
|
|
39
|
-
expect(items).toHaveLength(0)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it("should parse task items with various bullet formats", () => {
|
|
43
|
-
const taskContent = `
|
|
44
|
-
- [ ] Dash format unchecked
|
|
45
|
-
- [x] Dash format checked
|
|
46
|
-
* [ ] Asterisk format unchecked
|
|
47
|
-
* [X] Asterisk format uppercase
|
|
48
|
-
- [ ] Indented unchecked
|
|
49
|
-
- [x] Indented checked
|
|
50
|
-
`
|
|
51
|
-
|
|
52
|
-
const items = parseTaskItems(taskContent)
|
|
53
|
-
expect(items).toHaveLength(6)
|
|
54
|
-
expect(items[0]!.isComplete).toBe(false)
|
|
55
|
-
expect(items[1]!.isComplete).toBe(true)
|
|
56
|
-
expect(items[2]!.isComplete).toBe(false)
|
|
57
|
-
expect(items[3]!.isComplete).toBe(true)
|
|
58
|
-
expect(items[4]!.isComplete).toBe(false)
|
|
59
|
-
expect(items[5]!.isComplete).toBe(true)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it("should track line numbers", () => {
|
|
63
|
-
const taskContent = `# Header
|
|
64
|
-
- [ ] First task
|
|
65
|
-
Some text
|
|
66
|
-
- [x] Second task`
|
|
67
|
-
|
|
68
|
-
const items = parseTaskItems(taskContent)
|
|
69
|
-
expect(items).toHaveLength(2)
|
|
70
|
-
expect(items[0]!.lineNumber).toBe(2)
|
|
71
|
-
expect(items[1]!.lineNumber).toBe(4)
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
describe("countTasks", () => {
|
|
76
|
-
it("should count completed and incomplete tasks", () => {
|
|
77
|
-
const items = [
|
|
78
|
-
{ text: "First", isComplete: false, lineNumber: 1 },
|
|
79
|
-
{ text: "Second", isComplete: true, lineNumber: 2 },
|
|
80
|
-
{ text: "Third", isComplete: false, lineNumber: 3 },
|
|
81
|
-
]
|
|
82
|
-
|
|
83
|
-
const counts = countTasks(items)
|
|
84
|
-
expect(counts.completed).toBe(1)
|
|
85
|
-
expect(counts.incomplete).toBe(2)
|
|
86
|
-
expect(counts.total).toBe(3)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it("should handle empty list", () => {
|
|
90
|
-
const counts = countTasks([])
|
|
91
|
-
expect(counts.completed).toBe(0)
|
|
92
|
-
expect(counts.incomplete).toBe(0)
|
|
93
|
-
expect(counts.total).toBe(0)
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
describe("areAllTasksComplete", () => {
|
|
98
|
-
it("should return true when all tasks are complete", () => {
|
|
99
|
-
const items = [
|
|
100
|
-
{ text: "First", isComplete: true, lineNumber: 1 },
|
|
101
|
-
{ text: "Second", isComplete: true, lineNumber: 2 },
|
|
102
|
-
]
|
|
103
|
-
expect(areAllTasksComplete(items)).toBe(true)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it("should return false when some tasks are incomplete", () => {
|
|
107
|
-
const items = [
|
|
108
|
-
{ text: "First", isComplete: true, lineNumber: 1 },
|
|
109
|
-
{ text: "Second", isComplete: false, lineNumber: 2 },
|
|
110
|
-
]
|
|
111
|
-
expect(areAllTasksComplete(items)).toBe(false)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it("should return true for empty list", () => {
|
|
115
|
-
expect(areAllTasksComplete([])).toBe(true)
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
describe("extractCompletionPromise", () => {
|
|
120
|
-
it("should extract completion promise from output", () => {
|
|
121
|
-
const output = "Some output\n<promise>COMPLETE</promise>"
|
|
122
|
-
expect(extractCompletionPromise(output)).toBe("COMPLETE")
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it("should return null when no promise found", () => {
|
|
126
|
-
const output = "No promise here"
|
|
127
|
-
expect(extractCompletionPromise(output)).toBeNull()
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it("should extract any promise content", () => {
|
|
131
|
-
const output = "<promise>custom message</promise>"
|
|
132
|
-
expect(extractCompletionPromise(output)).toBe("custom message")
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
describe("validateCompletion", () => {
|
|
137
|
-
it("should not throw when all tasks complete and claiming completion", () => {
|
|
138
|
-
expect(() => {
|
|
139
|
-
validateCompletion("COMPLETE", true)
|
|
140
|
-
}).not.toThrow()
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it("should not throw when not claiming completion", () => {
|
|
144
|
-
expect(() => {
|
|
145
|
-
validateCompletion(null, false)
|
|
146
|
-
}).not.toThrow()
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it("should throw when claiming completion but tasks remain", () => {
|
|
150
|
-
expect(() => {
|
|
151
|
-
validateCompletion("COMPLETE", false)
|
|
152
|
-
}).toThrow(
|
|
153
|
-
"Agent claimed completion with <promise>COMPLETE</promise> but incomplete tasks remain",
|
|
154
|
-
)
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it("should be case-insensitive for COMPLETE", () => {
|
|
158
|
-
expect(() => {
|
|
159
|
-
validateCompletion("complete", false)
|
|
160
|
-
}).toThrow()
|
|
161
|
-
expect(() => {
|
|
162
|
-
validateCompletion("Complete", false)
|
|
163
|
-
}).toThrow()
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it("should not throw for non-COMPLETE messages", () => {
|
|
167
|
-
expect(() => {
|
|
168
|
-
validateCompletion("IN_PROGRESS", false)
|
|
169
|
-
}).not.toThrow()
|
|
170
|
-
})
|
|
171
|
-
})
|
|
172
|
-
})
|
package/src/utils/task-parser.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for parsing and analyzing TASK.md files
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Represents a task item parsed from markdown
|
|
7
|
-
*/
|
|
8
|
-
interface TaskItem {
|
|
9
|
-
readonly text: string
|
|
10
|
-
readonly isComplete: boolean
|
|
11
|
-
readonly lineNumber: number
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Parse TASK.md content and extract task items
|
|
16
|
-
* Returns all checkbox items (both complete and incomplete)
|
|
17
|
-
*/
|
|
18
|
-
export function parseTaskItems(content: string): TaskItem[] {
|
|
19
|
-
const items: TaskItem[] = []
|
|
20
|
-
const lines = content.split("\n")
|
|
21
|
-
|
|
22
|
-
for (let i = 0; i < lines.length; i++) {
|
|
23
|
-
const line = lines[i]
|
|
24
|
-
if (!line) continue
|
|
25
|
-
// Match both [ ] (incomplete) and [x] or [X] (complete)
|
|
26
|
-
const match = line.match(/^[\s]*[-*]\s+\[([ xX])\]\s+(.+)$/)
|
|
27
|
-
if (match && match[1] && match[2]) {
|
|
28
|
-
const isComplete = match[1].toLowerCase() === "x"
|
|
29
|
-
const text = match[2]
|
|
30
|
-
items.push({
|
|
31
|
-
text,
|
|
32
|
-
isComplete,
|
|
33
|
-
lineNumber: i + 1, // 1-indexed
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return items
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Count completed and incomplete tasks
|
|
43
|
-
*/
|
|
44
|
-
export function countTasks(items: TaskItem[]): {
|
|
45
|
-
completed: number
|
|
46
|
-
incomplete: number
|
|
47
|
-
total: number
|
|
48
|
-
} {
|
|
49
|
-
const completed = items.filter((item) => item.isComplete).length
|
|
50
|
-
const incomplete = items.filter((item) => !item.isComplete).length
|
|
51
|
-
return {
|
|
52
|
-
completed,
|
|
53
|
-
incomplete,
|
|
54
|
-
total: items.length,
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Check if all tasks are complete
|
|
60
|
-
*/
|
|
61
|
-
export function areAllTasksComplete(items: TaskItem[]): boolean {
|
|
62
|
-
if (items.length === 0) return true
|
|
63
|
-
return items.every((item) => item.isComplete)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Extract the promise completion message from output
|
|
68
|
-
* Returns null if no completion message found
|
|
69
|
-
*/
|
|
70
|
-
export function extractCompletionPromise(output: string): string | null {
|
|
71
|
-
const match = output.match(/<promise>([^<]+)<\/promise>/)
|
|
72
|
-
return match && match[1] ? match[1] : null
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Validate that completion message aligns with task status
|
|
77
|
-
* Throws an error if the agent claims completion but tasks remain
|
|
78
|
-
*/
|
|
79
|
-
export function validateCompletion(
|
|
80
|
-
completionMessage: string | null,
|
|
81
|
-
allTasksComplete: boolean,
|
|
82
|
-
): void {
|
|
83
|
-
if (
|
|
84
|
-
completionMessage &&
|
|
85
|
-
completionMessage.toUpperCase() === "COMPLETE" &&
|
|
86
|
-
!allTasksComplete
|
|
87
|
-
) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
"Agent claimed completion with <promise>COMPLETE</promise> but incomplete tasks remain in TASK.md. " +
|
|
90
|
-
"This indicates the agent did not complete all work as required.",
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
}
|
package/templates/AGENCY.md
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Agent Instructions
|
|
2
|
-
|
|
3
|
-
## TASK.md
|
|
4
|
-
|
|
5
|
-
The `TASK.md` file describes the task being performed and should be kept updated as work progresses. This file serves as a living record of:
|
|
6
|
-
|
|
7
|
-
- What is being built or fixed
|
|
8
|
-
- Current progress and status
|
|
9
|
-
- Remaining work items
|
|
10
|
-
- Any important context or decisions
|
|
11
|
-
|
|
12
|
-
All work on this repository should begin by reading and understanding `TASK.md`. Whenever any significant progress is made, `TASK.md` should be updated to reflect the current state of work.
|
|
13
|
-
|
|
14
|
-
**Note:** When you receive the prompt "Start the task", `TASK.md` is already in your context. DO NOT re-read it — instead, proceed directly with the work described in the task.
|
|
15
|
-
|
|
16
|
-
## Commit Messages
|
|
17
|
-
|
|
18
|
-
When creating commit messages, do not reference changes to `TASK.md`, `AGENTS.md`, or any files tracked in `agency.json` (such as `opencode.json`). These are project management and configuration files that should not be mentioned in commit messages. Focus commit messages on actual code changes, features, fixes, and refactoring.
|
|
19
|
-
|
|
20
|
-
**Important:** Even when the only changes in a commit are to tracked files like `TASK.md`, you should still commit those changes. These updates should be co-located with the code changes they describe. Simply omit mentioning the tracked files in the commit message and focus the message on the actual code changes being made.
|
|
21
|
-
|
|
22
|
-
If a commit only updates `TASK.md`, use `--no-verify` to skip precommit hooks. Do not use `--no-verify` if the commit includes changes to any other file.
|
package/templates/AGENTS.md
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Repo Instructions
|
|
2
|
-
|
|
3
|
-
Follow these instructions when working in this repository.
|
|
4
|
-
|
|
5
|
-
You should have already read @AGENCY.md to understand how agency is use to work on features.
|
|
6
|
-
|
|
7
|
-
When you have read this file, move on to reading @TODO.md to understand the specific tasks.
|
|
8
|
-
|
|
9
|
-
## Rules
|
|
10
|
-
|
|
11
|
-
{human: fill this in}
|
package/templates/CLAUDE.md
DELETED
package/templates/TASK.md
DELETED
package/templates/opencode.json
DELETED