ai-development-protocol 1.0.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/dist/cli.js ADDED
@@ -0,0 +1,1163 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import path9 from "path";
5
+
6
+ // src/core/init.ts
7
+ import fs2 from "fs";
8
+ import path3 from "path";
9
+
10
+ // src/core/constants.ts
11
+ import path from "path";
12
+ var AI_DIR = ".ai";
13
+ var CONTEXT_DIR = path.join(AI_DIR, "context");
14
+ var DECISIONS_DIR = path.join(AI_DIR, "decisions");
15
+ var TASKS_DIR = path.join(AI_DIR, "tasks");
16
+ var REPORTS_DIR = path.join(AI_DIR, "reports");
17
+ var TASKS_SUBDIRS = ["planned", "active", "completed"];
18
+ var REPORTS_SUBDIRS = ["reviews", "security", "testing"];
19
+ var REQUIRED_CONTEXT_FILES = [
20
+ "project.md",
21
+ "requirements.md",
22
+ "architecture.md",
23
+ "development.md",
24
+ "operations.md"
25
+ ];
26
+
27
+ // src/utils/fs.ts
28
+ import fs from "fs";
29
+ import path2 from "path";
30
+ function findProjectRoot(startDir = process.cwd()) {
31
+ let current = path2.resolve(startDir);
32
+ while (true) {
33
+ if (fs.existsSync(path2.join(current, ".ai"))) {
34
+ return current;
35
+ }
36
+ if (fs.existsSync(path2.join(current, "agent.md"))) {
37
+ return current;
38
+ }
39
+ if (fs.existsSync(path2.join(current, ".git"))) {
40
+ return current;
41
+ }
42
+ const parent = path2.dirname(current);
43
+ if (parent === current) {
44
+ return path2.resolve(startDir);
45
+ }
46
+ current = parent;
47
+ }
48
+ }
49
+ function ensureDir(dirPath) {
50
+ if (!fs.existsSync(dirPath)) {
51
+ fs.mkdirSync(dirPath, { recursive: true });
52
+ }
53
+ }
54
+ function slugify(text) {
55
+ return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
56
+ }
57
+ function writeFileAtomic(filePath, content) {
58
+ ensureDir(path2.dirname(filePath));
59
+ fs.writeFileSync(filePath, content, "utf-8");
60
+ }
61
+
62
+ // src/templates/readme.ts
63
+ function generateReadme(projectName = "Project") {
64
+ return `# AI Project Knowledge Base (${projectName})
65
+
66
+ This directory maintains the living engineering knowledge for **${projectName}**, following the **AI Project Development Protocol (v1.0.0)**.
67
+
68
+ ## How This Works
69
+
70
+ 1. **Conversation is the Primary Interface**: The project owner interacts through natural dialogue.
71
+ 2. **AI-Maintained**: The AI agent creates, updates, and synchronizes these artifacts progressively as decisions are confirmed.
72
+ 3. **Living Knowledge**: Designed to be accumulative, preserving context, architectural decisions, and tasks without documentation drift.
73
+
74
+ ## Directory Structure
75
+
76
+ \`\`\`text
77
+ .ai/
78
+ \u251C\u2500\u2500 README.md # This guide
79
+ \u251C\u2500\u2500 context/ # Authoritative system context
80
+ \u2502 \u251C\u2500\u2500 project.md # Identity, scope, state, and goals
81
+ \u2502 \u251C\u2500\u2500 requirements.md # Requirements, assumptions, and open questions
82
+ \u2502 \u251C\u2500\u2500 architecture.md # System structure, components, boundaries
83
+ \u2502 \u251C\u2500\u2500 development.md # Conventions, workflows, testing rules
84
+ \u2502 \u2514\u2500\u2500 operations.md # Deployment, environments, runbooks
85
+ \u251C\u2500\u2500 decisions/ # Architecture Decision Records (ADRs)
86
+ \u251C\u2500\u2500 tasks/ # Actionable work breakdown
87
+ \u2502 \u251C\u2500\u2500 planned/ # Ready to be started
88
+ \u2502 \u251C\u2500\u2500 active/ # Currently in progress
89
+ \u2502 \u2514\u2500\u2500 completed/ # Done and validated
90
+ \u2514\u2500\u2500 reports/ # Periodic evaluation records
91
+ \u251C\u2500\u2500 reviews/ # Code and design reviews
92
+ \u251C\u2500\u2500 security/ # Security audits
93
+ \u2514\u2500\u2500 testing/ # Test summaries
94
+ \`\`\`
95
+
96
+ ## Protocol Reference
97
+
98
+ See \`agent.md\` in the project root for the complete specification.
99
+ `;
100
+ }
101
+
102
+ // src/templates/project.ts
103
+ function generateProjectContext(meta = {}) {
104
+ const name = meta.name || "My Project";
105
+ const purpose = meta.purpose || "To be defined through discovery.";
106
+ const problem = meta.problem || "To be identified through conversation.";
107
+ const targetUsers = meta.targetUsers || "To be determined.";
108
+ const valueProposition = meta.valueProposition || "To be articulated.";
109
+ return `# Project Identity & State
110
+
111
+ ## 1. Project Identity
112
+
113
+ * **Project Name:** ${name}
114
+ * **Purpose:** ${purpose}
115
+ * **Problem:** ${problem}
116
+ * **Target Users:** ${targetUsers}
117
+ * **Value Proposition:** ${valueProposition}
118
+
119
+ ---
120
+
121
+ ## 2. Product Scope
122
+
123
+ ### Current Goals
124
+ - [ ] Initialize project foundation and requirements discovery.
125
+
126
+ ### MVP Scope
127
+ *To be defined.*
128
+
129
+ ### Future Possibilities
130
+ *To be explored.*
131
+
132
+ ### Out of Scope
133
+ *To be established.*
134
+
135
+ ---
136
+
137
+ ## 3. Project State
138
+
139
+ * **Current Goal:** Establish problem and user requirements.
140
+ * **Current Phase:** Level 1 \u2014 DISCOVERY
141
+ * **Confirmed Decisions:** Initial protocol structure established.
142
+ * **Active Work:** Initial discovery.
143
+ * **Open Questions:** See \`requirements.md\`.
144
+ * **Deferred Work:** None yet.
145
+ * **Known Constraints:** None yet.
146
+ * **Known Risks:** None identified.
147
+ `;
148
+ }
149
+
150
+ // src/templates/requirements.ts
151
+ function generateRequirementsContext() {
152
+ return `# Requirements & Discovery
153
+
154
+ ## 1. Functional Requirements
155
+
156
+ *Each requirement should preserve rationale (Reason) and confirmation status.*
157
+
158
+ *No confirmed functional requirements yet.*
159
+
160
+ ---
161
+
162
+ ## 2. Non-Functional Requirements
163
+
164
+ * **Performance:** Standard expectations.
165
+ * **Security:** Secure by default.
166
+ * **Maintainability:** Modular and clean code standards.
167
+
168
+ ---
169
+
170
+ ## 3. Constraints & Assumptions
171
+
172
+ ### Constraints
173
+ * None established.
174
+
175
+ ### Assumptions
176
+ * None established.
177
+
178
+ ---
179
+
180
+ ## 4. Open Questions
181
+
182
+ <!-- Questions being explored. When answered, move to confirmed decisions and remove from here. -->
183
+
184
+ - What is the primary problem this project solves?
185
+ - Who is the initial target audience?
186
+ - What are the absolute minimum features required for the MVP?
187
+ `;
188
+ }
189
+
190
+ // src/templates/architecture.ts
191
+ function generateArchitectureContext() {
192
+ return `# Architecture & System Design
193
+
194
+ ## 1. System Overview
195
+
196
+ *A high-level summary of the architecture once established.*
197
+
198
+ ---
199
+
200
+ ## 2. Core Components & Boundaries
201
+
202
+ *To be defined.*
203
+
204
+ ---
205
+
206
+ ## 3. Technology Stack
207
+
208
+ * **Runtime / Language:** To be determined.
209
+ * **Storage / Database:** To be determined.
210
+ * **Frameworks / Libraries:** To be determined.
211
+
212
+ ---
213
+
214
+ ## 4. Architectural Decisions Index
215
+
216
+ *See \`.ai/decisions/\` for detailed ADRs.*
217
+
218
+ | ID | Title | Status | Date |
219
+ |---|---|---|---|
220
+ | - | No ADRs created yet | - | - |
221
+ `;
222
+ }
223
+
224
+ // src/templates/development.ts
225
+ function generateDevelopmentContext() {
226
+ return `# Development Workflow & Conventions
227
+
228
+ ## 1. Environment & Setup
229
+
230
+ *Prerequisites and commands to get started.*
231
+
232
+ ---
233
+
234
+ ## 2. Coding Conventions
235
+
236
+ * Code style, linting, formatting rules, and naming conventions.
237
+
238
+ ---
239
+
240
+ ## 3. Implementation Workflow
241
+
242
+ Before implementing significant features:
243
+ 1. Understand the requirement.
244
+ 2. Inspect existing implementation.
245
+ 3. Identify affected components and boundaries.
246
+ 4. Check relevant architecture and ADRs.
247
+ 5. Identify dependencies and risks.
248
+ 6. Produce a concise implementation approach.
249
+ 7. Implement cleanly.
250
+ 8. Validate (tests, build, type checks).
251
+
252
+ ---
253
+
254
+ ## 4. Testing & Validation Strategy
255
+
256
+ * Test commands, coverage expectations, and verification steps.
257
+ `;
258
+ }
259
+
260
+ // src/templates/operations.ts
261
+ function generateOperationsContext() {
262
+ return `# Operations & Deployment
263
+
264
+ ## 1. Deployment Strategy
265
+
266
+ *Target hosting, CI/CD pipelines, containerization, and release process.*
267
+
268
+ ---
269
+
270
+ ## 2. Environment Configuration
271
+
272
+ *Required environment variables and secret management.*
273
+
274
+ ---
275
+
276
+ ## 3. Observability & Monitoring
277
+
278
+ *Logging standards, health checks, metrics, and error alerting.*
279
+ `;
280
+ }
281
+
282
+ // src/templates/agentBridge.ts
283
+ function generateAgentBridge(projectName = "Project") {
284
+ return `# AI Coding Assistant Instructions
285
+
286
+ You are the AI engineering partner for **${projectName}**, operating under the **AI Project Development Protocol** specified in \`agent.md\`.
287
+
288
+ ---
289
+
290
+ ## \u{1F680} Session Initialization (Every Fresh Session)
291
+
292
+ Whenever a new conversation session starts:
293
+
294
+ 1. **Check Project State First**:
295
+ - Inspect \`.ai/context/project.md\` and \`.ai/context/requirements.md\` (or run \`npx aidp status\`).
296
+ - Identify: Current Phase, Current Goal, Active Tasks, and Open Questions.
297
+
298
+ 2. **If This Is a Brand New / Greenfield Project (Level 0 or Level 1)**:
299
+ - Proactively greet the project owner:
300
+ > *"Tell me what you're thinking about. It doesn't need to be fully defined. We'll progressively turn the idea into a product, requirements, architecture, and implementation plan."*
301
+ - Guide the owner through discovery: problem, target users, core workflow, and MVP boundary.
302
+ - Do NOT start generating large boilerplate code until the core idea and architecture are confirmed.
303
+
304
+ 3. **If the Project Is Already Underway (Level 2+)**:
305
+ - Briefly summarize where the project currently stands based on \`.ai/context/project.md\` and active tasks.
306
+ - Ask how the owner would like to proceed with the active or planned tasks.
307
+
308
+ ---
309
+
310
+ ## \u{1F4CB} Core Operating Rules (from \`agent.md\`)
311
+
312
+ * **Conversation is the Primary Interface**: The project owner interacts through chat; you (the AI) maintain the \`.ai/\` artifacts automatically.
313
+ * **Brainstorming vs. Decisions**: Treat exploratory ideas as proposals. Only record ADRs when the owner explicitly confirms a technical choice.
314
+ * **Preserve Reasoning**: Every architectural choice must be recorded as an ADR with Problem, Options, Decision, and Consequences.
315
+ * **Implementation Boundary**: Do not write production code until the owner signals clear implementation intent (e.g. *"Build it"*, *"Let's implement this"*).
316
+ * **Validation Truth**: Always run real tests and build commands in the terminal before marking any task as complete. Never claim validation that was not run.
317
+ `;
318
+ }
319
+
320
+ // src/core/init.ts
321
+ import { fileURLToPath } from "url";
322
+ function initProtocol(options = {}) {
323
+ const root = path3.resolve(options.targetDir || process.cwd());
324
+ const aiPath = path3.join(root, AI_DIR);
325
+ const isAlreadyInit = fs2.existsSync(aiPath);
326
+ const createdFiles = [];
327
+ const skippedFiles = [];
328
+ ensureDir(aiPath);
329
+ ensureDir(path3.join(root, CONTEXT_DIR));
330
+ ensureDir(path3.join(root, DECISIONS_DIR));
331
+ for (const sub of TASKS_SUBDIRS) {
332
+ ensureDir(path3.join(root, TASKS_DIR, sub));
333
+ }
334
+ for (const sub of REPORTS_SUBDIRS) {
335
+ ensureDir(path3.join(root, REPORTS_DIR, sub));
336
+ }
337
+ const projectName = options.projectName || path3.basename(root);
338
+ const bridgeContent = generateAgentBridge(projectName);
339
+ const files = {
340
+ [path3.join(root, AI_DIR, "README.md")]: generateReadme(projectName),
341
+ [path3.join(root, CONTEXT_DIR, "project.md")]: generateProjectContext({
342
+ name: projectName,
343
+ ...options.meta
344
+ }),
345
+ [path3.join(root, CONTEXT_DIR, "requirements.md")]: generateRequirementsContext(),
346
+ [path3.join(root, CONTEXT_DIR, "architecture.md")]: generateArchitectureContext(),
347
+ [path3.join(root, CONTEXT_DIR, "development.md")]: generateDevelopmentContext(),
348
+ [path3.join(root, CONTEXT_DIR, "operations.md")]: generateOperationsContext()
349
+ };
350
+ if (options.includeBridges !== false) {
351
+ files[path3.join(root, "AGENTS.md")] = bridgeContent;
352
+ files[path3.join(root, "CLAUDE.md")] = bridgeContent;
353
+ files[path3.join(root, ".cursorrules")] = bridgeContent;
354
+ files[path3.join(root, ".github", "copilot-instructions.md")] = bridgeContent;
355
+ const rootAgentMd = path3.join(root, "agent.md");
356
+ if (!fs2.existsSync(rootAgentMd) || options.force) {
357
+ try {
358
+ const sourceAgentMd = fileURLToPath(new URL("../../agent.md", import.meta.url));
359
+ if (fs2.existsSync(sourceAgentMd)) {
360
+ files[rootAgentMd] = fs2.readFileSync(sourceAgentMd, "utf-8");
361
+ }
362
+ } catch {
363
+ }
364
+ }
365
+ }
366
+ for (const [filePath, content] of Object.entries(files)) {
367
+ if (fs2.existsSync(filePath) && !options.force) {
368
+ skippedFiles.push(path3.relative(root, filePath));
369
+ } else {
370
+ writeFileAtomic(filePath, content);
371
+ createdFiles.push(path3.relative(root, filePath));
372
+ }
373
+ }
374
+ const emptyDirs = [
375
+ path3.join(root, DECISIONS_DIR),
376
+ ...TASKS_SUBDIRS.map((s) => path3.join(root, TASKS_DIR, s)),
377
+ ...REPORTS_SUBDIRS.map((s) => path3.join(root, REPORTS_DIR, s))
378
+ ];
379
+ for (const dir of emptyDirs) {
380
+ const keepPath = path3.join(dir, ".gitkeep");
381
+ if (!fs2.existsSync(keepPath) && fs2.readdirSync(dir).length === 0) {
382
+ writeFileAtomic(keepPath, "");
383
+ }
384
+ }
385
+ return {
386
+ aiPath,
387
+ createdFiles,
388
+ skippedFiles,
389
+ alreadyInitialized: isAlreadyInit && !options.force
390
+ };
391
+ }
392
+
393
+ // src/core/status.ts
394
+ import fs6 from "fs";
395
+ import path7 from "path";
396
+
397
+ // src/core/adr.ts
398
+ import fs3 from "fs";
399
+ import path4 from "path";
400
+
401
+ // src/templates/adr.ts
402
+ function generateAdr(options) {
403
+ const padId = String(options.id).padStart(4, "0");
404
+ const dateStr = options.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
405
+ const status = options.status || "Accepted";
406
+ const problem = options.problem || "Describe the architectural context and problem here.";
407
+ const optionsList = options.options && options.options.length > 0 ? options.options.map((opt, i) => `${i + 1}. **${opt}**`).join("\n") : "1. **Option 1:** Description\n2. **Option 2:** Description";
408
+ const decision = options.decision || "Describe the decision made and rationale.";
409
+ const consequences = options.consequences || "Describe positive and negative implications.";
410
+ return `# ADR-${padId}: ${options.title}
411
+
412
+ * **Status:** ${status}
413
+ * **Date:** ${dateStr}
414
+ * **Deciders:** AI & Project Owner
415
+
416
+ ## 1. Problem
417
+ ${problem}
418
+
419
+ ## 2. Options Considered
420
+ ${optionsList}
421
+
422
+ ## 3. Trade-offs
423
+ *Pros and cons evaluated during discussion.*
424
+
425
+ ## 4. Decision
426
+ ${decision}
427
+
428
+ ## 5. Consequences
429
+ ${consequences}
430
+ `;
431
+ }
432
+
433
+ // src/core/adr.ts
434
+ function listAdrs(projectRoot = findProjectRoot()) {
435
+ const decisionsPath = path4.join(projectRoot, DECISIONS_DIR);
436
+ if (!fs3.existsSync(decisionsPath)) {
437
+ return [];
438
+ }
439
+ const files = fs3.readdirSync(decisionsPath).filter((f) => f.endsWith(".md") && /^\d{4}-/.test(f)).sort();
440
+ const records = [];
441
+ for (const filename of files) {
442
+ const filePath = path4.join(decisionsPath, filename);
443
+ const content = fs3.readFileSync(filePath, "utf-8");
444
+ const idMatch = filename.match(/^(\d{4})/);
445
+ const id = idMatch ? parseInt(idMatch[1], 10) : 0;
446
+ const formattedId = idMatch ? idMatch[1] : "0000";
447
+ const titleMatch = content.match(/^#\s+ADR-\d+:\s*(.+)$/m);
448
+ const title2 = titleMatch ? titleMatch[1].trim() : filename.replace(/^\d{4}-|\.md$/g, "");
449
+ const statusMatch = content.match(/\*\s*\*\*Status:\*\*\s*(.+)$/m);
450
+ const status = statusMatch ? statusMatch[1].trim() : "Unknown";
451
+ const dateMatch = content.match(/\*\s*\*\*Date:\*\*\s*(.+)$/m);
452
+ const date = dateMatch ? dateMatch[1].trim() : "";
453
+ records.push({
454
+ id,
455
+ formattedId,
456
+ title: title2,
457
+ status,
458
+ date,
459
+ filename,
460
+ filePath
461
+ });
462
+ }
463
+ return records;
464
+ }
465
+ function getNextAdrId(projectRoot = findProjectRoot()) {
466
+ const adrs = listAdrs(projectRoot);
467
+ if (adrs.length === 0) return 1;
468
+ const maxId = Math.max(...adrs.map((a) => a.id));
469
+ return maxId + 1;
470
+ }
471
+ function createAdr(options) {
472
+ const root = options.projectRoot || findProjectRoot();
473
+ const nextId = getNextAdrId(root);
474
+ const formattedId = String(nextId).padStart(4, "0");
475
+ const slug = slugify(options.title);
476
+ const filename = `${formattedId}-${slug}.md`;
477
+ const decisionsPath = path4.join(root, DECISIONS_DIR);
478
+ ensureDir(decisionsPath);
479
+ const filePath = path4.join(decisionsPath, filename);
480
+ const content = generateAdr({
481
+ ...options,
482
+ id: nextId
483
+ });
484
+ writeFileAtomic(filePath, content);
485
+ syncAdrWithArchitecture(root, {
486
+ formattedId,
487
+ title: options.title,
488
+ status: options.status || "Accepted",
489
+ date: options.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
490
+ filename
491
+ });
492
+ return {
493
+ id: nextId,
494
+ formattedId,
495
+ title: options.title,
496
+ status: options.status || "Accepted",
497
+ date: options.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
498
+ filename,
499
+ filePath
500
+ };
501
+ }
502
+ function syncAdrWithArchitecture(projectRoot, adr) {
503
+ const archFile = path4.join(projectRoot, CONTEXT_DIR, "architecture.md");
504
+ if (!fs3.existsSync(archFile)) return;
505
+ let content = fs3.readFileSync(archFile, "utf-8");
506
+ const tableHeaderRegex = /(\| ID \| Title \| Status \| Date \|\r?\n\|---\|---\|---\|---\|\r?\n)([\s\S]*?)(?=\r?\n#|$)/;
507
+ const row = `| [ADR-${adr.formattedId}](../decisions/${adr.filename}) | ${adr.title} | ${adr.status} | ${adr.date} |`;
508
+ if (tableHeaderRegex.test(content)) {
509
+ content = content.replace(tableHeaderRegex, (match, header, body) => {
510
+ let rows = body.split(/\r?\n/).map((r) => r.trim()).filter((r) => r.length > 0 && !r.includes("No ADRs created yet"));
511
+ rows.push(row);
512
+ return `${header}${rows.join("\n")}
513
+ `;
514
+ });
515
+ writeFileAtomic(archFile, content);
516
+ }
517
+ }
518
+
519
+ // src/core/task.ts
520
+ import fs4 from "fs";
521
+ import path5 from "path";
522
+
523
+ // src/templates/task.ts
524
+ function generateTask(options) {
525
+ const status = options.status || "planned";
526
+ const createdDate = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
527
+ const goal = options.goal || "Describe the objective of this task.";
528
+ const reqRef = options.requirementsRef || "Refer to requirements.md";
529
+ const archRef = options.architectureRef || "Refer to architecture.md";
530
+ const stepsList = options.steps && options.steps.length > 0 ? options.steps.map((s) => `- [ ] ${s}`).join("\n") : "- [ ] Step 1: Design and setup\n- [ ] Step 2: Implementation\n- [ ] Step 3: Tests";
531
+ const validationList = options.validation && options.validation.length > 0 ? options.validation.map((v) => `- [ ] ${v}`).join("\n") : "- [ ] Unit and integration tests pass\n- [ ] Working behavior validated";
532
+ return `# ${options.id.toUpperCase()}: ${options.title}
533
+
534
+ * **Status:** ${status.toUpperCase()}
535
+ * **Created:** ${createdDate}
536
+ * **Category:** Implementation
537
+
538
+ ## Goal
539
+ ${goal}
540
+
541
+ ## Requirements Reference
542
+ ${reqRef}
543
+
544
+ ## Architecture Reference
545
+ ${archRef}
546
+
547
+ ## Implementation Steps
548
+ ${stepsList}
549
+
550
+ ## Validation Criteria
551
+ ${validationList}
552
+ `;
553
+ }
554
+
555
+ // src/core/task.ts
556
+ function listTasks(projectRoot = findProjectRoot()) {
557
+ const result = {
558
+ planned: [],
559
+ active: [],
560
+ completed: []
561
+ };
562
+ const tasksBase = path5.join(projectRoot, TASKS_DIR);
563
+ if (!fs4.existsSync(tasksBase)) {
564
+ return result;
565
+ }
566
+ for (const status of TASKS_SUBDIRS) {
567
+ const dir = path5.join(tasksBase, status);
568
+ if (!fs4.existsSync(dir)) continue;
569
+ const files = fs4.readdirSync(dir).filter((f) => f.endsWith(".md"));
570
+ for (const filename of files) {
571
+ const filePath = path5.join(dir, filename);
572
+ const content = fs4.readFileSync(filePath, "utf-8");
573
+ const titleMatch = content.match(/^#\s+([^:]+):\s*(.+)$/m);
574
+ const id = titleMatch ? titleMatch[1].trim() : filename.replace(/\.md$/, "");
575
+ const title2 = titleMatch ? titleMatch[2].trim() : filename.replace(/\.md$/, "");
576
+ result[status].push({
577
+ id,
578
+ title: title2,
579
+ status,
580
+ filename,
581
+ filePath
582
+ });
583
+ }
584
+ }
585
+ return result;
586
+ }
587
+ function getNextTaskId(projectRoot = findProjectRoot()) {
588
+ const all = listTasks(projectRoot);
589
+ const allTasks = [...all.planned, ...all.active, ...all.completed];
590
+ let maxNum = 0;
591
+ for (const t of allTasks) {
592
+ const m = t.filename.match(/task-(\d+)/i) || t.id.match(/task-(\d+)/i);
593
+ if (m) {
594
+ const num = parseInt(m[1], 10);
595
+ if (num > maxNum) maxNum = num;
596
+ }
597
+ }
598
+ return `TASK-${String(maxNum + 1).padStart(4, "0")}`;
599
+ }
600
+ function createTask(options) {
601
+ const root = options.projectRoot || findProjectRoot();
602
+ const id = options.id || getNextTaskId(root);
603
+ const status = options.status || "planned";
604
+ const slug = slugify(options.title);
605
+ const filename = `${id.toLowerCase()}-${slug}.md`;
606
+ const destDir = path5.join(root, TASKS_DIR, status);
607
+ ensureDir(destDir);
608
+ const filePath = path5.join(destDir, filename);
609
+ const content = generateTask({
610
+ ...options,
611
+ id,
612
+ status
613
+ });
614
+ writeFileAtomic(filePath, content);
615
+ return {
616
+ id,
617
+ title: options.title,
618
+ status,
619
+ filename,
620
+ filePath
621
+ };
622
+ }
623
+ function moveTask(taskIdOrName, newStatus, projectRoot = findProjectRoot()) {
624
+ const all = listTasks(projectRoot);
625
+ let found = null;
626
+ const search = taskIdOrName.toLowerCase();
627
+ for (const status of TASKS_SUBDIRS) {
628
+ const match = all[status].find(
629
+ (t) => t.id.toLowerCase() === search || t.filename.toLowerCase().includes(search)
630
+ );
631
+ if (match) {
632
+ found = match;
633
+ break;
634
+ }
635
+ }
636
+ if (!found) {
637
+ throw new Error(`Task matching "${taskIdOrName}" was not found.`);
638
+ }
639
+ if (found.status === newStatus) {
640
+ return found;
641
+ }
642
+ const oldPath = found.filePath;
643
+ const newDir = path5.join(projectRoot, TASKS_DIR, newStatus);
644
+ ensureDir(newDir);
645
+ const newPath = path5.join(newDir, found.filename);
646
+ let content = fs4.readFileSync(oldPath, "utf-8");
647
+ content = content.replace(/\*\s*\*\*Status:\*\*\s*(.+)$/m, `* **Status:** ${newStatus.toUpperCase()}`);
648
+ fs4.unlinkSync(oldPath);
649
+ writeFileAtomic(newPath, content);
650
+ return {
651
+ ...found,
652
+ status: newStatus,
653
+ filePath: newPath
654
+ };
655
+ }
656
+
657
+ // src/core/question.ts
658
+ import fs5 from "fs";
659
+ import path6 from "path";
660
+ function listOpenQuestions(projectRoot = findProjectRoot()) {
661
+ const reqPath = path6.join(projectRoot, CONTEXT_DIR, "requirements.md");
662
+ if (!fs5.existsSync(reqPath)) return [];
663
+ const content = fs5.readFileSync(reqPath, "utf-8");
664
+ const sectionMatch = content.match(/## 4\. Open Questions([\s\S]*?)(?=\r?\n##|$)/);
665
+ if (!sectionMatch) return [];
666
+ const lines = sectionMatch[1].split(/\r?\n/);
667
+ const questions = [];
668
+ for (const line of lines) {
669
+ const trimmed = line.trim();
670
+ if (trimmed.startsWith("- ") || trimmed.startsWith("* ")) {
671
+ questions.push(trimmed.slice(2).trim());
672
+ }
673
+ }
674
+ return questions;
675
+ }
676
+ function addOpenQuestion(question, projectRoot = findProjectRoot()) {
677
+ const reqPath = path6.join(projectRoot, CONTEXT_DIR, "requirements.md");
678
+ if (!fs5.existsSync(reqPath)) {
679
+ throw new Error(`requirements.md not found at ${reqPath}`);
680
+ }
681
+ let content = fs5.readFileSync(reqPath, "utf-8");
682
+ const sectionHeader = "## 4. Open Questions";
683
+ const idx = content.indexOf(sectionHeader);
684
+ if (idx === -1) {
685
+ content += `
686
+
687
+ ${sectionHeader}
688
+
689
+ - ${question}
690
+ `;
691
+ } else {
692
+ content = content.replace(/(## 4\. Open Questions[\s\S]*?)(\r?\n\r?\n##|\r?\n?$)/, (match, prefix, suffix) => {
693
+ return `${prefix.trimEnd()}
694
+ - ${question}
695
+ ${suffix}`;
696
+ });
697
+ }
698
+ writeFileAtomic(reqPath, content);
699
+ }
700
+
701
+ // src/core/status.ts
702
+ function getProjectStatus(projectRoot = findProjectRoot()) {
703
+ const projectFile = path7.join(projectRoot, CONTEXT_DIR, "project.md");
704
+ const isInitialized = fs6.existsSync(projectFile);
705
+ if (!isInitialized) {
706
+ return {
707
+ isInitialized: false,
708
+ name: path7.basename(projectRoot),
709
+ currentGoal: "Not initialized",
710
+ currentPhase: "Unknown",
711
+ adrs: [],
712
+ tasks: { planned: [], active: [], completed: [] },
713
+ openQuestions: []
714
+ };
715
+ }
716
+ const projectContent = fs6.readFileSync(projectFile, "utf-8");
717
+ const nameMatch = projectContent.match(/\*\s*\*\*Project Name:\*\*\s*(.+)$/m);
718
+ const name = nameMatch ? nameMatch[1].trim() : path7.basename(projectRoot);
719
+ const goalMatch = projectContent.match(/\*\s*\*\*Current Goal:\*\*\s*(.+)$/m);
720
+ const currentGoal = goalMatch ? goalMatch[1].trim() : "Unspecified";
721
+ const phaseMatch = projectContent.match(/\*\s*\*\*Current Phase:\*\*\s*(.+)$/m);
722
+ const currentPhase = phaseMatch ? phaseMatch[1].trim() : "Unknown";
723
+ const adrs = listAdrs(projectRoot);
724
+ const tasks = listTasks(projectRoot);
725
+ const openQuestions = listOpenQuestions(projectRoot);
726
+ return {
727
+ isInitialized: true,
728
+ name,
729
+ currentGoal,
730
+ currentPhase,
731
+ adrs,
732
+ tasks,
733
+ openQuestions
734
+ };
735
+ }
736
+
737
+ // src/core/validate.ts
738
+ import fs7 from "fs";
739
+ import path8 from "path";
740
+ function validateProtocol(projectRoot = findProjectRoot()) {
741
+ const errors = [];
742
+ const warnings = [];
743
+ let checkedItems = 0;
744
+ const aiPath = path8.join(projectRoot, AI_DIR);
745
+ checkedItems++;
746
+ if (!fs7.existsSync(aiPath)) {
747
+ return {
748
+ valid: false,
749
+ errors: [`AI protocol directory '${AI_DIR}' does not exist at ${projectRoot}`],
750
+ warnings: [],
751
+ checkedItems
752
+ };
753
+ }
754
+ checkedItems++;
755
+ const readmePath = path8.join(aiPath, "README.md");
756
+ if (!fs7.existsSync(readmePath)) {
757
+ warnings.push(`Missing '${AI_DIR}/README.md' guide.`);
758
+ }
759
+ const contextPath = path8.join(projectRoot, CONTEXT_DIR);
760
+ checkedItems++;
761
+ if (!fs7.existsSync(contextPath)) {
762
+ errors.push(`Missing context directory at '${CONTEXT_DIR}'.`);
763
+ } else {
764
+ for (const file of REQUIRED_CONTEXT_FILES) {
765
+ checkedItems++;
766
+ const filePath = path8.join(contextPath, file);
767
+ if (!fs7.existsSync(filePath)) {
768
+ errors.push(`Missing required context file: '${path8.join(CONTEXT_DIR, file)}'.`);
769
+ }
770
+ }
771
+ }
772
+ const decisionsPath = path8.join(projectRoot, DECISIONS_DIR);
773
+ checkedItems++;
774
+ if (!fs7.existsSync(decisionsPath)) {
775
+ errors.push(`Missing decisions directory at '${DECISIONS_DIR}'.`);
776
+ } else {
777
+ const files = fs7.readdirSync(decisionsPath).filter((f) => f.endsWith(".md"));
778
+ for (const f of files) {
779
+ checkedItems++;
780
+ if (!/^\d{4}-[\w-]+\.md$/.test(f)) {
781
+ warnings.push(`ADR file '${f}' does not follow the naming convention: '0001-slug-title.md'.`);
782
+ }
783
+ }
784
+ }
785
+ const tasksPath = path8.join(projectRoot, TASKS_DIR);
786
+ checkedItems++;
787
+ if (!fs7.existsSync(tasksPath)) {
788
+ errors.push(`Missing tasks directory at '${TASKS_DIR}'.`);
789
+ } else {
790
+ for (const sub of TASKS_SUBDIRS) {
791
+ checkedItems++;
792
+ const subPath = path8.join(tasksPath, sub);
793
+ if (!fs7.existsSync(subPath)) {
794
+ errors.push(`Missing task subfolder: '${path8.join(TASKS_DIR, sub)}'.`);
795
+ }
796
+ }
797
+ }
798
+ const reportsPath = path8.join(projectRoot, REPORTS_DIR);
799
+ checkedItems++;
800
+ if (!fs7.existsSync(reportsPath)) {
801
+ warnings.push(`Missing reports directory at '${REPORTS_DIR}'.`);
802
+ } else {
803
+ for (const sub of REPORTS_SUBDIRS) {
804
+ checkedItems++;
805
+ const subPath = path8.join(reportsPath, sub);
806
+ if (!fs7.existsSync(subPath)) {
807
+ warnings.push(`Missing report subfolder: '${path8.join(REPORTS_DIR, sub)}'.`);
808
+ }
809
+ }
810
+ }
811
+ return {
812
+ valid: errors.length === 0,
813
+ errors,
814
+ warnings,
815
+ checkedItems
816
+ };
817
+ }
818
+
819
+ // src/utils/format.ts
820
+ var isColorSupported = !process.env.NO_COLOR && (process.stdout?.isTTY ?? false);
821
+ var colors = {
822
+ reset: isColorSupported ? "\x1B[0m" : "",
823
+ bold: isColorSupported ? "\x1B[1m" : "",
824
+ dim: isColorSupported ? "\x1B[2m" : "",
825
+ italic: isColorSupported ? "\x1B[3m" : "",
826
+ underline: isColorSupported ? "\x1B[4m" : "",
827
+ red: isColorSupported ? "\x1B[31m" : "",
828
+ green: isColorSupported ? "\x1B[32m" : "",
829
+ yellow: isColorSupported ? "\x1B[33m" : "",
830
+ blue: isColorSupported ? "\x1B[34m" : "",
831
+ magenta: isColorSupported ? "\x1B[35m" : "",
832
+ cyan: isColorSupported ? "\x1B[36m" : "",
833
+ gray: isColorSupported ? "\x1B[90m" : ""
834
+ };
835
+ function success(msg) {
836
+ return `${colors.green}\u2714${colors.reset} ${msg}`;
837
+ }
838
+ function info(msg) {
839
+ return `${colors.cyan}\u2139${colors.reset} ${msg}`;
840
+ }
841
+ function warn(msg) {
842
+ return `${colors.yellow}\u26A0${colors.reset} ${msg}`;
843
+ }
844
+ function error(msg) {
845
+ return `${colors.red}\u2716${colors.reset} ${msg}`;
846
+ }
847
+ function title(msg) {
848
+ return `${colors.bold}${colors.cyan}${msg}${colors.reset}`;
849
+ }
850
+
851
+ // src/cli.ts
852
+ function runCli(argv) {
853
+ const args = argv.slice(2);
854
+ const command = args[0] || "help";
855
+ switch (command) {
856
+ case "init": {
857
+ handleInit(args.slice(1));
858
+ break;
859
+ }
860
+ case "status": {
861
+ handleStatus(args.slice(1));
862
+ break;
863
+ }
864
+ case "validate": {
865
+ handleValidate(args.slice(1));
866
+ break;
867
+ }
868
+ case "adr": {
869
+ handleAdr(args.slice(1));
870
+ break;
871
+ }
872
+ case "task": {
873
+ handleTask(args.slice(1));
874
+ break;
875
+ }
876
+ case "question": {
877
+ handleQuestion(args.slice(1));
878
+ break;
879
+ }
880
+ case "help":
881
+ case "--help":
882
+ case "-h":
883
+ printHelp();
884
+ break;
885
+ case "version":
886
+ case "--version":
887
+ case "-v":
888
+ console.log("aidp (AI Development Protocol CLI) v1.0.0");
889
+ break;
890
+ default:
891
+ console.error(error(`Unknown command: "${command}"`));
892
+ console.log(info("Run `aidp help` for a list of available commands."));
893
+ process.exit(1);
894
+ }
895
+ }
896
+ function handleInit(args) {
897
+ let targetDir = process.cwd();
898
+ let projectName;
899
+ let force = false;
900
+ for (let i = 0; i < args.length; i++) {
901
+ if (args[i] === "--force" || args[i] === "-f") {
902
+ force = true;
903
+ } else if (args[i] === "--name" || args[i] === "-n") {
904
+ projectName = args[++i];
905
+ } else if (!args[i].startsWith("-")) {
906
+ targetDir = path9.resolve(args[i]);
907
+ }
908
+ }
909
+ console.log(title("Initializing AI Project Development Protocol..."));
910
+ const res = initProtocol({ targetDir, projectName, force });
911
+ if (res.createdFiles.length > 0) {
912
+ console.log(success(`Created ${res.createdFiles.length} file(s) in ${res.aiPath}:`));
913
+ for (const f of res.createdFiles) {
914
+ console.log(` + ${f}`);
915
+ }
916
+ }
917
+ if (res.skippedFiles.length > 0) {
918
+ console.log(warn(`Skipped ${res.skippedFiles.length} existing file(s) (use --force to overwrite):`));
919
+ for (const f of res.skippedFiles) {
920
+ console.log(` ~ ${f}`);
921
+ }
922
+ }
923
+ console.log(success("Initialization complete!"));
924
+ }
925
+ function handleStatus(args) {
926
+ const targetDir = args[0] ? path9.resolve(args[0]) : findProjectRoot();
927
+ const status = getProjectStatus(targetDir);
928
+ if (!status.isInitialized) {
929
+ console.log(warn(`No AI Project Development Protocol found at ${targetDir}`));
930
+ console.log(info("Run `aidp init` to set up the protocol."));
931
+ return;
932
+ }
933
+ console.log(title(`=== ${status.name} ===`));
934
+ console.log(`${colors.bold}Current Phase:${colors.reset} ${status.currentPhase}`);
935
+ console.log(`${colors.bold}Current Goal:${colors.reset} ${status.currentGoal}`);
936
+ console.log();
937
+ const totalTasks = status.tasks.planned.length + status.tasks.active.length + status.tasks.completed.length;
938
+ console.log(`${colors.bold}Tasks (${totalTasks}):${colors.reset}`);
939
+ console.log(` ${colors.blue}Active (${status.tasks.active.length}):${colors.reset}`);
940
+ for (const t of status.tasks.active) {
941
+ console.log(` * [${t.id}] ${t.title}`);
942
+ }
943
+ console.log(` ${colors.yellow}Planned (${status.tasks.planned.length}):${colors.reset}`);
944
+ for (const t of status.tasks.planned) {
945
+ console.log(` * [${t.id}] ${t.title}`);
946
+ }
947
+ console.log(` ${colors.green}Completed (${status.tasks.completed.length}):${colors.reset}`);
948
+ for (const t of status.tasks.completed) {
949
+ console.log(` * [${t.id}] ${t.title}`);
950
+ }
951
+ console.log();
952
+ console.log(`${colors.bold}Architectural Decisions (${status.adrs.length}):${colors.reset}`);
953
+ if (status.adrs.length === 0) {
954
+ console.log(" (None recorded yet)");
955
+ } else {
956
+ for (const adr of status.adrs) {
957
+ console.log(` * ADR-${adr.formattedId}: ${adr.title} [${adr.status}] (${adr.date})`);
958
+ }
959
+ }
960
+ console.log();
961
+ console.log(`${colors.bold}Open Questions (${status.openQuestions.length}):${colors.reset}`);
962
+ if (status.openQuestions.length === 0) {
963
+ console.log(" (No open questions)");
964
+ } else {
965
+ for (const q of status.openQuestions) {
966
+ console.log(` ? ${q}`);
967
+ }
968
+ }
969
+ }
970
+ function handleValidate(args) {
971
+ const targetDir = args[0] ? path9.resolve(args[0]) : findProjectRoot();
972
+ console.log(title("Validating AI Project Development Protocol structure..."));
973
+ const report = validateProtocol(targetDir);
974
+ if (report.warnings.length > 0) {
975
+ console.log(warn(`Warnings (${report.warnings.length}):`));
976
+ for (const w of report.warnings) {
977
+ console.log(` \u26A0 ${w}`);
978
+ }
979
+ }
980
+ if (report.errors.length > 0) {
981
+ console.log(error(`Errors (${report.errors.length}):`));
982
+ for (const e of report.errors) {
983
+ console.log(` \u2716 ${e}`);
984
+ }
985
+ console.log(error(`Validation FAILED with ${report.errors.length} error(s).`));
986
+ process.exit(1);
987
+ } else {
988
+ console.log(success(`Validation PASSED! Checked ${report.checkedItems} items.`));
989
+ }
990
+ }
991
+ function handleAdr(args) {
992
+ const sub = args[0];
993
+ if (sub === "list") {
994
+ const root = findProjectRoot();
995
+ const adrs = listAdrs(root);
996
+ console.log(title(`Architecture Decision Records (${adrs.length}):`));
997
+ if (adrs.length === 0) {
998
+ console.log(info("No ADRs found. Create one with `aidp adr new <title>`."));
999
+ return;
1000
+ }
1001
+ for (const adr of adrs) {
1002
+ console.log(` ADR-${adr.formattedId} [${adr.status}] - ${adr.title} (${adr.date})`);
1003
+ }
1004
+ return;
1005
+ }
1006
+ if (sub === "new") {
1007
+ const titleParts = [];
1008
+ let status = "Accepted";
1009
+ for (let i = 1; i < args.length; i++) {
1010
+ if (args[i] === "--status" && args[i + 1]) {
1011
+ status = args[++i];
1012
+ } else {
1013
+ titleParts.push(args[i]);
1014
+ }
1015
+ }
1016
+ const titleStr = titleParts.join(" ").trim();
1017
+ if (!titleStr) {
1018
+ console.error(error("Please provide a title for the ADR: `aidp adr new <title>`"));
1019
+ process.exit(1);
1020
+ }
1021
+ const record = createAdr({ title: titleStr, status });
1022
+ console.log(success(`Created ADR-${record.formattedId}: ${record.title}`));
1023
+ console.log(info(`File saved to: ${record.filePath}`));
1024
+ return;
1025
+ }
1026
+ console.error(error(`Unknown adr subcommand: "${sub}"`));
1027
+ console.log(info("Available subcommands: `aidp adr list`, `aidp adr new <title>`"));
1028
+ process.exit(1);
1029
+ }
1030
+ function handleTask(args) {
1031
+ const sub = args[0];
1032
+ if (sub === "list") {
1033
+ const root = findProjectRoot();
1034
+ const tasks = listTasks(root);
1035
+ console.log(title("Project Tasks:"));
1036
+ for (const s of ["active", "planned", "completed"]) {
1037
+ console.log(`
1038
+ ${colors.bold}${s.toUpperCase()} (${tasks[s].length}):${colors.reset}`);
1039
+ if (tasks[s].length === 0) {
1040
+ console.log(" (None)");
1041
+ } else {
1042
+ for (const t of tasks[s]) {
1043
+ console.log(` * [${t.id}] ${t.title}`);
1044
+ }
1045
+ }
1046
+ }
1047
+ return;
1048
+ }
1049
+ if (sub === "new") {
1050
+ const titleParts = [];
1051
+ let status = "planned";
1052
+ let goal;
1053
+ for (let i = 1; i < args.length; i++) {
1054
+ if (args[i] === "--status" && args[i + 1]) {
1055
+ status = args[++i];
1056
+ } else if (args[i] === "--active") {
1057
+ status = "active";
1058
+ } else if (args[i] === "--goal" && args[i + 1]) {
1059
+ goal = args[++i];
1060
+ } else {
1061
+ titleParts.push(args[i]);
1062
+ }
1063
+ }
1064
+ const titleStr = titleParts.join(" ").trim();
1065
+ if (!titleStr) {
1066
+ console.error(error("Please provide a title for the task: `aidp task new <title>`"));
1067
+ process.exit(1);
1068
+ }
1069
+ const task = createTask({ title: titleStr, status, goal });
1070
+ console.log(success(`Created task [${task.id}] in ${task.status}: ${task.title}`));
1071
+ console.log(info(`File saved to: ${task.filePath}`));
1072
+ return;
1073
+ }
1074
+ if (sub === "move") {
1075
+ const taskId = args[1];
1076
+ const targetStatus = args[2];
1077
+ if (!taskId || !targetStatus) {
1078
+ console.error(error("Usage: `aidp task move <taskId> <planned|active|completed>`"));
1079
+ process.exit(1);
1080
+ }
1081
+ if (!["planned", "active", "completed"].includes(targetStatus)) {
1082
+ console.error(error(`Invalid status "${targetStatus}". Must be planned, active, or completed.`));
1083
+ process.exit(1);
1084
+ }
1085
+ try {
1086
+ const moved = moveTask(taskId, targetStatus);
1087
+ console.log(success(`Moved [${moved.id}] "${moved.title}" to ${moved.status}`));
1088
+ } catch (err) {
1089
+ console.error(error(err.message));
1090
+ process.exit(1);
1091
+ }
1092
+ return;
1093
+ }
1094
+ console.error(error(`Unknown task subcommand: "${sub}"`));
1095
+ console.log(info("Available subcommands: `aidp task list`, `aidp task new <title>`, `aidp task move <id> <status>`"));
1096
+ process.exit(1);
1097
+ }
1098
+ function handleQuestion(args) {
1099
+ const sub = args[0];
1100
+ if (sub === "list") {
1101
+ const root = findProjectRoot();
1102
+ const questions = listOpenQuestions(root);
1103
+ console.log(title(`Open Questions (${questions.length}):`));
1104
+ if (questions.length === 0) {
1105
+ console.log(info("No open questions tracked."));
1106
+ return;
1107
+ }
1108
+ for (const q of questions) {
1109
+ console.log(` ? ${q}`);
1110
+ }
1111
+ return;
1112
+ }
1113
+ if (sub === "add") {
1114
+ const qStr = args.slice(1).join(" ").trim();
1115
+ if (!qStr) {
1116
+ console.error(error("Please provide a question: `aidp question add <question>`"));
1117
+ process.exit(1);
1118
+ }
1119
+ addOpenQuestion(qStr);
1120
+ console.log(success(`Added open question to requirements.md: "${qStr}"`));
1121
+ return;
1122
+ }
1123
+ console.error(error(`Unknown question subcommand: "${sub}"`));
1124
+ console.log(info("Available subcommands: `aidp question list`, `aidp question add <question>`"));
1125
+ process.exit(1);
1126
+ }
1127
+ function printHelp() {
1128
+ console.log(`
1129
+ ${colors.bold}${colors.cyan}AI Project Development Protocol (AIDP) CLI${colors.reset}
1130
+ Version 1.0.0 \u2014 Tool-agnostic protocol automation and management
1131
+
1132
+ ${colors.bold}USAGE:${colors.reset}
1133
+ aidp <command> [options]
1134
+
1135
+ ${colors.bold}COMMANDS:${colors.reset}
1136
+ ${colors.green}init [path]${colors.reset} Scaffold the .ai/ protocol directory & context files
1137
+ --name, -n <name> Set project name
1138
+ --force, -f Overwrite existing files
1139
+
1140
+ ${colors.green}status [path]${colors.reset} Show project state, active tasks, ADRs, questions
1141
+ ${colors.green}validate [path]${colors.reset} Verify protocol directory structure & context
1142
+
1143
+ ${colors.green}adr list${colors.reset} List all Architecture Decision Records
1144
+ ${colors.green}adr new <title> [options]${colors.reset} Create a new numbered ADR
1145
+ --status <status> Status (Proposed | Accepted | Rejected | Superseded)
1146
+
1147
+ ${colors.green}task list${colors.reset} List all tasks by lifecycle status
1148
+ ${colors.green}task new <title> [options]${colors.reset} Create a new task specification
1149
+ --active Set status to active (default: planned)
1150
+ --goal <text> Set task goal
1151
+ ${colors.green}task move <id> <status>${colors.reset} Move task between planned | active | completed
1152
+
1153
+ ${colors.green}question list${colors.reset} List open questions from requirements.md
1154
+ ${colors.green}question add <question>${colors.reset} Add a new open question to requirements.md
1155
+
1156
+ ${colors.green}help, --help${colors.reset} Show this help message
1157
+ ${colors.green}version, --version${colors.reset} Display version number
1158
+ `);
1159
+ }
1160
+
1161
+ // bin/cli.ts
1162
+ runCli(process.argv);
1163
+ //# sourceMappingURL=cli.js.map