sawbuck 0.1.37 → 0.1.38
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/contract-cmd.mjs +2 -0
- package/exclude.mjs +24 -0
- package/package.json +2 -1
- package/sawbuck-hook.mjs +1 -17
- package/sawbuck.mjs +2 -0
package/contract-cmd.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// here, adds your tests and tripwires if there is none, then the supply-chain protections and the architecture
|
|
4
4
|
// lock. Zero-LLM. Re-running changes nothing. Protected product code (auth, billing) is suggested, never added:
|
|
5
5
|
// freezing those is a person's decision.
|
|
6
|
+
import { ensureExcluded } from './exclude.mjs';
|
|
6
7
|
import { execFileSync } from 'node:child_process';
|
|
7
8
|
import { readFileSync, writeFileSync, existsSync, mkdtempSync, rmSync, cpSync, mkdirSync } from 'node:fs';
|
|
8
9
|
import { join, dirname, resolve } from 'node:path';
|
|
@@ -48,6 +49,7 @@ try {
|
|
|
48
49
|
const next = text.replace(/^\.promptwheel\/?[ \t]*$/gm, '.promptwheel/*\n!.promptwheel/packs/');
|
|
49
50
|
if (next !== text) writeFileSync(f, next);
|
|
50
51
|
}
|
|
52
|
+
ensureExcluded(repo);
|
|
51
53
|
const locked = existsSync(join(repo, 'promptwheel.lock.json'));
|
|
52
54
|
if (!locked) run([join(pack, 'lock.mjs'), 'write', repo], repo);
|
|
53
55
|
const added = (cfg.mandates.length - before.m) + (cfg.metrics.length - before.g);
|
package/exclude.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Keep sawbuck's own record out of git status, before anything is written: signed files are the person's to
|
|
2
|
+
// commit, the record is not, and an untracked .sawbuck/ would trip the rule that protects it.
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
5
|
+
import { resolve, dirname } from 'node:path';
|
|
6
|
+
const sh = (r, ...a) => { try { return execFileSync('git', ['-C', r, ...a], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return ''; } };
|
|
7
|
+
|
|
8
|
+
export function ensureExcluded(r) {
|
|
9
|
+
try {
|
|
10
|
+
// the repository's own exclude file, wherever its git dir is (a worktree or submodule keeps it elsewhere)
|
|
11
|
+
const rel = sh(r, 'rev-parse', '--git-path', 'info/exclude');
|
|
12
|
+
if (!rel) return;
|
|
13
|
+
const f = resolve(r, rel);
|
|
14
|
+
// the record dirs + the logbook's root ledger cache (events.jsonl). Rewrite the block to
|
|
15
|
+
// the CURRENT set rather than bail on presence, so a grown set retrofits existing repos.
|
|
16
|
+
const WANT = '# >>> sawbuck >>>\n.sawbuck/\n.promptwheel/*\n!.promptwheel/packs/\nevents.jsonl\nannotations.jsonl\nannotation-reviews.jsonl\ndecision-cards.jsonl\nLOGBOOK.md\nJOURNEY.md\n.claude/settings.local.json\n# <<< sawbuck <<<\n';
|
|
17
|
+
const body = existsSync(f) ? readFileSync(f, 'utf8') : '';
|
|
18
|
+
const stripped = body.replace(/# >>> sawbuck >>>[\s\S]*?# <<< sawbuck <<<\n?/g, '');
|
|
19
|
+
const next = (stripped && !stripped.endsWith('\n') ? stripped + '\n' : stripped) + WANT;
|
|
20
|
+
if (next === body) return;
|
|
21
|
+
mkdirSync(dirname(f), { recursive: true });
|
|
22
|
+
writeFileSync(f, next);
|
|
23
|
+
} catch {}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sawbuck",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"description": "Sign your agent to a contract: a deterministic, zero-LLM check re-proves each win from the agent's source edits alone, so a test that went green by editing the test, grader, or config is sent back. For Claude Code, with Codex support in beta.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"files": [
|
|
40
40
|
"sawbuck.mjs",
|
|
41
41
|
"rewrites.mjs",
|
|
42
|
+
"exclude.mjs",
|
|
42
43
|
"contract-cmd.mjs",
|
|
43
44
|
"bins.mjs",
|
|
44
45
|
"contract.mjs",
|
package/sawbuck-hook.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { ensureExcluded } from './exclude.mjs';
|
|
2
3
|
// sawbuck-hook.mjs: the Sawbuck Claude Code Stop-hook entry.
|
|
3
4
|
// Each turn: if a new commit landed, run reflect on it; then render the dispatch
|
|
4
5
|
// on a fresh catch OR every N turns (config). State in .sawbuck/state.json.
|
|
@@ -202,20 +203,3 @@ saveState();
|
|
|
202
203
|
// dirties the worktree, including under `attach --user`, which is global and cannot
|
|
203
204
|
// set this up per-repo at attach time. Idempotent; markers match the CLI's so a
|
|
204
205
|
// per-repo `detach` removes them.
|
|
205
|
-
function ensureExcluded(r) {
|
|
206
|
-
try {
|
|
207
|
-
// the repository's own exclude file, wherever its git dir is (a worktree or submodule keeps it elsewhere)
|
|
208
|
-
const rel = sh(r, 'rev-parse', '--git-path', 'info/exclude');
|
|
209
|
-
if (!rel) return;
|
|
210
|
-
const f = resolve(r, rel);
|
|
211
|
-
// the record dirs + the logbook's root ledger cache (events.jsonl). Rewrite the block to
|
|
212
|
-
// the CURRENT set rather than bail on presence, so a grown set retrofits existing repos.
|
|
213
|
-
const WANT = '# >>> sawbuck >>>\n.sawbuck/\n.promptwheel/*\n!.promptwheel/packs/\nevents.jsonl\nannotations.jsonl\nannotation-reviews.jsonl\ndecision-cards.jsonl\nLOGBOOK.md\nJOURNEY.md\n.claude/settings.local.json\n# <<< sawbuck <<<\n';
|
|
214
|
-
const body = existsSync(f) ? readFileSync(f, 'utf8') : '';
|
|
215
|
-
const stripped = body.replace(/# >>> sawbuck >>>[\s\S]*?# <<< sawbuck <<<\n?/g, '');
|
|
216
|
-
const next = (stripped && !stripped.endsWith('\n') ? stripped + '\n' : stripped) + WANT;
|
|
217
|
-
if (next === body) return;
|
|
218
|
-
mkdirSync(dirname(f), { recursive: true });
|
|
219
|
-
writeFileSync(f, next);
|
|
220
|
-
} catch {}
|
|
221
|
-
}
|
package/sawbuck.mjs
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { execFileSync } from 'node:child_process';
|
|
16
16
|
import { erasedBlock } from './rewrites.mjs';
|
|
17
|
+
import { ensureExcluded } from './exclude.mjs';
|
|
17
18
|
import { readFileSync, writeFileSync, existsSync, chmodSync, mkdirSync, unlinkSync, rmdirSync, rmSync, readdirSync } from 'node:fs';
|
|
18
19
|
import { basename, join, dirname, resolve } from 'node:path';
|
|
19
20
|
import { fileURLToPath } from 'node:url';
|
|
@@ -244,6 +245,7 @@ switch (cmd) {
|
|
|
244
245
|
if (flags.has('--hook') && !process.stdin.isTTY) { try { hookEvent = String(JSON.parse(readFileSync(0, 'utf8') || '{}').hook_event_name || ''); } catch {} }
|
|
245
246
|
if (hookEvent === 'UserPromptSubmit' && top) {
|
|
246
247
|
try {
|
|
248
|
+
ensureExcluded(repo); // before the first write: under user-level hooks nothing else has done it yet
|
|
247
249
|
const stDir = join(repo, '.sawbuck'); const stPath = join(stDir, 'state.json');
|
|
248
250
|
let st = {}; try { st = JSON.parse(readFileSync(stPath, 'utf8')); } catch {}
|
|
249
251
|
const now = new Date().toISOString();
|