oathbound 0.1.1 → 0.1.2
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 +1 -1
- package/cli.ts +43 -17
- package/package.json +2 -2
package/README.md
CHANGED
package/cli.ts
CHANGED
|
@@ -7,19 +7,20 @@ import { writeFileSync, readFileSync, unlinkSync, existsSync, readdirSync, statS
|
|
|
7
7
|
import { join, relative } from 'node:path';
|
|
8
8
|
import { tmpdir } from 'node:os';
|
|
9
9
|
|
|
10
|
-
const VERSION = '0.1.
|
|
10
|
+
const VERSION = '0.1.2';
|
|
11
11
|
|
|
12
12
|
// --- Supabase ---
|
|
13
13
|
const SUPABASE_URL = 'https://mjnfqagwuewhgwbtrdgs.supabase.co';
|
|
14
14
|
const SUPABASE_ANON_KEY = 'sb_publishable_T-rk0azNRqAMLLGCyadyhQ_ulk9685n';
|
|
15
15
|
|
|
16
|
-
// --- ANSI ---
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
16
|
+
// --- ANSI (respect NO_COLOR standard: https://no-color.org) ---
|
|
17
|
+
const USE_COLOR = process.env.NO_COLOR === undefined && process.stderr.isTTY;
|
|
18
|
+
const TEAL = USE_COLOR ? '\x1b[38;2;63;168;164m' : ''; // brand teal #3fa8a4
|
|
19
|
+
const GREEN = USE_COLOR ? '\x1b[32m' : '';
|
|
20
|
+
const RED = USE_COLOR ? '\x1b[31m' : '';
|
|
21
|
+
const DIM = USE_COLOR ? '\x1b[2m' : '';
|
|
22
|
+
const BOLD = USE_COLOR ? '\x1b[1m' : '';
|
|
23
|
+
const RESET = USE_COLOR ? '\x1b[0m' : '';
|
|
23
24
|
|
|
24
25
|
// --- Types ---
|
|
25
26
|
interface SkillRow {
|
|
@@ -67,7 +68,7 @@ function spinner(text: string): { stop: () => void } {
|
|
|
67
68
|
return {
|
|
68
69
|
stop() {
|
|
69
70
|
clearInterval(interval);
|
|
70
|
-
process.stdout.write('\r\x1b[2K');
|
|
71
|
+
process.stdout.write(USE_COLOR ? '\r\x1b[2K' : '\n');
|
|
71
72
|
},
|
|
72
73
|
};
|
|
73
74
|
}
|
|
@@ -180,8 +181,14 @@ async function readStdin(): Promise<string> {
|
|
|
180
181
|
|
|
181
182
|
// --- Verify (SessionStart hook) ---
|
|
182
183
|
async function verify(): Promise<void> {
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
let input: Record<string, unknown>;
|
|
185
|
+
try {
|
|
186
|
+
input = JSON.parse(await readStdin());
|
|
187
|
+
} catch {
|
|
188
|
+
process.stderr.write('oathbound verify: invalid JSON on stdin\n');
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
const sessionId: string = input.session_id as string;
|
|
185
192
|
if (!sessionId) {
|
|
186
193
|
process.stderr.write('oathbound verify: no session_id in stdin\n');
|
|
187
194
|
process.exit(1);
|
|
@@ -276,9 +283,15 @@ async function verify(): Promise<void> {
|
|
|
276
283
|
|
|
277
284
|
// --- Verify --check (PreToolUse hook) ---
|
|
278
285
|
async function verifyCheck(): Promise<void> {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
286
|
+
let input: Record<string, unknown>;
|
|
287
|
+
try {
|
|
288
|
+
input = JSON.parse(await readStdin());
|
|
289
|
+
} catch {
|
|
290
|
+
process.stderr.write('oathbound verify --check: invalid JSON on stdin\n');
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
const sessionId: string = input.session_id as string;
|
|
294
|
+
const skillName: string | undefined = (input.tool_input as Record<string, unknown> | undefined)?.skill as string | undefined;
|
|
282
295
|
|
|
283
296
|
if (!sessionId || !skillName) {
|
|
284
297
|
// Can't verify — allow through (non-skill invocation or missing context)
|
|
@@ -291,7 +304,13 @@ async function verifyCheck(): Promise<void> {
|
|
|
291
304
|
process.exit(0);
|
|
292
305
|
}
|
|
293
306
|
|
|
294
|
-
|
|
307
|
+
let state: SessionState;
|
|
308
|
+
try {
|
|
309
|
+
state = JSON.parse(readFileSync(stateFile, 'utf-8'));
|
|
310
|
+
} catch {
|
|
311
|
+
process.stderr.write('oathbound verify --check: corrupt session state file\n');
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
295
314
|
|
|
296
315
|
// Extract just the skill name (strip namespace/ prefix if present)
|
|
297
316
|
const baseName = skillName.includes(':') ? skillName.split(':').pop()! : skillName;
|
|
@@ -380,7 +399,7 @@ async function pull(skillArg: string): Promise<void> {
|
|
|
380
399
|
}
|
|
381
400
|
|
|
382
401
|
const buffer = Buffer.from(await blob.arrayBuffer());
|
|
383
|
-
const tarFile =
|
|
402
|
+
const tarFile = join(tmpdir(), `oathbound-${name}-${Date.now()}.tar.gz`);
|
|
384
403
|
|
|
385
404
|
// 3. Hash and verify
|
|
386
405
|
const verify = spinner('Verifying...');
|
|
@@ -395,7 +414,14 @@ async function pull(skillArg: string): Promise<void> {
|
|
|
395
414
|
}
|
|
396
415
|
|
|
397
416
|
// 4. Find target directory and extract
|
|
398
|
-
|
|
417
|
+
let skillsDir = findSkillsDir();
|
|
418
|
+
if (!skillsDir.endsWith('.claude/skills') && !skillsDir.includes('.claude/skills')) {
|
|
419
|
+
// findSkillsDir() fell back to cwd — create .claude/skills instead of extracting into project root
|
|
420
|
+
skillsDir = join(process.cwd(), '.claude', 'skills');
|
|
421
|
+
const { mkdirSync } = await import('node:fs');
|
|
422
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
423
|
+
console.log(`${DIM} Created ${skillsDir}${RESET}`);
|
|
424
|
+
}
|
|
399
425
|
writeFileSync(tarFile, buffer);
|
|
400
426
|
try {
|
|
401
427
|
execFileSync('tar', ['-xf', tarFile, '-C', skillsDir], { stdio: 'pipe' });
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oathbound",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Install verified Claude Code skills from the Oath Bound registry",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Josh Anderson",
|
|
7
7
|
"homepage": "https://oathbound.ai",
|
|
8
8
|
"repository": {
|