duaer-spec 0.2.0 → 0.4.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.
@@ -1,35 +1,28 @@
1
1
  ---
2
- description: Features and hotfixes must go through Duaer; no code from verbal asks alone
2
+ description: Prefer simple /duaer-do; Spec before code; handoff before claiming done
3
3
  alwaysApply: true
4
4
  ---
5
5
 
6
- # Duaer required (NON-NEGOTIABLE)
6
+ # Duaer (keep it simple)
7
7
 
8
- Treat agents as digital employees: assign a Brief (Spec) before code; accept
9
- only via converge. See `DUADER.md` and `.duaer/memory/constitution.md`.
8
+ Treat agents as digital employees. Prefer **one** everyday command:
10
9
 
11
- If this rule conflicts with root `AGENTS.md` / `docs/agent/` (worktrees, commits,
12
- Issue/PR), **AGENTS.md wins**.
10
+ `/duaer-do <ask>` then `duaer status` if needed.
13
11
 
14
- ## When Duaer is mandatory
12
+ If this conflicts with `AGENTS.md` (worktrees, commits, Issue/PR), **AGENTS.md wins**.
15
13
 
16
- Always write/update a feature spec (Brief) before coding for:
14
+ ## Everyday
17
15
 
18
- - New features / user-visible behavior
19
- - Architecture or contract changes
20
- - Hotfixes / regressions (still Spec; shorter path allowed)
16
+ 1. `/duaer-do` assign Brief, implement, converge
17
+ 2. Do **not** claim "done" unless the job is accepted (`duaer status`)
18
+ 3. Do **not** make the user chain specify → plan → tasks → implement → converge for normal asks
21
19
 
22
- Forbidden: jump from chat to code with no `spec.md` / feature directory under `.duaer/specs/`.
20
+ ## When to use step skills
23
21
 
24
- ## Default flow
22
+ - `/duaer-specify` only — reshape the Brief
23
+ - `/duaer-plan` / `/duaer-tasks` — large or architectural work
24
+ - `/duaer-implement` / `/duaer-converge` — resume mid-job
25
25
 
26
- `/duaer-specify` `/duaer-plan` `/duaer-tasks` `/duaer-implement` `/duaer-converge`
26
+ Hotfix: still a short Spec + accept (or `/duaer-do`).
27
27
 
28
- ## Hotfix (still Spec, shortened)
29
-
30
- `/duaer-specify` (mark hotfix: symptom / root cause / acceptance) → `/duaer-tasks` → `/duaer-implement` → `/duaer-converge`
31
-
32
- - Implement only from `tasks.md` when it exists
33
- - Update `.duaer/memory/project-context.md` when architecture changes
34
- - Run the verification named in the feature docs before merge
35
- - Before merge: `duaer check . --gate` (Spec present, no open tasks, `delivery.json` accepted)
28
+ Forbidden: jump from chat to code with no Spec under `.duaer/specs/`.
@@ -265,15 +265,18 @@ Append to the **end** of `tasks.md`, per the append contract:
265
265
  path instead — do not stamp accepted).
266
266
  - Report: **"✅ Converged — the implementation satisfies the spec, plan, and tasks."**
267
267
  - Include the summary counts of what was checked.
268
- - Tell the user they can run `duaer check . --gate` to verify the machine gate.
268
+ - Tell the user they can run `duaer job .` to see handoff status (policy default: coach).
269
+ - Reminder: this is **job acceptance**, not a repository merge lock.
269
270
  ### 8. Provide Next Actions (Handoff)
270
271
 
271
272
  - On `tasks_appended`: state how many tasks were appended under which phase, and recommend
272
273
  running `/duaer-implement` to complete them; note that a follow-up converge
273
274
  run will find fewer or no remaining items. Remind: `delivery.json` is `open` —
274
- `duaer check --gate` will fail until accepted.
275
- - On `converged`: recommend `duaer check . --gate`, then review / opening a PR. No further
276
- implement pass is needed for this feature's specified scope.
275
+ under coach/strict, do **not** claim the job is done; `duaer job .` will show unfinished.
276
+ - On `converged`: recommend `duaer job .`, then ask the user to review. No further
277
+ implement pass is needed for this feature's specified scope. Use the handoff script:
278
+ "✅ Job accepted — Brief satisfied per converge; ready for your review."
279
+ Do not frame this as a git/CI merge requirement.
277
280
 
278
281
  ### 9. Check for extension hooks
279
282
 
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: "duaer-do"
3
+ description: "Simple one-shot: turn a user ask into an assigned job, implement it, and hand off. Prefer this for everyday work instead of chaining specify/plan/tasks/implement/converge."
4
+ compatibility: "Requires duaer-spec project structure with .duaer/ directory"
5
+ metadata:
6
+ author: "duaer-spec"
7
+ source: "duaer-spec"
8
+ ---
9
+
10
+ ## User Input
11
+
12
+ ```text
13
+ $ARGUMENTS
14
+ ```
15
+
16
+ You **MUST** use the user input as the job request (if not empty).
17
+
18
+ ## Goal
19
+
20
+ **Everyday path — keep it simple.** Run one digital-employee job end-to-end without
21
+ asking the user to chain slash commands.
22
+
23
+ Do **not** lecture about methodology. Do the work.
24
+
25
+ ## Steps (always in order)
26
+
27
+ 1. **Assign** — Ensure an active feature Brief exists:
28
+ - If there is already a clear active job (`.duaer/active-job.json` + `spec.md`)
29
+ that matches this ask, update that `spec.md` acceptance as needed.
30
+ - Otherwise run the same outcome as `/duaer-specify` for this ask
31
+ (create `.duaer/specs/<nnn-slug>/spec.md`, write `.duaer/active-job.json`).
32
+ - For small asks: keep the Spec short (what / why / acceptance only).
33
+
34
+ 2. **Break down (lightweight)** — If `tasks.md` is missing:
35
+ - Write a short `tasks.md` with a few `- [ ]` items from the Spec.
36
+ - Skip `/duaer-plan` unless architecture or contracts clearly change.
37
+ - If `tasks.md` already exists, use it.
38
+
39
+ 3. **Work** — Implement open tasks (same rules as `/duaer-implement`).
40
+ Mark tasks `[x]` as you finish them. Do not expand scope past the Spec.
41
+
42
+ 4. **Accept** — Run the same outcome as `/duaer-converge`:
43
+ - Compare code to Spec; append gap tasks or stamp `delivery.json` accepted.
44
+ - If gaps were appended, either implement them now (preferred for small jobs)
45
+ or stop and tell the user what remains — once.
46
+
47
+ 5. **Handoff line** — End with exactly one clear status:
48
+ - If accepted: `✅ Job accepted — ready for your review.`
49
+ - If not: `⏳ Job not accepted yet — <one sentence what to do next>.`
50
+ - Do **not** claim "done" unless `delivery.json` is `accepted` and tasks are clear
51
+ (coach/strict policy). Git is not locked.
52
+
53
+ ## Hotfix
54
+
55
+ If the user says hotfix/bug/regression: still write a short Spec (symptom / cause /
56
+ acceptance), then tasks → implement → converge. Same handoff line.
57
+
58
+ ## Out of scope for this command
59
+
60
+ - Do not require the user to run `/duaer-plan`, `/duaer-checklist`, or `/duaer-analyze`.
61
+ - Do not discuss policy modes unless they ask.
62
+ - Do not turn this into a merge/CI lecture.
@@ -104,11 +104,23 @@ Given that feature description, do this:
104
104
  Write the actual resolved directory path value (for example, `specs/003-user-auth`), not the literal string `SPECIFY_FEATURE_DIRECTORY`.
