duaer-spec 0.6.0 → 0.6.2

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.
@@ -42,7 +42,8 @@ cd .worktree/<request-id>
42
42
  - One logical change per commit; leave the worktree clean
43
43
  - User/protocol-visible changes update E2E scenario docs
44
44
  - Merge into **`develop`** when done (hotfix: **`main`**, then back-merge **`develop`**)
45
- - Stop worktree services → remove worktree → delete short branch
45
+ - **Handoff (mandatory):** stop worktree-bound processes → remove `.worktree/<id>`
46
+ restart from primary checkout on `develop` (`duaer handoff [--run]`, `.duaer/handoff.json`)
46
47
  - Push only when the user explicitly asks
47
48
  - Promote **`develop` → `main`** only when the user asks to go online
48
49
 
@@ -27,6 +27,10 @@ For new behavior, fixes, refactors that change product behavior, or architecture
27
27
  4. Do **not** dump a methodology tutorial unless they ask how Duaer works.
28
28
  5. Merge to **`develop`** when done (hotfix: **`main`**, then back-merge
29
29
  **`develop`**). Promote to **`main`** only if the user asks to go online.
30
+ 6. **Handoff (mandatory):** stop processes bound to `.worktree/<id>/`, remove
31
+ the worktree, then restart services from the primary checkout on `develop`
32
+ using `.duaer/handoff.json` / `duaer handoff [--run]`. Never leave the user
33
+ on a dead worktree server.
30
34
  See `docs/agent/branching-and-release.md`.
31
35
 
32
36
  ## Handoff
@@ -0,0 +1,9 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "onWorktreeRemove": {
4
+ "stop": "Stop any process whose working directory is under the request worktree (dev servers, file watchers, local APIs).",
5
+ "restartFromPrimary": true,
6
+ "commands": []
7
+ },
8
+ "notes": "After merging into develop (or hotfix main), stop worktree-bound processes, remove .worktree/<id>, then restart from the primary checkout. Add commands such as \"npm run dev\" when this project needs a local service after handoff. Empty commands = stop only; agent still reports handoff."
9
+ }
package/ADOPT.md CHANGED
@@ -1,49 +1,39 @@
1
1
  # Adopt duaer-spec
2
2
 
3
- Install once so the coding agent behaves as a **digital employee**: it runs
4
- Brief → work → accept **without** the human operating slash commands or CLI.
5
-
6
- ## Install (human, once)
3
+ ## Install
7
4
 
8
5
  ```bash
9
6
  npx duaer-spec init --here
10
- # pin: npx github:fujiezee/duaer-spec@v0.6.0 duaer init --here
11
7
  ```
12
8
 
9
+ ## Update
10
+
13
11
  ```bash
14
- npx duaer-spec init --here --method
15
- npx duaer-spec init --here --ops # default integration note: develop
12
+ npx duaer-spec update
16
13
  ```
17
14
 
18
- Ensure the repo has **`main`** and **`develop`**. Agent ops require both, plus
19
- `feat/*` / `fix/*` for work. See
20
- [`docs/agent/branching-and-release.md`](docs/agent/branching-and-release.md).
15
+ That’s it. If you customized local files, glance at `git diff` once.
21
16
 
22
- After that: talk to the agent in plain language. Do not teach them `/duaer-*`.
17
+ Ensure the repo has **`main`** and **`develop`**. See
18
+ [`docs/agent/branching-and-release.md`](docs/agent/branching-and-release.md).
23
19
 
24
- ## What gets installed
20
+ Then talk to the agent in plain language.
25
21
 
26
- - Always-on rule: agent runs the autonomous job loop on product work
27
- - Playbook skill `duaer-do` (agent follows it; human need not invoke it)
28
- - Agent ops: `AGENTS.md`, branching/release docs, mandatory `.worktree/`
29
- - Optional step skills for large jobs (agent-only)
22
+ ## Options
30
23
 
31
- ## Branch model (mandatory)
24
+ ```bash
25
+ npx duaer-spec init --here --method
26
+ npx duaer-spec init --here --ops
27
+ npx duaer-spec update --method # rare; usually omit
28
+ ```
32
29
 
