Skip to content

Commit 2fb1d34

Browse files
authored
Merge branch 'main' into dev
2 parents 7f72ed0 + 835f1cc commit 2fb1d34

9 files changed

Lines changed: 614 additions & 3 deletions

.githooks/post-checkout

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ -n "${GUARDEX_CLI_ENTRY:-}" ]]; then
5+
node_bin="${GUARDEX_NODE_BIN:-node}"
6+
exec "$node_bin" "$GUARDEX_CLI_ENTRY" 'hook' 'run' 'post-checkout' "$@"
7+
fi
8+
9+
resolve_guardex_cli() {
10+
if [[ -n "${GUARDEX_CLI_BIN:-}" ]]; then
11+
printf '%s' "$GUARDEX_CLI_BIN"
12+
return 0
13+
fi
14+
if command -v gx >/dev/null 2>&1; then
15+
printf '%s' "gx"
16+
return 0
17+
fi
18+
if command -v gitguardex >/dev/null 2>&1; then
19+
printf '%s' "gitguardex"
20+
return 0
21+
fi
22+
echo "[gitguardex-shim] Missing gx CLI in PATH." >&2
23+
exit 1
24+
}
25+
26+
cli_bin="$(resolve_guardex_cli)"
27+
exec "$cli_bin" 'hook' 'run' 'post-checkout' "$@"

.githooks/post-merge

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ -n "${GUARDEX_CLI_ENTRY:-}" ]]; then
5+
node_bin="${GUARDEX_NODE_BIN:-node}"
6+
exec "$node_bin" "$GUARDEX_CLI_ENTRY" 'hook' 'run' 'post-merge' "$@"
7+
fi
8+
9+
resolve_guardex_cli() {
10+
if [[ -n "${GUARDEX_CLI_BIN:-}" ]]; then
11+
printf '%s' "$GUARDEX_CLI_BIN"
12+
return 0
13+
fi
14+
if command -v gx >/dev/null 2>&1; then
15+
printf '%s' "gx"
16+
return 0
17+
fi
18+
if command -v gitguardex >/dev/null 2>&1; then
19+
printf '%s' "gitguardex"
20+
return 0
21+
fi
22+
echo "[gitguardex-shim] Missing gx CLI in PATH." >&2
23+
exit 1
24+
}
25+
26+
cli_bin="$(resolve_guardex_cli)"
27+
exec "$cli_bin" 'hook' 'run' 'post-merge' "$@"

.githooks/pre-commit

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ -n "${GUARDEX_CLI_ENTRY:-}" ]]; then
5+
node_bin="${GUARDEX_NODE_BIN:-node}"
6+
exec "$node_bin" "$GUARDEX_CLI_ENTRY" 'hook' 'run' 'pre-commit' "$@"
7+
fi
8+
9+
resolve_guardex_cli() {
10+
if [[ -n "${GUARDEX_CLI_BIN:-}" ]]; then
11+
printf '%s' "$GUARDEX_CLI_BIN"
12+
return 0
13+
fi
14+
if command -v gx >/dev/null 2>&1; then
15+
printf '%s' "gx"
16+
return 0
17+
fi
18+
if command -v gitguardex >/dev/null 2>&1; then
19+
printf '%s' "gitguardex"
20+
return 0
21+
fi
22+
echo "[gitguardex-shim] Missing gx CLI in PATH." >&2
23+
exit 1
24+
}
25+
26+
cli_bin="$(resolve_guardex_cli)"
27+
exec "$cli_bin" 'hook' 'run' 'pre-commit' "$@"

