dw-kit 1.7.0-rc.2 → 1.8.0-rc.1

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,35 @@
1
- ---
2
- date: [ISO timestamp — e.g. 2026-04-02T14:30:00]
3
- from: [agent-role — researcher | planner | developer | reviewer | debugger]
4
- to: [agent-role — planner | developer | user]
5
- task: [task-name]
6
- status: DONE | DONE_WITH_CONCERNS | BLOCKED | NEEDS_CONTEXT
7
- ---
8
-
9
- ## Summary
10
-
11
- [1-3 câu tóm tắt kết quả / findings]
12
-
13
- ## Details
14
-
15
- [Chi tiết findings, decisions, hoặc implementation notes]
16
-
17
- ## Concerns (nếu DONE_WITH_CONCERNS)
18
-
19
- - [Concern 1]
20
- - [Concern 2]
21
-
22
- ## Blockers (nếu BLOCKED)
23
-
24
- - **Blocker**: [Mô tả vấn đề]
25
- - **Owner**: [Ai cần resolve]
26
- - **Unblock by**: [Action cần làm]
27
-
28
- ## Needs (nếu NEEDS_CONTEXT)
29
-
30
- - [ ] [Thông tin cần thêm]
31
- - [ ] [Quyết định cần từ user/TL]
32
-
33
- ## Next Steps
34
-
35
- - [Bước tiếp theo đề xuất]
1
+ ---
2
+ date: [ISO timestamp — e.g. 2026-04-02T14:30:00]
3
+ from: [agent-role — researcher | planner | developer | reviewer | debugger]
4
+ to: [agent-role — planner | developer | user]
5
+ task: [task-name]
6
+ status: DONE | DONE_WITH_CONCERNS | BLOCKED | NEEDS_CONTEXT
7
+ ---
8
+
9
+ ## Summary
10
+
11
+ [1-3 câu tóm tắt kết quả / findings]
12
+
13
+ ## Details
14
+
15
+ [Chi tiết findings, decisions, hoặc implementation notes]
16
+
17
+ ## Concerns (nếu DONE_WITH_CONCERNS)
18
+
19
+ - [Concern 1]
20
+ - [Concern 2]
21
+
22
+ ## Blockers (nếu BLOCKED)
23
+
24
+ - **Blocker**: [Mô tả vấn đề]
25
+ - **Owner**: [Ai cần resolve]
26
+ - **Unblock by**: [Action cần làm]
27
+
28
+ ## Needs (nếu NEEDS_CONTEXT)
29
+
30
+ - [ ] [Thông tin cần thêm]
31
+ - [ ] [Quyết định cần từ user/TL]
32
+
33
+ ## Next Steps
34
+
35
+ - [Bước tiếp theo đề xuất]
@@ -0,0 +1,54 @@
1
+ # ============================================================================
2
+ # dw-kit Agent Runtime Config — v1
3
+ # Reference: docs/dw_remote_agent_first_context_strategy.md §8 Priority 3
4
+ # ============================================================================
5
+ # Used by `dw session start --agent <name>`. Each agent is a thin descriptor
6
+ # of an EXTERNAL CLI coding agent dw-kit orchestrates (Claude Code, Codex,
7
+ # Gemini, etc.). dw-kit does NOT bundle these agents — they must be installed
8
+ # separately. Per research §14: "Do not replace ... Orchestrate them."
9
+ # ----------------------------------------------------------------------------
10
+
11
+ schema_version: agents@v1
12
+
13
+ agents:
14
+ # Anthropic Claude Code — non-interactive print mode.
15
+ # Install: https://docs.claude.com/en/docs/claude-code
16
+ # F-18 (Win32): goal_mode=stdin so the prompt goes via the child's stdin
17
+ # instead of an arg. Win32 PATHEXT resolution requires shell:true for
18
+ # .cmd shims (claude.cmd), and shell:true + args[] (Node DEP0190) is unsafe
19
+ # for multi-line / quoted prompts — stdin keeps user content out of the
20
+ # shell command line entirely.
21
+ claude:
22
+ command: claude
23
+ args: ["--print"]
24
+ goal_mode: stdin
25
+ env: {}
26
+ description: "Claude Code (Anthropic) — non-interactive print mode"
27
+
28
+ # OpenAI Codex CLI — non-interactive exec mode.
29
+ # Install: npm install -g @openai/codex
30
+ codex:
31
+ command: codex
32
+ args: ["exec"]
33
+ goal_mode: stdin
34
+ env: {}
35
+ description: "Codex CLI (OpenAI) — non-interactive exec mode"
36
+
37
+ # Google Gemini CLI — placeholder shape; adjust args once installed.
38
+ # Install: see Gemini CLI docs (subject to change).
39
+ gemini:
40
+ command: gemini
41
+ args: ["chat", "--once"]
42
+ goal_mode: stdin
43
+ env: {}
44
+ description: "Gemini CLI (Google) — single-prompt mode [placeholder]"
45
+
46
+ # Hermetic test agent used by smoke-test. Echoes the goal then exits 0.
47
+ # NOT for real use — kept here so tests can `dw session start --agent _smoke_echo`
48
+ # without depending on any external binary.
49
+ _smoke_echo:
50
+ command: node
51
+ args: ["-e", "process.stdout.write(`SMOKE_ECHO_GOAL: ${process.argv[1] || ''}\\n`); setTimeout(() => process.exit(0), 50);"]
52
+ goal_mode: trailing-arg
53
+ env: {}
54
+ description: "Internal smoke-test echo agent (do not use for real work)"
@@ -0,0 +1,8 @@
1
+ # .dw/config/connectors.local.yml — GITIGNORED. Secrets live here.
2
+ # Created/updated by `dw connector telegram setup`.
3
+ # Token + allow-list overrides the template in .dw/config/connectors.yml.
4
+ telegram:
5
+ enabled: true
6
+ bot_token: 8693679403:AAG9FrgUd5Ig9eDTAWnA9RqhbMnShRi3Si0
7
+ allowed_user_ids:
8
+ - 6603235862
@@ -0,0 +1,64 @@
1
+ # ============================================================================
2
+ # dw-kit Connectors Config — v1
3
+ # Reference: docs/connector-telegram.md
4
+ # Goal: G-rgoal-realtime-orch phase 2 (chat platform adapters)
5
+ # ============================================================================
6
+ # Connectors bridge external chat platforms (Telegram, Zalo, Slack later) to
7
+ # the local dw session runtime. The chat side speaks "/status", "/sessions",
8
+ # "/start <agent> <goal>", "/logs <id>" etc.; the connector translates and
9
+ # calls the same session-store API as `dw session *`.
10
+ #
11
+ # SECRETS (bot tokens) should NEVER be committed. Prefer environment vars:
12
+ # DW_TG_BOT_TOKEN=... for Telegram
13
+ # This file holds policy (enabled, allow-list) and non-secret defaults.
14
+ # ----------------------------------------------------------------------------
15
+
16
+ schema_version: connectors@v1
17
+
18
+ telegram:
19
+ enabled: false # Safe default. Enable in connectors.local.yml (gitignored).
20
+ bot_token: "" # leave empty; pass via DW_TG_BOT_TOKEN env
21
+ allowed_user_ids: [] # numeric Telegram user IDs. Empty = bot rejects all. Maintainer keeps their own id in a local-only override (NOT committed — `.dw/config/` ships to npm).
22
+ default_workspace: "" # empty = use cwd at start time
23
+ default_agent: claude # used by /start when agent name omitted
24
+ long_poll_timeout_sec: 25 # Telegram supports up to 50; lower if network is flaky
25
+
26
+ # Voice channel (Phase 4 substrate of G-rgoal-realtime-orch).
27
+ # `dw voice` boots a localhost HTTP server with a browser page using Web
28
+ # Speech API for ASR + TTS. Optional orchestrator hybrid: when the regex
29
+ # parser misses, hand the transcript to the configured agent (Claude Code /
30
+ # Codex / Gemini) with a voice-aware system prompt.
31
+ voice:
32
+ # Default UI language for SpeechRecognition + TTS. User can override on the
33
+ # page via dropdown. Common values: en-US · vi-VN · ja-JP · ko-KR · zh-CN.
34
+ # Browser support varies — Chrome / Edge / Safari are best.
35
+ lang: en-US
36
+ # Extra languages to surface in the dropdown (beyond `lang` + en-US default).
37
+ extra_langs: [vi-VN]
38
+ # Server-side TTS fallback when no native browser/OS voice matches the
39
+ # selected language. F-23 (G-dogfood-v1.7): a fresh Windows install has
40
+ # no Vietnamese SAPI voice; we proxy MP3 from Google Translate's public
41
+ # TTS endpoint. Modes:
42
+ # auto — only when no native voice matches (default; balanced)
43
+ # always — always use server-side (best for cross-machine consistency)
44
+ # none — never use server-side (privacy: spoken text stays local)
45
+ # Privacy note: in `auto`/`always`, the spoken text leaves the machine to
46
+ # translate.google.com. Disable with `none` if that is unacceptable.
47
+ fallback_tts: auto
48
+ orchestrator:
49
+ enabled: false # opt-in. To enable WITHOUT committing it, add the
50
+ # same `voice:` section to `.dw/config/connectors.local.yml`
51
+ # (gitignored) — the local file overrides this template
52
+ # via deep-merge (same pattern as bot_token + allow-list).
53
+ agent: claude # must exist in .dw/config/agents.yml; CLI must be on PATH
54
+ timeout_ms: 30000 # max wait per turn (Claude is typically 2-8s)
55
+
56
+ # Future connectors land here:
57
+ #
58
+ # zalo:
59
+ # enabled: false
60
+ # ...
61
+ #
62
+ # slack:
63
+ # enabled: false
64
+ # ...
@@ -1,53 +1,53 @@
1
- # Agent Communication Protocol — dw-kit v1.2
2
-
3
- ## Mục Đích
4
-
5
- Khi một task lớn cần nhiều "vai" khác nhau (researcher → planner → developer), việc ghi lại kết quả từng bước giúp:
6
- - Team members (người hoặc agent) biết chính xác task đang ở đâu
7
- - Audit trail rõ ràng: ai quyết định gì, lúc nào
8
- - Session tiếp theo có thể tiếp tục mà không cần hỏi lại
9
-
10
- ## Convention: Reports Directory
11
-
12
- ```
13
- .dw/tasks/[task-name]/
14
- ├── [name]-context.md # Research findings
15
- ├── [name]-plan.md # Implementation plan
16
- ├── [name]-progress.md # Progress tracking
17
- └── reports/ # Agent communication (v1.2+)
18
- ├── 260402-1430-from-researcher-to-planner-analysis.md
19
- ├── 260402-1500-from-planner-to-developer-subtask-1.md
20
- └── 260402-1600-from-developer-to-reviewer-pr-ready.md
21
- ```
22
-
23
- **Filename format**: `[YYMMDD-HHMM]-from-[role]-to-[role]-[description].md`
24
-
25
- ## Status Codes
26
-
27
- | Status | Nghĩa |
28
- |--------|-------|
29
- | `DONE` | Hoàn thành, output sẵn sàng để dùng |
30
- | `DONE_WITH_CONCERNS` | Xong nhưng có điểm đáng chú ý / cần review |
31
- | `BLOCKED` | Bị chặn, cần action từ bên ngoài để tiếp tục |
32
- | `NEEDS_CONTEXT` | Thiếu thông tin, cần human confirm |
33
-
34
- ## Khi Nào Tạo Report
35
-
36
- - Sau khi `dw-research` hoàn thành → report `from-researcher-to-planner`
37
- - Sau khi `dw-plan` approved → report `from-planner-to-developer`
38
- - Khi phát hiện blocker trong execute → report `from-developer-to-user` với `BLOCKED`
39
- - Sau khi review xong → report `from-reviewer-to-developer`
40
-
41
- ## Khi Nào KHÔNG Cần Report
42
-
43
- - Tasks `quick` depth (≤2 files, hotfix) → không cần overhead này
44
- - Solo dev, single session → progress.md đã đủ
45
- - Thông tin đã có trong context.md / plan.md → không duplicate
46
-
47
- ## Template
48
-
49
- Dùng `.claude/templates/agent-report.md`
50
-
51
- ## Lưu Ý Quan Trọng
52
-
53
- Reports là **cho con người đọc**, không phải protocol cho AI. Claude Code đã communicate qua conversation context. Reports giúp team members theo dõi task cross-session, không phải AI-to-AI messaging.
1
+ # Agent Communication Protocol — dw-kit v1.2
2
+
3
+ ## Mục Đích
4
+
5
+ Khi một task lớn cần nhiều "vai" khác nhau (researcher → planner → developer), việc ghi lại kết quả từng bước giúp:
6
+ - Team members (người hoặc agent) biết chính xác task đang ở đâu
7
+ - Audit trail rõ ràng: ai quyết định gì, lúc nào
8
+ - Session tiếp theo có thể tiếp tục mà không cần hỏi lại
9
+
10
+ ## Convention: Reports Directory
11
+
12
+ ```
13
+ .dw/tasks/[task-name]/
14
+ ├── [name]-context.md # Research findings
15
+ ├── [name]-plan.md # Implementation plan
16
+ ├── [name]-progress.md # Progress tracking
17
+ └── reports/ # Agent communication (v1.2+)
18
+ ├── 260402-1430-from-researcher-to-planner-analysis.md
19
+ ├── 260402-1500-from-planner-to-developer-subtask-1.md
20
+ └── 260402-1600-from-developer-to-reviewer-pr-ready.md
21
+ ```
22
+
23
+ **Filename format**: `[YYMMDD-HHMM]-from-[role]-to-[role]-[description].md`
24
+
25
+ ## Status Codes
26
+
27
+ | Status | Nghĩa |
28
+ |--------|-------|
29
+ | `DONE` | Hoàn thành, output sẵn sàng để dùng |
30
+ | `DONE_WITH_CONCERNS` | Xong nhưng có điểm đáng chú ý / cần review |
31
+ | `BLOCKED` | Bị chặn, cần action từ bên ngoài để tiếp tục |
32
+ | `NEEDS_CONTEXT` | Thiếu thông tin, cần human confirm |
33
+
34
+ ## Khi Nào Tạo Report
35
+
36
+ - Sau khi `dw-research` hoàn thành → report `from-researcher-to-planner`
37
+ - Sau khi `dw-plan` approved → report `from-planner-to-developer`
38
+ - Khi phát hiện blocker trong execute → report `from-developer-to-user` với `BLOCKED`
39
+ - Sau khi review xong → report `from-reviewer-to-developer`
40
+
41
+ ## Khi Nào KHÔNG Cần Report
42
+
43
+ - Tasks `quick` depth (≤2 files, hotfix) → không cần overhead này
44
+ - Solo dev, single session → progress.md đã đủ
45
+ - Thông tin đã có trong context.md / plan.md → không duplicate
46
+
47
+ ## Template
48
+
49
+ Dùng `.claude/templates/agent-report.md`
50
+
51
+ ## Lưu Ý Quan Trọng
52
+
53
+ Reports là **cho con người đọc**, không phải protocol cho AI. Claude Code đã communicate qua conversation context. Reports giúp team members theo dõi task cross-session, không phải AI-to-AI messaging.
package/CLAUDE.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Workflow toolkit codebase. Rules live in `.claude/rules/` (auto-loaded).
4
4
 
