ripp-cli 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/README.md +292 -0
- package/index.js +1350 -0
- package/lib/ai-provider.js +354 -0
- package/lib/analyzer.js +394 -0
- package/lib/build.js +338 -0
- package/lib/config.js +277 -0
- package/lib/confirmation.js +183 -0
- package/lib/discovery.js +119 -0
- package/lib/evidence.js +368 -0
- package/lib/init.js +488 -0
- package/lib/linter.js +309 -0
- package/lib/migrate.js +203 -0
- package/lib/packager.js +374 -0
- package/package.json +40 -0
package/lib/init.js
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { createDefaultConfig } = require('./config');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Initialize RIPP in a repository
|
|
7
|
+
* Creates scaffolding for RIPP artifacts, GitHub Actions, and Intent Packages
|
|
8
|
+
*/
|
|
9
|
+
function initRepository(options = {}) {
|
|
10
|
+
const force = options.force || false;
|
|
11
|
+
const results = {
|
|
12
|
+
created: [],
|
|
13
|
+
skipped: [],
|
|
14
|
+
errors: []
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// 1. Create /ripp directory
|
|
18
|
+
try {
|
|
19
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
20
|
+
if (!fs.existsSync(rippDir)) {
|
|
21
|
+
fs.mkdirSync(rippDir, { recursive: true });
|
|
22
|
+
results.created.push('ripp/');
|
|
23
|
+
} else {
|
|
24
|
+
results.skipped.push('ripp/ (already exists)');
|
|
25
|
+
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
results.errors.push(`Failed to create ripp/: ${error.message}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 2. Create /ripp/README.md
|
|
31
|
+
try {
|
|
32
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
33
|
+
const rippReadmePath = path.join(rippDir, 'README.md');
|
|
34
|
+
if (!fs.existsSync(rippReadmePath) || force) {
|
|
35
|
+
const rippReadme = generateRippReadme();
|
|
36
|
+
fs.writeFileSync(rippReadmePath, rippReadme);
|
|
37
|
+
results.created.push('ripp/README.md');
|
|
38
|
+
} else {
|
|
39
|
+
results.skipped.push('ripp/README.md (already exists, use --force to overwrite)');
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
results.errors.push(`Failed to create ripp/README.md: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 3. Create /ripp/intent directory for intent RIPP files (formerly features/)
|
|
46
|
+
try {
|
|
47
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
48
|
+
const intentDir = path.join(rippDir, 'intent');
|
|
49
|
+
const legacyFeaturesDir = path.join(rippDir, 'features');
|
|
50
|
+
|
|
51
|
+
// Check for legacy features/ directory
|
|
52
|
+
if (fs.existsSync(legacyFeaturesDir) && !fs.existsSync(intentDir)) {
|
|
53
|
+
results.skipped.push(
|
|
54
|
+
'ripp/features/ (legacy directory exists - use "ripp migrate" to update)'
|
|
55
|
+
);
|
|
56
|
+
} else if (!fs.existsSync(intentDir)) {
|
|
57
|
+
fs.mkdirSync(intentDir, { recursive: true });
|
|
58
|
+
results.created.push('ripp/intent/');
|
|
59
|
+
} else {
|
|
60
|
+
results.skipped.push('ripp/intent/ (already exists)');
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
results.errors.push(`Failed to create ripp/intent/: ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 4. Create /ripp/intent/.gitkeep
|
|
67
|
+
try {
|
|
68
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
69
|
+
const intentDir = path.join(rippDir, 'intent');
|
|
70
|
+
const gitkeepPath = path.join(intentDir, '.gitkeep');
|
|
71
|
+
if (fs.existsSync(intentDir) && (!fs.existsSync(gitkeepPath) || force)) {
|
|
72
|
+
fs.writeFileSync(gitkeepPath, '');
|
|
73
|
+
results.created.push('ripp/intent/.gitkeep');
|
|
74
|
+
} else if (fs.existsSync(gitkeepPath)) {
|
|
75
|
+
results.skipped.push('ripp/intent/.gitkeep (already exists, use --force to overwrite)');
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
results.errors.push(`Failed to create ripp/intent/.gitkeep: ${error.message}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 5. Create /ripp/output directory (groups generated artifacts)
|
|
82
|
+
try {
|
|
83
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
84
|
+
const outputDir = path.join(rippDir, 'output');
|
|
85
|
+
if (!fs.existsSync(outputDir)) {
|
|
86
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
87
|
+
results.created.push('ripp/output/');
|
|
88
|
+
} else {
|
|
89
|
+
results.skipped.push('ripp/output/ (already exists)');
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
results.errors.push(`Failed to create ripp/output/: ${error.message}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 6. Create /ripp/output/handoffs directory (validated, ready-to-deliver packets)
|
|
96
|
+
try {
|
|
97
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
98
|
+
const handoffsDir = path.join(rippDir, 'output', 'handoffs');
|
|
99
|
+
const legacyHandoffsDir = path.join(rippDir, 'handoffs');
|
|
100
|
+
|
|
101
|
+
// Check for legacy handoffs/ directory
|
|
102
|
+
if (fs.existsSync(legacyHandoffsDir) && !fs.existsSync(handoffsDir)) {
|
|
103
|
+
results.skipped.push(
|
|
104
|
+
'ripp/handoffs/ (legacy directory exists - use "ripp migrate" to update)'
|
|
105
|
+
);
|
|
106
|
+
} else if (!fs.existsSync(handoffsDir)) {
|
|
107
|
+
fs.mkdirSync(handoffsDir, { recursive: true });
|
|
108
|
+
results.created.push('ripp/output/handoffs/');
|
|
109
|
+
} else {
|
|
110
|
+
results.skipped.push('ripp/output/handoffs/ (already exists)');
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
results.errors.push(`Failed to create ripp/output/handoffs/: ${error.message}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 7. Create /ripp/output/handoffs/.gitkeep
|
|
117
|
+
try {
|
|
118
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
119
|
+
const handoffsDir = path.join(rippDir, 'output', 'handoffs');
|
|
120
|
+
const gitkeepPath = path.join(handoffsDir, '.gitkeep');
|
|
121
|
+
if (fs.existsSync(handoffsDir) && (!fs.existsSync(gitkeepPath) || force)) {
|
|
122
|
+
fs.writeFileSync(gitkeepPath, '');
|
|
123
|
+
results.created.push('ripp/output/handoffs/.gitkeep');
|
|
124
|
+
} else if (fs.existsSync(gitkeepPath)) {
|
|
125
|
+
results.skipped.push(
|
|
126
|
+
'ripp/output/handoffs/.gitkeep (already exists, use --force to overwrite)'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
results.errors.push(`Failed to create ripp/output/handoffs/.gitkeep: ${error.message}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 8. Create /ripp/output/packages directory (generated outputs, gitignored)
|
|
134
|
+
try {
|
|
135
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
136
|
+
const packagesDir = path.join(rippDir, 'output', 'packages');
|
|
137
|
+
const legacyPackagesDir = path.join(rippDir, 'packages');
|
|
138
|
+
|
|
139
|
+
// Check for legacy packages/ directory
|
|
140
|
+
if (fs.existsSync(legacyPackagesDir) && !fs.existsSync(packagesDir)) {
|
|
141
|
+
results.skipped.push(
|
|
142
|
+
'ripp/packages/ (legacy directory exists - use "ripp migrate" to update)'
|
|
143
|
+
);
|
|
144
|
+
} else if (!fs.existsSync(packagesDir)) {
|
|
145
|
+
fs.mkdirSync(packagesDir, { recursive: true });
|
|
146
|
+
results.created.push('ripp/output/packages/');
|
|
147
|
+
} else {
|
|
148
|
+
results.skipped.push('ripp/output/packages/ (already exists)');
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
results.errors.push(`Failed to create ripp/output/packages/: ${error.message}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 9. Create /ripp/output/packages/.gitkeep
|
|
155
|
+
try {
|
|
156
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
157
|
+
const packagesDir = path.join(rippDir, 'output', 'packages');
|
|
158
|
+
const gitkeepPath = path.join(packagesDir, '.gitkeep');
|
|
159
|
+
if (fs.existsSync(packagesDir) && (!fs.existsSync(gitkeepPath) || force)) {
|
|
160
|
+
fs.writeFileSync(gitkeepPath, '');
|
|
161
|
+
results.created.push('ripp/output/packages/.gitkeep');
|
|
162
|
+
} else if (fs.existsSync(gitkeepPath)) {
|
|
163
|
+
results.skipped.push(
|
|
164
|
+
'ripp/output/packages/.gitkeep (already exists, use --force to overwrite)'
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
results.errors.push(`Failed to create ripp/output/packages/.gitkeep: ${error.message}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 10. Create /ripp/.gitignore
|
|
172
|
+
try {
|
|
173
|
+
const rippDir = path.join(process.cwd(), 'ripp');
|
|
174
|
+
const gitignorePath = path.join(rippDir, '.gitignore');
|
|
175
|
+
if (!fs.existsSync(gitignorePath) || force) {
|
|
176
|
+
const gitignoreContent = generateRippGitignore();
|
|
177
|
+
fs.writeFileSync(gitignorePath, gitignoreContent);
|
|
178
|
+
results.created.push('ripp/.gitignore');
|
|
179
|
+
} else {
|
|
180
|
+
results.skipped.push('ripp/.gitignore (already exists, use --force to overwrite)');
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
results.errors.push(`Failed to create ripp/.gitignore: ${error.message}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 11. Create .github/workflows directory
|
|
187
|
+
try {
|
|
188
|
+
const workflowsDir = path.join(process.cwd(), '.github', 'workflows');
|
|
189
|
+
if (!fs.existsSync(workflowsDir)) {
|
|
190
|
+
fs.mkdirSync(workflowsDir, { recursive: true });
|
|
191
|
+
results.created.push('.github/workflows/');
|
|
192
|
+
} else {
|
|
193
|
+
results.skipped.push('.github/workflows/ (already exists)');
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
results.errors.push(`Failed to create .github/workflows/: ${error.message}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 12. Create GitHub Action workflow
|
|
200
|
+
try {
|
|
201
|
+
const workflowsDir = path.join(process.cwd(), '.github', 'workflows');
|
|
202
|
+
const workflowPath = path.join(workflowsDir, 'ripp-validate.yml');
|
|
203
|
+
if (!fs.existsSync(workflowPath) || force) {
|
|
204
|
+
const workflow = generateGitHubActionWorkflow();
|
|
205
|
+
fs.writeFileSync(workflowPath, workflow);
|
|
206
|
+
results.created.push('.github/workflows/ripp-validate.yml');
|
|
207
|
+
} else {
|
|
208
|
+
results.skipped.push(
|
|
209
|
+
'.github/workflows/ripp-validate.yml (already exists, use --force to overwrite)'
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
results.errors.push(`Failed to create .github/workflows/ripp-validate.yml: ${error.message}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 13. Create .ripp/config.yaml (vNext)
|
|
217
|
+
try {
|
|
218
|
+
const configResult = createDefaultConfig(process.cwd(), { force });
|
|
219
|
+
if (configResult.created) {
|
|
220
|
+
results.created.push('.ripp/config.yaml');
|
|
221
|
+
} else {
|
|
222
|
+
results.skipped.push('.ripp/config.yaml (already exists, use --force to overwrite)');
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
results.errors.push(`Failed to create .ripp/config.yaml: ${error.message}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return results;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function generateRippReadme() {
|
|
232
|
+
return `# RIPP - Regenerative Intent Prompting Protocol
|
|
233
|
+
|
|
234
|
+
This directory contains RIPP (Regenerative Intent Prompting Protocol) packets for this repository.
|
|
235
|
+
|
|
236
|
+
## What is RIPP?
|
|
237
|
+
|
|
238
|
+
RIPP is a **structured specification format** for capturing feature requirements, design decisions, and implementation contracts in a machine-readable, human-reviewable format.
|
|
239
|
+
|
|
240
|
+
A RIPP packet is a YAML or JSON document that describes:
|
|
241
|
+
- **What** a feature does and **why** it exists
|
|
242
|
+
- **How** users interact with it
|
|
243
|
+
- **What data** it produces and consumes
|
|
244
|
+
- **Who** can do what (permissions)
|
|
245
|
+
- **What can go wrong** and how to handle it
|
|
246
|
+
- **How to verify** correctness (acceptance tests)
|
|
247
|
+
|
|
248
|
+
**Key Benefits:**
|
|
249
|
+
- Preserve original intent during implementation
|
|
250
|
+
- Create reviewable, versioned contracts
|
|
251
|
+
- Enable automated validation
|
|
252
|
+
- Bridge prototyping and production teams
|
|
253
|
+
- Prevent intent erosion and undocumented assumptions
|
|
254
|
+
|
|
255
|
+
## What RIPP is NOT
|
|
256
|
+
|
|
257
|
+
**RIPP is not a code migration tool:**
|
|
258
|
+
- Does not transform prototype code into production code
|
|
259
|
+
- Does not provide refactoring or modernization capabilities
|
|
260
|
+
- Does not attempt lift-and-shift operations
|
|
261
|
+
|
|
262
|
+
**RIPP is not a code generator:**
|
|
263
|
+
- RIPP is a specification format, not a code generation framework
|
|
264
|
+
- While tools MAY generate code from RIPP packets, this is optional
|
|
265
|
+
- RIPP does not prescribe implementation details
|
|
266
|
+
|
|
267
|
+
**RIPP is not a production hardening helper:**
|
|
268
|
+
- Does not scan code for vulnerabilities
|
|
269
|
+
- Does not inject security controls into existing code
|
|
270
|
+
- Does not automatically make prototype code production-ready
|
|
271
|
+
|
|
272
|
+
## Directory Structure
|
|
273
|
+
|
|
274
|
+
\`\`\`
|
|
275
|
+
ripp/
|
|
276
|
+
├── README.md # This file
|
|
277
|
+
├── .gitignore # Ignores generated packages
|
|
278
|
+
├── intent/ # Work-in-progress RIPP packets (human-authored intent)
|
|
279
|
+
│ ├── auth-login.ripp.yaml
|
|
280
|
+
│ ├── user-profile.ripp.yaml
|
|
281
|
+
│ └── ...
|
|
282
|
+
└── output/ # Generated artifacts (machine-generated)
|
|
283
|
+
├── handoffs/ # Validated, approved RIPP packets ready for delivery
|
|
284
|
+
│ ├── sample.ripp.yaml
|
|
285
|
+
│ └── ...
|
|
286
|
+
└── packages/ # Generated output formats (.zip, .md, .json) [gitignored]
|
|
287
|
+
├── handoff.zip
|
|
288
|
+
├── sample.md
|
|
289
|
+
└── ...
|
|
290
|
+
\`\`\`
|
|
291
|
+
|
|
292
|
+
**Directory Naming:**
|
|
293
|
+
- \`intent/\` — Human-authored intent specifications (source of truth)
|
|
294
|
+
- \`output/\` — Machine-generated artifacts (derived from intent)
|
|
295
|
+
- \`handoffs/\` — Finalized packets ready for delivery
|
|
296
|
+
- \`packages/\` — Packaged outputs (markdown, JSON, ZIP)
|
|
297
|
+
|
|
298
|
+
**Legacy directories** (\`features/\`, \`handoffs/\`, \`packages/\` at root level) are supported for backward compatibility. Use \`ripp migrate\` to update to the new structure.
|
|
299
|
+
|
|
300
|
+
## What is RIPP vs What are Artifacts?
|
|
301
|
+
|
|
302
|
+
**RIPP** is the protocol specification — the format and rules for capturing intent.
|
|
303
|
+
|
|
304
|
+
**Artifacts** in RIPP context means:
|
|
305
|
+
- The conceptual outputs of the specification process
|
|
306
|
+
- NOT a specific directory named "artifacts"
|
|
307
|
+
- Includes: intent packets, handoffs, packages, and derived documentation
|
|
308
|
+
|
|
309
|
+
This repository uses **explicit directory names** (\`intent/\`, \`output/\`) to avoid confusion about what goes where.
|
|
310
|
+
|
|
311
|
+
## Workflow
|
|
312
|
+
|
|
313
|
+
### 1. Create new packets in \`intent/\`
|
|
314
|
+
|
|
315
|
+
Place your work-in-progress RIPP packets in \`ripp/intent/\` with the naming convention:
|
|
316
|
+
|
|
317
|
+
\`\`\`
|
|
318
|
+
<feature-name>.ripp.yaml
|
|
319
|
+
\`\`\`
|
|
320
|
+
|
|
321
|
+
Example: \`auth-login.ripp.yaml\`
|
|
322
|
+
|
|
323
|
+
### 2. Minimum required structure (Level 1)
|
|
324
|
+
|
|
325
|
+
\`\`\`yaml
|
|
326
|
+
ripp_version: "1.0"
|
|
327
|
+
packet_id: auth-login
|
|
328
|
+
title: User Authentication and Login
|
|
329
|
+
created: 2024-01-15
|
|
330
|
+
updated: 2024-01-15
|
|
331
|
+
status: draft
|
|
332
|
+
level: 1
|
|
333
|
+
|
|
334
|
+
purpose:
|
|
335
|
+
problem: "Users need a secure way to authenticate and access their accounts"
|
|
336
|
+
solution: "Implement JWT-based authentication with email/password login"
|
|
337
|
+
value: "Enables secure user access and personalized experiences"
|
|
338
|
+
|
|
339
|
+
ux_flow:
|
|
340
|
+
- step: "User navigates to login page"
|
|
341
|
+
- step: "User enters email and password"
|
|
342
|
+
- step: "System validates credentials"
|
|
343
|
+
- step: "User is redirected to dashboard"
|
|
344
|
+
|
|
345
|
+
data_contracts:
|
|
346
|
+
inputs:
|
|
347
|
+
- name: email
|
|
348
|
+
type: string
|
|
349
|
+
format: email
|
|
350
|
+
required: true
|
|
351
|
+
- name: password
|
|
352
|
+
type: string
|
|
353
|
+
required: true
|
|
354
|
+
outputs:
|
|
355
|
+
- name: auth_token
|
|
356
|
+
type: string
|
|
357
|
+
description: "JWT access token"
|
|
358
|
+
\`\`\`
|
|
359
|
+
|
|
360
|
+
### 3. Validate your packets
|
|
361
|
+
|
|
362
|
+
\`\`\`bash
|
|
363
|
+
# Validate all packets in intent/
|
|
364
|
+
ripp validate ripp/intent/
|
|
365
|
+
|
|
366
|
+
# Validate a specific file
|
|
367
|
+
ripp validate ripp/intent/auth-login.ripp.yaml
|
|
368
|
+
|
|
369
|
+
# Enforce minimum conformance level
|
|
370
|
+
ripp validate ripp/intent/ --min-level 2
|
|
371
|
+
|
|
372
|
+
# Also supports legacy paths (features/, handoffs/, packages/)
|
|
373
|
+
ripp validate ripp/features/ # backward compatible
|
|
374
|
+
\`\`\`
|
|
375
|
+
|
|
376
|
+
### 4. Lint for best practices
|
|
377
|
+
|
|
378
|
+
\`\`\`bash
|
|
379
|
+
ripp lint ripp/intent/
|
|
380
|
+
ripp lint ripp/intent/ --strict
|
|
381
|
+
\`\`\`
|
|
382
|
+
|
|
383
|
+
### 5. Move validated packets to \`output/handoffs/\`
|
|
384
|
+
|
|
385
|
+
When a packet is validated and approved, move it to the handoffs directory:
|
|
386
|
+
|
|
387
|
+
\`\`\`bash
|
|
388
|
+
mv ripp/intent/my-feature.ripp.yaml ripp/output/handoffs/
|
|
389
|
+
\`\`\`
|
|
390
|
+
|
|
391
|
+
### 6. Package for delivery
|
|
392
|
+
|
|
393
|
+
\`\`\`bash
|
|
394
|
+
# Package a single packet
|
|
395
|
+
ripp package --in ripp/output/handoffs/my-feature.ripp.yaml --out ripp/output/packages/handoff.md
|
|
396
|
+
|
|
397
|
+
# Package to JSON
|
|
398
|
+
ripp package --in ripp/output/handoffs/my-feature.ripp.yaml --out ripp/output/packages/packaged.json
|
|
399
|
+
\`\`\`
|
|
400
|
+
|
|
401
|
+
### 7. Deliver the package
|
|
402
|
+
|
|
403
|
+
Share the generated files in \`ripp/output/packages/\` with receiving teams or upload to artifact repositories.
|
|
404
|
+
|
|
405
|
+
## Validation
|
|
406
|
+
|
|
407
|
+
This repository includes automated RIPP validation via GitHub Actions.
|
|
408
|
+
|
|
409
|
+
Every pull request that modifies RIPP files will be automatically validated.
|
|
410
|
+
|
|
411
|
+
## RIPP Levels
|
|
412
|
+
|
|
413
|
+
RIPP supports three conformance levels:
|
|
414
|
+
|
|
415
|
+
- **Level 1**: Basic (purpose, ux_flow, data_contracts)
|
|
416
|
+
- **Level 2**: Standard (adds api_contracts, permissions, failure_modes)
|
|
417
|
+
- **Level 3**: Complete (adds audit_events, nfrs, acceptance_tests)
|
|
418
|
+
|
|
419
|
+
Choose the level appropriate for your feature's complexity and criticality.
|
|
420
|
+
|
|
421
|
+
## Resources
|
|
422
|
+
|
|
423
|
+
- **CLI Documentation**: \`ripp --help\`
|
|
424
|
+
- **Protocol Website**: https://dylan-natter.github.io/ripp-protocol
|
|
425
|
+
- **GitHub Repository**: https://github.com/Dylan-Natter/ripp-protocol
|
|
426
|
+
- **Issues & Support**: https://github.com/Dylan-Natter/ripp-protocol/issues
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
**Write specs first. Ship with confidence.**
|
|
431
|
+
`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function generateRippGitignore() {
|
|
435
|
+
return `# Generated packages (deliverables)
|
|
436
|
+
# These are built artifacts that should not be committed
|
|
437
|
+
output/packages/
|
|
438
|
+
|
|
439
|
+
# Keep directory structure
|
|
440
|
+
!.gitkeep
|
|
441
|
+
`;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function generateGitHubActionWorkflow() {
|
|
445
|
+
return `name: Validate RIPP Packets
|
|
446
|
+
|
|
447
|
+
on:
|
|
448
|
+
pull_request:
|
|
449
|
+
paths:
|
|
450
|
+
- 'ripp/**/*.ripp.yaml'
|
|
451
|
+
- 'ripp/**/*.ripp.json'
|
|
452
|
+
workflow_dispatch:
|
|
453
|
+
|
|
454
|
+
jobs:
|
|
455
|
+
validate:
|
|
456
|
+
name: Validate RIPP Packets
|
|
457
|
+
runs-on: ubuntu-latest
|
|
458
|
+
|
|
459
|
+
permissions:
|
|
460
|
+
contents: read
|
|
461
|
+
|
|
462
|
+
steps:
|
|
463
|
+
- name: Checkout repository
|
|
464
|
+
uses: actions/checkout@v4
|
|
465
|
+
|
|
466
|
+
- name: Setup Node.js
|
|
467
|
+
uses: actions/setup-node@v4
|
|
468
|
+
with:
|
|
469
|
+
node-version: '18'
|
|
470
|
+
|
|
471
|
+
- name: Install dependencies
|
|
472
|
+
run: npm ci
|
|
473
|
+
|
|
474
|
+
- name: Validate RIPP packets
|
|
475
|
+
run: npm run ripp:validate
|
|
476
|
+
|
|
477
|
+
- name: Summary
|
|
478
|
+
if: always()
|
|
479
|
+
run: echo "✅ RIPP validation complete"
|
|
480
|
+
`;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
module.exports = {
|
|
484
|
+
initRepository,
|
|
485
|
+
generateRippReadme,
|
|
486
|
+
generateRippGitignore,
|
|
487
|
+
generateGitHubActionWorkflow
|
|
488
|
+
};
|