105
105
  This allows downstream commands (`/duaer-plan`, `/duaer-tasks`, etc.) to locate the feature directory without relying on git branch name conventions.
106
106
 
107
+ - Also write / overwrite `.duaer/active-job.json` (job handoff pointer):
108
+ ```json
109
+ {
110
+ "schemaVersion": 1,
111
+ "feature": "<directory-name only, e.g. 003-user-auth>",
112
+ "featureDirectory": "<resolved feature dir>",
113
+ "updatedAt": "<ISO-8601 UTC>",
114
+ "source": "duaer-specify"
115
+ }
116
+ ```
117
+ `feature` is the basename under `.duaer/specs/` (or `specs/`) that `duaer job` resolves.
118
+
107
119
  **IMPORTANT**:
108
120
  - You must only create one feature per `/duaer-specify` invocation
109
121
  - The spec directory name and the git branch name are independent — they may be the same but that is the user's choice
110
122
  - The spec directory and file are always created by this command, never by the hook
111
-
123
+ - Setting `active-job.json` makes this the job the employee must hand off; it does **not** lock git
112
124
  4. Load the resolved active `spec-template` file to understand required sections.
113
125
 
114
126
  5. **IF EXISTS**: Load `.duaer/memory/constitution.md` for project principles and governance constraints.
@@ -0,0 +1,6 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "mode": "coach",
4
+ "scope": "active",
5
+ "notes": "Job-level handoff. off=record only; coach=guide (default); strict=do not claim done until accepted. Not a git merge lock."
6
+ }
@@ -10,13 +10,14 @@ Duaer for what to build.
10
10
 
11
11
  ### I. Spec before code (NON-NEGOTIABLE)
12
12
 
13
- Features, architecture changes, and hotfixes start with a Duaer feature
14
- artifact before implementation. Default path:
13
+ Everyday: `/duaer-do` (assigns Spec, implements, converges).
14
+ Or the full chain. Never skip Spec or converge.
15
15
 
16
- `constitution` `specify` (optional `clarify`) → `plan` → (optional `checklist` / `analyze`) → `tasks` → `implement` → `converge`
16
+ Default path for fine control:
17
17
 
18
- Hotfix may shorten to: `specify` (mark hotfix) → `tasks` → `implement` → `converge`.
19
- Never skip `specify` or `converge`.
18
+ `specify` (optional plan/tasks) → `implement` → `converge` → `duaer status`
19
+
20
+ Hotfix may shorten; still Spec + converge (or `/duaer-do`).
20
21
 
21
22
  ### II. Read as-is before changing
22
23
 
@@ -36,7 +37,6 @@ Do not expand scope without updating the spec and tasks.
36
37
  Mark tasks complete only when the stated verification (tests, manual checks,
37
38
  or converge) has been performed or explicitly waived in the feature docs.
38
39
 
39
- Before merge, run `duaer check . --gate`. It fails on missing Spec, open
40
- `- [ ]` tasks, or `delivery.json` that is not `status: "accepted"`. Converge
41
- assessment is still agent-assisted; the gate makes the **handoff state**
42
- machine-checkable.
40
+ **Job handoff (not a git lock):** prefer `duaer status`. Default policy is
41
+ `coach` do not claim "done" while the active job is unfinished. Users should
42
+ not need to learn policy modes for everyday work.
package/ADOPT.md CHANGED
@@ -1,77 +1,45 @@
1
1
  # Adopt duaer-spec
2
2
 
3
- Onboard a **digital employee** into another repository: install the Duaer
4
- delivery OS (method) and the agent-ops contract (how they are allowed to work).
3
+ Install Duaer so agents act like digital employees with a simple everyday loop.
5
4
 
6
- ## Recommended: CLI
7
-
8
- From the target project (Node 18+):
5
+ ## Install
9
6
 
10
7
  ```bash
11
8
  npx duaer-spec init --here
12
-
13
- # Pin a release
14
- npx github:fujiezee/duaer-spec@v0.2.0 duaer init --here
15
-
16
- # Lite (method only) / ops only / other integration branch
17
- npx duaer-spec init --here --method
18
- npx duaer-spec init --here --ops --branch develop
9
+ # pin: npx github:fujiezee/duaer-spec@v0.4.0 duaer init --here
19
10
  ```
20
11
 
21
- From a clone of this repo:
22
-
23
12
  ```bash
24
- node bin/duaer.mjs init /path/to/project --all
25
- node bin/duaer.mjs check /path/to/project
13
+ npx duaer-spec init --here --method # lite
14
+ npx duaer-spec init --here --ops --branch develop
26
15
  ```
27
16
 
28
- `--force` overwrites managed files.
29
-
30
- Then:
17
+ ## Everyday use
31
18
 
32
- 1. **Orient** edit `.duaer/memory/constitution.md` and `project-context.md`
33
- 2. **Confirm workplace** `docs/baseline.md` (integration branch)
34
- 3. **Assign → work → accept → gate** — `/duaer-specify` → `/duaer-plan` → `/duaer-tasks` → `/duaer-implement` → `/duaer-converge` → `duaer check . --gate`
19
+ 1. `/duaer-do <ask>` in Cursor
20
+ 2. `duaer status` when you want to see if the job is accepted
35
21
 
36
- A job without Spec is not assigned. Open tasks or a non-`accepted` `delivery.json`
37
- is not accepted. `duaer check --gate` is the machine merge check.
22
+ No need to learn policy modes or the full skill chain for normal work.
38
23
 
39
- ## Manual copy (optional)
24
+ ## Finer control (optional)
40
25
 
41
- ### Agent ops
42
-
43
- ```bash
44
- cp AGENTS.md /path/to/project/
45
- mkdir -p /path/to/project/.cursor/rules /path/to/project/docs/agent
46
- cp .cursor/rules/agents-workflow.mdc /path/to/project/.cursor/rules/
47
- cp .cursor/rules/ai-ui-copy.mdc /path/to/project/.cursor/rules/
48
- cp docs/agent/workflow.md docs/agent/change-checklist.md docs/agent/e2e-test-plan.md \
49
- /path/to/project/docs/agent/
50
- ```
51
-
52
- ### Duaer method
53
-
54
- ```bash
55
- rsync -a .duaer/ /path/to/project/.duaer/
56
- mkdir -p /path/to/project/.cursor/skills /path/to/project/.cursor/rules
57
- rsync -a .cursor/skills/ /path/to/project/.cursor/skills/
58
- cp .cursor/rules/duaer-spec.mdc /path/to/project/.cursor/rules/
59
- cp DUADER.md /path/to/project/
60
- ```
26
+ | Command | When |
27
+ |---|---|
28
+ | `/duaer-specify` | Only write / reshape the Brief |
29
+ | `/duaer-plan` · `/duaer-tasks` | Large or architectural jobs |
30
+ | `/duaer-implement` | Resume coding from `tasks.md` |
31
+ | `/duaer-converge` | Re-check Spec vs code |
32
+ | `duaer policy . strict` | Harder “don’t claim done” (still not a git lock) |
61
33
 
