feat(cli): extract Obelisk runtime into npm package

Add @obelisk-apps/cli with the existing build, search, query, and attune contract plus official skill installation.

Separate the docs-only skill artifact, bootstrap installer, release layout, cross-platform CI, and package-level regression coverage.
This commit is contained in:
tommy0103
2026-07-16 17:26:23 +08:00
parent 49158e9b9c
commit 90191e4604
64 changed files with 1112 additions and 745 deletions
+3 -3
View File
@@ -1,11 +1,11 @@
// Obelisk Core package (see docs/adr/0003-core-typescript-esm-precompiled.md).
//
// The single shared implementation behind every transport. runtime.js (skill),
// and later the CLI and MCP server, are thin shells over these four functions;
// The single shared implementation behind every transport. The CLI and later
// the MCP server are thin shells over these four functions;
// none of them re-implement retrieval or own the DB lifecycle.
//
// Authored in TypeScript with erasable-only syntax so Node can run it directly
// via type stripping in development, while the skill artifact ships readable,
// via type stripping in development, while the CLI package ships readable,
// non-bundled tsc output. Core source lives in the @obelisk/core workspace.
import { createContext, runInNewContext } from 'node:vm';
+1 -1
View File
@@ -2,7 +2,7 @@
//
// Provider-agnostic and binding-agnostic: it consumes the IndexRecord stream
// from any adapter's parse() and writes rows into the injected database handle
// (node:sqlite for the skill/CLI, better-sqlite3 for the app — they share the
// (node:sqlite for the CLI, better-sqlite3 for the app — they share the
// prepare/run/get API). It is the ONLY layer that touches the database and the
// only place that knows the schema. Adapters stay pure.
//
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env node
// Skill transport: a typed thin CLI shell over the Obelisk Core package.
// It only parses args, reads script files, prints JSON, and owns the uniform
// { error, stack } + exit-1 error envelope. All logic lives in Core.
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const fs = require('node:fs');
const path = require('node:path');
import { DB_PATH, buildIndex, searchText, executeQuery, executeAttune } from './core.ts';
async function main() {
const args = process.argv.slice(2);
// Uniform error envelope across all four verbs: a failure is reported as
// { error, stack } on stdout with exit code 1, never a raw crash on stderr.
const fail = (e: unknown): void => {
const error = e instanceof Error ? e : new Error(String(e));
process.stdout.write(JSON.stringify({ error: error.message, stack: error.stack }) + '\n');
process.exitCode = 1;
};
const emit = (r: unknown): void => {
process.stdout.write(JSON.stringify(r, null, 2) + '\n');
};
if (args[0] === '--build') {
try {
buildIndex({ force: true });
process.stdout.write(JSON.stringify({ ok: true, db: DB_PATH }) + '\n');
} catch (e) { fail(e); }
return;
}
if (args[0] === '--search' && args[1]) {
try { emit(searchText(args.slice(1).join(' '))); } catch (e) { fail(e); }
return;
}
if (args[0] === '--query' && args[1]) {
try { emit(await executeQuery(fs.readFileSync(path.resolve(args[1]), 'utf8'))); } catch (e) { fail(e); }
return;
}
if (args[0] === '--attune' && args[1]) {
try { emit(await executeAttune(fs.readFileSync(path.resolve(args[1]), 'utf8'))); } catch (e) { fail(e); }
return;
}
process.stderr.write('Usage:\n node runtime.js --build\n node runtime.js --search "text"\n node runtime.js --query <file.js>\n node runtime.js --attune <file.js>\n');
process.exitCode = 1;
}
main();
+1 -1
View File
@@ -1,6 +1,6 @@
// Binding-agnostic SQLite write plumbing shared from the Core package
// (docs/adr/0006). The injected db must expose `exec(sql)`; this works for both
// node:sqlite (skill/CLI) and better-sqlite3 (app), same injection model as
// node:sqlite (CLI) and better-sqlite3 (app), same injection model as
// `persist`.
export interface WriteTxDb {