@markjaquith/agency 1.9.6 → 1.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markjaquith/agency",
3
- "version": "1.9.6",
3
+ "version": "1.10.0",
4
4
  "description": "Manages personal agents files",
5
5
  "keywords": [
6
6
  "agents",
@@ -78,6 +78,29 @@ describe("emit command", () => {
78
78
  })
79
79
 
80
80
  describe("basic functionality", () => {
81
+ test("fails when there are uncommitted changes", async () => {
82
+ // Create uncommitted changes
83
+ await Bun.write(join(tempDir, "uncommitted.txt"), "uncommitted\n")
84
+
85
+ expect(
86
+ runTestEffect(emit({ silent: true, skipFilter: true })),
87
+ ).rejects.toThrow(/uncommitted changes/)
88
+ })
89
+
90
+ test("fails when there are staged but uncommitted changes", async () => {
91
+ // Create a staged but uncommitted change
92
+ await Bun.write(join(tempDir, "staged.txt"), "staged\n")
93
+ await Bun.spawn(["git", "add", "staged.txt"], {
94
+ cwd: tempDir,
95
+ stdout: "pipe",
96
+ stderr: "pipe",
97
+ }).exited
98
+
99
+ expect(
100
+ runTestEffect(emit({ silent: true, skipFilter: true })),
101
+ ).rejects.toThrow(/uncommitted changes/)
102
+ })
103
+
81
104
  test("throws error when git-filter-repo is not installed", async () => {
82
105
  const hasGitFilterRepo = await checkGitFilterRepo()
83
106
  if (hasGitFilterRepo) {
@@ -54,6 +54,21 @@ export const emitCore = (gitRoot: string, options: EmitOptions) =>
54
54
  const fs = yield* FileSystemService
55
55
  const filterRepo = yield* FilterRepoService
56
56
 
57
+ // Check for uncommitted changes
58
+ const statusOutput = yield* git.getStatus(gitRoot)
59
+
60
+ if (statusOutput && statusOutput.trim().length > 0) {
61
+ return yield* Effect.fail(
62
+ new Error(
63
+ `You have uncommitted changes. Please commit or stash them before emitting.\n` +
64
+ `The emit command performs internal checkouts that would destroy uncommitted work.\n` +
65
+ `Run 'git status' to see the changes.`,
66
+ ),
67
+ )
68
+ }
69
+
70
+ verboseLog(`Working directory is clean`)
71
+
57
72
  // Check if git-filter-repo is installed
58
73
  const hasFilterRepo = yield* filterRepo.isInstalled()
59
74
  if (!hasFilterRepo) {
@@ -30,9 +30,9 @@ async function setupAgencyJson(gitRoot: string): Promise<void> {
30
30
  await addAndCommit(gitRoot, "agency.json", "Add agency.json")
31
31
  }
32
32
 
33
- async function setupBareRemote(tempDir: string): Promise<string> {
34
- // Create a bare repository to use as remote
35
- const remoteDir = join(tempDir, "remote.git")
33
+ async function setupBareRemote(): Promise<string> {
34
+ // Create a bare repository outside the working tree to avoid polluting git status
35
+ const remoteDir = await createTempDir()
36
36
  await Bun.spawn(["git", "init", "--bare", remoteDir], {
37
37
  stdout: "pipe",
38
38
  stderr: "pipe",
@@ -64,7 +64,7 @@ describe("push command", () => {
64
64
  }
65
65
 
66
66
  // Setup bare remote and push main
67
- remoteDir = await setupBareRemote(tempDir)
67
+ remoteDir = await setupBareRemote()
68
68
  await Bun.spawn(["git", "remote", "add", "origin", remoteDir], {
69
69
  cwd: tempDir,
70
70
  stdout: "pipe",
@@ -88,9 +88,37 @@ describe("push command", () => {
88
88
  process.chdir(originalCwd)
89
89
  delete process.env.AGENCY_CONFIG_PATH
90
90
  await cleanupTempDir(tempDir)
91
+ await cleanupTempDir(remoteDir)
91
92
  })
92
93
 
93
94
  describe("basic functionality", () => {
95
+ test("fails when there are uncommitted changes", async () => {
96
+ // Create uncommitted changes
97
+ await Bun.write(join(tempDir, "uncommitted.txt"), "uncommitted\n")
98
+
99
+ expect(
100
+ runTestEffect(
101
+ push({ baseBranch: "main", silent: true, skipFilter: true }),
102
+ ),
103
+ ).rejects.toThrow(/uncommitted changes/)
104
+ })
105
+
106
+ test("fails when there are staged but uncommitted changes", async () => {
107
+ // Create a staged but uncommitted change
108
+ await Bun.write(join(tempDir, "staged.txt"), "staged\n")
109
+ await Bun.spawn(["git", "add", "staged.txt"], {
110
+ cwd: tempDir,
111
+ stdout: "pipe",
112
+ stderr: "pipe",
113
+ }).exited
114
+
115
+ expect(
116
+ runTestEffect(
117
+ push({ baseBranch: "main", silent: true, skipFilter: true }),
118
+ ),
119
+ ).rejects.toThrow(/uncommitted changes/)
120
+ })
121
+
94
122
  test("creates emit branch, pushes it, and returns to source", async () => {
95
123
  // We're on agency--feature branch (source)
96
124
  expect(await getCurrentBranch(tempDir)).toBe("agency--feature")
@@ -91,6 +91,21 @@ const pushCore = (gitRoot: string, options: PushOptions) =>
91
91
  const git = yield* GitService
92
92
  const configService = yield* ConfigService
93
93
 
94
+ // Check for uncommitted changes
95
+ const statusOutput = yield* git.getStatus(gitRoot)
96
+
97
+ if (statusOutput && statusOutput.trim().length > 0) {
98
+ return yield* Effect.fail(
99
+ new Error(
100
+ `You have uncommitted changes. Please commit or stash them before pushing.\n` +
101
+ `The push command performs internal checkouts that would destroy uncommitted work.\n` +
102
+ `Run 'git status' to see the changes.`,
103
+ ),
104
+ )
105
+ }
106
+
107
+ verboseLog(`Working directory is clean`)
108
+
94
109
  // Load config to check emit branch pattern
95
110
  const config = yield* configService.loadConfig()
96
111
 
@@ -18,3 +18,5 @@ All work on this repository should begin by reading and understanding `TASK.md`.
18
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
19
 
20
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.