ripp-cli 1.0.0 → 1.2.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.
- package/CHANGELOG.md +63 -0
- package/README.md +57 -0
- package/index.js +138 -8
- package/lib/build.js +116 -10
- package/lib/checklist-parser.js +224 -0
- package/lib/config.js +2 -3
- package/lib/confirmation.js +77 -6
- package/lib/doctor.js +370 -0
- package/lib/evidence.js +210 -7
- package/lib/metrics.js +410 -0
- package/lib/packager.js +26 -14
- package/package.json +25 -8
- package/schema/evidence-pack.schema.json +201 -0
- package/schema/intent-candidates.schema.json +109 -0
- package/schema/intent-confirmed.schema.json +85 -0
- package/schema/ripp-1.0.schema.json +543 -0
- package/schema/ripp-config.schema.json +104 -0
package/lib/metrics.js
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Gather metrics about the RIPP workflow in the current repository.
|
|
7
|
+
* Metrics are best-effort and never fabricated - if data is unavailable, it is marked as N/A.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} rippDir - Path to .ripp directory (default: ./.ripp)
|
|
10
|
+
* @returns {object} Metrics object with evidence, discovery, validation, and workflow stats
|
|
11
|
+
*/
|
|
12
|
+
function gatherMetrics(rippDir = './.ripp') {
|
|
13
|
+
const metrics = {
|
|
14
|
+
timestamp: new Date().toISOString(),
|
|
15
|
+
evidence: gatherEvidenceMetrics(rippDir),
|
|
16
|
+
discovery: gatherDiscoveryMetrics(rippDir),
|
|
17
|
+
validation: gatherValidationMetrics(rippDir),
|
|
18
|
+
workflow: gatherWorkflowMetrics(rippDir)
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return metrics;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gather evidence pack metrics
|
|
26
|
+
*/
|
|
27
|
+
function gatherEvidenceMetrics(rippDir) {
|
|
28
|
+
const evidenceIndexPath = path.join(rippDir, 'evidence', 'evidence.index.json');
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(evidenceIndexPath)) {
|
|
31
|
+
return {
|
|
32
|
+
status: 'not_built',
|
|
33
|
+
file_count: 0,
|
|
34
|
+
total_size: 0,
|
|
35
|
+
coverage_percent: 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const evidenceIndex = JSON.parse(fs.readFileSync(evidenceIndexPath, 'utf8'));
|
|
41
|
+
const fileCount = evidenceIndex.total_files || evidenceIndex.files?.length || 0;
|
|
42
|
+
const totalSize = evidenceIndex.total_size || 0;
|
|
43
|
+
|
|
44
|
+
// Calculate coverage: evidence files vs total git-tracked files
|
|
45
|
+
let gitFileCount = 0;
|
|
46
|
+
try {
|
|
47
|
+
const gitFiles = execSync('git ls-files --exclude-standard', {
|
|
48
|
+
encoding: 'utf8',
|
|
49
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
50
|
+
cwd: path.dirname(rippDir)
|
|
51
|
+
});
|
|
52
|
+
gitFileCount = gitFiles
|
|
53
|
+
.trim()
|
|
54
|
+
.split('\n')
|
|
55
|
+
.filter(f => f.length > 0).length;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
// Not a git repo or git command failed, coverage unknown
|
|
58
|
+
gitFileCount = fileCount; // Assume 100% if git unavailable
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const coveragePercent = gitFileCount > 0 ? Math.round((fileCount / gitFileCount) * 100) : 0;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
status: 'built',
|
|
65
|
+
file_count: fileCount,
|
|
66
|
+
total_size: totalSize,
|
|
67
|
+
coverage_percent: coveragePercent,
|
|
68
|
+
last_build: evidenceIndex.timestamp || 'unknown'
|
|
69
|
+
};
|
|
70
|
+
} catch (error) {
|
|
71
|
+
return {
|
|
72
|
+
status: 'error',
|
|
73
|
+
file_count: 0,
|
|
74
|
+
total_size: 0,
|
|
75
|
+
coverage_percent: 0,
|
|
76
|
+
error: error.message
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Gather discovery metrics (AI-generated candidates)
|
|
83
|
+
*/
|
|
84
|
+
function gatherDiscoveryMetrics(rippDir) {
|
|
85
|
+
const candidatesPath = path.join(rippDir, 'intent.candidates.yaml');
|
|
86
|
+
|
|
87
|
+
if (!fs.existsSync(candidatesPath)) {
|
|
88
|
+
return {
|
|
89
|
+
status: 'not_run',
|
|
90
|
+
candidate_count: 0
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const yaml = require('js-yaml');
|
|
96
|
+
const candidatesContent = fs.readFileSync(candidatesPath, 'utf8');
|
|
97
|
+
const candidates = yaml.load(candidatesContent);
|
|
98
|
+
|
|
99
|
+
const candidateCount = candidates.candidates?.length || 0;
|
|
100
|
+
|
|
101
|
+
// Calculate average confidence if available
|
|
102
|
+
let avgConfidence = null;
|
|
103
|
+
if (candidates.candidates && candidateCount > 0) {
|
|
104
|
+
const confidences = candidates.candidates
|
|
105
|
+
.map(c => c.confidence)
|
|
106
|
+
.filter(conf => typeof conf === 'number' && conf >= 0 && conf <= 1);
|
|
107
|
+
|
|
108
|
+
if (confidences.length > 0) {
|
|
109
|
+
avgConfidence = confidences.reduce((sum, c) => sum + c, 0) / confidences.length;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Quality score (simple heuristic: avg confidence * candidate count normalization)
|
|
114
|
+
const qualityScore =
|
|
115
|
+
avgConfidence !== null
|
|
116
|
+
? Math.round(avgConfidence * Math.min(candidateCount / 3, 1) * 100)
|
|
117
|
+
: null;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
status: 'completed',
|
|
121
|
+
candidate_count: candidateCount,
|
|
122
|
+
avg_confidence: avgConfidence !== null ? Math.round(avgConfidence * 100) / 100 : null,
|
|
123
|
+
quality_score: qualityScore,
|
|
124
|
+
model: candidates.metadata?.model || 'unknown'
|
|
125
|
+
};
|
|
126
|
+
} catch (error) {
|
|
127
|
+
return {
|
|
128
|
+
status: 'error',
|
|
129
|
+
candidate_count: 0,
|
|
130
|
+
error: error.message
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Gather validation metrics
|
|
137
|
+
*/
|
|
138
|
+
function gatherValidationMetrics(rippDir) {
|
|
139
|
+
// Look for canonical handoff packet
|
|
140
|
+
const handoffPath = path.join(rippDir, 'handoff.ripp.yaml');
|
|
141
|
+
|
|
142
|
+
if (!fs.existsSync(handoffPath)) {
|
|
143
|
+
return {
|
|
144
|
+
status: 'not_validated',
|
|
145
|
+
last_run: null
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
// Get file mtime as proxy for last validation
|
|
151
|
+
const stats = fs.statSync(handoffPath);
|
|
152
|
+
const lastRun = stats.mtime.toISOString();
|
|
153
|
+
|
|
154
|
+
// Attempt basic validation check (packet must be parseable YAML with ripp_version)
|
|
155
|
+
const yaml = require('js-yaml');
|
|
156
|
+
const packetContent = fs.readFileSync(handoffPath, 'utf8');
|
|
157
|
+
const packet = yaml.load(packetContent);
|
|
158
|
+
|
|
159
|
+
const isValid = packet && packet.ripp_version === '1.0' && packet.packet_id && packet.level;
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
status: isValid ? 'pass' : 'fail',
|
|
163
|
+
last_run: lastRun,
|
|
164
|
+
level: packet.level || null
|
|
165
|
+
};
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return {
|
|
168
|
+
status: 'fail',
|
|
169
|
+
last_run: null,
|
|
170
|
+
error: error.message
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Gather workflow completion metrics
|
|
177
|
+
*/
|
|
178
|
+
function gatherWorkflowMetrics(rippDir) {
|
|
179
|
+
// Define expected artifacts for each workflow step
|
|
180
|
+
const steps = {
|
|
181
|
+
initialized: fs.existsSync(path.join(rippDir, 'config.yaml')),
|
|
182
|
+
evidence_built: fs.existsSync(path.join(rippDir, 'evidence', 'evidence.index.json')),
|
|
183
|
+
discovery_run: fs.existsSync(path.join(rippDir, 'intent.candidates.yaml')),
|
|
184
|
+
checklist_generated: fs.existsSync(path.join(rippDir, 'intent.checklist.md')),
|
|
185
|
+
artifacts_built: fs.existsSync(path.join(rippDir, 'handoff.ripp.yaml'))
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const completedSteps = Object.values(steps).filter(Boolean).length;
|
|
189
|
+
const totalSteps = Object.keys(steps).length;
|
|
190
|
+
const completionPercent = Math.round((completedSteps / totalSteps) * 100);
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
completion_percent: completionPercent,
|
|
194
|
+
steps_completed: completedSteps,
|
|
195
|
+
steps_total: totalSteps,
|
|
196
|
+
steps: steps
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Format metrics as human-readable text
|
|
202
|
+
*/
|
|
203
|
+
function formatMetricsText(metrics) {
|
|
204
|
+
const lines = [];
|
|
205
|
+
|
|
206
|
+
lines.push('RIPP Workflow Metrics');
|
|
207
|
+
lines.push('='.repeat(60));
|
|
208
|
+
lines.push('');
|
|
209
|
+
|
|
210
|
+
// Evidence metrics
|
|
211
|
+
lines.push('Evidence Pack:');
|
|
212
|
+
if (metrics.evidence.status === 'built') {
|
|
213
|
+
lines.push(` Status: ✓ Built`);
|
|
214
|
+
lines.push(` Files: ${metrics.evidence.file_count}`);
|
|
215
|
+
lines.push(` Size: ${formatBytes(metrics.evidence.total_size)}`);
|
|
216
|
+
lines.push(` Coverage: ${metrics.evidence.coverage_percent}% of git-tracked files`);
|
|
217
|
+
lines.push(` Last Build: ${formatTimestamp(metrics.evidence.last_build)}`);
|
|
218
|
+
} else if (metrics.evidence.status === 'not_built') {
|
|
219
|
+
lines.push(` Status: ✗ Not built`);
|
|
220
|
+
lines.push(` Next Step: Run 'ripp evidence build'`);
|
|
221
|
+
} else {
|
|
222
|
+
lines.push(` Status: ✗ Error`);
|
|
223
|
+
lines.push(` Error: ${metrics.evidence.error || 'Unknown'}`);
|
|
224
|
+
}
|
|
225
|
+
lines.push('');
|
|
226
|
+
|
|
227
|
+
// Discovery metrics
|
|
228
|
+
lines.push('Intent Discovery:');
|
|
229
|
+
if (metrics.discovery.status === 'completed') {
|
|
230
|
+
lines.push(` Status: ✓ Completed`);
|
|
231
|
+
lines.push(` Candidates: ${metrics.discovery.candidate_count}`);
|
|
232
|
+
if (metrics.discovery.avg_confidence !== null) {
|
|
233
|
+
lines.push(` Avg Confidence: ${(metrics.discovery.avg_confidence * 100).toFixed(0)}%`);
|
|
234
|
+
}
|
|
235
|
+
if (metrics.discovery.quality_score !== null) {
|
|
236
|
+
lines.push(` Quality Score: ${metrics.discovery.quality_score}/100`);
|
|
237
|
+
}
|
|
238
|
+
lines.push(` Model: ${metrics.discovery.model}`);
|
|
239
|
+
} else if (metrics.discovery.status === 'not_run') {
|
|
240
|
+
lines.push(` Status: ✗ Not run`);
|
|
241
|
+
lines.push(` Next Step: Run 'ripp discover'`);
|
|
242
|
+
} else {
|
|
243
|
+
lines.push(` Status: ✗ Error`);
|
|
244
|
+
lines.push(` Error: ${metrics.discovery.error || 'Unknown'}`);
|
|
245
|
+
}
|
|
246
|
+
lines.push('');
|
|
247
|
+
|
|
248
|
+
// Validation metrics
|
|
249
|
+
lines.push('Validation:');
|
|
250
|
+
if (metrics.validation.status === 'pass') {
|
|
251
|
+
lines.push(` Status: ✓ Pass`);
|
|
252
|
+
lines.push(` Level: ${metrics.validation.level}`);
|
|
253
|
+
lines.push(` Last Run: ${formatTimestamp(metrics.validation.last_run)}`);
|
|
254
|
+
} else if (metrics.validation.status === 'fail') {
|
|
255
|
+
lines.push(` Status: ✗ Fail`);
|
|
256
|
+
if (metrics.validation.error) {
|
|
257
|
+
lines.push(` Error: ${metrics.validation.error}`);
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
lines.push(` Status: - Not validated`);
|
|
261
|
+
lines.push(` Next Step: Run 'ripp build' to create handoff packet`);
|
|
262
|
+
}
|
|
263
|
+
lines.push('');
|
|
264
|
+
|
|
265
|
+
// Workflow completion
|
|
266
|
+
lines.push('Workflow Progress:');
|
|
267
|
+
lines.push(
|
|
268
|
+
` Completion: ${metrics.workflow.completion_percent}% (${metrics.workflow.steps_completed}/${metrics.workflow.steps_total} steps)`
|
|
269
|
+
);
|
|
270
|
+
lines.push(` Steps:`);
|
|
271
|
+
lines.push(
|
|
272
|
+
` ${metrics.workflow.steps.initialized ? '✓' : '✗'} Initialized (.ripp/config.yaml)`
|
|
273
|
+
);
|
|
274
|
+
lines.push(` ${metrics.workflow.steps.evidence_built ? '✓' : '✗'} Evidence Built`);
|
|
275
|
+
lines.push(` ${metrics.workflow.steps.discovery_run ? '✓' : '✗'} Discovery Run`);
|
|
276
|
+
lines.push(` ${metrics.workflow.steps.checklist_generated ? '✓' : '✗'} Checklist Generated`);
|
|
277
|
+
lines.push(` ${metrics.workflow.steps.artifacts_built ? '✓' : '✗'} Artifacts Built`);
|
|
278
|
+
lines.push('');
|
|
279
|
+
|
|
280
|
+
lines.push('='.repeat(60));
|
|
281
|
+
lines.push(`Generated: ${formatTimestamp(metrics.timestamp)}`);
|
|
282
|
+
|
|
283
|
+
return lines.join('\n');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Format bytes as human-readable size
|
|
288
|
+
*/
|
|
289
|
+
function formatBytes(bytes) {
|
|
290
|
+
if (bytes === 0) return '0 B';
|
|
291
|
+
const k = 1024;
|
|
292
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
293
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
294
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Format ISO timestamp as human-readable
|
|
299
|
+
*/
|
|
300
|
+
function formatTimestamp(timestamp) {
|
|
301
|
+
if (!timestamp || timestamp === 'unknown') return 'Unknown';
|
|
302
|
+
try {
|
|
303
|
+
const date = new Date(timestamp);
|
|
304
|
+
return date.toLocaleString();
|
|
305
|
+
} catch (error) {
|
|
306
|
+
return timestamp;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Load metrics history from .ripp/metrics-history.json
|
|
312
|
+
*/
|
|
313
|
+
function loadMetricsHistory(rippDir) {
|
|
314
|
+
const historyPath = path.join(rippDir, 'metrics-history.json');
|
|
315
|
+
|
|
316
|
+
if (!fs.existsSync(historyPath)) {
|
|
317
|
+
return [];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
const historyContent = fs.readFileSync(historyPath, 'utf8');
|
|
322
|
+
return JSON.parse(historyContent);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.error(`Warning: Could not load metrics history: ${error.message}`);
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Save metrics to history
|
|
331
|
+
*/
|
|
332
|
+
function saveMetricsHistory(rippDir, metrics) {
|
|
333
|
+
const historyPath = path.join(rippDir, 'metrics-history.json');
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const history = loadMetricsHistory(rippDir);
|
|
337
|
+
|
|
338
|
+
// Add current metrics to history (keep last 50 entries)
|
|
339
|
+
history.push({
|
|
340
|
+
timestamp: metrics.timestamp,
|
|
341
|
+
evidence: metrics.evidence,
|
|
342
|
+
discovery: metrics.discovery,
|
|
343
|
+
validation: metrics.validation,
|
|
344
|
+
workflow: metrics.workflow
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const trimmedHistory = history.slice(-50);
|
|
348
|
+
|
|
349
|
+
fs.writeFileSync(historyPath, JSON.stringify(trimmedHistory, null, 2), 'utf8');
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error(`Warning: Could not save metrics history: ${error.message}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Format metrics history as text
|
|
357
|
+
*/
|
|
358
|
+
function formatMetricsHistory(history) {
|
|
359
|
+
if (history.length === 0) {
|
|
360
|
+
return 'No metrics history available. Run `ripp metrics --report` multiple times to build history.';
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const lines = [];
|
|
364
|
+
lines.push('RIPP Metrics History');
|
|
365
|
+
lines.push('='.repeat(60));
|
|
366
|
+
lines.push('');
|
|
367
|
+
|
|
368
|
+
// Show trends for key metrics
|
|
369
|
+
const recent = history.slice(-10); // Last 10 entries
|
|
370
|
+
|
|
371
|
+
lines.push('Recent Trends (last 10 runs):');
|
|
372
|
+
lines.push('');
|
|
373
|
+
|
|
374
|
+
// Evidence coverage trend
|
|
375
|
+
lines.push('Evidence Coverage:');
|
|
376
|
+
recent.forEach((entry, idx) => {
|
|
377
|
+
const coverage = entry.evidence?.coverage_percent || 0;
|
|
378
|
+
const bar = '█'.repeat(Math.round(coverage / 5));
|
|
379
|
+
lines.push(` ${formatTimestamp(entry.timestamp).padEnd(25)} ${bar} ${coverage}%`);
|
|
380
|
+
});
|
|
381
|
+
lines.push('');
|
|
382
|
+
|
|
383
|
+
// Discovery quality trend
|
|
384
|
+
lines.push('Discovery Quality Score:');
|
|
385
|
+
recent.forEach((entry, idx) => {
|
|
386
|
+
const quality = entry.discovery?.quality_score || 0;
|
|
387
|
+
const bar = '█'.repeat(Math.round(quality / 5));
|
|
388
|
+
lines.push(` ${formatTimestamp(entry.timestamp).padEnd(25)} ${bar} ${quality}/100`);
|
|
389
|
+
});
|
|
390
|
+
lines.push('');
|
|
391
|
+
|
|
392
|
+
// Workflow completion trend
|
|
393
|
+
lines.push('Workflow Completion:');
|
|
394
|
+
recent.forEach((entry, idx) => {
|
|
395
|
+
const completion = entry.workflow?.completion_percent || 0;
|
|
396
|
+
const bar = '█'.repeat(Math.round(completion / 5));
|
|
397
|
+
lines.push(` ${formatTimestamp(entry.timestamp).padEnd(25)} ${bar} ${completion}%`);
|
|
398
|
+
});
|
|
399
|
+
lines.push('');
|
|
400
|
+
|
|
401
|
+
return lines.join('\n');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
module.exports = {
|
|
405
|
+
gatherMetrics,
|
|
406
|
+
formatMetricsText,
|
|
407
|
+
loadMetricsHistory,
|
|
408
|
+
saveMetricsHistory,
|
|
409
|
+
formatMetricsHistory
|
|
410
|
+
};
|
package/lib/packager.js
CHANGED
|
@@ -150,28 +150,40 @@ function formatAsYaml(packaged, options = {}) {
|
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
152
|
* Format packaged packet as Markdown
|
|
153
|
+
* @param {Object} packaged - Packaged RIPP packet
|
|
154
|
+
* @param {Object} options - Formatting options
|
|
155
|
+
* @param {boolean} options.single - Generate consolidated single-file format
|
|
153
156
|
*/
|
|
154
157
|
function formatAsMarkdown(packaged, options = {}) {
|
|
158
|
+
const isSingle = options.single || false;
|
|
155
159
|
let md = '';
|
|
156
160
|
|
|
157
161
|
// Header
|
|
158
162
|
md += `# ${packaged.title}\n\n`;
|
|
159
|
-
md += `**Packet ID**: \`${packaged.packet_id}\` \n`;
|
|
160
|
-
md += `**Level**: ${packaged.level} \n`;
|
|
161
|
-
md += `**Status**: ${packaged.status} \n`;
|
|
162
|
-
md += `**Created**: ${packaged.created} \n`;
|
|
163
|
-
md += `**Updated**: ${packaged.updated} \n`;
|
|
164
|
-
|
|
165
|
-
if (packaged.version) {
|
|
166
|
-
md += `**Version**: ${packaged.version} \n`;
|
|
167
|
-
}
|
|
168
163
|
|
|
169
|
-
|
|
164
|
+
if (isSingle) {
|
|
165
|
+
// Consolidated single-file format (more concise, optimized for AI consumption)
|
|
166
|
+
md += `> **RIPP Handoff Document**\n`;
|
|
167
|
+
md += `> Packet ID: \`${packaged.packet_id}\` | Level: ${packaged.level} | Status: ${packaged.status}\n\n`;
|
|
168
|
+
} else {
|
|
169
|
+
// Standard format with full metadata
|
|
170
|
+
md += `**Packet ID**: \`${packaged.packet_id}\` \n`;
|
|
171
|
+
md += `**Level**: ${packaged.level} \n`;
|
|
172
|
+
md += `**Status**: ${packaged.status} \n`;
|
|
173
|
+
md += `**Created**: ${packaged.created} \n`;
|
|
174
|
+
md += `**Updated**: ${packaged.updated} \n`;
|
|
175
|
+
|
|
176
|
+
if (packaged.version) {
|
|
177
|
+
md += `**Version**: ${packaged.version} \n`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
md += '\n---\n\n';
|
|
170
181
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
182
|
+
// Packaging metadata (omit in single-file mode for brevity)
|
|
183
|
+
md += '## Packaging Information\n\n';
|
|
184
|
+
md += `This document was packaged by \`${packaged._meta.packaged_by}\` on ${packaged._meta.packaged_at}.\n\n`;
|
|
185
|
+
md += '---\n\n';
|
|
186
|
+
}
|
|
175
187
|
|
|
176
188
|
// Purpose
|
|
177
189
|
md += '## Purpose\n\n';
|
package/package.json
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ripp-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Official CLI validator for Regenerative Intent Prompting Protocol (RIPP)",
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "Official CLI validator and tooling for Regenerative Intent Prompting Protocol (RIPP)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"ripp": "
|
|
7
|
+
"ripp": "index.js"
|
|
8
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"lib/",
|
|
12
|
+
"schema/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"CHANGELOG.md"
|
|
15
|
+
],
|
|
9
16
|
"scripts": {
|
|
10
|
-
"test": "
|
|
17
|
+
"test": "node test/checklist-parser.test.js && node test/metrics.test.js && node test/doctor.test.js && node test/package.test.js && node test/integration.test.js"
|
|
11
18
|
},
|
|
12
19
|
"keywords": [
|
|
13
20
|
"ripp",
|
|
@@ -21,7 +28,7 @@
|
|
|
21
28
|
"license": "MIT",
|
|
22
29
|
"repository": {
|
|
23
30
|
"type": "git",
|
|
24
|
-
"url": "https://github.com/Dylan-Natter/ripp-protocol.git",
|
|
31
|
+
"url": "git+https://github.com/Dylan-Natter/ripp-protocol.git",
|
|
25
32
|
"directory": "tools/ripp-cli"
|
|
26
33
|
},
|
|
27
34
|
"bugs": {
|
|
@@ -30,11 +37,21 @@
|
|
|
30
37
|
"homepage": "https://dylan-natter.github.io/ripp-protocol",
|
|
31
38
|
"dependencies": {
|
|
32
39
|
"ajv": "^8.12.0",
|
|
33
|
-
"ajv-formats": "^
|
|
34
|
-
"glob": "^
|
|
40
|
+
"ajv-formats": "^3.0.1",
|
|
41
|
+
"glob": "^13.0.0",
|
|
35
42
|
"js-yaml": "^4.1.0"
|
|
36
43
|
},
|
|
37
44
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
45
|
+
"node": ">=20.0.0"
|
|
46
|
+
},
|
|
47
|
+
"pkg": {
|
|
48
|
+
"assets": [
|
|
49
|
+
"../../schema/**/*"
|
|
50
|
+
],
|
|
51
|
+
"targets": [
|
|
52
|
+
"node20-macos-arm64",
|
|
53
|
+
"node20-macos-x64"
|
|
54
|
+
],
|
|
55
|
+
"outputPath": "dist"
|
|
39
56
|
}
|
|
40
57
|
}
|