62
34
  ## Precedence
63
35
 
64
- 1. `AGENTS.md` / agent-ops — how employees operate
36
+ 1. `AGENTS.md` — how employees operate
65
37
  2. `DUADER.md` / `.duaer/` — what to build
66
- 3. Product overlays (do not copy `examples/` blindly)
67
38
 
68
39
  ## Updates
69
40
 
70
- Re-run `duaer init --force` (review the diff) or pin to a commit/tag. Adopters
71
- are not auto-updated.
41
+ `duaer init --force` (review the diff) or pin a tag. No auto-update.
72
42
 
73
43
  ## Do not copy
74
44
 
75
- - `examples/` samples only
76
- - `SOURCE.md` — provenance for maintainers
77
- - `bin/` / `package.json` — only needed if you vendor the CLI itself
45
+ `examples/`, `SOURCE.md`, `bin/` / `package.json` (unless vendoring the CLI)
package/CHANGELOG.md CHANGED
@@ -1,54 +1,44 @@
1
1
  # Changelog
2
2
 
3
- ## 0.2.0 — 2026-09-15
3
+ ## 0.4.0 — 2026-09-15
4
4
 
5
- ### Controllable delivery (machine gate)
5
+ ### Simple everyday ops
6
6
 
7
- - `/duaer-converge` writes `.duaer/specs/<feature>/delivery.json`
8
- (`accepted` | `open`)
9
- - `duaer check` reports workplace **and** delivery status
10
- - `duaer check --gate` fails on missing Spec, open tasks, or non-accepted stamp
11
- - Constitution, DUADER, README, ADOPT, and merge checklist require the gate
12
- - Honest scope: gate checks **handoff state**; Spec↔code judgment remains
13
- agent-assisted
7
+ - **`/duaer-do`** one Cursor skill for assign → work → accept
8
+ - **`duaer status`** — plain “accepted / not done” (alias of `job`)
9
+ - README / init / rules lead with hire → `/duaer-do` → `status`
10
+ - Step skills and policy remain available but are advanced, not the default story
14
11
 
15
- ### E2E
16
-
17
- - E2E-003 / E2E-004 / E2E-005 for delivery report and gate
18
-
19
- ## 0.1.1 — 2026-09-15
12
+ ## 0.3.0 — 2026-09-15
20
13
 
21
- ### Docs / product identity
14
+ ### Job handoff (not a repo lock)
22
15
 
23
- - Position Duaer as a **delivery OS for AI digital employees**: hire → assign
24
- (Spec) work accept (converge)
25
- - README, ADOPT, DUADER, AGENTS intro, and CLI `init` / `check` / help copy
26
- use that language
27
- - E2E catalog: E2E-001 (init next steps), E2E-002 (README positioning)
16
+ - Default policy **`coach`**: guide the employee; do not claim "done" until the
17
+ **active job** is accepted git merge is not blocked
18
+ - `.duaer/delivery-policy.json` modes: `off` | `coach` | `strict`
19
+ - `.duaer/active-job.json` set by `/duaer-specify`; `duaer job` reports status
20
+ - `duaer policy` to show/set mode
21
+ - `duaer check` defaults to active-job scope; `--strict` / policy `strict` optional
22
+ - `--gate` kept as deprecated alias for `--strict` (job handoff, not CI)
23
+ - `--all-jobs` optional; historical features no longer fail the default path
24
+ - Docs/rules/constitution demote merge-gate language
28
25
 
29
- ## 0.1.0 — 2026-09-15
26
+ ### E2E
30
27
 
31
- First public methodology release.
28
+ - E2E-004–007 for coach default, strict optional, active-job scope
32
29
 
33
- ### Method
30
+ ## 0.2.0 — 2026-09-15
34
31
 
35
- - Own **Duaer** Spec-Driven loop: specify → plan → tasks → implement → converge
36
- - Home directory: `.duaer/` (not an embedded third-party kit)
37
- - Cursor skills: `/duaer-specify`, `/duaer-plan`, `/duaer-tasks`, `/duaer-implement`, `/duaer-converge`, …
38
- - Agent ops (`AGENTS.md`) remain authoritative over the method when they conflict
32
+ ### Controllable delivery (machine gate)
39
33
 
40
- ### CLI
34
+ - `/duaer-converge` writes `.duaer/specs/<feature>/delivery.json`
35
+ - `duaer check` / `--gate` for handoff state
36
+ - Honest scope: handoff state checkable; Spec↔code still agent-assisted
41
37
 
42
- - `duaer init [dir] --all|--method|--ops [--force] [--branch <name>]`
43
- - `duaer check [dir]`
44
- - `duaer version`
38
+ ## 0.1.1 2026-09-15
45
39
 
46
- ### Install
40
+ - Digital-employee delivery OS positioning
47
41
 
48
- ```bash
49
- # From GitHub (works now)
50
- npx github:fujiezee/duaer-spec@v0.1.0 duaer init --here
42
+ ## 0.1.0 — 2026-09-15
51
43
 
52
- # From npm (after publish)
53
- npx duaer-spec init --here
54
- ```
44
+ - First public methodology release
package/DUADER.md CHANGED
@@ -1,40 +1,33 @@
1
1
  # Duaer methodology
2
2
 
3
- **duaer-spec** owns this method. Treat AI coding agents as **digital employees**:
4
- you assign a Brief (Spec), they follow a fixed work order, and delivery only
5
- counts after **converge** accepts the result against that Brief.
3
+ Treat AI coding agents as **digital employees**. Everyday path is short; the
4
+ full chain exists when you need it.
6
5
 
7
- Artifacts live under `.duaer/`; Cursor skills are `duaer-*` at `.cursor/skills/`.
6
+ **Precedence:** [`AGENTS.md`](AGENTS.md) wins on isolation, commits, Issue/PR.
8
7
 
9
- **Precedence:** root [`AGENTS.md`](AGENTS.md) / [`docs/agent/`](docs/agent/) win
10
- over anything here for isolation, commits, and Issue/PR gates.
8
+ ## Everyday (default)
11
9
 
12
- ## Read before work
13
-
14
- 1. `.duaer/memory/constitution.md` — process principles
15
- 2. `.duaer/memory/project-context.md` — as-is implementation truth
16
- 3. `.duaer/memory/testing.md` — verification expectations
17
- 4. Active feature under `.duaer/specs/<nnn-slug>/` when one exists
10
+ ```text
11
+ /duaer-do <ask> → duaer status
12
+ ```
18
13
 
19
- ## Standard path
14
+ `/duaer-do` assigns a Brief, implements, converges, and reports handoff.
15
+ Do not require the user to run every phase skill.
20
16
 
21
- **Full (recommended):**
17
+ ## Full path (optional)
22
18
 
