r2pde-ai 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ This project adheres to [Semantic Versioning](https://semver.org).
5
+
6
+
7
+ ## [0.1.1] - 2026-05-14
8
+
9
+ ### Changed
10
+ - Refactored `wave:prompt` to clarify that the AI API only generates optimized prompts for Copilot, never code.
11
+ - Updated all documentation (README, GUIDE, doctor, CLI help) to explain the new prompt optimization flow and Copilot's role.
12
+
13
+ ### Added
14
+ - `init` command — interactive project initialization with full .r2pde-ai structure
15
+ - `doctor` command — environment health check
16
+ - `manifesto:create` / `manifesto:delete` — manifesto artifact management
17
+ - `contract:create` / `contract:delete` — contract artifact management
18
+ - `requirement:create` / `requirement:delete` — requirement artifact management
19
+ - `score` / `score:config` — quality score engine with traffic light system
20
+ - `wave:prompt` — consolidated AI copilot prompt generation per wave
21
+ - `config:set` / `config:lang` — configuration management
22
+ - `reset` — prompts folder cleanup
23
+ - `logs` / `logs --clear` — audit log management
24
+ - Automated tests with Vitest (14 tests, 100% pass)
25
+ - Enhanced --help with examples and documentation
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 r2pde-ai contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,373 @@
1
+ # r2pde-ai
2
+
3
+ > **Pilot Driven Engineering** — A CLI framework that bridges the gap between architectural intent and AI-generated code.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/r2pde-ai.svg)](https://www.npmjs.com/package/r2pde-ai)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen)](https://nodejs.org)
8
+
9
+ ---
10
+
11
+ ## What is r2pde-ai?
12
+
13
+ `r2pde-ai` is a CLI framework for solo developers working with AI copilots (GitHub Copilot, Claude, etc.) inside VS Code. It structures your project's architectural decisions — manifestos, contracts, and specifications — into versioned markdown artifacts that guide the AI with precision, reducing ambiguity, token waste, and code drift.
14
+
15
+ The result: less programming, more engineering. Less guessing, more intent.
16
+
17
+ ---
18
+
19
+ ## How it works
20
+
21
+ ```
22
+ Developer defines → CLI structures → AI receives context → AI delivers code
23
+ ```
24
+
25
+ 1. Install the package globally
26
+ 2. Run `r2pde-ai init` inside your project folder
27
+ 3. Use CLI commands to generate manifestos, contracts, and specifications
28
+ 4. Use the generated prompts with your AI copilot
29
+ 5. Everything is versioned, logged, and git-tracked
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ npm install -g r2pde-ai
37
+ ```
38
+
39
+ **Requirements**
40
+ - Node.js >= 20.0.0 (LTS)
41
+ - npm >= 10
42
+ - Git initialized in your project
43
+
44
+ ---
45
+
46
+ ## Quick Start
47
+
48
+ ```bash
49
+ # Create and navigate to your project folder
50
+ mkdir my-project && cd my-project
51
+
52
+ # Initialize git
53
+ git init
54
+
55
+ # Initialize r2pde-ai
56
+ r2pde-ai init
57
+
58
+ # Check environment health
59
+ r2pde-ai doctor
60
+
61
+ # Read the user guide to understand artifacts
62
+ # See .r2pde-ai/GUIDE.md
63
+
64
+ # Create your first manifesto
65
+ r2pde-ai manifesto:create
66
+
67
+ # Evaluate quality score
68
+ r2pde-ai score
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Project Structure
74
+
75
+ After `init`, the following structure is created inside your project:
76
+
77
+ ```
78
+ .r2pde-ai/
79
+ ├── pde.index.md # Master index — always paste this into your AI copilot first
80
+ ├── pde.config.json # Central configuration file
81
+ ├── GUIDE.md # Human-readable guide — artifact types, structure, examples
82
+ ├── templates/
83
+ │ ├── manifesto.template.md # Template used by CLI when generating manifestos
84
+ │ ├── contract.template.md # Template used by CLI when generating contracts
85
+ │ └── requirement.template.md # Template used by CLI when generating requirements
86
+ ├── manifestos/ # Guiding principles (UI, UX, code philosophy)
87
+ ├── contracts/ # Rigid rules (architecture, TDD, DDD, security, routes)
88
+ ├── requirements/ # Application specification (goals, flows, domains)
89
+ ├── waves/ # Implementation waves and progress tracking
90
+ ├── prompts/ # Generated prompts for AI copilot (manually deletable)
91
+ └── logs/ # Audit log of all CLI commands executed
92
+ ```
93
+
94
+ ### About Templates
95
+
96
+ Templates live in `.r2pde-ai/templates/` and serve two purposes:
97
+
98
+ - **For the CLI** — used as the base structure when generating new artifacts via commands
99
+ - **For the developer** — reference models showing what each artifact should contain and how it should be structured
100
+
101
+ Templates can be customized. Once edited, the CLI will use your version as the base for all future artifact generation in that project — making every artifact consistent with your project's specific style and conventions.
102
+
103
+ ### About GUIDE.md
104
+
105
+ `GUIDE.md` is generated by `init` and serves as the human-readable reference for the framework. It explains each artifact type, its purpose, its structure, and includes annotated examples. Consult it whenever you need to understand what to put in a manifesto, contract, or requirement.
106
+
107
+ ---
108
+
109
+ ## Core Concepts
110
+
111
+ ### Manifestos
112
+ Flexible guiding principles covering UI, UX, code philosophy, and development culture.
113
+
114
+ ### Contracts
115
+ Rigid, non-negotiable rules covering architecture, TDD, DDD, security, permissions, routing, tests, and commit conventions.
116
+
117
+ ### Requirements
118
+ Full application specification: objectives, functional and non-functional requirements, business rules, use cases, flows, domains, and acceptance criteria.
119
+
120
+ ### Waves
121
+ Incremental implementation phases:
122
+ 1. Framework → 2. Architecture → 3. Conceptual environment → 4. Core (errors, logs, security) → 5. Authentication → 6. Users → 7. UI/UX → 8. Tests → 9. Documentation
123
+
124
+ ### Quality Score
125
+ Before generating any prompt, the CLI evaluates all artifacts from the current wave forward and returns a traffic-light score:
126
+ - 🟢 **Green** — consistent, proceed
127
+ - 🟡 **Yellow** — gaps or ambiguities detected, proceed with awareness
128
+ - 🔴 **Red** — critical conflicts or broken dependencies, strongly advised to resolve first
129
+
130
+ The score is contextual — it evaluates from the current wave forward, not the entire project. Thresholds are configurable.
131
+
132
+ ---
133
+
134
+ ## CLI Commands
135
+
136
+ | Command | Description |
137
+ |---|---|
138
+ | `r2pde-ai init` | Initialize framework in current directory |
139
+ | `r2pde-ai doctor` | Check environment health |
140
+ | `r2pde-ai manifesto:create` | Create a new manifesto |
141
+ | `r2pde-ai manifesto:delete` | Delete a manifesto |
142
+ | `r2pde-ai contract:create` | Create a new contract |
143
+ | `r2pde-ai contract:delete` | Delete a contract |
144
+ | `r2pde-ai requirement:create` | Create a new requirement |
145
+ | `r2pde-ai requirement:delete` | Delete a requirement |
146
+ | `r2pde-ai score` | Evaluate quality score from current context |
147
+ | `r2pde-ai score:config` | Configure score thresholds |
148
+ | `r2pde-ai wave:prompt` | Generate consolidated prompt for current wave |
149
+ | `r2pde-ai config:set` | Update configuration values |
150
+ | `r2pde-ai config:lang` | Set language for messages and artifacts |
151
+ | `r2pde-ai reset` | Reset prompts folder (with confirmation) |
152
+ | `r2pde-ai logs` | View audit log |
153
+
154
+ ---
155
+
156
+ ## Configuration
157
+
158
+ The `pde.config.json` file controls all framework behavior. Every setting has a sensible default and can be changed via commands.
159
+
160
+ ```json
161
+ {
162
+ "version": "1.0.0",
163
+ "language": "en",
164
+ "artifactsLanguage": "en",
165
+ "git": {
166
+ "autoCommit": false,
167
+ "commitMessagePrefix": "pde:"
168
+ },
169
+ "score": {
170
+ "green": { "max": 30 },
171
+ "yellow": { "max": 70 },
172
+ "red": { "max": 100 }
173
+ },
174
+ "ai": {
175
+ "apiUrl": "",
176
+ "apiKey": "",
177
+ "model": ""
178
+ },
179
+ "project": {
180
+ "type": "",
181
+ "architecture": "",
182
+ "maturity": "",
183
+ "hasAuth": false,
184
+ "hasPayment": false,
185
+ "hasIntegrations": false,
186
+ "audience": "",
187
+ "stack": ""
188
+ }
189
+ }
190
+ ```
191
+
192
+ ### Core Design Principle
193
+
194
+ > *Every behavior has a default. Every default is configurable. Every configuration has a command.*
195
+
196
+ ---
197
+
198
+
199
+ ## AI API Integration (Advanced Mode)
200
+
201
+ r2pde-ai works in two modes:
202
+
203
+ **Offline mode (default)** — The CLI generates structured prompts that you copy and paste into GitHub Copilot. No external AI required.
204
+
205
+ **API mode** — When an external AI API is configured, the CLI sends your project context to the AI, which generates an optimized, more precise prompt for GitHub Copilot. You still copy and paste into Copilot — but the prompt is significantly better.
206
+
207
+ **Important:** The AI API never generates code directly. It generates better prompts for Copilot. Copilot is always responsible for generating the code.
208
+
209
+ To enable API mode:
210
+ ```bash
211
+ r2pde-ai config:set ai.apiUrl https://api.anthropic.com
212
+ r2pde-ai config:set ai.apiKey your-api-key
213
+ r2pde-ai config:set ai.model claude-sonnet-4-20250514
214
+ ```
215
+
216
+ Supported: any API following the Anthropic messages format. OpenAI-compatible APIs may require adapter configuration in future versions.
217
+
218
+ ---
219
+
220
+ ## Git Integration
221
+
222
+ `r2pde-ai` is designed to live inside a git repository. Every artifact is a markdown file — versionable, diffable, and auditable.
223
+
224
+ Auto-commit (optional):
225
+ ```bash
226
+ r2pde-ai config:set git.autoCommit true
227
+ ```
228
+
229
+ When enabled, every CLI command that creates or modifies an artifact automatically stages and commits the change with a standardized message.
230
+
231
+ ---
232
+
233
+ ## Internationalization
234
+
235
+ Default language is English for both CLI messages and artifact structure. To change:
236
+
237
+ ```bash
238
+ r2pde-ai config:lang --messages pt
239
+ r2pde-ai config:lang --artifacts pt
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Contributing
245
+
246
+ We welcome contributions of all kinds — bug reports, feature requests, documentation improvements, and code.
247
+
248
+ ### Before You Start
249
+
250
+ 1. Read this README fully
251
+ 2. Check [open issues](../../issues) — your idea may already be in progress
252
+ 3. For significant changes, open an issue first to discuss the approach
253
+ 4. Keep changes focused — one concern per pull request
254
+
255
+ ### Development Setup
256
+
257
+ ```bash
258
+ # Fork and clone the repository
259
+ git clone https://github.com/your-username/r2pde-ai.git
260
+ cd r2pde-ai
261
+
262
+ # Install dependencies
263
+ npm install
264
+
265
+ # Build
266
+ npm run build
267
+
268
+ # Link locally for testing
269
+ npm link
270
+
271
+ # Run tests
272
+ npm test
273
+ ```
274
+
275
+ ### Branching Convention
276
+
277
+ | Branch | Purpose |
278
+ |---|---|
279
+ | `main` | Stable, released code |
280
+ | `dev` | Active development |
281
+ | `feature/short-description` | New features |
282
+ | `fix/short-description` | Bug fixes |
283
+ | `docs/short-description` | Documentation only |
284
+
285
+ All pull requests target `dev`. Never submit directly to `main`.
286
+
287
+ ### Commit Convention
288
+
289
+ We follow [Conventional Commits](https://www.conventionalcommits.org):
290
+
291
+ ```
292
+ feat: add score:config command
293
+ fix: resolve wave detection on nested folders
294
+ docs: update contributing guide
295
+ chore: bump dependencies
296
+ refactor: simplify artifact loader
297
+ test: add score threshold validation
298
+ ```
299
+
300
+ ### Pull Request Process
301
+
302
+ 1. Branch from `dev`
303
+ 2. Write or update tests for your change
304
+ 3. Ensure `npm test` passes with no failures
305
+ 4. Update documentation if the change affects behavior or commands
306
+ 5. Open a PR against `dev` with a clear title and description
307
+ 6. Reference any related issue with `Closes #number`
308
+ 7. Wait for review — we aim to respond within 72 hours
309
+
310
+ ### Code Standards
311
+
312
+ - TypeScript strict mode — no `any`
313
+ - All public functions documented with JSDoc
314
+ - File names in `kebab-case`
315
+ - Classes and components in `PascalCase`
316
+ - Methods and properties in `camelCase`
317
+ - Constants in `UPPER_SNAKE_CASE`
318
+ - Custom errors in `PascalCase` + `Error` suffix
319
+
320
+ ### Reporting Bugs
321
+
322
+ Open an issue with:
323
+ - Node.js version (`node --version`)
324
+ - r2pde-ai version (`r2pde-ai --version`)
325
+ - Operating system
326
+ - Command that triggered the issue
327
+ - Expected vs actual behavior
328
+ - Minimal reproduction steps
329
+
330
+ ### Requesting Features
331
+
332
+ Open an issue with:
333
+ - Problem you are trying to solve
334
+ - How you currently work around it
335
+ - Proposed solution or command
336
+ - Any alternative approaches considered
337
+
338
+ ### Code of Conduct
339
+
340
+ Be direct, be respectful, be constructive. We value clarity over ceremony. Discussions should focus on ideas, not individuals. Maintainers reserve the right to close unconstructive threads.
341
+
342
+ ---
343
+
344
+ ## Versioning
345
+
346
+ This project follows [Semantic Versioning](https://semver.org):
347
+
348
+ - `MAJOR` — breaking changes to CLI commands or artifact structure
349
+ - `MINOR` — new commands or backward-compatible features
350
+ - `PATCH` — bug fixes and internal improvements
351
+
352
+ Changelogs are maintained in [CHANGELOG.md](./CHANGELOG.md).
353
+
354
+ ---
355
+
356
+ ## License
357
+
358
+ MIT © r2pde-ai contributors
359
+
360
+ See [LICENSE](./LICENSE) for full terms.
361
+
362
+ ---
363
+
364
+ ## Roadmap
365
+
366
+ - [ ] `init` interactive mode with project type and architecture selection
367
+ - [ ] Template customization per project
368
+ - [ ] API mode with full AI integration
369
+ - [ ] Quality score engine
370
+ - [ ] Wave prompt consolidation
371
+ - [ ] VS Code extension for inline command execution
372
+ - [ ] Multi-language artifact support
373
+ - [ ] Plugin system for custom contracts and manifestos
package/dist/cli.js ADDED
@@ -0,0 +1,97 @@
1
+ import { resetCommand } from './commands/reset.js';
2
+ import { logsCommand } from './commands/logs.js';
3
+ import { requirementCreateCommand } from './commands/requirement-create.js';
4
+ import { requirementDeleteCommand } from './commands/requirement-delete.js';
5
+ import { scoreCommand } from './commands/score.js';
6
+ import { scoreConfigCommand } from './commands/score-config.js';
7
+ import { wavePromptCommand } from './commands/wave-prompt.js';
8
+ import { configSetCommand } from './commands/config-set.js';
9
+ import { configLangCommand } from './commands/config-lang.js';
10
+ import { Command } from 'commander';
11
+ import { initCommand } from './commands/init.js';
12
+ import { doctorCommand } from './commands/doctor.js';
13
+ import { manifestoCreateCommand } from './commands/manifesto-create.js';
14
+ import { manifestoDeleteCommand } from './commands/manifesto-delete.js';
15
+ import { contractCreateCommand } from './commands/contract-create.js';
16
+ import { contractDeleteCommand } from './commands/contract-delete.js';
17
+ const program = new Command();
18
+ program.addHelpText('after', `
19
+ Examples:
20
+ $ r2pde-ai init
21
+ $ r2pde-ai doctor
22
+ $ r2pde-ai manifesto:create
23
+ $ r2pde-ai contract:create
24
+ $ r2pde-ai requirement:create
25
+ $ r2pde-ai score
26
+ $ r2pde-ai score --from contracts
27
+ $ r2pde-ai wave:prompt
28
+ $ r2pde-ai config:set git.autoCommit true
29
+ $ r2pde-ai config:lang
30
+ $ r2pde-ai logs
31
+ $ r2pde-ai logs --clear
32
+ $ r2pde-ai reset
33
+
34
+ Documentation:
35
+ After init, read .r2pde-ai/GUIDE.md for full artifact reference.
36
+ Always paste .r2pde-ai/pde.index.md into your AI copilot before any prompt.
37
+ `);
38
+ program.addCommand(resetCommand);
39
+ program.addCommand(logsCommand);
40
+ program
41
+ .command('requirement:create')
42
+ .description('Create a new requirement')
43
+ .action(() => {
44
+ void requirementCreateCommand();
45
+ });
46
+ program
47
+ .command('requirement:delete')
48
+ .description('Delete an existing requirement')
49
+ .action(() => {
50
+ void requirementDeleteCommand();
51
+ });
52
+ program
53
+ .command('contract:create')
54
+ .description('Create a new contract')
55
+ .action(() => {
56
+ void contractCreateCommand();
57
+ });
58
+ program
59
+ .command('contract:delete')
60
+ .description('Delete an existing contract')
61
+ .action(() => {
62
+ void contractDeleteCommand();
63
+ });
64
+ program
65
+ .command('manifesto:create')
66
+ .description('Create a new manifesto')
67
+ .action(() => {
68
+ void manifestoCreateCommand();
69
+ });
70
+ program
71
+ .command('manifesto:delete')
72
+ .description('Delete an existing manifesto')
73
+ .action(() => {
74
+ void manifestoDeleteCommand();
75
+ });
76
+ program.addCommand(scoreCommand);
77
+ program.addCommand(scoreConfigCommand);
78
+ program.addCommand(wavePromptCommand);
79
+ program.addCommand(configSetCommand);
80
+ program.addCommand(configLangCommand);
81
+ program
82
+ .name('r2pde-ai')
83
+ .description('Pilot Driven Engineering A CLI framework that bridges the gap between architectural intent and AI-generated code.')
84
+ .version('0.1.0');
85
+ program
86
+ .command('init')
87
+ .description('Initialize r2pde-ai in the current project')
88
+ .action(() => {
89
+ void initCommand();
90
+ });
91
+ program
92
+ .command('doctor')
93
+ .description('Check the health of the r2pde-ai environment')
94
+ .action(() => {
95
+ void doctorCommand();
96
+ });
97
+ program.parse();
@@ -0,0 +1,32 @@
1
+ import { Command } from 'commander';
2
+ import { select } from '@inquirer/prompts';
3
+ import { loadConfig, saveConfig } from '../core/config.js';
4
+ import { logInfo, logSuccess } from '../core/logger.js';
5
+ export const configLangCommand = new Command('config:lang')
6
+ .description('Set language for messages and artifacts')
7
+ .action(async () => {
8
+ const cwd = process.cwd();
9
+ const config = loadConfig(cwd);
10
+ logInfo('The language for all CLI output messages.');
11
+ const language = await select({
12
+ message: 'Language for CLI messages:',
13
+ choices: [
14
+ { name: 'English', value: 'en' },
15
+ { name: 'Português', value: 'pt' }
16
+ ],
17
+ default: config.language || 'en',
18
+ });
19
+ logInfo('The language used when generating markdown artifact files.');
20
+ const artifactsLanguage = await select({
21
+ message: 'Language for artifacts:',
22
+ choices: [
23
+ { name: 'English', value: 'en' },
24
+ { name: 'Português', value: 'pt' }
25
+ ],
26
+ default: config.artifactsLanguage || 'en',
27
+ });
28
+ config.language = language;
29
+ config.artifactsLanguage = artifactsLanguage;
30
+ saveConfig(cwd, config);
31
+ logSuccess('Language settings updated.');
32
+ });
@@ -0,0 +1,38 @@
1
+ import { Command } from 'commander';
2
+ import { loadConfig, saveConfig } from '../core/config.js';
3
+ import { logSuccess, logWarn } from '../core/logger.js';
4
+ function setByDot(obj, path, value) {
5
+ const keys = path.split('.');
6
+ let cur = obj;
7
+ for (let i = 0; i < keys.length - 1; ++i) {
8
+ if (!(keys[i] in cur))
9
+ return false;
10
+ cur = cur[keys[i]];
11
+ }
12
+ const last = keys[keys.length - 1];
13
+ if (!(last in cur))
14
+ return false;
15
+ cur[last] = value;
16
+ return true;
17
+ }
18
+ export const configSetCommand = new Command('config:set')
19
+ .description('Set a configuration value')
20
+ .argument('<key>', 'Config key (dot notation)')
21
+ .argument('<value>', 'Value to set')
22
+ .action((key, value) => {
23
+ const cwd = process.cwd();
24
+ const config = loadConfig(cwd);
25
+ let v = value;
26
+ if (value === 'true')
27
+ v = true;
28
+ else if (value === 'false')
29
+ v = false;
30
+ else if (!isNaN(Number(value)))
31
+ v = Number(value);
32
+ const ok = setByDot(config, key, v);
33
+ if (!ok) {
34
+ logWarn(`Key does not exist in config: ${key}`);
35
+ }
36
+ saveConfig(cwd, config);
37
+ logSuccess(`Config updated: ${key} = ${v}`);
38
+ });
@@ -0,0 +1,101 @@
1
+ import { input, select, confirm } from '@inquirer/prompts';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import { getPaths } from '../core/paths.js';
5
+ import { logError, logWarn, logInfo, logSuccess } from '../core/logger.js';
6
+ import { gitAddAndCommit } from '../core/git.js';
7
+ import { loadConfig } from '../core/config.js';
8
+ function toKebabCase(str) {
9
+ return str
10
+ .normalize('NFD')
11
+ .replace(/[^\w\s-]/g, '')
12
+ .trim()
13
+ .toLowerCase()
14
+ .replace(/\s+/g, '-')
15
+ .replace(/-+/g, '-');
16
+ }
17
+ export async function contractCreateCommand() {
18
+ const cwd = process.cwd();
19
+ const paths = getPaths(cwd);
20
+ if (!fs.existsSync(paths.root)) {
21
+ logError('r2pde-ai not initialized. Run r2pde-ai init first.');
22
+ return;
23
+ }
24
+ // Step 2 — Interactive prompts
25
+ logInfo('The name of this contract. Should reflect what rule it enforces (e.g. API Security, Commit Convention).');
26
+ const name = await input({ message: 'Contract name:', validate: (inputVal) => inputVal.trim() !== '' || 'Name is required' });
27
+ logInfo('The category this contract belongs to. Used for organization and score evaluation.');
28
+ const type = await select({ message: 'Type:', choices: [
29
+ { name: 'Architecture', value: 'architecture' },
30
+ { name: 'TDD', value: 'tdd' },
31
+ { name: 'DDD', value: 'ddd' },
32
+ { name: 'Security', value: 'security' },
33
+ { name: 'Permissions', value: 'permissions' },
34
+ { name: 'Routing', value: 'routing' },
35
+ { name: 'Tests', value: 'tests' },
36
+ { name: 'Commits', value: 'commits' },
37
+ { name: 'Other', value: 'other' }
38
+ ] });
39
+ logInfo('Mandatory contracts are evaluated strictly. Recommended contracts generate warnings only.');
40
+ const enforcement = await select({ message: 'Enforcement level:', choices: [
41
+ { name: 'Mandatory', value: 'mandatory' },
42
+ { name: 'Recommended', value: 'recommended' }
43
+ ] });
44
+ logInfo('A brief summary of what this contract enforces and the consequences of violation.');
45
+ const description = await input({ message: 'Brief description:', validate: (inputVal) => inputVal.trim() !== '' || 'Description is required' });
46
+ const kebabName = toKebabCase(name);
47
+ const filename = `${kebabName}.md`;
48
+ const filePath = path.join(paths.contracts, filename);
49
+ // Step 3 — Check for duplicates
50
+ if (fs.existsSync(filePath)) {
51
+ logWarn('Contract already exists.');
52
+ logInfo('Select Yes to overwrite the existing contract file.');
53
+ const overwrite = await confirm({ message: 'Contract already exists. Overwrite?', default: false });
54
+ if (!overwrite) {
55
+ logInfo('Operation cancelled.');
56
+ return;
57
+ }
58
+ }
59
+ // Step 4 — Generate contract file
60
+ const templatePath = path.join(paths.templates, 'contract.template.md');
61
+ if (!fs.existsSync(templatePath)) {
62
+ logError('Contract template not found.');
63
+ return;
64
+ }
65
+ let template = fs.readFileSync(templatePath, { encoding: 'utf8' });
66
+ template = template.replace('[Name]', name);
67
+ const meta = `---\nname: ${name}\ntype: ${type}\nenforcement: ${enforcement}\ndescription: ${description}\ncreatedAt: ${new Date().toISOString()}\n---\n\n`;
68
+ const content = meta + template;
69
+ fs.writeFileSync(filePath, content, { encoding: 'utf8' });
70
+ // Step 5 — Update pde.index.md
71
+ if (fs.existsSync(paths.index)) {
72
+ let indexContent = fs.readFileSync(paths.index, { encoding: 'utf8' });
73
+ const contractsSection = '- Contracts: .r2pde-ai/contracts/';
74
+ const newLine = ` - ${name}: .r2pde-ai/contracts/${filename}`;
75
+ if (indexContent.includes(contractsSection)) {
76
+ const lines = indexContent.split('\n');
77
+ const idx = lines.findIndex(l => l.trim() === contractsSection);
78
+ if (idx !== -1) {
79
+ let insertAt = idx + 1;
80
+ while (insertAt < lines.length && (lines[insertAt].startsWith(' - ') || lines[insertAt].trim() === ''))
81
+ insertAt++;
82
+ lines.splice(insertAt, 0, newLine);
83
+ indexContent = lines.join('\n');
84
+ fs.writeFileSync(paths.index, indexContent, { encoding: 'utf8' });
85
+ }
86
+ }
87
+ }
88
+ else {
89
+ logWarn('pde.index.md not found. Skipping index update.');
90
+ }
91
+ // Step 6 — Log entry
92
+ const logPath = path.join(paths.logs, 'pde.log.md');
93
+ const logLine = `- ${new Date().toISOString()} | contract:create | ${filename} | created\n`;
94
+ fs.ensureFileSync(logPath);
95
+ fs.appendFileSync(logPath, logLine, { encoding: 'utf8' });
96
+ // Step 7 — Git commit if enabled
97
+ const config = loadConfig(cwd);
98
+ gitAddAndCommit(cwd, `feat(contract): add ${name}`, config);
99
+ // Step 8 — Final output
100
+ logSuccess(`Contract '${name}' created at .r2pde-ai/contracts/${filename}`);
101
+ }