cc-safety-net 0.8.0 → 0.8.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/README.md +6 -89
- package/dist/bin/cc-safety-net.js +24 -9
- package/dist/bin/doctor/hooks.d.ts +1 -0
- package/dist/bin/doctor/types.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -164,11 +164,9 @@ Running both together provides defense-in-depth. Sandboxing handles unknown thre
|
|
|
164
164
|
```bash
|
|
165
165
|
/plugin marketplace add kenryu42/cc-marketplace
|
|
166
166
|
/plugin install safety-net@cc-marketplace
|
|
167
|
+
/reload-plugins
|
|
167
168
|
```
|
|
168
169
|
|
|
169
|
-
> [!NOTE]
|
|
170
|
-
> After installing the plugin, you need to restart your Claude Code for it to take effect.
|
|
171
|
-
|
|
172
170
|
### Claude Code Auto-Update
|
|
173
171
|
|
|
174
172
|
1. Run `/plugin` → Select `Marketplaces` → Choose `cc-marketplace` → Enable auto-update
|
|
@@ -207,95 +205,14 @@ gemini extensions install https://github.com/kenryu42/gemini-safety-net
|
|
|
207
205
|
|
|
208
206
|
### GitHub Copilot CLI Installation
|
|
209
207
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
> [!NOTE]
|
|
213
|
-
> Copilot CLI currently supports two hook configuration styles:
|
|
214
|
-
>
|
|
215
|
-
> - Hook files:
|
|
216
|
-
> - repository: `.github/hooks/*.json`
|
|
217
|
-
> - user: `~/.copilot/hooks/*.json` on Copilot CLI `0.0.422+`
|
|
218
|
-
> - Inline `hooks` inside Copilot config files on Copilot CLI `1.0.8+`:
|
|
219
|
-
> - user: `~/.copilot/config.json`
|
|
220
|
-
> - repository: `.github/copilot/settings.json`
|
|
221
|
-
> - local override: `.github/copilot/settings.local.json`
|
|
222
|
-
>
|
|
223
|
-
> Copilot settings cascade from user -> repository -> local (later files override earlier ones, so local overrides repository overrides user). `disableAllHooks: true` disables both repo-level and user-level hooks. If you use `COPILOT_HOME`, replace `~/.copilot` with that directory.
|
|
224
|
-
|
|
225
|
-
#### Option A: Hook Files
|
|
226
|
-
|
|
227
|
-
This is the classic hook-file format. It still works, and it is the easiest shared setup for a repository.
|
|
228
|
-
|
|
229
|
-
1. **Create the hooks directory** in your repository:
|
|
230
|
-
|
|
231
|
-
```bash
|
|
232
|
-
mkdir -p .github/hooks
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
2. **Create `.github/hooks/safety-net.json`**:
|
|
236
|
-
|
|
237
|
-
```json
|
|
238
|
-
{
|
|
239
|
-
"version": 1,
|
|
240
|
-
"hooks": {
|
|
241
|
-
"preToolUse": [
|
|
242
|
-
{
|
|
243
|
-
"type": "command",
|
|
244
|
-
"bash": "npx -y cc-safety-net --copilot-cli",
|
|
245
|
-
"cwd": ".",
|
|
246
|
-
"timeoutSec": 15
|
|
247
|
-
}
|
|
248
|
-
]
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
3. **Restart Copilot CLI** — hooks are loaded at session start.
|
|
254
|
-
|
|
255
|
-
The hook will intercept shell commands and block destructive operations before they execute.
|
|
256
|
-
|
|
257
|
-
To install the same hook globally for your user account on Copilot CLI `0.0.422+`, place the same JSON file in:
|
|
258
|
-
|
|
259
|
-
- `~/.copilot/hooks/safety-net.json`
|
|
260
|
-
|
|
261
|
-
#### Option B: Inline Hooks In Copilot Settings
|
|
262
|
-
|
|
263
|
-
On Copilot CLI `1.0.8+`, you can define the same hook inline in Copilot settings files instead of a separate `.json` file under `.github/hooks` or `~/.copilot/hooks`.
|
|
264
|
-
|
|
265
|
-
```json
|
|
266
|
-
{
|
|
267
|
-
"hooks": {
|
|
268
|
-
"preToolUse": [
|
|
269
|
-
{
|
|
270
|
-
"type": "command",
|
|
271
|
-
"bash": "npx -y cc-safety-net --copilot-cli",
|
|
272
|
-
"cwd": ".",
|
|
273
|
-
"timeoutSec": 15
|
|
274
|
-
}
|
|
275
|
-
]
|
|
276
|
-
}
|
|
277
|
-
}
|
|
208
|
+
```bash
|
|
209
|
+
/plugin install kenryu42/copilot-safety-net
|
|
278
210
|
```
|
|
279
211
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
- `~/.copilot/config.json`
|
|
283
|
-
- `.github/copilot/settings.json`
|
|
284
|
-
- `.github/copilot/settings.local.json`
|
|
285
|
-
|
|
286
|
-
Recommended usage:
|
|
287
|
-
|
|
288
|
-
- Use `~/.copilot/config.json` for your personal default across repositories.
|
|
289
|
-
- Use `.github/copilot/settings.json` for a committed repository-wide setup.
|
|
290
|
-
- Use `.github/copilot/settings.local.json` for personal repo-specific overrides, and add it to `.gitignore`.
|
|
291
|
-
|
|
292
|
-
If you need to turn hooks off explicitly, set:
|
|
212
|
+
> [!NOTE]
|
|
213
|
+
> After installing the plugin, you need to restart your Copilot CLI for it to take effect.
|
|
293
214
|
|
|
294
|
-
|
|
295
|
-
{
|
|
296
|
-
"disableAllHooks": true
|
|
297
|
-
}
|
|
298
|
-
```
|
|
215
|
+
---
|
|
299
216
|
|
|
300
217
|
## Status Line Integration
|
|
301
218
|
|
|
@@ -3731,6 +3731,7 @@ function analyzeCommand(command, options = {}) {
|
|
|
3731
3731
|
}
|
|
3732
3732
|
|
|
3733
3733
|
// src/bin/doctor/hooks.ts
|
|
3734
|
+
var COPILOT_PLUGIN_CONFIG_PATH = "copilot-plugin";
|
|
3734
3735
|
var SELF_TEST_CASES = [
|
|
3735
3736
|
{ command: "git reset --hard", description: "git reset --hard", expectBlocked: true },
|
|
3736
3737
|
{ command: "rm -rf /", description: "rm -rf /", expectBlocked: true },
|
|
@@ -4178,13 +4179,15 @@ function detectAllHooks(cwd, options) {
|
|
|
4178
4179
|
errors: errors.length > 0 ? errors : undefined
|
|
4179
4180
|
};
|
|
4180
4181
|
}
|
|
4181
|
-
if (hooksCheck.activeConfigPaths.length > 0) {
|
|
4182
|
+
if (options?.copilotPluginInstalled === true || hooksCheck.activeConfigPaths.length > 0) {
|
|
4183
|
+
const viaPlugin = options?.copilotPluginInstalled === true;
|
|
4184
|
+
const primaryConfigPath = hooksCheck.activeConfigPaths[0];
|
|
4182
4185
|
return {
|
|
4183
4186
|
platform: "copilot-cli",
|
|
4184
4187
|
status: "configured",
|
|
4185
|
-
method: "hook config",
|
|
4186
|
-
configPath:
|
|
4187
|
-
configPaths: hooksCheck.activeConfigPaths,
|
|
4188
|
+
method: viaPlugin ? "plugin list" : "hook config",
|
|
4189
|
+
configPath: primaryConfigPath ?? (viaPlugin ? COPILOT_PLUGIN_CONFIG_PATH : undefined),
|
|
4190
|
+
configPaths: hooksCheck.activeConfigPaths.length > 0 ? hooksCheck.activeConfigPaths : undefined,
|
|
4188
4191
|
selfTest: runSelfTest(),
|
|
4189
4192
|
errors: errors.length > 0 ? errors : undefined
|
|
4190
4193
|
};
|
|
@@ -4205,11 +4208,12 @@ function detectAllHooks(cwd, options) {
|
|
|
4205
4208
|
|
|
4206
4209
|
// src/bin/doctor/system-info.ts
|
|
4207
4210
|
import { spawn } from "node:child_process";
|
|
4208
|
-
var CURRENT_VERSION = "0.8.
|
|
4211
|
+
var CURRENT_VERSION = "0.8.1";
|
|
4209
4212
|
var VERSION_FETCH_TIMEOUT_MS = 2000;
|
|
4210
4213
|
function getPackageVersion() {
|
|
4211
4214
|
return CURRENT_VERSION;
|
|
4212
4215
|
}
|
|
4216
|
+
var COPILOT_PLUGIN_ID = "copilot-safety-net";
|
|
4213
4217
|
var defaultVersionFetcher = async (args) => {
|
|
4214
4218
|
const [cmd, ...rest] = args;
|
|
4215
4219
|
if (!cmd)
|
|
@@ -4260,6 +4264,12 @@ function parseVersion(output) {
|
|
|
4260
4264
|
`)[0]?.trim();
|
|
4261
4265
|
return firstLine || null;
|
|
4262
4266
|
}
|
|
4267
|
+
function hasCopilotSafetyNetPlugin(output) {
|
|
4268
|
+
if (!output)
|
|
4269
|
+
return false;
|
|
4270
|
+
const pluginPattern = new RegExp(`(^|[^a-z0-9-])${COPILOT_PLUGIN_ID}([^a-z0-9-]|$)`, "m");
|
|
4271
|
+
return pluginPattern.test(output);
|
|
4272
|
+
}
|
|
4263
4273
|
async function getSystemInfo(fetcher = defaultVersionFetcher) {
|
|
4264
4274
|
const fetchCopilotVersion = async () => {
|
|
4265
4275
|
const binaryVersionPromise = fetcher(["copilot", "--binary-version"]);
|
|
@@ -4270,14 +4280,15 @@ async function getSystemInfo(fetcher = defaultVersionFetcher) {
|
|
|
4270
4280
|
}
|
|
4271
4281
|
return fallbackVersionPromise;
|
|
4272
4282
|
};
|
|
4273
|
-
const [claudeRaw, openCodeRaw, geminiRaw, copilotRaw, nodeRaw, npmRaw, bunRaw] = await Promise.all([
|
|
4283
|
+
const [claudeRaw, openCodeRaw, geminiRaw, copilotRaw, nodeRaw, npmRaw, bunRaw, pluginListRaw] = await Promise.all([
|
|
4274
4284
|
fetcher(["claude", "--version"]),
|
|
4275
4285
|
fetcher(["opencode", "--version"]),
|
|
4276
4286
|
fetcher(["gemini", "--version"]),
|
|
4277
4287
|
fetchCopilotVersion(),
|
|
4278
4288
|
fetcher(["node", "--version"]),
|
|
4279
4289
|
fetcher(["npm", "--version"]),
|
|
4280
|
-
fetcher(["bun", "--version"])
|
|
4290
|
+
fetcher(["bun", "--version"]),
|
|
4291
|
+
fetcher(["copilot", "plugin", "list"])
|
|
4281
4292
|
]);
|
|
4282
4293
|
return {
|
|
4283
4294
|
version: CURRENT_VERSION,
|
|
@@ -4288,6 +4299,7 @@ async function getSystemInfo(fetcher = defaultVersionFetcher) {
|
|
|
4288
4299
|
nodeVersion: parseVersion(nodeRaw),
|
|
4289
4300
|
npmVersion: parseVersion(npmRaw),
|
|
4290
4301
|
bunVersion: parseVersion(bunRaw),
|
|
4302
|
+
copilotPluginInstalled: hasCopilotSafetyNetPlugin(pluginListRaw),
|
|
4291
4303
|
platform: `${process.platform} ${process.arch}`
|
|
4292
4304
|
};
|
|
4293
4305
|
}
|
|
@@ -4353,7 +4365,10 @@ function parseDoctorFlags(args) {
|
|
|
4353
4365
|
async function runDoctor(options = {}) {
|
|
4354
4366
|
const cwd = options.cwd ?? process.cwd();
|
|
4355
4367
|
const system = await getSystemInfo();
|
|
4356
|
-
const hooks = detectAllHooks(cwd, {
|
|
4368
|
+
const hooks = detectAllHooks(cwd, {
|
|
4369
|
+
copilotCliVersion: system.copilotCliVersion,
|
|
4370
|
+
copilotPluginInstalled: system.copilotPluginInstalled
|
|
4371
|
+
});
|
|
4357
4372
|
const configInfo = getConfigInfo(cwd);
|
|
4358
4373
|
const environment = getEnvironmentInfo();
|
|
4359
4374
|
const activity = getActivitySummary(7);
|
|
@@ -5276,7 +5291,7 @@ function formatTraceJson(result) {
|
|
|
5276
5291
|
return JSON.stringify(result, null, 2);
|
|
5277
5292
|
}
|
|
5278
5293
|
// src/bin/help.ts
|
|
5279
|
-
var version = "0.8.
|
|
5294
|
+
var version = "0.8.1";
|
|
5280
5295
|
var INDENT = " ";
|
|
5281
5296
|
var PROGRAM_NAME = "cc-safety-net";
|
|
5282
5297
|
function formatOptionFlags(option) {
|
|
@@ -6,6 +6,7 @@ import type { LoadConfigOptions } from '@/core/config';
|
|
|
6
6
|
interface HookDetectOptions extends LoadConfigOptions {
|
|
7
7
|
homeDir?: string;
|
|
8
8
|
copilotCliVersion?: string | null;
|
|
9
|
+
copilotPluginInstalled?: boolean;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Strip JSONC-style comments and trailing commas from a string.
|
|
@@ -105,6 +105,8 @@ export interface SystemInfo {
|
|
|
105
105
|
npmVersion: string | null;
|
|
106
106
|
/** Bun version (from `bun --version`) */
|
|
107
107
|
bunVersion: string | null;
|
|
108
|
+
/** Whether the copilot-safety-net plugin is installed (from `copilot plugin list`) */
|
|
109
|
+
copilotPluginInstalled: boolean;
|
|
108
110
|
/** Platform (e.g., "darwin arm64") */
|
|
109
111
|
platform: string;
|
|
110
112
|
}
|
package/package.json
CHANGED