23
- 1. `/duaer-constitution` — only when principles change
24
- 2. `/duaer-specify` — **Assign**: what / why / acceptance (not stack trivia)
25
- 3. `/duaer-clarify` — optional
26
- 4. `/duaer-plan` — technical plan aligned with project-context
27
- 5. `/duaer-checklist` — optional quality checklist
28
- 6. `/duaer-tasks` — checkbox task breakdown
29
- 7. `/duaer-analyze` — optional consistency check
30
- 8. `/duaer-implement` — implement tasks only
31
- 9. `/duaer-converge` — **Accept**: compare result to Spec; write `delivery.json`; append remaining tasks if gaps
32
- 10. `duaer check . --gate` — **Gate**: fail merge if Spec missing, tasks open, or stamp not accepted
19
+ 1. `/duaer-specify` — Brief
20
+ 2. `/duaer-plan` — when architecture/contracts change
21
+ 3. `/duaer-tasks` — explicit checkbox breakdown
22
+ 4. `/duaer-implement` — code from tasks
23
+ 5. `/duaer-converge` — accept stamp (`delivery.json`)
24
+ 6. `duaer status` — may we claim this job done?
33
25
 
34
- **Small feature:** specifyplan → tasks → implement → converge `check --gate`
26
+ Hotfix: short Spec → tasks → implement → converge (or just `/duaer-do`).
35
27
 
36
- **Hotfix:** specify (mark hotfix) tasks implement converge `check --gate`
37
- Never skip specify (assign), converge (accept), or the gate.
28
+ Never skip a Brief or an accept step. Under default **coach** policy, do not
29
+ claim "done" while `duaer status` is unfinished. This is **job handoff**, not a
30
+ git merge lock.
38
31
 
39
32
  ## Install
40
33
 
@@ -42,18 +35,17 @@ Never skip specify (assign), converge (accept), or the gate.
42
35
  npx duaer-spec init --here
43
36
  ```
44
37
 
45
- Or see [`ADOPT.md`](ADOPT.md). No third-party Spec CLI is required — `duaer`
46
- installs this repository's method files into the target project.
38
+ See [`ADOPT.md`](ADOPT.md).
47
39
 
48
- ## Feature directory shape
40
+ ## Layout
49
41
 
50
42
  ```text
43
+ .duaer/delivery-policy.json # default coach — usually ignore
44
+ .duaer/active-job.json # current job
51
45
  .duaer/specs/<nnn-slug>/
52
- spec.md # Brief
53
- plan.md # after /duaer-plan
54
- tasks.md # after /duaer-tasks
55
- delivery.json # after /duaer-converge (accepted | open)
56
- … # research / contracts as needed
46
+ spec.md
47
+ tasks.md
48
+ delivery.json
57
49
  ```
58
50
 
59
51
  ## Related
package/README.md CHANGED
@@ -1,102 +1,51 @@
1
1
  # duaer-spec
2
2
 
3
- **Duaer** is a delivery operating system for AI coding agents treated as
4
- **digital employees**.
3
+ **Duaer** makes AI coding agents work like digital employees: assign a job,
4
+ get a controllable handoff — without a rigid git lock.
5
5
 
6
- Agents write the code. Duaer makes the **handoff controllable**: every job has a
7
- Brief (Spec), follows a fixed work order, and only counts as done when the
8
- **delivery gate** passes — not after a lucky chat.
9
-
10
- Cursor is the labor. Duaer is hire → assign → accept.
11
-
12
- ## Install (onboard the employee)
6
+ ## Everyday (this is enough)
13
7
 
14
8
  ```bash
15
9
  npx duaer-spec init --here
16
- # or pin: npx github:fujiezee/duaer-spec@v0.2.0 duaer init --here
17
10
  ```
18
11
 
19
- ```bash
20
- # Lite: method only (personal / small changes)
21
- npx duaer-spec init --here --method
12
+ In Cursor, one command:
22
13
 
23
- # Full ops on another integration branch
24
- npx duaer-spec init --here --ops --branch develop
14
+ ```text
15
+ /duaer-do <what you want built or fixed>
25
16
  ```
26
17
 
27
- Verify: `npx duaer-spec check .`
28
- Merge gate: `npx duaer-spec check . --gate`
29
- Details: [`ADOPT.md`](ADOPT.md)
30
-
31
- ## How you run them
32
-
33
- | Step | Meaning | Command |
34
- |---|---|---|
35
- | **Hire** | Install SOP + gates into the repo | `duaer init` |
36
- | **Assign** | Write the Brief (what / why / acceptance) | `/duaer-specify` |
37
- | **Work** | Plan, break down, implement | `/duaer-plan` → `/duaer-tasks` → `/duaer-implement` |
38
- | **Accept** | Converge stamps `delivery.json`; gaps stay open | `/duaer-converge` |
39
- | **Gate** | Machine check before merge | `duaer check . --gate` |
40
-
41
- Never skip **Assign** or **Accept**. Hotfix may shorten the middle; Spec,
42
- converge, and the gate stay.
43
-
44
- ### What the gate actually enforces
45
-
46
- `duaer check --gate` fails when any feature under `.duaer/specs/` has:
47
-
48
- - missing `spec.md`
49
- - open `- [ ]` tasks in `tasks.md`
50
- - missing `delivery.json`, or `status` other than `accepted`
51
-
52
- Converge judgment is still agent-assisted. The gate makes **handoff state**
53
- checkable (and CI-friendly), not a substitute for tests.
18
+ See if the job can be reported done:
54
19
 
55
- ## Two layers
56
-
57
- | Layer | Role | Where |
58
- |---|---|---|
59
- | **Agent ops** | How the employee is allowed to operate | [`AGENTS.md`](AGENTS.md), [`docs/agent/`](docs/agent/) |
60
- | **Duaer method** | What to build (Spec → Plan → Tasks → Implement → Converge) | [`DUADER.md`](DUADER.md), [`.duaer/`](.duaer/), [`.cursor/skills/`](.cursor/skills/) |
61
-
62
- When they conflict, **agent ops win** — process beats improvisation.
20
+ ```bash
21
+ duaer status
22
+ ```
63
23
 
64
- ## Default loop
24
+ That’s the whole loop: **hire → do → status**.
65
25
 
66
- ```text
67
- constitution → specify → plan → tasks → implement → converge → check --gate
68
- ```
26
+ ## What `/duaer-do` does
69
27
 
70
- Small change: `specify plan → tasks → implement → converge`
71
- Hotfix: `specify (hotfix) → tasks → implement → converge`
28
+ 1. Writes a short Brief (Spec)
29
+ 2. Implements it
30
+ 3. Checks against the Brief and stamps accept / still-open
72
31
 
73
- Slash skills: `/duaer-specify`, `/duaer-plan`, `/duaer-tasks`, `/duaer-implement`,
74
- `/duaer-converge`,
32
+ You do **not** need to chain specify plan tasks implement → converge
33
+ for normal work. Those remain available when you want finer control.
75
34
 
76
- ## Layout
35
+ ## Optional
77
36
 