33
- | Branch | Role |
34
- |---|---|
35
- | `main` | Production / online |
36
- | `develop` | Day-to-day integration |
37
- | `feat/<name>` | Features → `develop` |
38
- | `fix/<name>` | Fixes → `develop` (hotfix via `main` then back to `develop`) |
30
+ ## What you get
39
31
 
40
- Go-live: promote `develop` `main` only when you ask to ship.
32
+ - Autonomous job loop (ask Brief work accept)
33
+ - `.duaer/handoff.json` + `duaer handoff` after worktree remove
34
+ - Agent ops: `main` / `develop` / `feat` / `fix` + `.worktree/`
41
35
 
42
36
  ## Precedence
43
37
 
44
- 1. `AGENTS.md` — how employees operate
45
- 2. `DUADER.md` / `.duaer/` — how jobs are briefed
46
-
47
- ## Updates
48
-
49
- `duaer init --force` (review the diff) or pin a tag.
38
+ 1. `AGENTS.md`
39
+ 2. `DUADER.md` / `.duaer/`
package/AGENTS.md CHANGED
@@ -251,7 +251,9 @@ git worktree prune
251
251
  * Use `git branch -d` (not `-D`)
252
252
  * Delete only your own worktree and branch
253
253
  * Never commit `.worktree/` (ignored)
254
- * Stop worktree-bound services before remove; restart from primary checkout if needed
254
+ * Stop worktree-bound services before remove; then restart from the primary
255
+ checkout on **`develop`** via `.duaer/handoff.json` / `duaer handoff [--run]`
256
+ (mandatory handoff — see [branching-and-release](docs/agent/branching-and-release.md))
255
257
 
256
258
  ## Development Workflow
257
259
 
@@ -267,7 +269,8 @@ git worktree prune
267
269
  10. Commit each logical change
268
270
  11. Refresh against latest **`develop`** (or **`main`** for hotfix)
269
271
  12. Merge into local **`develop`** (hotfix: **`main`**, then back-merge to **`develop`**)
270
- 13. Stop worktree services; remove worktree; delete short branch
272
+ 13. Stop worktree services; remove worktree; delete short branch; **handoff**
273
+ restart on `develop` (`duaer handoff [--run]` / `.duaer/handoff.json`)
271
274
  14. Push only when explicitly requested
272
275
  15. Promote **`develop` → `main`** only when the user explicitly asks to go online
273
276
 
package/CHANGELOG.md CHANGED
@@ -1,47 +1,32 @@
1
1
  # Changelog
2
2
 
3
- ## 0.6.0 — 2026-09-15
4
-
5
- ### Mandatory branch model + release flows
6
-
7
- - Required branches: **`main`** (production), **`develop`** (integration),
8
- **`feat/*`**, **`fix/*`**
9
- - New doc: [`docs/agent/branching-and-release.md`](docs/agent/branching-and-release.md)
10
- — day-to-day, go-live, feature / bug / hotfix / issue / PR flows
11
- - Agent ops merge target is **`develop`**; promote to **`main`** only when
12
- shipping; hotfixes from `main` then back-merge `develop`
13
- - Worktree cleanup includes stopping services and restarting on `develop` if needed
14
- - `duaer init` default `--branch` is `develop`
3
+ ## 0.6.2 — 2026-09-15
15
4
 
16
- ## 0.5.1 — 2026-09-15
17
-
18
- ### Mandatory in-repo worktrees
5
+ ### Simple update
19
6
 
20
- - Request worktrees **must** live under **`.worktree/<request-id>`**
21
- - `.worktree/` is gitignored; `duaer init` ensures the ignore rule
7
+ - `npx duaer-spec update` refreshes an existing install (no `--force` flags to remember)
8
+ - Remembers prior init mode from `.duaer/duaer-init.json`
9
+ - README / ADOPT: install + update are each one line
22
10
 
23
- ## 0.5.0 — 2026-09-15
11
+ ## 0.6.1 — 2026-09-15
24
12
 
25
- ### Autonomous digital employee
13
+ ### Worktree develop service handoff
26
14
 
27
- - Humans ask; agents run Brief → work → accept without slash-command ops
15
+ - `.duaer/handoff.json` + `duaer handoff [--run]`
16
+ - Mandatory restart on develop after worktree remove
28
17
 