5
5
  **v2.0 direction:** Context-First SDLC Governance Layer (5 pillars — see `.dw/core/PILLARS.md`)
6
- **Current:** v1.6.0-rc.1 on npm `rc` tag (2026-05-20) + **v1.7.0-rc.1 candidate** on `feat/goals-okr-v1.7` shipping ADR-0010 Goals Management Layer (Accepted Round 2 all 4 Critical + 7 Warnings + 4 Suggestions resolved; `bcurts/agentchattr` summary primitive + bigokr icon/cycle/progress/constellation visualization borrowed; 150/150 smoke tests). Active ADRs: ADR-0001 (Pragmatic Lean), ADR-0005/0006 (Supply-Chain Guard; sunset review 2026-08-12), ADR-0008 (Task Docs v3, v1.5), ADR-0009 (Agent OS, v1.6), ADR-0010 (Goals Layer, v1.7). v1.4 cuts pending telemetry.
6
+ **Current:** **v1.8.0-rc.1** (2026-05-25) 4/5 phase substrate for [G-rgoal-realtime-orch](.dw/goals/G-rgoal-realtime-orch/goal.md) voice-meeting Root Goal: persistent CLI-agent session runtime (`dw session *`), Telegram chat bridge (`dw connector telegram setup`) with 1-command interactive wizard, multi-workspace registry (`dw workspace *`), and browser voice MVP (`dw voice`) with hybrid orchestrator fallback (Claude/Codex/Gemini), full bilingual UX (en + vi), voice-not-installed fallback via Google Translate TTS proxy. **v1.7.0 stable** on `main` (2026-05-24). 284/284 smoke tests. Active ADRs: ADR-0001 (Pragmatic Lean), ADR-0005/0006 (Supply-Chain Guard; sunset review 2026-08-12), ADR-0008 (Task Docs v3, v1.5), ADR-0009 (Agent OS, v1.6), ADR-0010 (Goals Layer, v1.7). ADR-0011 (Session Runtime + Voice Orchestrator, v1.8) pending codification.
7
7
 
