duaer-spec 0.1.0 → 0.2.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/.cursor/rules/duaer-spec.mdc +4 -2
- package/.cursor/skills/duaer-converge/SKILL.md +46 -7
- package/.duaer/memory/constitution.md +5 -0
- package/ADOPT.md +15 -13
- package/AGENTS.md +4 -2
- package/CHANGELOG.md +26 -0
- package/DUADER.md +17 -12
- package/README.md +48 -20
- package/bin/duaer.mjs +200 -27
- package/docs/agent/README.md +2 -2
- package/docs/agent/change-checklist.md +2 -0
- package/docs/agent/e2e-test-plan.md +13 -2
- package/package.json +5 -3
|
@@ -5,14 +5,15 @@ alwaysApply: true
|
|
|
5
5
|
|
|
6
6
|
# Duaer required (NON-NEGOTIABLE)
|
|
7
7
|
|
|
8
|
-
|
|
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`.
|
|
9
10
|
|
|
10
11
|
If this rule conflicts with root `AGENTS.md` / `docs/agent/` (worktrees, commits,
|
|
11
12
|
Issue/PR), **AGENTS.md wins**.
|
|
12
13
|
|
|
13
14
|
## When Duaer is mandatory
|
|
14
15
|
|
|
15
|
-
Always write/update a feature spec before coding for:
|
|
16
|
+
Always write/update a feature spec (Brief) before coding for:
|
|
16
17
|
|
|
17
18
|
- New features / user-visible behavior
|
|
18
19
|
- Architecture or contract changes
|
|
@@ -31,3 +32,4 @@ Forbidden: jump from chat to code with no `spec.md` / feature directory under `.
|
|
|
31
32
|
- Implement only from `tasks.md` when it exists
|
|
32
33
|
- Update `.duaer/memory/project-context.md` when architecture changes
|
|
33
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)
|
|
@@ -71,10 +71,17 @@ of remaining work as a new, traceable task** at the bottom of `tasks.md` so that
|
|
|
71
71
|
This is **not** a diff tool and does **not** track changes. It assesses the present state
|
|
72
72
|
of the code relative to the feature's artifacts — no git, no branch comparison, no history.
|
|
73
73
|
|
|
74
|
+
**Delivery stamp:** Always update `FEATURE_DIR/delivery.json` after assessment (see Step 7).
|
|
75
|
+
That file is what `duaer check --gate` reads for machine-checkable acceptance.
|
|
76
|
+
|
|
74
77
|
## Operating Constraints
|
|
75
78
|
|
|
76
|
-
**APPEND-ONLY,
|
|
77
|
-
|
|
79
|
+
**APPEND-ONLY FOR TASKS, STAMP FOR DELIVERY**: The command's writes are limited to:
|
|
80
|
+
|
|
81
|
+
1. appending a new `## Phase N: Convergence` section to `tasks.md` when findings exist;
|
|
82
|
+
2. writing / overwriting `FEATURE_DIR/delivery.json` (the machine-readable accept stamp).
|
|
83
|
+
|
|
84
|
+
It MUST NOT:
|
|
78
85
|
|
|
79
86
|
- modify `spec.md` or `plan.md` in any way;
|
|
80
87
|
- rewrite, renumber, reorder, or delete any existing task (including tasks from a prior
|
|
@@ -83,7 +90,8 @@ of the code relative to the feature's artifacts — no git, no branch comparison
|
|
|
83
90
|
job of `/duaer-implement`.
|
|
84
91
|
|
|
85
92
|
When the codebase already satisfies everything, the command MUST leave `tasks.md`
|
|
86
|
-
**byte-for-byte unchanged** (no empty Convergence header) and
|
|
93
|
+
**byte-for-byte unchanged** (no empty Convergence header) and still write
|
|
94
|
+
`delivery.json` with `status: "accepted"`.
|
|
87
95
|
|
|
88
96
|
**Constitution Authority**: The project constitution (`.duaer/memory/constitution.md`) is
|
|
89
97
|
**non-negotiable**. Code that violates a MUST principle is the highest-severity finding and
|
|
@@ -221,20 +229,51 @@ Append to the **end** of `tasks.md`, per the append contract:
|
|
|
221
229
|
`CRITICAL`.
|
|
222
230
|
4. Never reuse or renumber existing IDs. If a prior Convergence phase exists, add a new,
|
|
223
231
|
separately-numbered one below it — do not touch the old one.
|
|
232
|
+
5. **Write / overwrite** `FEATURE_DIR/delivery.json`:
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"schemaVersion": 1,
|
|
237
|
+
"status": "open",
|
|
238
|
+
"outcome": "tasks_appended",
|
|
239
|
+
"checkedAt": "<ISO-8601 UTC>",
|
|
240
|
+
"openTasks": <count of - [ ] in tasks.md after append>,
|
|
241
|
+
"findings": <number of findings appended>,
|
|
242
|
+
"source": "duaer-converge"
|
|
243
|
+
}
|
|
244
|
+
```
|
|
224
245
|
|
|
225
246
|
**If there are no actionable findings** (`converged` outcome):
|
|
226
247
|
|
|
227
248
|
- Do **not** modify `tasks.md` at all — no empty phase header.
|
|
249
|
+
- **Write / overwrite** `FEATURE_DIR/delivery.json` with:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"schemaVersion": 1,
|
|
254
|
+
"status": "accepted",
|
|
255
|
+
"outcome": "converged",
|
|
256
|
+
"checkedAt": "<ISO-8601 UTC>",
|
|
257
|
+
"openTasks": 0,
|
|
258
|
+
"findings": 0,
|
|
259
|
+
"source": "duaer-converge"
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Set `openTasks` to the count of unchecked `- [ ]` lines currently in `tasks.md`
|
|
264
|
+
(must be `0` for `status: "accepted"`; if any remain, treat as `tasks_appended`
|
|
265
|
+
path instead — do not stamp accepted).
|
|
228
266
|
- Report: **"✅ Converged — the implementation satisfies the spec, plan, and tasks."**
|
|
229
267
|
- Include the summary counts of what was checked.
|
|
230
|
-
|
|
268
|
+
- Tell the user they can run `duaer check . --gate` to verify the machine gate.
|
|
231
269
|
### 8. Provide Next Actions (Handoff)
|
|
232
270
|
|
|
233
271
|
- On `tasks_appended`: state how many tasks were appended under which phase, and recommend
|
|
234
272
|
running `/duaer-implement` to complete them; note that a follow-up converge
|
|
235
|
-
run will find fewer or no remaining items.
|
|
236
|
-
|
|
237
|
-
|
|
273
|
+
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.
|
|
238
277
|
|
|
239
278
|
### 9. Check for extension hooks
|
|
240
279
|
|
|
@@ -35,3 +35,8 @@ Do not expand scope without updating the spec and tasks.
|
|
|
35
35
|
|
|
36
36
|
Mark tasks complete only when the stated verification (tests, manual checks,
|
|
37
37
|
or converge) has been performed or explicitly waived in the feature docs.
|
|
38
|
+
|
|
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.
|
package/ADOPT.md
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
# Adopt duaer-spec
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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).
|
|
5
5
|
|
|
6
6
|
## Recommended: CLI
|
|
7
7
|
|
|
8
8
|
From the target project (Node 18+):
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
# GitHub pin
|
|
12
|
-
npx github:fujiezee/duaer-spec@v0.1.0 duaer init --here
|
|
13
|
-
|
|
14
|
-
# npm (after publish)
|
|
15
11
|
npx duaer-spec init --here
|
|
16
12
|
|
|
17
|
-
#
|
|
18
|
-
npx github:fujiezee/duaer-spec@v0.
|
|
19
|
-
|
|
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
|
|
20
19
|
```
|
|
21
20
|
|
|
22
21
|
From a clone of this repo:
|
|
@@ -30,9 +29,12 @@ node bin/duaer.mjs check /path/to/project
|
|
|
30
29
|
|
|
31
30
|
Then:
|
|
32
31
|
|
|
33
|
-
1.
|
|
34
|
-
2. Confirm `docs/baseline.md` (integration branch)
|
|
35
|
-
3.
|
|
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`
|
|
35
|
+
|
|
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.
|
|
36
38
|
|
|
37
39
|
## Manual copy (optional)
|
|
38
40
|
|
|
@@ -59,7 +61,7 @@ cp DUADER.md /path/to/project/
|
|
|
59
61
|
|
|
60
62
|
## Precedence
|
|
61
63
|
|
|
62
|
-
1. `AGENTS.md` / agent-ops — how
|
|
64
|
+
1. `AGENTS.md` / agent-ops — how employees operate
|
|
63
65
|
2. `DUADER.md` / `.duaer/` — what to build
|
|
64
66
|
3. Product overlays (do not copy `examples/` blindly)
|
|
65
67
|
|
package/AGENTS.md
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
Mandatory rules for AI coding agents working in **duaer-spec**, and for any
|
|
4
4
|
repository that adopts this file as its agent-ops contract.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
Treat agents as **digital employees**: this file is the workplace policy for
|
|
7
|
+
*how* they may operate (isolation, commits, Issue/PR gates). Duaer
|
|
8
|
+
([`DUADER.md`](DUADER.md)) is how jobs are briefed and accepted. If Duaer
|
|
9
|
+
(`.duaer/`), `examples/`, or other overlays conflict with this file,
|
|
8
10
|
**follow this file**.
|
|
9
11
|
|
|
10
12
|
See also: [workflow](docs/agent/workflow.md) · [change checklist](docs/agent/change-checklist.md) · [ADOPT](ADOPT.md)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-09-15
|
|
4
|
+
|
|
5
|
+
### Controllable delivery (machine gate)
|
|
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
|
|
14
|
+
|
|
15
|
+
### E2E
|
|
16
|
+
|
|
17
|
+
- E2E-003 / E2E-004 / E2E-005 for delivery report and gate
|
|
18
|
+
|
|
19
|
+
## 0.1.1 — 2026-09-15
|
|
20
|
+
|
|
21
|
+
### Docs / product identity
|
|
22
|
+
|
|
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)
|
|
28
|
+
|
|
3
29
|
## 0.1.0 — 2026-09-15
|
|
4
30
|
|
|
5
31
|
First public methodology release.
|
package/DUADER.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Duaer methodology
|
|
2
2
|
|
|
3
|
-
**duaer-spec** owns this method.
|
|
4
|
-
|
|
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.
|
|
6
|
+
|
|
7
|
+
Artifacts live under `.duaer/`; Cursor skills are `duaer-*` at `.cursor/skills/`.
|
|
5
8
|
|
|
6
9
|
**Precedence:** root [`AGENTS.md`](AGENTS.md) / [`docs/agent/`](docs/agent/) win
|
|
7
10
|
over anything here for isolation, commits, and Issue/PR gates.
|
|
@@ -18,24 +21,25 @@ over anything here for isolation, commits, and Issue/PR gates.
|
|
|
18
21
|
**Full (recommended):**
|
|
19
22
|
|
|
20
23
|
1. `/duaer-constitution` — only when principles change
|
|
21
|
-
2. `/duaer-specify` — what / why / acceptance (not stack trivia)
|
|
24
|
+
2. `/duaer-specify` — **Assign**: what / why / acceptance (not stack trivia)
|
|
22
25
|
3. `/duaer-clarify` — optional
|
|
23
26
|
4. `/duaer-plan` — technical plan aligned with project-context
|
|
24
27
|
5. `/duaer-checklist` — optional quality checklist
|
|
25
28
|
6. `/duaer-tasks` — checkbox task breakdown
|
|
26
29
|
7. `/duaer-analyze` — optional consistency check
|
|
27
30
|
8. `/duaer-implement` — implement tasks only
|
|
28
|
-
9. `/duaer-converge` — compare result to
|
|
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
|
|
29
33
|
|
|
30
|
-
**Small feature:** specify → plan → tasks → implement → converge
|
|
34
|
+
**Small feature:** specify → plan → tasks → implement → converge → `check --gate`
|
|
31
35
|
|
|
32
|
-
**Hotfix:** specify (mark hotfix) → tasks → implement → converge
|
|
33
|
-
Never skip specify or
|
|
36
|
+
**Hotfix:** specify (mark hotfix) → tasks → implement → converge → `check --gate`
|
|
37
|
+
Never skip specify (assign), converge (accept), or the gate.
|
|
34
38
|
|
|
35
39
|
## Install
|
|
36
40
|
|
|
37
41
|
```bash
|
|
38
|
-
npx
|
|
42
|
+
npx duaer-spec init --here
|
|
39
43
|
```
|
|
40
44
|
|
|
41
45
|
Or see [`ADOPT.md`](ADOPT.md). No third-party Spec CLI is required — `duaer`
|
|
@@ -45,10 +49,11 @@ installs this repository's method files into the target project.
|
|
|
45
49
|
|
|
46
50
|
```text
|
|
47
51
|
.duaer/specs/<nnn-slug>/
|
|
48
|
-
spec.md
|
|
49
|
-
plan.md
|
|
50
|
-
tasks.md
|
|
51
|
-
|
|
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
|
|
52
57
|
```
|
|
53
58
|
|
|
54
59
|
## Related
|
package/README.md
CHANGED
|
@@ -1,44 +1,70 @@
|
|
|
1
1
|
# duaer-spec
|
|
2
2
|
|
|
3
|
-
**Duaer** is a
|
|
4
|
-
|
|
5
|
-
Issue/PR gates.
|
|
3
|
+
**Duaer** is a delivery operating system for AI coding agents treated as
|
|
4
|
+
**digital employees**.
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
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
9
|
|
|
10
|
-
|
|
10
|
+
Cursor is the labor. Duaer is hire → assign → accept.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
# GitHub (available now)
|
|
14
|
-
npx github:fujiezee/duaer-spec@v0.1.0 duaer init --here
|
|
12
|
+
## Install (onboard the employee)
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
```bash
|
|
17
15
|
npx duaer-spec init --here
|
|
16
|
+
# or pin: npx github:fujiezee/duaer-spec@v0.2.0 duaer init --here
|
|
18
17
|
```
|
|
19
18
|
|
|
20
19
|
```bash
|
|
21
|
-
# method only
|
|
22
|
-
npx
|
|
23
|
-
|
|
20
|
+
# Lite: method only (personal / small changes)
|
|
21
|
+
npx duaer-spec init --here --method
|
|
22
|
+
|
|
23
|
+
# Full ops on another integration branch
|
|
24
|
+
npx duaer-spec init --here --ops --branch develop
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
Verify: `npx
|
|
27
|
+
Verify: `npx duaer-spec check .`
|
|
28
|
+
Merge gate: `npx duaer-spec check . --gate`
|
|
27
29
|
Details: [`ADOPT.md`](ADOPT.md)
|
|
28
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.
|
|
54
|
+
|
|
29
55
|
## Two layers
|
|
30
56
|
|
|
31
57
|
| Layer | Role | Where |
|
|
32
58
|
|---|---|---|
|
|
33
|
-
| **Agent ops** | How
|
|
59
|
+
| **Agent ops** | How the employee is allowed to operate | [`AGENTS.md`](AGENTS.md), [`docs/agent/`](docs/agent/) |
|
|
34
60
|
| **Duaer method** | What to build (Spec → Plan → Tasks → Implement → Converge) | [`DUADER.md`](DUADER.md), [`.duaer/`](.duaer/), [`.cursor/skills/`](.cursor/skills/) |
|
|
35
61
|
|
|
36
|
-
When they conflict, **agent ops win
|
|
62
|
+
When they conflict, **agent ops win** — process beats improvisation.
|
|
37
63
|
|
|
38
64
|
## Default loop
|
|
39
65
|
|
|
40
66
|
```text
|
|
41
|
-
constitution → specify → plan → tasks → implement → converge
|
|
67
|
+
constitution → specify → plan → tasks → implement → converge → check --gate
|
|
42
68
|
```
|
|
43
69
|
|
|
44
70
|
Small change: `specify → plan → tasks → implement → converge`
|
|
@@ -52,14 +78,16 @@ Slash skills: `/duaer-specify`, `/duaer-plan`, `/duaer-tasks`, `/duaer-implement
|
|
|
52
78
|
```text
|
|
53
79
|
bin/duaer.mjs CLI (duaer init | check | version)
|
|
54
80
|
package.json npm package metadata
|
|
55
|
-
AGENTS.md Agent-ops contract
|
|
56
|
-
DUADER.md Methodology conventions
|
|
57
|
-
ADOPT.md
|
|
81
|
+
AGENTS.md Agent-ops contract (how employees operate)
|
|
82
|
+
DUADER.md Methodology conventions (how work is briefed)
|
|
83
|
+
ADOPT.md Onboarding guide
|
|
58
84
|
.duaer/ Memory, templates, workflows, scripts
|
|
85
|
+
.duaer/specs/<feature>/ Brief + tasks + delivery.json stamp
|
|
59
86
|
.cursor/rules/ Agent-ops + Duaer rules
|
|
60
87
|
.cursor/skills/ duaer-* skills
|
|
61
88
|
docs/agent/ Workflow detail + checklists
|
|
62
89
|
docs/maintaining.md How maintainers evolve the method
|
|
90
|
+
docs/npm-trusted-publishing.md npm Trusted Publishing (OIDC) setup
|
|
63
91
|
examples/ Optional product overlays (not defaults)
|
|
64
92
|
```
|
|
65
93
|
|
package/bin/duaer.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* duaer — CLI for the duaer-spec
|
|
3
|
+
* duaer — CLI for the duaer-spec delivery OS (digital employees)
|
|
4
4
|
*
|
|
5
5
|
* duaer init [dir] [--all|--method|--ops] [--force] [--branch <name>]
|
|
6
|
-
* duaer check [dir]
|
|
6
|
+
* duaer check [dir] [--workplace|--delivery|--gate]
|
|
7
7
|
* duaer version
|
|
8
8
|
* duaer help
|
|
9
9
|
*/
|
|
@@ -24,26 +24,35 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
|
24
24
|
const PKG_ROOT = resolve(__dirname, '..')
|
|
25
25
|
const PKG = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8'))
|
|
26
26
|
|
|
27
|
-
const USAGE = `duaer —
|
|
27
|
+
const USAGE = `duaer — Delivery OS for AI digital employees (duaer-spec ${PKG.version})
|
|
28
|
+
|
|
29
|
+
Hire agents into a repo, assign Briefs (Specs), accept only when the gate passes.
|
|
28
30
|
|
|
29
31
|
Usage:
|
|
30
|
-
duaer init [dir] [options]
|
|
31
|
-
duaer check [dir]
|
|
32
|
-
duaer version
|
|
33
|
-
duaer help
|
|
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
|
|
34
36
|
|
|
35
37
|
Init options:
|
|
36
|
-
--all
|
|
37
|
-
--method
|
|
38
|
-
--ops
|
|
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)
|
|
39
41
|
--force Overwrite existing managed files
|
|
40
42
|
--branch <n> Integration branch for baseline note (default: main)
|
|
41
43
|
--here Same as dir=.
|
|
42
44
|
|
|
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)
|
|
51
|
+
|
|
43
52
|
Examples:
|
|
44
|
-
npx
|
|
45
|
-
node bin/duaer.mjs init ../my-app --all
|
|
53
|
+
npx duaer-spec init --here
|
|
46
54
|
duaer check .
|
|
55
|
+
duaer check . --gate
|
|
47
56
|
`
|
|
48
57
|
|
|
49
58
|
function parseArgs(argv) {
|
|
@@ -54,6 +63,9 @@ function parseArgs(argv) {
|
|
|
54
63
|
mode: 'all',
|
|
55
64
|
force: false,
|
|
56
65
|
branch: 'main',
|
|
66
|
+
workplace: false,
|
|
67
|
+
delivery: false,
|
|
68
|
+
gate: false,
|
|
57
69
|
}
|
|
58
70
|
const rest = args.slice(1)
|
|
59
71
|
for (let i = 0; i < rest.length; i++) {
|
|
@@ -63,6 +75,9 @@ function parseArgs(argv) {
|
|
|
63
75
|
else if (a === '--ops') out.mode = 'ops'
|
|
64
76
|
else if (a === '--force') out.force = true
|
|
65
77
|
else if (a === '--here') out.dir = '.'
|
|
78
|
+
else if (a === '--workplace') out.workplace = true
|
|
79
|
+
else if (a === '--delivery') out.delivery = true
|
|
80
|
+
else if (a === '--gate') out.gate = true
|
|
66
81
|
else if (a === '--branch') {
|
|
67
82
|
out.branch = rest[++i]
|
|
68
83
|
if (!out.branch) throw new Error('--branch requires a value')
|
|
@@ -86,7 +101,6 @@ function copyPath(from, to, { force }) {
|
|
|
86
101
|
if (existsSync(to) && !force) {
|
|
87
102
|
const st = statSync(to)
|
|
88
103
|
if (st.isDirectory()) {
|
|
89
|
-
// merge: copy children carefully
|
|
90
104
|
for (const name of readdirSync(from)) {
|
|
91
105
|
copyPath(join(from, name), join(to, name), { force })
|
|
92
106
|
}
|
|
@@ -218,20 +232,101 @@ function cmdInit(opts) {
|
|
|
218
232
|
}
|
|
219
233
|
|
|
220
234
|
console.log(`
|
|
221
|
-
|
|
235
|
+
Hired.
|
|
222
236
|
|
|
223
|
-
Next:
|
|
224
|
-
1.
|
|
225
|
-
2.
|
|
226
|
-
3.
|
|
227
|
-
4.
|
|
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
|
|
228
245
|
|
|
246
|
+
No Spec = not assigned. Open tasks or open delivery.json = not accepted.
|
|
229
247
|
See DUADER.md and AGENTS.md in the target project.
|
|
230
248
|
`)
|
|
231
249
|
}
|
|
232
250
|
|
|
233
|
-
function
|
|
234
|
-
|
|
251
|
+
function countOpenTasks(tasksPath) {
|
|
252
|
+
if (!existsSync(tasksPath)) return null
|
|
253
|
+
const text = readFileSync(tasksPath, 'utf8')
|
|
254
|
+
const open = (text.match(/^\s*-\s*\[\s\]/gm) || []).length
|
|
255
|
+
const done = (text.match(/^\s*-\s*\[x\]/gim) || []).length
|
|
256
|
+
return { open, done }
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function readDelivery(path) {
|
|
260
|
+
if (!existsSync(path)) return null
|
|
261
|
+
try {
|
|
262
|
+
return JSON.parse(readFileSync(path, 'utf8'))
|
|
263
|
+
} catch {
|
|
264
|
+
return { status: 'invalid', error: 'unreadable delivery.json' }
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function listFeatures(target) {
|
|
269
|
+
const root = join(target, '.duaer', 'specs')
|
|
270
|
+
if (!existsSync(root)) return []
|
|
271
|
+
return readdirSync(root)
|
|
272
|
+
.filter((name) => {
|
|
273
|
+
try {
|
|
274
|
+
return statSync(join(root, name)).isDirectory()
|
|
275
|
+
} catch {
|
|
276
|
+
return false
|
|
277
|
+
}
|
|
278
|
+
})
|
|
279
|
+
.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
|
+
}
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
name,
|
|
320
|
+
hasSpec,
|
|
321
|
+
tasks,
|
|
322
|
+
stamp,
|
|
323
|
+
verdict,
|
|
324
|
+
blockers,
|
|
325
|
+
}
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function checkWorkplace(target) {
|
|
235
330
|
const checks = [
|
|
236
331
|
['.duaer/memory/constitution.md', 'method'],
|
|
237
332
|
['DUADER.md', 'method'],
|
|
@@ -242,17 +337,95 @@ function cmdCheck(dir) {
|
|
|
242
337
|
['docs/agent/workflow.md', 'ops'],
|
|
243
338
|
]
|
|
244
339
|
let missing = 0
|
|
245
|
-
console.log(
|
|
340
|
+
console.log('## Workplace\n')
|
|
246
341
|
for (const [rel, kind] of checks) {
|
|
247
342
|
const ok = existsSync(join(target, rel))
|
|
248
343
|
console.log(`${ok ? 'ok ' : 'MISS'} [${kind}] ${rel}`)
|
|
249
344
|
if (!ok) missing++
|
|
250
345
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
346
|
+
return missing
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function checkDelivery(target, { gate }) {
|
|
350
|
+
const features = listFeatures(target)
|
|
351
|
+
console.log('\n## Delivery\n')
|
|
352
|
+
if (features.length === 0) {
|
|
353
|
+
console.log('ok no features under .duaer/specs/ (nothing to accept)')
|
|
354
|
+
return { blocked: 0, unaccepted: 0, features: 0 }
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let blocked = 0
|
|
358
|
+
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
|
+
}
|
|
368
|
+
if (f.verdict === 'blocked') blocked++
|
|
369
|
+
if (f.verdict === 'unaccepted') unaccepted++
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (gate) {
|
|
373
|
+
console.log('\nGate mode: every feature must be accepted.')
|
|
374
|
+
}
|
|
375
|
+
return { blocked, unaccepted, features: features.length }
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function cmdCheck(opts) {
|
|
379
|
+
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
|
|
383
|
+
|
|
384
|
+
console.log(`Checking ${target}${opts.gate ? ' (gate)' : ''}\n`)
|
|
385
|
+
|
|
386
|
+
let workplaceMissing = 0
|
|
387
|
+
if (runWorkplace) {
|
|
388
|
+
workplaceMissing = checkWorkplace(target)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
let delivery = { blocked: 0, unaccepted: 0, features: 0 }
|
|
392
|
+
if (runDelivery) {
|
|
393
|
+
delivery = checkDelivery(target, { gate: opts.gate })
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
let fail = false
|
|
397
|
+
if (workplaceMissing) {
|
|
398
|
+
console.log(`\n${workplaceMissing} workplace file(s) missing — run: duaer init ${opts.dir} --all`)
|
|
399
|
+
fail = true
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (runDelivery) {
|
|
403
|
+
if (delivery.blocked) {
|
|
404
|
+
console.log(`\n${delivery.blocked} feature(s) blocked — close open tasks / fix Spec / re-run converge`)
|
|
405
|
+
fail = true
|
|
406
|
+
}
|
|
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
|
+
}
|
|
418
|
+
|
|
419
|
+
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.')
|
|
424
|
+
} else {
|
|
425
|
+
console.log('\nDelivery report done.')
|
|
426
|
+
}
|
|
254
427
|
} else {
|
|
255
|
-
|
|
428
|
+
process.exitCode = 1
|
|
256
429
|
}
|
|
257
430
|
}
|
|
258
431
|
|
|
@@ -272,7 +445,7 @@ function main() {
|
|
|
272
445
|
cmdInit(opts)
|
|
273
446
|
break
|
|
274
447
|
case 'check':
|
|
275
|
-
cmdCheck(opts
|
|
448
|
+
cmdCheck(opts)
|
|
276
449
|
break
|
|
277
450
|
case 'version':
|
|
278
451
|
case '--version':
|
package/docs/agent/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Agent workflow (duaer-spec)
|
|
2
2
|
|
|
3
|
-
Authoritative docs for *how*
|
|
4
|
-
when they conflict.
|
|
3
|
+
Authoritative docs for *how* digital employees operate (workplace policy).
|
|
4
|
+
Wins over Duaer and `examples/` when they conflict.
|
|
5
5
|
|
|
6
6
|
| Doc | Description |
|
|
7
7
|
|---|---|
|
|
@@ -94,6 +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
98
|
- [ ] Branch refreshed against latest `main`.
|
|
98
99
|
- [ ] Merged into local `main` (and/or remote PR/MR when required).
|
|
99
100
|
- [ ] Only this request's logical changes included.
|
|
@@ -121,6 +122,7 @@ See [spec update guidance](workflow.md#3-spec-update-guidance).
|
|
|
121
122
|
| 3 | Impacted specs updated | [R1](workflow.md#r1--spec-first--spec-sync) |
|
|
122
123
|
| 4 | E2E docs updated or confirmed unnecessary | [R3](workflow.md#r3--e2e-coverage-doc) |
|
|
123
124
|
| 5 | Targeted validation done or waived with reason | Development loop |
|
|
125
|
+
| 5a | `duaer check . --gate` when features exist | Delivery OS gate |
|
|
124
126
|
| 6 | Conventional commits | [R2](workflow.md#r2--commit-per-change) |
|
|
125
127
|
| 7 | No secrets or local data | [§4](workflow.md#4-what-never-to-commit) |
|
|
126
128
|
| 8 | Merged into `main`; worktree and branch removed | [R4](workflow.md#r4--request-branch--worktree--merge-gate) · [§6.1](#61-merge-cleanup) |
|
|
@@ -20,9 +20,16 @@ Status: planned | automated | manual
|
|
|
20
20
|
|
|
21
21
|
## Catalog
|
|
22
22
|
|
|
23
|
+
This repository’s own product surface (CLI + docs). Adopters replace or extend
|
|
24
|
+
the table for their app.
|
|
25
|
+
|
|
23
26
|
| ID | Title | Status |
|
|
24
27
|
|---|---|---|
|
|
25
|
-
|
|
|
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 |
|
|
26
33
|
|
|
27
34
|
## Traceability
|
|
28
35
|
|
|
@@ -30,4 +37,8 @@ Keep this matrix current when scenarios or specs change.
|
|
|
30
37
|
|
|
31
38
|
| Scenario | Spec / feature | Notes |
|
|
32
39
|
|---|---|---|
|
|
33
|
-
|
|
|
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 |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "duaer-spec",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Delivery OS for AI digital employees: Spec briefs, agent ops, check --gate handoffs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"duaer": "bin/duaer.mjs",
|
|
@@ -36,11 +36,13 @@
|
|
|
36
36
|
"homepage": "https://github.com/fujiezee/duaer-spec#readme",
|
|
37
37
|
"keywords": [
|
|
38
38
|
"duaer",
|
|
39
|
+
"digital-employee",
|
|
40
|
+
"delivery-os",
|
|
39
41
|
"spec-driven",
|
|
40
42
|
"agents",
|
|
41
43
|
"cursor",
|
|
42
44
|
"ai-engineering",
|
|
43
|
-
"
|
|
45
|
+
"controllable-delivery"
|
|
44
46
|
],
|
|
45
47
|
"license": "MIT",
|
|
46
48
|
"scripts": {
|