29
- ## 0.4.0 — 2026-09-15
30
-
31
- - `/duaer-do` + `duaer status` everyday path (later reframed as agent-internal)
32
-
33
- ## 0.3.0 — 2026-09-15
18
+ ## 0.6.0 — 2026-09-15
34
19
 
35
- - Job handoff policy (`coach` / `strict` / `off`)
20
+ - Mandatory `main` / `develop` / `feat/*` / `fix/*` + release flows
36
21
 
37
- ## 0.2.0 — 2026-09-15
22
+ ## 0.5.1 — 2026-09-15
38
23
 
39
- - `delivery.json` handoff stamp
24
+ - Mandatory `.worktree/` (gitignored)
40
25
 
41
- ## 0.1.1 — 2026-09-15
26
+ ## 0.5.0 — 2026-09-15
42
27
 
43
- - Digital-employee positioning
28
+ - Autonomous digital employee
44
29
 
45
- ## 0.1.0 — 2026-09-15
30
+ ## 0.4.0 — 0.1.0
46
31
 
47
- - First public methodology release
32
+ See git history for earlier notes.
package/README.md CHANGED
@@ -13,8 +13,13 @@ talk; the employee runs the process.
13
13
  npx duaer-spec init --here
14
14
  ```
15
15
 
16
- Then use Cursor as usual — describe what you want. No slash-command chain
17
- required.
16
+ ## Update
17
+
18
+ ```bash
19
+ npx duaer-spec update
20
+ ```
21
+
22
+ Then use Cursor as usual — describe what you want.
18
23
 
19
24
  ## What the employee does (by itself)
20
25
 
@@ -38,10 +43,13 @@ everyday human UI.
38
43
  Go-live and flows by issue type:
39
44
  [`docs/agent/branching-and-release.md`](docs/agent/branching-and-release.md).
40
45
 
46
+ **After merge:** `duaer handoff [--run]` restarts local services on `develop`
47
+ (configure commands in `.duaer/handoff.json`).
48
+
41
49
  ## Optional
42
50
 
43
51
  ```bash
44
- npx duaer-spec@0.6.0 init --here --method # method only
52
+ npx duaer-spec init --here --method
45
53
  ```
46
54
 
47
55
  Details: [`ADOPT.md`](ADOPT.md) · Method: [`DUADER.md`](DUADER.md) · Ops: [`AGENTS.md`](AGENTS.md)
package/bin/duaer.mjs CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  statSync,
16
16
  writeFileSync,
17
17
  } from 'node:fs'
18
+ import { spawn } from 'node:child_process'
18
19
  import { dirname, join, resolve } from 'node:path'
19
20
  import { fileURLToPath } from 'node:url'
20
21
 
@@ -26,19 +27,16 @@ const POLICY_MODES = new Set(['off', 'coach', 'strict'])
26
27
 
27
28
  const USAGE = `duaer — digital-employee delivery (duaer-spec ${PKG.version})
28
29
 
29
- Human (once):
30
- duaer init [--here] Install so the agent works as a digital employee
30
+ Install: npx duaer-spec init --here
31
+ Update: npx duaer-spec update
31
32
 
32
- After install: talk to the agent in plain language. Do not operate /duaer-* phases.
33
+ Then talk to the agent in plain language.
33
34
 
34
- Agent / optional diagnostics:
35
- duaer status [dir]
36
- duaer check | policy | version
35
+ Also:
36
+ duaer handoff [--run] Restart services on develop after worktree remove
37
+ duaer status | check | policy | version | help
37
38
 
