@markjaquith/agency 1.11.1 → 2.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.
Files changed (132) hide show
  1. package/README.md +313 -117
  2. package/cli.ts +124 -420
  3. package/index.ts +1 -1
  4. package/package.json +12 -17
  5. package/skills/agency/SKILL.md +266 -0
  6. package/src/cli.test.ts +290 -0
  7. package/src/commands/description.test.ts +103 -0
  8. package/src/commands/epic.test.ts +103 -0
  9. package/src/commands/epic.ts +101 -0
  10. package/src/commands/init.test.ts +42 -290
  11. package/src/commands/init.ts +15 -123
  12. package/src/commands/phase.ts +120 -0
  13. package/src/commands/pr.test.ts +19 -170
  14. package/src/commands/pr.ts +23 -179
  15. package/src/commands/repo.test.ts +80 -0
  16. package/src/commands/repo.ts +83 -0
  17. package/src/commands/status.test.ts +35 -477
  18. package/src/commands/status.ts +24 -213
  19. package/src/commands/task-phase.test.ts +206 -0
  20. package/src/commands/task.ts +83 -1285
  21. package/src/commands/validate.test.ts +91 -0
  22. package/src/commands/validate.ts +50 -0
  23. package/src/commands/work.test.ts +326 -399
  24. package/src/commands/work.ts +153 -153
  25. package/src/services/EpicService.test.ts +71 -0
  26. package/src/services/EpicService.ts +135 -0
  27. package/src/services/FileSystemService.ts +61 -92
  28. package/src/services/PhaseService.ts +323 -0
  29. package/src/services/PullRequestService.test.ts +386 -0
  30. package/src/services/PullRequestService.ts +143 -0
  31. package/src/services/RepositoryService.test.ts +106 -0
  32. package/src/services/RepositoryService.ts +159 -0
  33. package/src/services/TaskPhaseService.test.ts +217 -0
  34. package/src/services/TaskService.ts +192 -0
  35. package/src/services/WorkbaseService.test.ts +446 -0
  36. package/src/services/WorkbaseService.ts +493 -0
  37. package/src/services/WorktreeService.test.ts +599 -0
  38. package/src/services/WorktreeService.ts +357 -0
  39. package/src/test-utils.ts +41 -438
  40. package/src/types.ts +1 -215
  41. package/src/utils/command.ts +1 -5
  42. package/src/utils/effect.test.ts +24 -0
  43. package/src/utils/effect.ts +3 -273
  44. package/src/utils/process.test.ts +0 -15
  45. package/src/utils/process.ts +1 -60
  46. package/src/workbase/frontmatter.test.ts +50 -0
  47. package/src/workbase/frontmatter.ts +77 -0
  48. package/src/workbase/repository-reference.test.ts +25 -0
  49. package/src/workbase/repository-reference.ts +18 -0
  50. package/src/workbase/schemas.test.ts +146 -0
  51. package/src/workbase/schemas.ts +78 -0
  52. package/src/workbase/work-target.test.ts +77 -0
  53. package/src/workbase/work-target.ts +180 -0
  54. package/src/workbase/worktree-command.test.ts +61 -0
  55. package/src/workbase/worktree-command.ts +52 -0
  56. package/src/commands/base.test.ts +0 -198
  57. package/src/commands/base.ts +0 -198
  58. package/src/commands/clean.test.ts +0 -299
  59. package/src/commands/clean.ts +0 -274
  60. package/src/commands/completions.test.ts +0 -51
  61. package/src/commands/completions.ts +0 -308
  62. package/src/commands/emit.integration.test.ts +0 -549
  63. package/src/commands/emit.test.ts +0 -576
  64. package/src/commands/emit.ts +0 -700
  65. package/src/commands/emitted.test.ts +0 -226
  66. package/src/commands/emitted.ts +0 -57
  67. package/src/commands/loop.test.ts +0 -621
  68. package/src/commands/loop.ts +0 -406
  69. package/src/commands/merge.integration.test.ts +0 -195
  70. package/src/commands/merge.test.ts +0 -246
  71. package/src/commands/merge.ts +0 -244
  72. package/src/commands/pull.test.ts +0 -395
  73. package/src/commands/pull.ts +0 -205
  74. package/src/commands/push.test.ts +0 -641
  75. package/src/commands/push.ts +0 -482
  76. package/src/commands/rebase.test.ts +0 -521
  77. package/src/commands/rebase.ts +0 -232
  78. package/src/commands/save.test.ts +0 -247
  79. package/src/commands/save.ts +0 -162
  80. package/src/commands/source.test.ts +0 -195
  81. package/src/commands/source.ts +0 -72
  82. package/src/commands/switch.test.ts +0 -194
  83. package/src/commands/switch.ts +0 -84
  84. package/src/commands/task-branching.test.ts +0 -633
  85. package/src/commands/task-continue.test.ts +0 -311
  86. package/src/commands/task-edit.test.ts +0 -235
  87. package/src/commands/task-main.test.ts +0 -1066
  88. package/src/commands/task-squash.test.ts +0 -266
  89. package/src/commands/tasks.test.ts +0 -391
  90. package/src/commands/tasks.ts +0 -161
  91. package/src/commands/template-delete.test.ts +0 -178
  92. package/src/commands/template-delete.ts +0 -98
  93. package/src/commands/template-list.test.ts +0 -135
  94. package/src/commands/template-list.ts +0 -87
  95. package/src/commands/template-view.test.ts +0 -158
  96. package/src/commands/template-view.ts +0 -86
  97. package/src/commands/template.test.ts +0 -32
  98. package/src/commands/template.ts +0 -96
  99. package/src/commands/use.test.ts +0 -87
  100. package/src/commands/use.ts +0 -97
  101. package/src/constants.ts +0 -10
  102. package/src/errors.ts +0 -17
  103. package/src/schemas.ts +0 -33
  104. package/src/services/AgencyMetadataService.ts +0 -311
  105. package/src/services/ClaudeService.test.ts +0 -184
  106. package/src/services/ClaudeService.ts +0 -91
  107. package/src/services/ConfigService.ts +0 -115
  108. package/src/services/FilterRepoService.ts +0 -164
  109. package/src/services/FormatterService.test.ts +0 -432
  110. package/src/services/FormatterService.ts +0 -219
  111. package/src/services/GitService.ts +0 -1187
  112. package/src/services/MockFilterRepoService.ts +0 -140
  113. package/src/services/OpencodeService.ts +0 -263
  114. package/src/services/PromptService.ts +0 -183
  115. package/src/services/TemplateService.ts +0 -81
  116. package/src/types/native-exec.d.ts +0 -8
  117. package/src/utils/colors.ts +0 -178
  118. package/src/utils/git-path.ts +0 -6
  119. package/src/utils/glob.test.ts +0 -161
  120. package/src/utils/glob.ts +0 -78
  121. package/src/utils/paths.ts +0 -51
  122. package/src/utils/pr-branch.test.ts +0 -376
  123. package/src/utils/pr-branch.ts +0 -508
  124. package/src/utils/spinner.test.ts +0 -45
  125. package/src/utils/spinner.ts +0 -96
  126. package/src/utils/task-parser.test.ts +0 -172
  127. package/src/utils/task-parser.ts +0 -93
  128. package/templates/AGENCY.md +0 -22
  129. package/templates/AGENTS.md +0 -11
  130. package/templates/CLAUDE.md +0 -3
  131. package/templates/TASK.md +0 -5
  132. package/templates/opencode.json +0 -4