78
- ```text
79
- bin/duaer.mjs CLI (duaer init | check | version)
80
- package.json npm package metadata
81
- AGENTS.md Agent-ops contract (how employees operate)
82
- DUADER.md Methodology conventions (how work is briefed)
83
- ADOPT.md Onboarding guide
84
- .duaer/ Memory, templates, workflows, scripts
85
- .duaer/specs/<feature>/ Brief + tasks + delivery.json stamp
86
- .cursor/rules/ Agent-ops + Duaer rules
87
- .cursor/skills/ duaer-* skills
88
- docs/agent/ Workflow detail + checklists
89
- docs/maintaining.md How maintainers evolve the method
90
- docs/npm-trusted-publishing.md npm Trusted Publishing (OIDC) setup
91
- examples/ Optional product overlays (not defaults)
37
+ ```bash
38
+ duaer status # active job: accepted or not
39
+ npx duaer-spec@0.4.0 init --here --method # method only
92
40
  ```
93
41
 
94
- ## Defaults (this repo)
42
+ Advanced (policy, full check, step-by-step skills): [`ADOPT.md`](ADOPT.md) · [`DUADER.md`](DUADER.md)
95
43
 
96
- - Integration branch: **`main`**
97
- - One request → one branch + one worktree → merge → delete worktree
98
- - Push only when explicitly requested
44
+ ## Two layers
99
45
 
100
- ## Learn more
46
+ | Layer | Role |
47
+ |---|---|
48
+ | **Agent ops** | How agents may operate — [`AGENTS.md`](AGENTS.md) |
49
+ | **Duaer method** | How jobs are briefed and accepted — [`.duaer/`](.duaer/), skills |
101
50
 
102
- Method: [`DUADER.md`](DUADER.md) · Agent ops: [`AGENTS.md`](AGENTS.md)
51
+ When they conflict, **agent ops win**.
package/bin/duaer.mjs CHANGED
@@ -2,10 +2,8 @@
2
2
  /**
3
3
  * duaer — CLI for the duaer-spec delivery OS (digital employees)
4
4
  *
5
- * duaer init [dir] [--all|--method|--ops] [--force] [--branch <name>]
6
- * duaer check [dir] [--workplace|--delivery|--gate]
7
- * duaer version
8
- * duaer help
5
+ * Everyday: duaer init | duaer status
6
+ * Advanced: check | job | policy | version
9
7
  */
10
8
 
11
9
  import {
@@ -24,35 +22,27 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
24
22
  const PKG_ROOT = resolve(__dirname, '..')
25
23
  const PKG = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8'))
26
24
 
27
- const USAGE = `duaer Delivery OS for AI digital employees (duaer-spec ${PKG.version})
25
+ const POLICY_MODES = new Set(['off', 'coach', 'strict'])
28
26
 
29
- Hire agents into a repo, assign Briefs (Specs), accept only when the gate passes.
27
+ const USAGE = `duaer digital-employee delivery (duaer-spec ${PKG.version})
30
28
 
31
- Usage:
32
- duaer init [dir] [options] Hire / onboard into a project
33
- duaer check [dir] [options] Workplace + delivery status
34
- duaer version Print version
35
- duaer help Show this help
29
+ Everyday:
30
+ duaer init [--here] Hire into a project
31
+ duaer status [dir] Is the active job accepted?
36
32
 
37
- Init options:
38
- --all Full hire: agent ops + method (default)
39
- --method Lite: method only (.duaer, skills, DUADER.md, duaer-spec rule)
40
- --ops Ops only (AGENTS.md, docs/agent, ops rules)
41
- --force Overwrite existing managed files
42
- --branch <n> Integration branch for baseline note (default: main)
43
- --here Same as dir=.
33
+ In Cursor: /duaer-do <what you want>
44
34
 
45
- Check options:
46
- --workplace Only verify install files
47
- --delivery Only report feature Brief / open tasks / delivery.json
48
- --gate Fail unless every feature is accepted (merge gate)
49
- (default) Workplace + delivery report; exit 1 on workplace miss
50
- or delivery blockers (missing Spec / open tasks / open stamp)
35
+ Advanced:
36
+ duaer check [dir] [--workplace|--job|--all-jobs|--strict]
37
+ duaer job [dir] (same as status)
38
+ duaer policy [dir] [off|coach|strict]
39
+ duaer version
40
+
41
+ Init: --all (default) | --method | --ops | --force | --branch <n> | --here
51
42
 
52
43
  Examples:
53
44
  npx duaer-spec init --here
54
- duaer check .
55
- duaer check . --gate
45
+ duaer status
56
46
  `
57
47
 
