@gatebolt/cli 0.6.0 → 0.8.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/README.md +5 -4
- package/dist/agents/claude-code.js +28 -39
- package/dist/commands/hook.js +60 -23
- package/dist/commands/init.js +30 -13
- package/dist/git.js +15 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,10 +56,11 @@ gatebolt init
|
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
It **links** the repository (writing a committable `.gatebolt`, exactly like `gatebolt link`),
|
|
59
|
-
**wires** your agent's declare → observe hooks — for Claude Code,
|
|
60
|
-
`.claude/settings.
|
|
61
|
-
that reconciles an open declaration before a push.
|
|
62
|
-
in), and is safe to re-run — it never duplicates
|
|
59
|
+
**wires** your agent's declare → observe hooks — for Claude Code, merged into the committable
|
|
60
|
+
`.claude/settings.json` so enforcement travels to every clone and worktree — and installs a
|
|
61
|
+
**pre-push backstop** at `.git/hooks/pre-push` that reconciles an open declaration before a push.
|
|
62
|
+
It reuses a saved profile (it never signs you in), and is safe to re-run — it never duplicates
|
|
63
|
+
hooks.
|
|
63
64
|
|
|
64
65
|
| Flag | Description |
|
|
65
66
|
| ------------------ | ------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -2,9 +2,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { isRecord } from "../json.js";
|
|
4
4
|
const CLAUDE_DIR = ".claude";
|
|
5
|
-
const SETTINGS_FILE = "settings.
|
|
6
|
-
const GITIGNORE_FILE = ".gitignore";
|
|
7
|
-
const GITIGNORE_ENTRY = `${CLAUDE_DIR}/${SETTINGS_FILE}`;
|
|
5
|
+
const SETTINGS_FILE = "settings.json";
|
|
8
6
|
const PRE_TOOL_USE = "PreToolUse";
|
|
9
7
|
const STOP = "Stop";
|
|
10
8
|
const EDIT_TOOLS_MATCHER = "Edit|Write|NotebookEdit";
|
|
@@ -13,18 +11,39 @@ const GUARD_SUB = "guard";
|
|
|
13
11
|
const POST_TURN_SUB = "post-turn";
|
|
14
12
|
const ENFORCE_STRICT = "strict";
|
|
15
13
|
const ENFORCE_LENIENT = "lenient";
|
|
14
|
+
const DENY_DECISION = "deny";
|
|
15
|
+
export const MISSING_CLI_MESSAGE = "GateBolt: CLI not found on PATH - install it: npm i -g @gatebolt/cli";
|
|
16
|
+
function missingCliDenyPayload() {
|
|
17
|
+
return JSON.stringify({
|
|
18
|
+
hookSpecificOutput: {
|
|
19
|
+
hookEventName: PRE_TOOL_USE,
|
|
20
|
+
permissionDecision: DENY_DECISION,
|
|
21
|
+
permissionDecisionReason: MISSING_CLI_MESSAGE,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function guardCommand(cli) {
|
|
26
|
+
return (`command -v ${cli} >/dev/null 2>&1 && exec ${cli} hook ${GUARD_SUB} || ` +
|
|
27
|
+
`{ [ "$GATEBOLT_ENFORCE" = ${ENFORCE_STRICT} ] && ` +
|
|
28
|
+
`{ echo "${MISSING_CLI_MESSAGE}" >&2; ` +
|
|
29
|
+
`printf '%s\\n' '${missingCliDenyPayload()}'; }; exit 0; }`);
|
|
30
|
+
}
|
|
31
|
+
function postTurnCommand(cli) {
|
|
32
|
+
return (`command -v ${cli} >/dev/null 2>&1 && exec ${cli} hook ${POST_TURN_SUB} || ` +
|
|
33
|
+
`echo "${MISSING_CLI_MESSAGE}" >&2`);
|
|
34
|
+
}
|
|
16
35
|
export function mergeSettings(existing, opts) {
|
|
17
36
|
const settings = structuredClone(existing);
|
|
18
37
|
const hooks = plainObject(settings.hooks, "hooks");
|
|
19
38
|
settings.hooks = hooks;
|
|
20
39
|
let changed = false;
|
|
21
40
|
const preToolUse = arrayField(hooks[PRE_TOOL_USE], `hooks.${PRE_TOOL_USE}`);
|
|
22
|
-
if (ensureHookCommand(preToolUse, `hook ${GUARD_SUB}`, opts.command, EDIT_TOOLS_MATCHER)) {
|
|
41
|
+
if (ensureHookCommand(preToolUse, `hook ${GUARD_SUB}`, guardCommand(opts.command), EDIT_TOOLS_MATCHER)) {
|
|
23
42
|
changed = true;
|
|
24
43
|
}
|
|
25
44
|
hooks[PRE_TOOL_USE] = preToolUse;
|
|
26
45
|
const stop = arrayField(hooks[STOP], `hooks.${STOP}`);
|
|
27
|
-
if (ensureHookCommand(stop, `hook ${POST_TURN_SUB}`, opts.command)) {
|
|
46
|
+
if (ensureHookCommand(stop, `hook ${POST_TURN_SUB}`, postTurnCommand(opts.command))) {
|
|
28
47
|
changed = true;
|
|
29
48
|
}
|
|
30
49
|
hooks[STOP] = stop;
|
|
@@ -59,13 +78,6 @@ export function mergeSettings(existing, opts) {
|
|
|
59
78
|
}
|
|
60
79
|
return { settings, changed };
|
|
61
80
|
}
|
|
62
|
-
export function withGitignoreEntry(existing, entry) {
|
|
63
|
-
const present = existing.split("\n").some((line) => line.trim() === entry);
|
|
64
|
-
if (present)
|
|
65
|
-
return null;
|
|
66
|
-
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
67
|
-
return `${existing}${prefix}${entry}\n`;
|
|
68
|
-
}
|
|
69
81
|
function plainObject(value, label) {
|
|
70
82
|
if (value === undefined)
|
|
71
83
|
return {};
|
|
@@ -80,11 +92,8 @@ function arrayField(value, label) {
|
|
|
80
92
|
return value;
|
|
81
93
|
throw new Error(`${SETTINGS_FILE}: "${label}" must be a JSON array.`);
|
|
82
94
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
function ensureHookCommand(entries, suffix, command, matcher) {
|
|
86
|
-
const desired = `${command} ${suffix}`;
|
|
87
|
-
const existing = findGateboltHook(entries, suffix);
|
|
95
|
+
function ensureHookCommand(entries, marker, desired, matcher) {
|
|
96
|
+
const existing = findGateboltHook(entries, marker);
|
|
88
97
|
if (existing) {
|
|
89
98
|
if (existing.command === desired)
|
|
90
99
|
return false;
|
|
@@ -95,14 +104,14 @@ function ensureHookCommand(entries, suffix, command, matcher) {
|
|
|
95
104
|
entries.push(matcher === undefined ? { hooks: [hook] } : { matcher, hooks: [hook] });
|
|
96
105
|
return true;
|
|
97
106
|
}
|
|
98
|
-
function findGateboltHook(entries,
|
|
107
|
+
function findGateboltHook(entries, marker) {
|
|
99
108
|
for (const entry of entries) {
|
|
100
109
|
if (!isRecord(entry) || !Array.isArray(entry.hooks))
|
|
101
110
|
continue;
|
|
102
111
|
for (const hook of entry.hooks) {
|
|
103
112
|
if (isRecord(hook) &&
|
|
104
113
|
typeof hook.command === "string" &&
|
|
105
|
-
hook.command.
|
|
114
|
+
hook.command.includes(marker)) {
|
|
106
115
|
return hook;
|
|
107
116
|
}
|
|
108
117
|
}
|
|
@@ -116,11 +125,9 @@ async function wireClaudeCode(root, opts) {
|
|
|
116
125
|
await mkdir(join(root, CLAUDE_DIR), { recursive: true });
|
|
117
126
|
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
118
127
|
}
|
|
119
|
-
const gitignoreChanged = await ensureGitignore(root);
|
|
120
128
|
return {
|
|
121
129
|
settingsPath,
|
|
122
130
|
settingsChanged: changed,
|
|
123
|
-
gitignoreChanged,
|
|
124
131
|
enforce: effectiveEnforce(settings),
|
|
125
132
|
};
|
|
126
133
|
}
|
|
@@ -147,24 +154,6 @@ async function readSettings(path) {
|
|
|
147
154
|
}
|
|
148
155
|
return data;
|
|
149
156
|
}
|
|
150
|
-
async function ensureGitignore(root) {
|
|
151
|
-
const path = join(root, GITIGNORE_FILE);
|
|
152
|
-
const updated = withGitignoreEntry(await readTextOrEmpty(path), GITIGNORE_ENTRY);
|
|
153
|
-
if (updated === null)
|
|
154
|
-
return false;
|
|
155
|
-
await writeFile(path, updated, "utf8");
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
async function readTextOrEmpty(path) {
|
|
159
|
-
try {
|
|
160
|
-
return await readFile(path, "utf8");
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
if (isNotFound(error))
|
|
164
|
-
return "";
|
|
165
|
-
throw error;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
157
|
function safeParse(raw) {
|
|
169
158
|
try {
|
|
170
159
|
return JSON.parse(raw);
|
package/dist/commands/hook.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
2
3
|
import { formatError } from "../errors.js";
|
|
3
4
|
import { hasCommits, repoContext } from "../git.js";
|
|
5
|
+
import { isRecord } from "../json.js";
|
|
4
6
|
import { readSession } from "../session.js";
|
|
5
7
|
import { RequestError } from "../transport.js";
|
|
6
8
|
import { runObserve } from "./observe.js";
|
|
@@ -21,33 +23,68 @@ task — specific repo-relative paths, not broad globs like src/** :
|
|
|
21
23
|
The GateBolt CLI is already configured for this repository. Once it
|
|
22
24
|
prints "Declared change …", retry the edit.`;
|
|
23
25
|
}
|
|
24
|
-
export async function runHook(argv) {
|
|
25
|
-
anchorToProjectDir();
|
|
26
|
-
drainStdin();
|
|
26
|
+
export async function runHook(argv, input = {}) {
|
|
27
27
|
const sub = argv[0] ?? "";
|
|
28
28
|
switch (sub) {
|
|
29
29
|
case "guard":
|
|
30
|
-
return runGuard();
|
|
30
|
+
return runGuard(input);
|
|
31
31
|
case "post-turn":
|
|
32
|
-
return runPostTurn();
|
|
32
|
+
return runPostTurn(input);
|
|
33
33
|
case "pre-push":
|
|
34
|
-
return runPrePush();
|
|
34
|
+
return runPrePush(input);
|
|
35
35
|
default:
|
|
36
36
|
throw new Error(`Unknown hook '${sub}'. Expected one of: guard, post-turn, pre-push.`);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (dir && existsSync(dir))
|
|
39
|
+
async function resolveRepo(input) {
|
|
40
|
+
const candidates = [
|
|
41
|
+
input.filePath ? dirname(input.filePath) : undefined,
|
|
42
|
+
input.cwd,
|
|
43
|
+
process.env.CLAUDE_PROJECT_DIR,
|
|
44
|
+
process.cwd(),
|
|
45
|
+
];
|
|
46
|
+
for (const dir of candidates) {
|
|
47
|
+
if (!dir || !existsSync(dir))
|
|
48
|
+
continue;
|
|
50
49
|
process.chdir(dir);
|
|
50
|
+
const repo = await hookRepoContext();
|
|
51
|
+
if (repo)
|
|
52
|
+
return repo;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
export async function readHookPayload() {
|
|
57
|
+
const data = safeJsonParse(await readStdin());
|
|
58
|
+
if (!isRecord(data))
|
|
59
|
+
return {};
|
|
60
|
+
const toolInput = isRecord(data.tool_input) ? data.tool_input : undefined;
|
|
61
|
+
return {
|
|
62
|
+
cwd: typeof data.cwd === "string" ? data.cwd : undefined,
|
|
63
|
+
filePath: toolInput && typeof toolInput.file_path === "string"
|
|
64
|
+
? toolInput.file_path
|
|
65
|
+
: undefined,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function safeJsonParse(raw) {
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(raw);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function readStdin() {
|
|
77
|
+
if (process.stdin.isTTY)
|
|
78
|
+
return Promise.resolve("");
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
const chunks = [];
|
|
81
|
+
const done = () => resolve(Buffer.concat(chunks).toString("utf8"));
|
|
82
|
+
process.stdin.on("data", (chunk) => {
|
|
83
|
+
chunks.push(chunk);
|
|
84
|
+
});
|
|
85
|
+
process.stdin.on("end", done);
|
|
86
|
+
process.stdin.on("error", done);
|
|
87
|
+
});
|
|
51
88
|
}
|
|
52
89
|
async function hookRepoContext() {
|
|
53
90
|
try {
|
|
@@ -57,10 +94,10 @@ async function hookRepoContext() {
|
|
|
57
94
|
return null;
|
|
58
95
|
}
|
|
59
96
|
}
|
|
60
|
-
async function runGuard() {
|
|
97
|
+
async function runGuard(input) {
|
|
61
98
|
if (process.env.GATEBOLT_ENFORCE !== ENFORCE_STRICT)
|
|
62
99
|
return;
|
|
63
|
-
const repo = await
|
|
100
|
+
const repo = await resolveRepo(input);
|
|
64
101
|
if (!repo)
|
|
65
102
|
return;
|
|
66
103
|
if (await readSession(repo.gitDir))
|
|
@@ -71,8 +108,8 @@ async function runGuard() {
|
|
|
71
108
|
const cli = process.env.GATEBOLT_CLI ?? DEFAULT_CLI;
|
|
72
109
|
process.stdout.write(denyPayload(declareInstructions(cli)));
|
|
73
110
|
}
|
|
74
|
-
async function runPostTurn() {
|
|
75
|
-
const repo = await
|
|
111
|
+
async function runPostTurn(input) {
|
|
112
|
+
const repo = await resolveRepo(input);
|
|
76
113
|
if (!repo)
|
|
77
114
|
return;
|
|
78
115
|
if (!(await readSession(repo.gitDir)))
|
|
@@ -86,8 +123,8 @@ async function runPostTurn() {
|
|
|
86
123
|
}
|
|
87
124
|
// Backstop for the interrupt case the Stop hook misses: an open, un-observed
|
|
88
125
|
// declaration.
|
|
89
|
-
async function runPrePush() {
|
|
90
|
-
const repo = await
|
|
126
|
+
async function runPrePush(input) {
|
|
127
|
+
const repo = await resolveRepo(input);
|
|
91
128
|
if (!repo)
|
|
92
129
|
return;
|
|
93
130
|
if (!(await readSession(repo.gitDir)))
|
package/dist/commands/init.js
CHANGED
|
@@ -3,9 +3,9 @@ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { delimiter, dirname, isAbsolute, join, relative } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
|
-
import { claudeCodeAdapter } from "../agents/claude-code.js";
|
|
6
|
+
import { claudeCodeAdapter, MISSING_CLI_MESSAGE, } from "../agents/claude-code.js";
|
|
7
7
|
import { repoIdentityFromName, resolveConnection } from "../config.js";
|
|
8
|
-
import { hooksPath, repoContext } from "../git.js";
|
|
8
|
+
import { hooksPath, isPathIgnored, isUncommitted, repoContext, } from "../git.js";
|
|
9
9
|
import { isRecord } from "../json.js";
|
|
10
10
|
import { pinRepoProfile, profileLoader, readRepoLink } from "../profiles.js";
|
|
11
11
|
import { deriveIdentity, linkRepository } from "./link.js";
|
|
@@ -15,10 +15,10 @@ const PRE_PUSH_FILE = "pre-push";
|
|
|
15
15
|
const PRE_PUSH_MARKER = "GateBolt pre-push backstop";
|
|
16
16
|
const HOOK_MODE = 0o755;
|
|
17
17
|
const ID_PREFIX_LEN = 8;
|
|
18
|
-
const GITIGNORE_ENTRY = ".claude/settings.local.json";
|
|
19
18
|
const NO_REMOTE = "GateBolt cannot derive this repository's identity from its `origin` remote. Re-run with --name <a-name-for-this-repo>.";
|
|
20
19
|
const NO_KEY = "No GateBolt key found. Run `gatebolt login` (or `gatebolt configure`) first, then re-run `gatebolt init`.";
|
|
21
|
-
const EPHEMERAL_CLI = "GateBolt is running from a temporary npx/dlx cache, so the hooks would break when the package manager prunes it. Install it durably
|
|
20
|
+
const EPHEMERAL_CLI = "GateBolt is running from a temporary npx/dlx cache, so the hooks would break when the package manager prunes it. Install it durably so `gatebolt` is on PATH on every machine — `npm i -g @gatebolt/cli` — then re-run `gatebolt init`. To link only, pass --no-hooks --no-pre-push.";
|
|
21
|
+
const NOT_ON_PATH = "GateBolt isn't on your PATH, so a committed hook command would only name a path that exists on this machine. Install it durably so `gatebolt` is on PATH on every machine — `npm i -g @gatebolt/cli` — then re-run `gatebolt init`. To link only, pass --no-hooks --no-pre-push.";
|
|
22
22
|
export async function runInit(argv) {
|
|
23
23
|
const { values } = parseArgs({
|
|
24
24
|
args: argv,
|
|
@@ -47,6 +47,7 @@ export async function runInit(argv) {
|
|
|
47
47
|
const prePush = values["no-pre-push"]
|
|
48
48
|
? null
|
|
49
49
|
: await installPrePush(command, Boolean(values.force));
|
|
50
|
+
const settingsState = await settingsGitState(wired[0]?.result.settingsPath);
|
|
50
51
|
process.stdout.write(summary({
|
|
51
52
|
root: repo.root,
|
|
52
53
|
link,
|
|
@@ -54,6 +55,7 @@ export async function runInit(argv) {
|
|
|
54
55
|
prePush,
|
|
55
56
|
enforce: wired[0]?.result.enforce ?? enforce ?? "lenient",
|
|
56
57
|
noHooks: Boolean(values["no-hooks"]),
|
|
58
|
+
...settingsState,
|
|
57
59
|
}));
|
|
58
60
|
}
|
|
59
61
|
export function parseEnforce(value) {
|
|
@@ -94,14 +96,25 @@ async function ensureLink(connection, root, name) {
|
|
|
94
96
|
created: result.created,
|
|
95
97
|
};
|
|
96
98
|
}
|
|
99
|
+
async function settingsGitState(settingsPath) {
|
|
100
|
+
if (!settingsPath) {
|
|
101
|
+
return { settingsIgnored: false, settingsUncommitted: false };
|
|
102
|
+
}
|
|
103
|
+
const settingsIgnored = await isPathIgnored(settingsPath);
|
|
104
|
+
const settingsUncommitted = settingsIgnored
|
|
105
|
+
? false
|
|
106
|
+
: await isUncommitted(settingsPath);
|
|
107
|
+
return { settingsIgnored, settingsUncommitted };
|
|
108
|
+
}
|
|
97
109
|
function resolveCommand(root) {
|
|
98
110
|
const cli = fileURLToPath(new URL("../index.js", import.meta.url));
|
|
99
111
|
if (isEphemeralPath(cli)) {
|
|
100
112
|
throw new Error(EPHEMERAL_CLI);
|
|
101
113
|
}
|
|
102
|
-
if (onDurablePath("gatebolt", root))
|
|
103
|
-
|
|
104
|
-
|
|
114
|
+
if (!onDurablePath("gatebolt", root)) {
|
|
115
|
+
throw new Error(NOT_ON_PATH);
|
|
116
|
+
}
|
|
117
|
+
return "gatebolt";
|
|
105
118
|
}
|
|
106
119
|
// npx/dlx run from throwaway caches that get pruned, breaking any hook wired
|
|
107
120
|
// here: npm marks them with an `_npx` segment, pnpm with `dlx`, yarn with `xfs-…`.
|
|
@@ -122,9 +135,6 @@ function isInsideRoot(dir, root) {
|
|
|
122
135
|
const rel = relative(root, dir);
|
|
123
136
|
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
124
137
|
}
|
|
125
|
-
function quote(value) {
|
|
126
|
-
return /\s/.test(value) ? `"${value}"` : value;
|
|
127
|
-
}
|
|
128
138
|
async function wireAgents(root, command, enforce) {
|
|
129
139
|
const wired = [];
|
|
130
140
|
for (const adapter of ADAPTERS) {
|
|
@@ -153,6 +163,10 @@ export function prePushScript(command) {
|
|
|
153
163
|
return [
|
|
154
164
|
"#!/usr/bin/env sh",
|
|
155
165
|
`# ${PRE_PUSH_MARKER} — reconciles an open declaration before a push.`,
|
|
166
|
+
`if ! command -v ${command} >/dev/null 2>&1; then`,
|
|
167
|
+
` echo "${MISSING_CLI_MESSAGE}" >&2`,
|
|
168
|
+
" exit 0",
|
|
169
|
+
"fi",
|
|
156
170
|
`exec ${command} hook ${PRE_PUSH_FILE}`,
|
|
157
171
|
"",
|
|
158
172
|
].join("\n");
|
|
@@ -175,6 +189,12 @@ function summary(parts) {
|
|
|
175
189
|
"",
|
|
176
190
|
enforceLine(parts.enforce),
|
|
177
191
|
];
|
|
192
|
+
if (!parts.noHooks && parts.settingsIgnored) {
|
|
193
|
+
lines.push("Warning: .claude/settings.json is gitignored, so committing it won't work — enforcement won't travel to other clones or worktrees until you remove the ignore rule and commit it.");
|
|
194
|
+
}
|
|
195
|
+
else if (!parts.noHooks && parts.settingsUncommitted) {
|
|
196
|
+
lines.push("Commit .claude/settings.json so every worktree, clone, and machine enforces this policy.");
|
|
197
|
+
}
|
|
178
198
|
if (!parts.link.existed) {
|
|
179
199
|
lines.push("Commit .gatebolt so every clone and CI job resolves the same repository.");
|
|
180
200
|
}
|
|
@@ -194,9 +214,6 @@ function hookLines(root, wired, noHooks) {
|
|
|
194
214
|
for (const { displayName, result } of wired) {
|
|
195
215
|
const verb = result.settingsChanged ? "wired" : "already wired";
|
|
196
216
|
lines.push(`${displayName}: ${verb} → ${relative(root, result.settingsPath)}`);
|
|
197
|
-
if (result.gitignoreChanged) {
|
|
198
|
-
lines.push(` ignored ${GITIGNORE_ENTRY} in .gitignore`);
|
|
199
|
-
}
|
|
200
217
|
}
|
|
201
218
|
return lines;
|
|
202
219
|
}
|
package/dist/git.js
CHANGED
|
@@ -34,6 +34,21 @@ export async function hooksPath() {
|
|
|
34
34
|
const output = await git(["rev-parse", "--git-path", "hooks"]);
|
|
35
35
|
return resolve(output.trim());
|
|
36
36
|
}
|
|
37
|
+
export async function isPathIgnored(path) {
|
|
38
|
+
try {
|
|
39
|
+
await git(["check-ignore", "--quiet", path]);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (gitExitedNonZero(error))
|
|
44
|
+
return false;
|
|
45
|
+
throw gitFailure(error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function isUncommitted(path) {
|
|
49
|
+
const output = await git(["status", "--porcelain", "--", path]);
|
|
50
|
+
return output.trim() !== "";
|
|
51
|
+
}
|
|
37
52
|
export async function remoteOriginUrl() {
|
|
38
53
|
try {
|
|
39
54
|
const output = await git(["remote", "get-url", "origin"]);
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { runConfigure } from "./commands/configure.js";
|
|
3
3
|
import { runDeclare } from "./commands/declare.js";
|
|
4
|
-
import { runHook } from "./commands/hook.js";
|
|
4
|
+
import { readHookPayload, runHook } from "./commands/hook.js";
|
|
5
5
|
import { runInit } from "./commands/init.js";
|
|
6
6
|
import { runLink } from "./commands/link.js";
|
|
7
7
|
import { runLogin } from "./commands/login.js";
|
|
@@ -43,7 +43,7 @@ async function dispatch(command, argv) {
|
|
|
43
43
|
case "init":
|
|
44
44
|
return runInit(argv);
|
|
45
45
|
case "hook":
|
|
46
|
-
return runHook(argv);
|
|
46
|
+
return runHook(argv, await readHookPayload());
|
|
47
47
|
default:
|
|
48
48
|
process.stderr.write(`${USAGE}\nRun \`gatebolt --help\` for details.\n`);
|
|
49
49
|
process.exitCode = 1;
|
package/package.json
CHANGED