@jeger-ai/opengantry 3.6.0 → 3.7.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.
Files changed (53) hide show
  1. package/README.md +21 -0
  2. package/dist/cli/cage-bin.d.ts +3 -0
  3. package/dist/cli/cage-bin.d.ts.map +1 -0
  4. package/dist/cli/cage-bin.js +11 -0
  5. package/dist/cli/cage-bin.js.map +1 -0
  6. package/dist/cli/commands/cage.d.ts +15 -0
  7. package/dist/cli/commands/cage.d.ts.map +1 -0
  8. package/dist/cli/commands/cage.js +71 -0
  9. package/dist/cli/commands/cage.js.map +1 -0
  10. package/dist/cli/lib/cage/cage-process.d.ts +18 -0
  11. package/dist/cli/lib/cage/cage-process.d.ts.map +1 -0
  12. package/dist/cli/lib/cage/cage-process.js +39 -0
  13. package/dist/cli/lib/cage/cage-process.js.map +1 -0
  14. package/dist/cli/lib/cage/cage-push-guard.d.ts +23 -0
  15. package/dist/cli/lib/cage/cage-push-guard.d.ts.map +1 -0
  16. package/dist/cli/lib/cage/cage-push-guard.js +116 -0
  17. package/dist/cli/lib/cage/cage-push-guard.js.map +1 -0
  18. package/dist/cli/lib/cage/cage-report.d.ts +71 -0
  19. package/dist/cli/lib/cage/cage-report.d.ts.map +1 -0
  20. package/dist/cli/lib/cage/cage-report.js +118 -0
  21. package/dist/cli/lib/cage/cage-report.js.map +1 -0
  22. package/dist/cli/lib/cage/cage-revert.d.ts +18 -0
  23. package/dist/cli/lib/cage/cage-revert.d.ts.map +1 -0
  24. package/dist/cli/lib/cage/cage-revert.js +56 -0
  25. package/dist/cli/lib/cage/cage-revert.js.map +1 -0
  26. package/dist/cli/lib/cage/cage-rules.d.ts +24 -0
  27. package/dist/cli/lib/cage/cage-rules.d.ts.map +1 -0
  28. package/dist/cli/lib/cage/cage-rules.js +94 -0
  29. package/dist/cli/lib/cage/cage-rules.js.map +1 -0
  30. package/dist/cli/lib/cage/cage-run.d.ts +34 -0
  31. package/dist/cli/lib/cage/cage-run.d.ts.map +1 -0
  32. package/dist/cli/lib/cage/cage-run.js +107 -0
  33. package/dist/cli/lib/cage/cage-run.js.map +1 -0
  34. package/dist/cli/lib/cage/cage-snapshot.d.ts +36 -0
  35. package/dist/cli/lib/cage/cage-snapshot.d.ts.map +1 -0
  36. package/dist/cli/lib/cage/cage-snapshot.js +128 -0
  37. package/dist/cli/lib/cage/cage-snapshot.js.map +1 -0
  38. package/dist/cli/lib/cage/cage-watch.d.ts +36 -0
  39. package/dist/cli/lib/cage/cage-watch.d.ts.map +1 -0
  40. package/dist/cli/lib/cage/cage-watch.js +92 -0
  41. package/dist/cli/lib/cage/cage-watch.js.map +1 -0
  42. package/dist/cli/lib/version.gen.d.ts +1 -1
  43. package/dist/cli/lib/version.gen.js +1 -1
  44. package/dist/cli/program-cage.d.ts +5 -0
  45. package/dist/cli/program-cage.d.ts.map +1 -0
  46. package/dist/cli/program-cage.js +29 -0
  47. package/dist/cli/program-cage.js.map +1 -0
  48. package/dist/cli/program.d.ts.map +1 -1
  49. package/dist/cli/program.js +2 -0
  50. package/dist/cli/program.js.map +1 -1
  51. package/package.json +3 -2
  52. package/templates/.gitagent/foreman/SUBSTRATE.version.json +1 -1
  53. package/templates/integrations/compatibility.json +1 -1