38
- Init: --all (default) | --method | --ops | --force | --branch <n> (default: develop) | --here
39
-
40
- Example:
41
- npx duaer-spec init --here
39
+ Init options: --all | --method | --ops | --force | --branch <n> | --here
42
40
  `
43
41
 
44
42
  function parseArgs(argv) {
@@ -55,20 +53,29 @@ function parseArgs(argv) {
55
53
  allJobs: false,
56
54
  strict: false,
57
55
  policyMode: null,
56
+ run: false,
57
+ modeExplicit: false,
58
58
  }
59
59
  const rest = args.slice(1)
60
60
  for (let i = 0; i < rest.length; i++) {
61
61
  const a = rest[i]
62
- if (a === '--all') out.mode = 'all'
63
- else if (a === '--method') out.mode = 'method'
64
- else if (a === '--ops') out.mode = 'ops'
65
- else if (a === '--force') out.force = true
62
+ if (a === '--all') {
63
+ out.mode = 'all'
64
+ out.modeExplicit = true
65
+ } else if (a === '--method') {
66
+ out.mode = 'method'
67
+ out.modeExplicit = true
68
+ } else if (a === '--ops') {
69
+ out.mode = 'ops'
70
+ out.modeExplicit = true
71
+ } else if (a === '--force') out.force = true
66
72
  else if (a === '--here') out.dir = '.'
67
73
  else if (a === '--workplace') out.workplace = true
68
74
  else if (a === '--delivery') out.delivery = true
69
75
  else if (a === '--job') out.job = true
70
76
  else if (a === '--all-jobs') out.allJobs = true
71
77
  else if (a === '--strict' || a === '--gate') out.strict = true
78
+ else if (a === '--run') out.run = true
72
79
  else if (a === '--branch') {
73
80
  out.branch = rest[++i]
74
81
  if (!out.branch) throw new Error('--branch requires a value')
@@ -170,11 +177,37 @@ function writeDefaultPolicy(target, { force }) {
170
177
  console.log(' .duaer/delivery-policy.json (mode=coach)')
171
178
  }
172
179
 
180
+ function defaultHandoff() {
181
+ return {
182
+ schemaVersion: 1,
183
+ onWorktreeRemove: {
184
+ stop:
185
+ 'Stop any process whose working directory is under the request worktree (dev servers, file watchers, local APIs).',
186
+ restartFromPrimary: true,
187
+ commands: [],
188
+ },
189
+ notes:
190
+ 'After merging into develop, stop worktree processes, remove .worktree/<id>, then restart from the primary checkout. Add commands such as "npm run dev" when needed.',
191
+ }
192
+ }
193
+
194
+ function writeDefaultHandoff(target, { force }) {
195
+ const path = join(target, '.duaer', 'handoff.json')
196
+ if (existsSync(path) && !force) {
197
+ console.log(`skip (exists): ${path}`)
198
+ return
199
+ }
200
+ ensureDir(dirname(path))
201
+ writeFileSync(path, JSON.stringify(defaultHandoff(), null, 2) + '\n')
202
+ console.log(' .duaer/handoff.json')
203
+ }
204
+
173
205
  function installMethod(target, opts) {
174
206
  console.log('Installing Duaer method…')
175
207
  copyPath(join(PKG_ROOT, '.duaer'), join(target, '.duaer'), opts)
176
208
  console.log(' .duaer/')
177
209
  writeDefaultPolicy(target, opts)
210
+ writeDefaultHandoff(target, opts)
178
211
 
179
212
  ensureDir(join(target, '.cursor', 'skills'))
180
213
  const skillsRoot = join(PKG_ROOT, '.cursor', 'skills')
@@ -303,10 +336,31 @@ function cmdInit(opts) {
303
336
  console.log(`
304
337
  Hired.
305
338
 
306
- You: describe work in Cursor (plain language).
307
- Agent: runs Brief → work → accept by itself — do not operate slash phases.
339
+ Talk to the agent in plain language.
340
+ Later update: npx duaer-spec update
341
+ `)
342
+ }
308
343
 
309
- Optional diagnostic: duaer status
344
+ function cmdUpdate(opts) {
345
+ const target = resolve(opts.dir)
346
+ const marker = readJson(join(target, '.duaer', 'duaer-init.json'), null)
347
+ if (!marker && !existsSync(join(target, '.duaer')) && !existsSync(join(target, 'DUADER.md'))) {
348
+ console.log('No duaer install found here. Use: npx duaer-spec init --here')
349
+ process.exitCode = 1
350
+ return
351
+ }
352
+
353
+ if (!opts.modeExplicit && marker?.mode && ['all', 'method', 'ops'].includes(marker.mode)) {
354
+ opts.mode = marker.mode
355
+ }
356
+ if (marker?.branch) opts.branch = marker.branch
357
+ opts.force = true
358
+
359
+ console.log(`Updating → ${PKG.version}`)
360
+ cmdInit(opts)
361
+ console.log(`
362
+ Updated.
363
+ If you customized constitution / baseline / handoff commands, check git diff once.
310
364
  `)
311
365
  }
312
366
 
@@ -596,6 +650,49 @@ Not a git merge lock.
596
650
  `)
