@vaultcompass/vault-guard 1.1.1 → 1.2.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/dist/cli-entry.js +2 -5
- package/dist/cli.js +47 -19
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.js +3 -1
- package/dist/commands/init.d.ts +40 -0
- package/dist/commands/init.js +488 -0
- package/dist/init/templates.d.ts +28 -0
- package/dist/init/templates.js +119 -0
- package/package.json +3 -3
package/dist/cli-entry.js
CHANGED
|
@@ -9,11 +9,8 @@ if (nodeMajor < 22) {
|
|
|
9
9
|
}
|
|
10
10
|
const program = (0, cli_1.buildCli)();
|
|
11
11
|
// Parse arguments and execute command
|
|
12
|
-
program.parseAsync().
|
|
13
|
-
// Successful completion
|
|
14
|
-
process.exit(0);
|
|
15
|
-
}).catch((error) => {
|
|
12
|
+
program.parseAsync().catch((error) => {
|
|
16
13
|
// Handle errors
|
|
17
14
|
console.error(error);
|
|
18
|
-
process.
|
|
15
|
+
process.exitCode = 1;
|
|
19
16
|
});
|
package/dist/cli.js
CHANGED
|
@@ -47,11 +47,17 @@ const suggest_model_1 = require("./commands/suggest-model");
|
|
|
47
47
|
const proxy_1 = require("./commands/proxy");
|
|
48
48
|
const data_1 = require("./commands/data");
|
|
49
49
|
const config_1 = require("./commands/config");
|
|
50
|
+
const init_1 = require("./commands/init");
|
|
50
51
|
function readCliVersion() {
|
|
51
52
|
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
52
53
|
const raw = fs.readFileSync(pkgPath, 'utf-8');
|
|
53
54
|
return JSON.parse(raw).version;
|
|
54
55
|
}
|
|
56
|
+
function setExitCode(exitCode) {
|
|
57
|
+
if (exitCode !== 0) {
|
|
58
|
+
process.exitCode = exitCode;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
55
61
|
function buildCli() {
|
|
56
62
|
const program = new commander_1.Command();
|
|
57
63
|
program
|
|
@@ -64,8 +70,7 @@ function buildCli() {
|
|
|
64
70
|
.description('Validate the nearest .vault-guard.json (structure + scanner load)')
|
|
65
71
|
.action(async () => {
|
|
66
72
|
const exitCode = await (0, config_1.configValidateCommand)(process.cwd());
|
|
67
|
-
|
|
68
|
-
process.exit(exitCode);
|
|
73
|
+
setExitCode(exitCode);
|
|
69
74
|
});
|
|
70
75
|
// Scan command
|
|
71
76
|
program
|
|
@@ -77,9 +82,37 @@ function buildCli() {
|
|
|
77
82
|
.action(async (path, options) => {
|
|
78
83
|
const format = options.format ?? 'text';
|
|
79
84
|
const exitCode = await (0, scan_1.scanCommand)(path, format, Boolean(options.staged));
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
setExitCode(exitCode);
|
|
86
|
+
});
|
|
87
|
+
program
|
|
88
|
+
.command('init')
|
|
89
|
+
.description('Initialize Vault Guard in this repository (config, CI workflow, agent rules, hook)')
|
|
90
|
+
.option('--dry-run', 'Print the file manifest and planned actions without writing', false)
|
|
91
|
+
.option('--revert', 'Remove files recorded in .vault-guard/manifest.json', false)
|
|
92
|
+
.option('--json', 'Machine-readable output', false)
|
|
93
|
+
.option('-m, --manager <manager>', 'Hook integration when installing pre-commit: native | husky | lefthook | precommit', 'native')
|
|
94
|
+
.option('--skip-hook', 'Do not install a pre-commit hook', false)
|
|
95
|
+
.option('--skip-workflow', 'Do not create .github/workflows/vault-guard.yml', false)
|
|
96
|
+
.option('--skip-config', 'Do not create .vault-guard.json', false)
|
|
97
|
+
.option('--skip-agent-rules', 'Do not create .vault-guard/mcp-snippet.json or agent-rules.md', false)
|
|
98
|
+
.action(async (options) => {
|
|
99
|
+
const m = (options.manager ?? 'native').toLowerCase();
|
|
100
|
+
if (!['native', 'husky', 'lefthook', 'precommit'].includes(m)) {
|
|
101
|
+
console.error(`Unknown manager: ${options.manager}`);
|
|
102
|
+
process.exitCode = 1;
|
|
103
|
+
return;
|
|
82
104
|
}
|
|
105
|
+
const exitCode = await (0, init_1.initCommand)({
|
|
106
|
+
dryRun: Boolean(options.dryRun),
|
|
107
|
+
revert: Boolean(options.revert),
|
|
108
|
+
json: Boolean(options.json),
|
|
109
|
+
manager: m,
|
|
110
|
+
skipHook: Boolean(options.skipHook),
|
|
111
|
+
skipWorkflow: Boolean(options.skipWorkflow),
|
|
112
|
+
skipConfig: Boolean(options.skipConfig),
|
|
113
|
+
skipAgentRules: Boolean(options.skipAgentRules),
|
|
114
|
+
});
|
|
115
|
+
setExitCode(exitCode);
|
|
83
116
|
});
|
|
84
117
|
// Install-hook command
|
|
85
118
|
program
|
|
@@ -90,7 +123,8 @@ function buildCli() {
|
|
|
90
123
|
const m = (options.manager ?? 'native').toLowerCase();
|
|
91
124
|
if (!['native', 'husky', 'lefthook', 'precommit'].includes(m)) {
|
|
92
125
|
console.error(`Unknown manager: ${options.manager}`);
|
|
93
|
-
process.
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return;
|
|
94
128
|
}
|
|
95
129
|
await (0, install_hook_1.installHookCommand)(m);
|
|
96
130
|
});
|
|
@@ -108,9 +142,7 @@ function buildCli() {
|
|
|
108
142
|
.argument('[files...]', 'Files to check')
|
|
109
143
|
.action(async (files) => {
|
|
110
144
|
const exitCode = await (0, fix_1.fixCommand)(files);
|
|
111
|
-
|
|
112
|
-
process.exit(exitCode);
|
|
113
|
-
}
|
|
145
|
+
setExitCode(exitCode);
|
|
114
146
|
});
|
|
115
147
|
// Check command
|
|
116
148
|
program
|
|
@@ -119,9 +151,7 @@ function buildCli() {
|
|
|
119
151
|
.argument('[files...]', 'Files to check')
|
|
120
152
|
.action(async (files) => {
|
|
121
153
|
const exitCode = await (0, check_1.checkCommand)(files);
|
|
122
|
-
|
|
123
|
-
process.exit(exitCode);
|
|
124
|
-
}
|
|
154
|
+
setExitCode(exitCode);
|
|
125
155
|
});
|
|
126
156
|
program
|
|
127
157
|
.command('statusline')
|
|
@@ -162,7 +192,8 @@ function buildCli() {
|
|
|
162
192
|
const n = Number(options.maxRpm);
|
|
163
193
|
if (!Number.isFinite(n) || n < 1) {
|
|
164
194
|
console.error('--max-rpm must be a positive number');
|
|
165
|
-
process.
|
|
195
|
+
process.exitCode = 1;
|
|
196
|
+
return;
|
|
166
197
|
}
|
|
167
198
|
maxRpm = Math.floor(n);
|
|
168
199
|
}
|
|
@@ -192,7 +223,7 @@ function buildCli() {
|
|
|
192
223
|
}
|
|
193
224
|
catch (e) {
|
|
194
225
|
console.error(String(e));
|
|
195
|
-
process.
|
|
226
|
+
process.exitCode = 1;
|
|
196
227
|
}
|
|
197
228
|
});
|
|
198
229
|
// `data` parent command — inspects, exports, and resets the local
|
|
@@ -207,8 +238,7 @@ function buildCli() {
|
|
|
207
238
|
.option('--json', 'Print JSON', false)
|
|
208
239
|
.action(async (options) => {
|
|
209
240
|
const exitCode = await (0, data_1.dataStatusCommand)({ json: Boolean(options.json) });
|
|
210
|
-
|
|
211
|
-
process.exit(exitCode);
|
|
241
|
+
setExitCode(exitCode);
|
|
212
242
|
});
|
|
213
243
|
dataCmd
|
|
214
244
|
.command('reset')
|
|
@@ -222,8 +252,7 @@ function buildCli() {
|
|
|
222
252
|
dryRun: Boolean(options.dryRun),
|
|
223
253
|
json: Boolean(options.json),
|
|
224
254
|
});
|
|
225
|
-
|
|
226
|
-
process.exit(exitCode);
|
|
255
|
+
setExitCode(exitCode);
|
|
227
256
|
});
|
|
228
257
|
dataCmd
|
|
229
258
|
.command('export')
|
|
@@ -233,8 +262,7 @@ function buildCli() {
|
|
|
233
262
|
.action(async (options) => {
|
|
234
263
|
const fmt = options.format === 'jsonl' ? 'jsonl' : 'json';
|
|
235
264
|
const exitCode = await (0, data_1.dataExportCommand)({ output: options.output, format: fmt });
|
|
236
|
-
|
|
237
|
-
process.exit(exitCode);
|
|
265
|
+
setExitCode(exitCode);
|
|
238
266
|
});
|
|
239
267
|
return program;
|
|
240
268
|
}
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { scanCommand } from './scan';
|
|
2
2
|
export { configValidateCommand } from './config';
|
|
3
3
|
export { installHookCommand } from './install-hook';
|
|
4
|
+
export { initCommand, type InitOptions } from './init';
|
|
4
5
|
export { tokensCommand } from './tokens';
|
|
5
6
|
export { fixCommand } from './fix';
|
|
6
7
|
export { checkCommand } from './check';
|
package/dist/commands/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dataExportCommand = exports.dataResetCommand = exports.dataStatusCommand = exports.proxyCommand = exports.suggestModelCommand = exports.statuslineCommand = exports.checkCommand = exports.fixCommand = exports.tokensCommand = exports.installHookCommand = exports.configValidateCommand = exports.scanCommand = void 0;
|
|
3
|
+
exports.dataExportCommand = exports.dataResetCommand = exports.dataStatusCommand = exports.proxyCommand = exports.suggestModelCommand = exports.statuslineCommand = exports.checkCommand = exports.fixCommand = exports.tokensCommand = exports.initCommand = exports.installHookCommand = exports.configValidateCommand = exports.scanCommand = void 0;
|
|
4
4
|
var scan_1 = require("./scan");
|
|
5
5
|
Object.defineProperty(exports, "scanCommand", { enumerable: true, get: function () { return scan_1.scanCommand; } });
|
|
6
6
|
var config_1 = require("./config");
|
|
7
7
|
Object.defineProperty(exports, "configValidateCommand", { enumerable: true, get: function () { return config_1.configValidateCommand; } });
|
|
8
8
|
var install_hook_1 = require("./install-hook");
|
|
9
9
|
Object.defineProperty(exports, "installHookCommand", { enumerable: true, get: function () { return install_hook_1.installHookCommand; } });
|
|
10
|
+
var init_1 = require("./init");
|
|
11
|
+
Object.defineProperty(exports, "initCommand", { enumerable: true, get: function () { return init_1.initCommand; } });
|
|
10
12
|
var tokens_1 = require("./tokens");
|
|
11
13
|
Object.defineProperty(exports, "tokensCommand", { enumerable: true, get: function () { return tokens_1.tokensCommand; } });
|
|
12
14
|
var fix_1 = require("./fix");
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type HookManager } from '@vaultcompass/vault-guard-core';
|
|
2
|
+
export interface InitOptions {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
revert?: boolean;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
manager?: HookManager;
|
|
8
|
+
skipHook?: boolean;
|
|
9
|
+
skipWorkflow?: boolean;
|
|
10
|
+
skipConfig?: boolean;
|
|
11
|
+
skipAgentRules?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface InitConflict {
|
|
14
|
+
path: string;
|
|
15
|
+
reason: 'exists' | 'foreign_manifest' | 'manifest_mismatch' | 'not_a_git_repository' | 'foreign_hook';
|
|
16
|
+
}
|
|
17
|
+
export interface InitPlannedAction {
|
|
18
|
+
kind: 'create' | 'hook-install' | 'skip';
|
|
19
|
+
path: string;
|
|
20
|
+
detail?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface InitResult {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
dryRun: boolean;
|
|
25
|
+
reverted: boolean;
|
|
26
|
+
alreadyInitialized: boolean;
|
|
27
|
+
actions: InitPlannedAction[];
|
|
28
|
+
conflicts: InitConflict[];
|
|
29
|
+
hook?: {
|
|
30
|
+
manager: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
installed: boolean;
|
|
33
|
+
};
|
|
34
|
+
manifestPath: string;
|
|
35
|
+
mcpMergeHint: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function planInit(options?: InitOptions): InitResult;
|
|
38
|
+
export declare function applyInit(plan: InitResult, options?: InitOptions): InitResult;
|
|
39
|
+
export declare function revertInit(options?: InitOptions): InitResult;
|
|
40
|
+
export declare function initCommand(options?: InitOptions): Promise<number>;
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.planInit = planInit;
|
|
40
|
+
exports.applyInit = applyInit;
|
|
41
|
+
exports.revertInit = revertInit;
|
|
42
|
+
exports.initCommand = initCommand;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const vault_guard_core_1 = require("@vaultcompass/vault-guard-core");
|
|
46
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
+
const templates_1 = require("../init/templates");
|
|
48
|
+
const MCP_MERGE_HINT = 'Merge .vault-guard/mcp-snippet.json into your editor MCP config (~/.cursor/mcp.json or Claude Desktop).';
|
|
49
|
+
function isGitRepo(cwd) {
|
|
50
|
+
return fs.existsSync(path.join(cwd, '.git'));
|
|
51
|
+
}
|
|
52
|
+
function readFileIfExists(filePath) {
|
|
53
|
+
try {
|
|
54
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function parseManifest(content) {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(content);
|
|
63
|
+
if (parsed.initVersion !== '1' || !Array.isArray(parsed.files)) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function managedContentPaths(options) {
|
|
73
|
+
const skip = new Set();
|
|
74
|
+
if (options.skipConfig)
|
|
75
|
+
skip.add('.vault-guard.json');
|
|
76
|
+
if (options.skipWorkflow)
|
|
77
|
+
skip.add('.github/workflows/vault-guard.yml');
|
|
78
|
+
if (options.skipAgentRules) {
|
|
79
|
+
skip.add('.vault-guard/mcp-snippet.json');
|
|
80
|
+
skip.add('.vault-guard/agent-rules.md');
|
|
81
|
+
}
|
|
82
|
+
return templates_1.MANAGED_FILE_PATHS.filter((p) => p !== templates_1.MANIFEST_RELATIVE_PATH && !skip.has(p));
|
|
83
|
+
}
|
|
84
|
+
function hookRelativePath(cwd, hookPath) {
|
|
85
|
+
return path.relative(cwd, hookPath).split(path.sep).join('/');
|
|
86
|
+
}
|
|
87
|
+
function foreignHookConflict(cwd, manager) {
|
|
88
|
+
const hook = new vault_guard_core_1.PreCommitHook();
|
|
89
|
+
const hookPath = hook.getPreCommitHookPath(cwd, manager);
|
|
90
|
+
if (!fs.existsSync(hookPath))
|
|
91
|
+
return undefined;
|
|
92
|
+
if (hook.isInstalled({ cwd, manager }))
|
|
93
|
+
return undefined;
|
|
94
|
+
const rel = hookRelativePath(cwd, hookPath);
|
|
95
|
+
if (manager === 'husky') {
|
|
96
|
+
const content = readFileIfExists(hookPath) ?? '';
|
|
97
|
+
if (content.includes('vault-guard'))
|
|
98
|
+
return undefined;
|
|
99
|
+
return { path: rel, reason: 'foreign_hook' };
|
|
100
|
+
}
|
|
101
|
+
if (manager === 'lefthook') {
|
|
102
|
+
const localPath = path.join(cwd, 'lefthook-local.yml');
|
|
103
|
+
if (!fs.existsSync(localPath))
|
|
104
|
+
return undefined;
|
|
105
|
+
const content = readFileIfExists(localPath) ?? '';
|
|
106
|
+
if (content.includes('vault-guard scan --staged'))
|
|
107
|
+
return undefined;
|
|
108
|
+
return { path: 'lefthook-local.yml', reason: 'foreign_hook' };
|
|
109
|
+
}
|
|
110
|
+
if (manager === 'precommit') {
|
|
111
|
+
const configPath = path.join(cwd, '.pre-commit-config.yaml');
|
|
112
|
+
if (!fs.existsSync(configPath))
|
|
113
|
+
return undefined;
|
|
114
|
+
const content = readFileIfExists(configPath) ?? '';
|
|
115
|
+
if (content.includes('vault-guard scan --staged'))
|
|
116
|
+
return undefined;
|
|
117
|
+
return { path: '.pre-commit-config.yaml', reason: 'foreign_hook' };
|
|
118
|
+
}
|
|
119
|
+
const content = readFileIfExists(hookPath) ?? '';
|
|
120
|
+
if (content.trim().length === 0)
|
|
121
|
+
return undefined;
|
|
122
|
+
return { path: rel, reason: 'foreign_hook' };
|
|
123
|
+
}
|
|
124
|
+
function ensureParentDir(filePath) {
|
|
125
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
126
|
+
}
|
|
127
|
+
function planInit(options = {}) {
|
|
128
|
+
const cwd = options.cwd ?? process.cwd();
|
|
129
|
+
const dryRun = Boolean(options.dryRun);
|
|
130
|
+
const manager = options.manager ?? 'native';
|
|
131
|
+
const actions = [];
|
|
132
|
+
const conflicts = [];
|
|
133
|
+
const trackedFiles = [];
|
|
134
|
+
const manifestAbs = path.join(cwd, templates_1.MANIFEST_RELATIVE_PATH);
|
|
135
|
+
const manifestRaw = readFileIfExists(manifestAbs);
|
|
136
|
+
const manifestParsed = manifestRaw ? parseManifest(manifestRaw) : undefined;
|
|
137
|
+
if (manifestRaw && !manifestParsed) {
|
|
138
|
+
conflicts.push({ path: templates_1.MANIFEST_RELATIVE_PATH, reason: 'foreign_manifest' });
|
|
139
|
+
}
|
|
140
|
+
for (const rel of managedContentPaths(options)) {
|
|
141
|
+
const abs = path.join(cwd, rel);
|
|
142
|
+
const expected = (0, templates_1.templateContentForPath)(rel);
|
|
143
|
+
const current = readFileIfExists(abs);
|
|
144
|
+
if (current === undefined) {
|
|
145
|
+
actions.push({ kind: 'create', path: rel });
|
|
146
|
+
trackedFiles.push({ path: rel, action: 'created' });
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (current === expected) {
|
|
150
|
+
actions.push({ kind: 'skip', path: rel, detail: 'content matches template' });
|
|
151
|
+
trackedFiles.push({ path: rel, action: 'created' });
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
conflicts.push({ path: rel, reason: 'exists' });
|
|
155
|
+
}
|
|
156
|
+
let hookState;
|
|
157
|
+
if (!options.skipHook) {
|
|
158
|
+
if (!isGitRepo(cwd)) {
|
|
159
|
+
conflicts.push({ path: '.git', reason: 'not_a_git_repository' });
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const foreignHook = foreignHookConflict(cwd, manager);
|
|
163
|
+
if (foreignHook) {
|
|
164
|
+
conflicts.push(foreignHook);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const hook = new vault_guard_core_1.PreCommitHook();
|
|
168
|
+
const hookPath = hook.getPreCommitHookPath(cwd, manager);
|
|
169
|
+
if (hook.isInstalled({ cwd, manager })) {
|
|
170
|
+
hookState = { manager, path: hookPath, installed: true };
|
|
171
|
+
actions.push({ kind: 'skip', path: hookPath, detail: 'hook already installed' });
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
hookState = { manager, path: hookPath, installed: false };
|
|
175
|
+
actions.push({ kind: 'hook-install', path: hookPath, detail: manager });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const hookMeta = hookState?.path && !options.skipHook
|
|
181
|
+
? { manager, path: hookRelativePath(cwd, hookState.path) }
|
|
182
|
+
: undefined;
|
|
183
|
+
function manifestMatchesState(manifest) {
|
|
184
|
+
if (manifest.templateVersion !== templates_1.INIT_TEMPLATE_VERSION)
|
|
185
|
+
return false;
|
|
186
|
+
const expectedPaths = trackedFiles
|
|
187
|
+
.map(f => f.path)
|
|
188
|
+
.sort()
|
|
189
|
+
.join('\0');
|
|
190
|
+
const actualPaths = manifest.files
|
|
191
|
+
.map(f => f.path)
|
|
192
|
+
.sort()
|
|
193
|
+
.join('\0');
|
|
194
|
+
if (expectedPaths !== actualPaths)
|
|
195
|
+
return false;
|
|
196
|
+
if (hookMeta) {
|
|
197
|
+
return manifest.hookManager === hookMeta.manager && manifest.hookPath === hookMeta.path;
|
|
198
|
+
}
|
|
199
|
+
return manifest.hookManager === undefined && manifest.hookPath === undefined;
|
|
200
|
+
}
|
|
201
|
+
if (manifestRaw === undefined && conflicts.length === 0) {
|
|
202
|
+
actions.push({ kind: 'create', path: templates_1.MANIFEST_RELATIVE_PATH });
|
|
203
|
+
}
|
|
204
|
+
else if (manifestParsed) {
|
|
205
|
+
if (manifestMatchesState(manifestParsed)) {
|
|
206
|
+
actions.push({ kind: 'skip', path: templates_1.MANIFEST_RELATIVE_PATH, detail: 'manifest current' });
|
|
207
|
+
}
|
|
208
|
+
else if (conflicts.length === 0) {
|
|
209
|
+
conflicts.push({ path: templates_1.MANIFEST_RELATIVE_PATH, reason: 'manifest_mismatch' });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const alreadyInitialized = conflicts.length === 0 &&
|
|
213
|
+
actions.length > 0 &&
|
|
214
|
+
actions.every(a => a.kind === 'skip');
|
|
215
|
+
return {
|
|
216
|
+
ok: conflicts.length === 0,
|
|
217
|
+
dryRun,
|
|
218
|
+
reverted: false,
|
|
219
|
+
alreadyInitialized,
|
|
220
|
+
actions,
|
|
221
|
+
conflicts,
|
|
222
|
+
hook: hookState,
|
|
223
|
+
manifestPath: templates_1.MANIFEST_RELATIVE_PATH,
|
|
224
|
+
mcpMergeHint: MCP_MERGE_HINT,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function applyInit(plan, options = {}) {
|
|
228
|
+
if (!plan.ok || plan.dryRun || plan.alreadyInitialized) {
|
|
229
|
+
return plan;
|
|
230
|
+
}
|
|
231
|
+
const cwd = options.cwd ?? process.cwd();
|
|
232
|
+
const manager = options.manager ?? 'native';
|
|
233
|
+
const trackedFiles = [];
|
|
234
|
+
const createdPaths = [];
|
|
235
|
+
const rollbackCreatedFiles = () => {
|
|
236
|
+
for (const rel of createdPaths.reverse()) {
|
|
237
|
+
const abs = path.join(cwd, rel);
|
|
238
|
+
try {
|
|
239
|
+
if (fs.existsSync(abs))
|
|
240
|
+
fs.unlinkSync(abs);
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
/* best effort */
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
if (plan.hook && !plan.hook.installed && !options.skipHook) {
|
|
248
|
+
const hook = new vault_guard_core_1.PreCommitHook();
|
|
249
|
+
const result = hook.install({ cwd, manager });
|
|
250
|
+
if (!result.success) {
|
|
251
|
+
return {
|
|
252
|
+
...plan,
|
|
253
|
+
ok: false,
|
|
254
|
+
actions: [
|
|
255
|
+
...plan.actions,
|
|
256
|
+
{
|
|
257
|
+
kind: 'skip',
|
|
258
|
+
path: plan.hook.path ?? 'pre-commit',
|
|
259
|
+
detail: `hook install failed: ${result.message}`,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
plan.hook = {
|
|
265
|
+
manager,
|
|
266
|
+
path: result.hookPath ?? plan.hook.path,
|
|
267
|
+
installed: true,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
for (const action of plan.actions) {
|
|
272
|
+
if (action.kind !== 'create' || action.path === templates_1.MANIFEST_RELATIVE_PATH)
|
|
273
|
+
continue;
|
|
274
|
+
const abs = path.join(cwd, action.path);
|
|
275
|
+
ensureParentDir(abs);
|
|
276
|
+
fs.writeFileSync(abs, (0, templates_1.templateContentForPath)(action.path), {
|
|
277
|
+
encoding: 'utf8',
|
|
278
|
+
flag: 'wx',
|
|
279
|
+
});
|
|
280
|
+
trackedFiles.push({ path: action.path, action: 'created' });
|
|
281
|
+
createdPaths.push(action.path);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
rollbackCreatedFiles();
|
|
286
|
+
if (plan.hook?.installed && !options.skipHook) {
|
|
287
|
+
new vault_guard_core_1.PreCommitHook().uninstall({ cwd, manager });
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
...plan,
|
|
291
|
+
ok: false,
|
|
292
|
+
actions: [
|
|
293
|
+
...plan.actions,
|
|
294
|
+
{
|
|
295
|
+
kind: 'skip',
|
|
296
|
+
path: 'init',
|
|
297
|
+
detail: `file write failed: ${String(error)}`,
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
const hookMeta = plan.hook?.path && !options.skipHook
|
|
303
|
+
? { manager, path: hookRelativePath(cwd, plan.hook.path) }
|
|
304
|
+
: undefined;
|
|
305
|
+
const allTracked = trackedFiles.length > 0
|
|
306
|
+
? trackedFiles
|
|
307
|
+
: managedContentPaths(options).map(p => ({ path: p, action: 'created' }));
|
|
308
|
+
const manifestContent = (0, templates_1.buildManifestContent)(allTracked, hookMeta);
|
|
309
|
+
const manifestAbs = path.join(cwd, templates_1.MANIFEST_RELATIVE_PATH);
|
|
310
|
+
try {
|
|
311
|
+
if (!fs.existsSync(manifestAbs)) {
|
|
312
|
+
ensureParentDir(manifestAbs);
|
|
313
|
+
fs.writeFileSync(manifestAbs, manifestContent, { encoding: 'utf8', flag: 'wx' });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
rollbackCreatedFiles();
|
|
318
|
+
if (plan.hook?.installed && !options.skipHook) {
|
|
319
|
+
new vault_guard_core_1.PreCommitHook().uninstall({ cwd, manager });
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
...plan,
|
|
323
|
+
ok: false,
|
|
324
|
+
actions: [
|
|
325
|
+
...plan.actions,
|
|
326
|
+
{
|
|
327
|
+
kind: 'skip',
|
|
328
|
+
path: templates_1.MANIFEST_RELATIVE_PATH,
|
|
329
|
+
detail: `manifest write failed: ${String(error)}`,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return { ...plan, ok: true };
|
|
335
|
+
}
|
|
336
|
+
function revertInit(options = {}) {
|
|
337
|
+
const cwd = options.cwd ?? process.cwd();
|
|
338
|
+
const dryRun = Boolean(options.dryRun);
|
|
339
|
+
const manifestAbs = path.join(cwd, templates_1.MANIFEST_RELATIVE_PATH);
|
|
340
|
+
const raw = readFileIfExists(manifestAbs);
|
|
341
|
+
if (!raw) {
|
|
342
|
+
return {
|
|
343
|
+
ok: false,
|
|
344
|
+
dryRun,
|
|
345
|
+
reverted: false,
|
|
346
|
+
alreadyInitialized: false,
|
|
347
|
+
actions: [],
|
|
348
|
+
conflicts: [{ path: templates_1.MANIFEST_RELATIVE_PATH, reason: 'foreign_manifest' }],
|
|
349
|
+
manifestPath: templates_1.MANIFEST_RELATIVE_PATH,
|
|
350
|
+
mcpMergeHint: MCP_MERGE_HINT,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const manifest = parseManifest(raw);
|
|
354
|
+
if (!manifest) {
|
|
355
|
+
return {
|
|
356
|
+
ok: false,
|
|
357
|
+
dryRun,
|
|
358
|
+
reverted: false,
|
|
359
|
+
alreadyInitialized: false,
|
|
360
|
+
actions: [],
|
|
361
|
+
conflicts: [{ path: templates_1.MANIFEST_RELATIVE_PATH, reason: 'foreign_manifest' }],
|
|
362
|
+
manifestPath: templates_1.MANIFEST_RELATIVE_PATH,
|
|
363
|
+
mcpMergeHint: MCP_MERGE_HINT,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
const actions = [];
|
|
367
|
+
const filePaths = [...manifest.files.map(f => f.path)].reverse();
|
|
368
|
+
for (const rel of filePaths) {
|
|
369
|
+
actions.push({
|
|
370
|
+
kind: 'skip',
|
|
371
|
+
path: rel,
|
|
372
|
+
detail: dryRun ? 'would remove file' : 'removed file',
|
|
373
|
+
});
|
|
374
|
+
if (!dryRun) {
|
|
375
|
+
const abs = path.join(cwd, rel);
|
|
376
|
+
if (fs.existsSync(abs))
|
|
377
|
+
fs.unlinkSync(abs);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (manifest.hookManager) {
|
|
381
|
+
actions.push({
|
|
382
|
+
kind: 'hook-install',
|
|
383
|
+
path: manifest.hookPath ?? 'pre-commit',
|
|
384
|
+
detail: dryRun ? 'would uninstall hook' : 'uninstalled hook',
|
|
385
|
+
});
|
|
386
|
+
if (!dryRun) {
|
|
387
|
+
new vault_guard_core_1.PreCommitHook().uninstall({
|
|
388
|
+
cwd,
|
|
389
|
+
manager: manifest.hookManager,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
actions.push({
|
|
394
|
+
kind: 'skip',
|
|
395
|
+
path: templates_1.MANIFEST_RELATIVE_PATH,
|
|
396
|
+
detail: dryRun ? 'would remove manifest' : 'removed manifest',
|
|
397
|
+
});
|
|
398
|
+
if (!dryRun && fs.existsSync(manifestAbs)) {
|
|
399
|
+
fs.unlinkSync(manifestAbs);
|
|
400
|
+
const vgDir = path.join(cwd, '.vault-guard');
|
|
401
|
+
try {
|
|
402
|
+
if (fs.existsSync(vgDir) && fs.readdirSync(vgDir).length === 0) {
|
|
403
|
+
fs.rmdirSync(vgDir);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
/* best effort */
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
ok: true,
|
|
412
|
+
dryRun,
|
|
413
|
+
reverted: !dryRun,
|
|
414
|
+
alreadyInitialized: false,
|
|
415
|
+
actions,
|
|
416
|
+
conflicts: [],
|
|
417
|
+
manifestPath: templates_1.MANIFEST_RELATIVE_PATH,
|
|
418
|
+
mcpMergeHint: MCP_MERGE_HINT,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function printHuman(result, options) {
|
|
422
|
+
if (options.revert) {
|
|
423
|
+
if (!result.ok) {
|
|
424
|
+
console.error(chalk_1.default.red('❌ Revert failed:'), 'no valid init manifest found');
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
console.log(result.dryRun
|
|
428
|
+
? chalk_1.default.blue('🔍 Dry-run revert plan')
|
|
429
|
+
: chalk_1.default.green.bold('✅ Reverted Vault Guard init artifacts'));
|
|
430
|
+
for (const a of result.actions) {
|
|
431
|
+
console.log(chalk_1.default.gray(` ${a.detail ?? a.path}`));
|
|
432
|
+
}
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (result.conflicts.length > 0) {
|
|
436
|
+
console.error(chalk_1.default.red.bold('❌ Init blocked — conflicts (no automatic overwrites):'));
|
|
437
|
+
for (const c of result.conflicts) {
|
|
438
|
+
console.error(chalk_1.default.white(` ${c.path}`), chalk_1.default.gray(`(${c.reason})`));
|
|
439
|
+
}
|
|
440
|
+
console.error(chalk_1.default.gray('\nResolve manually, then re-run vault-guard init.'));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (result.alreadyInitialized) {
|
|
444
|
+
console.log(chalk_1.default.green('✅ Already initialized — no changes needed'));
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (result.dryRun) {
|
|
448
|
+
console.log(chalk_1.default.blue('🔍 Dry-run — would apply:'));
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
console.log(chalk_1.default.green.bold('✅ Vault Guard initialized'));
|
|
452
|
+
}
|
|
453
|
+
for (const a of result.actions) {
|
|
454
|
+
if (a.kind === 'create') {
|
|
455
|
+
console.log(chalk_1.default.white(` ${result.dryRun ? 'create' : 'created'} ${a.path}`));
|
|
456
|
+
}
|
|
457
|
+
else if (a.kind === 'hook-install') {
|
|
458
|
+
console.log(chalk_1.default.white(` ${result.dryRun ? 'install' : 'installed'} hook (${a.detail})`));
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
console.log(chalk_1.default.gray(`\n${result.mcpMergeHint}`));
|
|
462
|
+
console.log(chalk_1.default.gray(`Manifest: ${result.manifestPath}`));
|
|
463
|
+
}
|
|
464
|
+
async function initCommand(options = {}) {
|
|
465
|
+
if (options.revert) {
|
|
466
|
+
const result = revertInit(options);
|
|
467
|
+
if (options.json) {
|
|
468
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
printHuman(result, options);
|
|
472
|
+
}
|
|
473
|
+
return result.ok ? 0 : 1;
|
|
474
|
+
}
|
|
475
|
+
const plan = planInit(options);
|
|
476
|
+
const result = plan.dryRun || plan.alreadyInitialized ? plan : applyInit(plan, options);
|
|
477
|
+
if (options.json) {
|
|
478
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
printHuman(result, options);
|
|
482
|
+
}
|
|
483
|
+
if (result.conflicts.length > 0)
|
|
484
|
+
return 2;
|
|
485
|
+
if (!result.ok)
|
|
486
|
+
return 1;
|
|
487
|
+
return 0;
|
|
488
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Stable init template version — bump when file contents change materially. */
|
|
2
|
+
export declare const INIT_TEMPLATE_VERSION = "1";
|
|
3
|
+
export declare const MANIFEST_RELATIVE_PATH = ".vault-guard/manifest.json";
|
|
4
|
+
export declare const MANAGED_FILE_PATHS: readonly [".vault-guard.json", ".github/workflows/vault-guard.yml", ".vault-guard/mcp-snippet.json", ".vault-guard/agent-rules.md", ".vault-guard/manifest.json"];
|
|
5
|
+
export type ManagedFilePath = (typeof MANAGED_FILE_PATHS)[number];
|
|
6
|
+
export declare function defaultVaultGuardConfigJson(): string;
|
|
7
|
+
export declare function githubWorkflowYaml(): string;
|
|
8
|
+
export declare function mcpSnippetJson(): string;
|
|
9
|
+
export declare function agentRulesMarkdown(): string;
|
|
10
|
+
export declare function templateContentForPath(relativePath: ManagedFilePath): string;
|
|
11
|
+
export interface InitManifest {
|
|
12
|
+
initVersion: string;
|
|
13
|
+
templateVersion: string;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
hookManager?: string;
|
|
16
|
+
hookPath?: string;
|
|
17
|
+
files: Array<{
|
|
18
|
+
path: string;
|
|
19
|
+
action: 'created';
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export declare function buildManifestContent(files: Array<{
|
|
23
|
+
path: string;
|
|
24
|
+
action: 'created';
|
|
25
|
+
}>, hook?: {
|
|
26
|
+
manager: string;
|
|
27
|
+
path: string;
|
|
28
|
+
}): string;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MANAGED_FILE_PATHS = exports.MANIFEST_RELATIVE_PATH = exports.INIT_TEMPLATE_VERSION = void 0;
|
|
4
|
+
exports.defaultVaultGuardConfigJson = defaultVaultGuardConfigJson;
|
|
5
|
+
exports.githubWorkflowYaml = githubWorkflowYaml;
|
|
6
|
+
exports.mcpSnippetJson = mcpSnippetJson;
|
|
7
|
+
exports.agentRulesMarkdown = agentRulesMarkdown;
|
|
8
|
+
exports.templateContentForPath = templateContentForPath;
|
|
9
|
+
exports.buildManifestContent = buildManifestContent;
|
|
10
|
+
/** Stable init template version — bump when file contents change materially. */
|
|
11
|
+
exports.INIT_TEMPLATE_VERSION = '1';
|
|
12
|
+
exports.MANIFEST_RELATIVE_PATH = '.vault-guard/manifest.json';
|
|
13
|
+
exports.MANAGED_FILE_PATHS = [
|
|
14
|
+
'.vault-guard.json',
|
|
15
|
+
'.github/workflows/vault-guard.yml',
|
|
16
|
+
'.vault-guard/mcp-snippet.json',
|
|
17
|
+
'.vault-guard/agent-rules.md',
|
|
18
|
+
exports.MANIFEST_RELATIVE_PATH,
|
|
19
|
+
];
|
|
20
|
+
function defaultVaultGuardConfigJson() {
|
|
21
|
+
return `${JSON.stringify({
|
|
22
|
+
ignore: {
|
|
23
|
+
patterns: ['**/__tests__/**', 'fixtures/**', 'bench/fixtures/**'],
|
|
24
|
+
},
|
|
25
|
+
}, null, 2)}\n`;
|
|
26
|
+
}
|
|
27
|
+
function githubWorkflowYaml() {
|
|
28
|
+
return `name: Vault Guard
|
|
29
|
+
|
|
30
|
+
on:
|
|
31
|
+
push:
|
|
32
|
+
branches: [main]
|
|
33
|
+
pull_request:
|
|
34
|
+
branches: [main]
|
|
35
|
+
|
|
36
|
+
jobs:
|
|
37
|
+
secrets:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
41
|
+
- uses: vaultcompasshq/vault-guard@v1.1.2
|
|
42
|
+
with:
|
|
43
|
+
version: latest
|
|
44
|
+
path: .
|
|
45
|
+
format: sarif
|
|
46
|
+
sarif-output: vault-guard-results.sarif
|
|
47
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
48
|
+
if: always()
|
|
49
|
+
with:
|
|
50
|
+
sarif_file: vault-guard-results.sarif
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
function mcpSnippetJson() {
|
|
54
|
+
return `${JSON.stringify({
|
|
55
|
+
mcpServers: {
|
|
56
|
+
'vault-guard': {
|
|
57
|
+
command: 'npx',
|
|
58
|
+
args: ['-y', '@vaultcompass/vault-guard-mcp'],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
}, null, 2)}\n`;
|
|
62
|
+
}
|
|
63
|
+
function agentRulesMarkdown() {
|
|
64
|
+
return `# Vault Guard — agent guardrails
|
|
65
|
+
|
|
66
|
+
Vault Guard is the local secret scanner for this repository. Follow these rules
|
|
67
|
+
before writing, editing, or committing code.
|
|
68
|
+
|
|
69
|
+
## Before applying edits
|
|
70
|
+
|
|
71
|
+
1. Call the Vault Guard MCP tool \`scan_text\` on any proposed file content that
|
|
72
|
+
may contain credentials (API keys, tokens, connection strings, private keys).
|
|
73
|
+
2. If findings are returned, do **not** write the secret material. Redact or
|
|
74
|
+
replace with environment variables / placeholders and scan again.
|
|
75
|
+
3. For whole files on disk, use \`scan_file\`. For directories, use
|
|
76
|
+
\`scan_workspace\`.
|
|
77
|
+
|
|
78
|
+
## Before committing
|
|
79
|
+
|
|
80
|
+
- Ensure \`vault-guard scan --staged\` passes (pre-commit hook enforces this).
|
|
81
|
+
- Never use \`git commit --no-verify\` to bypass secret checks unless the user
|
|
82
|
+
explicitly requests an emergency bypass.
|
|
83
|
+
|
|
84
|
+
## Merge MCP config (manual)
|
|
85
|
+
|
|
86
|
+
Copy the \`mcpServers\` block from \`.vault-guard/mcp-snippet.json\` into your
|
|
87
|
+
editor MCP config (e.g. \`~/.cursor/mcp.json\` or Claude Desktop config). Vault
|
|
88
|
+
Guard does not modify files outside this repository.
|
|
89
|
+
|
|
90
|
+
## History scanning
|
|
91
|
+
|
|
92
|
+
Vault Guard does not scan Git history. Use Gitleaks or TruffleHog for retroactive
|
|
93
|
+
history mining alongside Vault Guard's working-tree protection.
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
function templateContentForPath(relativePath) {
|
|
97
|
+
switch (relativePath) {
|
|
98
|
+
case '.vault-guard.json':
|
|
99
|
+
return defaultVaultGuardConfigJson();
|
|
100
|
+
case '.github/workflows/vault-guard.yml':
|
|
101
|
+
return githubWorkflowYaml();
|
|
102
|
+
case '.vault-guard/mcp-snippet.json':
|
|
103
|
+
return mcpSnippetJson();
|
|
104
|
+
case '.vault-guard/agent-rules.md':
|
|
105
|
+
return agentRulesMarkdown();
|
|
106
|
+
default:
|
|
107
|
+
throw new Error(`No template for ${relativePath}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function buildManifestContent(files, hook) {
|
|
111
|
+
const manifest = {
|
|
112
|
+
initVersion: '1',
|
|
113
|
+
templateVersion: exports.INIT_TEMPLATE_VERSION,
|
|
114
|
+
createdAt: new Date().toISOString(),
|
|
115
|
+
files,
|
|
116
|
+
...(hook ? { hookManager: hook.manager, hookPath: hook.path } : {}),
|
|
117
|
+
};
|
|
118
|
+
return `${JSON.stringify(manifest, null, 2)}\n`;
|
|
119
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaultcompass/vault-guard",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Block secrets at commit and in CI. Pre-commit hooks, SARIF output, and fast staged-file scans for AI-native workflows.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"chalk": "^4.1.2",
|
|
38
38
|
"commander": "^12.0.0",
|
|
39
|
-
"@vaultcompass/vault-guard-core": "1.
|
|
40
|
-
"@vaultcompass/vault-guard-telemetry": "1.
|
|
39
|
+
"@vaultcompass/vault-guard-core": "1.2.0",
|
|
40
|
+
"@vaultcompass/vault-guard-telemetry": "1.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/jest": "^30.0.0",
|