.githooks/pre-push

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ -n "${GUARDEX_CLI_ENTRY:-}" ]]; then
5+
node_bin="${GUARDEX_NODE_BIN:-node}"
6+
exec "$node_bin" "$GUARDEX_CLI_ENTRY" 'hook' 'run' 'pre-push' "$@"
7+
fi
8+
9+
resolve_guardex_cli() {
10+
if [[ -n "${GUARDEX_CLI_BIN:-}" ]]; then
11+
printf '%s' "$GUARDEX_CLI_BIN"
12+
return 0
13+
fi
14+
if command -v gx >/dev/null 2>&1; then
15+
printf '%s' "gx"
16+
return 0
17+
fi
18+
if command -v gitguardex >/dev/null 2>&1; then
19+
printf '%s' "gitguardex"
20+
return 0
21+
fi
22+
echo "[gitguardex-shim] Missing gx CLI in PATH." >&2
23+
exit 1
24+
}
25+
26+
cli_bin="$(resolve_guardex_cli)"
27+
exec "$cli_bin" 'hook' 'run' 'pre-push' "$@"

docs/qa/release-readiness-0.14.4.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ Candidate branch: `hotrelease/0.14.4-gpt55`
2525
| TypeScript build | `npm run build` | PASS |
2626
| Targeted model/default suites | `node --test dist/agents/__tests__/definitions.test.js dist/agents/__tests__/native-config.test.js dist/team/__tests__/model-contract.test.js dist/utils/__tests__/agents-model-table.test.js dist/cli/__tests__/setup-agents-overwrite.test.js` | PASS |
2727
| Targeted executor launch defaults | `node --test --test-name-pattern=... dist/team/__tests__/runtime.test.js` | PASS |
28-
| Earlier pre-interruption gates | `npm run lint`, `npm run check:no-unused`, `cargo test --workspace` | PASS |
28+
| Scope check | `git diff --name-status v0.14.3..HEAD` plus plugin-path grep | PASS |
2929

3030
## Known limits
3131

32-
- External push, GitHub PR/merge, and tag publication depend on local credentials/network availability.
33-
- CI merge gating still depends on the opened PR reaching green on GitHub.
32+
- CI merge gating still depends on the replacement PR reaching green on GitHub.
33+
- This branch is rebuilt from `v0.14.3` and intentionally excludes unrelated dev/plugin-layout changes.
3434

3535
## Verdict
3636

