@jakobm/peacekeeper 0.14.0 → 0.15.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
CHANGED
|
@@ -57,7 +57,9 @@ values cause failure without changing the file. Repeating the command on a
|
|
|
57
57
|
complete valid file leaves it unchanged.
|
|
58
58
|
|
|
59
59
|
All CLI commands validate configuration before executing, except help,
|
|
60
|
-
`check-project --explain`, and `update-config`.
|
|
60
|
+
`check-project --explain`, and `update-config`. Checkpoint hooks instead validate
|
|
61
|
+
the event worktree configuration when the event requires instructions or a check.
|
|
62
|
+
An incomplete existing file fails
|
|
61
63
|
with missing field paths and a repair hint. Updates introducing new fields
|
|
62
64
|
therefore require `peacekeeper update-config` before ordinary commands can run.
|
|
63
65
|
|
|
@@ -92,6 +94,8 @@ Complete example:
|
|
|
92
94
|
|
|
93
95
|
`loggingEnabled` has the bundled default `true`; set it to `false` to disable invocation logs and the background cleanup worker's log file. Existing logs are kept. The setting is read from the primary checkout's configuration, including for linked worktrees. SessionStart and Stop logs include the process and event working directories, event type, primary-checkout classification, and hook response or error. Only selected event fields are logged, never the full incoming payload. These logs require the Peacekeeper script to start; a missing Node.js executable or package must be diagnosed in Codex's own logs.
|
|
94
96
|
|
|
97
|
+
Checkpoint hook failures write the cause to stderr and exit with code 2 instead of reporting success. For `Stop`, this requests continued work under the Codex hook protocol. Invocation logging is advisory: a logging failure is reported on stderr, but the checkpoint instructions or check still run and retain their result. Behavior configuration comes from the event's worktree, while log settings come from the primary checkout. A logging warning alone does not change the exit code.
|
|
98
|
+
|
|
95
99
|
To publish checkpoint branches automatically, set `git.checkpointPushRemotes` (fragment):
|
|
96
100
|
|
|
97
101
|
```json
|
|
@@ -240,6 +244,9 @@ does not grant trust automatically.
|
|
|
240
244
|
## Check project conventions locally
|
|
241
245
|
|
|
242
246
|
Run `peacekeeper check-project` to check the local project configuration.
|
|
247
|
+
All projects must declare nonblank `author` and `repository` values in
|
|
248
|
+
`package.json`. npm object forms are supported with nonblank `author.name`
|
|
249
|
+
and `repository.url` strings.
|
|
243
250
|
Run `peacekeeper check-project --explain` for the complete expected
|
|
244
251
|
architecture, requirements, configuration examples, and verification limits.
|
|
245
252
|
The command prints the bundled [formal requirements](scripts/check-project-requirements.txt),
|
package/bin/peacekeeper.js
CHANGED
|
@@ -45,7 +45,8 @@ function main() {
|
|
|
45
45
|
}
|
|
46
46
|
const informational = (args.length === 1 && ['--help', '-h'].includes(args[0]))
|
|
47
47
|
|| (command === 'check-project' && args.length === 1 && args[0] === '--explain');
|
|
48
|
-
|
|
48
|
+
const checkpointHook = ['checkpoint-codex-session-start', 'checkpoint-codex-stop'].includes(command);
|
|
49
|
+
if (command !== 'update-config' && !checkpointHook && !informational) require('../scripts/project-config').projectConfig();
|
|
49
50
|
const script = path.resolve(__dirname, '..', 'scripts', `${command}.js`);
|
|
50
51
|
const result = spawnSync(process.execPath, [script, ...args], {
|
|
51
52
|
cwd: process.cwd(),
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jakobm/peacekeeper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Shared repository test, Git worktree, and Codex hook commands",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"author": "Jakob Mattsson",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/jakobmattsson/peacekeeper.git"
|
|
@@ -39,6 +39,13 @@ Project configuration
|
|
|
39
39
|
}
|
|
40
40
|
projectChecks and both its fields are mandatory in an existing configuration.
|
|
41
41
|
|
|
42
|
+
Package metadata: all projects, including private packages
|
|
43
|
+
package.json must declare author and repository.
|
|
44
|
+
author must be a nonblank string or an object with a nonblank string name.
|
|
45
|
+
repository must be a nonblank string (including npm repository shorthand)
|
|
46
|
+
or an object with a nonblank string url. Whitespace-only values fail.
|
|
47
|
+
These checks validate presence, not repository reachability or author identity.
|
|
48
|
+
|
|
42
49
|
Toolchain: package.json and all Node-based CI jobs
|
|
43
50
|
Pin exact versions in package.json with:
|
|
44
51
|
volta pin node@<exact-version> npm@<exact-version>
|
package/scripts/check-project.js
CHANGED
|
@@ -40,6 +40,14 @@ function checkProject(cwd = process.cwd()) {
|
|
|
40
40
|
try { action(); } catch (error) { errors.push(`[${category}] ${error.message}`); }
|
|
41
41
|
finally { category = previous; }
|
|
42
42
|
};
|
|
43
|
+
check('package', () => {
|
|
44
|
+
const nonblank = value => typeof value === 'string' && value.trim().length > 0;
|
|
45
|
+
requireThat(nonblank(manifest.author) || nonblank(manifest.author?.name),
|
|
46
|
+
'package.json: author must be a nonblank string or an object with a nonblank name.');
|
|
47
|
+
requireThat(nonblank(manifest.repository) || nonblank(manifest.repository?.url),
|
|
48
|
+
'package.json: repository must be a nonblank string or an object with a nonblank url.');
|
|
49
|
+
});
|
|
50
|
+
|
|
43
51
|
const workflows = new Map();
|
|
44
52
|
check('CI', () => {
|
|
45
53
|
const directory = path.join(root, '.github/workflows');
|
|
@@ -227,7 +235,7 @@ function main() {
|
|
|
227
235
|
if (errors.length) {
|
|
228
236
|
for (const error of errors) console.error(`FAIL ${error}`);
|
|
229
237
|
process.exitCode = 1;
|
|
230
|
-
} else console.log(`Project checks passed: tools, hooks, CI${published ? ', publication' : ' (private package; publication skipped)'}.`);
|
|
238
|
+
} else console.log(`Project checks passed: package, tools, hooks, CI${published ? ', publication' : ' (private package; publication skipped)'}.`);
|
|
231
239
|
}
|
|
232
240
|
if (require.main === module) {
|
|
233
241
|
try { main(); } catch (error) { console.error(`FAIL ${error.message}`); process.exitCode = 1; }
|
|
@@ -78,8 +78,18 @@ function runLoggedCheckpointHook(buildResponse, errorPrefix) {
|
|
|
78
78
|
let logPath;
|
|
79
79
|
let event;
|
|
80
80
|
let response;
|
|
81
|
+
function log(action) {
|
|
82
|
+
try {
|
|
83
|
+
action();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
process.stderr.write(`${errorPrefix}: logging failed: ${error.message}\n`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
log(() => { logPath = logScriptInvocation(); });
|
|
89
|
+
function record(value) {
|
|
90
|
+
if (logPath) log(() => fs.appendFileSync(logPath, `${JSON.stringify(value)}\n`));
|
|
91
|
+
}
|
|
81
92
|
try {
|
|
82
|
-
logPath = logScriptInvocation();
|
|
83
93
|
const input = fs.readFileSync(0, 'utf8').trim();
|
|
84
94
|
event = input === '' ? null : JSON.parse(input);
|
|
85
95
|
const details = {
|
|
@@ -88,16 +98,20 @@ function runLoggedCheckpointHook(buildResponse, errorPrefix) {
|
|
|
88
98
|
source: event?.source,
|
|
89
99
|
stopHookActive: event?.stop_hook_active,
|
|
90
100
|
};
|
|
91
|
-
|
|
101
|
+
record({ event: details });
|
|
92
102
|
if (typeof event?.cwd === 'string') {
|
|
93
103
|
const primaryWorktree = isPrimaryWorktree(event.cwd);
|
|
94
|
-
|
|
104
|
+
record({ primaryWorktree });
|
|
95
105
|
}
|
|
96
106
|
response = buildResponse(event);
|
|
97
107
|
} catch (error) {
|
|
98
|
-
|
|
108
|
+
const message = `${errorPrefix}: ${error.message}`;
|
|
109
|
+
process.stderr.write(`${message}\n`);
|
|
110
|
+
process.exitCode = 2;
|
|
111
|
+
record({ error: message });
|
|
112
|
+
return;
|
|
99
113
|
}
|
|
100
|
-
|
|
114
|
+
record({ response });
|
|
101
115
|
process.stdout.write(`${JSON.stringify(response)}\n`);
|
|
102
116
|
}
|
|
103
117
|
|