package/README.md CHANGED
@@ -139,6 +139,23 @@ See [`docs/DOMAINS.md`](docs/DOMAINS.md) for adapter details and [`docs/AGENT-GR
139
139
 
140
140
  ---
141
141
 
142
+ ## Try it in 60 seconds: `gantry cage`
143
+
144
+ No setup. Start your agent inside the cage, exactly as you normally would:
145
+
146
+ ```bash
147
+ npx -p @jeger-ai/opengantry opengantry-cage -- claude
148
+ # installed (npm install -g @jeger-ai/opengantry): gantry cage -- claude, gantry cage -- aider
149
+ ```
150
+
151
+ While the session runs, cage checks about once a second and restores changes to CI configs, `.env` / `.env.*`, `.git/config`, `.git/hooks/` (and `core.hooksPath`) and your manifest `forbidden_zones` from an in-memory snapshot. Lockfile changes are listed but kept. A `git push` from inside the session is refused when the outgoing commits touch those paths, and the agent sees why. When you quit, cage prints one report: everything it restored, any refused pushes, exit `3` if anything was touched (otherwise the agent's own exit code).
152
+
153
+ Nothing is printed into the agent's screen while it runs: events go to a session log in your temp dir (path shown at start), with a terminal bell per restore. If the agent keeps rewriting the same file, cage stops restoring it after 3 tries, marks it contested and restores it once at exit.
154
+
155
+ Cage restores; it does not block. A change can be committed in the second before the next check, `git push --no-verify` skips the push guard, and a committed change stays in local history (cage restores the working tree only). Reads, network calls and writes outside the protected set are not seen. `--no-watch` checks once at exit instead. For scope enforced on every write, use missions (below). Details: [`docs/FEATURES.md`](docs/FEATURES.md#zero-config-cage-gantry-cage).
156
+
157
+ ---
158
+
142
159
  ## Feature tour: what to try first
143
160
 
144
161
  ### 1. Bootstrap a repo
@@ -214,6 +231,10 @@ On failure, external agents ingest `findings[]`:
214
231
  }
215
232
  ```
216
233
 
234
+ **v3.7.1:** `gantry cage` restores protected files live during interactive agent sessions (default; `--no-watch` for exit-only) and refuses `git push` of protected paths from inside the session. See [Try it in 60 seconds](#try-it-in-60-seconds-gantry-cage).
235
+
236
+ **v3.7.0:** zero-config `gantry cage -- <cmd>` (and `npx -p @jeger-ai/opengantry opengantry-cage -- <cmd>`) restores CI configs, `.env*`, git hooks/config and manifest forbidden zones after a one-shot agent run, reports lockfile changes, and exits 3 on tampering. See [Try it in 60 seconds](#try-it-in-60-seconds-gantry-cage).
237
+
217
238
  **v3.6.0:** `gate_adapter: tsc|eslint` fails closed on chained `gate_command`s (`GXT_GATE_ADAPTER_MISCONFIG`). `gantry hooks install` arms the tracked pre-commit hook so `gantry tmvc guard --strict` blocks commits outside the pinned mission. Optional local sqlite-vec contract drift index (`gantry contract embed`, `legislate --from-intent --embedding-file`). See [`docs/CHANGELOG.md`](docs/CHANGELOG.md) upgrade notes.
218
239
 
219
240
  **v3.5.0:** Cursor MCP uses a byte-identical `./scripts/mcp-launcher.sh` (no mcp.json parity exemption). `gantry verify --format sarif|junit` writes the document only on stdout and diagnostics on stderr ([`docs/CI.md`](docs/CI.md)). `gantry contract preflight` is advisory and fail-open ([ADR-0046](.gitagent/out-of-scope/ADR-0046-experimental-contract-preflight.md)).
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cage-bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-bin.d.ts","sourceRoot":"","sources":["../../src/cli/cage-bin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { CLI_VERSION } from "./lib/constants.js";
4
+ import { reportUserFacingError } from "./lib/errors.js";
5
+ import { resetOutputContext } from "./lib/output-context.js";
6
+ import { configureCageCommand } from "./program-cage.js";
7
+ /** Standalone `opengantry-cage [options] -- <command...>`, equivalent to `gantry cage`. */
8
+ resetOutputContext();
9
+ const program = configureCageCommand(new Command("opengantry-cage")).version(CLI_VERSION);
10
+ program.parseAsync(process.argv).catch(reportUserFacingError);
11
+ //# sourceMappingURL=cage-bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-bin.js","sourceRoot":"","sources":["../../src/cli/cage-bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,2FAA2F;AAC3F,kBAAkB,EAAE,CAAC;AACrB,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;AAC1F,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface CageCliOptions {
2
+ command: string[];
3
+ json?: boolean;
4
+ reportOnly?: boolean;
5
+ maxFileBytes?: string;
6
+ /** Commander sets false for `--no-watch`. */
7
+ watch?: boolean;
8
+ watchIntervalMs?: string;
9
+ }
10
+ export declare function runCageCommand(options: CageCliOptions): Promise<void>;
11
+ /** `gantry cage-guard pre-push --root <dir>`: called by the cage session pre-push hook. */
12
+ export declare function runCageGuardPrePush(options: {
13
+ root: string;
14
+ }): Promise<void>;
15
+ //# sourceMappingURL=cage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cage.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AASD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC3E;AAED,2FAA2F;AAC3F,wBAAsB,mBAAmB,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAalF"}
@@ -0,0 +1,71 @@
1
+ import fs from "node:fs";
2
+ import { logError, readStdinIfEmpty, setExitCode } from "../lib/cli-io.js";
3
+ import { emitCliJson, runUserCommandAsync } from "../lib/command-boundary.js";
4
+ import { CAGE_SESSION_LOG_ENV, findPushViolations } from "../lib/cage/cage-push-guard.js";
5
+ import { formatCageReport } from "../lib/cage/cage-report.js";
6
+ import { buildCagePlan } from "../lib/cage/cage-rules.js";
7
+ import { runCage } from "../lib/cage/cage-run.js";
8
+ function parseNonNegativeInt(raw) {
9
+ if (raw === undefined)
10
+ return { ok: true, value: undefined };
11
+ const t = raw.trim();
12
+ if (!/^\d+$/.test(t))
13
+ return { ok: false };
14
+ return { ok: true, value: Number.parseInt(t, 10) };
15
+ }
16
+ export async function runCageCommand(options) {
17
+ const command = options.command[0] === "--" ? options.command.slice(1) : options.command;
18
+ if (command.length === 0) {
19
+ logError("cage: missing command. Use: gantry cage -- <command...>");
20
+ setExitCode(2);
21
+ return;
22
+ }
23
+ const max = parseNonNegativeInt(options.maxFileBytes);
24
+ const interval = parseNonNegativeInt(options.watchIntervalMs);
25
+ if (!max.ok || !interval.ok || interval.value === 0) {
26
+ logError("cage: --max-file-bytes must be a non-negative integer and --watch-interval-ms a positive integer");
27
+ setExitCode(2);
28
+ return;
29
+ }
30
+ await runUserCommandAsync({ json: options.json }, async () => {
31
+ const report = await runCage({
32
+ command,
33
+ reportOnly: options.reportOnly,
34
+ maxFileBytes: max.value,
35
+ watch: options.watch !== false,
36
+ watchIntervalMs: interval.value,
37
+ // The only line printed before the command starts; nothing is written while it runs except a bell.
38
+ onStart: ({ sessionLog, protectedFiles, watch }) => {
39
+ const mode = watch ? "watching" : "checking at exit";
40
+ const logPart = sessionLog ? `; live events: ${sessionLog}` : "";
41
+ console.error(`cage: ${mode} ${String(protectedFiles)} protected file(s)${logPart}`);
42
+ },
43
+ });
44
+ if (options.json) {
45
+ emitCliJson(report);
46
+ }
47
+ else {
48
+ for (const line of formatCageReport(report))
49
+ console.error(line);
50
+ }
51
+ if (report.exit_code !== 0)
52
+ setExitCode(report.exit_code);
53
+ });
54
+ }
55
+ /** `gantry cage-guard pre-push --root <dir>`: called by the cage session pre-push hook. */
56
+ export async function runCageGuardPrePush(options) {
57
+ const stdin = await readStdinIfEmpty("");
58
+ const violations = findPushViolations(buildCagePlan(options.root), stdin);
59
+ if (violations.length === 0)
60
+ return;
61
+ const list = violations.map((v) => `${v.path} (${v.rule})`).join(", ");
62
+ console.error(`gantry cage: push refused: outgoing commits change protected paths: ${list}`);
63
+ console.error("gantry cage: revert those commits, or push outside the cage session.");
64
+ const logPath = process.env[CAGE_SESSION_LOG_ENV];
65
+ if (logPath && fs.existsSync(logPath)) {
66
+ const event = { at: new Date().toISOString(), event: "push_refused", paths: violations };
67
+ fs.appendFileSync(logPath, `${JSON.stringify(event)}\n`);
68
+ }
69
+ setExitCode(1);
70
+ }
71
+ //# sourceMappingURL=cage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage.js","sourceRoot":"","sources":["../../../src/cli/commands/cage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface CageProcessResult {
2
+ code: number | null;
3
+ signal: NodeJS.Signals | null;
4
+ /** Spawn failure (e.g. ENOENT); the command never ran. */
5
+ spawnError: string | null;
6
+ }
7
+ /** Shell convention: a child killed by signal N exits 128 + N. */
8
+ export declare function signalExitCode(signal: NodeJS.Signals): number;
9
+ /**
10
+ * Run the caged command with inherited stdio so interactive agents keep their TTY.
11
+ *
12
+ * The cage must outlive the child to diff and revert, so while it runs:
13
+ * - SIGINT is swallowed here; a terminal Ctrl-C already reaches the child through the
14
+ * shared process group, and forwarding it again would double-interrupt.
15
+ * - SIGTERM / SIGHUP are forwarded to the child instead of killing the cage.
16
+ */
17
+ export declare function spawnCaged(argv: readonly string[], cwd: string, extraEnv?: NodeJS.ProcessEnv): Promise<CageProcessResult>;
18
+ //# sourceMappingURL=cage-process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-process.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-process.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,0DAA0D;IAC1D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,kEAAkE;AAClE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAG7D;AAID;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAM,CAAC,UAAe,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAkB5B"}
@@ -0,0 +1,39 @@
1
+ import { spawn } from "node:child_process";
2
+ import os from "node:os";
3
+ /** Shell convention: a child killed by signal N exits 128 + N. */
4
+ export function signalExitCode(signal) {
5
+ const n = os.constants.signals[signal];
6
+ return typeof n === "number" ? 128 + n : 1;
7
+ }
8
+ const FORWARDED_SIGNALS = ["SIGTERM", "SIGHUP"];
9
+ /**
10
+ * Run the caged command with inherited stdio so interactive agents keep their TTY.
11
+ *
12
+ * The cage must outlive the child to diff and revert, so while it runs:
13
+ * - SIGINT is swallowed here; a terminal Ctrl-C already reaches the child through the
14
+ * shared process group, and forwarding it again would double-interrupt.
15
+ * - SIGTERM / SIGHUP are forwarded to the child instead of killing the cage.
16
+ */
17
+ export async function spawnCaged(argv, cwd, extraEnv = {}) {
18
+ const [command, ...args] = argv;
19
+ const child = spawn(command, args, { cwd, stdio: "inherit", env: { ...process.env, ...extraEnv } });
20
+ const ignoreInt = () => { };
21
+ const forward = (sig) => {
22
+ child.kill(sig);
23
+ };
24
+ process.on("SIGINT", ignoreInt);
25
+ for (const sig of FORWARDED_SIGNALS)
26
+ process.on(sig, forward);
27
+ try {
28
+ return await new Promise((resolve) => {
29
+ child.on("error", (err) => resolve({ code: null, signal: null, spawnError: err.message }));
30
+ child.on("exit", (code, signal) => resolve({ code, signal, spawnError: null }));
31
+ });
32
+ }
33
+ finally {
34
+ process.off("SIGINT", ignoreInt);
35
+ for (const sig of FORWARDED_SIGNALS)
36
+ process.off(sig, forward);
37
+ }
38
+ }
39
+ //# sourceMappingURL=cage-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-process.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AASzB,kEAAkE;AAClE,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,iBAAiB,GAA8B,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAE3E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,IAAuB,EACvB,GAAW,EACX,WAA8B,EAAE;IAEhC,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,EAAE,CAAC,CAAC;IACrG,MAAM,SAAS,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,CAAC,GAAmB,EAAQ,EAAE;QAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,iBAAiB;QAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,OAAO,CAAoB,CAAC,OAAO,EAAE,EAAE;YACtD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC3F,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACjC,KAAK,MAAM,GAAG,IAAI,iBAAiB;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type CagePlan, type CageRuleId } from "./cage-rules.js";
2
+ /** Env var the session hook reads to find the cage session log. */
3
+ export declare const CAGE_SESSION_LOG_ENV = "GANTRY_CAGE_SESSION_LOG";
4
+ export interface CagePushGuard {
5
+ hooksDir: string;
6
+ /** Env additions for the caged process tree (GIT_CONFIG_* appends, never replaces, existing entries). */
7
+ env: NodeJS.ProcessEnv;
8
+ dispose(): void;
9
+ }
10
+ /**
11
+ * Create a temporary hooks dir outside the repo and the env that points the caged process tree at it.
12
+ * Returns null outside a git work tree. Nothing is written into the repository.
13
+ */
14
+ export declare function createCagePushGuard(root: string, sessionLogPath: string | null): CagePushGuard | null;
15
+ /** Rule for a repo-relative path in an outgoing commit; lockfiles (report-only) never block a push. */
16
+ export declare function pushGuardRule(plan: CagePlan, rel: string): CageRuleId | null;
17
+ export interface PushGuardViolation {
18
+ path: string;
19
+ rule: CageRuleId;
20
+ }
21
+ /** Parse pre-push stdin (`<local ref> <local sha> <remote ref> <remote sha>` per line) and list protected paths. */
22
+ export declare function findPushViolations(plan: CagePlan, prePushStdin: string): PushGuardViolation[];
23
+ //# sourceMappingURL=cage-push-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-push-guard.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-push-guard.ts"],"names":[],"mappings":"AAMA,OAAO,EAAqC,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA2BpG,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,4BAA4B,CAAC;AAE9D,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,yGAAyG;IACzG,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAwCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,aAAa,GAAG,IAAI,CAUrG;AAED,uGAAuG;AACvG,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAS5E;AAMD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,oHAAoH;AACpH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAa7F"}
@@ -0,0 +1,116 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { toPosixRel } from "../cli-io.js";
6
+ import { gitRun } from "../git/git.js";
7
+ import { CAGE_RULE_MODES, cageBasenameRule } from "./cage-rules.js";
8
+ /** Standard client-side git hooks; each session wrapper chains to the repo's real hook of the same name. */
9
+ const GIT_HOOK_NAMES = [
10
+ "applypatch-msg",
11
+ "pre-applypatch",
12
+ "post-applypatch",
13
+ "pre-commit",
14
+ "pre-merge-commit",
15
+ "prepare-commit-msg",
16
+ "commit-msg",
17
+ "post-commit",
18
+ "pre-rebase",
19
+ "post-checkout",
20
+ "post-merge",
21
+ "pre-push",
22
+ "pre-auto-gc",
23
+ "post-rewrite",
24
+ "sendemail-validate",
25
+ "fsmonitor-watchman",
26
+ "post-index-change",
27
+ "reference-transaction",
28
+ "push-to-checkout",
29
+ ];
30
+ const ZERO_SHA = /^0+$/;
31
+ /** Env var the session hook reads to find the cage session log. */
32
+ export const CAGE_SESSION_LOG_ENV = "GANTRY_CAGE_SESSION_LOG";
33
+ function shellQuote(s) {
34
+ return `'${s.replace(/'/g, `'\\''`)}'`;
35
+ }
36
+ function activeHooksDir(root) {
37
+ const r = gitRun(root, ["rev-parse", "--path-format=absolute", "--git-path", "hooks"]);
38
+ const out = r.stdout.trim();
39
+ return r.ok && out ? path.resolve(out) : null;
40
+ }
41
+ /** The CLI entry the session pre-push hook calls (`dist/cli/index.js`, next to this module's `lib/`). */
42
+ function cliEntry() {
43
+ return fileURLToPath(new URL("../../index.js", import.meta.url));
44
+ }
45
+ function hookScript(name, realHooks, root) {
46
+ const real = shellQuote(path.join(realHooks, name));
47
+ const guard = name === "pre-push"
48
+ ? `stdin_copy="$(cat)"\nprintf '%s\\n' "$stdin_copy" | ${shellQuote(process.execPath)} ${shellQuote(cliEntry())} cage-guard pre-push --root ${shellQuote(root)} || exit 1\n`
49
+ : "";
50
+ const chain = name === "pre-push"
51
+ ? `if [ -x ${real} ]; then printf '%s\\n' "$stdin_copy" | ${real} "$@"; exit $?; fi\n`
52
+ : `if [ -x ${real} ]; then exec ${real} "$@"; fi\n`;
53
+ return `#!/bin/sh\n# gantry cage session hook (temporary; removed when the cage session ends)\n${guard}${chain}exit 0\n`;
54
+ }
55
+ function gitConfigEnv(hooksDir) {
56
+ const raw = process.env.GIT_CONFIG_COUNT;
57
+ const n = raw && /^\d+$/.test(raw) ? Number.parseInt(raw, 10) : 0;
58
+ return {
59
+ GIT_CONFIG_COUNT: String(n + 1),
60
+ [`GIT_CONFIG_KEY_${String(n)}`]: "core.hooksPath",
61
+ [`GIT_CONFIG_VALUE_${String(n)}`]: hooksDir,
62
+ };
63
+ }
64
+ /**
65
+ * Create a temporary hooks dir outside the repo and the env that points the caged process tree at it.
66
+ * Returns null outside a git work tree. Nothing is written into the repository.
67
+ */
68
+ export function createCagePushGuard(root, sessionLogPath) {
69
+ const realHooks = activeHooksDir(root);
70
+ if (!realHooks)
71
+ return null;
72
+ const hooksDir = fs.mkdtempSync(path.join(os.tmpdir(), "gantry-cage-hooks-"));
73
+ for (const name of GIT_HOOK_NAMES) {
74
+ fs.writeFileSync(path.join(hooksDir, name), hookScript(name, realHooks, root), { mode: 0o755 });
75
+ }
76
+ const env = { ...gitConfigEnv(hooksDir) };
77
+ if (sessionLogPath)
78
+ env[CAGE_SESSION_LOG_ENV] = sessionLogPath;
79
+ return { hooksDir, env, dispose: () => fs.rmSync(hooksDir, { recursive: true, force: true }) };
80
+ }
81
+ /** Rule for a repo-relative path in an outgoing commit; lockfiles (report-only) never block a push. */
82
+ export function pushGuardRule(plan, rel) {
83
+ for (const t of plan.targets) {
84
+ if (t.rule !== "ci_config" && t.rule !== "manifest_forbidden_zone")
85
+ continue;
86
+ const base = toPosixRel(plan.root, t.abs);
87
+ if (base.startsWith(".."))
88
+ continue;
89
+ if (rel === base || rel.startsWith(`${base}/`))
90
+ return t.rule;
91
+ }
92
+ const rule = cageBasenameRule(path.posix.basename(rel));
93
+ return rule && CAGE_RULE_MODES[rule] === "revert" ? rule : null;
94
+ }
95
+ function outgoingRange(localSha, remoteSha) {
96
+ return ZERO_SHA.test(remoteSha) ? [localSha, "--not", "--remotes"] : [`${remoteSha}..${localSha}`];
97
+ }
98
+ /** Parse pre-push stdin (`<local ref> <local sha> <remote ref> <remote sha>` per line) and list protected paths. */
99
+ export function findPushViolations(plan, prePushStdin) {
100
+ const hits = new Map();
101
+ for (const line of prePushStdin.split(/\r?\n/)) {
102
+ const [, localSha, , remoteSha] = line.trim().split(/\s+/);
103
+ if (!localSha || !remoteSha || ZERO_SHA.test(localSha))
104
+ continue;
105
+ const r = gitRun(plan.root, ["log", "--format=", "--name-only", ...outgoingRange(localSha, remoteSha)]);
106
+ if (!r.ok)
107
+ continue;
108
+ for (const rel of r.stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)) {
109
+ const rule = pushGuardRule(plan, rel);
110
+ if (rule)
111
+ hits.set(rel, rule);
112
+ }
113
+ }
114
+ return [...hits].map(([p, rule]) => ({ path: p, rule })).sort((a, b) => a.path.localeCompare(b.path));
115
+ }
116
+ //# sourceMappingURL=cage-push-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-push-guard.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-push-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import type { CageRuleId } from "./cage-rules.js";
2
+ import type { CageOutcome, CageResolvedChange } from "./cage-revert.js";
3
+ export declare const CAGE_REPORT_SCHEMA: "gantry.cage-report.v1";
4
+ /** Limits with `--no-watch` (after-exit check only), stated in every such report. */
5
+ export declare const CAGE_LIMITS: readonly string[];
6
+ /** One-line stderr form of {@link CAGE_LIMITS}; `--json` keeps the full list. */
7
+ export declare const CAGE_LIMITS_SUMMARY = "after-exit check only; reads, network calls and writes outside the protected set are not detected";
8
+ /** Limits in watch mode (the default): live restore, not prevention. */
9
+ export declare const CAGE_WATCH_LIMITS: readonly string[];
10
+ /** One-line stderr form of {@link CAGE_WATCH_LIMITS}. */
11
+ export declare const CAGE_WATCH_LIMITS_SUMMARY = "live restore, not blocking; reads, network calls and writes outside the protected set are not detected";
12
+ /** Printed once under a violation report: the path from cage to project-specific missions. */
13
+ export declare const CAGE_UPGRADE_FOOTER = "Need project-specific boundaries or task contracts? Run: npx -p @jeger-ai/opengantry gantry init";
14
+ export type CageStatus = "ok" | "violations_reverted" | "violations_unresolved" | "runtime_error";
15
+ /** One changed protected path. Digests and outcomes only; never file bodies (ADR-0034). */
16
+ export interface CageReportChange {
17
+ path: string;
18
+ kind: "added" | "modified" | "deleted";
19
+ rule: CageRuleId;
20
+ sha256_before: string | null;
21
+ sha256_after: string | null;
22
+ outcome: CageOutcome;
23
+ error_code: string | null;
24
+ }
25
+ export interface CageReport {
26
+ report_schema: typeof CAGE_REPORT_SCHEMA;
27
+ status: CageStatus;
28
+ exit_code: number;
29
+ /** argv[0] only: arguments can carry tokens. */
30
+ command: string;
31
+ command_exit_code: number | null;
32
+ command_signal: string | null;
33
+ runtime_error: string | null;
34
+ root: string;
35
+ revert_mode: "revert" | "report_only";
36
+ max_file_bytes: number;
37
+ protected_files: number;
38
+ manifest_zones: number;
39
+ changes: CageReportChange[];
40
+ watch: {
41
+ enabled: boolean;
42
+ interval_ms: number | null;
43
+ live_restores: number;
44
+ contested: string[];
45
+ session_log: string | null;
46
+ };
47
+ push_guard: {
48
+ enabled: boolean;
49
+ refused_pushes: number;
50
+ };
51
+ limits: readonly string[];
52
+ }
53
+ export interface CageReportInput {
54
+ command: string;
55
+ commandExitCode: number | null;
56
+ commandSignal: NodeJS.Signals | null;
57
+ runtimeError: string | null;
58
+ root: string;
59
+ reportOnly: boolean;
60
+ maxFileBytes: number;
61
+ protectedFiles: number;
62
+ manifestZones: number;
63
+ changes: readonly CageResolvedChange[];
64
+ watch: CageReport["watch"];
65
+ pushGuard: CageReport["push_guard"];
66
+ signalExitCode: (signal: NodeJS.Signals) => number;
67
+ }
68
+ export declare function buildCageReport(input: CageReportInput): CageReport;
69
+ /** Human summary (stderr), so it never mixes into the wrapped command's stdout. */
70
+ export declare function formatCageReport(report: CageReport): string[];
71
+ //# sourceMappingURL=cage-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-report.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ export const CAGE_REPORT_SCHEMA = "gantry.cage-report.v1";
2
+ /** Limits with `--no-watch` (after-exit check only), stated in every such report. */
3
+ export const CAGE_LIMITS = [
4
+ "detects protected-path writes after the command exits; it does not block them while it runs",
5
+ "reads are not detected: the command can still read .env and other secrets",
6
+ "network and API side effects are not detected",
7
+ "writes outside the protected set, and outside the repository, are not checked",
8
+ "background processes that outlive the command can still write after the check",
9
+ ];
10
+ /** One-line stderr form of {@link CAGE_LIMITS}; `--json` keeps the full list. */
11
+ export const CAGE_LIMITS_SUMMARY = "after-exit check only; reads, network calls and writes outside the protected set are not detected";
12
+ /** Limits in watch mode (the default): live restore, not prevention. */
13
+ export const CAGE_WATCH_LIMITS = [
14
+ "restores protected-path writes about once per watch interval while the command runs; it does not block them",
15
+ "a change can be committed or used before the next check; the push guard covers git push from inside the session only",
16
+ "reads are not detected: the command can still read .env and other secrets",
17
+ "network and API side effects are not detected",
18
+ "writes outside the protected set, and outside the repository, are not checked",
19
+ ];
20
+ /** One-line stderr form of {@link CAGE_WATCH_LIMITS}. */
21
+ export const CAGE_WATCH_LIMITS_SUMMARY = "live restore, not blocking; reads, network calls and writes outside the protected set are not detected";
22
+ /** Printed once under a violation report: the path from cage to project-specific missions. */
23
+ export const CAGE_UPGRADE_FOOTER = "Need project-specific boundaries or task contracts? Run: npx -p @jeger-ai/opengantry gantry init";
24
+ const VIOLATION_EXIT_CODE = 3;
25
+ const RUNTIME_ERROR_EXIT_CODE = 2;
26
+ const RESOLVED_OUTCOMES = new Set(["reverted", "removed"]);
27
+ function cageStatus(input) {
28
+ const violations = input.changes.filter((c) => c.mode === "revert");
29
+ if (violations.length > 0) {
30
+ return violations.every((c) => RESOLVED_OUTCOMES.has(c.outcome)) ? "violations_reverted" : "violations_unresolved";
31
+ }
32
+ return input.runtimeError ? "runtime_error" : "ok";
33
+ }
34
+ function cageExitCode(status, input) {
35
+ if (status === "violations_reverted" || status === "violations_unresolved")
36
+ return VIOLATION_EXIT_CODE;
37
+ if (status === "runtime_error")
38
+ return RUNTIME_ERROR_EXIT_CODE;
39
+ if (input.commandSignal)
40
+ return input.signalExitCode(input.commandSignal);
41
+ return input.commandExitCode ?? RUNTIME_ERROR_EXIT_CODE;
42
+ }
43
+ export function buildCageReport(input) {
44
+ const status = cageStatus(input);
45
+ return {
46
+ report_schema: CAGE_REPORT_SCHEMA,
47
+ status,
48
+ exit_code: cageExitCode(status, input),
49
+ command: input.command,
50
+ command_exit_code: input.commandExitCode,
51
+ command_signal: input.commandSignal,
52
+ runtime_error: input.runtimeError,
53
+ root: input.root,
54
+ revert_mode: input.reportOnly ? "report_only" : "revert",
55
+ max_file_bytes: input.maxFileBytes,
56
+ protected_files: input.protectedFiles,
57
+ manifest_zones: input.manifestZones,
58
+ changes: input.changes.map((c) => ({
59
+ path: c.rel,
60
+ kind: c.kind,
61
+ rule: c.rule,
62
+ sha256_before: c.before?.sha256 ?? null,
63
+ sha256_after: c.after?.sha256 ?? null,
64
+ outcome: c.outcome,
65
+ error_code: c.errorCode,
66
+ })),
67
+ watch: input.watch,
68
+ push_guard: input.pushGuard,
69
+ limits: input.watch.enabled ? CAGE_WATCH_LIMITS : CAGE_LIMITS,
70
+ };
71
+ }
72
+ function changeLine(c) {
73
+ const err = c.error_code ? ` ${c.error_code}` : "";
74
+ return ` ${c.outcome.padEnd(13)} ${c.kind.padEnd(8)} ${c.path} (${c.rule})${err}`;
75
+ }
76
+ function sessionLines(report) {
77
+ const out = [];
78
+ const w = report.watch;
79
+ if (w.live_restores > 0) {
80
+ out.push(`cage: restored ${String(w.live_restores)} protected change(s) during the session`);
81
+ }
82
+ if (w.contested.length > 0) {
83
+ out.push(`cage: contested (kept being rewritten; restored once at exit): ${w.contested.join(", ")}`);
84
+ }
85
+ if (report.push_guard.refused_pushes > 0) {
86
+ out.push(`cage: refused ${String(report.push_guard.refused_pushes)} push(es) touching protected paths`);
87
+ }
88
+ if (w.session_log && (w.live_restores > 0 || report.push_guard.refused_pushes > 0)) {
89
+ out.push(`cage: session log: ${w.session_log}`);
90
+ }
91
+ return out;
92
+ }
93
+ /** Human summary (stderr), so it never mixes into the wrapped command's stdout. */
94
+ export function formatCageReport(report) {
95
+ const lines = [];
96
+ const n = report.changes.length;
97
+ if (report.runtime_error)
98
+ lines.push(`cage: command did not run: ${report.runtime_error}`);
99
+ if (n === 0) {
100
+ lines.push(`cage: ${report.status} — ${String(report.protected_files)} protected file(s) unchanged`);
101
+ }
102
+ else {
103
+ lines.push(`cage: ${report.status} — ${String(n)} protected change(s)`);
104
+ for (const c of report.changes)
105
+ lines.push(changeLine(c));
106
+ }
107
+ if (report.changes.some((c) => c.outcome === "detect_only")) {
108
+ lines.push(`cage: detect_only files exceeded the ${String(report.max_file_bytes)}-byte per-file cap or the total snapshot budget and were not restored`);
109
+ }
110
+ lines.push(...sessionLines(report));
111
+ const summary = report.watch.enabled ? CAGE_WATCH_LIMITS_SUMMARY : CAGE_LIMITS_SUMMARY;
112
+ lines.push(`cage: exit ${String(report.exit_code)}; limits: ${summary}`);
113
+ if (report.status === "violations_reverted" || report.status === "violations_unresolved") {
114
+ lines.push(`cage: ${CAGE_UPGRADE_FOOTER}`);
115
+ }
116
+ return lines;
117
+ }
118
+ //# sourceMappingURL=cage-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-report.js","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { CageChange } from "./cage-snapshot.js";
2
+ /**
3
+ * - `reverted`: baseline bytes (or symlink) restored
4
+ * - `removed`: an added file was deleted
5
+ * - `kept`: report-only rule (lockfiles); change left in place
6
+ * - `reported`: `--report-only` run; change left in place
7
+ * - `detect_only`: no baseline bytes (over size cap or budget); change left in place
8
+ * - `revert_failed`: restore attempted and failed (`error_code` says why)
9
+ */
10
+ export type CageOutcome = "reverted" | "removed" | "kept" | "reported" | "detect_only" | "revert_failed";
11
+ export interface CageResolvedChange extends CageChange {
12
+ outcome: CageOutcome;
13
+ errorCode: string | null;
14
+ }
15
+ export declare function resolveCageChanges(changes: readonly CageChange[], opts: {
16
+ reportOnly: boolean;
17
+ }): CageResolvedChange[];
18
+ //# sourceMappingURL=cage-revert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cage-revert.d.ts","sourceRoot":"","sources":["../../../../src/cli/lib/cage/cage-revert.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,oBAAoB,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,GAAG,eAAe,CAAC;AAEzG,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA0CD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,IAAI,EAAE;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,GAC5B,kBAAkB,EAAE,CAUtB"}