@nightowlsdev/cli 0.1.1 → 0.2.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.
package/README.md CHANGED
@@ -22,6 +22,7 @@ npx @nightowlsdev/cli init
22
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
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
24
  | `owl db types` | Generate TypeScript types for the `nightowls` schema (`supabase gen types typescript --schema nightowls`). |
25
+ | `owl mcp` | Start the Night Owls MCP server on stdio (exposes `nightowls_*` tools to external coding agents such as Claude Code or Cursor). |
25
26
 
26
27
  ## Adapters and what `owl install` wires
27
28
 
@@ -45,6 +46,7 @@ provider wires `modelFactory` and `models.allow` is env-driven from it).
45
46
  | `model-openrouter` | model | any model via OpenRouter. env `OPENROUTER_API_KEY`, `MODEL_ID`; config `modelFactory = openrouterModels()`. Command: `info`. |
46
47
  | `model-anthropic` | model | Anthropic Claude models (native `@ai-sdk/anthropic`). env `ANTHROPIC_API_KEY`, `MODEL_ID`; config `modelFactory = anthropicModels()`. Command: `info`. |
47
48
  | `model-openai` | model | OpenAI models (native `@ai-sdk/openai`). env `OPENAI_API_KEY`, `MODEL_ID`; config `modelFactory = openaiModels()`. Command: `info`. |
49
+ | `model-ollama` | model | Models via Ollama (OpenAI-compatible endpoint). Local needs **no API key**; Ollama Cloud uses one. env `OLLAMA_BASE_URL` (default `http://localhost:11434/v1`), `OLLAMA_API_KEY` (Cloud only), `MODEL_ID` (e.g. `llama3.1`); config `modelFactory = ollamaModels()`. Command: `info`. |
48
50
 
49
51
  Telemetry composing is verified at the gate: the generated `nightowls.config.ts` with storage + runner + auth
50
52
  + a model provider (`modelFactory = anthropicModels()`) + **both** telemetry exporters **parses and