58
48
  function parseArgs(argv) {
@@ -65,7 +55,10 @@ function parseArgs(argv) {
65
55
  branch: 'main',
66
56
  workplace: false,
67
57
  delivery: false,
68
- gate: false,
58
+ job: false,
59
+ allJobs: false,
60
+ strict: false,
61
+ policyMode: null,
69
62
  }
70
63
  const rest = args.slice(1)
71
64
  for (let i = 0; i < rest.length; i++) {
@@ -77,12 +70,18 @@ function parseArgs(argv) {
77
70
  else if (a === '--here') out.dir = '.'
78
71
  else if (a === '--workplace') out.workplace = true
79
72
  else if (a === '--delivery') out.delivery = true
80
- else if (a === '--gate') out.gate = true
73
+ else if (a === '--job') out.job = true
74
+ else if (a === '--all-jobs') out.allJobs = true
75
+ else if (a === '--strict' || a === '--gate') out.strict = true
81
76
  else if (a === '--branch') {
82
77
  out.branch = rest[++i]
83
78
  if (!out.branch) throw new Error('--branch requires a value')
79
+ } else if (POLICY_MODES.has(a) && out.cmd === 'policy') {
80
+ out.policyMode = a
84
81
  } else if (a.startsWith('-')) {
85
82
  throw new Error(`Unknown flag: ${a}`)
83
+ } else if (out.cmd === 'policy' && POLICY_MODES.has(a)) {
84
+ out.policyMode = a
86
85
  } else {
87
86
  out.dir = a
88
87
  }
@@ -124,10 +123,62 @@ function writeIfNeeded(path, content, { force }) {
124
123
  console.log(`wrote ${path}`)
125
124
  }
126
125
 
126
+ function readJson(path, fallback = null) {
127
+ if (!existsSync(path)) return fallback
128
+ try {
129
+ return JSON.parse(readFileSync(path, 'utf8'))
130
+ } catch {
131
+ return fallback
132
+ }
133
+ }
134
+
135
+ function defaultPolicy() {
136
+ return {
137
+ schemaVersion: 1,
138
+ mode: 'coach',
139
+ scope: 'active',
140
+ notes:
141
+ 'Job-level handoff. off=record only; coach=guide (default); strict=do not claim done until accepted. Not a git merge lock.',
142
+ }
143
+ }
144
+
145
+ function policyPath(target) {
146
+ return join(target, '.duaer', 'delivery-policy.json')
147
+ }
148
+
149
+ function activeJobPath(target) {
150
+ return join(target, '.duaer', 'active-job.json')
151
+ }
152
+
153
+ function loadPolicy(target) {
154
+ const p = readJson(policyPath(target), null)
155
+ if (!p) return defaultPolicy()
156
+ const mode = POLICY_MODES.has(p.mode) ? p.mode : 'coach'
157
+ return { ...defaultPolicy(), ...p, mode }
158
+ }
159
+
160
+ function writePolicy(target, mode) {
161
+ const next = { ...defaultPolicy(), mode, updatedAt: new Date().toISOString() }
162
+ ensureDir(join(target, '.duaer'))
163
+ writeFileSync(policyPath(target), JSON.stringify(next, null, 2) + '\n')
164
+ return next
165
+ }
166
+
167
+ function writeDefaultPolicy(target, { force }) {
168
+ const path = policyPath(target)
169
+ if (existsSync(path) && !force) {
170
+ console.log(`skip (exists): ${path}`)
171
+ return
172
+ }
173
+ writeFileSync(path, JSON.stringify(defaultPolicy(), null, 2) + '\n')
174
+ console.log(' .duaer/delivery-policy.json (mode=coach)')
175
+ }
176
+
127
177
  function installMethod(target, opts) {
128
178
  console.log('Installing Duaer method…')
129
179
  copyPath(join(PKG_ROOT, '.duaer'), join(target, '.duaer'), opts)
130
180
  console.log(' .duaer/')
181
+ writeDefaultPolicy(target, opts)
131
182
 
132
183
  ensureDir(join(target, '.cursor', 'skills'))
133
184
  const skillsRoot = join(PKG_ROOT, '.cursor', 'skills')
@@ -232,19 +283,12 @@ function cmdInit(opts) {
232
283
  }
233
284
 
234
285
  console.log(`
235
- Hired.
236
-
237
- Next (digital employee loop):
238
- 1. Orient — edit .duaer/memory/constitution.md and project-context.md
239
- 2. Workplace — confirm docs/baseline.md (integration branch: ${opts.branch})
240
- 3. Assign — /duaer-specify (Brief: what / why / acceptance)
241
- 4. Work — /duaer-plan → /duaer-tasks → /duaer-implement
242
- 5. Accept — /duaer-converge (writes delivery.json; gaps stay open)
243
- 6. Gate — duaer check . --gate before merge
244
- 7. Policy — AGENTS.md wins over DUADER.md when they conflict
245
-
246
- No Spec = not assigned. Open tasks or open delivery.json = not accepted.
247
- See DUADER.md and AGENTS.md in the target project.
286
+ Hired. Everyday loop:
287
+
288
+ 1. In Cursor: /duaer-do <what you want>
289
+ 2. Optional: duaer status
290
+
291
+ That is enough for normal work. Advanced: ADOPT.md / DUADER.md
248
292
  `)
249
293
  }
250
294
 
@@ -265,6 +309,55 @@ function readDelivery(path) {
265
309
  }
266
310
  }
267
311
 
312
+ function featureVerdict(name, dir) {
313
+ const specPath = join(dir, 'spec.md')
314
+ const tasksPath = join(dir, 'tasks.md')
315
+ const deliveryPath = join(dir, 'delivery.json')
316
+ const tasks = countOpenTasks(tasksPath)
317
+ const delivery = readDelivery(deliveryPath)
318
+ const hasSpec = existsSync(specPath)
319
+ const stamp = delivery?.status || 'none'
320
+ let verdict = 'accepted'
321
+ const blockers = []
322
+
323
+ if (!hasSpec) {
324
+ verdict = 'blocked'
325
+ blockers.push('missing spec.md')
326
+ }
327
+ if (tasks && tasks.open > 0) {
328
+ verdict = 'blocked'
329
+ blockers.push(`${tasks.open} open task(s)`)
330
+ }
331
+ if (stamp === 'open' || stamp === 'invalid') {
332
+ verdict = 'blocked'
333
+ blockers.push(`delivery.json status=${stamp}`)
334
+ }
335
+ if (hasSpec && tasks && tasks.open === 0 && (stamp === 'none' || !delivery)) {
336
+ verdict = 'unaccepted'
337
+ blockers.push('no delivery.json (run /duaer-converge)')
338
+ }
339
+ if (hasSpec && !tasks && (stamp === 'none' || !delivery)) {
340
+ verdict = 'unaccepted'
341
+ blockers.push('no tasks.md / no delivery.json')
342
+ }
343
+ if (stamp === 'accepted' && tasks && tasks.open === 0 && hasSpec) {
344
+ verdict = 'accepted'
345
+ blockers.length = 0
346
+ } else if (stamp === 'accepted' && (!tasks || tasks.open === 0) && hasSpec && !tasks) {
347
+ // accepted stamp with no tasks file — treat as accepted only if stamp says so
348
+ // keep unaccepted path above for !tasks && no stamp
349
+ }
350
+
351
+ // Stamp accepted but open tasks remain → blocked (fake stamp)
352
+ if (stamp === 'accepted' && tasks && tasks.open > 0) {
353
+ verdict = 'blocked'
354
+ blockers.length = 0
355
+ blockers.push('delivery.json accepted but open tasks remain')
356
+ }
357
+
358
+ return { name, hasSpec, tasks, stamp, verdict, blockers }
359
+ }
360
+
268
361
  function listFeatures(target) {
269
362
  const root = join(target, '.duaer', 'specs')
270
363
  if (!existsSync(root)) return []
@@ -277,53 +370,28 @@ function listFeatures(target) {
277
370
  }
278
371
  })
279
372
  .sort()
280
- .map((name) => {
281
- const dir = join(root, name)
282
- const specPath = join(dir, 'spec.md')
283
- const tasksPath = join(dir, 'tasks.md')
284
- const deliveryPath = join(dir, 'delivery.json')
285
- const tasks = countOpenTasks(tasksPath)
286
- const delivery = readDelivery(deliveryPath)
287
- const hasSpec = existsSync(specPath)
288
- const stamp = delivery?.status || 'none'
289
- let verdict = 'accepted'
290
- const blockers = []
291
-
292
- if (!hasSpec) {
293
- verdict = 'blocked'
294
- blockers.push('missing spec.md')
295
- }
296
- if (tasks && tasks.open > 0) {
297
- verdict = 'blocked'
298
- blockers.push(`${tasks.open} open task(s)`)
299
- }
300
- if (stamp === 'open' || stamp === 'invalid') {
301
- verdict = 'blocked'
302
- blockers.push(`delivery.json status=${stamp}`)
303
- }
304
- if (hasSpec && tasks && tasks.open === 0 && (stamp === 'none' || !delivery)) {
305
- // Brief exists and tasks clear, but never stamped by converge
306
- verdict = 'unaccepted'
307
- blockers.push('no delivery.json (run /duaer-converge)')
308
- }
309
- if (hasSpec && !tasks && (stamp === 'none' || !delivery)) {
310
- verdict = 'unaccepted'
311
- blockers.push('no tasks.md / no delivery.json')
312
- }
313
- if (stamp === 'accepted' && tasks && tasks.open === 0 && hasSpec) {
314
- verdict = 'accepted'
315
- blockers.length = 0
316
- }
373
+ .map((name) => featureVerdict(name, join(root, name)))
374
+ }
317
375
 
318
- return {
319
- name,
320
- hasSpec,
321
- tasks,
322
- stamp,
323
- verdict,
324
- blockers,
325
- }
326
- })
376
+ function resolveActiveJobName(target, features) {
377
+ const marker = readJson(activeJobPath(target), null)
378
+ if (marker?.feature && features.some((f) => f.name === marker.feature)) {
379
+ return marker.feature
380
+ }
381
+ if (features.length === 0) return null
382
+ // Prefer newest directory name (lexicographic works for NNN-slug)
383
+ return features[features.length - 1].name
384
+ }
385
+
386
+ function printFeature(f, { label } = {}) {
387
+ const taskInfo = f.tasks
388
+ ? `tasks open=${f.tasks.open} done=${f.tasks.done}`
389
+ : 'no tasks.md'
390
+ const prefix = label ? `${label} ` : ''
391
+ console.log(
392
+ `${prefix}${f.verdict.padEnd(10)} ${f.name} spec=${f.hasSpec ? 'yes' : 'NO'} stamp=${f.stamp} ${taskInfo}`,
393
+ )
394
+ for (const b of f.blockers) console.log(` · ${b}`)
327
395
  }