8
8
  ---
9
9
 
package/bin/dw.mjs CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const [major] = process.versions.node.split('.').map(Number);
4
4
  if (major < 18) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dw-kit",
3
- "version": "1.7.0-rc.2",
3
+ "version": "1.8.0-rc.1",
4
4
  "description": "AI development workflow toolkit — structured, quality-assured, team-ready. From requirements to dashboard.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -180,6 +180,7 @@ export function run(argv) {
180
180
  .description('Watch task.md for changes and live-reload the HTML preview in browser (local server, debounced)')
181
181
  .option('-p, --port <port>', 'Local server port (auto-finds next available if busy)', parseInt)
182
182
  .option('--rotate-token', 'Regenerate .dw/cache/watch.token at startup (invalidates old browser sessions; C-3)')
183
+ .option('--no-open', 'Skip auto-opening browser tab (also implied by DW_NO_OPEN=1, CI=true, or non-TTY stdout — fixes #23)')
183
184
  .action(async (taskName, opts) => {
184
185
  const { taskWatchCommand } = await import('./commands/task-watch.mjs');
185
186
  await taskWatchCommand(taskName, opts);
@@ -293,6 +294,173 @@ export function run(argv) {
293
294
  await agentVerifyCommand(taskId, opts);
294
295
  });
295
296
 
297
+ // ─ session: persistent CLI-agent session runtime (G-rgoal-realtime-orch phase 1) ─
298
+ const sessionCmd = program
299
+ .command('session')
300
+ .description('Persistent CLI-agent session runtime (orchestrates Claude Code / Codex / Gemini): start · list · show · logs · stop. Sessions survive SSH disconnect.');
301
+
302
+ sessionCmd
303
+ .command('start')
304
+ .description('Start a CLI coding agent as a detached session (survives SSH disconnect)')
305
+ .requiredOption('-a, --agent <name>', 'Agent name from .dw/config/agents.yml (e.g. claude, codex, gemini)')
306
+ .requiredOption('-g, --goal <text>', 'Goal/prompt to pass to the agent')
307
+ .option('-w, --workspace <path>', 'Working directory for the agent (default: cwd)')
308
+ .action(async (opts) => {
309
+ const { sessionStartCommand } = await import('./commands/session.mjs');
310
+ await sessionStartCommand(opts);
311
+ });
312
+
313
+ sessionCmd
314
+ .command('list')
315
+ .description('List active and recent sessions (this workspace, or --all-workspaces)')
316
+ .option('-s, --status <name>', 'Filter by status (running|completed|failed|stopped|exited|all)', 'all')
317
+ .option('-n, --limit <n>', 'Cap rows', parseInt)
318
+ .option('--all-workspaces', 'Aggregate sessions across every registered workspace (phase 3)')
319
+ .action(async (opts) => {
320
+ const { sessionListCommand } = await import('./commands/session.mjs');
321
+ await sessionListCommand(opts);
322
+ });
323
+
324
+ sessionCmd
325
+ .command('show <session-id>')
326
+ .description('Show full snapshot of a session (state + last 10 output lines)')
327
+ .action(async (sessionId) => {
328
+ const { sessionShowCommand } = await import('./commands/session.mjs');
329
+ await sessionShowCommand(sessionId);
330
+ });
331
+
332
+ sessionCmd
333
+ .command('logs <session-id>')
334
+ .description('Print session output log (default: full output.log; --events for JSON events)')
335
+ .option('-n, --tail <n>', 'Tail last N lines/events', parseInt)
336
+ .option('--events', 'Read events.jsonl instead of raw output.log')
337
+ .option('--bytes <n>', 'Cap output at N tail bytes', parseInt)
338
+ .action(async (sessionId, opts) => {
339
+ const { sessionLogsCommand } = await import('./commands/session.mjs');
340
+ await sessionLogsCommand(sessionId, opts);
341
+ });
342
+
343
+ sessionCmd
344
+ .command('stop <session-id>')
345
+ .description('Send SIGTERM (or --signal) to a session\'s process')
346
+ .option('--signal <name>', 'Signal to send (default SIGTERM)', 'SIGTERM')
347
+ .action(async (sessionId, opts) => {
348
+ const { sessionStopCommand } = await import('./commands/session.mjs');
349
+ await sessionStopCommand(sessionId, opts);
350
+ });
351
+
352
+ // ─ voice: browser-based voice MVP (G-rgoal phase 4 — KR-A destination) ─
353
+ program
354
+ .command('voice')
355
+ .description('Browser-based voice MVP — speak dw commands, hear results. Local server + Web Speech API. Spike scope: localhost only.')
356
+ .option('-p, --port <port>', 'Local server port (default 4500; auto-finds next free)', parseInt)
357
+ .option('--default-agent <name>', 'Default agent for "start ..." without explicit agent name')
358
+ .option('--no-open', 'Skip auto-opening browser tab (also implied by DW_NO_OPEN=1, CI=true, or non-TTY stdout)')
359
+ .action(async (opts) => {
360
+ const { voiceCommand } = await import('./commands/voice.mjs');
361
+ await voiceCommand(opts);
362
+ });
363
+
364
+ // ─ workspace: cross-project orchestration registry (G-rgoal phase 3) ─
365
+ const workspaceCmd = program
366
+ .command('workspace')
367
+ .description('Multi-workspace registry: register · list · remove · resolve. Lets one orchestrator drive sessions across multiple .dw/ projects on this host.');
368
+
369
+ workspaceCmd
370
+ .command('register')
371
+ .description('Register the current directory (or --path) as a named workspace')
372
+ .requiredOption('-n, --name <alias>', 'Short alias (a-z A-Z 0-9 . _ -, ≤64 chars)')
373
+ .option('-p, --path <abs>', 'Absolute path to workspace root (default: cwd)')
374
+ .action(async (opts) => {
375
+ const { workspaceRegisterCommand } = await import('./commands/workspace.mjs');
376
+ await workspaceRegisterCommand(opts);
377
+ });
378
+
379
+ workspaceCmd
380
+ .command('list')
381
+ .description('List registered workspaces (with session tally per workspace)')
382
+ .action(async (opts) => {
383
+ const { workspaceListCommand } = await import('./commands/workspace.mjs');
384
+ await workspaceListCommand(opts);
385
+ });
386
+
387
+ workspaceCmd
388
+ .command('remove')
389
+ .description('Remove a workspace from the registry')
390
+ .requiredOption('-n, --name <alias>', 'Workspace alias')
391
+ .action(async (opts) => {
392
+ const { workspaceRemoveCommand } = await import('./commands/workspace.mjs');
393
+ await workspaceRemoveCommand(opts);
394
+ });
395
+
396
+ workspaceCmd
397
+ .command('resolve')
398
+ .description('Print the absolute path of a registered workspace (scriptable)')
399
+ .requiredOption('-n, --name <alias>', 'Workspace alias')
400
+ .action(async (opts) => {
401
+ const { workspaceResolveCommand } = await import('./commands/workspace.mjs');
402
+ await workspaceResolveCommand(opts);
403
+ });
404
+
405
+ // ─ connector: chat platform adapters (G-rgoal phase 2) ─
406
+ const connectorCmd = program
407
+ .command('connector')
408
+ .description('Chat platform connectors (Telegram first; Zalo/Slack later). list · telegram start/check.');
409
+
410
+ connectorCmd
411
+ .command('list')
412
+ .description('Show configured connectors and their enabled/disabled status')
413
+ .action(async () => {
414
+ const { connectorListCommand } = await import('./commands/connector.mjs');
415
+ await connectorListCommand();
416
+ });
417
+
418
+ const telegramCmd = connectorCmd
419
+ .command('telegram')
420
+ .description('Telegram bot connector: bridges chat messages to dw session commands');
421
+
422
+ telegramCmd
423
+ .command('start')
424
+ .description('Start the long-poll loop (foreground). Ctrl+C / SIGTERM to stop.')
425
+ .option('--token <tok>', 'Override bot token (else DW_TG_BOT_TOKEN env or connectors.yml)')
426
+ .option('--base-url <url>', 'Override Telegram API base URL (test/mock only)')
427
+ .option('--once', 'Single getUpdates poll then exit (for diagnostics + tests)')
428
+ .action(async (opts) => {
429
+ const { connectorTelegramStartCommand } = await import('./commands/connector.mjs');
430
+ await connectorTelegramStartCommand(opts);
431
+ });
432
+
433
+ telegramCmd
434
+ .command('setup')
435
+ .description('Interactive wizard: prompts token, validates, auto-discovers your user_id, writes connectors.local.yml (gitignored)')
436
+ .option('--token <tok>', 'Pre-provide token (skips the password prompt)')
437
+ .option('--base-url <url>', 'Override Telegram API base URL (test/mock only)')
438
+ .action(async (opts) => {
439
+ const { connectorTelegramSetupCommand } = await import('./commands/connector.mjs');
440
+ await connectorTelegramSetupCommand(opts);
441
+ });
442
+
443
+ telegramCmd
444
+ .command('check')
445
+ .description('Sanity-check config + reach Telegram API (calls getMe)')
446
+ .option('--token <tok>', 'Override bot token')
447
+ .option('--base-url <url>', 'Override Telegram API base URL')
448
+ .action(async (opts) => {
449
+ const { connectorTelegramCheckCommand } = await import('./commands/connector.mjs');
450
+ await connectorTelegramCheckCommand(opts);
451
+ });
452
+
453
+ sessionCmd
454
+ .command('prune')
455
+ .description('Remove old terminated sessions (default: >=7d ended; never prunes running)')
456
+ .option('--older-than <dur>', 'Age cutoff like 7d / 24h / 90m / 30s', '7d')
457
+ .option('--status <list>', 'Comma-separated terminal statuses to consider (default: completed,failed,stopped,exited)', '')
458
+ .option('--dry-run', 'Show what would be removed without deleting')
459
+ .action(async (opts) => {
460
+ const { sessionPruneCommand } = await import('./commands/session.mjs');
461
+ await sessionPruneCommand(opts);
462
+ });
463
+
296
464
  const goalCmd = program
297
465
  .command('goal')
298
466
  .description('Goals Management Layer (ADR-0010): strategic layer above tasks. new · show · link · summary · portfolio · lint · bump · delete · view');