@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
package/src/utils/colors.ts
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Color highlighting utilities for CLI output
|
|
3
|
-
*
|
|
4
|
-
* This module provides centralized color management for highlighting
|
|
5
|
-
* meaningful values in CLI feedback. All highlight types use the same
|
|
6
|
-
* base color initially, but are separated into buckets to allow for
|
|
7
|
-
* future customization.
|
|
8
|
-
*
|
|
9
|
-
* Colors can be disabled via:
|
|
10
|
-
* - NO_COLOR environment variable (standard)
|
|
11
|
-
* - Setting colorsEnabled to false
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
// ANSI color codes
|
|
15
|
-
const RESET = "\x1b[0m"
|
|
16
|
-
const CYAN_BRIGHT = "\x1b[96m"
|
|
17
|
-
const GREEN = "\x1b[32m"
|
|
18
|
-
const YELLOW = "\x1b[33m"
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Central color configuration
|
|
22
|
-
* All buckets use the same color initially (bright cyan)
|
|
23
|
-
* but can be easily changed independently in the future
|
|
24
|
-
*/
|
|
25
|
-
const COLORS = {
|
|
26
|
-
branch: CYAN_BRIGHT,
|
|
27
|
-
template: CYAN_BRIGHT,
|
|
28
|
-
file: CYAN_BRIGHT,
|
|
29
|
-
setting: CYAN_BRIGHT,
|
|
30
|
-
value: CYAN_BRIGHT,
|
|
31
|
-
commit: CYAN_BRIGHT,
|
|
32
|
-
pattern: CYAN_BRIGHT,
|
|
33
|
-
remote: YELLOW,
|
|
34
|
-
} as const
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Global flag to enable/disable colors
|
|
38
|
-
* Defaults to enabled unless NO_COLOR environment variable is set
|
|
39
|
-
*/
|
|
40
|
-
let colorsEnabled = !process.env.NO_COLOR
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Enable or disable color output
|
|
44
|
-
* @param enabled - Whether to enable colors
|
|
45
|
-
*/
|
|
46
|
-
export function setColorsEnabled(enabled: boolean): void {
|
|
47
|
-
colorsEnabled = enabled
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Check if colors are currently enabled
|
|
52
|
-
*/
|
|
53
|
-
function areColorsEnabled(): boolean {
|
|
54
|
-
return colorsEnabled
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Internal helper to apply color formatting
|
|
59
|
-
* Returns plain text if colors are disabled
|
|
60
|
-
*/
|
|
61
|
-
function colorize(text: string, color: string): string {
|
|
62
|
-
if (!colorsEnabled) {
|
|
63
|
-
return text
|
|
64
|
-
}
|
|
65
|
-
return `${color}${text}${RESET}`
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Highlight a branch name
|
|
70
|
-
* @example highlight.branch("main") -> "\x1b[96mmain\x1b[0m"
|
|
71
|
-
*/
|
|
72
|
-
function branch(name: string): string {
|
|
73
|
-
return colorize(name, COLORS.branch)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Highlight a template name
|
|
78
|
-
* @example highlight.template("my-template") -> "\x1b[96mmy-template\x1b[0m"
|
|
79
|
-
*/
|
|
80
|
-
function template(name: string): string {
|
|
81
|
-
return colorize(name, COLORS.template)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Highlight a file name or path
|
|
86
|
-
* @example highlight.file("AGENTS.md") -> "\x1b[96mAGENTS.md\x1b[0m"
|
|
87
|
-
*/
|
|
88
|
-
function file(name: string): string {
|
|
89
|
-
return colorize(name, COLORS.file)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Highlight a setting name
|
|
94
|
-
* @example highlight.setting("agency.template") -> "\x1b[96magency.template\x1b[0m"
|
|
95
|
-
*/
|
|
96
|
-
function setting(name: string): string {
|
|
97
|
-
return colorize(name, COLORS.setting)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Highlight a numeric value or count
|
|
102
|
-
* @example highlight.value("3") -> "\x1b[96m3\x1b[0m"
|
|
103
|
-
*/
|
|
104
|
-
function value(val: string | number): string {
|
|
105
|
-
return colorize(String(val), COLORS.value)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Highlight a commit hash
|
|
110
|
-
* @example highlight.commit("abc123") -> "\x1b[96mabc123\x1b[0m"
|
|
111
|
-
*/
|
|
112
|
-
function commit(hash: string): string {
|
|
113
|
-
return colorize(hash, COLORS.commit)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Highlight a pattern or placeholder
|
|
118
|
-
* @example highlight.pattern("{task}") -> "\x1b[96m{task}\x1b[0m"
|
|
119
|
-
*/
|
|
120
|
-
function pattern(text: string): string {
|
|
121
|
-
return colorize(text, COLORS.pattern)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Highlight a git remote name
|
|
126
|
-
* @example highlight.remote("origin") -> "\x1b[33morigin\x1b[0m"
|
|
127
|
-
*/
|
|
128
|
-
function remote(name: string): string {
|
|
129
|
-
return colorize(name, COLORS.remote)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Prepends a green checkmark to a message for success output
|
|
134
|
-
* Uses the same checkmark symbol and color as ora for consistency
|
|
135
|
-
* @param message - The message to prepend the checkmark to
|
|
136
|
-
* @example log(done(`Merged ${highlight.branch("main")}`))
|
|
137
|
-
* @example log(done("Operation completed"))
|
|
138
|
-
*/
|
|
139
|
-
export function done(message: string): string {
|
|
140
|
-
const checkmark = colorsEnabled ? `\x1b[32m✔\x1b[0m` : "✔"
|
|
141
|
-
return `${checkmark} ${message}`
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Prepends an info icon to a message for informational output
|
|
146
|
-
* @param message - The message to prepend the info icon to
|
|
147
|
-
* @example log(info("Skipping task description"))
|
|
148
|
-
* @example log(info(`File already exists`))
|
|
149
|
-
*/
|
|
150
|
-
export function info(message: string): string {
|
|
151
|
-
return `ⓘ ${message}`
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Returns "s" if count is not 1, empty string otherwise
|
|
156
|
-
* Useful for pluralizing words in messages
|
|
157
|
-
* @param count - The count to check
|
|
158
|
-
* @example `Committed ${count} file${plural(count)}` -> "Committed 1 file" or "Committed 3 files"
|
|
159
|
-
*/
|
|
160
|
-
export function plural(count: number): string {
|
|
161
|
-
return count === 1 ? "" : "s"
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Default export as namespace for convenient usage
|
|
166
|
-
* @example import highlight from "./colors"
|
|
167
|
-
* @example console.log(`Switched to ${highlight.branch("main")}`)
|
|
168
|
-
*/
|
|
169
|
-
export default {
|
|
170
|
-
branch,
|
|
171
|
-
template,
|
|
172
|
-
file,
|
|
173
|
-
setting,
|
|
174
|
-
value,
|
|
175
|
-
commit,
|
|
176
|
-
pattern,
|
|
177
|
-
remote,
|
|
178
|
-
}
|
package/src/utils/git-path.ts
DELETED
package/src/utils/glob.test.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { test, expect, describe, beforeAll, afterAll } from "bun:test"
|
|
2
|
-
import { mkdtemp, rm, mkdir, writeFile } from "node:fs/promises"
|
|
3
|
-
import { tmpdir } from "node:os"
|
|
4
|
-
import { join } from "node:path"
|
|
5
|
-
import {
|
|
6
|
-
isGlobPattern,
|
|
7
|
-
matchesGlob,
|
|
8
|
-
expandGlobs,
|
|
9
|
-
dirToGlobPattern,
|
|
10
|
-
getTopLevelDir,
|
|
11
|
-
} from "./glob"
|
|
12
|
-
|
|
13
|
-
describe("isGlobPattern", () => {
|
|
14
|
-
test("returns true for patterns with asterisk", () => {
|
|
15
|
-
expect(isGlobPattern("*.ts")).toBe(true)
|
|
16
|
-
expect(isGlobPattern("**/*.ts")).toBe(true)
|
|
17
|
-
expect(isGlobPattern("plans/*")).toBe(true)
|
|
18
|
-
expect(isGlobPattern("plans/**")).toBe(true)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
test("returns true for patterns with question mark", () => {
|
|
22
|
-
expect(isGlobPattern("file?.ts")).toBe(true)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test("returns true for patterns with character classes", () => {
|
|
26
|
-
expect(isGlobPattern("[abc].ts")).toBe(true)
|
|
27
|
-
expect(isGlobPattern("file[0-9].ts")).toBe(true)
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test("returns false for plain file paths", () => {
|
|
31
|
-
expect(isGlobPattern("foo.ts")).toBe(false)
|
|
32
|
-
expect(isGlobPattern("plans/README.md")).toBe(false)
|
|
33
|
-
expect(isGlobPattern("src/utils/glob.ts")).toBe(false)
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
describe("matchesGlob", () => {
|
|
38
|
-
test("matches files with simple wildcard", () => {
|
|
39
|
-
expect(matchesGlob("*.ts", "file.ts")).toBe(true)
|
|
40
|
-
expect(matchesGlob("*.ts", "file.js")).toBe(false)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
test("matches files in directories with **", () => {
|
|
44
|
-
expect(matchesGlob("plans/**", "plans/foo.md")).toBe(true)
|
|
45
|
-
expect(matchesGlob("plans/**", "plans/sub/bar.md")).toBe(true)
|
|
46
|
-
expect(matchesGlob("plans/**", "other/foo.md")).toBe(false)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
test("matches nested directory patterns", () => {
|
|
50
|
-
expect(matchesGlob("src/**/*.ts", "src/utils/glob.ts")).toBe(true)
|
|
51
|
-
expect(matchesGlob("src/**/*.ts", "src/glob.ts")).toBe(true)
|
|
52
|
-
expect(matchesGlob("src/**/*.ts", "lib/glob.ts")).toBe(false)
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
describe("dirToGlobPattern", () => {
|
|
57
|
-
test("converts directory to glob pattern", () => {
|
|
58
|
-
expect(dirToGlobPattern("plans")).toBe("plans/**")
|
|
59
|
-
expect(dirToGlobPattern("src/components")).toBe("src/components/**")
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
test("removes trailing slash before adding glob", () => {
|
|
63
|
-
expect(dirToGlobPattern("plans/")).toBe("plans/**")
|
|
64
|
-
expect(dirToGlobPattern("plans//")).toBe("plans/**")
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
describe("getTopLevelDir", () => {
|
|
69
|
-
test("returns top-level directory from path", () => {
|
|
70
|
-
expect(getTopLevelDir("plans/foo.md")).toBe("plans")
|
|
71
|
-
expect(getTopLevelDir("plans/sub/bar.md")).toBe("plans")
|
|
72
|
-
expect(getTopLevelDir("src/utils/glob.ts")).toBe("src")
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test("returns null for root-level files", () => {
|
|
76
|
-
expect(getTopLevelDir("README.md")).toBe(null)
|
|
77
|
-
expect(getTopLevelDir("file.ts")).toBe(null)
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
describe("expandGlobs", () => {
|
|
82
|
-
let tempDir: string
|
|
83
|
-
|
|
84
|
-
beforeAll(async () => {
|
|
85
|
-
// Create temp directory with test files
|
|
86
|
-
tempDir = await mkdtemp(join(tmpdir(), "glob-test-"))
|
|
87
|
-
|
|
88
|
-
// Create directory structure:
|
|
89
|
-
// tempDir/
|
|
90
|
-
// plans/
|
|
91
|
-
// foo.md
|
|
92
|
-
// sub/
|
|
93
|
-
// bar.md
|
|
94
|
-
// other/
|
|
95
|
-
// baz.md
|
|
96
|
-
// root.txt
|
|
97
|
-
await mkdir(join(tempDir, "plans"))
|
|
98
|
-
await mkdir(join(tempDir, "plans", "sub"))
|
|
99
|
-
await mkdir(join(tempDir, "other"))
|
|
100
|
-
await mkdir(join(tempDir, ".agents", "foo"), { recursive: true })
|
|
101
|
-
|
|
102
|
-
await writeFile(join(tempDir, "plans", "foo.md"), "foo")
|
|
103
|
-
await writeFile(join(tempDir, "plans", "sub", "bar.md"), "bar")
|
|
104
|
-
await writeFile(join(tempDir, "other", "baz.md"), "baz")
|
|
105
|
-
await writeFile(join(tempDir, ".agents", "foo", "bar.whatever"), "dot")
|
|
106
|
-
await writeFile(join(tempDir, "root.txt"), "root")
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
afterAll(async () => {
|
|
110
|
-
await rm(tempDir, { recursive: true })
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
test("expands glob pattern to matching files", async () => {
|
|
114
|
-
const files = await expandGlobs(["plans/**"], tempDir)
|
|
115
|
-
expect(files.sort()).toEqual(["plans/foo.md", "plans/sub/bar.md"].sort())
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
test("preserves non-glob paths as-is", async () => {
|
|
119
|
-
const files = await expandGlobs(["root.txt"], tempDir)
|
|
120
|
-
expect(files).toEqual(["root.txt"])
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test("combines glob and non-glob patterns", async () => {
|
|
124
|
-
const files = await expandGlobs(["plans/**", "root.txt"], tempDir)
|
|
125
|
-
expect(files.sort()).toEqual(
|
|
126
|
-
["plans/foo.md", "plans/sub/bar.md", "root.txt"].sort(),
|
|
127
|
-
)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
test("deduplicates results", async () => {
|
|
131
|
-
const files = await expandGlobs(
|
|
132
|
-
["plans/**", "plans/foo.md", "plans/**"],
|
|
133
|
-
tempDir,
|
|
134
|
-
)
|
|
135
|
-
// Should not have duplicates
|
|
136
|
-
const uniqueFiles = [...new Set(files)]
|
|
137
|
-
expect(files.length).toBe(uniqueFiles.length)
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
test("returns non-glob paths even if they don't exist", async () => {
|
|
141
|
-
const files = await expandGlobs(["nonexistent.txt"], tempDir)
|
|
142
|
-
expect(files).toEqual(["nonexistent.txt"])
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
test("returns empty array for non-matching glob", async () => {
|
|
146
|
-
const files = await expandGlobs(["nonexistent/**"], tempDir)
|
|
147
|
-
expect(files).toEqual([])
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
test("handles multiple glob patterns", async () => {
|
|
151
|
-
const files = await expandGlobs(["plans/**", "other/**"], tempDir)
|
|
152
|
-
expect(files.sort()).toEqual(
|
|
153
|
-
["plans/foo.md", "plans/sub/bar.md", "other/baz.md"].sort(),
|
|
154
|
-
)
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
test("expands glob patterns inside dot-directories", async () => {
|
|
158
|
-
const files = await expandGlobs([".agents/**"], tempDir)
|
|
159
|
-
expect(files).toEqual([".agents/foo/bar.whatever"])
|
|
160
|
-
})
|
|
161
|
-
})
|
package/src/utils/glob.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilities for glob pattern matching and expansion.
|
|
3
|
-
* Uses Bun's built-in Glob API.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Check if a string contains glob pattern characters.
|
|
8
|
-
* This detects wildcards like *, **, ?, and character classes like [abc].
|
|
9
|
-
*/
|
|
10
|
-
export function isGlobPattern(str: string): boolean {
|
|
11
|
-
return /[*?[\]]/.test(str)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Check if a path matches a glob pattern.
|
|
16
|
-
* Uses Bun.Glob for pattern matching.
|
|
17
|
-
*/
|
|
18
|
-
export function matchesGlob(pattern: string, path: string): boolean {
|
|
19
|
-
const glob = new Bun.Glob(pattern)
|
|
20
|
-
return glob.match(path)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Expand glob patterns to actual file paths.
|
|
25
|
-
* Non-glob paths are returned as-is.
|
|
26
|
-
*
|
|
27
|
-
* @param patterns - Array of file paths or glob patterns
|
|
28
|
-
* @param cwd - Working directory to resolve patterns against
|
|
29
|
-
* @returns Array of expanded file paths (deduplicated)
|
|
30
|
-
*/
|
|
31
|
-
export async function expandGlobs(
|
|
32
|
-
patterns: string[],
|
|
33
|
-
cwd: string,
|
|
34
|
-
): Promise<string[]> {
|
|
35
|
-
const files = new Set<string>()
|
|
36
|
-
|
|
37
|
-
for (const pattern of patterns) {
|
|
38
|
-
if (isGlobPattern(pattern)) {
|
|
39
|
-
// Expand glob pattern
|
|
40
|
-
const glob = new Bun.Glob(pattern)
|
|
41
|
-
for await (const file of glob.scan({ cwd, dot: true })) {
|
|
42
|
-
files.add(file)
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
// Non-glob path, add as-is
|
|
46
|
-
files.add(pattern)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return Array.from(files)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Convert a directory path to a glob pattern.
|
|
55
|
-
* Appends /** to match all files recursively in the directory.
|
|
56
|
-
*
|
|
57
|
-
* @param dirPath - Directory path (e.g., "plans" or "plans/")
|
|
58
|
-
* @returns Glob pattern (e.g., "plans/**")
|
|
59
|
-
*/
|
|
60
|
-
export function dirToGlobPattern(dirPath: string): string {
|
|
61
|
-
// Remove trailing slash if present
|
|
62
|
-
const normalized = dirPath.replace(/\/+$/, "")
|
|
63
|
-
return `${normalized}/**`
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Extract the top-level directory from a file path.
|
|
68
|
-
*
|
|
69
|
-
* @param filePath - File path (e.g., "plans/foo/bar.md")
|
|
70
|
-
* @returns Top-level directory name (e.g., "plans") or null if no directory
|
|
71
|
-
*/
|
|
72
|
-
export function getTopLevelDir(filePath: string): string | null {
|
|
73
|
-
const parts = filePath.split("/")
|
|
74
|
-
if (parts.length > 1) {
|
|
75
|
-
return parts[0] || null
|
|
76
|
-
}
|
|
77
|
-
return null
|
|
78
|
-
}
|
package/src/utils/paths.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilities for resolving agency configuration paths.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { homedir } from "node:os"
|
|
6
|
-
import { join } from "node:path"
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get the agency configuration directory.
|
|
10
|
-
* Defaults to ~/.config/agency, can be overridden via AGENCY_CONFIG_DIR env var.
|
|
11
|
-
* @param override - Optional override for the config directory (used in testing)
|
|
12
|
-
*/
|
|
13
|
-
export function getAgencyConfigDir(override?: string): string {
|
|
14
|
-
return (
|
|
15
|
-
override ||
|
|
16
|
-
process.env.AGENCY_CONFIG_DIR ||
|
|
17
|
-
join(homedir(), ".config", "agency")
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Get the path to the agency config file (agency.json).
|
|
23
|
-
* Can be overridden via AGENCY_CONFIG_PATH env var.
|
|
24
|
-
* @param configDir - Optional config directory override
|
|
25
|
-
*/
|
|
26
|
-
export function getAgencyConfigPath(configDir?: string): string {
|
|
27
|
-
return (
|
|
28
|
-
process.env.AGENCY_CONFIG_PATH ||
|
|
29
|
-
join(getAgencyConfigDir(configDir), "agency.json")
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Get the templates directory path.
|
|
35
|
-
* @param configDir - Optional config directory override
|
|
36
|
-
*/
|
|
37
|
-
export function getTemplatesDir(configDir?: string): string {
|
|
38
|
-
return join(getAgencyConfigDir(configDir), "templates")
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Get the path to a specific template directory.
|
|
43
|
-
* @param templateName - Name of the template
|
|
44
|
-
* @param configDir - Optional config directory override
|
|
45
|
-
*/
|
|
46
|
-
export function getTemplateDir(
|
|
47
|
-
templateName: string,
|
|
48
|
-
configDir?: string,
|
|
49
|
-
): string {
|
|
50
|
-
return join(getTemplatesDir(configDir), templateName)
|
|
51
|
-
}
|