@cleocode/git-shim 2026.4.154 → 2026.4.157

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.
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Result returned by a boundary predicate when an operation must be blocked.
3
+ *
4
+ * Stable shape so callers can render consistent error envelopes.
5
+ *
6
+ * @task T1591
7
+ */
8
+ export interface BoundaryViolation {
9
+ /** CLEO error code (`E_GIT_BOUNDARY_*`). */
10
+ code: 'E_GIT_BOUNDARY_WORKTREE_PATH' | 'E_GIT_BOUNDARY_COMMIT_TASK_ID' | 'E_GIT_BOUNDARY_MERGE_FORBIDDEN' | 'E_GIT_BOUNDARY_CHERRY_PICK_TASK_BRANCH';
11
+ /** Which boundary letter (a/b/c/d) — kept in audit log for grouping. */
12
+ boundary: 'a' | 'b' | 'c' | 'd';
13
+ /** Short human-readable summary of the violation. */
14
+ message: string;
15
+ /** Suggested operator action (always includes the override path). */
16
+ remediation: string;
17
+ /** Free-form context attached to the audit record. */
18
+ context: Record<string, string>;
19
+ }
20
+ /**
21
+ * Boundary (a) — Worktree-path enforcement.
22
+ *
23
+ * Rejects `git add` of a path outside the active worktree. The shim runs
24
+ * with `cwd === process.cwd()`, so any relative path is rooted there.
25
+ * Absolute paths are checked verbatim.
26
+ *
27
+ * `git add` flags that don't take paths (`-A`, `--all`, `-u`, `--update`,
28
+ * `-i`, `--interactive`, `-p`, `--patch`) are allowed as-is — git itself
29
+ * scopes them to the current repo, which is the worktree.
30
+ *
31
+ * @param args - argv slice after the `add` subcommand.
32
+ * @param cwd - Current working directory at invocation time.
33
+ * @param worktreePath - Active worktree root.
34
+ * @returns Violation when an explicit path escapes the worktree, else null.
35
+ *
36
+ * @task T1591
37
+ */
38
+ export declare function validateAddPaths(args: ReadonlyArray<string>, cwd: string, worktreePath: string): BoundaryViolation | null;
39
+ /**
40
+ * Extract every commit-message subject from a `git commit` invocation.
41
+ *
42
+ * Handles `-m <msg>`, `--message <msg>`, `-m=<msg>`, `--message=<msg>`. Each
43
+ * occurrence contributes to a multi-paragraph commit; the **first** message is
44
+ * the subject. We validate every `-m` since git concatenates them with blank
45
+ * lines.
46
+ *
47
+ * @param args - argv slice after the `commit` subcommand.
48
+ * @returns The list of message values present, in order.
49
+ *
50
+ * @task T1591
51
+ */
52
+ export declare function extractCommitMessages(args: ReadonlyArray<string>): string[];
53
+ /**
54
+ * Determine whether the given commit invocation will produce a new commit.
55
+ *
56
+ * Read-only flags like `--dry-run`, `--allow-empty-message`, the absence of
57
+ * `-m` (which opens an editor — caught by the editor-side hook in T1588), and
58
+ * `--amend` without `-m` all pass through. The shim only enforces when a
59
+ * subject is supplied inline.
60
+ *
61
+ * @param args - argv slice after the `commit` subcommand.
62
+ * @returns true when at least one inline `-m` message was supplied.
63
+ *
64
+ * @task T1591
65
+ */
66
+ export declare function commitHasInlineMessage(args: ReadonlyArray<string>): boolean;
67
+ /**
68
+ * Boundary (b) — Commit T-ID gate.
69
+ *
70
+ * Rejects `git commit -m "<msg>"` when the subject lacks a CLEO task ID
71
+ * (`T<NUM>`). Multi-`-m` invocations validate the FIRST message (the
72
+ * subject — git concatenates subsequent `-m` values as paragraphs).
73
+ *
74
+ * @param args - argv slice after the `commit` subcommand.
75
+ * @param expectedTaskId - Optional hard-anchor: when provided, the subject
76
+ * must contain this exact ID, not just any `T<NUM>`.
77
+ * @returns Violation when the inline subject lacks a task ID, else null.
78
+ *
79
+ * @task T1591
80
+ */
81
+ export declare function validateCommitSubject(args: ReadonlyArray<string>, expectedTaskId: string | null): BoundaryViolation | null;
82
+ /**
83
+ * Boundary (c) — Merge restriction.
84
+ *
85
+ * Rejects `git merge` invocations from agent worktrees unless the
86
+ * `CLEO_ORCHESTRATE_MERGE` env var is set. That env var is supplied
87
+ * exclusively by `completeAgentWorktreeViaMerge` (ADR-062 / T1587), so a
88
+ * direct `git merge` from an agent will always fail.
89
+ *
90
+ * Merge subcommand variants that DON'T merge (`--abort`, `--continue`,
91
+ * `--quit`) pass through.
92
+ *
93
+ * @param args - argv slice after the `merge` subcommand.
94
+ * @param env - Snapshot of relevant env vars.
95
+ * @returns Violation when merge is blocked, else null.
96
+ *
97
+ * @task T1591
98
+ * @adr ADR-062
99
+ */
100
+ export declare function validateMergeAllowed(args: ReadonlyArray<string>, env: {
101
+ CLEO_ORCHESTRATE_MERGE?: string;
102
+ }): BoundaryViolation | null;
103
+ /**
104
+ * Boundary (d) — Cherry-pick refusal from worktree branches.
105
+ *
106
+ * Rejects `git cherry-pick <ref>` when `<ref>` is a `task/T<NUM>` branch.
107
+ * Cherry-pick from those branches is the deprecated integration path
108
+ * (ADR-062 supersedes it with `git merge --no-ff`).
109
+ *
110
+ * Also catches `git cherry-pick task/T<NUM>..HEAD` and similar range syntax.
111
+ *
112
+ * @param args - argv slice after the `cherry-pick` subcommand.
113
+ * @returns Violation when a task-branch ref is referenced, else null.
114
+ *
115
+ * @task T1591
116
+ * @adr ADR-062
117
+ */
118
+ export declare function validateCherryPickSource(args: ReadonlyArray<string>): BoundaryViolation | null;
119
+ //# sourceMappingURL=boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"AAuBA;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,IAAI,EACA,8BAA8B,GAC9B,+BAA+B,GAC/B,gCAAgC,GAChC,wCAAwC,CAAC;IAC7C,wEAAwE;IACxE,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAChC,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAQD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,GACnB,iBAAiB,GAAG,IAAI,CAyB1B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAoB3E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAE3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,iBAAiB,GAAG,IAAI,CAgC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,GAAG,EAAE;IAAE,sBAAsB,CAAC,EAAE,MAAM,CAAA;CAAE,GACvC,iBAAiB,GAAG,IAAI,CAoB1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,iBAAiB,GAAG,IAAI,CAoB9F"}
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Boundary-enforcement predicates for the git-shim (T1591 L2 fence).
3
+ *
4
+ * Pure functions — no I/O, no spawning. Each predicate returns either
5
+ * `null` (allowed) or a {@link BoundaryViolation} (must block).
6
+ *
7
+ * The four T1591 boundaries:
8
+ *
9
+ * 1. **(a) Worktree-path enforcement** — `git add <path>` MUST stage files
10
+ * inside the active worktree. Cross-worktree adds are blocked.
11
+ * 2. **(b) Commit T-ID gate** — `git commit -m "<msg>"` MUST embed `T<NUM>`
12
+ * in the subject line.
13
+ * 3. **(c) Merge restriction** — `git merge` is blocked unless
14
+ * `CLEO_ORCHESTRATE_MERGE=1` (set by `completeAgentWorktreeViaMerge`).
15
+ * 4. **(d) Cherry-pick refusal** — `git cherry-pick <ref>` is blocked when
16
+ * `<ref>` matches `task/T\d+` (T1587 anti-pattern).
17
+ *
18
+ * @task T1591
19
+ * @adr ADR-062
20
+ */
21
+ import { isAbsolute, resolve } from 'node:path';
22
+ import { isPathInsideWorktree } from './worktree-path.js';
23
+ /** Regex that matches CLEO task IDs in commit subjects. Project-agnostic. */
24
+ const TASK_ID_PATTERN = /\bT\d+\b/;
25
+ /** Regex that matches a worktree branch ref (`task/T<NUM>`). */
26
+ const TASK_BRANCH_PATTERN = /^task\/T\d+$/;
27
+ /**
28
+ * Boundary (a) — Worktree-path enforcement.
29
+ *
30
+ * Rejects `git add` of a path outside the active worktree. The shim runs
31
+ * with `cwd === process.cwd()`, so any relative path is rooted there.
32
+ * Absolute paths are checked verbatim.
33
+ *
34
+ * `git add` flags that don't take paths (`-A`, `--all`, `-u`, `--update`,
35
+ * `-i`, `--interactive`, `-p`, `--patch`) are allowed as-is — git itself
36
+ * scopes them to the current repo, which is the worktree.
37
+ *
38
+ * @param args - argv slice after the `add` subcommand.
39
+ * @param cwd - Current working directory at invocation time.
40
+ * @param worktreePath - Active worktree root.
41
+ * @returns Violation when an explicit path escapes the worktree, else null.
42
+ *
43
+ * @task T1591
44
+ */
45
+ export function validateAddPaths(args, cwd, worktreePath) {
46
+ // Walk through args; non-flag arguments are pathspecs.
47
+ for (const arg of args) {
48
+ if (arg.startsWith('-'))
49
+ continue; // flag — skip
50
+ // Pathspec leaders like `:` or `:/` are skipped — git treats them specially.
51
+ if (arg === '.' || arg === '*' || arg === '')
52
+ continue;
53
+ if (arg.startsWith(':'))
54
+ continue;
55
+ const absolute = isAbsolute(arg) ? arg : resolve(cwd, arg);
56
+ if (!isPathInsideWorktree(absolute, worktreePath)) {
57
+ return {
58
+ code: 'E_GIT_BOUNDARY_WORKTREE_PATH',
59
+ boundary: 'a',
60
+ message: `git add refused — path "${arg}" resolves outside agent worktree (${worktreePath})`,
61
+ remediation: 'Stage only files inside the worktree. To bypass for emergency hotfix set CLEO_ALLOW_GIT=1 (audited).',
62
+ context: {
63
+ attempted_path: arg,
64
+ resolved_path: absolute,
65
+ worktree_path: worktreePath,
66
+ },
67
+ };
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ /**
73
+ * Extract every commit-message subject from a `git commit` invocation.
74
+ *
75
+ * Handles `-m <msg>`, `--message <msg>`, `-m=<msg>`, `--message=<msg>`. Each
76
+ * occurrence contributes to a multi-paragraph commit; the **first** message is
77
+ * the subject. We validate every `-m` since git concatenates them with blank
78
+ * lines.
79
+ *
80
+ * @param args - argv slice after the `commit` subcommand.
81
+ * @returns The list of message values present, in order.
82
+ *
83
+ * @task T1591
84
+ */
85
+ export function extractCommitMessages(args) {
86
+ const messages = [];
87
+ for (let i = 0; i < args.length; i++) {
88
+ const a = args[i];
89
+ if (a === undefined)
90
+ continue;
91
+ if (a === '-m' || a === '--message') {
92
+ const next = args[i + 1];
93
+ if (next !== undefined)
94
+ messages.push(next);
95
+ i++;
96
+ continue;
97
+ }
98
+ if (a.startsWith('-m=')) {
99
+ messages.push(a.slice(3));
100
+ continue;
101
+ }
102
+ if (a.startsWith('--message=')) {
103
+ messages.push(a.slice('--message='.length));
104
+ }
105
+ }
106
+ return messages;
107
+ }
108
+ /**
109
+ * Determine whether the given commit invocation will produce a new commit.
110
+ *
111
+ * Read-only flags like `--dry-run`, `--allow-empty-message`, the absence of
112
+ * `-m` (which opens an editor — caught by the editor-side hook in T1588), and
113
+ * `--amend` without `-m` all pass through. The shim only enforces when a
114
+ * subject is supplied inline.
115
+ *
116
+ * @param args - argv slice after the `commit` subcommand.
117
+ * @returns true when at least one inline `-m` message was supplied.
118
+ *
119
+ * @task T1591
120
+ */
121
+ export function commitHasInlineMessage(args) {
122
+ return extractCommitMessages(args).length > 0;
123
+ }
124
+ /**
125
+ * Boundary (b) — Commit T-ID gate.
126
+ *
127
+ * Rejects `git commit -m "<msg>"` when the subject lacks a CLEO task ID
128
+ * (`T<NUM>`). Multi-`-m` invocations validate the FIRST message (the
129
+ * subject — git concatenates subsequent `-m` values as paragraphs).
130
+ *
131
+ * @param args - argv slice after the `commit` subcommand.
132
+ * @param expectedTaskId - Optional hard-anchor: when provided, the subject
133
+ * must contain this exact ID, not just any `T<NUM>`.
134
+ * @returns Violation when the inline subject lacks a task ID, else null.
135
+ *
136
+ * @task T1591
137
+ */
138
+ export function validateCommitSubject(args, expectedTaskId) {
139
+ const messages = extractCommitMessages(args);
140
+ if (messages.length === 0)
141
+ return null; // editor flow — handled by hooks (T1588)
142
+ const subject = messages[0] ?? '';
143
+ if (expectedTaskId) {
144
+ // Anchor check: subject must contain the active worktree's task ID.
145
+ const literal = new RegExp(`\\b${expectedTaskId}\\b`);
146
+ if (!literal.test(subject)) {
147
+ return {
148
+ code: 'E_GIT_BOUNDARY_COMMIT_TASK_ID',
149
+ boundary: 'b',
150
+ message: `git commit refused — subject "${subject}" missing task ID "${expectedTaskId}"`,
151
+ remediation: `Include "${expectedTaskId}" in the subject (e.g. "feat(${expectedTaskId}): …"). Bypass with CLEO_ALLOW_GIT=1 (audited).`,
152
+ context: { subject, expected_task_id: expectedTaskId },
153
+ };
154
+ }
155
+ return null;
156
+ }
157
+ // No expected ID — fall back to any `T<NUM>` (project-agnostic CLEO convention).
158
+ if (!TASK_ID_PATTERN.test(subject)) {
159
+ return {
160
+ code: 'E_GIT_BOUNDARY_COMMIT_TASK_ID',
161
+ boundary: 'b',
162
+ message: `git commit refused — subject "${subject}" missing CLEO task ID (T<NUM>)`,
163
+ remediation: 'Add a task ID to the subject (e.g. "fix(T1234): …"). Bypass with CLEO_ALLOW_GIT=1 (audited).',
164
+ context: { subject },
165
+ };
166
+ }
167
+ return null;
168
+ }
169
+ /**
170
+ * Boundary (c) — Merge restriction.
171
+ *
172
+ * Rejects `git merge` invocations from agent worktrees unless the
173
+ * `CLEO_ORCHESTRATE_MERGE` env var is set. That env var is supplied
174
+ * exclusively by `completeAgentWorktreeViaMerge` (ADR-062 / T1587), so a
175
+ * direct `git merge` from an agent will always fail.
176
+ *
177
+ * Merge subcommand variants that DON'T merge (`--abort`, `--continue`,
178
+ * `--quit`) pass through.
179
+ *
180
+ * @param args - argv slice after the `merge` subcommand.
181
+ * @param env - Snapshot of relevant env vars.
182
+ * @returns Violation when merge is blocked, else null.
183
+ *
184
+ * @task T1591
185
+ * @adr ADR-062
186
+ */
187
+ export function validateMergeAllowed(args, env) {
188
+ // Allow control-flow flags that don't perform a merge.
189
+ for (const arg of args) {
190
+ if (arg === '--abort' || arg === '--continue' || arg === '--quit') {
191
+ return null;
192
+ }
193
+ }
194
+ if (env.CLEO_ORCHESTRATE_MERGE === '1')
195
+ return null;
196
+ return {
197
+ code: 'E_GIT_BOUNDARY_MERGE_FORBIDDEN',
198
+ boundary: 'c',
199
+ message: 'git merge refused — agents MUST NOT merge directly (ADR-062 / T1587)',
200
+ remediation: 'Use `cleo orchestrate complete <taskId>` so the orchestrator runs git merge --no-ff with the right env. Bypass with CLEO_ALLOW_GIT=1 (audited).',
201
+ context: {
202
+ args: args.join(' '),
203
+ },
204
+ };
205
+ }
206
+ /**
207
+ * Boundary (d) — Cherry-pick refusal from worktree branches.
208
+ *
209
+ * Rejects `git cherry-pick <ref>` when `<ref>` is a `task/T<NUM>` branch.
210
+ * Cherry-pick from those branches is the deprecated integration path
211
+ * (ADR-062 supersedes it with `git merge --no-ff`).
212
+ *
213
+ * Also catches `git cherry-pick task/T<NUM>..HEAD` and similar range syntax.
214
+ *
215
+ * @param args - argv slice after the `cherry-pick` subcommand.
216
+ * @returns Violation when a task-branch ref is referenced, else null.
217
+ *
218
+ * @task T1591
219
+ * @adr ADR-062
220
+ */
221
+ export function validateCherryPickSource(args) {
222
+ for (const arg of args) {
223
+ if (arg.startsWith('-'))
224
+ continue;
225
+ // Range syntax like `task/T123..HEAD` or `task/T123^..task/T123`.
226
+ const refs = arg.split(/\.{2,3}/);
227
+ for (const ref of refs) {
228
+ const trimmed = ref.replace(/[\^~].*$/, '');
229
+ if (TASK_BRANCH_PATTERN.test(trimmed)) {
230
+ return {
231
+ code: 'E_GIT_BOUNDARY_CHERRY_PICK_TASK_BRANCH',
232
+ boundary: 'd',
233
+ message: `git cherry-pick refused — source ref "${trimmed}" is an agent worktree branch (ADR-062: use git merge --no-ff)`,
234
+ remediation: 'Run `cleo orchestrate complete <taskId>` to integrate via merge. Bypass with CLEO_ALLOW_GIT=1 (audited).',
235
+ context: { source_ref: trimmed, original_arg: arg },
236
+ };
237
+ }
238
+ }
239
+ }
240
+ return null;
241
+ }
242
+ //# sourceMappingURL=boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,18 +1,22 @@
1
1
  /**
2
- * @cleocode/git-shim — Harness-agnostic git branch-mutation fence.
2
+ * @cleocode/git-shim — Harness-agnostic git fence (T1118 + T1591).
3
3
  *
4
- * Exports the denylist utilities so other packages can inspect the
5
- * blocked operation table without executing the shim binary.
6
- *
7
- * The shim binary itself lives at `dist/shim.js` and is registered
8
- * as the `git` bin entry in package.json.
4
+ * Re-exports the denylist + boundary predicates so other packages can inspect
5
+ * the shim's enforcement without executing the binary. The shim binary itself
6
+ * lives at `dist/shim.js` and is registered as the `git` bin entry in
7
+ * package.json.
9
8
  *
10
9
  * @task T1118
11
10
  * @task T1121
11
+ * @task T1591
12
12
  * @packageDocumentation
13
13
  */
14
- export type {} from './denylist.js';
14
+ export type { AuditOutcome, AuditRecord } from './audit-log.js';
15
+ export { resolveAuditLogPath, writeAuditRecord } from './audit-log.js';
16
+ export type { BoundaryViolation } from './boundary.js';
17
+ export { commitHasInlineMessage, extractCommitMessages, validateAddPaths, validateCherryPickSource, validateCommitSubject, validateMergeAllowed, } from './boundary.js';
15
18
  export { findDeniedOp, GIT_OP_DENYLIST, RESTRICTED_ROLES } from './denylist.js';
19
+ export { extractTaskIdFromWorktreePath, isInsideWorktreesRoot, isPathInsideWorktree, resolveActiveWorktree, resolveCleoWorktreesRoot, resolveProjectWorktreeRoot, } from './worktree-path.js';
16
20
  /**
17
21
  * Install the shim symlink so that `git` resolves to this shim when
18
22
  * the shim directory is on PATH.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,YAAY,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEhF;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiC/F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiC/F"}
package/dist/index.js CHANGED
@@ -1,17 +1,20 @@
1
1
  /**
2
- * @cleocode/git-shim — Harness-agnostic git branch-mutation fence.
2
+ * @cleocode/git-shim — Harness-agnostic git fence (T1118 + T1591).
3
3
  *
4
- * Exports the denylist utilities so other packages can inspect the
5
- * blocked operation table without executing the shim binary.
6
- *
7
- * The shim binary itself lives at `dist/shim.js` and is registered
8
- * as the `git` bin entry in package.json.
4
+ * Re-exports the denylist + boundary predicates so other packages can inspect
5
+ * the shim's enforcement without executing the binary. The shim binary itself
6
+ * lives at `dist/shim.js` and is registered as the `git` bin entry in
7
+ * package.json.
9
8
  *
10
9
  * @task T1118
11
10
  * @task T1121
11
+ * @task T1591
12
12
  * @packageDocumentation
13
13
  */
14
+ export { resolveAuditLogPath, writeAuditRecord } from './audit-log.js';
15
+ export { commitHasInlineMessage, extractCommitMessages, validateAddPaths, validateCherryPickSource, validateCommitSubject, validateMergeAllowed, } from './boundary.js';
14
16
  export { findDeniedOp, GIT_OP_DENYLIST, RESTRICTED_ROLES } from './denylist.js';
17
+ export { extractTaskIdFromWorktreePath, isInsideWorktreesRoot, isPathInsideWorktree, resolveActiveWorktree, resolveCleoWorktreesRoot, resolveProjectWorktreeRoot, } from './worktree-path.js';
15
18
  /**
16
19
  * Install the shim symlink so that `git` resolves to this shim when
17
20
  * the shim directory is on PATH.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEhF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAe,EAAE,WAAmB;IAC3E,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACnF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAE3C,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAEtC,qDAAqD;IACrD,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,MAAM,KAAK,WAAW;gBAAE,OAAO,IAAI,CAAC,CAAC,kBAAkB;YAC3D,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC;gBACH,UAAU,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACnC,wCAAwC;QACxC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9C,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;IACnF,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAe,EAAE,WAAmB;IAC3E,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACnF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAE3C,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAEtC,qDAAqD;IACrD,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,MAAM,KAAK,WAAW;gBAAE,OAAO,IAAI,CAAC,CAAC,kBAAkB;YAC3D,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC;gBACH,UAAU,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACnC,wCAAwC;QACxC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9C,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;IACnF,CAAC;AACH,CAAC"}
package/dist/shim.d.ts CHANGED
@@ -1,30 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * git-shim — harness-agnostic git branch-mutation fence (T1118 L2).
3
+ * git-shim — harness-agnostic git fence (T1118 L2 + T1591 boundary fence).
4
4
  *
5
5
  * Usage: place this binary on PATH BEFORE real git, and export:
6
6
  * CLEO_AGENT_ROLE=worker (or lead|subagent)
7
- * CLEO_WORKTREE_ROOT=<path>
7
+ * CLEO_WORKTREE_ROOT=<path> (optional — auto-detected from cwd)
8
+ * CLEO_TASK_ID=T<NUM> (optional — auto-detected from worktree path)
8
9
  *
9
- * Behaviour:
10
- * - If CLEO_AGENT_ROLE is a restricted role AND the git subcommand is in
11
- * the denylist AND CLEO_ALLOW_BRANCH_OPS is not set: print structured
12
- * error to stderr, exit 1.
13
- * - If CLEO_ALLOW_BRANCH_OPS=1: log a warning and pass through (escape hatch).
14
- * - Otherwise: exec real git with all original args (zero overhead path).
10
+ * Layered enforcement (in order, all under restricted-role gate):
11
+ * 1. T1118 denylist (branch-mutation ops). Bypass: `CLEO_ALLOW_BRANCH_OPS=1`.
12
+ * 2. T1591 boundary fence:
13
+ * (a) git add path inside worktree
14
+ * (b) git commit subject contains T-ID
15
+ * (c) git merge requires CLEO_ORCHESTRATE_MERGE=1
16
+ * (d) git cherry-pick refuses task/T<NUM> source
17
+ * Bypass any of (a)-(d): `CLEO_ALLOW_GIT=1` (audited).
15
18
  *
16
- * The real git binary is resolved from CLEO_REAL_GIT_PATH env (override) or
17
- * discovered by walking PATH and skipping any entry that resolves to this
18
- * shim itself (detected via CLEO_SHIM_MARKER env set by the shim installer).
19
+ * Audit log at `<XDG>/cleo/audit/git-shim.jsonl` (override `CLEO_AUDIT_LOG_PATH`).
19
20
  *
20
21
  * Exit codes:
21
- * 0 — passthrough succeeded (real git exited 0)
22
- * 1 — git op blocked by shim
23
- * 77 — well-known CLEO_GIT_BLOCKED sentinel (same as exit 1 but distinguishable)
22
+ * 0 — passthrough succeeded
23
+ * 1 — generic shim error (real git not found)
24
+ * 77 — CLEO_GIT_BLOCKED sentinel (legacy denylist + new boundary fence)
24
25
  * N — real git exit code propagated
25
26
  *
26
27
  * @task T1118
27
28
  * @task T1121
29
+ * @task T1591
28
30
  */
29
31
  export {};
30
32
  //# sourceMappingURL=shim.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shim.d.ts","sourceRoot":"","sources":["../src/shim.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG"}
1
+ {"version":3,"file":"shim.d.ts","sourceRoot":"","sources":["../src/shim.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG"}