@golden-frijoles/kit 0.5.4 → 0.7.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.
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ // session-note.mjs — append ONE intent-only line to the session journal (README.md D1, D2). This is
3
+ // deliberately the cheap half of session continuity (D1): no gh calls, no git status, no derivation at
4
+ // all — just scripts/lib/log-branch.mjs's appendLineToBranch, reused unchanged, on the dedicated
5
+ // `claude/session-journal` branch (never the working tree, never `main`).
6
+ //
7
+ // Usage:
8
+ // node scripts/session-note.mjs --kind decision "text" [--refs PR#312,D4] [--session label]
9
+ //
10
+ // Kinds: decision | doing | next | blocked | declined. Append-only — no update, no delete, no status
11
+ // field (D2); the history is the point, a mutable journal would invite an agent to overwrite it instead.
12
+ //
13
+ // Fails SOFT (D1): a failed append (network/auth/push race) prints a warning and exits 0 — losing a
14
+ // journal line must never break the work the agent was actually doing. A USAGE error (bad --kind, no
15
+ // text) is different: that's the caller's bug, so it exits 1 loudly instead of silently no-op'ing.
16
+
17
+ import { realpathSync } from 'node:fs';
18
+ import { fileURLToPath } from 'node:url';
19
+ import { dirname, join } from 'node:path';
20
+ import { appendLineToBranch } from './lib/log-branch.mjs';
21
+ import {
22
+ buildJournalEntry,
23
+ serializeEntry,
24
+ VALID_KINDS,
25
+ JOURNAL_BRANCH,
26
+ JOURNAL_PATH,
27
+ } from './lib/session-journal.mjs';
28
+
29
+ const __dirname = dirname(fileURLToPath(import.meta.url));
30
+ const ROOT = join(__dirname, '..');
31
+
32
+ function help() {
33
+ return [
34
+ 'Usage: node scripts/session-note.mjs --kind <kind> "<text>" [--refs a,b] [--session label]',
35
+ `Kinds: ${VALID_KINDS.join(' | ')}`,
36
+ '',
37
+ 'Appends one JSON line to claude/session-journal via scripts/lib/log-branch.mjs. Append-only —',
38
+ 'no update, no delete, no status field. Fails soft: a failed append warns and still exits 0.',
39
+ ].join('\n');
40
+ }
41
+
42
+ // Exported for scripts/session-note.test.mjs — a small hand-rolled parser (no npm dep), same style as
43
+ // scripts/babysit-pr.mjs's parseArgs. The positional (non-flag) argument is the note text.
44
+ export function parseArgs(argv) {
45
+ const out = { kind: null, text: null, refs: null, session: null, help: false };
46
+ for (let i = 0; i < argv.length; i += 1) {
47
+ const a = argv[i];
48
+ if (a === '--kind') out.kind = argv[++i];
49
+ else if (a === '--refs') out.refs = argv[++i];
50
+ else if (a === '--session') out.session = argv[++i];
51
+ else if (a === '--help' || a === '-h') out.help = true;
52
+ else if (!a.startsWith('-') && out.text === null) out.text = a;
53
+ else throw new Error(`unknown argument '${a}'`);
54
+ }
55
+ return out;
56
+ }
57
+
58
+ // `deps` injectable so scripts/session-note.test.mjs can drive this with no real git/network I/O — the
59
+ // same pattern cross-agent-cli.mjs's runWithCodexFallback uses. Returns the process exit code it decided
60
+ // on (never calls process.exit itself), so tests can assert on it directly.
61
+ export async function main(argv = process.argv.slice(2), deps = {}) {
62
+ const { append = appendLineToBranch, log = console.log, warn = console.error, cwd = ROOT, now } = deps;
63
+
64
+ let args;
65
+ try {
66
+ args = parseArgs(argv);
67
+ } catch (e) {
68
+ warn(`✗ ${e.message}\n\n${help()}`);
69
+ return 1;
70
+ }
71
+ if (args.help) {
72
+ log(help());
73
+ return 0;
74
+ }
75
+
76
+ let entry;
77
+ try {
78
+ entry = buildJournalEntry({
79
+ kind: args.kind,
80
+ text: args.text,
81
+ session: args.session,
82
+ refs: args.refs,
83
+ now,
84
+ });
85
+ } catch (e) {
86
+ warn(`✗ ${e.message}\n\n${help()}`);
87
+ return 1;
88
+ }
89
+
90
+ const ok = append({
91
+ cwd,
92
+ branch: JOURNAL_BRANCH,
93
+ path: JOURNAL_PATH,
94
+ line: serializeEntry(entry),
95
+ message: `chore(session-note): ${entry.kind} — ${entry.text.slice(0, 60)}`,
96
+ });
97
+
98
+ if (!ok) {
99
+ // D1: fails soft — this decision was NOT journalled, but that must never be treated as a reason to
100
+ // stop or retry the caller's actual work.
101
+ warn(
102
+ `⚠ session-note: failed to append to ${JOURNAL_BRANCH} — this ${entry.kind} was NOT journalled. ` +
103
+ 'Continuing anyway (session-note fails soft by design).'
104
+ );
105
+ return 0;
106
+ }
107
+
108
+ log(`✓ journalled [${entry.kind}] to ${JOURNAL_BRANCH}: ${entry.text}`);
109
+ return 0;
110
+ }
111
+
112
+ // realpath on both sides: through a symlinked path a plain compare is false and the script exits 0 having
113
+ // done nothing (#189 review).
114
+ const isMain = (() => {
115
+ try {
116
+ return (
117
+ !!process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))
118
+ );
119
+ } catch {
120
+ return false;
121
+ }
122
+ })();
123
+ if (isMain) {
124
+ main().then((code) => {
125
+ process.exitCode = code;
126
+ });
127
+ }