@@ -1,219 +0,0 @@
1
- import { Effect } from "effect"
2
- import { resolve } from "path"
3
- import { FileSystemService } from "./FileSystemService"
4
-
5
- type PackageJson = {
6
- dependencies?: Record<string, string>
7
- devDependencies?: Record<string, string>
8
- }
9
-
10
- type PackageManager = "bun" | "yarn" | "pnpm" | "npm"
11
-
12
- type Formatter = "prettier" | "oxfmt"
13
-
14
- /**
15
- * Service for detecting and running code formatters on agency-created files.
16
- *
17
- * When agency creates files (markdown, json), pre-commit or pre-push hooks
18
- * in the target project may fail if those files aren't formatted according
19
- * to the project's formatter settings. This service detects the project's
20
- * formatter (prettier or oxfmt) and package manager, then runs the formatter
21
- * on the specified files. Failures are silently ignored.
22
- */
23
- export class FormatterService extends Effect.Service<FormatterService>()(
24
- "FormatterService",
25
- {
26
- sync: () => ({
27
- /**
28
- * Detect which package manager the project uses by checking for lock files.
29
- * Falls back to npm if no lock file is found but package.json exists.
30
- * Returns null if no package.json exists.
31
- */
32
- detectPackageManager: (gitRoot: string) =>
33
- Effect.gen(function* () {
34
- const fs = yield* FileSystemService
35
-
36
- // Check for package.json first - if it doesn't exist, no package manager
37
- const hasPackageJson = yield* fs.exists(
38
- resolve(gitRoot, "package.json"),
39
- )
40
- if (!hasPackageJson) {
41
- return null
42
- }
43
-
44
- // Check lock files in priority order
45
- if (yield* fs.exists(resolve(gitRoot, "bun.lockb")))
46
- return "bun" as PackageManager
47
- if (yield* fs.exists(resolve(gitRoot, "bun.lock")))
48
- return "bun" as PackageManager
49
- if (yield* fs.exists(resolve(gitRoot, "yarn.lock")))
50
- return "yarn" as PackageManager
51
- if (yield* fs.exists(resolve(gitRoot, "pnpm-lock.yaml")))
52
- return "pnpm" as PackageManager
53
- if (yield* fs.exists(resolve(gitRoot, "package-lock.json")))
54
- return "npm" as PackageManager
55
-
56
- // Default to npm if package.json exists but no lock file found
57
- return "npm" as PackageManager
58
- }),
59
-
60
- /**
61
- * Detect which formatter is available in the project's package.json.
62
- * Checks both dependencies and devDependencies for prettier and oxfmt.
63
- * Returns null if neither is found.
64
- */
65
- detectFormatter: (gitRoot: string) =>
66
- Effect.gen(function* () {
67
- const fs = yield* FileSystemService
68
-
69
- const packageJsonPath = resolve(gitRoot, "package.json")
70
- const exists = yield* fs.exists(packageJsonPath)
71
- if (!exists) {
72
- return null
73
- }
74
-
75
- const packageJson = yield* fs.readJSON<PackageJson>(packageJsonPath)
76
-
77
- const allDeps = {
78
- ...packageJson.dependencies,
79
- ...packageJson.devDependencies,
80
- }
81
-
82
- // Check for oxfmt first (newer/faster), then prettier
83
- if ("oxfmt" in allDeps) return "oxfmt" as Formatter
84
- if ("prettier" in allDeps) return "prettier" as Formatter
85
-
86
- return null
87
- }).pipe(Effect.catchAll(() => Effect.succeed(null))),
88
-
89
- /**
90
- * Build the command to run a formatter via the detected package manager.
91
- * Returns the command args array, or null if the formatter/pm combo isn't supported.
92
- */
93
- buildFormatterCommand: (
94
- formatter: Formatter,
95
- packageManager: PackageManager,
96
- files: string[],
97
- ) =>
98
- Effect.sync(() => {
99
- if (files.length === 0) return null
100
-
101
- // Build the runner prefix based on package manager
102
- const runnerPrefix: string[] = (() => {
103
- switch (packageManager) {
104
- case "bun":
105
- return ["bun", "x"]
106
- case "yarn":
107
- return ["yarn", "dlx"]
108
- case "pnpm":
109
- return ["pnpm", "exec"]
110
- case "npm":
111
- return ["npx"]
112
- }
113
- })()
114
-
115
- // Build the formatter command
116
- switch (formatter) {
117
- case "prettier":
118
- return [...runnerPrefix, "prettier", "--write", ...files]
119
- case "oxfmt":
120
- return [...runnerPrefix, "oxfmt", ...files]
121
- default:
122
- return null
123
- }
124
- }),
125
-
126
- /**
127
- * Format the given files using the project's detected formatter.
128
- * Silently returns if no formatter is detected or if the command fails.
129
- * This is the main entry point for formatting agency-created files.
130
- */
131
- formatFiles: (
132
- gitRoot: string,
133
- files: string[],
134
- verboseLog: (msg: string) => void,
135
- ) =>
136
- Effect.gen(function* () {
137
- const fs = yield* FileSystemService
138
-
139
- if (files.length === 0) {
140
- verboseLog("No files to format")
141
- return
142
- }
143
-
144
- // Filter to only .md and .json/.jsonc files (the types agency creates)
145
- const formattableFiles = files.filter(
146
- (f) =>
147
- f.endsWith(".md") || f.endsWith(".json") || f.endsWith(".jsonc"),
148
- )
149
-
150
- if (formattableFiles.length === 0) {
151
- verboseLog("No formattable files (md/json/jsonc) found")
152
- return
153
- }
154
-
155
- // Use yield* on the service methods via self-reference through the service tag
156
- const formatterService = yield* FormatterService
157
-
158
- const formatter = yield* formatterService.detectFormatter(gitRoot)
159
- if (!formatter) {
160
- verboseLog("No formatter (prettier/oxfmt) detected in package.json")
161
- return
162
- }
163
-
164
- const packageManager =
165
- yield* formatterService.detectPackageManager(gitRoot)
166
- if (!packageManager) {
167
- verboseLog("No package manager detected")
168
- return
169
- }
170
-
171
- verboseLog(
172
- `Detected formatter: ${formatter}, package manager: ${packageManager}`,
173
- )
174
-
175
- // Build absolute paths for the files
176
- const absolutePaths = formattableFiles.map((f) => resolve(gitRoot, f))
177
-
178
- const command = yield* formatterService.buildFormatterCommand(
179
- formatter,
180
- packageManager,
181
- absolutePaths,
182
- )
183
- if (!command) {
184
- verboseLog("Could not build formatter command")
185
- return
186
- }
187
-
188
- verboseLog(`Running formatter: ${command.join(" ")}`)
189
-
190
- // Run the formatter, silently ignoring failures
191
- yield* fs
192
- .runCommand(command, {
193
- cwd: gitRoot,
194
- captureOutput: true,
195
- })
196
- .pipe(
197
- Effect.catchAll((err) => {
198
- verboseLog(`Formatter failed (silently ignoring): ${err}`)
199
- return Effect.void
200
- }),
201
- )
202
-
203
- verboseLog(
204
- `Formatted ${formattableFiles.length} file(s) with ${formatter}`,
205
- )
206
- }).pipe(
207
- // Catch any unexpected errors silently
208
- Effect.catchAll((err) => {
209
- verboseLog(`Formatting failed (silently ignoring): ${err}`)
210
- return Effect.void
211
- }),
212
- Effect.catchAllDefect((defect) => {
213
- verboseLog(`Formatting defect (silently ignoring): ${defect}`)
214
- return Effect.void
215
- }),
216
- ),
217
- }),
218
- },
219
- ) {}