scripts/agent-session-state.js

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require('node:fs');
4+
const path = require('node:path');
5+
6+
function resolveSessionSchemaModule() {
7+
const candidates = [
8+
path.resolve(__dirname, '..', 'vscode', 'guardex-active-agents', 'session-schema.js'),
9+
path.resolve(__dirname, '..', 'templates', 'vscode', 'guardex-active-agents', 'session-schema.js'),
10+
];
11+
12+
for (const candidate of candidates) {
13+
if (fs.existsSync(candidate)) {
14+
return require(candidate);
15+
}
16+
}
17+
18+
throw new Error('Could not resolve Guardex active-agent session schema module.');
19+
}
20+
21+
const sessionSchema = resolveSessionSchemaModule();
22+
23+
function usage() {
24+
return (
25+
'Usage:\n' +
26+
' node scripts/agent-session-state.js start --repo <path> --branch <name> --task <task> --agent <agent> --worktree <path> --pid <pid> --cli <name> [--task-mode <caveman|omx>] [--openspec-tier <T0|T1|T2|T3>] [--routing-reason <text>] [--state <working|thinking|idle>]\n' +
27+
' node scripts/agent-session-state.js heartbeat --repo <path> --branch <name> [--state <working|thinking|idle>]\n' +
28+
' node scripts/agent-session-state.js terminate --repo <path> --branch <name>\n' +
29+
' node scripts/agent-session-state.js stop --repo <path> --branch <name>\n'
30+
);
31+
}
32+
33+
function parseOptions(argv) {
34+
const options = {};
35+
for (let index = 0; index < argv.length; index += 1) {
36+
const token = argv[index];
37+
if (!token.startsWith('--')) {
38+
throw new Error(`Unexpected argument: ${token}`);
39+
}
40+
const key = token.slice(2);
41+
const value = argv[index + 1];
42+
if (!value || value.startsWith('--')) {
43+
throw new Error(`Missing value for --${key}`);
44+
}
45+
options[key] = value;
46+
index += 1;
47+
}
48+
return options;
49+
}
50+
51+
function requireOption(options, key) {
52+
const value = options[key];
53+
if (!value) {
54+
throw new Error(`Missing required option --${key}`);
55+
}
56+
return value;
57+
}
58+
59+
function writeSessionRecord(options) {
60+
const repoRoot = requireOption(options, 'repo');
61+
const branch = requireOption(options, 'branch');
62+
const record = sessionSchema.buildSessionRecord({
63+
repoRoot,
64+
branch,
65+
taskName: requireOption(options, 'task'),
66+
agentName: requireOption(options, 'agent'),
67+
worktreePath: requireOption(options, 'worktree'),
68+
pid: requireOption(options, 'pid'),
69+
cliName: requireOption(options, 'cli'),
70+
taskMode: options['task-mode'],
71+
openspecTier: options['openspec-tier'],
72+
taskRoutingReason: options['routing-reason'],
73+
state: options.state,
74+
});
75+
76+
const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
77+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
78+
fs.writeFileSync(targetPath, `${JSON.stringify(record, null, 2)}\n`, 'utf8');
79+
}
80+
81+
function refreshSessionRecord(options) {
82+
const repoRoot = requireOption(options, 'repo');
83+
const branch = requireOption(options, 'branch');
84+
const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
85+
if (!fs.existsSync(targetPath)) {
86+
return;
87+
}
88+
89+
const parsed = JSON.parse(fs.readFileSync(targetPath, 'utf8'));
90+
const nextRecord = {
91+
...parsed,
92+
lastHeartbeatAt: new Date().toISOString(),
93+
};
94+
if (options.state) {
95+
nextRecord.state = options.state;
96+
}
97+
98+
fs.writeFileSync(targetPath, `${JSON.stringify(nextRecord, null, 2)}\n`, 'utf8');
99+
}
100+
101+
function readSessionRecord(options) {
102+
const repoRoot = requireOption(options, 'repo');
103+
const branch = requireOption(options, 'branch');
104+
const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
105+
if (!fs.existsSync(targetPath)) {
106+
return null;
107+
}
108+
return JSON.parse(fs.readFileSync(targetPath, 'utf8'));
109+
}
110+
111+
function terminateSessionProcess(options) {
112+
const record = readSessionRecord(options);
113+
const pid = Number(record?.pid);
114+
if (!Number.isInteger(pid) || pid <= 0) {
115+
throw new Error('No live pid recorded for branch.');
116+
}
117+
118+
try {
119+
process.kill(pid, 'SIGTERM');
120+
} catch (error) {
121+
if (error?.code === 'ESRCH') {
122+
return;
123+
}
124+
throw error;
125+
}
126+
}
127+
128+
function removeSessionRecord(options) {
129+
const repoRoot = requireOption(options, 'repo');
130+
const branch = requireOption(options, 'branch');
131+
const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
132+
if (fs.existsSync(targetPath)) {
133+
fs.unlinkSync(targetPath);
134+
}
135+
}
136+
137+
function main() {
138+
const [command, ...rest] = process.argv.slice(2);
139+
if (!command || ['-h', '--help', 'help'].includes(command)) {
140+
process.stdout.write(usage());
141+
return;
142+
}
143+
144+
const options = parseOptions(rest);
145+
if (command === 'start') {
146+
writeSessionRecord(options);
147+
return;
148+
}
149+
if (command === 'heartbeat') {
150+
refreshSessionRecord(options);
151+
return;
152+
}
153+
if (command === 'terminate') {
154+
terminateSessionProcess(options);
155+
return;
156+
}
157+
if (command === 'stop') {
158+
removeSessionRecord(options);
159+
return;
160+
}
161+
162+
throw new Error(`Unknown subcommand: ${command}`);
163+
}
164+
165+
try {
166+
main();
167+
} catch (error) {
168+
process.stderr.write(`[guardex-active-session] ${error.message}\n`);
169+
process.stderr.write(usage());
170+
process.exitCode = 1;
171+
}

0 commit comments

Comments
 (0)