@@ -104,7 +106,7 @@ An adapter plugs into the CLI by exporting a `nightOwlsPlugin` plain object that
104
106
  export interface NightOwlsPlugin {
105
107
  name: string; // short name, e.g. "storage-supabase"
106
108
  version: string;
107
- kind?: "storage" | "runner" | "auth" | "telemetry" | "ui" | "connector";
109
+ kind?: "storage" | "runner" | "auth" | "telemetry" | "ui" | "connector" | "model";
108
110
  pkg: string; // npm name, e.g. "@nightowlsdev/storage-supabase"
109
111
  description?: string; // one-line summary shown by `owl plugins` + `owl <plugin> --help`
110
112
  migrations?: { version: string; name: string; sql: string }[]; // installed into supabase/migrations/
@@ -113,7 +115,7 @@ export interface NightOwlsPlugin {
113
115
  config?: { // inserted at the `// nightowls:<marker>` line
114
116
  import: string;
115
117
  snippet: string;
116
- marker: "storage" | "auth" | "runner" | "telemetry" | "connector";
118
+ marker: "storage" | "auth" | "runner" | "telemetry" | "connector" | "model";
117
119
  };
118
120
  // Runs after the declarative wiring on init/install (idempotent; print-only — never applies DDL).
119
121
  init?: (ctx: { cwd: string; log: (msg: string) => void }) => void | Promise<void>;
@@ -148,7 +150,22 @@ into your `supabase/migrations/` (via `owl install` / `owl db install`); your ho
148
150
  There is one migration system — the host's — and Night Owls never runs DDL itself. See
149
151
  `docs/spec/2026-06-03-owl-schema-and-cli.md`.
150
152
 
151
- ## Roadmap
153
+ ## `owl mcp`
152
154
 
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.
155
+ Run the Night Owls MCP server on stdio. This exposes `nightowls_*` tools to external coding agents
156
+ (Claude Code, Cursor, or any MCP client) so they can query the local Night Owls environment without
157
+ running CLI subcommands manually.
158
+
159
+ ```bash
160
+ owl mcp
161
+ ```
162
+
163
+ The server starts on stdio (JSON-RPC frames on stdout; human-readable diagnostics on stderr). Currently
164
+ exposes one tool:
165
+
166
+ | Tool | Description |
167
+ | --- | --- |
168
+ | `nightowls_doctor` | Read-only diagnostics: confirms the bootstrap store is reachable, reports Node version and cwd. `readOnlyHint: true`. |
169
+
170
+ Engine-wall §1 / Option B is enforced here: `owl mcp` uses the raw `@modelcontextprotocol/sdk` and
171
+ `@nightowlsdev/storage-local`'s factory — **zero `@mastra/*` imports** enter the CLI binary.
package/dist/cli.js CHANGED
@@ -35,16 +35,17 @@ function migrationStamp(base, i) {
35
35
  const p = (n) => String(n).padStart(2, "0");
36
36
  return `${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}`;
37
37
  }
38
- var INSTALLED_FILE = /_corale_(.+)\.sql$/;
38
+ var INSTALLED_FILE = /_nightowls_(.+)\.sql$/;
39
39
  async function runDbInstall(deps) {
40
40
  const migrations = collectMigrations(deps.plugins);
41
41
  const dir = `${deps.root.replace(/\/$/, "")}/${deps.out.replace(/^\//, "")}`;
42
- await deps.fs.mkdir(dir, { recursive: true }).catch(() => void 0);
42
+ if (!deps.dryRun) await deps.fs.mkdir(dir, { recursive: true }).catch(() => void 0);
43
43
  const existing = await deps.fs.readdir(dir).catch(() => []);
44
44
  const installed = new Set(
45
45
  existing.map((f) => INSTALLED_FILE.exec(f)?.[1]).filter((v) => Boolean(v))
46
46
  );
47
47
  const base = deps.now();
48
+ const stamp = deps.stamp ?? ((_v, i2, b) => migrationStamp(b, i2));
48
49
  const written = [];
49
50
  const skipped = [];
50
51
  let i = 0;
@@ -53,13 +54,13 @@ async function runDbInstall(deps) {
53
54
  skipped.push(m.version);
54
55
  continue;
55
56
  }
56
- const file = `${migrationStamp(base, i)}_corale_${m.version}.sql`;
57
- await deps.fs.writeFile(`${dir}/${file}`, `${m.sql.trimStart()}
57
+ const file = `${stamp(m.version, i, base)}_nightowls_${m.version}.sql`;
58
+ if (!deps.dryRun) await deps.fs.writeFile(`${dir}/${file}`, `${m.sql.trimStart()}
58
59
  `);
59
60
  written.push({ version: m.version, file });
60
61
  i += 1;
61
62
  }
62
- return { written, skipped };
63
+ return { written, skipped, dryRun: Boolean(deps.dryRun) };
63
64
  }
64
65
 
65
66
  // src/templates.ts
@@ -93,7 +94,7 @@ let telemetry: Extract<NonNullable<Parameters<typeof defineSwarm>[0]["telemetry"
93
94
  // snippet can reassign it; the placeholder throws until a real provider is wired. \`models.allow\` is
94
95
  // env-driven (\`MODEL_ID\`), so the scaffold is runnable once a model plugin + MODEL_ID are set.
95
96
  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
+ throw new Error("Install a model provider: owl install model-anthropic (or model-openai / model-vercel-gateway / model-openrouter / model-ollama).");
97
98
  };
98
99
  // nightowls:model
99
100
 
@@ -386,19 +387,22 @@ db.command("types").description("Generate TypeScript types for the nightowls sch
386
387
  const out = await runDbTypes({ exec });
387
388
  process.stdout.write(out);
388
389
  });
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
+ 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").option("--dry-run", "preview the migration set without writing any files", false).action(async (opts) => {
390
391
  const cwd = process.cwd();
391
392
  const plugins = await discoverPlugins(nodeDiscoverDeps(cwd));
392
- const { written, skipped } = await runDbInstall({
393
+ const { written, skipped, dryRun } = await runDbInstall({
393
394
  plugins,
394
395
  root: cwd,
395
396
  out: opts.out,
396
397
  fs: await nodeFs(),
397
- now: () => /* @__PURE__ */ new Date()
398
+ now: () => /* @__PURE__ */ new Date(),
399
+ dryRun: opts.dryRun
398
400
  });
399
- for (const w of written) console.log(`wrote ${opts.out}/${w.file}`);
401
+ const verb = dryRun ? "would write" : "wrote";
402
+ for (const w of written) console.log(`${verb} ${opts.out}/${w.file}`);
400
403
  for (const version of skipped) console.log(`skipped ${version} (already installed)`);
401
404
  if (!written.length) console.log("Nothing to install \u2014 all nightowls migrations already present.");
405
+ else if (dryRun) console.log(`Dry run \u2014 ${written.length} migration(s) would be written to ${opts.out}/. Re-run without --dry-run to apply.`);
402
406
  });
403
407
  program.command("plugins").description("List installed Night Owls plugins and the commands they expose.").action(async () => {
404
408
  const cwd = process.cwd();
@@ -406,7 +410,7 @@ program.command("plugins").description("List installed Night Owls plugins and th
406
410
  runPluginsList(plugins, console.log);
407
411
  });
408
412
  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");
413
+ const { runMcpServer } = await import("./mcp-AX7MPTKC.js");
410
414
  await runMcpServer();
411
415
  });
412
416
  async function main() {
@@ -30,7 +30,7 @@ async function runMcpServer() {
30
30
  const server = buildMcpServer();
31
31
  const transport = new StdioServerTransport();
32
32
  await server.connect(transport);
33
- console.error("[nightowls] mcp server ready on stdio (nightowls_doctor)");
33
+ console.error("[@nightowlsdev/cli] mcp server ready on stdio (nightowls_doctor)");
34
34
  }
35
35
  export {
36
36
  buildMcpServer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nightowlsdev/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -36,21 +36,22 @@
36
36
  "tsup": "8.5.1",
37
37
  "typescript": "6.0.3",
38
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",
39
+ "@nightowlsdev/core": "0.5.0",
40
+ "@nightowlsdev/auth-supabase": "2.0.0",
41
+ "@nightowlsdev/storage-supabase": "2.0.0",
42
+ "@nightowlsdev/auth-auth0": "2.0.0",
43
+ "@nightowlsdev/telemetry-otel": "2.0.0",
44
+ "@nightowlsdev/runner-background": "2.0.0",
45
+ "@nightowlsdev/runner-nextjs": "2.0.0",
46
+ "@nightowlsdev/mcp": "2.0.0",
47
+ "@nightowlsdev/telemetry-langfuse": "2.0.0",
45
48
  "@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",
49
+ "@nightowlsdev/model-openai": "0.2.0",
50
+ "@nightowlsdev/model-openrouter": "0.2.0",
52
51
  "@nightowlsdev/model-vercel-gateway": "0.1.0",
53
- "@nightowlsdev/model-openrouter": "0.1.0"
52
+ "@nightowlsdev/tsconfig": "0.0.0",
53
+ "@nightowlsdev/eslint-config": "0.0.0",
54
+ "@nightowlsdev/model-ollama": "0.1.0"
54
55
  },
55
56
  "scripts": {
56
57
  "build": "tsup",