597
651
  }
598
652
 
653
+ function cmdHandoff(opts) {
654
+ const target = resolve(opts.dir)
655
+ const cfg = readJson(join(target, '.duaer', 'handoff.json'), defaultHandoff())
656
+ const block = cfg.onWorktreeRemove || {}
657
+ const commands = Array.isArray(block.commands) ? block.commands.filter(Boolean) : []
658
+
659
+ console.log(`Handoff @ ${target}`)
660
+ console.log('(worktree → merged develop / hotfix main)\n')
661
+ console.log('1. Confirm request branch is merged into develop (hotfix: main + back-merge develop)')
662
+ console.log(`2. Stop: ${block.stop || defaultHandoff().onWorktreeRemove.stop}`)
663
+ console.log('3. git worktree remove .worktree/<id> && git branch -d feat|fix/<name>')
664
+ console.log('4. Restart from primary checkout on the merged branch:\n')
665
+
666
+ if (!commands.length) {
667
+ console.log(' (no commands in .duaer/handoff.json)')
668
+ console.log(' Add e.g. "npm run dev" under onWorktreeRemove.commands')
669
+ console.log(' If you started a server in the worktree, re-run that same command here.')
670
+ } else {
671
+ for (const c of commands) console.log(` $ ${c}`)
672
+ }
673
+
674
+ if (opts.run) {
675
+ if (!commands.length) {
676
+ console.log('\n--run: nothing to start (commands empty).')
677
+ return
678
+ }
679
+ console.log('')
680
+ for (const c of commands) {
681
+ const child = spawn(c, {
682
+ shell: true,
683
+ cwd: target,
684
+ detached: true,
685
+ stdio: 'ignore',
686
+ })
687
+ child.unref()
688
+ console.log(`started (detached) pid=${child.pid}: ${c}`)
689
+ }
690
+ console.log('\nServices should now be bound to the primary checkout, not .worktree/.')
691
+ } else if (commands.length) {
692
+ console.log('\nTip: duaer handoff --run # start the commands above in the background')
693
+ }
694
+ }
695
+
599
696
  function main() {
600
697
  let opts
601
698
  try {
@@ -611,9 +708,15 @@ function main() {
611
708
  case 'init':
612
709
  cmdInit(opts)
613
710
  break
711
+ case 'update':
712
+ cmdUpdate(opts)
713
+ break
614
714
  case 'check':
615
715
  cmdCheck(opts)
616
716
  break
717
+ case 'handoff':
718
+ cmdHandoff(opts)
719
+ break
617
720
  case 'job':
618
721
  case 'status':
619
722
  cmdJob(opts)
@@ -48,9 +48,11 @@ update develop
48
48
  → git worktree add .worktree/<id> -b feat|fix/<name> develop
49
49
  → work only in that worktree (Duaer job loop as needed)
50
50
  → merge into local develop
51
- stop any server started in the worktree
52
- remove .worktree/<id>; delete feat|fix/<name>
53
- (optional) start/restart services from the primary checkout on develop
51
+ handoff (mandatory):
52
+ stop processes bound to the worktree
53
+ remove .worktree/<id>; delete feat|fix/<name>
54
+ restart services from the primary checkout on develop
55
+ (see .duaer/handoff.json / `duaer handoff [--run]`)
54
56
  → push develop only if the user asked
55
57
  ```
56
58
 
@@ -66,15 +68,44 @@ cd .worktree/feat-login
66
68
  cd ../.. # primary checkout
67
69
  git switch develop
68
70
  git merge feat/login
69
- # stop processes that used .worktree/feat-login
71
+
72
+ # Handoff — do not skip
73
+ # 1) stop anything started under .worktree/feat-login
70
74
  git worktree remove .worktree/feat-login
71
75
  git branch -d feat/login
76
+ # 2) restart on develop (primary checkout)
77
+ duaer handoff --run
78
+ # or: npm run dev # if listed in .duaer/handoff.json
72
79
  ```
73
80
 
74
- **After merge / before delete worktree:** if a local service was running from the
75
- worktree, stop it; if the user still needs it, start it again from the primary
76
- checkout on **`develop`** (merged tree). Do not expect the old worktree process
77
- to survive directory removal.
81
+ ### Worktree develop service handoff (mandatory)
82
+
83
+ Deleting a worktree **removes that directory**. Any server started there will
84
+ break. Agents **must**:
85
+
86
+ 1. Merge the request branch into **`develop`** (hotfix: **`main`**, then
87
+ back-merge **`develop`**)
88
+ 2. **Stop** processes whose cwd is under `.worktree/<id>/`
89
+ 3. `git worktree remove .worktree/<id>` and delete the short branch
90
+ 4. **Restart** from the **primary checkout** on the merged long-lived branch:
91
+ - Read `.duaer/handoff.json` → `onWorktreeRemove.commands`
92
+ - Run `duaer handoff` (prints the plan) or `duaer handoff --run` (starts
93
+ configured commands in the background from the project root)
94
+ - If `commands` is empty but the agent started a server in the worktree,
95
+ restart the **same** command from the primary checkout on `develop`
96
+ - Tell the user the new service is on the merged branch, not the old worktree
97
+
98
+ Configure per project by editing `.duaer/handoff.json`, for example:
99
+
100
+ ```json
101
+ {
102
+ "schemaVersion": 1,
103
+ "onWorktreeRemove": {
104
+ "restartFromPrimary": true,
105
+ "commands": ["npm run dev"]
106
+ }
107
+ }
108
+ ```
78
109
 
79
110
  ---
80
111
 
@@ -98,7 +98,8 @@ See [spec update guidance](workflow.md#3-spec-update-guidance).
98
98
  - [ ] Active job handoff considered by the **agent** (do not require the human to run CLI for routine work).
99
99
  - [ ] Branch refreshed against latest `develop` (hotfix: `main`).
100
100
  - [ ] Merged into local `develop` (hotfix: `main`, then back-merge `develop`).
101
- - [ ] Worktree services stopped; worktree removed; short branch deleted.
101
+ - [ ] Worktree services stopped; worktree removed; short branch deleted;
102
+ **handoff** restart on `develop` (`duaer handoff` / `.duaer/handoff.json`).
102
103
  - [ ] Only this request's logical changes included.
103
104
  - [ ] Push performed only if the user explicitly asked for this request.
104
105
  - [ ] `develop` → `main` promotion only if the user asked to go online.
@@ -33,6 +33,8 @@ the table for their app.
33
33
  | E2E-006 | `duaer-do` skill describes agent-triggered loop | manual |
34
34
  | E2E-007 | Request worktrees use `.worktree/<id>`; `.worktree/` gitignored | manual |
35
35
  | E2E-008 | Docs require main+develop+feat+fix and typed go-live flows | manual |
36
+ | E2E-009 | After merge, handoff restarts services on develop (`duaer handoff`) | manual |
37
+ | E2E-010 | `npx duaer-spec update` refreshes an existing install | manual |
36
38
 
37
39
  ## Traceability
38
40
 
@@ -46,3 +48,5 @@ the table for their app.
46
48
  | E2E-006 | `duaer-do` | Internal playbook |
47
49
  | E2E-007 | `.worktree/` + `.gitignore` | Mandatory isolation |
48
50
  | E2E-008 | `branching-and-release.md` | Branch + release matrix |
51
+ | E2E-009 | `.duaer/handoff.json` / `duaer handoff` | Service handoff |
52
+ | E2E-010 | `duaer update` | One-line refresh |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "duaer-spec",
3
- "version": "0.6.0",
4
- "description": "Digital employees: autonomous Spec→work→accept; mandatory main/develop/feat/fix",
3
+ "version": "0.6.2",
4
+ "description": "Digital employees: autonomous delivery; main/develop/feat/fix; simple update",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "duaer": "bin/duaer.mjs",