Skip to content

Commit 291eae6

Browse files
authored
Merge pull request #10 from indexzero/vnext
(pre-release) vlurp@2.0.0
2 parents c5f11b7 + 4cfc728 commit 291eae6

28 files changed

Lines changed: 3144 additions & 364 deletions

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,47 @@ vlurp whoever/cool-configs --filter "lib/**" --filter "doc/**"
4545

4646
# vlurp only markdown files
4747
vlurp user/repo --filter "*.md"
48+
49+
# Use a preset instead of manual filters
50+
vlurp user/repo --preset claude
51+
vlurp user/repo --preset skills
52+
53+
# Auto-detect repo structure and apply appropriate filters
54+
vlurp user/repo --auto
55+
56+
# Preview what would be fetched
57+
vlurp user/repo --dry-run
58+
59+
# Process a .vlurpfile
60+
vlurp batch .vlurpfile
61+
vlurp batch .vlurpfile --dry-run
62+
```
63+
64+
## Presets
65+
66+
| Preset | Description | Filters |
67+
|--------|-------------|---------|
68+
| `claude` | Claude Code config | `.claude/**`, `CLAUDE.md` |
69+
| `skills` | Agent skills | `skills/**`, `SKILL.md`, `**/*.md` |
70+
| `agents` | Agent definitions | `agents/**`, `commands/**`, `**/*.md` |
71+
| `docs` | Documentation | `**/*.md` (excluding boilerplate) |
72+
| `all-md` | All markdown | `**/*.md` |
73+
| `minimal` | Minimal claude | `.claude/**`, `CLAUDE.md` only |
74+
75+
## Batch Processing
76+
77+
Process multiple repos from a `.vlurpfile`:
78+
79+
```bash
80+
vlurp batch .vlurpfile
81+
```
82+
83+
Example `.vlurpfile`:
84+
```bash
85+
# Comments start with #
86+
vlurp anthropics/skills -d ./vlurp --preset skills
87+
vlurp obra/superpowers -d ./vlurp --preset claude
88+
vlurp user/repo -d ./vlurp --filter "docs/**"
4889
```
4990

5091
## Features

src/cli.js

Lines changed: 173 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,37 @@
11
import process from 'node:process';
2-
import { jack } from 'jackspeak';
3-
import { render } from 'ink';
2+
import {jack} from 'jackspeak';
3+
import {render} from 'ink';
44
import React from 'react';
5-
import { FetchCommand } from './commands/fetch.js';
5+
import {FetchCommand} from './commands/fetch.js';
6+
import {BatchCommand} from './commands/batch.js';
7+
import {VerifyCommand} from './commands/verify.js';
8+
import {PinCommand} from './commands/pin.js';
9+
import {OutdatedCommand} from './commands/outdated.js';
10+
import {DiffCommand} from './commands/diff.js';
11+
import {ScanCommand} from './commands/scan.js';
12+
import {CatalogCommand} from './commands/catalog.js';
13+
import {PRESETS} from './presets.js';
614

715
const j = jack({
8-
usage: 'vlurp [source] [options]'
16+
usage: 'vlurp [command] [source] [options]'
917
})
1018
.description('A fun CLI tool to quickly fetch GitHub repositories and gists')
1119
.opt({
1220
d: {
1321
description: 'Root directory for vlurping',
1422
short: 'd'
23+
},
24+
preset: {
25+
description: 'Use a preset filter configuration (claude, skills, agents, docs, all-md, minimal)',
26+
hint: 'name'
27+
},
28+
ref: {
29+
description: 'Pin fetch to a specific git ref (commit SHA, tag, or branch)',
30+
hint: 'sha|tag|branch'
31+
},
32+
as: {
33+
description: 'Override output directory name (flatten path structure)',
34+
hint: 'name'
1535
}
1636
})
1737
.optList({
@@ -25,28 +45,73 @@ const j = jack({
2545
description: 'Show this help message',
2646
short: 'h'
2747
},
48+
auto: {
49+
description: 'Auto-detect repository structure and apply appropriate filters',
50+
short: 'a'
51+
},
52+
'dry-run': {
53+
description: 'Preview what would be fetched without actually fetching',
54+
short: 'n'
55+
},
56+
quiet: {
57+
description: 'Suppress non-essential output',
58+
short: 'q'
59+
},
2860
force: {
2961
description: 'Force overwrite existing directories without prompting',
3062
short: 'f'
3163
}
3264
});
3365

34-
const { values, positionals } = j.parse();
66+
const {values, positionals} = j.parse();
3567

3668
if (values.help) {
3769
console.log(`vlurp - A fun CLI tool to quickly fetch GitHub repositories and gists
3870
71+
Commands:
72+
vlurp <source> Fetch a single repository
73+
vlurp batch <file> Process a .vlurpfile
74+
vlurp verify <path> Verify file integrity against lineage records
75+
vlurp pin [source] Pin unpinned sources to current upstream HEAD
76+
vlurp outdated [file] Check for upstream changes
77+
vlurp diff <source> Show content diff against upstream
78+
vlurp scan <path> Analyze content for injection/escalation patterns
79+
vlurp catalog <path> Generate catalog.json of indexed skills
80+
3981
Usage:
40-
vlurp <user>/<repo> Fetch to ./<user>/<repo>
41-
vlurp <user>/<repo> -d <root> Fetch to <root>/<user>/<repo>
42-
vlurp <url> Fetch GitHub/Gist URL to ./<user>/<repo>
43-
vlurp <url> -d <root> Fetch to <root>/<user>/<repo>
82+
vlurp <user>/<repo> Fetch to ./<user>/<repo>
83+
vlurp <user>/<repo> --ref <sha> Fetch pinned to commit
84+
vlurp <user>/<repo> --as <name> -d ./skills Fetch to ./skills/<name>
85+
vlurp <url> -d <root> Fetch to <root>/<user>/<repo>
86+
vlurp batch <vlurpfile> Process batch file
87+
vlurp verify ./skills Check files against .vlurp.jsonl
88+
vlurp pin Pin all unpinned in .vlurpfile
89+
vlurp outdated .vlurpfile Check what has changed upstream
90+
vlurp diff user/repo -d ./skills Show diff against upstream
91+
92+
Presets:
93+
${Object.entries(PRESETS).map(([name, config]) =>
94+
` ${name.padEnd(10)} ${config.description}`).join('\n')}
4495
4596
Examples:
46-
vlurp facebook/react Fetch with default filters
47-
vlurp nodejs/node --filter "*.js" Fetch only JavaScript files
48-
vlurp user/repo --filter "src/**" Fetch src folder
49-
vlurp user/repo --force Force overwrite existing directory
97+
vlurp user/repo --ref abc1234 Fetch pinned to commit
98+
vlurp user/repo --as myskill -d ./sk Fetch to ./sk/myskill
99+
vlurp user/repo --preset claude Use claude preset filters
100+
vlurp user/repo --auto Auto-detect structure
101+
vlurp batch .vlurpfile Process batch file
102+
vlurp batch .vlurpfile --dry-run Preview batch operations
103+
vlurp verify skills/ Verify integrity
104+
vlurp pin Pin all sources
105+
vlurp outdated .vlurpfile Check upstream
106+
vlurp diff user/repo -d ./skills Content diff
107+
vlurp scan skills/ Scan for threats
108+
vlurp catalog skills/ Build catalog
109+
110+
.vlurpfile Format:
111+
# Comments start with #
112+
vlurp user/repo -d ./vlurp --ref abc1234
113+
vlurp user/repo -d ./vlurp --filter "claude/**" --as myname
114+
vlurp user/repo --preset skills
50115
51116
Options:
52117
${j.usage()}`);
@@ -58,11 +123,100 @@ if (positionals.length === 0) {
58123
process.exit(1);
59124
}
60125

61-
const source = positionals[0];
126+
const command = positionals[0];
62127
const rootDir = values.d;
63-
const filters = values.filter;
64-
const {force} = values;
128+
const {force, auto, preset, quiet} = values;
129+
const {ref} = values;
130+
const asName = values.as;
131+
const dryRun = values['dry-run'];
132+
133+
// Handle subcommands
134+
switch (command) {
135+
case 'batch': {
136+
const vlurpfile = positionals[1];
137+
if (!vlurpfile) {
138+
console.error('Error: batch command requires a .vlurpfile path');
139+
console.error('Usage: vlurp batch <vlurpfile>');
140+
process.exit(1);
141+
}
142+
143+
render(React.createElement(BatchCommand, {
144+
vlurpfile, dryRun, force, quiet
145+
}));
146+
147+
break;
148+
}
149+
150+
case 'verify': {
151+
const targetPath = positionals[1] || '.';
152+
render(React.createElement(VerifyCommand, {targetPath}));
153+
154+
break;
155+
}
156+
157+
case 'pin': {
158+
const source = positionals[1] || null;
159+
const vlurpfilePath = positionals[2] || null;
160+
render(React.createElement(PinCommand, {source, vlurpfilePath}));
65161

66-
render(React.createElement(FetchCommand, {
67-
source, rootDir, filters, force
68-
}));
162+
break;
163+
}
164+
165+
case 'outdated': {
166+
const vlurpfileArg = positionals[1] || null;
167+
render(React.createElement(OutdatedCommand, {vlurpfilePath: vlurpfileArg}));
168+
169+
break;
170+
}
171+
172+
case 'scan': {
173+
const scanPath = positionals[1] || '.';
174+
render(React.createElement(ScanCommand, {targetPath: scanPath}));
175+
176+
break;
177+
}
178+
179+
case 'catalog': {
180+
const catalogPath = positionals[1] || '.';
181+
render(React.createElement(CatalogCommand, {targetPath: catalogPath}));
182+
183+
break;
184+
}
185+
186+
case 'diff': {
187+
const diffSource = positionals[1];
188+
if (!diffSource) {
189+
console.error('Error: diff command requires a source');
190+
console.error('Usage: vlurp diff <user/repo> [-d <root>]');
191+
process.exit(1);
192+
}
193+
194+
render(React.createElement(DiffCommand, {source: diffSource, rootDir}));
195+
196+
break;
197+
}
198+
199+
default: {
200+
// Regular fetch command
201+
const source = command;
202+
203+
// Resolve filters from preset or explicit filters
204+
let filters = values.filter;
205+
if (preset && PRESETS[preset]) {
206+
filters = PRESETS[preset].filters;
207+
}
208+
209+
render(React.createElement(FetchCommand, {
210+
source,
211+
rootDir,
212+
filters,
213+
force,
214+
auto,
215+
dryRun,
216+
quiet,
217+
ref,
218+
asName,
219+
preset
220+
}));
221+
}
222+
}

0 commit comments

Comments
 (0)