@cad0p/pi-steering 0.1.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/CHANGELOG.md +36 -0
- package/LICENSE +29 -0
- package/README.md +920 -0
- package/dist/__test-helpers__.d.ts +95 -0
- package/dist/__test-helpers__.d.ts.map +1 -0
- package/dist/__test-helpers__.js +174 -0
- package/dist/__test-helpers__.js.map +1 -0
- package/dist/bin/pi-steering.d.ts +9 -0
- package/dist/bin/pi-steering.d.ts.map +1 -0
- package/dist/bin/pi-steering.js +506 -0
- package/dist/bin/pi-steering.js.map +1 -0
- package/dist/compat.d.ts +50 -0
- package/dist/compat.d.ts.map +1 -0
- package/dist/compat.js +180 -0
- package/dist/compat.js.map +1 -0
- package/dist/defaults.d.ts +61 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +106 -0
- package/dist/defaults.js.map +1 -0
- package/dist/define-config.d.ts +235 -0
- package/dist/define-config.d.ts.map +1 -0
- package/dist/define-config.js +118 -0
- package/dist/define-config.js.map +1 -0
- package/dist/define-predicate.d.ts +49 -0
- package/dist/define-predicate.d.ts.map +1 -0
- package/dist/define-predicate.js +35 -0
- package/dist/define-predicate.js.map +1 -0
- package/dist/evaluator-internals/context.d.ts +186 -0
- package/dist/evaluator-internals/context.d.ts.map +1 -0
- package/dist/evaluator-internals/context.js +219 -0
- package/dist/evaluator-internals/context.js.map +1 -0
- package/dist/evaluator-internals/override.d.ts +19 -0
- package/dist/evaluator-internals/override.d.ts.map +1 -0
- package/dist/evaluator-internals/override.js +79 -0
- package/dist/evaluator-internals/override.js.map +1 -0
- package/dist/evaluator-internals/predicates.d.ts +152 -0
- package/dist/evaluator-internals/predicates.d.ts.map +1 -0
- package/dist/evaluator-internals/predicates.js +849 -0
- package/dist/evaluator-internals/predicates.js.map +1 -0
- package/dist/evaluator-internals/speculative-synthesis.d.ts +102 -0
- package/dist/evaluator-internals/speculative-synthesis.d.ts.map +1 -0
- package/dist/evaluator-internals/speculative-synthesis.js +101 -0
- package/dist/evaluator-internals/speculative-synthesis.js.map +1 -0
- package/dist/evaluator.d.ts +101 -0
- package/dist/evaluator.d.ts.map +1 -0
- package/dist/evaluator.js +714 -0
- package/dist/evaluator.js.map +1 -0
- package/dist/helpers/walker-unknown-cwd-reason.d.ts +68 -0
- package/dist/helpers/walker-unknown-cwd-reason.d.ts.map +1 -0
- package/dist/helpers/walker-unknown-cwd-reason.js +73 -0
- package/dist/helpers/walker-unknown-cwd-reason.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/drop-unused-observers.d.ts +43 -0
- package/dist/internal/drop-unused-observers.d.ts.map +1 -0
- package/dist/internal/drop-unused-observers.js +50 -0
- package/dist/internal/drop-unused-observers.js.map +1 -0
- package/dist/internal/finalize-plugin-state.d.ts +22 -0
- package/dist/internal/finalize-plugin-state.d.ts.map +1 -0
- package/dist/internal/finalize-plugin-state.js +14 -0
- package/dist/internal/finalize-plugin-state.js.map +1 -0
- package/dist/internal/merge-observers.d.ts +30 -0
- package/dist/internal/merge-observers.d.ts.map +1 -0
- package/dist/internal/merge-observers.js +27 -0
- package/dist/internal/merge-observers.js.map +1 -0
- package/dist/internal/pattern-utils.d.ts +33 -0
- package/dist/internal/pattern-utils.d.ts.map +1 -0
- package/dist/internal/pattern-utils.js +20 -0
- package/dist/internal/pattern-utils.js.map +1 -0
- package/dist/internal/ref-text.d.ts +11 -0
- package/dist/internal/ref-text.d.ts.map +1 -0
- package/dist/internal/ref-text.js +15 -0
- package/dist/internal/ref-text.js.map +1 -0
- package/dist/internal/session-runtime.d.ts +44 -0
- package/dist/internal/session-runtime.d.ts.map +1 -0
- package/dist/internal/session-runtime.js +143 -0
- package/dist/internal/session-runtime.js.map +1 -0
- package/dist/internal/watch-matcher.d.ts +56 -0
- package/dist/internal/watch-matcher.d.ts.map +1 -0
- package/dist/internal/watch-matcher.js +179 -0
- package/dist/internal/watch-matcher.js.map +1 -0
- package/dist/loader.d.ts +97 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +527 -0
- package/dist/loader.js.map +1 -0
- package/dist/observer-dispatcher.d.ts +69 -0
- package/dist/observer-dispatcher.d.ts.map +1 -0
- package/dist/observer-dispatcher.js +184 -0
- package/dist/observer-dispatcher.js.map +1 -0
- package/dist/plugin-merger.d.ts +175 -0
- package/dist/plugin-merger.d.ts.map +1 -0
- package/dist/plugin-merger.js +450 -0
- package/dist/plugin-merger.js.map +1 -0
- package/dist/plugins/git/branch-tracker.d.ts +85 -0
- package/dist/plugins/git/branch-tracker.d.ts.map +1 -0
- package/dist/plugins/git/branch-tracker.js +181 -0
- package/dist/plugins/git/branch-tracker.js.map +1 -0
- package/dist/plugins/git/cwd-extensions.d.ts +10 -0
- package/dist/plugins/git/cwd-extensions.d.ts.map +1 -0
- package/dist/plugins/git/cwd-extensions.js +155 -0
- package/dist/plugins/git/cwd-extensions.js.map +1 -0
- package/dist/plugins/git/git-ops.d.ts +89 -0
- package/dist/plugins/git/git-ops.d.ts.map +1 -0
- package/dist/plugins/git/git-ops.js +107 -0
- package/dist/plugins/git/git-ops.js.map +1 -0
- package/dist/plugins/git/index.d.ts +225 -0
- package/dist/plugins/git/index.d.ts.map +1 -0
- package/dist/plugins/git/index.js +52 -0
- package/dist/plugins/git/index.js.map +1 -0
- package/dist/plugins/git/predicates.d.ts +312 -0
- package/dist/plugins/git/predicates.d.ts.map +1 -0
- package/dist/plugins/git/predicates.js +580 -0
- package/dist/plugins/git/predicates.js.map +1 -0
- package/dist/plugins/git/rules.d.ts +209 -0
- package/dist/plugins/git/rules.d.ts.map +1 -0
- package/dist/plugins/git/rules.js +267 -0
- package/dist/plugins/git/rules.js.map +1 -0
- package/dist/schema.d.ts +1799 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +4 -0
- package/dist/schema.js.map +1 -0
- package/dist/testing/index.d.ts +621 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +747 -0
- package/dist/testing/index.js.map +1 -0
- package/examples/README.md +83 -0
- package/examples/combined-git-discipline/README.md +52 -0
- package/examples/combined-git-discipline/node_modules/.bin/pi-steering +43 -0
- package/examples/combined-git-discipline/package.json +18 -0
- package/examples/combined-git-discipline/steering.json +27 -0
- package/examples/combined-git-discipline/steering.test.ts +42 -0
- package/examples/combined-git-discipline/steering.ts +64 -0
- package/examples/combined-git-discipline/tsconfig.json +7 -0
- package/examples/draft-prs-only/README.md +42 -0
- package/examples/draft-prs-only/node_modules/.bin/pi-steering +43 -0
- package/examples/draft-prs-only/package.json +18 -0
- package/examples/draft-prs-only/steering.json +12 -0
- package/examples/draft-prs-only/steering.test.ts +48 -0
- package/examples/draft-prs-only/steering.ts +36 -0
- package/examples/draft-prs-only/tsconfig.json +7 -0
- package/examples/dynamic-reason-runtime-cwd/README.md +148 -0
- package/examples/dynamic-reason-runtime-cwd/node_modules/.bin/pi-steering +43 -0
- package/examples/dynamic-reason-runtime-cwd/package.json +18 -0
- package/examples/dynamic-reason-runtime-cwd/steering.test.ts +130 -0
- package/examples/dynamic-reason-runtime-cwd/steering.ts +82 -0
- package/examples/dynamic-reason-runtime-cwd/tsconfig.json +7 -0
- package/examples/force-push-strict/README.md +75 -0
- package/examples/force-push-strict/node_modules/.bin/pi-steering +43 -0
- package/examples/force-push-strict/package.json +18 -0
- package/examples/force-push-strict/steering.json +12 -0
- package/examples/force-push-strict/steering.test.ts +51 -0
- package/examples/force-push-strict/steering.ts +54 -0
- package/examples/force-push-strict/tsconfig.json +7 -0
- package/examples/no-amend/README.md +52 -0
- package/examples/no-amend/node_modules/.bin/pi-steering +43 -0
- package/examples/no-amend/package.json +18 -0
- package/examples/no-amend/steering.cwd-scoped.json +12 -0
- package/examples/no-amend/steering.json +11 -0
- package/examples/no-amend/steering.test.ts +49 -0
- package/examples/no-amend/steering.ts +38 -0
- package/examples/no-amend/tsconfig.json +7 -0
- package/examples/work-item-plugin/README.md +103 -0
- package/examples/work-item-plugin/node_modules/.bin/pi-steering +43 -0
- package/examples/work-item-plugin/package.json +19 -0
- package/examples/work-item-plugin/src/index.test.ts +168 -0
- package/examples/work-item-plugin/src/index.ts +122 -0
- package/examples/work-item-plugin/src/observers/npm-test-tracker.test.ts +95 -0
- package/examples/work-item-plugin/src/observers/npm-test-tracker.ts +88 -0
- package/examples/work-item-plugin/src/observers/retest-required-tracker.test.ts +81 -0
- package/examples/work-item-plugin/src/observers/retest-required-tracker.ts +81 -0
- package/examples/work-item-plugin/src/predicates/work-item-format.test.ts +157 -0
- package/examples/work-item-plugin/src/predicates/work-item-format.ts +99 -0
- package/examples/work-item-plugin/src/rules/commit-description-check.test.ts +107 -0
- package/examples/work-item-plugin/src/rules/commit-description-check.ts +83 -0
- package/examples/work-item-plugin/src/rules/commit-requires-work-item.test.ts +79 -0
- package/examples/work-item-plugin/src/rules/commit-requires-work-item.ts +56 -0
- package/examples/work-item-plugin/src/rules/push-requires-tests.test.ts +245 -0
- package/examples/work-item-plugin/src/rules/push-requires-tests.ts +62 -0
- package/examples/work-item-plugin/tsconfig.json +8 -0
- package/package.json +88 -0
- package/skills/steering-authoring/SKILL.md +102 -0
- package/src/INVARIANTS.md +116 -0
- package/src/__test-helpers__.ts +243 -0
- package/src/bin/pi-steering.test.ts +920 -0
- package/src/bin/pi-steering.ts +584 -0
- package/src/compat.test.ts +431 -0
- package/src/compat.ts +282 -0
- package/src/defaults.test.ts +585 -0
- package/src/defaults.ts +148 -0
- package/src/define-config.test.ts +888 -0
- package/src/define-config.ts +375 -0
- package/src/define-predicate.test.ts +70 -0
- package/src/define-predicate.ts +57 -0
- package/src/evaluator-internals/context.ts +290 -0
- package/src/evaluator-internals/override.test.ts +99 -0
- package/src/evaluator-internals/override.ts +83 -0
- package/src/evaluator-internals/predicates.ts +1037 -0
- package/src/evaluator-internals/speculative-synthesis.test.ts +411 -0
- package/src/evaluator-internals/speculative-synthesis.ts +199 -0
- package/src/evaluator.test.ts +6296 -0
- package/src/evaluator.ts +1106 -0
- package/src/factory-time-load.test.ts +630 -0
- package/src/helpers/walker-unknown-cwd-reason.test.ts +73 -0
- package/src/helpers/walker-unknown-cwd-reason.ts +83 -0
- package/src/index.test.ts +836 -0
- package/src/index.ts +229 -0
- package/src/internal/drop-unused-observers.test.ts +141 -0
- package/src/internal/drop-unused-observers.ts +81 -0
- package/src/internal/finalize-plugin-state.ts +43 -0
- package/src/internal/merge-observers.ts +51 -0
- package/src/internal/pattern-utils.test.ts +52 -0
- package/src/internal/pattern-utils.ts +38 -0
- package/src/internal/ref-text.test.ts +54 -0
- package/src/internal/ref-text.ts +17 -0
- package/src/internal/session-runtime.test.ts +629 -0
- package/src/internal/session-runtime.ts +201 -0
- package/src/internal/watch-matcher.test.ts +326 -0
- package/src/internal/watch-matcher.ts +200 -0
- package/src/jsdoc-propagation.test.ts +277 -0
- package/src/loader.test.ts +1098 -0
- package/src/loader.ts +581 -0
- package/src/not-block-onunknown.test.ts +554 -0
- package/src/observer-dispatcher.test.ts +939 -0
- package/src/observer-dispatcher.ts +290 -0
- package/src/per-predicate-typing.test.ts +361 -0
- package/src/plugin-merger.test.ts +790 -0
- package/src/plugin-merger.ts +598 -0
- package/src/plugins/git/README.md +485 -0
- package/src/plugins/git/branch-tracker.test.ts +281 -0
- package/src/plugins/git/branch-tracker.ts +187 -0
- package/src/plugins/git/cwd-extensions.test.ts +230 -0
- package/src/plugins/git/cwd-extensions.ts +160 -0
- package/src/plugins/git/git-ops.test.ts +397 -0
- package/src/plugins/git/git-ops.ts +173 -0
- package/src/plugins/git/index.ts +253 -0
- package/src/plugins/git/integration.test.ts +705 -0
- package/src/plugins/git/predicates.test.ts +1401 -0
- package/src/plugins/git/predicates.ts +693 -0
- package/src/plugins/git/rules.test.ts +1001 -0
- package/src/plugins/git/rules.ts +307 -0
- package/src/schema.test.ts +259 -0
- package/src/schema.ts +1963 -0
- package/src/testing/index.test.ts +1696 -0
- package/src/testing/index.ts +1472 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Part of pi-steering.
|
|
3
|
+
/**
|
|
4
|
+
* `branch` tracker for the git plugin.
|
|
5
|
+
*
|
|
6
|
+
* Models the effective git branch AT EACH extracted command within a
|
|
7
|
+
* single `tool_call`. The motivating case is the ADR's
|
|
8
|
+
* `git checkout A && git commit` example: a session-level
|
|
9
|
+
* "current branch" query would miss the mid-command checkout and allow
|
|
10
|
+
* the commit on what looks like a non-protected branch. A walker-backed
|
|
11
|
+
* tracker folds the `git checkout` delta into the branch value seen by
|
|
12
|
+
* the subsequent `git commit`, so a rule gated on
|
|
13
|
+
* `when: { branch: /^main$/ }` correctly fires if the chain ends up on
|
|
14
|
+
* `main`.
|
|
15
|
+
*
|
|
16
|
+
* Semantics:
|
|
17
|
+
* - `git checkout X` - sequential; branch becomes `X` for the rest
|
|
18
|
+
* of this scope.
|
|
19
|
+
* - `git switch X` - sequential; same as above.
|
|
20
|
+
* - `git checkout -b NEW` / `git switch -c NEW` - sequential; branch
|
|
21
|
+
* becomes `NEW`. The `-b` / `-c` token is consumed; the following
|
|
22
|
+
* argument is the new branch name.
|
|
23
|
+
* - Anything else under `git` (including `git commit`, `git status`,
|
|
24
|
+
* `git checkout -- FILE`) leaves the branch unchanged. Returning
|
|
25
|
+
* `current` (not `undefined`) is important: a `git commit` that
|
|
26
|
+
* happens to carry a non-static arg (e.g. `-m "$MSG"`) must NOT
|
|
27
|
+
* collapse branch state to `unknown`.
|
|
28
|
+
* - Non-static branch names (`git checkout $BR`, `git checkout "$BR"`)
|
|
29
|
+
* return `undefined`, which the walker translates to the tracker's
|
|
30
|
+
* `unknown` sentinel. Predicates then apply their `onUnknown`
|
|
31
|
+
* policy (default `"block"` - see the `branch` predicate handler).
|
|
32
|
+
*
|
|
33
|
+
* Subshells: `isolated` - a `(git checkout X)` inside parens cannot
|
|
34
|
+
* change the enclosing shell's branch (real git semantics - the
|
|
35
|
+
* subshell has no effect on the parent's working tree state for this
|
|
36
|
+
* dimension).
|
|
37
|
+
*
|
|
38
|
+
* The tracker distinguishes two "I don't know the branch" states so
|
|
39
|
+
* predicates can apply the correct policy:
|
|
40
|
+
*
|
|
41
|
+
* - `initial: NO_CHECKOUT_IN_CHAIN` - no branch-changing modifier
|
|
42
|
+
* has fired in this ref's scope. The plugin cannot synchronously
|
|
43
|
+
* know the session's current branch at construction time; the
|
|
44
|
+
* `branch` predicate handler shells out via `ctx.exec("git",
|
|
45
|
+
* ["branch", "--show-current"])` to learn it. (A session-start
|
|
46
|
+
* prefetch is a reasonable future optimization but out of scope
|
|
47
|
+
* here.)
|
|
48
|
+
* - `unknown: "unknown"` - a modifier FIRED but couldn't resolve
|
|
49
|
+
* statically (e.g. `git checkout $VAR`). The predicate must NOT
|
|
50
|
+
* shell out in this case: `git branch --show-current` would
|
|
51
|
+
* return the PRE-checkout branch and silently defeat the
|
|
52
|
+
* walker's tracking. Predicates apply their `onUnknown` policy
|
|
53
|
+
* instead (default `"block"` -> fail-closed).
|
|
54
|
+
*
|
|
55
|
+
* The `NO_CHECKOUT_IN_CHAIN` sentinel is chosen to be a string that
|
|
56
|
+
* cannot occur as a real git branch name (git refuses refs containing
|
|
57
|
+
* `:`). Exported so tests and plugin authors can reference it; it
|
|
58
|
+
* should never be constructed ad-hoc.
|
|
59
|
+
*
|
|
60
|
+
* ## Note for plugin authors
|
|
61
|
+
*
|
|
62
|
+
* This tracker is a canonical example of the strict Tracker contract -
|
|
63
|
+
* unresolvable modifier targets return `undefined`, NOT `current`. The
|
|
64
|
+
* built-in `cwdTracker.cd` modifier is a documented Phase-1 exception;
|
|
65
|
+
* do not copy its "return current" shortcut in new trackers.
|
|
66
|
+
*/
|
|
67
|
+
import { isStaticallyResolvable, } from "@cad0p/unbash-walker";
|
|
68
|
+
/**
|
|
69
|
+
* Sentinel value for `branchTracker.initial` - marks "no branch-
|
|
70
|
+
* changing modifier has fired in this ref's scope yet". Distinct
|
|
71
|
+
* from the `"unknown"` sentinel (which marks "a modifier DID fire
|
|
72
|
+
* but couldn't resolve statically"). See the file JSDoc for why the
|
|
73
|
+
* distinction matters to the `branch` predicate's exec fallback
|
|
74
|
+
* decision. The colon is chosen because git rejects it in branch
|
|
75
|
+
* names, so this value cannot collide with a real branch.
|
|
76
|
+
*/
|
|
77
|
+
export const NO_CHECKOUT_IN_CHAIN = "pi-steering:no-checkout-in-chain";
|
|
78
|
+
/** Branch-changing git subcommands. */
|
|
79
|
+
const CHECKOUT_SUBCOMMANDS = new Set(["checkout", "switch"]);
|
|
80
|
+
/** `git checkout -b NEW` / `git switch -c NEW` - new-branch flags. */
|
|
81
|
+
const NEW_BRANCH_FLAGS = new Set(["-b", "-c"]);
|
|
82
|
+
/**
|
|
83
|
+
* Sequential modifier that applies to every `git`-basename command.
|
|
84
|
+
*
|
|
85
|
+
* Returns the new branch for `checkout` / `switch` invocations;
|
|
86
|
+
* returns `current` unchanged for every other git subcommand (so
|
|
87
|
+
* `git commit -m "x"` does NOT collapse the branch to `unknown`);
|
|
88
|
+
* returns `undefined` when the target branch name can't be resolved
|
|
89
|
+
* statically.
|
|
90
|
+
*
|
|
91
|
+
* Pre-subcommand flags are skipped before we look at the subcommand
|
|
92
|
+
* token. Git accepts `git -C PATH`, `git -c KEY=VAL`, `git --no-pager`,
|
|
93
|
+
* `git --git-dir=/x`, `git --work-tree=/y`, `git --paginate`, etc.
|
|
94
|
+
* before the subcommand. Without this skip, `git -C /other checkout
|
|
95
|
+
* feat` would look at the `-C` flag as the subcommand, miss the
|
|
96
|
+
* checkout, and silently bypass `no-main-commit`. This mirrors the
|
|
97
|
+
* `applyGitCwd` flag scanner in `unbash-walker/src/trackers/cwd.ts`:
|
|
98
|
+
* only `-C <path>` and `-c <key=value>` consume an additional token;
|
|
99
|
+
* every other flag form (short cluster, long with or without attached
|
|
100
|
+
* value) is a single token.
|
|
101
|
+
*/
|
|
102
|
+
const gitBranchModifier = {
|
|
103
|
+
scope: "sequential",
|
|
104
|
+
apply: (args, current) => {
|
|
105
|
+
// Scan past pre-subcommand flags to find the real subcommand.
|
|
106
|
+
// Known value-consuming flags: `-C <path>`, `-c <key=val>`.
|
|
107
|
+
// Others (`--no-pager`, `--git-dir=/x`, `--work-tree=/y`,
|
|
108
|
+
// `--paginate`, `-p`, etc.) are single tokens.
|
|
109
|
+
let i = 0;
|
|
110
|
+
while (i < args.length) {
|
|
111
|
+
const tokWord = args[i];
|
|
112
|
+
const tok = tokWord?.value ?? tokWord?.text ?? "";
|
|
113
|
+
if (!tok.startsWith("-"))
|
|
114
|
+
break; // found the subcommand
|
|
115
|
+
if (tok === "-C" || tok === "-c") {
|
|
116
|
+
i += 2;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
i++; // --long-flag, --long=value, or -shortcluster
|
|
120
|
+
}
|
|
121
|
+
// No subcommand - e.g. bare `git` or `git --help`. Branch unchanged.
|
|
122
|
+
const subcmdWord = args[i];
|
|
123
|
+
if (!subcmdWord)
|
|
124
|
+
return current;
|
|
125
|
+
const subcmd = subcmdWord.value ?? subcmdWord.text;
|
|
126
|
+
if (!subcmd || !CHECKOUT_SUBCOMMANDS.has(subcmd))
|
|
127
|
+
return current;
|
|
128
|
+
// `git checkout` / `git switch` with no further args: also a
|
|
129
|
+
// help / error case. Leave branch alone.
|
|
130
|
+
const firstArgWord = args[i + 1];
|
|
131
|
+
if (!firstArgWord)
|
|
132
|
+
return current;
|
|
133
|
+
const firstArg = firstArgWord.value ?? firstArgWord.text;
|
|
134
|
+
if (firstArg === undefined)
|
|
135
|
+
return current;
|
|
136
|
+
// `git checkout --help` / `git checkout -h` - help request, not a
|
|
137
|
+
// branch change. Every other unrecognised flag form (e.g. `--`, `.`,
|
|
138
|
+
// `--force`) still flows through the default branch-name path and
|
|
139
|
+
// is accepted as a documented false-positive (see test cases).
|
|
140
|
+
if (firstArg === "--help" || firstArg === "-h")
|
|
141
|
+
return current;
|
|
142
|
+
// `git checkout -b NEW` / `git switch -c NEW` - the branch
|
|
143
|
+
// argument is at position i + 2.
|
|
144
|
+
if (NEW_BRANCH_FLAGS.has(firstArg)) {
|
|
145
|
+
const newBranchWord = args[i + 2];
|
|
146
|
+
if (!newBranchWord)
|
|
147
|
+
return current;
|
|
148
|
+
if (!isStaticallyResolvable(newBranchWord))
|
|
149
|
+
return undefined;
|
|
150
|
+
const newBranch = newBranchWord.value ?? newBranchWord.text;
|
|
151
|
+
return newBranch === undefined ? current : newBranch;
|
|
152
|
+
}
|
|
153
|
+
// Plain `git checkout X` / `git switch X`. Note: `git checkout --
|
|
154
|
+
// FILE` (restore path) is NOT a branch change, but the first arg
|
|
155
|
+
// `--` happens to be statically resolvable AND `!= undefined`.
|
|
156
|
+
// That's a false-positive corner we accept for Phase 4 - agents
|
|
157
|
+
// rarely emit the `--` separator form, and the alternative
|
|
158
|
+
// (hard-coding a `--` check) brittles against other pathspec
|
|
159
|
+
// conventions. Follow-up if it bites.
|
|
160
|
+
if (!isStaticallyResolvable(firstArgWord))
|
|
161
|
+
return undefined;
|
|
162
|
+
return firstArg;
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* The branch tracker.
|
|
167
|
+
*
|
|
168
|
+
* Registered by the git plugin under `trackers.branch`. Walker-merged
|
|
169
|
+
* with any future plugin wanting to extend branch semantics (though
|
|
170
|
+
* the tracker name `branch` is expected to stay owned by the git
|
|
171
|
+
* plugin - name collisions are a hard error per the plugin-merger).
|
|
172
|
+
*/
|
|
173
|
+
export const branchTracker = {
|
|
174
|
+
initial: NO_CHECKOUT_IN_CHAIN,
|
|
175
|
+
unknown: "unknown",
|
|
176
|
+
modifiers: {
|
|
177
|
+
git: gitBranchModifier,
|
|
178
|
+
},
|
|
179
|
+
subshellSemantics: "isolated",
|
|
180
|
+
};
|
|
181
|
+
//# sourceMappingURL=branch-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-tracker.js","sourceRoot":"","sources":["../../../src/plugins/git/branch-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Modifier } from "@cad0p/unbash-walker";
|
|
2
|
+
/**
|
|
3
|
+
* Modifiers the git plugin registers under `cwd.git`. Exposed as an
|
|
4
|
+
* array so the plugin merger sees each entry distinctly; the runtime
|
|
5
|
+
* could today also accept a single modifier, but the array form
|
|
6
|
+
* leaves room for future git-specific cwd tweaks without changing
|
|
7
|
+
* the plugin manifest.
|
|
8
|
+
*/
|
|
9
|
+
export declare const gitCwdExtensions: readonly Modifier<unknown>[];
|
|
10
|
+
//# sourceMappingURL=cwd-extensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cwd-extensions.d.ts","sourceRoot":"","sources":["../../../src/plugins/git/cwd-extensions.ts"],"names":[],"mappings":"AAqFA,OAAO,EAA0B,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAiE7E;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,QAAQ,CAAC,OAAO,CAAC,EAExD,CAAC"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Part of pi-steering.
|
|
3
|
+
/**
|
|
4
|
+
* `cwd` tracker extensions for the git plugin.
|
|
5
|
+
*
|
|
6
|
+
* The built-in `cwdTracker` (in unbash-walker) handles `git -C DIR
|
|
7
|
+
* subcmd`. Two narrower git flags - `--git-dir=/path` and
|
|
8
|
+
* `--work-tree=/path` - express the same "run this command as if the
|
|
9
|
+
* working directory were /path" intent, but weren't modelled in the
|
|
10
|
+
* walker's core to keep its git knowledge minimal. Parsing them lives
|
|
11
|
+
* in the git plugin as a tracker extension: registered under
|
|
12
|
+
* `trackerExtensions.cwd.git`, the modifier layers on top of the core
|
|
13
|
+
* cwd tracker's own `git` per-command modifier.
|
|
14
|
+
*
|
|
15
|
+
* Scope is `per-command` - consistent with `-C` and git's own runtime
|
|
16
|
+
* behavior: these flags affect only the command they're attached to.
|
|
17
|
+
*
|
|
18
|
+
* Composition with `-C`:
|
|
19
|
+
*
|
|
20
|
+
* `git -C /a --git-dir=/b status`
|
|
21
|
+
*
|
|
22
|
+
* The plugin-merger appends this extension's modifier AFTER the core
|
|
23
|
+
* `-C` modifier under the same basename. The walker then runs BOTH
|
|
24
|
+
* modifiers left-to-right against the same arg list, each layering
|
|
25
|
+
* its own contribution on top of the running recorded value:
|
|
26
|
+
*
|
|
27
|
+
* 1. Core `-C /a` modifier (scans pre-subcommand flags only):
|
|
28
|
+
* running cwd "/initial" -> "/a"
|
|
29
|
+
* 2. This `--git-dir=/b` modifier (scans all args for the two
|
|
30
|
+
* specific flag forms):
|
|
31
|
+
* running cwd "/a" -> "/b"
|
|
32
|
+
*
|
|
33
|
+
* The LATER modifier wins on absolute paths; for relative paths it
|
|
34
|
+
* joins against the running value from the previous step. Test
|
|
35
|
+
* expectation for `git -C /a --git-dir=b status` is `/a/b`.
|
|
36
|
+
*
|
|
37
|
+
* Order in the source matters too:
|
|
38
|
+
*
|
|
39
|
+
* - `git --git-dir=/b -C /a status` -> `/b` (core `-C` runs on
|
|
40
|
+
* the pre-sequential value `/initial`, producing `/a`; then
|
|
41
|
+
* this modifier runs against the same args, overriding to
|
|
42
|
+
* `/b`).
|
|
43
|
+
* - `git -C /a --git-dir=/b status` -> `/b` (same left-to-right
|
|
44
|
+
* modifier order; the core scanner produces `/a`, this one
|
|
45
|
+
* overrides to `/b`).
|
|
46
|
+
*
|
|
47
|
+
* In both cases the `--git-dir=` / `--work-tree=` flags win, matching
|
|
48
|
+
* git's documented precedence.
|
|
49
|
+
*
|
|
50
|
+
* Known limitations:
|
|
51
|
+
*
|
|
52
|
+
* - `--git-dir` without an `=` (space-separated form:
|
|
53
|
+
* `git --git-dir /path ...`) is NOT handled. Both git and shell
|
|
54
|
+
* tokenize this as two words; walker modifiers today only see the
|
|
55
|
+
* single `--git-dir` token. Wiring a two-token form is possible
|
|
56
|
+
* but agents overwhelmingly emit the `=` form; follow-up.
|
|
57
|
+
* - Non-static targets (`--git-dir=$VAR`) return `undefined`,
|
|
58
|
+
* collapsing the per-command cwd to `"unknown"`. Predicates then
|
|
59
|
+
* apply `onUnknown`.
|
|
60
|
+
* - `--work-tree` without `--git-dir` is semantically incomplete in
|
|
61
|
+
* real git (the gitdir still points at the default), but for cwd
|
|
62
|
+
* tracking purposes we treat them identically - they both mean
|
|
63
|
+
* "the command's effective cwd is this path". If a rule author
|
|
64
|
+
* needs the distinction (e.g. a repo-path vs tree-path
|
|
65
|
+
* predicate), they can split it in their own plugin.
|
|
66
|
+
*
|
|
67
|
+
* Known accepted false-positives:
|
|
68
|
+
*
|
|
69
|
+
* - `git log --git-dir=/repo` -> cwd recorded as /repo. Real git
|
|
70
|
+
* treats post-subcommand `--git-dir=` as a pathspec, not a
|
|
71
|
+
* global flag. Over-match rate is low in practice (agents emit
|
|
72
|
+
* `git --git-dir=/x log`, not the inverse), and stopping the
|
|
73
|
+
* scan at the first non-flag token would break composition with
|
|
74
|
+
* the core `-C` modifier which iterates the same args list.
|
|
75
|
+
* Accepted as a Phase-4 corner.
|
|
76
|
+
* - `git diff -- --git-dir=/x` -> cwd recorded as /x. Arguments
|
|
77
|
+
* after `--` are always pathspecs in real git. Same mitigation
|
|
78
|
+
* rationale as above; stopping the scan at `--` would still
|
|
79
|
+
* need to run after the core `-C` scanner has finished, not
|
|
80
|
+
* before, to avoid the composition breakage.
|
|
81
|
+
*/
|
|
82
|
+
import * as path from "node:path";
|
|
83
|
+
import { isStaticallyResolvable } from "@cad0p/unbash-walker";
|
|
84
|
+
/** Regex matched against each `git`-command argument token. */
|
|
85
|
+
const GIT_DIR_RE = /^--git-dir=(.+)$/;
|
|
86
|
+
const WORK_TREE_RE = /^--work-tree=(.+)$/;
|
|
87
|
+
/**
|
|
88
|
+
* Read a word's static value. Falls back to `text` when `value`
|
|
89
|
+
* isn't materialised (pure literal fast path in unbash).
|
|
90
|
+
*/
|
|
91
|
+
function wordValue(w) {
|
|
92
|
+
return w?.value ?? w?.text;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Scan git's pre-subcommand flags for `--git-dir=DIR` / `--work-tree=DIR`
|
|
96
|
+
* and layer the target on top of the current cwd.
|
|
97
|
+
*
|
|
98
|
+
* Stops at the first non-flag token (the subcommand) - identical shape
|
|
99
|
+
* to the core `-C` scanner. Multiple occurrences compose left-to-right
|
|
100
|
+
* (an absolute path replaces, a relative path joins to the running
|
|
101
|
+
* cwd). If we encounter a target we can't statically resolve, we bail
|
|
102
|
+
* returning `undefined`; the walker translates that to the cwd
|
|
103
|
+
* tracker's `"unknown"` sentinel.
|
|
104
|
+
*/
|
|
105
|
+
const gitCwdExtension = {
|
|
106
|
+
scope: "per-command",
|
|
107
|
+
apply: (args, current) => {
|
|
108
|
+
let cwd = current;
|
|
109
|
+
// Scan ALL args (not just pre-subcommand): we match only the two
|
|
110
|
+
// specific flag forms `--git-dir=X` / `--work-tree=X`, so there's
|
|
111
|
+
// no ambiguity with subcommand arguments. `git log
|
|
112
|
+
// --grep="--git-dir=foo"` is safe because the quoted string is a
|
|
113
|
+
// SINGLE argument whose text starts with `--grep`, not
|
|
114
|
+
// `--git-dir`, so the regex doesn't match.
|
|
115
|
+
//
|
|
116
|
+
// A stop-at-non-flag scan (mirroring the core `-C` modifier)
|
|
117
|
+
// would break composition with `-C`: the walker runs this
|
|
118
|
+
// modifier AGAINST THE SAME args the core `-C` modifier saw, not
|
|
119
|
+
// a reduced arg list. On `git -C /a --git-dir=/b status`, the
|
|
120
|
+
// args are still `[-C, /a, --git-dir=/b, status]`; a stop-at-
|
|
121
|
+
// non-flag scan would terminate at `/a` (the value of `-C`,
|
|
122
|
+
// which doesn't start with `-`) and miss the `--git-dir=` flag.
|
|
123
|
+
for (const w of args) {
|
|
124
|
+
const tok = wordValue(w);
|
|
125
|
+
if (tok === undefined)
|
|
126
|
+
continue;
|
|
127
|
+
const gitDirMatch = tok.match(GIT_DIR_RE);
|
|
128
|
+
const workTreeMatch = tok.match(WORK_TREE_RE);
|
|
129
|
+
const match = gitDirMatch ?? workTreeMatch;
|
|
130
|
+
if (!match)
|
|
131
|
+
continue;
|
|
132
|
+
// `--git-dir=$VAR` / `--work-tree=$(...)` - the captured
|
|
133
|
+
// piece is there in the raw text, but the overall Word is
|
|
134
|
+
// non-static. Refuse to invent a value.
|
|
135
|
+
if (!isStaticallyResolvable(w))
|
|
136
|
+
return undefined;
|
|
137
|
+
const target = match[1];
|
|
138
|
+
if (target === undefined || target.length === 0)
|
|
139
|
+
continue;
|
|
140
|
+
cwd = path.isAbsolute(target) ? target : path.join(cwd, target);
|
|
141
|
+
}
|
|
142
|
+
return cwd;
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Modifiers the git plugin registers under `cwd.git`. Exposed as an
|
|
147
|
+
* array so the plugin merger sees each entry distinctly; the runtime
|
|
148
|
+
* could today also accept a single modifier, but the array form
|
|
149
|
+
* leaves room for future git-specific cwd tweaks without changing
|
|
150
|
+
* the plugin manifest.
|
|
151
|
+
*/
|
|
152
|
+
export const gitCwdExtensions = [
|
|
153
|
+
gitCwdExtension,
|
|
154
|
+
];
|
|
155
|
+
//# sourceMappingURL=cwd-extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cwd-extensions.js","sourceRoot":"","sources":["../../../src/plugins/git/cwd-extensions.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,uBAAuB;AAEvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,sBAAsB,EAAiB,MAAM,sBAAsB,CAAC;AAE7E,+DAA+D;AAC/D,MAAM,UAAU,GAAG,kBAAkB,CAAC;AACtC,MAAM,YAAY,GAAG,oBAAoB,CAAC;AAE1C;;;GAGG;AACH,SAAS,SAAS,CAAC,CAAmB;IACpC,OAAO,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,IAAI,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,eAAe,GAAqB;IACxC,KAAK,EAAE,aAAa;IACpB,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,GAAG,GAAG,OAAO,CAAC;QAClB,iEAAiE;QACjE,kEAAkE;QAClE,mDAAmD;QACnD,iEAAiE;QACjE,uDAAuD;QACvD,2CAA2C;QAC3C,EAAE;QACF,6DAA6D;QAC7D,0DAA0D;QAC1D,iEAAiE;QACjE,8DAA8D;QAC9D,8DAA8D;QAC9D,4DAA4D;QAC5D,gEAAgE;QAChE,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,GAAG,KAAK,SAAS;gBAAE,SAAS;YAEhC,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAC1C,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAG,WAAW,IAAI,aAAa,CAAC;YAC3C,IAAI,CAAC,KAAK;gBAAE,SAAS;YAErB,yDAAyD;YACzD,0DAA0D;YAC1D,wCAAwC;YACxC,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC;gBAAE,OAAO,SAAS,CAAC;YAEjD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAC1D,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAiC;IAC5D,eAAoC;CACrC,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw git query helpers for the git plugin — reusable functions that
|
|
3
|
+
* shell out to git and return trimmed raw data (or `null` on any
|
|
4
|
+
* failure). Exposed so downstream plugins can compose per-directory
|
|
5
|
+
* scans without re-implementing the shell invocations, and so the
|
|
6
|
+
* predicates in this plugin stay thin pattern-matching wrappers
|
|
7
|
+
* around the helpers.
|
|
8
|
+
*
|
|
9
|
+
* Each helper:
|
|
10
|
+
*
|
|
11
|
+
* - takes `(ctx, cwd?)`; `cwd` defaults to `ctx.cwd`, letting
|
|
12
|
+
* callers iterate over per-subpackage git dirs (common in
|
|
13
|
+
* multi-package workspace contexts like `cr --all`).
|
|
14
|
+
* - returns the raw data on success, `null` on any failure
|
|
15
|
+
* (non-zero exit, spawn error, exception thrown inside
|
|
16
|
+
* `ctx.exec`). Callers layer their own "what does null mean"
|
|
17
|
+
* policy — predicates apply `onUnknown`; custom logic can
|
|
18
|
+
* inspect the null directly.
|
|
19
|
+
* - relies on the evaluator's per-tool_call `exec` memoization
|
|
20
|
+
* (`(cmd, args, cwd)` tuple) so multiple helpers reading the
|
|
21
|
+
* same repo state don't re-fork git.
|
|
22
|
+
*
|
|
23
|
+
* ## Consumer note
|
|
24
|
+
*
|
|
25
|
+
* The helpers return **raw query results** and are INTENTIONALLY free
|
|
26
|
+
* of the `onUnknown` / pattern-matching layer. That layer lives in
|
|
27
|
+
* `predicates.ts`. If you want predicate-style semantics, call the
|
|
28
|
+
* matching `when.*` handler (also re-exported from the plugin index)
|
|
29
|
+
* rather than the helper.
|
|
30
|
+
*
|
|
31
|
+
* ## Branch caveat
|
|
32
|
+
*
|
|
33
|
+
* {@link getBranch} intentionally DOES NOT consult `walkerState.branch`
|
|
34
|
+
* — the walker-state short-circuit is a predicate-layer concern (see
|
|
35
|
+
* the `branch` predicate's JSDoc for the three-way tracker
|
|
36
|
+
* discrimination). Downstream plugins iterating per-directory usually
|
|
37
|
+
* care about the on-disk branch at each directory, not the
|
|
38
|
+
* walker-tracked branch of the current bash chain at `ctx.cwd`; the
|
|
39
|
+
* helper therefore always shells out.
|
|
40
|
+
*/
|
|
41
|
+
import type { PredicateContext } from "../../schema.ts";
|
|
42
|
+
/**
|
|
43
|
+
* `git branch --show-current` at `cwd` (default: `ctx.cwd`). Returns
|
|
44
|
+
* the current branch name, or `null` when the command fails OR stdout
|
|
45
|
+
* is empty (detached HEAD).
|
|
46
|
+
*
|
|
47
|
+
* Does NOT consult `ctx.walkerState.branch` — see file header
|
|
48
|
+
* "Branch caveat" for why. Predicates that need the walker-state
|
|
49
|
+
* short-circuit should use the `branch` predicate handler instead.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getBranch(ctx: PredicateContext, cwd?: string): Promise<string | null>;
|
|
52
|
+
/**
|
|
53
|
+
* `git rev-parse --abbrev-ref @{upstream}` at `cwd` (default:
|
|
54
|
+
* `ctx.cwd`). Returns the tracking branch name (e.g. `origin/main`),
|
|
55
|
+
* or `null` when no upstream is configured or the command fails.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getUpstream(ctx: PredicateContext, cwd?: string): Promise<string | null>;
|
|
58
|
+
/**
|
|
59
|
+
* `git rev-list --count <wrt>..HEAD` at `cwd` (default: `ctx.cwd`).
|
|
60
|
+
* Returns the number of commits HEAD is ahead of `wrt`, or `null`
|
|
61
|
+
* when the command fails (e.g. `wrt` doesn't resolve, detached HEAD,
|
|
62
|
+
* not a repo).
|
|
63
|
+
*
|
|
64
|
+
* `wrt` defaults to `@{upstream}`. Pass a specific ref like
|
|
65
|
+
* `"origin/main"` when the upstream configuration isn't guaranteed.
|
|
66
|
+
*/
|
|
67
|
+
export declare function getCommitsAhead(ctx: PredicateContext, wrt?: string, cwd?: string): Promise<number | null>;
|
|
68
|
+
/**
|
|
69
|
+
* `git diff --cached --quiet` at `cwd` (default: `ctx.cwd`). Exit
|
|
70
|
+
* code discrimination:
|
|
71
|
+
*
|
|
72
|
+
* - `0` → no staged changes → returns `false`
|
|
73
|
+
* - `1` → staged changes exist → returns `true`
|
|
74
|
+
* - anything else (spawn error, weird exit) → returns `null`
|
|
75
|
+
*/
|
|
76
|
+
export declare function getStagedChanges(ctx: PredicateContext, cwd?: string): Promise<boolean | null>;
|
|
77
|
+
/**
|
|
78
|
+
* `git status --porcelain` at `cwd` (default: `ctx.cwd`). Returns
|
|
79
|
+
* `true` when the working tree is clean (empty output), `false`
|
|
80
|
+
* when dirty, `null` on any command failure.
|
|
81
|
+
*/
|
|
82
|
+
export declare function getWorkingTreeClean(ctx: PredicateContext, cwd?: string): Promise<boolean | null>;
|
|
83
|
+
/**
|
|
84
|
+
* `git config --get remote.origin.url` at `cwd` (default: `ctx.cwd`).
|
|
85
|
+
* Returns the origin URL string, or `null` when no origin is
|
|
86
|
+
* configured or the command fails.
|
|
87
|
+
*/
|
|
88
|
+
export declare function getRemoteUrl(ctx: PredicateContext, cwd?: string): Promise<string | null>;
|
|
89
|
+
//# sourceMappingURL=git-ops.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-ops.d.ts","sourceRoot":"","sources":["../../../src/plugins/git/git-ops.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAuBxD;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,gBAAgB,EACrB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,gBAAgB,EACrB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,gBAAgB,EACrB,GAAG,GAAE,MAAsB,EAC3B,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,gBAAgB,EACrB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAUzB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,gBAAgB,EACrB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAIzB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,gBAAgB,EACrB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Part of pi-steering.
|
|
3
|
+
/**
|
|
4
|
+
* Run a git command in the given cwd and return its trimmed stdout on
|
|
5
|
+
* exit 0, or `null` on any failure. Thin wrapper used by every helper
|
|
6
|
+
* here so failure modes (non-zero exit, spawn error, exception)
|
|
7
|
+
* collapse to a uniform `null`.
|
|
8
|
+
*/
|
|
9
|
+
async function tryGit(ctx, args, cwd) {
|
|
10
|
+
try {
|
|
11
|
+
const opts = cwd !== undefined ? { cwd } : { cwd: ctx.cwd };
|
|
12
|
+
const res = await ctx.exec("git", [...args], opts);
|
|
13
|
+
if (res.exitCode !== 0)
|
|
14
|
+
return null;
|
|
15
|
+
return res.stdout.trim();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* `git branch --show-current` at `cwd` (default: `ctx.cwd`). Returns
|
|
23
|
+
* the current branch name, or `null` when the command fails OR stdout
|
|
24
|
+
* is empty (detached HEAD).
|
|
25
|
+
*
|
|
26
|
+
* Does NOT consult `ctx.walkerState.branch` — see file header
|
|
27
|
+
* "Branch caveat" for why. Predicates that need the walker-state
|
|
28
|
+
* short-circuit should use the `branch` predicate handler instead.
|
|
29
|
+
*/
|
|
30
|
+
export async function getBranch(ctx, cwd) {
|
|
31
|
+
const out = await tryGit(ctx, ["branch", "--show-current"], cwd);
|
|
32
|
+
if (out === null || out.length === 0)
|
|
33
|
+
return null;
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* `git rev-parse --abbrev-ref @{upstream}` at `cwd` (default:
|
|
38
|
+
* `ctx.cwd`). Returns the tracking branch name (e.g. `origin/main`),
|
|
39
|
+
* or `null` when no upstream is configured or the command fails.
|
|
40
|
+
*/
|
|
41
|
+
export async function getUpstream(ctx, cwd) {
|
|
42
|
+
const out = await tryGit(ctx, ["rev-parse", "--abbrev-ref", "@{upstream}"], cwd);
|
|
43
|
+
if (out === null || out.length === 0)
|
|
44
|
+
return null;
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `git rev-list --count <wrt>..HEAD` at `cwd` (default: `ctx.cwd`).
|
|
49
|
+
* Returns the number of commits HEAD is ahead of `wrt`, or `null`
|
|
50
|
+
* when the command fails (e.g. `wrt` doesn't resolve, detached HEAD,
|
|
51
|
+
* not a repo).
|
|
52
|
+
*
|
|
53
|
+
* `wrt` defaults to `@{upstream}`. Pass a specific ref like
|
|
54
|
+
* `"origin/main"` when the upstream configuration isn't guaranteed.
|
|
55
|
+
*/
|
|
56
|
+
export async function getCommitsAhead(ctx, wrt = "@{upstream}", cwd) {
|
|
57
|
+
const out = await tryGit(ctx, ["rev-list", "--count", `${wrt}..HEAD`], cwd);
|
|
58
|
+
if (out === null)
|
|
59
|
+
return null;
|
|
60
|
+
const count = Number.parseInt(out, 10);
|
|
61
|
+
return Number.isFinite(count) ? count : null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `git diff --cached --quiet` at `cwd` (default: `ctx.cwd`). Exit
|
|
65
|
+
* code discrimination:
|
|
66
|
+
*
|
|
67
|
+
* - `0` → no staged changes → returns `false`
|
|
68
|
+
* - `1` → staged changes exist → returns `true`
|
|
69
|
+
* - anything else (spawn error, weird exit) → returns `null`
|
|
70
|
+
*/
|
|
71
|
+
export async function getStagedChanges(ctx, cwd) {
|
|
72
|
+
try {
|
|
73
|
+
const opts = cwd !== undefined ? { cwd } : { cwd: ctx.cwd };
|
|
74
|
+
const res = await ctx.exec("git", ["diff", "--cached", "--quiet"], opts);
|
|
75
|
+
if (res.exitCode === 0)
|
|
76
|
+
return false;
|
|
77
|
+
if (res.exitCode === 1)
|
|
78
|
+
return true;
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `git status --porcelain` at `cwd` (default: `ctx.cwd`). Returns
|
|
87
|
+
* `true` when the working tree is clean (empty output), `false`
|
|
88
|
+
* when dirty, `null` on any command failure.
|
|
89
|
+
*/
|
|
90
|
+
export async function getWorkingTreeClean(ctx, cwd) {
|
|
91
|
+
const out = await tryGit(ctx, ["status", "--porcelain"], cwd);
|
|
92
|
+
if (out === null)
|
|
93
|
+
return null;
|
|
94
|
+
return out.length === 0;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* `git config --get remote.origin.url` at `cwd` (default: `ctx.cwd`).
|
|
98
|
+
* Returns the origin URL string, or `null` when no origin is
|
|
99
|
+
* configured or the command fails.
|
|
100
|
+
*/
|
|
101
|
+
export async function getRemoteUrl(ctx, cwd) {
|
|
102
|
+
const out = await tryGit(ctx, ["config", "--get", "remote.origin.url"], cwd);
|
|
103
|
+
if (out === null || out.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=git-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-ops.js","sourceRoot":"","sources":["../../../src/plugins/git/git-ops.ts"],"names":[],"mappings":"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"}
|