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
+16
View File
@@ -0,0 +1,16 @@
# Obelisk CLI
The local Obelisk runtime used by coding agents. It indexes Claude Code and
Codex transcripts into `~/.obelisk/obelisk.sqlite` and exposes the stable
`build`, `search`, `query`, and `attune` process interface.
```bash
npm install --global @obelisk-apps/cli
obelisk --version
obelisk install
obelisk --query /tmp/query.mjs
```
`obelisk install` installs the separate docs-only agent skill from
`tommy0103/obelisk-skill`. The CLI itself remains daemon-free: each command
refreshes the local index when write ownership is available, then exits.
+24
View File
@@ -0,0 +1,24 @@
{
"name": "@obelisk-apps/cli",
"version": "0.1.0",
"description": "Local Obelisk runtime for coding agents.",
"license": "AGPL-3.0",
"type": "module",
"bin": {
"obelisk": "dist/cli/src/obelisk.js"
},
"files": [
"dist",
"README.md"
],
"engines": {
"node": ">=22.13.0"
},
"scripts": {
"build": "node scripts/build.mjs",
"prepack": "npm run build"
},
"publishConfig": {
"access": "public"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { execFileSync } from 'node:child_process';
import { copyFileSync, mkdirSync, rmSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const cliRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const repoRoot = resolve(cliRoot, '../..');
const outDir = resolve(cliRoot, 'dist');
const tsc = resolve(repoRoot, 'node_modules/typescript/bin/tsc');
rmSync(outDir, { recursive: true, force: true });
execFileSync(process.execPath, [tsc, '-p', resolve(cliRoot, 'tsconfig.build.json')], {
cwd: repoRoot,
stdio: 'inherit',
});
const schemaTarget = resolve(outDir, 'core/src/schema.sql');
mkdirSync(dirname(schemaTarget), { recursive: true });
copyFileSync(resolve(repoRoot, 'packages/core/src/schema.sql'), schemaTarget);
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env node
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
import {
DB_PATH,
buildIndex,
searchText,
executeQuery,
executeAttune,
} from '../../core/src/core.ts';
async function main() {
const args = process.argv.slice(2);
const fail = (value: unknown): void => {
const error = value instanceof Error ? value : new Error(String(value));
process.stdout.write(JSON.stringify({ error: error.message, stack: error.stack }) + '\n');
process.exitCode = 1;
};
const emit = (value: unknown): void => {
process.stdout.write(JSON.stringify(value, null, 2) + '\n');
};
if (args[0] === '--version' || args[0] === '-v') {
const packageJson = JSON.parse(
readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'),
) as { version: string };
process.stdout.write(`${packageJson.version}\n`);
return;
}
if (args[0] === '--build') {
try {
buildIndex({ force: true });
process.stdout.write(JSON.stringify({ ok: true, db: DB_PATH }) + '\n');
} catch (error) { fail(error); }
return;
}
if (args[0] === '--search' && args[1]) {
try { emit(searchText(args.slice(1).join(' '))); } catch (error) { fail(error); }
return;
}
if (args[0] === '--query' && args[1]) {
try { emit(await executeQuery(readFileSync(resolve(args[1]), 'utf8'))); } catch (error) { fail(error); }
return;
}
if (args[0] === '--attune' && args[1]) {
try { emit(await executeAttune(readFileSync(resolve(args[1]), 'utf8'))); } catch (error) { fail(error); }
return;
}
if (args[0] === 'install') {
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
const child = spawnSync(
npx,
['--yes', 'skills', 'add', 'tommy0103/obelisk-skill', ...args.slice(1)],
{ stdio: 'inherit', shell: process.platform === 'win32' },
);
if (child.error) {
process.stderr.write(`Unable to run the skills installer: ${child.error.message}\n`);
process.exitCode = 1;
} else {
process.exitCode = child.status ?? 1;
}
return;
}
process.stderr.write('Usage:\n obelisk install [skills options]\n obelisk --build\n obelisk --search "text"\n obelisk --query <file.js>\n obelisk --attune <file.js>\n');
process.exitCode = 1;
}
void main();
+17
View File
@@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"noEmit": false,
"outDir": "dist",
"rootDir": "..",
"declaration": false,
"rewriteRelativeImportExtensions": true
},
"include": [
"src/**/*.ts",
"../core/src/**/*.ts"
],
"exclude": [
"../core/src/runtime.ts"
]
}