@nightowlsdev/cli 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Night Owls contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # `@nightowlsdev/cli`
2
+
3
+ The pluggable Night Owls CLI. One thin binary — `owl` — that scaffolds a project, installs adapters, and
4
+ **contributes** each adapter's migrations into your `supabase/migrations/`. Each `@nightowlsdev/*` adapter
5
+ contributes a **declarative** manifest; the CLI discovers the installed adapters from your `package.json`
6
+ and acts on their manifests. All codegen lives in the CLI, so adapters stay data-only (no dependency cycle).
7
+
8
+ **Night Owls never runs DDL against your database.** It only ever *contributes* migrations into your
9
+ `supabase/migrations/`; you apply them with your own tooling (e.g. `supabase db push`).
10
+
11
+ ```bash
12
+ npx @nightowlsdev/cli init
13
+ ```
14
+
15
+ ## Commands
16
+
17
+ | Command | What it does |
18
+ | --- | --- |
19
+ | `owl init [plugin]` | Scaffold `nightowls.config.ts` + `.env.example` and install the selected adapters (each one **installs** its migrations into `supabase/migrations/` and runs its `init` hook). Idempotent; never clobbers an existing config. With a `[plugin]` arg (e.g. `owl init storage-supabase`), re-init **just that one** plugin against the existing project — re-apply its wiring + re-run its `init` hook (useful to integrate a plugin after the fact). Flags: `--storage <adapter>`, `--runner <adapter>`, `--no-storage`, `--no-runner`, `-y/--yes`. |
20
+ | `owl install <adapter>` | Add `@nightowlsdev/<adapter>` as a dependency and apply its boilerplate: merge its env vars into `.env.example`, scaffold its files (only if absent), insert its config import + wiring snippet into `nightowls.config.ts`, **install its migrations into `supabase/migrations/`**, and run its `init` hook. Idempotent. |
21
+ | `owl plugins` | List the installed `@nightowlsdev/*` plugins, their kind + description, and the commands each exposes. |
22
+ | `owl <plugin> <cmd>` | Run a plugin's own subcommand (e.g. `owl storage-supabase info`, `owl runner-nextjs routes`). `owl <plugin> --help` lists a plugin's commands; `owl <plugin> <cmd> --help` shows a command's flags. |
23
+ | `owl db install` | (Re-)install Night Owls' migrations into the host's classic `supabase/migrations/` as timestamped `.sql` files, so they run with the app's own via `supabase db push`. Useful after upgrading an adapter. Idempotent. Flag: `--out <dir>` (default `supabase/migrations`). |
24
+ | `owl db types` | Generate TypeScript types for the `nightowls` schema (`supabase gen types typescript --schema nightowls`). |
25
+
26
+ ## Adapters and what `owl install` wires
27
+
28
+ Each adapter contributes env vars, a config snippet (or files + guidance), an `init` hint, and an `info`
29
+ command. **Install ONE storage adapter, ONE auth provider, and ONE model provider; telemetry COMPOSES
30
+ (install as many as you like); `runner-background` is opt-in (`runner-nextjs` is the default).** The scaffolded
31
+ `nightowls.config.ts` is a **starting point you complete** — fill in your `agents` (and set `MODEL_ID`; a model
32
+ provider wires `modelFactory` and `models.allow` is env-driven from it).
33
+
34
+ | Adapter | kind | `owl install` wiring |
35
+ | --- | --- | --- |
36
+ | `storage-supabase` | storage | env `SUPABASE_URL`, `SUPABASE_SECRET_KEY`, `DATABASE_URL`, `SUPABASE_ANON_KEY`; config `storage = createSupabaseStorage({ url, secretKey, dbUrl })`; installs its migrations into `supabase/migrations/`. Command: `info`. |
37
+ | `runner-nextjs` | runner | scaffolds `app/api/swarm/*` route handlers; config `runner = createNextjsRunner({ swarm, auth, storage })`. The interactive (SSE) default. Command: `routes`. |
38
+ | `auth-supabase` | auth | env `SUPABASE_URL`, `SUPABASE_ANON_KEY` (anon key only — verifies the user JWT, never the secret key); config `auth = supabaseAuth({ url, anonKey })`. Command: `info`. |
39
+ | `auth-auth0` | auth | env `AUTH0_ISSUER_BASE_URL`, `AUTH0_AUDIENCE`; config `auth = auth0Auth({ issuerBaseUrl, audience })`. **Install ONE auth provider** (reconcile a duplicate `auth = …` if you install both). Command: `info`. |
40
+ | `telemetry-otel` | telemetry | env `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_HEADERS`; config **composes** `telemetry = [...(telemetry ?? []), otelTelemetry({ url })]`. Command: `info`. |
41
+ | `telemetry-langfuse` | telemetry | env `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_BASEURL`; config **composes** `telemetry = [...(telemetry ?? []), langfuseTelemetry({ publicKey, secretKey, baseUrl })]`. Command: `info`. |
42
+ | `runner-background` | runner | **opt-in** durable HITL runner (Trigger.dev v4 / Vercel Workflow). env `DURABLE_BACKEND`, `TRIGGER_SECRET_KEY`; scaffolds `trigger/nightowls.ts` (task) + `lib/nightowls-durable.ts` (registry); config is **commented guidance** at the runner marker — you wire `createBackgroundRunner(...)` by hand. The `init` hook prints the setup steps. Command: `info`. |
43
+ | `mcp` | connector | connect external MCP servers as Night Owls tools (approval-gated, fenced). env `MCP_<SERVER>_URL`; config is **commented guidance** at the connector marker — MCP tools are **granted to specific agents' skills**, not assigned to a single binding. Command: `info`. |
44
+ | `model-vercel-gateway` | model | any model via the Vercel AI Gateway (`provider/model` ids, one key). env `AI_GATEWAY_API_KEY`, `MODEL_ID`; config `modelFactory = vercelGatewayModels()`. **Install ONE model provider** (reconcile a duplicate `modelFactory = …` if you install more than one). Command: `info`. |
45
+ | `model-openrouter` | model | any model via OpenRouter. env `OPENROUTER_API_KEY`, `MODEL_ID`; config `modelFactory = openrouterModels()`. Command: `info`. |
46
+ | `model-anthropic` | model | Anthropic Claude models (native `@ai-sdk/anthropic`). env `ANTHROPIC_API_KEY`, `MODEL_ID`; config `modelFactory = anthropicModels()`. Command: `info`. |
47
+ | `model-openai` | model | OpenAI models (native `@ai-sdk/openai`). env `OPENAI_API_KEY`, `MODEL_ID`; config `modelFactory = openaiModels()`. Command: `info`. |
48
+
49
+ Telemetry composing is verified at the gate: the generated `nightowls.config.ts` with storage + runner + auth
50
+ + a model provider (`modelFactory = anthropicModels()`) + **both** telemetry exporters **parses and
51
+ typechecks** against the real `@nightowlsdev/core` + adapter types (`packages/cli/test/config-typecheck.test.ts`).
52
+
53
+ ## How migrations land: install, then apply with your own tooling
54
+
55
+ Night Owls is a migration **contributor** — it never connects to your database to run DDL. `owl install`
56
+ (and `owl init`, which installs per adapter) **installs** each storage adapter's migrations into the
57
+ host's classic `supabase/migrations/` directory as timestamped files named
58
+ `<YYYYMMDDHHMMSS>_corale_<version>.sql` (e.g. `20260604093000_corale_0001_core.sql`). You then apply them
59
+ with **your own** tooling — `supabase db push` — alongside your app's own migrations. The install prints
60
+ the apply instruction; it does not run it.
61
+
62
+ `owl db install` is **idempotent**: it scans the out dir and skips any version already installed (matched by the
63
+ `_corale_<version>` filename token), so re-running it never overwrites or duplicates a file. Newly added
64
+ migrations are appended on the next install (e.g. after `owl install` of a newer adapter, or an explicit
65
+ `owl db install`). The install-time timestamp (base clock plus an index offset, so the files stay unique and
66
+ lexically ordered) **slots them in correctly whether you adopt Night Owls on day one or add it later** — they
67
+ sort after whatever the host already had.
68
+
69
+ ## Plugin commands, lifecycle hooks, and help
70
+
71
+ Beyond the declarative wiring, a plugin can carry **behavior** and **docs**:
72
+
73
+ - **Subcommands** — a plugin exposes `commands`, each surfaced as `owl <plugin-name> <command-name>`.
74
+ The CLI discovers the installed plugins on every run and registers their command groups before parsing,
75
+ so `owl storage-supabase info` and `owl runner-nextjs routes` "just work". A plugin command's
76
+ handler gets a structural context: `{ cwd, log, args, options }`. The reference commands are **pure** —
77
+ no DB, no network — honoring "Night Owls never acts on its own". A plugin whose `name` would shadow a
78
+ built-in (`init`/`install`/`db`/`plugins`/`help`) is **skipped with a warning** (the built-in always wins).
79
+ - **`init` hook** — a plugin's optional `init({ cwd, log })` runs **after** the declarative wiring during
80
+ `owl init`, `owl install <plugin>`, and `owl init <plugin>`. This integrates a plugin into an
81
+ already-inited project. The hook is **idempotent and print-only** — it MUST NOT apply DDL or touch a DB.
82
+ - **Help** — every plugin and command carries a `description`. `owl plugins` lists them; `owl <plugin>
83
+ --help` and `owl <plugin> <cmd> --help` render Commander's usage from those descriptions + options.
84
+
85
+ ```bash
86
+ owl plugins # list installed plugins, their kind/description, and their commands
87
+ owl storage-supabase --help # a plugin's commands
88
+ owl storage-supabase info # a pure command: the migrations + env this adapter contributes
89
+ owl runner-nextjs routes # a pure command: the App Router routes this runner scaffolds
90
+ owl init storage-supabase # re-init one plugin: re-apply wiring + re-run its init hook
91
+ ```
92
+
93
+ **Adapters stay import-free of `@nightowlsdev/cli`.** The `init`/command handler contexts are typed
94
+ **structurally** (a local `type Ctx = { cwd; log; args; options }` in the adapter) — no adapter imports the
95
+ CLI, so the published `.d.ts` references no `@nightowlsdev/cli` type (no dependency cycle). The CLI's
96
+ `conformance.test-d.ts` pins each manifest to `NightOwlsPlugin` structurally.
97
+
98
+ ## The declarative `nightOwlsPlugin` contract
99
+
100
+ An adapter plugs into the CLI by exporting a `nightOwlsPlugin` plain object that **structurally** matches
101
+ `NightOwlsPlugin` (it does NOT import the type — that would create a cycle, since the CLI imports the adapter):
102
+
103
+ ```ts
104
+ export interface NightOwlsPlugin {
105
+ name: string; // short name, e.g. "storage-supabase"
106
+ version: string;
107
+ kind?: "storage" | "runner" | "auth" | "telemetry" | "ui" | "connector";
108
+ pkg: string; // npm name, e.g. "@nightowlsdev/storage-supabase"
109
+ description?: string; // one-line summary shown by `owl plugins` + `owl <plugin> --help`
110
+ migrations?: { version: string; name: string; sql: string }[]; // installed into supabase/migrations/
111
+ env?: { key: string; example: string; comment?: string }[]; // merged into .env.example
112
+ files?: { path: string; contents: string }[]; // scaffolded if absent (never clobbered)
113
+ config?: { // inserted at the `// nightowls:<marker>` line
114
+ import: string;
115
+ snippet: string;
116
+ marker: "storage" | "auth" | "runner" | "telemetry" | "connector";
117
+ };
118
+ // Runs after the declarative wiring on init/install (idempotent; print-only — never applies DDL).
119
+ init?: (ctx: { cwd: string; log: (msg: string) => void }) => void | Promise<void>;
120
+ // Subcommands surfaced as `owl <plugin-name> <command-name>` (the ctx adds { args, options }).
121
+ commands?: {
122
+ name: string;
123
+ description: string;
124
+ options?: { flags: string; description: string; defaultValue?: string | boolean }[];
125
+ run: (ctx: {
126
+ cwd: string;
127
+ log: (msg: string) => void;
128
+ args: string[];
129
+ options: Record<string, unknown>;
130
+ }) => void | Promise<void>;
131
+ }[];
132
+ }
133
+ ```
134
+
135
+ The CLI discovers `@nightowlsdev/*` deps from the host `package.json`, dynamic-imports each one, and collects the
136
+ `nightOwlsPlugin` exports. Adapters without a manifest (e.g. `@nightowlsdev/react`) are ignored.
137
+
138
+ `config.snippet` is a **top-level statement** inserted above the `// nightowls:<marker>` line — it assigns the
139
+ scaffold's pre-declared binding (e.g. `storage = createSupabaseStorage({ … });`,
140
+ `runner = createNextjsRunner({ … });`), so installing several adapters into one `nightowls.config.ts` produces
141
+ valid, functional TypeScript. It is NOT an object-property fragment.
142
+
143
+ ## The nightowls schema
144
+
145
+ Night Owls' tables live in a dedicated `nightowls` schema (not `public`). Night Owls **contributes** its migrations
146
+ into your `supabase/migrations/` (via `owl install` / `owl db install`); your host's migration runner
147
+ (`supabase db push`) applies them and tracks them in the host's own `supabase_migrations.schema_migrations`.
148
+ There is one migration system — the host's — and Night Owls never runs DDL itself. See
149
+ `docs/spec/2026-06-03-owl-schema-and-cli.md`.
150
+
151
+ ## Roadmap
152
+
153
+ The agentic CLI layer (`owl doctor`, `owl mcp`, deep ts-morph config edits) is future work. Today the
154
+ config insertion is marker-based — the `// nightowls:<marker>` comment lines are stable insertion points.
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/registry.ts
4
+ async function discoverPlugins(deps) {
5
+ const pkg = await deps.readPkgJson();
6
+ const names = [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})].filter(
7
+ (n) => n.startsWith("@nightowlsdev/")
8
+ );
9
+ const out = [];
10
+ for (const name of names) {
11
+ const mod = await deps.importPlugin(name).catch(() => ({}));
12
+ if (mod.nightOwlsPlugin) out.push({ ...mod.nightOwlsPlugin, pkg: mod.nightOwlsPlugin.pkg ?? name });
13
+ }
14
+ return out;
15
+ }
16
+ function nodeDiscoverDeps(cwd) {
17
+ return {
18
+ readPkgJson: async () => JSON.parse(await (await import("fs/promises")).readFile(`${cwd}/package.json`, "utf8")),
19
+ importPlugin: async (pkg) => (await import("./node-EVZ52KTR.js")).hostImport(cwd, pkg)
20
+ };
21
+ }
22
+
23
+ export {
24
+ discoverPlugins,
25
+ nodeDiscoverDeps
26
+ };
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/node.ts
4
+ async function exec(cmd, args) {
5
+ const { execFile } = await import("child_process");
6
+ const { promisify } = await import("util");
7
+ const run = promisify(execFile);
8
+ const { stdout } = await run(cmd, args, { maxBuffer: 64 * 1024 * 1024 });
9
+ return { stdout: stdout.toString() };
10
+ }
11
+ async function detectPackageManager(cwd) {
12
+ const fs = await import("fs/promises");
13
+ const has = async (f) => {
14
+ try {
15
+ await fs.access(`${cwd}/${f}`);
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
20
+ };
21
+ if (await has("pnpm-lock.yaml")) return "pnpm";
22
+ if (await has("yarn.lock")) return "yarn";
23
+ return "npm";
24
+ }
25
+ async function addDep(pm, cwd, pkg) {
26
+ const { spawn } = await import("child_process");
27
+ const args = pm === "yarn" ? ["add", pkg] : pm === "npm" ? ["install", pkg] : ["add", pkg];
28
+ await new Promise((resolve, reject) => {
29
+ const child = spawn(pm, args, { cwd, stdio: "inherit", shell: process.platform === "win32" });
30
+ child.on("error", reject);
31
+ child.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`${pm} ${args.join(" ")} exited ${code}`)));
32
+ });
33
+ }
34
+ async function isInstalled(cwd, pkg) {
35
+ try {
36
+ const fs = await import("fs/promises");
37
+ const json = JSON.parse(await fs.readFile(`${cwd}/package.json`, "utf8"));
38
+ return Boolean(json.dependencies?.[pkg] ?? json.devDependencies?.[pkg]);
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+ async function hostImport(cwd, pkg) {
44
+ const { createRequire } = await import("module");
45
+ const { pathToFileURL } = await import("url");
46
+ const requireFromHost = createRequire(`${cwd}/package.json`);
47
+ const resolved = requireFromHost.resolve(pkg);
48
+ return await import(pathToFileURL(resolved).href);
49
+ }
50
+ async function loadPlugin(pkg, cwd) {
51
+ try {
52
+ const mod = await hostImport(cwd, pkg);
53
+ return mod.nightOwlsPlugin ?? null;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+ async function nodeFs() {
59
+ const fs = await import("fs/promises");
60
+ return {
61
+ readFile: (p, enc) => fs.readFile(p, enc),
62
+ writeFile: (p, data) => fs.writeFile(p, data),
63
+ mkdir: (p, opts) => fs.mkdir(p, opts),
64
+ readdir: (p) => fs.readdir(p)
65
+ };
66
+ }
67
+
68
+ export {
69
+ exec,
70
+ detectPackageManager,
71
+ addDep,
72
+ isInstalled,
73
+ hostImport,
74
+ loadPlugin,
75
+ nodeFs
76
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/cli.js ADDED
@@ -0,0 +1,421 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ discoverPlugins,
4
+ nodeDiscoverDeps
5
+ } from "./chunk-E2C2JZ2E.js";
6
+ import {
7
+ addDep,
8
+ detectPackageManager,
9
+ exec,
10
+ isInstalled,
11
+ loadPlugin,
12
+ nodeFs
13
+ } from "./chunk-YZIYBDZO.js";
14
+
15
+ // src/cli.ts
16
+ import { Command } from "commander";
17
+
18
+ // src/commands/db.ts
19
+ function collectMigrations(plugins) {
20
+ const all = plugins.flatMap((p) => p.migrations ?? []);
21
+ if (all.length === 0) {
22
+ throw new Error(
23
+ "No storage adapter installed (or it ships no migrations). Run `owl install storage-supabase` first."
24
+ );
25
+ }
26
+ return [...all].sort((a, b) => a.version.localeCompare(b.version));
27
+ }
28
+ async function runDbTypes(deps) {
29
+ const schema = deps.schema ?? "nightowls";
30
+ const { stdout } = await deps.exec("supabase", ["gen", "types", "typescript", "--schema", schema]);
31
+ return stdout;
32
+ }
33
+ function migrationStamp(base, i) {
34
+ const d = new Date(base.getTime() + i * 1e3);
35
+ const p = (n) => String(n).padStart(2, "0");
36
+ return `${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}`;
37
+ }
38
+ var INSTALLED_FILE = /_corale_(.+)\.sql$/;
39
+ async function runDbInstall(deps) {
40
+ const migrations = collectMigrations(deps.plugins);
41
+ const dir = `${deps.root.replace(/\/$/, "")}/${deps.out.replace(/^\//, "")}`;
42
+ await deps.fs.mkdir(dir, { recursive: true }).catch(() => void 0);
43
+ const existing = await deps.fs.readdir(dir).catch(() => []);
44
+ const installed = new Set(
45
+ existing.map((f) => INSTALLED_FILE.exec(f)?.[1]).filter((v) => Boolean(v))
46
+ );
47
+ const base = deps.now();
48
+ const written = [];
49
+ const skipped = [];
50
+ let i = 0;
51
+ for (const m of migrations) {
52
+ if (installed.has(m.version)) {
53
+ skipped.push(m.version);
54
+ continue;
55
+ }
56
+ const file = `${migrationStamp(base, i)}_corale_${m.version}.sql`;
57
+ await deps.fs.writeFile(`${dir}/${file}`, `${m.sql.trimStart()}
58
+ `);
59
+ written.push({ version: m.version, file });
60
+ i += 1;
61
+ }
62
+ return { written, skipped };
63
+ }
64
+
65
+ // src/templates.ts
66
+ var CONFIG_TEMPLATE = `import { defineSwarm } from "@nightowlsdev/core";
67
+ // nightowls:imports \u2014 adapter imports are inserted above this line by \`owl install\`.
68
+
69
+ /**
70
+ * Read your secrets here (e.g. via \`process.env\` or a validated env module). Required by the inserted
71
+ * adapter snippets: SUPABASE_URL, SUPABASE_SECRET_KEY, DATABASE_URL (see .env.example).
72
+ */
73
+ const env = process.env as Record<string, string>;
74
+
75
+ // Define your agents (slug, instructions, model_id, skills). See the docs for the full shape.
76
+ const AGENTS: Parameters<typeof defineSwarm>[0]["agents"] = [];
77
+
78
+ // Storage adapter \u2014 \`owl install storage-supabase\` assigns \`storage = \u2026\` above the marker below
79
+ // (and installs its migrations into supabase/migrations/ \u2014 apply them with \`supabase db push\`).
80
+ // Until then this is a placeholder; a real storage adapter is required to run.
81
+ let storage: Parameters<typeof defineSwarm>[0]["storage"] = undefined as never;
82
+ // nightowls:storage
83
+
84
+ // Telemetry exporter(s) \u2014 \`owl install telemetry-*\` COMPOSES into this array (each adapter pushes
85
+ // its exporter above the marker). \`defineSwarm\` accepts one OR many; many are best-effort composed
86
+ // (a throwing exporter never breaks a run). Declared as the ARRAY member of that union so multiple
87
+ // exporters coexist (\`telemetry = [...(telemetry ?? []), xTelemetry({...})];\`).
88
+ let telemetry: Extract<NonNullable<Parameters<typeof defineSwarm>[0]["telemetry"]>, readonly unknown[]> = [];
89
+ // nightowls:telemetry
90
+
91
+ // Model provider \u2014 \`owl install model-<provider>\` assigns \`modelFactory = \u2026\` above the marker below
92
+ // (install ONE \u2014 providers are mutually exclusive, like auth). Declared as a \`let\` so the inserted
93
+ // snippet can reassign it; the placeholder throws until a real provider is wired. \`models.allow\` is
94
+ // env-driven (\`MODEL_ID\`), so the scaffold is runnable once a model plugin + MODEL_ID are set.
95
+ let modelFactory: Parameters<typeof defineSwarm>[0]["modelFactory"] = () => {
96
+ throw new Error("Install a model provider: owl install model-anthropic (or model-openai / model-vercel-gateway / model-openrouter).");
97
+ };
98
+ // nightowls:model
99
+
100
+ export const swarm = defineSwarm({
101
+ storage,
102
+ agents: AGENTS,
103
+ models: { allow: env.MODEL_ID ? [env.MODEL_ID] : [] },
104
+ modelFactory,
105
+ cost: { maxSteps: 12, maxCostUsd: 0.5 },
106
+ telemetry,
107
+ });
108
+
109
+ // Server-side auth \u2014 \`owl install auth-*\` assigns \`auth = \u2026\` above the marker below (install ONE
110
+ // auth provider). Declared as a \`let\` so the inserted snippet can reassign it; the placeholder rejects
111
+ // every request (never trust the request body for identity \u2014 replace it with a real identity resolver).
112
+ let auth: { authenticate(req: Request): Promise<{ tenantId: string; userId: string; capabilities?: string[] } | null> } = {
113
+ authenticate: async () => null,
114
+ };
115
+ // nightowls:auth
116
+
117
+ // Runner \u2014 \`owl install runner-nextjs\` assigns \`runner = \u2026\` above the marker below. The structural
118
+ // type covers the three App Router handlers the scaffolded route files call (\`runner.chatRoute()\`, \u2026), so
119
+ // the host typechecks before AND after the assignment without the CLI importing the runner package.
120
+ type SwarmRunner = {
121
+ chatRoute(): { POST: (req: Request) => Promise<Response> };
122
+ resumeRoute(): { POST: (req: Request) => Promise<Response> };
123
+ eventsRoute(): { GET: (req: Request, ctx: { params: Promise<{ id: string }> }) => Promise<Response> };
124
+ };
125
+ let runner: SwarmRunner = undefined as never;
126
+ // nightowls:runner
127
+ export { runner };
128
+
129
+ // Connectors \u2014 \`owl install mcp\` inserts a guidance comment above the marker below. Connector tools
130
+ // (e.g. MCP servers) are GRANTED to specific agents' skills (approval-gated, fenced), not assigned to a
131
+ // single binding \u2014 so the inserted snippet is a comment, not an assignment.
132
+ // nightowls:connector
133
+ `;
134
+ var ENV_EXAMPLE_HEADER = `# Night Owls environment \u2014 copy to .env.local and fill in.
135
+ # Adapter variables are appended below by \`owl install <adapter>\`.
136
+ `;
137
+
138
+ // src/codegen.ts
139
+ async function readIfExists(fs, path) {
140
+ try {
141
+ return await fs.readFile(path, "utf8");
142
+ } catch {
143
+ return null;
144
+ }
145
+ }
146
+ function join(root, rel) {
147
+ return `${root.replace(/\/$/, "")}/${rel.replace(/^\//, "")}`;
148
+ }
149
+ function envLine(e) {
150
+ const base = `${e.key}=${e.example}`;
151
+ return e.comment ? `${base} # ${e.comment}` : base;
152
+ }
153
+ async function mergeEnv(fs, envPath, env) {
154
+ const current = await readIfExists(fs, envPath) ?? "";
155
+ const additions = [];
156
+ for (const e of env) {
157
+ const present = new RegExp(`^\\s*${e.key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=`, "m").test(current);
158
+ if (!present) additions.push(envLine(e));
159
+ }
160
+ if (additions.length === 0) return;
161
+ const needsNl = current.length > 0 && !current.endsWith("\n");
162
+ const next = current + (needsNl ? "\n" : "") + additions.join("\n") + "\n";
163
+ await fs.writeFile(envPath, next);
164
+ }
165
+ async function insertConfig(fs, configPath, plugin) {
166
+ const cfg = plugin.config;
167
+ if (!cfg) return;
168
+ let text = await readIfExists(fs, configPath);
169
+ if (text === null) return;
170
+ let changed = false;
171
+ if (!text.includes(cfg.import)) {
172
+ text = `${cfg.import}
173
+ ${text}`;
174
+ changed = true;
175
+ }
176
+ if (!text.includes(cfg.snippet)) {
177
+ const marker = `// nightowls:${cfg.marker}`;
178
+ const idx = text.indexOf(marker);
179
+ if (idx !== -1) {
180
+ const lineStart = text.lastIndexOf("\n", idx) + 1;
181
+ const indent = text.slice(lineStart, idx);
182
+ const insertion = `${indent}${cfg.snippet}
183
+ `;
184
+ text = text.slice(0, lineStart) + insertion + text.slice(lineStart);
185
+ changed = true;
186
+ }
187
+ }
188
+ if (changed) await fs.writeFile(configPath, text);
189
+ }
190
+ async function scaffoldFiles(fs, root, plugin) {
191
+ for (const f of plugin.files ?? []) {
192
+ const abs = join(root, f.path);
193
+ if (await readIfExists(fs, abs) !== null) continue;
194
+ const dir = abs.slice(0, abs.lastIndexOf("/"));
195
+ if (dir) await fs.mkdir(dir, { recursive: true });
196
+ await fs.writeFile(abs, f.contents);
197
+ }
198
+ }
199
+ async function applyInstall(args) {
200
+ const { plugin, root, fs } = args;
201
+ if (plugin.env?.length) await mergeEnv(fs, join(root, ".env.example"), plugin.env);
202
+ await scaffoldFiles(fs, root, plugin);
203
+ await insertConfig(fs, join(root, "nightowls.config.ts"), plugin);
204
+ }
205
+
206
+ // src/commands/install.ts
207
+ function toPkgName(adapter) {
208
+ return adapter.startsWith("@nightowlsdev/") ? adapter : `@nightowlsdev/${adapter}`;
209
+ }
210
+ async function runInstall(deps) {
211
+ const pkg = toPkgName(deps.adapter);
212
+ const already = deps.isInstalled ? await deps.isInstalled(pkg) : false;
213
+ let added = false;
214
+ if (!already) {
215
+ await deps.addDep(pkg);
216
+ added = true;
217
+ }
218
+ const plugin = await deps.loadPlugin(pkg);
219
+ if (!plugin) {
220
+ throw new Error(`${pkg} does not export a nightOwlsPlugin manifest \u2014 nothing to install.`);
221
+ }
222
+ await applyInstall({ plugin, root: deps.root, fs: deps.fs });
223
+ let migrations = [];
224
+ if (plugin.migrations?.length) {
225
+ const result = await runDbInstall({
226
+ plugins: [plugin],
227
+ root: deps.root,
228
+ out: "supabase/migrations",
229
+ fs: deps.fs,
230
+ now: deps.now ?? (() => /* @__PURE__ */ new Date())
231
+ });
232
+ migrations = result.written;
233
+ }
234
+ const log = deps.log ?? console.log;
235
+ let initRan = false;
236
+ if (plugin.init) {
237
+ await plugin.init({ cwd: deps.root, log });
238
+ initRan = true;
239
+ }
240
+ return { pkg, added, applied: true, migrations, initRan };
241
+ }
242
+
243
+ // src/commands/init.ts
244
+ async function readIfExists2(fs, path) {
245
+ try {
246
+ return await fs.readFile(path, "utf8");
247
+ } catch {
248
+ return null;
249
+ }
250
+ }
251
+ function join2(root, rel) {
252
+ return `${root.replace(/\/$/, "")}/${rel}`;
253
+ }
254
+ async function runInit(deps) {
255
+ const configPath = join2(deps.root, "nightowls.config.ts");
256
+ const envPath = join2(deps.root, ".env.example");
257
+ if (await readIfExists2(deps.fs, configPath) === null) {
258
+ await deps.fs.writeFile(configPath, CONFIG_TEMPLATE);
259
+ }
260
+ if (await readIfExists2(deps.fs, envPath) === null) {
261
+ await deps.fs.writeFile(envPath, ENV_EXAMPLE_HEADER);
262
+ }
263
+ const migrations = [];
264
+ for (const adapter of deps.adapters) {
265
+ const result = await runInstall({
266
+ adapter,
267
+ root: deps.root,
268
+ fs: deps.fs,
269
+ addDep: deps.addDep,
270
+ loadPlugin: deps.loadPlugin,
271
+ now: deps.now,
272
+ log: deps.log
273
+ });
274
+ migrations.push(...result.migrations);
275
+ }
276
+ return { migrations };
277
+ }
278
+
279
+ // src/commands/plugins.ts
280
+ function registerPluginCommands(program2, plugins, base) {
281
+ const taken = /* @__PURE__ */ new Set([...program2.commands.map((c) => c.name()), "help"]);
282
+ for (const p of plugins) {
283
+ if (!p.commands?.length) continue;
284
+ if (taken.has(p.name)) {
285
+ base.log(`(skipping plugin "${p.name}": shadows a built-in command)`);
286
+ continue;
287
+ }
288
+ taken.add(p.name);
289
+ const group = program2.command(p.name).description(p.description ?? `${p.kind ?? "plugin"} adapter (${p.pkg})`);
290
+ for (const cmd of p.commands) {
291
+ const sub = group.command(cmd.name).description(cmd.description);
292
+ for (const o of cmd.options ?? []) {
293
+ sub.option(o.flags, o.description, o.defaultValue);
294
+ }
295
+ sub.allowExcessArguments(true);
296
+ sub.action(async (...a) => {
297
+ const command = a[a.length - 1];
298
+ await cmd.run({ ...base, args: command.args ?? [], options: command.opts() });
299
+ });
300
+ }
301
+ }
302
+ }
303
+ function runPluginsList(plugins, log) {
304
+ if (!plugins.length) {
305
+ log("No @nightowlsdev/* plugins found in this project.");
306
+ return;
307
+ }
308
+ log("Installed Night Owls plugins:");
309
+ for (const p of plugins) {
310
+ const cmds = p.commands?.length ? ` [${p.commands.map((c) => c.name).join(", ")}]` : "";
311
+ log(` ${p.name.padEnd(20)} ${(p.kind ?? "").padEnd(10)} ${p.description ?? ""}${cmds}`);
312
+ }
313
+ }
314
+
315
+ // src/cli.ts
316
+ function reportInstalledMigrations(migrations) {
317
+ if (!migrations.length) return;
318
+ console.log(`\u2713 Installed Night Owls's migrations into supabase/migrations/ (${migrations.length} file(s)).`);
319
+ console.log(" Apply them with your own tooling: supabase db push");
320
+ }
321
+ var program = new Command();
322
+ program.name("owl").description("The pluggable Night Owls CLI: scaffold a config, install adapters, and contribute their migrations to your supabase/migrations/.").version("0.0.0");
323
+ program.command("init").argument("[plugin]", "re-init just one already-installed plugin (apply its wiring + run its init hook)").description(
324
+ "Scaffold nightowls.config.ts + .env.example and install the selected adapters (idempotent). With a [plugin] arg, re-init just that one plugin against the existing project."
325
+ ).option("--storage <adapter>", "storage adapter to install", "storage-supabase").option("--runner <adapter>", "runner adapter to install", "runner-nextjs").option("--no-storage", "skip installing a storage adapter").option("--no-runner", "skip installing a runner adapter").option("-y, --yes", "accept defaults (non-interactive)").action(
326
+ async (plugin, opts) => {
327
+ const cwd = process.cwd();
328
+ const pm = await detectPackageManager(cwd);
329
+ if (plugin) {
330
+ const pkg = toPkgName(plugin);
331
+ const result = await runInstall({
332
+ adapter: plugin,
333
+ root: cwd,
334
+ fs: await nodeFs(),
335
+ addDep: (p) => addDep(pm, cwd, p),
336
+ loadPlugin: (p) => loadPlugin(p, cwd),
337
+ isInstalled: (p) => isInstalled(cwd, p),
338
+ now: () => /* @__PURE__ */ new Date(),
339
+ log: console.log
340
+ });
341
+ console.log(`${result.added ? "Added" : "Found"} ${pkg}; re-applied its env/config/files.`);
342
+ reportInstalledMigrations(result.migrations);
343
+ if (result.initRan) console.log(`\u2713 Ran ${plugin}'s init hook.`);
344
+ return;
345
+ }
346
+ const adapters = [];
347
+ if (opts.storage) adapters.push(opts.storage);
348
+ if (opts.runner) adapters.push(opts.runner);
349
+ const { migrations } = await runInit({
350
+ root: cwd,
351
+ fs: await nodeFs(),
352
+ adapters,
353
+ loadPlugin: (p) => loadPlugin(p, cwd),
354
+ addDep: (p) => addDep(pm, cwd, p),
355
+ pm,
356
+ now: () => /* @__PURE__ */ new Date(),
357
+ log: console.log
358
+ });
359
+ console.log("Scaffolded nightowls.config.ts + .env.example.");
360
+ if (adapters.length) console.log(`Installed: ${adapters.join(", ")}.`);
361
+ reportInstalledMigrations(migrations);
362
+ console.log("Next: fill .env.local, then run your dev server.");
363
+ }
364
+ );
365
+ program.command("install").argument("<adapter>", "adapter short name, e.g. storage-supabase").description("Add a @nightowlsdev/<adapter> dependency and apply its env/config/files boilerplate (idempotent).").action(async (adapter) => {
366
+ const cwd = process.cwd();
367
+ const pm = await detectPackageManager(cwd);
368
+ const pkg = toPkgName(adapter);
369
+ const result = await runInstall({
370
+ adapter,
371
+ root: cwd,
372
+ fs: await nodeFs(),
373
+ addDep: (p) => addDep(pm, cwd, p),
374
+ loadPlugin: (p) => loadPlugin(p, cwd),
375
+ isInstalled: (p) => isInstalled(cwd, p),
376
+ now: () => /* @__PURE__ */ new Date(),
377
+ log: console.log
378
+ });
379
+ console.log(`${result.added ? "Added" : "Found"} ${pkg}; applied its env/config/files.`);
380
+ reportInstalledMigrations(result.migrations);
381
+ if (result.initRan) console.log(`\u2713 Ran ${adapter}'s init hook.`);
382
+ console.log("Next: fill .env.local, then run your dev server.");
383
+ });
384
+ var db = program.command("db").description("Manage nightowls migrations (install into your supabase/migrations, generate types).");
385
+ db.command("types").description("Generate TypeScript types for the nightowls schema (supabase gen types).").action(async () => {
386
+ const out = await runDbTypes({ exec });
387
+ process.stdout.write(out);
388
+ });
389
+ db.command("install").description("Install Night Owls' migrations into your supabase/migrations/ (timestamped, idempotent) \u2014 apply them with `supabase db push`.").option("--out <dir>", "target migrations dir (relative to cwd)", "supabase/migrations").action(async (opts) => {
390
+ const cwd = process.cwd();
391
+ const plugins = await discoverPlugins(nodeDiscoverDeps(cwd));
392
+ const { written, skipped } = await runDbInstall({
393
+ plugins,
394
+ root: cwd,
395
+ out: opts.out,
396
+ fs: await nodeFs(),
397
+ now: () => /* @__PURE__ */ new Date()
398
+ });
399
+ for (const w of written) console.log(`wrote ${opts.out}/${w.file}`);
400
+ for (const version of skipped) console.log(`skipped ${version} (already installed)`);
401
+ if (!written.length) console.log("Nothing to install \u2014 all nightowls migrations already present.");
402
+ });
403
+ program.command("plugins").description("List installed Night Owls plugins and the commands they expose.").action(async () => {
404
+ const cwd = process.cwd();
405
+ const plugins = await discoverPlugins(nodeDiscoverDeps(cwd));
406
+ runPluginsList(plugins, console.log);
407
+ });
408
+ program.command("mcp").description("Run the Night Owls MCP server on stdio (exposes nightowls_* tools to external agents).").action(async () => {
409
+ const { runMcpServer } = await import("./mcp-3ZFDXVDT.js");
410
+ await runMcpServer();
411
+ });
412
+ async function main() {
413
+ const cwd = process.cwd();
414
+ const plugins = await discoverPlugins(nodeDiscoverDeps(cwd)).catch(() => []);
415
+ registerPluginCommands(program, plugins, { cwd, log: console.log });
416
+ await program.parseAsync();
417
+ }
418
+ main().catch((err) => {
419
+ console.error(err instanceof Error ? err.message : err);
420
+ process.exitCode = 1;
421
+ });
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The declarative Night Owls plugin contract.
3
+ *
4
+ * Each `@nightowlsdev/*` adapter exports a `nightOwlsPlugin` plain object that STRUCTURALLY matches
5
+ * `NightOwlsPlugin` — adapters do NOT import this type (that would create a dependency cycle, since the
6
+ * CLI also imports the adapters). All codegen (env merge, file scaffold, config insertion, migrations)
7
+ * lives in the CLI; adapters stay data-only.
8
+ */
9
+ /** A single Night Owls migration: a stable `version` (ledger key), a human `name`, and `nightowls.*` SQL. */
10
+ interface Migration {
11
+ version: string;
12
+ name: string;
13
+ sql: string;
14
+ }
15
+ /** An environment variable the adapter needs. Appended to `.env.example` if its `key` is absent. */
16
+ interface PluginEnv {
17
+ key: string;
18
+ example: string;
19
+ comment?: string;
20
+ }
21
+ /** A file scaffolded into the host (e.g. a Next.js route handler). Written only if absent (idempotent). */
22
+ interface PluginFile {
23
+ path: string;
24
+ contents: string;
25
+ }
26
+ /** The `import` line + the wiring `snippet` inserted at the `// nightowls:<marker>` line in the config. */
27
+ interface PluginConfig {
28
+ import: string;
29
+ snippet: string;
30
+ marker: "storage" | "auth" | "runner" | "telemetry" | "connector" | "model";
31
+ }
32
+ /**
33
+ * Context passed to a plugin's `init` hook and command handlers. Structural — adapters type their
34
+ * handlers against an equivalent shape WITHOUT importing @nightowlsdev/cli (keeps their .d.ts cycle-free).
35
+ */
36
+ interface PluginContext {
37
+ /** The host project root (cwd). */
38
+ cwd: string;
39
+ /** Print a line to the user. */
40
+ log: (msg: string) => void;
41
+ }
42
+ /** The context a plugin command's `run` receives — the base context plus the parsed argv. */
43
+ interface PluginCommandContext extends PluginContext {
44
+ /** Positional args after `owl <plugin> <cmd>`. */
45
+ args: string[];
46
+ /** Parsed `--flag` options. */
47
+ options: Record<string, unknown>;
48
+ }
49
+ /** A flag a plugin command accepts (declarative; registered on the Commander subcommand). */
50
+ interface PluginOption {
51
+ /** e.g. "--json" or "--out <dir>". */
52
+ flags: string;
53
+ description: string;
54
+ defaultValue?: string | boolean;
55
+ }
56
+ /** A subcommand a plugin exposes, run as `owl <plugin-name> <command-name>`. */
57
+ interface PluginCommand {
58
+ name: string;
59
+ description: string;
60
+ options?: readonly PluginOption[];
61
+ /** Self-contained: uses only the ctx + the adapter's own logic + Node. MUST NOT import @nightowlsdev/cli. */
62
+ run: (ctx: PluginCommandContext) => Promise<void> | void;
63
+ }
64
+ /** The full adapter manifest the CLI discovers, dynamic-imports, and acts on. */
65
+ interface NightOwlsPlugin {
66
+ /** Adapter short name, e.g. "storage-supabase". */
67
+ name: string;
68
+ version: string;
69
+ kind?: "storage" | "runner" | "auth" | "telemetry" | "ui" | "connector" | "model";
70
+ /** npm name, e.g. "@nightowlsdev/storage-supabase". */
71
+ pkg: string;
72
+ /** One-line summary shown by `owl plugins` and `owl <plugin> --help`. */
73
+ description?: string;
74
+ migrations?: readonly Migration[];
75
+ env?: readonly PluginEnv[];
76
+ files?: readonly PluginFile[];
77
+ config?: PluginConfig;
78
+ /**
79
+ * Runs during `owl init`, `owl install <plugin>`, and `owl init <plugin>` — AFTER the
80
+ * declarative wiring. For integration that can't be expressed declaratively (idempotent; print-only
81
+ * side effects preferred — never apply DDL).
82
+ */
83
+ init?: (ctx: PluginContext) => Promise<void> | void;
84
+ /** Plugin-specific subcommands, exposed as `owl <plugin-name> <command-name>`. */
85
+ commands?: readonly PluginCommand[];
86
+ }
87
+
88
+ interface DiscoverDeps {
89
+ readPkgJson: () => Promise<{
90
+ dependencies?: Record<string, string>;
91
+ devDependencies?: Record<string, string>;
92
+ }>;
93
+ importPlugin: (pkg: string) => Promise<{
94
+ nightOwlsPlugin?: NightOwlsPlugin;
95
+ }>;
96
+ }
97
+ /**
98
+ * Discover installed `@nightowlsdev/*` adapters that export a `nightOwlsPlugin` manifest. Injected deps keep this
99
+ * hermetic (tests pass fakes; production uses `nodeDiscoverDeps`). `@nightowlsdev/*` deps without a manifest
100
+ * (e.g. `@nightowlsdev/react`) are silently ignored.
101
+ */
102
+ declare function discoverPlugins(deps: DiscoverDeps): Promise<NightOwlsPlugin[]>;
103
+ /** Production deps: read `<cwd>/package.json` + dynamic-import each adapter's manifest resolved from the
104
+ * HOST cwd (so a real host's installed adapters are discovered, not the CLI's own deps). */
105
+ declare function nodeDiscoverDeps(cwd: string): DiscoverDeps;
106
+
107
+ export { type DiscoverDeps, type Migration, type NightOwlsPlugin, type PluginCommand, type PluginCommandContext, type PluginConfig, type PluginContext, type PluginEnv, type PluginFile, type PluginOption, discoverPlugins, nodeDiscoverDeps };
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ discoverPlugins,
4
+ nodeDiscoverDeps
5
+ } from "./chunk-E2C2JZ2E.js";
6
+ export {
7
+ discoverPlugins,
8
+ nodeDiscoverDeps
9
+ };
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/mcp.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { createMastraLibsqlStore } from "@nightowlsdev/storage-local";
7
+ function doctorReport() {
8
+ const bootstrapStore = createMastraLibsqlStore({ url: ":memory:" });
9
+ return {
10
+ ok: true,
11
+ bootstrapStore: bootstrapStore ? "available (libsql :memory:)" : "unavailable",
12
+ node: process.version,
13
+ cwd: process.cwd()
14
+ };
15
+ }
16
+ function buildMcpServer() {
17
+ const server = new McpServer({ name: "nightowls", version: "0.0.0" });
18
+ server.registerTool(
19
+ "nightowls_doctor",
20
+ {
21
+ title: "Night Owls doctor",
22
+ description: "Report Night Owls environment diagnostics (read-only).",
23
+ annotations: { readOnlyHint: true }
24
+ },
25
+ async () => ({ content: [{ type: "text", text: JSON.stringify(doctorReport(), null, 2) }] })
26
+ );
27
+ return server;
28
+ }
29
+ async function runMcpServer() {
30
+ const server = buildMcpServer();
31
+ const transport = new StdioServerTransport();
32
+ await server.connect(transport);
33
+ console.error("[nightowls] mcp server ready on stdio (nightowls_doctor)");
34
+ }
35
+ export {
36
+ buildMcpServer,
37
+ doctorReport,
38
+ runMcpServer
39
+ };
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ addDep,
4
+ detectPackageManager,
5
+ exec,
6
+ hostImport,
7
+ isInstalled,
8
+ loadPlugin,
9
+ nodeFs
10
+ } from "./chunk-YZIYBDZO.js";
11
+ export {
12
+ addDep,
13
+ detectPackageManager,
14
+ exec,
15
+ hostImport,
16
+ isInstalled,
17
+ loadPlugin,
18
+ nodeFs
19
+ };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@nightowlsdev/cli",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "owl": "./dist/cli.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/cueplusplus/corale.git",
24
+ "directory": "packages/cli"
25
+ },
26
+ "homepage": "https://github.com/cueplusplus/corale#readme",
27
+ "dependencies": {
28
+ "commander": "^15.0.0",
29
+ "ts-morph": "^28.0.0",
30
+ "@modelcontextprotocol/sdk": "^1.29.0",
31
+ "@nightowlsdev/storage-local": "0.1.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24",
35
+ "memfs": "^4.57.0",
36
+ "tsup": "8.5.1",
37
+ "typescript": "6.0.3",
38
+ "vitest": "^3.2.0",
39
+ "@nightowlsdev/storage-supabase": "0.3.0",
40
+ "@nightowlsdev/core": "0.3.0",
41
+ "@nightowlsdev/telemetry-otel": "0.1.1",
42
+ "@nightowlsdev/telemetry-langfuse": "0.1.1",
43
+ "@nightowlsdev/runner-nextjs": "0.2.0",
44
+ "@nightowlsdev/runner-background": "0.2.0",
45
+ "@nightowlsdev/model-anthropic": "0.1.0",
46
+ "@nightowlsdev/auth-supabase": "0.1.1",
47
+ "@nightowlsdev/auth-auth0": "0.1.1",
48
+ "@nightowlsdev/eslint-config": "0.0.0",
49
+ "@nightowlsdev/mcp": "0.1.1",
50
+ "@nightowlsdev/tsconfig": "0.0.0",
51
+ "@nightowlsdev/model-openai": "0.1.0",
52
+ "@nightowlsdev/model-vercel-gateway": "0.1.0",
53
+ "@nightowlsdev/model-openrouter": "0.1.0"
54
+ },
55
+ "scripts": {
56
+ "build": "tsup",
57
+ "typecheck": "tsc --noEmit",
58
+ "test": "vitest run",
59
+ "lint": "eslint src"
60
+ }
61
+ }