@vibe-agent-toolkit/cli 0.1.34-rc.2 → 0.1.34

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.
Files changed (46) hide show
  1. package/dist/bin.js +4 -0
  2. package/dist/bin.js.map +1 -1
  3. package/dist/commands/audit/git-url-clone.d.ts +32 -0
  4. package/dist/commands/audit/git-url-clone.d.ts.map +1 -0
  5. package/dist/commands/audit/git-url-clone.js +135 -0
  6. package/dist/commands/audit/git-url-clone.js.map +1 -0
  7. package/dist/commands/audit/provenance.d.ts +33 -0
  8. package/dist/commands/audit/provenance.d.ts.map +1 -0
  9. package/dist/commands/audit/provenance.js +57 -0
  10. package/dist/commands/audit/provenance.js.map +1 -0
  11. package/dist/commands/audit.d.ts.map +1 -1
  12. package/dist/commands/audit.js +218 -72
  13. package/dist/commands/audit.js.map +1 -1
  14. package/dist/commands/claude/marketplace/validate.d.ts.map +1 -1
  15. package/dist/commands/claude/marketplace/validate.js +2 -1
  16. package/dist/commands/claude/marketplace/validate.js.map +1 -1
  17. package/dist/commands/corpus/index.d.ts +6 -0
  18. package/dist/commands/corpus/index.d.ts.map +1 -0
  19. package/dist/commands/corpus/index.js +53 -0
  20. package/dist/commands/corpus/index.js.map +1 -0
  21. package/dist/commands/corpus/report.d.ts +75 -0
  22. package/dist/commands/corpus/report.d.ts.map +1 -0
  23. package/dist/commands/corpus/report.js +83 -0
  24. package/dist/commands/corpus/report.js.map +1 -0
  25. package/dist/commands/corpus/runner.d.ts +24 -0
  26. package/dist/commands/corpus/runner.d.ts.map +1 -0
  27. package/dist/commands/corpus/runner.js +246 -0
  28. package/dist/commands/corpus/runner.js.map +1 -0
  29. package/dist/commands/corpus/scan.d.ts +15 -0
  30. package/dist/commands/corpus/scan.d.ts.map +1 -0
  31. package/dist/commands/corpus/scan.js +90 -0
  32. package/dist/commands/corpus/scan.js.map +1 -0
  33. package/dist/commands/corpus/seed.d.ts +178 -0
  34. package/dist/commands/corpus/seed.d.ts.map +1 -0
  35. package/dist/commands/corpus/seed.js +63 -0
  36. package/dist/commands/corpus/seed.js.map +1 -0
  37. package/dist/commands/inventory.d.ts +17 -0
  38. package/dist/commands/inventory.d.ts.map +1 -0
  39. package/dist/commands/inventory.js +90 -0
  40. package/dist/commands/inventory.js.map +1 -0
  41. package/dist/utils/git-url.d.ts +43 -0
  42. package/dist/utils/git-url.d.ts.map +1 -0
  43. package/dist/utils/git-url.js +135 -0
  44. package/dist/utils/git-url.js.map +1 -0
  45. package/docs/audit.md +54 -3
  46. package/package.json +11 -11
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Seed schema + parser for `corpus/seed.yaml`. The seed is the committed
3
+ * config of plugins tracked by `vat corpus scan`. Per-entry `validation:`
4
+ * mirrors the `skills.defaults.validation` block of the project config
5
+ * schema (severity overrides + allow entries).
6
+ */
7
+ import { z } from 'zod';
8
+ declare const ValidationAllowEntrySchema: z.ZodObject<{
9
+ code: z.ZodString;
10
+ reason: z.ZodString;
11
+ }, "strip", z.ZodTypeAny, {
12
+ code: string;
13
+ reason: string;
14
+ }, {
15
+ code: string;
16
+ reason: string;
17
+ }>;
18
+ declare const ValidationBlockSchema: z.ZodObject<{
19
+ severity: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<["error", "warning", "info", "ignore"]>>>;
20
+ allow: z.ZodOptional<z.ZodArray<z.ZodObject<{
21
+ code: z.ZodString;
22
+ reason: z.ZodString;
23
+ }, "strip", z.ZodTypeAny, {
24
+ code: string;
25
+ reason: string;
26
+ }, {
27
+ code: string;
28
+ reason: string;
29
+ }>, "many">>;
30
+ }, "strict", z.ZodTypeAny, {
31
+ allow?: {
32
+ code: string;
33
+ reason: string;
34
+ }[] | undefined;
35
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
36
+ }, {
37
+ allow?: {
38
+ code: string;
39
+ reason: string;
40
+ }[] | undefined;
41
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
42
+ }>;
43
+ declare const PluginEntrySchema: z.ZodObject<{
44
+ source: z.ZodString;
45
+ name: z.ZodString;
46
+ validation: z.ZodOptional<z.ZodObject<{
47
+ severity: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<["error", "warning", "info", "ignore"]>>>;
48
+ allow: z.ZodOptional<z.ZodArray<z.ZodObject<{
49
+ code: z.ZodString;
50
+ reason: z.ZodString;
51
+ }, "strip", z.ZodTypeAny, {
52
+ code: string;
53
+ reason: string;
54
+ }, {
55
+ code: string;
56
+ reason: string;
57
+ }>, "many">>;
58
+ }, "strict", z.ZodTypeAny, {
59
+ allow?: {
60
+ code: string;
61
+ reason: string;
62
+ }[] | undefined;
63
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
64
+ }, {
65
+ allow?: {
66
+ code: string;
67
+ reason: string;
68
+ }[] | undefined;
69
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
70
+ }>>;
71
+ }, "strict", z.ZodTypeAny, {
72
+ name: string;
73
+ source: string;
74
+ validation?: {
75
+ allow?: {
76
+ code: string;
77
+ reason: string;
78
+ }[] | undefined;
79
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
80
+ } | undefined;
81
+ }, {
82
+ name: string;
83
+ source: string;
84
+ validation?: {
85
+ allow?: {
86
+ code: string;
87
+ reason: string;
88
+ }[] | undefined;
89
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
90
+ } | undefined;
91
+ }>;
92
+ declare const SeedSchema: z.ZodObject<{
93
+ plugins: z.ZodArray<z.ZodObject<{
94
+ source: z.ZodString;
95
+ name: z.ZodString;
96
+ validation: z.ZodOptional<z.ZodObject<{
97
+ severity: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<["error", "warning", "info", "ignore"]>>>;
98
+ allow: z.ZodOptional<z.ZodArray<z.ZodObject<{
99
+ code: z.ZodString;
100
+ reason: z.ZodString;
101
+ }, "strip", z.ZodTypeAny, {
102
+ code: string;
103
+ reason: string;
104
+ }, {
105
+ code: string;
106
+ reason: string;
107
+ }>, "many">>;
108
+ }, "strict", z.ZodTypeAny, {
109
+ allow?: {
110
+ code: string;
111
+ reason: string;
112
+ }[] | undefined;
113
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
114
+ }, {
115
+ allow?: {
116
+ code: string;
117
+ reason: string;
118
+ }[] | undefined;
119
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
120
+ }>>;
121
+ }, "strict", z.ZodTypeAny, {
122
+ name: string;
123
+ source: string;
124
+ validation?: {
125
+ allow?: {
126
+ code: string;
127
+ reason: string;
128
+ }[] | undefined;
129
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
130
+ } | undefined;
131
+ }, {
132
+ name: string;
133
+ source: string;
134
+ validation?: {
135
+ allow?: {
136
+ code: string;
137
+ reason: string;
138
+ }[] | undefined;
139
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
140
+ } | undefined;
141
+ }>, "many">;
142
+ }, "strict", z.ZodTypeAny, {
143
+ plugins: {
144
+ name: string;
145
+ source: string;
146
+ validation?: {
147
+ allow?: {
148
+ code: string;
149
+ reason: string;
150
+ }[] | undefined;
151
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
152
+ } | undefined;
153
+ }[];
154
+ }, {
155
+ plugins: {
156
+ name: string;
157
+ source: string;
158
+ validation?: {
159
+ allow?: {
160
+ code: string;
161
+ reason: string;
162
+ }[] | undefined;
163
+ severity?: Record<string, "error" | "warning" | "info" | "ignore"> | undefined;
164
+ } | undefined;
165
+ }[];
166
+ }>;
167
+ export type ValidationAllowEntry = z.infer<typeof ValidationAllowEntrySchema>;
168
+ export type ValidationBlock = z.infer<typeof ValidationBlockSchema>;
169
+ export type PluginEntry = z.infer<typeof PluginEntrySchema>;
170
+ export type Seed = z.infer<typeof SeedSchema>;
171
+ /**
172
+ * Load and validate `corpus/seed.yaml` (or another seed-shaped YAML file).
173
+ * Throws on missing file, malformed YAML, schema violations, duplicate
174
+ * `source` keys, or duplicate `name` labels.
175
+ */
176
+ export declare function loadSeedFile(path: string): Seed;
177
+ export {};
178
+ //# sourceMappingURL=seed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../src/commands/corpus/seed.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,0BAA0B;;;;;;;;;EAG9B,CAAC;AAEH,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;EAKhB,CAAC;AAEZ,QAAA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASZ,CAAC;AAEZ,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIL,CAAC;AAEZ,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAyB/C"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Seed schema + parser for `corpus/seed.yaml`. The seed is the committed
3
+ * config of plugins tracked by `vat corpus scan`. Per-entry `validation:`
4
+ * mirrors the `skills.defaults.validation` block of the project config
5
+ * schema (severity overrides + allow entries).
6
+ */
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import * as yaml from 'js-yaml';
9
+ import { z } from 'zod';
10
+ const ValidationAllowEntrySchema = z.object({
11
+ code: z.string().min(1),
12
+ reason: z.string().min(1),
13
+ });
14
+ const ValidationBlockSchema = z
15
+ .object({
16
+ severity: z.record(z.string(), z.enum(['error', 'warning', 'info', 'ignore'])).optional(),
17
+ allow: z.array(ValidationAllowEntrySchema).optional(),
18
+ })
19
+ .strict();
20
+ const PluginEntrySchema = z
21
+ .object({
22
+ source: z.string().min(1),
23
+ name: z
24
+ .string()
25
+ .min(1)
26
+ .regex(/^[A-Za-z0-9_-]+$/, 'name must be [A-Za-z0-9_-]+ (presentation label)'),
27
+ validation: ValidationBlockSchema.optional(),
28
+ })
29
+ .strict();
30
+ const SeedSchema = z
31
+ .object({
32
+ plugins: z.array(PluginEntrySchema).min(1),
33
+ })
34
+ .strict();
35
+ /**
36
+ * Load and validate `corpus/seed.yaml` (or another seed-shaped YAML file).
37
+ * Throws on missing file, malformed YAML, schema violations, duplicate
38
+ * `source` keys, or duplicate `name` labels.
39
+ */
40
+ export function loadSeedFile(path) {
41
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- caller-supplied seed file path
42
+ if (!existsSync(path)) {
43
+ throw new Error(`Seed file not found: ${path}`);
44
+ }
45
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- caller-supplied seed file path
46
+ const raw = readFileSync(path, 'utf-8');
47
+ const parsed = yaml.load(raw);
48
+ const seed = SeedSchema.parse(parsed);
49
+ const sources = new Set();
50
+ const names = new Set();
51
+ for (const entry of seed.plugins) {
52
+ if (sources.has(entry.source)) {
53
+ throw new Error(`Seed has duplicate source: ${entry.source}`);
54
+ }
55
+ if (names.has(entry.name)) {
56
+ throw new Error(`Seed has duplicate name: ${entry.name}`);
57
+ }
58
+ sources.add(entry.source);
59
+ names.add(entry.name);
60
+ }
61
+ return seed;
62
+ }
63
+ //# sourceMappingURL=seed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../src/commands/corpus/seed.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,KAAK,IAAI,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC1B,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,CAAC;KAC5B,MAAM,CAAC;IACN,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACzF,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,QAAQ,EAAE;CACtD,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,iBAAiB,GAAG,CAAC;KACxB,MAAM,CAAC;IACN,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,KAAK,CAAC,kBAAkB,EAAE,kDAAkD,CAAC;IAChF,UAAU,EAAE,qBAAqB,CAAC,QAAQ,EAAE;CAC7C,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,UAAU,GAAG,CAAC;KACjB,MAAM,CAAC;IACN,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3C,CAAC;KACD,MAAM,EAAE,CAAC;AAOZ;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,qGAAqG;IACrG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,qGAAqG;IACrG,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1B,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { Command } from 'commander';
2
+ export interface InventoryCommandOptions {
3
+ user?: boolean;
4
+ system?: boolean;
5
+ format?: 'yaml' | 'json';
6
+ shallow?: boolean;
7
+ debug?: boolean;
8
+ }
9
+ /**
10
+ * Create and configure the `vat inventory` command.
11
+ */
12
+ export declare function createInventoryCommand(): Command;
13
+ /**
14
+ * Action handler for `vat inventory [path]`.
15
+ */
16
+ export declare function inventoryCommand(pathArg: string | undefined, options: InventoryCommandOptions): Promise<void>;
17
+ //# sourceMappingURL=inventory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory.d.ts","sourceRoot":"","sources":["../../src/commands/inventory.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,MAAM,WAAW,uBAAuB;IACvC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAgChD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,uBAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -0,0 +1,90 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { serializeInventory, serializeInventoryShallow, } from '@vibe-agent-toolkit/agent-skills';
3
+ import { extractClaudeInstallInventory, extractClaudeMarketplaceInventory, extractClaudePluginInventory, extractClaudeSkillInventory, getClaudeUserPaths, } from '@vibe-agent-toolkit/claude-marketplace';
4
+ import { safePath } from '@vibe-agent-toolkit/utils';
5
+ import { Command } from 'commander';
6
+ import { handleCommandError } from '../utils/command-error.js';
7
+ import { createLogger } from '../utils/logger.js';
8
+ /**
9
+ * Create and configure the `vat inventory` command.
10
+ */
11
+ export function createInventoryCommand() {
12
+ const command = new Command('inventory');
13
+ command
14
+ .description('Extract structural inventory of a plugin, marketplace, skill, or install root')
15
+ .argument('[path]', 'Path to inventory (directory or SKILL.md)')
16
+ .option('--user', 'Inventory the user-level Claude install (~/.claude/plugins)')
17
+ .option('--system', 'Inventory the system-level Claude install')
18
+ .option('--format <yaml|json>', 'Output format', 'yaml')
19
+ .option('--shallow', 'Omit nested inventories (paths only)')
20
+ .option('--debug', 'Verbose logging to stderr')
21
+ .action(inventoryCommand)
22
+ .addHelpText('after', `
23
+ Description:
24
+ Extract and emit the structural inventory of a Claude plugin, marketplace,
25
+ skill, or install root. Outputs YAML to stdout by default. Runs no validation
26
+ detectors — pure structural enumeration.
27
+
28
+ Output:
29
+ - schema: vat.inventory/v1alpha
30
+ - kind: marketplace | plugin | skill | install
31
+ - vendor: claude-code
32
+ - declared / discovered / references / unexpected (per kind)
33
+ - parseErrors: any manifest parse failures
34
+
35
+ Exit Codes:
36
+ 0 - Inventory extracted (parse errors surface in output, not as exit code)
37
+ 2 - System error (path not found, --system not supported, etc.)
38
+
39
+ Example:
40
+ $ vat inventory my-plugin/ # Inventory a single plugin
41
+ `);
42
+ return command;
43
+ }
44
+ /**
45
+ * Action handler for `vat inventory [path]`.
46
+ */
47
+ export async function inventoryCommand(pathArg, options) {
48
+ const logger = createLogger(options.debug === true ? { debug: true } : {});
49
+ const startTime = Date.now();
50
+ try {
51
+ const inv = await routeInventory(pathArg, options);
52
+ const format = options.format ?? 'yaml';
53
+ const out = options.shallow === true
54
+ ? serializeInventoryShallow(inv, format)
55
+ : serializeInventory(inv, format);
56
+ process.stdout.write(out);
57
+ process.exit(0);
58
+ }
59
+ catch (error) {
60
+ handleCommandError(error, logger, startTime, 'Inventory');
61
+ }
62
+ }
63
+ async function routeInventory(pathArg, options) {
64
+ if (options.user === true) {
65
+ return extractClaudeInstallInventory(getClaudeUserPaths());
66
+ }
67
+ if (options.system === true) {
68
+ throw new Error('--system inventory is not implemented in this version');
69
+ }
70
+ if (!pathArg) {
71
+ throw new Error('Path argument is required (or use --user / --system).');
72
+ }
73
+ const absolute = safePath.resolve(pathArg);
74
+ if (absolute.endsWith('SKILL.md') || absolute.endsWith('skill.md')) {
75
+ return extractClaudeSkillInventory(absolute);
76
+ }
77
+ const claudePluginDir = safePath.join(absolute, '.claude-plugin');
78
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- absolute is caller-resolved, used for presence check only
79
+ const hasMarketplace = existsSync(safePath.join(claudePluginDir, 'marketplace.json'));
80
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- absolute is caller-resolved, used for presence check only
81
+ const hasPlugin = existsSync(safePath.join(claudePluginDir, 'plugin.json'));
82
+ // A directory with marketplace.json but no plugin.json is a marketplace root.
83
+ // When both are present, the plugin extractor takes precedence (plugin is installed,
84
+ // marketplace.json is a cached metadata artifact alongside it).
85
+ if (hasMarketplace && !hasPlugin) {
86
+ return extractClaudeMarketplaceInventory(absolute);
87
+ }
88
+ return extractClaudePluginInventory(absolute);
89
+ }
90
+ //# sourceMappingURL=inventory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory.js","sourceRoot":"","sources":["../../src/commands/inventory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Parsed representation of a git URL accepted by `vat audit`.
3
+ *
4
+ * - `cloneUrl` is the URL passed to `git clone` (after stripping ref/subpath
5
+ * fragments and after expanding GitHub shorthand to a full HTTPS URL).
6
+ * - `ref` is an optional branch or tag name (deep commit SHAs are not
7
+ * guaranteed to work with shallow clone — see design spec for details).
8
+ * - `subpath` is an optional subdirectory within the cloned repo to audit.
9
+ */
10
+ export interface ParsedGitUrl {
11
+ cloneUrl: string;
12
+ ref?: string;
13
+ subpath?: string;
14
+ }
15
+ /**
16
+ * Parse a string into a {@link ParsedGitUrl}.
17
+ *
18
+ * Accepted forms:
19
+ * - `https://host/owner/repo.git`
20
+ * - `https://host/owner/repo.git#ref`
21
+ * - `https://host/owner/repo.git#ref:subpath`
22
+ * - `https://github.com/owner/repo/tree/<ref>/<subpath>` (GitHub web URL)
23
+ * - `owner/repo` (GitHub shorthand → expanded to HTTPS)
24
+ * - `git@host:owner/repo.git`
25
+ * - `ssh://git@host/owner/repo.git`
26
+ *
27
+ * Throws on malformed input.
28
+ */
29
+ export declare function parseGitUrl(input: string): ParsedGitUrl;
30
+ /**
31
+ * Detect whether a string should be treated as a git URL (for the polymorphic
32
+ * `[git-url-or-path]` audit argument). True for:
33
+ * - http(s):// URLs
34
+ * - ssh:// URLs
35
+ * - file:// URLs (used by integration tests against local bare repos)
36
+ * - git@host:path scp-style URLs
37
+ * - GitHub shorthand `owner/repo` (strict — no extensions, no extra slashes)
38
+ *
39
+ * Everything else (including relative paths like `./foo/bar`, absolute paths,
40
+ * and multi-segment paths like `foo/bar/baz`) is treated as a filesystem path.
41
+ */
42
+ export declare function isGitUrl(input: string): boolean;
43
+ //# sourceMappingURL=git-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-url.d.ts","sourceRoot":"","sources":["../../src/utils/git-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAgEvD;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAmB/C"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Split a URL with an optional `#ref[:subpath]` fragment into the base URL
3
+ * and fragment components. Keeps the fragment-handling logic local so the
4
+ * per-form regexes can stay simple and anchored.
5
+ */
6
+ function splitFragment(input) {
7
+ const hashIndex = input.indexOf('#');
8
+ if (hashIndex === -1) {
9
+ return { base: input };
10
+ }
11
+ const base = input.slice(0, hashIndex);
12
+ const fragment = input.slice(hashIndex + 1);
13
+ const colonIndex = fragment.indexOf(':');
14
+ if (colonIndex === -1) {
15
+ return { base, ref: fragment };
16
+ }
17
+ return {
18
+ base,
19
+ ref: fragment.slice(0, colonIndex),
20
+ subpath: fragment.slice(colonIndex + 1),
21
+ };
22
+ }
23
+ /**
24
+ * Parse a string into a {@link ParsedGitUrl}.
25
+ *
26
+ * Accepted forms:
27
+ * - `https://host/owner/repo.git`
28
+ * - `https://host/owner/repo.git#ref`
29
+ * - `https://host/owner/repo.git#ref:subpath`
30
+ * - `https://github.com/owner/repo/tree/<ref>/<subpath>` (GitHub web URL)
31
+ * - `owner/repo` (GitHub shorthand → expanded to HTTPS)
32
+ * - `git@host:owner/repo.git`
33
+ * - `ssh://git@host/owner/repo.git`
34
+ *
35
+ * Throws on malformed input.
36
+ */
37
+ export function parseGitUrl(input) {
38
+ const trimmed = input.trim();
39
+ if (trimmed === '') {
40
+ throw new Error(`Invalid git URL or path: <empty>.`);
41
+ }
42
+ // file:// form: file:///path/to/repo[#ref[:subpath]] — used primarily by
43
+ // integration tests that clone a local bare repo. `git clone` accepts
44
+ // file:// natively.
45
+ if (trimmed.startsWith('file://')) {
46
+ const { base, ref, subpath } = splitFragment(trimmed);
47
+ return buildParsed(base, ref, subpath);
48
+ }
49
+ // HTTPS .git form: https://host/path.git[#ref[:subpath]]
50
+ if (/^https?:\/\//.test(trimmed)) {
51
+ const { base, ref, subpath } = splitFragment(trimmed);
52
+ if (base.endsWith('.git')) {
53
+ return buildParsed(base, ref, subpath);
54
+ }
55
+ // GitHub web URL: https://github.com/owner/repo/tree/<ref>[/<subpath>]
56
+ const ghWeb =
57
+ // eslint-disable-next-line security/detect-unsafe-regex -- Anchored ^...$ with bounded character classes; the only variable-length group is the trailing subpath. Safe from ReDoS.
58
+ /^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/tree\/([^/]+)(?:\/(.+))?$/.exec(trimmed);
59
+ if (ghWeb) {
60
+ const owner = ghWeb[1] ?? '';
61
+ const repo = ghWeb[2] ?? '';
62
+ const webRef = ghWeb[3];
63
+ const webSubpath = ghWeb[4];
64
+ return buildParsed(`https://github.com/${owner}/${repo}.git`, webRef, webSubpath);
65
+ }
66
+ }
67
+ // SSH ssh:// form: ssh://git@host/path[#ref[:subpath]]
68
+ if (trimmed.startsWith('ssh://')) {
69
+ const { base, ref, subpath } = splitFragment(trimmed);
70
+ return buildParsed(base, ref, subpath);
71
+ }
72
+ // SSH scp-like form: git@host:owner/repo.git[#ref[:subpath]]
73
+ // Anchored, no alternation with nested quantifiers — safe from ReDoS.
74
+ if (/^[^@\s]+@[^:\s]+:[^#\s]+/.test(trimmed)) {
75
+ const { base, ref, subpath } = splitFragment(trimmed);
76
+ return buildParsed(base, ref, subpath);
77
+ }
78
+ // GitHub shorthand: owner/repo[#ref[:subpath]] (single slash in base)
79
+ const { base, ref, subpath } = splitFragment(trimmed);
80
+ const shorthand = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(base);
81
+ if (shorthand) {
82
+ const owner = shorthand[1] ?? '';
83
+ const repo = shorthand[2] ?? '';
84
+ return buildParsed(`https://github.com/${owner}/${repo}.git`, ref, subpath);
85
+ }
86
+ throw new Error(`Invalid git URL or path: ${input}. Accepted forms: ` +
87
+ `https://<host>/<owner>/<repo>.git, ` +
88
+ `git@<host>:<owner>/<repo>.git, ` +
89
+ `<owner>/<repo>, or a local filesystem path.`);
90
+ }
91
+ function buildParsed(cloneUrl, ref, subpath) {
92
+ const result = { cloneUrl };
93
+ if (ref !== undefined && ref !== '')
94
+ result.ref = ref;
95
+ if (subpath !== undefined && subpath !== '')
96
+ result.subpath = subpath;
97
+ return result;
98
+ }
99
+ /**
100
+ * Detect whether a string should be treated as a git URL (for the polymorphic
101
+ * `[git-url-or-path]` audit argument). True for:
102
+ * - http(s):// URLs
103
+ * - ssh:// URLs
104
+ * - file:// URLs (used by integration tests against local bare repos)
105
+ * - git@host:path scp-style URLs
106
+ * - GitHub shorthand `owner/repo` (strict — no extensions, no extra slashes)
107
+ *
108
+ * Everything else (including relative paths like `./foo/bar`, absolute paths,
109
+ * and multi-segment paths like `foo/bar/baz`) is treated as a filesystem path.
110
+ */
111
+ export function isGitUrl(input) {
112
+ const trimmed = input.trim();
113
+ if (trimmed === '')
114
+ return false;
115
+ if (/^https?:\/\//.test(trimmed))
116
+ return true;
117
+ if (trimmed.startsWith('ssh://'))
118
+ return true;
119
+ if (trimmed.startsWith('file://'))
120
+ return true;
121
+ // Match the same scp-style pattern parseGitUrl uses, including the
122
+ // requirement of a non-empty path segment after the colon. Without the
123
+ // trailing `[^#\s]+`, `isGitUrl` would accept inputs like `foo@host:`
124
+ // that parseGitUrl then rejects with a less-helpful error.
125
+ if (/^[^@\s]+@[^:\s]+:[^#\s]+/.test(trimmed))
126
+ return true;
127
+ // Strict GitHub shorthand: exactly two segments, no extension on second,
128
+ // no path separators beyond the single /. Strip any `#ref[:subpath]`
129
+ // fragment first so `owner/repo#main` and `owner/repo#main:sub` are
130
+ // recognized — `parseGitUrl` handles fragments uniformly across forms.
131
+ const hashIndex = trimmed.indexOf('#');
132
+ const base = hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex);
133
+ return /^[A-Za-z0-9_-]+\/[A-Za-z0-9_-]+$/.test(base);
134
+ }
135
+ //# sourceMappingURL=git-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-url.js","sourceRoot":"","sources":["../../src/utils/git-url.ts"],"names":[],"mappings":"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"}
package/docs/audit.md CHANGED
@@ -16,13 +16,14 @@ The `vat audit` command provides comprehensive validation for Claude plugins, ma
16
16
  ## Usage
17
17
 
18
18
  ```bash
19
- vat audit [path] [options]
19
+ vat audit [git-url-or-path] [options]
20
20
  ```
21
21
 
22
22
  ### Arguments
23
23
 
24
- - `[path]` - Path to audit (optional, default: current directory)
25
- - Can be: directory, registry file, SKILL.md file, or resource directory
24
+ - `[git-url-or-path]` - Path or git URL to audit (optional, default: current directory)
25
+ - Local path: directory, registry file, SKILL.md file, or resource directory
26
+ - Git URL: HTTPS, SSH, GitHub shorthand, or GitHub web URL (see "Auditing a remote git repo" below)
26
27
 
27
28
  ### Options
28
29
 
@@ -483,6 +484,56 @@ if [ $? -ne 0 ]; then
483
484
  fi
484
485
  ```
485
486
 
487
+ ## Auditing a remote git repo
488
+
489
+ `vat audit` accepts a git URL in addition to a local path. When given a URL, VAT performs a shallow clone into a temporary directory, runs the audit against the cloned tree, and always cleans up the tempdir on exit.
490
+
491
+ ### Accepted URL forms
492
+
493
+ | Form | Example |
494
+ |---|---|
495
+ | HTTPS git URL | `https://github.com/foo/bar.git` |
496
+ | HTTPS with ref | `https://github.com/foo/bar.git#v1.2.3` |
497
+ | HTTPS with ref + subpath | `https://github.com/foo/bar.git#main:plugins/baz` |
498
+ | GitHub web URL | `https://github.com/foo/bar/tree/main/plugins/baz` |
499
+ | GitHub shorthand | `foo/bar` |
500
+ | GitHub shorthand with ref | `foo/bar#main` |
501
+ | GitHub shorthand with ref + subpath | `foo/bar#main:plugins/baz` |
502
+ | SSH (scp form) | `git@github.com:foo/bar.git` |
503
+ | SSH (URL form) | `ssh://git@github.com/foo/bar.git` |
504
+
505
+ The `#ref[:subpath]` fragment form works on every URL form above.
506
+
507
+ ### Output
508
+
509
+ Output begins with a provenance header showing the URL, ref, and resolved commit SHA, then the normal audit output with paths relative to the cloned repo root. Each header line is emitted as a YAML comment so the rest of the output remains pipe-parseable (`vat audit <url> | yq` works without preprocessing):
510
+
511
+ ```
512
+ # Audited: https://github.com/foo/bar.git @ main (commit abc123de)
513
+ # Subpath: plugins/baz
514
+ ---
515
+ status: success
516
+ files:
517
+ - path: plugins/baz/SKILL.md
518
+ ...
519
+ ```
520
+
521
+ ### Authentication
522
+
523
+ Authentication is entirely passthrough to your local `git` configuration. SSH URLs use your SSH agent / keys; HTTPS URLs use whatever credential helper your `git` is configured with. VAT itself does not read tokens, environment variables, or credential files.
524
+
525
+ To audit a private repo: ensure `git clone <url>` works on your machine, then `vat audit <url>` will work too.
526
+
527
+ ### Debugging
528
+
529
+ Pass `--debug` to preserve the cloned tempdir for inspection (its location is printed to stderr at exit). You are responsible for cleanup when using this flag.
530
+
531
+ ### Limitations
532
+
533
+ - `--depth 1` cloning cannot resolve arbitrary deep commit SHAs. Use a branch or tag name for the ref.
534
+ - GitHub web URLs are parsed only for `github.com`. For GitLab/Bitbucket/Gitea, use the `.git` URL form directly.
535
+ - Cache-skip-on-unchanged-SHA is not implemented in v1; every URL audit pays the clone cost.
536
+
486
537
  ## Cross-Platform Considerations
487
538
 
488
539
  ### Path Separators