328
396
 
329
397
  function checkWorkplace(target) {
@@ -346,51 +414,91 @@ function checkWorkplace(target) {
346
414
  return missing
347
415
  }
348
416
 
349
- function checkDelivery(target, { gate }) {
417
+ function checkDelivery(target, { allJobs, policy }) {
350
418
  const features = listFeatures(target)
351
- console.log('\n## Delivery\n')
419
+ const activeName = resolveActiveJobName(target, features)
420
+ const active = features.find((f) => f.name === activeName) || null
421
+
422
+ console.log('\n## Handoff (jobs)\n')
423
+ console.log(`policy: ${policy.mode} scope: ${allJobs ? 'all' : 'active'}`)
424
+
352
425
  if (features.length === 0) {
353
- console.log('ok no features under .duaer/specs/ (nothing to accept)')
354
- return { blocked: 0, unaccepted: 0, features: 0 }
426
+ console.log('ok no features under .duaer/specs/ (nothing to hand off)')
427
+ return { blocked: 0, unaccepted: 0, features: 0, active: null, unfinished: false }
355
428
  }
356
429
 
430
+ if (allJobs) {
431
+ for (const f of features) {
432
+ printFeature(f, { label: f.name === activeName ? '★' : ' ' })
433
+ }
434
+ } else if (active) {
435
+ printFeature(active, { label: '★' })
436
+ console.log(`\nactive job: ${active.name} (from .duaer/active-job.json or latest spec)`)
437
+ } else {
438
+ console.log('ok no resolvable active job')
439
+ }
440
+
441
+ const focus = allJobs ? features : active ? [active] : []
357
442
  let blocked = 0
358
443
  let unaccepted = 0
359
- for (const f of features) {
360
- const taskInfo = f.tasks
361
- ? `tasks open=${f.tasks.open} done=${f.tasks.done}`
362
- : 'no tasks.md'
363
- const line = `${f.verdict.padEnd(10)} ${f.name} spec=${f.hasSpec ? 'yes' : 'NO'} stamp=${f.stamp} ${taskInfo}`
364
- console.log(line)
365
- if (f.blockers.length) {
366
- for (const b of f.blockers) console.log(` · ${b}`)
367
- }
444
+ for (const f of focus) {
368
445
  if (f.verdict === 'blocked') blocked++
369
446
  if (f.verdict === 'unaccepted') unaccepted++
370
447
  }
448
+ const unfinished = blocked + unaccepted > 0
449
+ return { blocked, unaccepted, features: focus.length, active, unfinished }
450
+ }
371
451
 
372
- if (gate) {
373
- console.log('\nGate mode: every feature must be accepted.')
452
+ function handoffAdvice(policy, unfinished, strictFlag) {
453
+ if (!unfinished) {
454
+ console.log('\nActive job accepted — employee may report delivery complete.')
455
+ return
456
+ }
457
+ if (policy.mode === 'off' && !strictFlag) {
458
+ console.log('\nHandoff incomplete (policy=off) — recorded only.')
459
+ return
460
+ }
461
+ if (policy.mode === 'coach' && !strictFlag) {
462
+ console.log(`
463
+ Coach: this job is not accepted yet.
464
+ → close open tasks, run /duaer-converge, then duaer job .
465
+ → do not tell the user the work is "done" until status=accepted
466
+ → git is not blocked; this is job etiquette, not a merge lock
467
+ `)
468
+ return
374
469
  }
375
- return { blocked, unaccepted, features: features.length }
470
+ console.log(`
471
+ Strict: active job not accepted — exit 1.
472
+ → /duaer-converge until delivery.json status=accepted and tasks clear
473
+ `)
376
474
  }
377
475
 
378
476
  function cmdCheck(opts) {
379
477
  const target = resolve(opts.dir)
380
- // Default: workplace + delivery. --gate alone is delivery-only (merge gate).
381
- const runWorkplace = opts.workplace || (!opts.delivery && !opts.gate)
382
- const runDelivery = opts.delivery || opts.gate || !opts.workplace
478
+ const policy = loadPolicy(target)
479
+ const strictFlag = opts.strict || policy.mode === 'strict'
480
+
481
+ // default: workplace + delivery; --workplace alone; job/delivery/strict/all-jobs → delivery
482
+ let wp = false
483
+ let del = false
484
+ if (opts.workplace && !opts.delivery && !opts.job && !opts.allJobs && !opts.strict) {
485
+ wp = true
486
+ } else if (opts.delivery || opts.job || opts.allJobs || opts.strict) {
487
+ del = true
488
+ if (opts.workplace) wp = true
489
+ } else {
490
+ wp = true
491
+ del = true
492
+ }
383
493
 
384
- console.log(`Checking ${target}${opts.gate ? ' (gate)' : ''}\n`)
494
+ console.log(`Checking ${target}${strictFlag ? ' (strict handoff)' : ''}\n`)
385
495
 
386
496
  let workplaceMissing = 0
387
- if (runWorkplace) {
388
- workplaceMissing = checkWorkplace(target)
389
- }
497
+ if (wp) workplaceMissing = checkWorkplace(target)
390
498
 
391
- let delivery = { blocked: 0, unaccepted: 0, features: 0 }
392
- if (runDelivery) {
393
- delivery = checkDelivery(target, { gate: opts.gate })
499
+ let delivery = { unfinished: false }
500
+ if (del) {
501
+ delivery = checkDelivery(target, { allJobs: opts.allJobs, policy })
394
502
  }
395
503
 
396
504
  let fail = false
@@ -399,36 +507,77 @@ function cmdCheck(opts) {
399
507
  fail = true
400
508
  }
401
509
 
402
- if (runDelivery) {
403
- if (delivery.blocked) {
404
- console.log(`\n${delivery.blocked} feature(s) blocked — close open tasks / fix Spec / re-run converge`)
510
+ if (del) {
511
+ handoffAdvice(policy, delivery.unfinished, opts.strict)
512
+ if (delivery.unfinished && strictFlag) {
405
513
  fail = true
406
514
  }
407
- if (opts.gate && delivery.unaccepted) {
408
- console.log(
409
- `\n${delivery.unaccepted} feature(s) not accepted — run /duaer-converge until delivery.json status=accepted`,
410
- )
411
- fail = true
412
- } else if (!opts.gate && delivery.unaccepted) {
413
- console.log(
414
- `\n${delivery.unaccepted} feature(s) not yet accepted (warning). Use --gate to fail the merge check.`,
415
- )
416
- }
417
515
  }
418
516
 
419
517
  if (!fail) {
420
- if (opts.gate) {
421
- console.log('\nGate passed — deliverable.')
422
- } else if (runWorkplace && !workplaceMissing) {
423
- console.log('\nWorkplace ready. Use --gate before merge when features exist.')
518
+ if (wp && !workplaceMissing && !del) {
519
+ console.log('\nWorkplace ready.')
520
+ } else if (!delivery.unfinished) {
521
+ console.log('\nCheck ok.')
424
522
  } else {
425
- console.log('\nDelivery report done.')
523
+ console.log('\nCheck ok (handoff still open under coach/off — see above).')
426
524
  }
427
525
  } else {
428
526
  process.exitCode = 1
429
527
  }
430
528
  }
431
529
 
530
+ function cmdJob(opts) {
531
+ const target = resolve(opts.dir)
532
+ const policy = loadPolicy(target)
533
+ const features = listFeatures(target)
534
+ const activeName = resolveActiveJobName(target, features)
535
+ const active = features.find((f) => f.name === activeName) || null
536
+
537
+ console.log(`Status @ ${target}`)
538
+ if (!active) {
539
+ console.log('No active job yet. In Cursor run: /duaer-do <what you want>')
540
+ return
541
+ }
542
+ if (active.verdict === 'accepted') {
543
+ console.log(`✅ accepted ${active.name}`)
544
+ console.log('Ready for your review.')
545
+ } else {
546
+ console.log(`⏳ not done ${active.name}`)
547
+ for (const b of active.blockers) console.log(` · ${b}`)
548
+ console.log('Next: /duaer-do (resume) or /duaer-converge')
549
+ }
550
+ if (policy.mode === 'strict' && active.verdict !== 'accepted') {
551
+ console.log('(policy=strict — do not claim done yet)')
552
+ }
553
+ }
554
+
555
+ function cmdPolicy(opts) {
556
+ const target = resolve(opts.dir)
557
+ if (opts.policyMode) {
558
+ if (!POLICY_MODES.has(opts.policyMode)) {
559
+ throw new Error(`Unknown policy mode: ${opts.policyMode}`)
560
+ }
561
+ const next = writePolicy(target, opts.policyMode)
562
+ console.log(`Wrote ${policyPath(target)}`)
563
+ console.log(`mode: ${next.mode}`)
564
+ return
565
+ }
566
+ const policy = loadPolicy(target)
567
+ const exists = existsSync(policyPath(target))
568
+ console.log(`Policy @ ${target}`)
569
+ console.log(`file: ${exists ? policyPath(target) : '(defaults; run init or: duaer policy . coach)'}`)
570
+ console.log(`mode: ${policy.mode}`)
571
+ console.log(`scope: ${policy.scope || 'active'}`)
572
+ console.log(`
573
+ Modes:
574
+ off record only
575
+ coach guide; do not claim done until accepted (default)
576
+ strict check fails + agents must not report done until accepted
577
+ Not a git merge lock.
578
+ `)
579
+ }
580
+
432
581
  function main() {
433
582
  let opts
434
583
  try {
@@ -447,6 +596,13 @@ function main() {
447
596
  case 'check':
448
597
  cmdCheck(opts)
449
598
  break
599
+ case 'job':
600
+ case 'status':
601
+ cmdJob(opts)
602
+ break
603
+ case 'policy':
604
+ cmdPolicy(opts)
605
+ break
450
606
  case 'version':
451
607
  case '--version':
452
608
  case '-v':
@@ -94,7 +94,7 @@ See [spec update guidance](workflow.md#3-spec-update-guidance).
94
94
 
95
95
  ## 6. Merge / PR
96
96
 
97
- - [ ] `duaer check . --gate` passed (or no `.duaer/specs/` features yet).
97
+ - [ ] Active job handoff considered: `duaer job .` (coach/strict: do not claim done if unfinished).
98
98
  - [ ] Branch refreshed against latest `main`.
99
99
  - [ ] Merged into local `main` (and/or remote PR/MR when required).
100
100
  - [ ] Only this request's logical changes included.
@@ -122,7 +122,7 @@ See [spec update guidance](workflow.md#3-spec-update-guidance).
122
122
  | 3 | Impacted specs updated | [R1](workflow.md#r1--spec-first--spec-sync) |
123
123
  | 4 | E2E docs updated or confirmed unnecessary | [R3](workflow.md#r3--e2e-coverage-doc) |
124
124
  | 5 | Targeted validation done or waived with reason | Development loop |
125
- | 5a | `duaer check . --gate` when features exist | Delivery OS gate |
125
+ | 5a | Active job handoff via `duaer job` (coach/strict) | Delivery OS not a git lock |
126
126
  | 6 | Conventional commits | [R2](workflow.md#r2--commit-per-change) |
127
127
  | 7 | No secrets or local data | [§4](workflow.md#4-what-never-to-commit) |
128
128
  | 8 | Merged into `main`; worktree and branch removed | [R4](workflow.md#r4--request-branch--worktree--merge-gate) · [§6.1](#61-merge-cleanup) |
@@ -25,11 +25,14 @@ the table for their app.
25
25
 
26
26
  | ID | Title | Status |
27
27
  |---|---|---|
28
- | E2E-001 | After `duaer init`, CLI prints hire assign → accept next steps | manual |
29
- | E2E-002 | README positions Duaer as delivery OS for digital employees | manual |
30
- | E2E-003 | `duaer check --delivery` reports open tasks as blocked | manual |
31
- | E2E-004 | `duaer check --gate` fails without `delivery.json` accepted | manual |
32
- | E2E-005 | After converge stamps `accepted`, `--gate` passes | manual |
28
+ | E2E-001 | After `duaer init`, CLI prints simple `/duaer-do` next steps | manual |
29
+ | E2E-002 | README everyday path is hire `/duaer-do` `status` | manual |
30
+ | E2E-003 | `duaer status` shows accepted / not done in plain language | manual |
31
+ | E2E-004 | Default `coach` policy: unfinished job warns but check exits 0 | manual |
32
+ | E2E-005 | After converge stamps `accepted`, `duaer status` shows accepted | manual |
33
+ | E2E-006 | `duaer policy . strict` then unfinished job makes check exit 1 | manual |
34
+ | E2E-007 | `--all-jobs` is optional; default scope is active job only | manual |
35
+ | E2E-008 | `/duaer-do` skill is installed by `duaer init` | manual |
33
36
 
34
37
  ## Traceability
35
38
 
@@ -37,8 +40,11 @@ Keep this matrix current when scenarios or specs change.
37
40
 
38
41
  | Scenario | Spec / feature | Notes |
39
42
  |---|---|---|
40
- | E2E-001 | `bin/duaer.mjs` init banner | Controllable-delivery onboarding |
41
- | E2E-002 | `README.md` / `ADOPT.md` | Product identity |
42
- | E2E-003 | `bin/duaer.mjs` delivery check | Open `- [ ]` tasks |
43
- | E2E-004 | `bin/duaer.mjs` `--gate` | Merge gate |
44
- | E2E-005 | `/duaer-converge` → `delivery.json` | Accept stamp |
43
+ | E2E-001 | `bin/duaer.mjs` init banner | Simple onboarding |
44
+ | E2E-002 | `README.md` | Everyday path |
45
+ | E2E-003 | `duaer status` | Plain language |
46
+ | E2E-004 | `delivery-policy.json` coach | Not a repo lock |
47
+ | E2E-005 | converge → `delivery.json` | Accept stamp |
48
+ | E2E-006 | policy strict | Optional |
49
+ | E2E-007 | `--all-jobs` vs active | Scope |
50
+ | E2E-008 | `.cursor/skills/duaer-do` | One-shot skill |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "duaer-spec",
3
- "version": "0.2.0",
4
- "description": "Delivery OS for AI digital employees: Spec briefs, agent ops, check --gate handoffs",
3
+ "version": "0.4.0",
4
+ "description": "Simple digital-employee delivery: /duaer-do + duaer status",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "duaer": "bin/duaer.mjs",