@markjaquith/agency 3.9.1 → 3.9.3
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
CHANGED
|
@@ -616,21 +616,20 @@ handoffs restore the terminal before starting the interactive worker, then reope
|
|
|
616
616
|
the TUI when the worker returns. A configured
|
|
617
617
|
external chooser continues to offer goals and **Browse items** through that chooser.
|
|
618
618
|
|
|
619
|
-
| Goal | Choose in `act`
|
|
620
|
-
| ------------------------------------ |
|
|
621
|
-
| Manage repositories | Add, link, set up, materialize, fetch, verify, rename, update, unlink, or remove aliases
|
|
622
|
-
| Create work | Create a standard task, multi-phase task, investigation, epic, or a task inside an epic
|
|
623
|
-
| Split work | Add a phase / split this task; name the existing work's first phase
|
|
624
|
-
| Work on a task or phase | Work on this item
|
|
625
|
-
| Move from investigation to execution | Create implementation follow-up
|
|
626
|
-
| Review someone else's work | Review a PR, or a remote branch/commit
|
|
627
|
-
|
|
|
628
|
-
|
|
|
629
|
-
|
|
|
630
|
-
|
|
|
631
|
-
|
|
|
632
|
-
|
|
|
633
|
-
| Check or refresh the workbase | Validate, diagnose, reconcile all state, or inspect/update managed integration files | `validate`, `doctor`, `sync-all`, `integration-status`, `integration-sync` |
|
|
619
|
+
| Goal | Choose in `act` | Discovery/action ID |
|
|
620
|
+
| ------------------------------------ | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
621
|
+
| Manage repositories | Add, link, set up, materialize, fetch, verify, rename, update, unlink, or remove aliases | `repo-add`, `repo-link`, `repo-setup`, `repo-materialize`, `repo-fetch`, `repo-verify`, `repo-rename`, `repo-remote`, `repo-unlink`, `repo-remove` |
|
|
622
|
+
| Create work | Create a standard task, multi-phase task, investigation, epic, or a task inside an epic | `task-create`, `multi-phase-create`, `investigation-create`, `epic-create`, `task-create-in-epic` |
|
|
623
|
+
| Split work | Add a phase / split this task; name the existing work's first phase | `split` |
|
|
624
|
+
| Work on a task or phase | Work on this item | `work` |
|
|
625
|
+
| Move from investigation to execution | Create implementation follow-up | `handoff` |
|
|
626
|
+
| Review someone else's work | Review a PR, or a remote branch/commit | `review`, `review-ref` |
|
|
627
|
+
| Publish or update PR status | Push without a PR, refresh provider state, create a PR, mark a GitHub PR ready, or close it | `push`, `sync`, `pr`, `pr-ready`, `pr-close` |
|
|
628
|
+
| Maintain review work | Fetch and repin a review task to its current source | `review-refresh` |
|
|
629
|
+
| Organize work | Rename an item, move a task into/out of an epic, or add/remove a sibling dependency | `rename`, `move-to-epic`, `remove-from-epic`, `dependency-add`, `dependency-remove` |
|
|
630
|
+
| Archive terminal work | Archive | `archive` |
|
|
631
|
+
| See work status | See current or ready work | `current-work`, `ready-work` |
|
|
632
|
+
| Check or refresh the workbase | Validate, diagnose, reconcile all state, or inspect/update managed integration files | `validate`, `doctor`, `sync-all`, `integration-status`, `integration-sync` |
|
|
634
633
|
|
|
635
634
|
After creating a task, phase, review, or implementation follow-up, choose **Work
|
|
636
635
|
on the new item now** or **Finish**. Only choosing Work prepares checkouts and
|
package/package.json
CHANGED
|
@@ -292,6 +292,9 @@ const actionPresentation: Record<
|
|
|
292
292
|
},
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
export const isActActionId = (id: string): boolean =>
|
|
296
|
+
Object.hasOwn(actionPresentation, id)
|
|
297
|
+
|
|
295
298
|
const present = (details: Details) => {
|
|
296
299
|
const presentation = actionPresentation[details.id]
|
|
297
300
|
if (!presentation)
|
|
@@ -386,12 +389,6 @@ export const actionGroups = [
|
|
|
386
389
|
icon: "",
|
|
387
390
|
actions: ["push", "sync", "pr", "pr-ready", "pr-close"],
|
|
388
391
|
},
|
|
389
|
-
{
|
|
390
|
-
id: "close",
|
|
391
|
-
label: "Close or reopen work",
|
|
392
|
-
icon: "",
|
|
393
|
-
actions: ["complete", "drop", "sync", "reopen"],
|
|
394
|
-
},
|
|
395
392
|
{
|
|
396
393
|
id: "archive",
|
|
397
394
|
label: "Archive finished work",
|
package/src/commands/act.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
actionGroups,
|
|
15
15
|
actActions,
|
|
16
16
|
actionOutput,
|
|
17
|
+
isActActionId,
|
|
17
18
|
type ActAction,
|
|
18
19
|
type ActEntity,
|
|
19
20
|
} from "./act-actions"
|
|
@@ -360,10 +361,7 @@ const actStep = (
|
|
|
360
361
|
if (session && options.directory && isPath && !selectedKey && !actionId)
|
|
361
362
|
session.activateTab("workbase")
|
|
362
363
|
state.view = !selectedKey && !actionId ? "home" : "flow"
|
|
363
|
-
if (
|
|
364
|
-
actionId &&
|
|
365
|
-
!actionGroups.some((group) => group.actions.some((id) => id === actionId))
|
|
366
|
-
)
|
|
364
|
+
if (actionId && !isActActionId(actionId))
|
|
367
365
|
return yield* Effect.fail(
|
|
368
366
|
new Error(
|
|
369
367
|
`Unknown action '${actionId}'; use agency act --json to discover actions`,
|
|
@@ -5,6 +5,7 @@ import { PhaseService } from "./PhaseService"
|
|
|
5
5
|
import { TaskService } from "./TaskService"
|
|
6
6
|
import { WorkbaseService } from "./WorkbaseService"
|
|
7
7
|
import { parseGitCommits, type PushCommitMetadata } from "./push-validation"
|
|
8
|
+
import { isDirtyGitStatus } from "./VersionControlService"
|
|
8
9
|
|
|
9
10
|
type PushCategory =
|
|
10
11
|
| "precondition"
|
|
@@ -374,11 +375,11 @@ const publishGit = (
|
|
|
374
375
|
const status = yield* git(
|
|
375
376
|
fs,
|
|
376
377
|
checkout,
|
|
377
|
-
["status", "--porcelain=v1"],
|
|
378
|
+
["status", "--porcelain=v1", "-z", "--untracked-files=all"],
|
|
378
379
|
"Failed to inspect Git status",
|
|
379
380
|
"inspect",
|
|
380
381
|
)
|
|
381
|
-
if (status.stdout
|
|
382
|
+
if (isDirtyGitStatus(status.stdout)) {
|
|
382
383
|
return yield* pushError(
|
|
383
384
|
"Cannot publish a dirty Git worktree; commit or discard changes first",
|
|
384
385
|
"precondition",
|
|
@@ -58,6 +58,17 @@ export interface VersionControlBackend {
|
|
|
58
58
|
) => Effect.Effect<void, unknown, any>
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
const ignoredUntrackedGitPath = /^(?:.*\/)?\.DS_Store$/
|
|
62
|
+
|
|
63
|
+
export const isDirtyGitStatus = (output: string): boolean =>
|
|
64
|
+
(output.includes("\0") ? output.split("\0") : output.split(/\r?\n/)).some(
|
|
65
|
+
(line) => {
|
|
66
|
+
if (!line) return false
|
|
67
|
+
if (!line.startsWith("?? ")) return true
|
|
68
|
+
return !ignoredUntrackedGitPath.test(line.slice(3))
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
|
|
61
72
|
class VersionControlError extends Data.TaggedError("VersionControlError")<{
|
|
62
73
|
readonly message: string
|
|
63
74
|
}> {}
|
|
@@ -267,10 +278,20 @@ export class GitVersionControlService extends Effect.Service<GitVersionControlSe
|
|
|
267
278
|
Effect.gen(function* () {
|
|
268
279
|
const fs = yield* FileSystemService
|
|
269
280
|
const result = yield* fs.runCommand(
|
|
270
|
-
[
|
|
281
|
+
[
|
|
282
|
+
"git",
|
|
283
|
+
"-C",
|
|
284
|
+
workspacePath,
|
|
285
|
+
"status",
|
|
286
|
+
"--porcelain=v1",
|
|
287
|
+
"-z",
|
|
288
|
+
"--untracked-files=all",
|
|
289
|
+
],
|
|
271
290
|
{ captureOutput: true },
|
|
272
291
|
)
|
|
273
|
-
return result.exitCode === 0
|
|
292
|
+
return result.exitCode === 0
|
|
293
|
+
? isDirtyGitStatus(result.stdout)
|
|
294
|
+
: null
|
|
274
295
|
}),
|
|
275
296
|
fetch: (repositoryPath, remote = "origin", branch) =>
|
|
276
297
|
Effect.gen(function* () {
|
|
@@ -18,7 +18,10 @@ import type { BaseCommandOptions } from "../utils/command"
|
|
|
18
18
|
import { createLoggers } from "../utils/effect"
|
|
19
19
|
import { createProgress } from "../utils/progress"
|
|
20
20
|
import { withWorktreeLocks } from "./WorktreeLock"
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
isDirtyGitStatus,
|
|
23
|
+
VersionControlService,
|
|
24
|
+
} from "./VersionControlService"
|
|
22
25
|
import type {
|
|
23
26
|
RegisteredWorkspace,
|
|
24
27
|
VersionControlBackend,
|
|
@@ -75,7 +78,7 @@ interface GitWorktree {
|
|
|
75
78
|
readonly branch?: string
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
const managedWorktreeIgnorePatterns = ["/.worktree.lock"] as const
|
|
81
|
+
const managedWorktreeIgnorePatterns = ["/.worktree.lock", ".DS_Store"] as const
|
|
79
82
|
const vcs = "git" as const
|
|
80
83
|
|
|
81
84
|
interface WorktreeOwner {
|
|
@@ -550,13 +553,21 @@ const inspectExecution = (
|
|
|
550
553
|
const status =
|
|
551
554
|
actual && actualExists
|
|
552
555
|
? yield* fs.runCommand(
|
|
553
|
-
[
|
|
556
|
+
[
|
|
557
|
+
"git",
|
|
558
|
+
"-C",
|
|
559
|
+
actual.path,
|
|
560
|
+
"status",
|
|
561
|
+
"--porcelain=v1",
|
|
562
|
+
"-z",
|
|
563
|
+
"--untracked-files=all",
|
|
564
|
+
],
|
|
554
565
|
{ captureOutput: true },
|
|
555
566
|
)
|
|
556
567
|
: null
|
|
557
568
|
const dirty = status
|
|
558
569
|
? status.exitCode === 0
|
|
559
|
-
? status.stdout
|
|
570
|
+
? isDirtyGitStatus(status.stdout)
|
|
560
571
|
: null
|
|
561
572
|
: null
|
|
562
573
|
const expectedRef = "branch" in checkout ? checkout.branch : checkout.ref
|
|
@@ -2076,7 +2087,15 @@ export class WorktreeService extends Effect.Service<WorktreeService>()(
|
|
|
2076
2087
|
}
|
|
2077
2088
|
if (checkoutExists) {
|
|
2078
2089
|
const status = yield* fs.runCommand(
|
|
2079
|
-
[
|
|
2090
|
+
[
|
|
2091
|
+
"git",
|
|
2092
|
+
"-C",
|
|
2093
|
+
checkoutPath,
|
|
2094
|
+
"status",
|
|
2095
|
+
"--porcelain=v1",
|
|
2096
|
+
"-z",
|
|
2097
|
+
"--untracked-files=all",
|
|
2098
|
+
],
|
|
2080
2099
|
{ captureOutput: true },
|
|
2081
2100
|
)
|
|
2082
2101
|
if (status.exitCode !== 0) {
|
|
@@ -2084,7 +2103,7 @@ export class WorktreeService extends Effect.Service<WorktreeService>()(
|
|
|
2084
2103
|
message: `Failed to remove worktree for '${alias}': checkout cleanliness could not be verified: ${status.stderr.trim() || `git status exited with code ${status.exitCode}`}`,
|
|
2085
2104
|
})
|
|
2086
2105
|
}
|
|
2087
|
-
if (status.stdout
|
|
2106
|
+
if (isDirtyGitStatus(status.stdout)) {
|
|
2088
2107
|
return yield* new WorktreeError({
|
|
2089
2108
|
message: `Failed to remove worktree for '${alias}': checkout has uncommitted changes`,
|
|
2090
2109
|
})
|