ripp-cli 1.0.0 → 1.0.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 +28 -0
- package/README.md +57 -0
- package/lib/evidence.js +210 -7
- package/package.json +16 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the RIPP CLI will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.1] - 2025-12-22
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Version bump to enable npm publishing of updates
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Synchronized package-lock.json Node.js engine requirement (>=20.0.0) with package.json
|
|
17
|
+
|
|
18
|
+
## [1.0.0] - Initial Release
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
- RIPP packet validation against JSON schema
|
|
23
|
+
- Support for YAML and JSON input formats
|
|
24
|
+
- Multi-file validation with glob patterns
|
|
25
|
+
- Linting with severity levels (error, warning, info)
|
|
26
|
+
- Strict mode for enhanced validation
|
|
27
|
+
- Evidence package generation for handoffs
|
|
28
|
+
- Portable binary distributions for macOS (ARM64 and x64)
|
package/README.md
CHANGED
|
@@ -274,6 +274,63 @@ npm link
|
|
|
274
274
|
ripp validate ../../examples/
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
+
## Publishing
|
|
278
|
+
|
|
279
|
+
### Prerequisites
|
|
280
|
+
|
|
281
|
+
To publish `ripp-cli` to npm, you need:
|
|
282
|
+
|
|
283
|
+
1. **npm Account**: A verified npm account with appropriate permissions
|
|
284
|
+
2. **NPM_TOKEN Secret**: Configured in GitHub repository secrets
|
|
285
|
+
|
|
286
|
+
### Setting Up NPM_TOKEN
|
|
287
|
+
|
|
288
|
+
The publishing workflow requires an npm **Granular Access Token** with specific permissions:
|
|
289
|
+
|
|
290
|
+
1. Log in to [npmjs.com](https://www.npmjs.com)
|
|
291
|
+
2. Go to **Access Tokens** → **Generate New Token** → **Granular Access Token**
|
|
292
|
+
3. Configure the token:
|
|
293
|
+
- **Permissions**: Select "Read and write" for packages
|
|
294
|
+
- **Packages and scopes**: Select "All packages" or specific packages
|
|
295
|
+
- **Organizations**: (if applicable) Select relevant organizations
|
|
296
|
+
- **Expiration**: Set appropriate expiration date
|
|
297
|
+
- **Bypass 2FA**: ✅ **MUST be enabled** for CI/CD automation
|
|
298
|
+
4. Copy the generated token
|
|
299
|
+
5. Add it to GitHub repository secrets:
|
|
300
|
+
- Go to repository **Settings** → **Secrets and variables** → **Actions**
|
|
301
|
+
- Click **New repository secret**
|
|
302
|
+
- Name: `NPM_TOKEN`
|
|
303
|
+
- Value: (paste your token)
|
|
304
|
+
|
|
305
|
+
**Important**: The token MUST have "Bypass 2FA requirement" enabled. Standard automation tokens may fail with E403 errors if 2FA is enabled on your npm account.
|
|
306
|
+
|
|
307
|
+
### Publishing Process
|
|
308
|
+
|
|
309
|
+
The package is published via the GitHub Actions workflow:
|
|
310
|
+
|
|
311
|
+
1. Go to **Actions** → **Publish NPM Package**
|
|
312
|
+
2. Click **Run workflow**
|
|
313
|
+
3. Configure options:
|
|
314
|
+
- **dry_run**: `true` (test) or `false` (publish)
|
|
315
|
+
- **tag**: `latest`, `next`, or `beta`
|
|
316
|
+
- **package_path**: (default: `tools/ripp-cli`)
|
|
317
|
+
4. Click **Run workflow**
|
|
318
|
+
|
|
319
|
+
**Workflow Features**:
|
|
320
|
+
|
|
321
|
+
- ✅ Validates package before publishing
|
|
322
|
+
- ✅ Checks version isn't already published
|
|
323
|
+
- ✅ Verifies npm authentication
|
|
324
|
+
- ✅ Runs tests and linting
|
|
325
|
+
- ✅ Dry-run mode for safe testing
|
|
326
|
+
- ✅ Detailed job summaries
|
|
327
|
+
|
|
328
|
+
**Version Management**:
|
|
329
|
+
|
|
330
|
+
- Bump version in `package.json` before publishing
|
|
331
|
+
- Follow [Semantic Versioning](https://semver.org/)
|
|
332
|
+
- Workflow will reject if version already exists
|
|
333
|
+
|
|
277
334
|
## Dependencies
|
|
278
335
|
|
|
279
336
|
- **ajv**: JSON Schema validator
|
package/lib/evidence.js
CHANGED
|
@@ -74,14 +74,15 @@ async function buildEvidencePack(cwd, config) {
|
|
|
74
74
|
*/
|
|
75
75
|
async function scanFiles(cwd, evidenceConfig) {
|
|
76
76
|
const files = [];
|
|
77
|
+
let excludedCount = 0;
|
|
77
78
|
const { includeGlobs, excludeGlobs, maxFileSize } = evidenceConfig;
|
|
78
79
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
for (const pattern of patterns) {
|
|
80
|
+
// Use glob with cwd option instead of joining paths
|
|
81
|
+
// This ensures patterns work correctly on all platforms
|
|
82
|
+
for (const pattern of includeGlobs) {
|
|
83
83
|
const matches = await glob(pattern, {
|
|
84
|
-
|
|
84
|
+
cwd: cwd,
|
|
85
|
+
ignore: excludeGlobs,
|
|
85
86
|
nodir: true,
|
|
86
87
|
absolute: true
|
|
87
88
|
});
|
|
@@ -92,6 +93,7 @@ async function scanFiles(cwd, evidenceConfig) {
|
|
|
92
93
|
|
|
93
94
|
// Skip files that are too large
|
|
94
95
|
if (stats.size > maxFileSize) {
|
|
96
|
+
excludedCount++;
|
|
95
97
|
continue;
|
|
96
98
|
}
|
|
97
99
|
|
|
@@ -109,12 +111,13 @@ async function scanFiles(cwd, evidenceConfig) {
|
|
|
109
111
|
});
|
|
110
112
|
} catch (error) {
|
|
111
113
|
// Skip files we can't read
|
|
114
|
+
excludedCount++;
|
|
112
115
|
continue;
|
|
113
116
|
}
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
return files;
|
|
120
|
+
return { files, excludedCount };
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
/**
|
|
@@ -151,7 +154,10 @@ async function extractEvidence(files, cwd) {
|
|
|
151
154
|
routes: [],
|
|
152
155
|
schemas: [],
|
|
153
156
|
auth: [],
|
|
154
|
-
workflows: []
|
|
157
|
+
workflows: [],
|
|
158
|
+
projectType: detectProjectType(files),
|
|
159
|
+
keyInsights: extractKeyInsights(files, cwd),
|
|
160
|
+
codeSnippets: extractKeyCodeSnippets(files)
|
|
155
161
|
};
|
|
156
162
|
|
|
157
163
|
for (const file of files) {
|
|
@@ -362,6 +368,203 @@ function redactSecrets(text) {
|
|
|
362
368
|
return redacted;
|
|
363
369
|
}
|
|
364
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Detect project type from evidence
|
|
373
|
+
*/
|
|
374
|
+
function detectProjectType(files) {
|
|
375
|
+
const indicators = {
|
|
376
|
+
cli: 0,
|
|
377
|
+
webApp: 0,
|
|
378
|
+
api: 0,
|
|
379
|
+
library: 0,
|
|
380
|
+
protocol: 0
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
for (const file of files) {
|
|
384
|
+
const content = file.content ? file.content.toLowerCase() : '';
|
|
385
|
+
const path = file.path.toLowerCase();
|
|
386
|
+
|
|
387
|
+
// CLI tool indicators
|
|
388
|
+
if (path.includes('/bin/') || path.includes('cli') || path.includes('command'))
|
|
389
|
+
indicators.cli += 3;
|
|
390
|
+
if (
|
|
391
|
+
content.includes('commander') ||
|
|
392
|
+
content.includes('yargs') ||
|
|
393
|
+
content.includes('process.argv')
|
|
394
|
+
)
|
|
395
|
+
indicators.cli += 2;
|
|
396
|
+
if (path === 'package.json' && content.includes('"bin"')) indicators.cli += 4;
|
|
397
|
+
|
|
398
|
+
// Web app indicators
|
|
399
|
+
if (path.includes('app/') || path.includes('pages/') || path.includes('components/'))
|
|
400
|
+
indicators.webApp += 3;
|
|
401
|
+
if (content.includes('react') || content.includes('vue') || content.includes('angular'))
|
|
402
|
+
indicators.webApp += 2;
|
|
403
|
+
if (path.includes('index.html') || path.includes('app.tsx')) indicators.webApp += 3;
|
|
404
|
+
|
|
405
|
+
// API indicators
|
|
406
|
+
if (path.includes('api/') || path.includes('routes/') || path.includes('controllers/'))
|
|
407
|
+
indicators.api += 3;
|
|
408
|
+
if (content.includes('express') || content.includes('fastify') || content.includes('koa'))
|
|
409
|
+
indicators.api += 2;
|
|
410
|
+
if (content.includes('@app.route') || content.includes('@route') || content.includes('router.'))
|
|
411
|
+
indicators.api += 2;
|
|
412
|
+
|
|
413
|
+
// Library indicators
|
|
414
|
+
if (path === 'package.json' && !content.includes('"bin"') && !content.includes('"scripts"'))
|
|
415
|
+
indicators.library += 2;
|
|
416
|
+
if (path.includes('lib/') || path.includes('src/index')) indicators.library += 1;
|
|
417
|
+
|
|
418
|
+
// Protocol/spec indicators
|
|
419
|
+
if (path.includes('spec.md') || path.includes('protocol') || path.includes('rfc'))
|
|
420
|
+
indicators.protocol += 4;
|
|
421
|
+
if (path.includes('schema/') && path.includes('.json')) indicators.protocol += 2;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Return type with highest score
|
|
425
|
+
const sorted = Object.entries(indicators).sort((a, b) => b[1] - a[1]);
|
|
426
|
+
return {
|
|
427
|
+
primary: sorted[0][0],
|
|
428
|
+
secondary: sorted[1][1] > 0 ? sorted[1][0] : null,
|
|
429
|
+
confidence: sorted[0][1] > 5 ? 'high' : sorted[0][1] > 2 ? 'medium' : 'low',
|
|
430
|
+
scores: indicators
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Extract key insights from README, package.json, and main files
|
|
436
|
+
*/
|
|
437
|
+
function extractKeyInsights(files, cwd) {
|
|
438
|
+
const insights = {
|
|
439
|
+
purpose: null,
|
|
440
|
+
description: null,
|
|
441
|
+
mainFeatures: [],
|
|
442
|
+
architecture: null
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
for (const file of files) {
|
|
446
|
+
const path = file.path.toLowerCase();
|
|
447
|
+
const content = file.content || '';
|
|
448
|
+
|
|
449
|
+
// Extract from README
|
|
450
|
+
if (path.includes('readme.md') || path.includes('readme.txt')) {
|
|
451
|
+
// Extract first paragraph as description
|
|
452
|
+
const lines = content.split('\n');
|
|
453
|
+
let desc = '';
|
|
454
|
+
for (const line of lines) {
|
|
455
|
+
if (line.trim() && !line.startsWith('#') && !line.startsWith('[')) {
|
|
456
|
+
desc += line.trim() + ' ';
|
|
457
|
+
if (desc.length > 200) break;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (desc) insights.description = desc.slice(0, 300);
|
|
461
|
+
|
|
462
|
+
// Extract features (look for bullet points or numbered lists)
|
|
463
|
+
const featureMatch = content.match(
|
|
464
|
+
/(?:features|capabilities|includes)[\s\S]{0,50}?\n((?:[-*]\s.+\n)+)/i
|
|
465
|
+
);
|
|
466
|
+
if (featureMatch) {
|
|
467
|
+
insights.mainFeatures = featureMatch[1]
|
|
468
|
+
.split('\n')
|
|
469
|
+
.map(f => f.replace(/^[-*]\s+/, '').trim())
|
|
470
|
+
.filter(f => f.length > 0)
|
|
471
|
+
.slice(0, 5);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Extract from package.json
|
|
476
|
+
if (path === 'package.json') {
|
|
477
|
+
try {
|
|
478
|
+
const pkg = JSON.parse(content);
|
|
479
|
+
if (pkg.description && !insights.description) {
|
|
480
|
+
insights.description = pkg.description;
|
|
481
|
+
}
|
|
482
|
+
if (pkg.name && !insights.purpose) {
|
|
483
|
+
insights.purpose = `${pkg.name}: ${pkg.description || 'No description'}`;
|
|
484
|
+
}
|
|
485
|
+
} catch (e) {
|
|
486
|
+
// Ignore JSON parse errors
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Extract from SPEC files
|
|
491
|
+
if (path.includes('spec.md') || path.includes('architecture.md')) {
|
|
492
|
+
const purposeMatch = content.match(/(?:purpose|goal|objective)[\s:]+([^\n]{50,300})/i);
|
|
493
|
+
if (purposeMatch && !insights.purpose) {
|
|
494
|
+
insights.purpose = purposeMatch[1].trim();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return insights;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Extract key code snippets (functions, classes, exports)
|
|
504
|
+
*/
|
|
505
|
+
function extractKeyCodeSnippets(files) {
|
|
506
|
+
const snippets = [];
|
|
507
|
+
let count = 0;
|
|
508
|
+
const maxSnippets = 15;
|
|
509
|
+
|
|
510
|
+
for (const file of files) {
|
|
511
|
+
if (count >= maxSnippets) break;
|
|
512
|
+
if (!file.content || file.type !== 'source') continue;
|
|
513
|
+
|
|
514
|
+
const content = file.content;
|
|
515
|
+
const path = file.path;
|
|
516
|
+
|
|
517
|
+
// Extract function definitions (JavaScript/TypeScript)
|
|
518
|
+
const funcMatches = content.matchAll(
|
|
519
|
+
/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)\s*{([^}]{0,200})/g
|
|
520
|
+
);
|
|
521
|
+
for (const match of funcMatches) {
|
|
522
|
+
if (count >= maxSnippets) break;
|
|
523
|
+
snippets.push({
|
|
524
|
+
file: path,
|
|
525
|
+
type: 'function',
|
|
526
|
+
name: match[1],
|
|
527
|
+
snippet: match[0].substring(0, 150)
|
|
528
|
+
});
|
|
529
|
+
count++;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Extract class definitions
|
|
533
|
+
const classMatches = content.matchAll(
|
|
534
|
+
/(?:export\s+)?class\s+(\w+)(?:\s+extends\s+\w+)?\s*{([^}]{0,150})/g
|
|
535
|
+
);
|
|
536
|
+
for (const match of classMatches) {
|
|
537
|
+
if (count >= maxSnippets) break;
|
|
538
|
+
snippets.push({
|
|
539
|
+
file: path,
|
|
540
|
+
type: 'class',
|
|
541
|
+
name: match[1],
|
|
542
|
+
snippet: match[0].substring(0, 150)
|
|
543
|
+
});
|
|
544
|
+
count++;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Extract key comments (JSDoc, purpose statements)
|
|
548
|
+
const commentMatches = content.matchAll(/\/\*\*\s*\n\s*\*\s*([^\n]{30,200})/g);
|
|
549
|
+
for (const match of commentMatches) {
|
|
550
|
+
if (count >= maxSnippets) break;
|
|
551
|
+
if (
|
|
552
|
+
match[1].toLowerCase().includes('purpose') ||
|
|
553
|
+
match[1].toLowerCase().includes('description')
|
|
554
|
+
) {
|
|
555
|
+
snippets.push({
|
|
556
|
+
file: path,
|
|
557
|
+
type: 'comment',
|
|
558
|
+
snippet: match[1].trim()
|
|
559
|
+
});
|
|
560
|
+
count++;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return snippets.slice(0, maxSnippets);
|
|
566
|
+
}
|
|
567
|
+
|
|
365
568
|
module.exports = {
|
|
366
569
|
buildEvidencePack,
|
|
367
570
|
redactSecrets
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ripp-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Official CLI validator for Regenerative Intent Prompting Protocol (RIPP)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"ripp": "
|
|
7
|
+
"ripp": "index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"test": "echo \"Warning: No tests specified\" && exit 0"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
|
24
|
-
"url": "https://github.com/Dylan-Natter/ripp-protocol.git",
|
|
24
|
+
"url": "git+https://github.com/Dylan-Natter/ripp-protocol.git",
|
|
25
25
|
"directory": "tools/ripp-cli"
|
|
26
26
|
},
|
|
27
27
|
"bugs": {
|
|
@@ -30,11 +30,21 @@
|
|
|
30
30
|
"homepage": "https://dylan-natter.github.io/ripp-protocol",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"ajv": "^8.12.0",
|
|
33
|
-
"ajv-formats": "^
|
|
34
|
-
"glob": "^
|
|
33
|
+
"ajv-formats": "^3.0.1",
|
|
34
|
+
"glob": "^13.0.0",
|
|
35
35
|
"js-yaml": "^4.1.0"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
38
|
+
"node": ">=20.0.0"
|
|
39
|
+
},
|
|
40
|
+
"pkg": {
|
|
41
|
+
"assets": [
|
|
42
|
+
"../../schema/**/*"
|
|
43
|
+
],
|
|
44
|
+
"targets": [
|
|
45
|
+
"node20-macos-arm64",
|
|
46
|
+
"node20-macos-x64"
|
|
47
|
+
],
|
|
48
|
+
"outputPath": "dist"
|
|
39
49
|
}
|
|
40
50
|
}
|