@novastorm-ai/cli 0.0.1 → 0.0.3

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.
@@ -1,3 +1,8 @@
1
+ import {
2
+ ConfigError,
3
+ DEFAULT_CONFIG
4
+ } from "./chunk-KKTDQOQX.js";
5
+
1
6
  // src/setup.ts
2
7
  import * as fs2 from "fs/promises";
3
8
  import * as path2 from "path";
@@ -8,10 +13,6 @@ import TOML2 from "@iarna/toml";
8
13
  import * as fs from "fs/promises";
9
14
  import * as path from "path";
10
15
  import TOML from "@iarna/toml";
11
- import {
12
- ConfigError,
13
- DEFAULT_CONFIG
14
- } from "@novastorm-ai/core";
15
16
  var NOVA_TOML = "nova.toml";
16
17
  var LOCAL_CONFIG_PATH = path.join(".nova", "config.toml");
17
18
  function deepMerge(target, source) {
@@ -170,7 +171,6 @@ var ConfigReader = class {
170
171
  };
171
172
 
172
173
  // src/setup.ts
173
- import { DEFAULT_CONFIG as DEFAULT_CONFIG2 } from "@novastorm-ai/core";
174
174
  var NOVA_DIR = ".nova";
175
175
  var LOCAL_CONFIG = "config.toml";
176
176
  async function runSetup(projectPath) {
@@ -219,7 +219,7 @@ Saved provider config to ${localConfigPath}`);
219
219
  const configReader = new ConfigReader();
220
220
  const exists = await configReader.exists(cwd);
221
221
  if (!exists) {
222
- await configReader.write(cwd, DEFAULT_CONFIG2);
222
+ await configReader.write(cwd, DEFAULT_CONFIG);
223
223
  console.log(`Created ${path2.join(cwd, "nova.toml")} with default configuration.`);
224
224
  }
225
225
  console.log("\nSetup complete!");
@@ -1,7 +1,31 @@
1
+ import {
2
+ DevServerRunner,
3
+ ProxyServer,
4
+ WebSocketServer
5
+ } from "./chunk-4AQQAQBM.js";
1
6
  import {
2
7
  ConfigReader,
3
8
  runSetup
4
- } from "./chunk-FYSTZ6K6.js";
9
+ } from "./chunk-QKD6A4EK.js";
10
+ import {
11
+ AgentPromptLoader,
12
+ Brain,
13
+ DEFAULT_CONFIG,
14
+ EnvDetector,
15
+ ExecutorPool,
16
+ GitManager,
17
+ Lane1Executor,
18
+ Lane2Executor,
19
+ Lane3Executor,
20
+ ManifestStore,
21
+ NovaDir,
22
+ NovaEventBus,
23
+ PathGuard,
24
+ ProjectIndexer,
25
+ ProjectScaffolder,
26
+ ProviderFactory,
27
+ SCAFFOLD_PRESETS
28
+ } from "./chunk-KKTDQOQX.js";
5
29
  import {
6
30
  __require
7
31
  } from "./chunk-3RG5ZIWI.js";
@@ -18,27 +42,157 @@ import * as path from "path";
18
42
  import chalk6 from "chalk";
19
43
  import ora2 from "ora";
20
44
  import { resolve as resolve2 } from "path";
21
- import {
22
- NovaEventBus,
23
- NovaDir,
24
- ProjectIndexer,
25
- Brain,
26
- ProviderFactory,
27
- ExecutorPool,
28
- Lane1Executor,
29
- Lane2Executor,
30
- GitManager,
31
- AgentPromptLoader,
32
- PathGuard,
33
- ManifestStore,
34
- EnvDetector
35
- } from "@novastorm-ai/core";
36
- import {
37
- DevServerRunner,
38
- ProxyServer,
39
- WebSocketServer
40
- } from "@novastorm-ai/proxy";
41
- import { LicenseChecker, Telemetry, NudgeRenderer } from "@novastorm-ai/licensing";
45
+
46
+ // ../licensing/dist/index.js
47
+ import { createHash } from "crypto";
48
+ import { execFile } from "child_process";
49
+ var DEFAULT_WINDOW_DAYS = 90;
50
+ var BOT_PATTERNS = [
51
+ /\[bot\]@/i,
52
+ /^dependabot/i,
53
+ /^renovate/i,
54
+ /^github-actions/i,
55
+ /noreply\.github\.com$/i
56
+ ];
57
+ function normalizeEmail(email) {
58
+ let normalized = email.toLowerCase().trim();
59
+ const atIndex = normalized.indexOf("@");
60
+ if (atIndex > 0) {
61
+ const local = normalized.slice(0, atIndex);
62
+ const domain = normalized.slice(atIndex);
63
+ const plusIndex = local.indexOf("+");
64
+ if (plusIndex > 0) {
65
+ normalized = local.slice(0, plusIndex) + domain;
66
+ }
67
+ }
68
+ return normalized;
69
+ }
70
+ function isBot(email) {
71
+ return BOT_PATTERNS.some((pattern) => pattern.test(email));
72
+ }
73
+ var TeamDetector = class {
74
+ detect(projectPath, options) {
75
+ const windowDays = options?.windowDays ?? DEFAULT_WINDOW_DAYS;
76
+ return new Promise((resolve4) => {
77
+ execFile(
78
+ "git",
79
+ ["log", "--format=%ae", `--since=${windowDays} days ago`],
80
+ { cwd: projectPath },
81
+ (error, stdout3) => {
82
+ if (error) {
83
+ resolve4({ devCount: 1, windowDays, botsFiltered: 0 });
84
+ return;
85
+ }
86
+ const rawEmails = stdout3.trim().split("\n").filter((line) => line.length > 0);
87
+ const normalizedEmails = rawEmails.map(normalizeEmail);
88
+ const humanEmails = /* @__PURE__ */ new Set();
89
+ const botEmails = /* @__PURE__ */ new Set();
90
+ for (const email of normalizedEmails) {
91
+ if (isBot(email)) {
92
+ botEmails.add(email);
93
+ } else {
94
+ humanEmails.add(email);
95
+ }
96
+ }
97
+ resolve4({
98
+ devCount: humanEmails.size === 0 ? 1 : humanEmails.size,
99
+ windowDays,
100
+ botsFiltered: botEmails.size
101
+ });
102
+ }
103
+ );
104
+ });
105
+ }
106
+ };
107
+ var FREE_DEV_LIMIT = 3;
108
+ var KEY_PATTERN = /^NOVA-([A-Z2-7]+)-([a-f0-9]{4})$/;
109
+ function computeChecksum(base32Body) {
110
+ return createHash("sha256").update(base32Body).digest("hex").slice(0, 4);
111
+ }
112
+ function validateKey(key) {
113
+ const match = KEY_PATTERN.exec(key);
114
+ if (!match) return false;
115
+ const [, body, checksum] = match;
116
+ return computeChecksum(body) === checksum;
117
+ }
118
+ var LicenseChecker = class {
119
+ teamDetector = new TeamDetector();
120
+ async check(projectPath, _config) {
121
+ const teamInfo = await this.teamDetector.detect(projectPath);
122
+ const devCount = teamInfo.devCount;
123
+ if (devCount <= FREE_DEV_LIMIT) {
124
+ return { valid: true, tier: "free", devCount };
125
+ }
126
+ const key = _config.license?.key ?? process.env["NOVA_LICENSE_KEY"] ?? "";
127
+ if (!key) {
128
+ return {
129
+ valid: false,
130
+ tier: "company",
131
+ devCount,
132
+ message: "Company license required: this project has more than 3 contributors. Set NOVA_LICENSE_KEY to continue."
133
+ };
134
+ }
135
+ if (!validateKey(key)) {
136
+ return {
137
+ valid: false,
138
+ tier: "company",
139
+ devCount,
140
+ message: "Invalid license key format. Expected NOVA-{BASE32}-{CHECKSUM}."
141
+ };
142
+ }
143
+ return { valid: true, tier: "company", devCount };
144
+ }
145
+ };
146
+ var TELEMETRY_ENDPOINT = "https://api.nova-architect.dev/v1/telemetry";
147
+ var TIMEOUT_MS = 3e3;
148
+ var Telemetry = class {
149
+ async send(payload) {
150
+ try {
151
+ const controller = new AbortController();
152
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
153
+ try {
154
+ const response = await fetch(TELEMETRY_ENDPOINT, {
155
+ method: "POST",
156
+ headers: { "Content-Type": "application/json" },
157
+ body: JSON.stringify(payload),
158
+ signal: controller.signal
159
+ });
160
+ if (response.ok) {
161
+ const data = await response.json();
162
+ return { nudgeLevel: data.nudge_level ?? 0 };
163
+ }
164
+ return null;
165
+ } finally {
166
+ clearTimeout(timeout);
167
+ }
168
+ } catch {
169
+ return null;
170
+ }
171
+ }
172
+ };
173
+ var NudgeRenderer = class {
174
+ render(context) {
175
+ switch (context.level) {
176
+ case 0:
177
+ return null;
178
+ case 1:
179
+ return "Nova Architect is free for teams of 3 or fewer. Learn more: https://nova-architect.dev/pricing";
180
+ case 2:
181
+ return `Your team has ${context.devCount} developers. A license is recommended. Visit https://nova-architect.dev/pricing`;
182
+ case 3:
183
+ return [
184
+ "+-------------------------------------------------+",
185
+ "| License Required |",
186
+ `| Your team of ${String(context.devCount).padEnd(3)} developers needs a |`,
187
+ "| commercial license. |",
188
+ "| -> https://nova-architect.dev/pricing |",
189
+ "+-------------------------------------------------+"
190
+ ].join("\n");
191
+ default:
192
+ return null;
193
+ }
194
+ }
195
+ };
42
196
 
43
197
  // src/logger.ts
44
198
  import chalk from "chalk";
@@ -92,7 +246,6 @@ import { readFile, writeFile } from "fs/promises";
92
246
  import { join } from "path";
93
247
  import { select, input } from "@inquirer/prompts";
94
248
  import { Separator } from "@inquirer/prompts";
95
- import { ProjectScaffolder, SCAFFOLD_PRESETS } from "@novastorm-ai/core";
96
249
  async function promptAndScaffold(projectPath) {
97
250
  console.log(
98
251
  chalk2.yellow("\nNo project detected.") + " What would you like to create?\n"
@@ -268,7 +421,6 @@ function mapDescriptionToCommand(desc) {
268
421
 
269
422
  // src/autofix.ts
270
423
  import chalk3 from "chalk";
271
- import { Lane3Executor } from "@novastorm-ai/core";
272
424
  var ERROR_PATTERNS = [
273
425
  /Module not found: Can't resolve '([^']+)'/,
274
426
  /Invalid src prop.*next\/image/i,
@@ -728,25 +880,25 @@ async function startCommand() {
728
880
  spinner.succeed(`License OK (${license.tier}, ${license.devCount} dev(s)).`);
729
881
  }
730
882
  if (config.telemetry.enabled && process.env["NOVA_TELEMETRY"] !== "false") {
731
- const { createHash } = await import("crypto");
883
+ const { createHash: createHash2 } = await import("crypto");
732
884
  const os = await import("os");
733
- const { execFile } = await import("child_process");
885
+ const { execFile: execFile2 } = await import("child_process");
734
886
  const mac = Object.values(os.networkInterfaces()).flat().find((i) => !i?.internal && i?.mac !== "00:00:00:00:00:00")?.mac ?? "";
735
- const machineId = createHash("sha256").update(os.hostname() + os.userInfo().username + mac).digest("hex");
887
+ const machineId = createHash2("sha256").update(os.hostname() + os.userInfo().username + mac).digest("hex");
736
888
  let projectHash;
737
889
  try {
738
890
  const remoteUrl = await new Promise((resolve4, reject) => {
739
- execFile("git", ["remote", "get-url", "origin"], { cwd }, (err, stdout3) => {
891
+ execFile2("git", ["remote", "get-url", "origin"], { cwd }, (err, stdout3) => {
740
892
  if (err) reject(err);
741
893
  else resolve4(stdout3.trim());
742
894
  });
743
895
  });
744
- projectHash = createHash("sha256").update(remoteUrl).digest("hex");
896
+ projectHash = createHash2("sha256").update(remoteUrl).digest("hex");
745
897
  } catch {
746
- projectHash = createHash("sha256").update(cwd).digest("hex");
898
+ projectHash = createHash2("sha256").update(cwd).digest("hex");
747
899
  }
748
900
  const telemetry = new Telemetry();
749
- const cliPkg = await import("./package-3YCVE5UE.js").catch(
901
+ const cliPkg = await import("./package-JK6SEE4M.js").catch(
750
902
  () => ({ default: { version: "0.0.1" } })
751
903
  );
752
904
  telemetry.send({
@@ -776,7 +928,7 @@ ${nudgeMessage}
776
928
  });
777
929
  }
778
930
  spinner.start("Detecting project...");
779
- const { StackDetector } = await import("@novastorm-ai/core");
931
+ const { StackDetector } = await import("./dist-EMATXD3M.js");
780
932
  const stackDetector = new StackDetector();
781
933
  let stack = await stackDetector.detectStack(cwd);
782
934
  let detectedDevCommand = await stackDetector.detectDevCommand(stack, cwd);
@@ -822,15 +974,15 @@ ${nudgeMessage}
822
974
  throw err;
823
975
  }
824
976
  spinner.succeed("Project indexed.");
825
- const { ProjectAnalyzer, RagIndexer, createEmbeddingService } = await import("@novastorm-ai/core");
826
- const { ProjectMapApi } = await import("@novastorm-ai/proxy");
977
+ const { ProjectAnalyzer, RagIndexer, createEmbeddingService } = await import("./dist-EMATXD3M.js");
978
+ const { ProjectMapApi } = await import("./dist-6FOBVQ63.js");
827
979
  const projectAnalyzer = new ProjectAnalyzer();
828
980
  spinner.start("Analyzing project structure...");
829
981
  const analysis = await projectAnalyzer.analyze(cwd, projectMap);
830
982
  spinner.succeed(`Project analyzed: ${analysis.fileCount} files, ${analysis.methods.length} methods.`);
831
983
  let ragIndexer = null;
832
984
  try {
833
- const { VectorStore } = await import("@novastorm-ai/core");
985
+ const { VectorStore } = await import("./dist-EMATXD3M.js");
834
986
  let embeddingProvider = "tfidf";
835
987
  let embeddingApiKey;
836
988
  let embeddingBaseUrl;
@@ -889,7 +1041,7 @@ ${nudgeMessage}
889
1041
  wsServer.start(httpServer);
890
1042
  }
891
1043
  proxyServer.setProjectMapApi(projectMapApi);
892
- const { GraphStore: GS, SearchRouter: SR } = await import("@novastorm-ai/core");
1044
+ const { GraphStore: GS, SearchRouter: SR } = await import("./dist-EMATXD3M.js");
893
1045
  const novaPath = novaDir.getPath(cwd);
894
1046
  const graphStoreForApi = new GS(novaPath);
895
1047
  const searchRouterForApi = new SR(graphStoreForApi);
@@ -913,7 +1065,7 @@ ${nudgeMessage}
913
1065
  }
914
1066
  if (!config.apiKeys.key && config.apiKeys.provider !== "ollama" && config.apiKeys.provider !== "claude-cli") {
915
1067
  console.log(chalk6.yellow("\nNo API key configured. Running setup...\n"));
916
- const { runSetup: runSetup2 } = await import("./setup-3KREUXRO.js");
1068
+ const { runSetup: runSetup2 } = await import("./setup-L5TRND4P.js");
917
1069
  await runSetup2(cwd);
918
1070
  const updatedConfig = await configReader.read(cwd);
919
1071
  config.apiKeys = updatedConfig.apiKeys;
@@ -1336,7 +1488,6 @@ async function chatCommand() {
1336
1488
  import * as path2 from "path";
1337
1489
  import { createInterface as createInterface2 } from "readline/promises";
1338
1490
  import { stdin, stdout } from "process";
1339
- import { DEFAULT_CONFIG } from "@novastorm-ai/core";
1340
1491
  async function initCommand() {
1341
1492
  const cwd = process.cwd();
1342
1493
  const configReader = new ConfigReader();
@@ -1422,10 +1573,9 @@ async function watchCommand() {
1422
1573
 
1423
1574
  // src/commands/license.ts
1424
1575
  import chalk7 from "chalk";
1425
- import { LicenseChecker as LicenseChecker2, TeamDetector } from "@novastorm-ai/licensing";
1426
- var KEY_PATTERN = /^NOVA-([A-Z2-7]+)-([a-f0-9]{4})$/;
1576
+ var KEY_PATTERN2 = /^NOVA-([A-Z2-7]+)-([a-f0-9]{4})$/;
1427
1577
  var VALIDATE_ENDPOINT = "https://api.nova-architect.dev/v1/license/validate";
1428
- var TIMEOUT_MS = 5e3;
1578
+ var TIMEOUT_MS2 = 5e3;
1429
1579
  async function licenseCommand(subcommand, key) {
1430
1580
  const cwd = process.cwd();
1431
1581
  const configReader = new ConfigReader();
@@ -1445,7 +1595,7 @@ async function licenseCommand(subcommand, key) {
1445
1595
  }
1446
1596
  }
1447
1597
  async function showStatus(cwd, config) {
1448
- const licenseChecker = new LicenseChecker2();
1598
+ const licenseChecker = new LicenseChecker();
1449
1599
  const teamDetector = new TeamDetector();
1450
1600
  const [license, teamInfo] = await Promise.all([
1451
1601
  licenseChecker.check(cwd, config),
@@ -1471,7 +1621,7 @@ async function showStatus(cwd, config) {
1471
1621
  console.log("");
1472
1622
  }
1473
1623
  async function activateKey(cwd, configReader, key) {
1474
- if (!KEY_PATTERN.test(key)) {
1624
+ if (!KEY_PATTERN2.test(key)) {
1475
1625
  console.error(chalk7.red("Invalid key format. Expected: NOVA-{BASE32}-{CHECKSUM}"));
1476
1626
  process.exit(1);
1477
1627
  }
@@ -1479,7 +1629,7 @@ async function activateKey(cwd, configReader, key) {
1479
1629
  let serverValid = true;
1480
1630
  try {
1481
1631
  const controller = new AbortController();
1482
- const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
1632
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS2);
1483
1633
  try {
1484
1634
  const response = await fetch(VALIDATE_ENDPOINT, {
1485
1635
  method: "POST",
@@ -1513,14 +1663,12 @@ async function activateKey(cwd, configReader, key) {
1513
1663
  import { createInterface as createInterface3 } from "readline/promises";
1514
1664
  import { stdin as stdin2, stdout as stdout2 } from "process";
1515
1665
  import chalk8 from "chalk";
1516
- import { ManifestStore as ManifestStore2 } from "@novastorm-ai/core";
1517
- import { NovaDir as NovaDir2 } from "@novastorm-ai/core";
1518
1666
  var SERVICE_TYPES = ["frontend", "backend", "worker", "gateway"];
1519
1667
  var ENTITY_TYPES = ["module", "external-service", "library", "shared-package"];
1520
1668
  async function entityCommand(subcommand, name) {
1521
1669
  const cwd = process.cwd();
1522
- const store = new ManifestStore2();
1523
- const novaDir = new NovaDir2();
1670
+ const store = new ManifestStore();
1671
+ const novaDir = new NovaDir();
1524
1672
  if (!novaDir.exists(cwd)) {
1525
1673
  await novaDir.init(cwd);
1526
1674
  }
@@ -1664,6 +1812,162 @@ async function entityRemove(cwd, store, name) {
1664
1812
  }
1665
1813
  }
1666
1814
 
1815
+ // src/commands/bible.ts
1816
+ import chalk9 from "chalk";
1817
+ var BIBLE_TEXT = `
1818
+ ${chalk9.cyan.bold("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
1819
+ ${chalk9.cyan.bold("\u2551")} ${chalk9.white.bold("[DOCUMENT_CLASS: MANIFESTO]")} ${chalk9.gray("[STATUS: DECLASSIFIED]")} ${chalk9.cyan.bold("\u2551")}
1820
+ ${chalk9.cyan.bold("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
1821
+
1822
+ ${chalk9.green.bold("Ambient Development")}
1823
+ ${chalk9.gray("A manifesto for a new approach to building software")}
1824
+ ${chalk9.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1825
+
1826
+ ${chalk9.cyan("PART I")} \u2014 ${chalk9.white.bold("The problem everyone is solving from the wrong end")}
1827
+
1828
+ We live in an era where AI can write code. GPT, Claude, Gemini, dozens
1829
+ of models \u2014 all generating functions, components, entire applications.
1830
+ Every month a new tool appears. Each one promises a revolution. Each
1831
+ one does the same thing \u2014 helps turn text into code faster.
1832
+
1833
+ And here's the paradox: ${chalk9.green("code was never the real bottleneck.")}
1834
+
1835
+ ${chalk9.gray("[RESEARCH] Bain 2025 \u2014 Where time actually goes")}
1836
+ ${chalk9.gray("Writing & testing code .......")} ${chalk9.dim("25-35%")}
1837
+ ${chalk9.green("Everything else .............")} ${chalk9.green("65-75%")}
1838
+ ${chalk9.gray("(Understanding, formulation, review, integration, deploy)")}
1839
+
1840
+ By speeding up code generation 10x, we only sped up the entire
1841
+ process by 20-30%.
1842
+
1843
+ ${chalk9.gray("Traditional")} \u2192 write code
1844
+ ${chalk9.gray("Vibe coding")} \u2192 write prompt
1845
+ ${chalk9.gray("Spec-driven")} \u2192 write spec
1846
+ ${chalk9.gray("Visual-first")} \u2192 click in special editor
1847
+ ${chalk9.green.bold("Ambient")} \u2192 ${chalk9.green("just use your app")}
1848
+
1849
+ ${chalk9.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1850
+
1851
+ ${chalk9.cyan("PART II")} \u2014 ${chalk9.white.bold("What is Ambient Development")}
1852
+
1853
+ ${chalk9.white.bold("Ambient Development")} \u2014 an approach to building software where the
1854
+ system continuously observes the application in use and builds it
1855
+ out across every level of the stack based on user behavior, voice
1856
+ commands, and visual cues.
1857
+
1858
+ ${chalk9.gray("\u266A Ambient Music")} \u2014 Creates atmosphere. Doesn't demand attention.
1859
+ ${chalk9.gray("\u25D0 Ambient Lighting")} \u2014 Creates space. You don't think about bulbs.
1860
+ ${chalk9.gray("\u25C8 Ambient Computing")} \u2014 Smart home, sensors. You live, it adapts.
1861
+ ${chalk9.green("\u2318 Ambient Dev")} \u2014 ${chalk9.green("You use the product. Development happens around you.")}
1862
+
1863
+ You stop switching between the role of user and the role of developer.
1864
+ ${chalk9.green("You are always the user. Development is ambient.")}
1865
+
1866
+ ${chalk9.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1867
+
1868
+ ${chalk9.cyan("PART III")} \u2014 ${chalk9.white.bold("Five principles")}
1869
+
1870
+ ${chalk9.green("01")} ${chalk9.white.bold("Usage as specification")}
1871
+ The best specification is not one written in a document. The best
1872
+ specification is a person's behavior inside the product.
1873
+
1874
+ ${chalk9.gray("[behavior]")} click on empty space \u2192 expects something there
1875
+ ${chalk9.gray("[behavior]")} repeat action 5x \u2192 needs automation
1876
+ ${chalk9.gray("[behavior]")} open page, leave in 1s \u2192 page doesn't deliver
1877
+ ${chalk9.green("[ambient]")} ${chalk9.green("behavior never lies. behavior is the spec.")}
1878
+
1879
+ ${chalk9.green("02")} ${chalk9.white.bold("Full stack vertical")}
1880
+ When you say "add a customers table with search," you don't mean
1881
+ "create a React component." You mean: I want to see my customers,
1882
+ and I want it to work.
1883
+
1884
+ ${chalk9.cyan("UI Component")} \u2195 ${chalk9.cyan("API Endpoint")} \u2195 ${chalk9.cyan("Database Query")} \u2195 ${chalk9.cyan("Migration")}
1885
+
1886
+ ${chalk9.green("03")} ${chalk9.white.bold("Three simultaneous modes")}
1887
+ ${chalk9.gray("PASSIVE")} \u{1F441} \u2014 Silently observes. Suggests improvements.
1888
+ ${chalk9.cyan("VOICE")} \u{1F3A4} \u2014 Say what you need without switching context.
1889
+ ${chalk9.yellow("VISUAL")} \u{1F446} \u2014 Click, circle, drag. Point and speak.
1890
+ ${chalk9.gray("All three work simultaneously. Not switches \u2014 layers.")}
1891
+
1892
+ ${chalk9.green("04")} ${chalk9.white.bold("Speed lanes")}
1893
+ ${chalk9.green("LANE 1")} <2s \u2014 CSS, texts, configs. No AI. Pattern matching.
1894
+ ${chalk9.cyan("LANE 2")} 10-30s \u2014 Single-file changes. Fast model.
1895
+ ${chalk9.yellow("LANE 3")} 1-5min \u2014 Multi-file features. Strong model.
1896
+ ${chalk9.red("LANE 4")} min-hrs \u2014 Background refactoring. Async.
1897
+
1898
+ ${chalk9.green("05")} ${chalk9.white.bold("Stack-agnostic")}
1899
+ ${chalk9.green("[scan]")} package.json \u2192 Next.js + TypeScript
1900
+ ${chalk9.green("[scan]")} .csproj \u2192 C# backend
1901
+ ${chalk9.green("[scan]")} docker-compose.yml \u2192 PostgreSQL
1902
+ ${chalk9.cyan("[ready]")} Stack detected. ${chalk9.green("Ambient mode activated.")}
1903
+
1904
+ ${chalk9.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1905
+
1906
+ ${chalk9.cyan("PART IV")} \u2014 ${chalk9.white.bold("What it looks like in practice")}
1907
+
1908
+ ${chalk9.gray("MORNING")}
1909
+ ${chalk9.gray("[you]")} open SaaS. check dashboard. "this table is slow."
1910
+ ${chalk9.cyan("[ambient]")} found: SELECT * without pagination
1911
+ ${chalk9.yellow("[lane 3]")} generating optimized query + pagination
1912
+ ${chalk9.green("[done]")} hot reload. table loads instantly. 2 minutes.
1913
+
1914
+ ${chalk9.gray("DAY")}
1915
+ ${chalk9.gray("[you]")} "Save button too small on mobile"
1916
+ ${chalk9.green("[lane 1]")} CSS injection. done. instant.
1917
+ ${chalk9.gray("[you]")} "Add timezone picker to project form"
1918
+ ${chalk9.cyan("[lane 2]")} component + API field. 20 seconds.
1919
+
1920
+ ${chalk9.gray("EVENING")}
1921
+ ${chalk9.green("[summary]")} 4 instant fixes | 2 fast changes | 1 feature
1922
+ ${chalk9.green("[result]")} ${chalk9.green("zero IDE. zero prompts. zero context switches.")}
1923
+
1924
+ ${chalk9.gray("NIGHT")}
1925
+ ${chalk9.gray("[you]")} "Refactor auth module \u2014 split into services"
1926
+ ${chalk9.yellow("[lane 4]")} background. agent running...
1927
+ ${chalk9.green("[morning]")} PR ready. all tests green.
1928
+
1929
+ ${chalk9.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1930
+
1931
+ ${chalk9.cyan("PART V")} \u2014 ${chalk9.white.bold("Who is this for")}
1932
+
1933
+ ${chalk9.green("\u2713")} Solo developer building a SaaS
1934
+ ${chalk9.green("\u2713")} Startup team of 2-5 shipping daily
1935
+ ${chalk9.green("\u2713")} Agency creating 10+ projects a year
1936
+ ${chalk9.green("\u2713")} CTO prototyping ideas before allocating the team
1937
+ ${chalk9.green("\u2713")} Enterprise teams looking for a multiplier
1938
+
1939
+ ${chalk9.gray("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}
1940
+
1941
+ ${chalk9.cyan("PART VI")} \u2014 ${chalk9.white.bold("The future")}
1942
+
1943
+ What happens when you remove the formulation step entirely?
1944
+
1945
+ ${chalk9.gray("Total time per task")} ${chalk9.green("\u2193 60-70%")}
1946
+ ${chalk9.gray("Context switches")} ${chalk9.green("\u2192 0")}
1947
+ ${chalk9.gray("Time-to-feedback")} ${chalk9.green("seconds, not hours")}
1948
+
1949
+ Code generation was step one. Ambient development is step two.
1950
+ Not "write code faster." ${chalk9.green.bold("Stop writing altogether.")}
1951
+
1952
+ ${chalk9.gray("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}
1953
+ ${chalk9.gray("Read the full version with infographics:")}
1954
+ ${chalk9.cyan("https://cli.novastorm.ai/bible/")}
1955
+
1956
+ ${chalk9.gray("GitHub:")} ${chalk9.cyan("https://github.com/novastorm-cli/nova")}
1957
+ ${chalk9.gray("npm:")} ${chalk9.cyan("npm install -g @novastorm-ai/cli")}
1958
+ ${chalk9.gray("X:")} ${chalk9.cyan("https://x.com/upranevich")}
1959
+ ${chalk9.gray("TG:")} ${chalk9.cyan("https://t.me/novastormcli")}
1960
+ ${chalk9.gray("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}
1961
+ `;
1962
+ async function bibleCommand(subcommand) {
1963
+ if (subcommand === "--read" || subcommand === "read" || !subcommand) {
1964
+ console.log(BIBLE_TEXT);
1965
+ } else {
1966
+ console.log(chalk9.yellow(`Unknown subcommand: ${subcommand}`));
1967
+ console.log(chalk9.gray("Usage: nova bible [--read]"));
1968
+ }
1969
+ }
1970
+
1667
1971
  // src/index.ts
1668
1972
  var __dirname = dirname(fileURLToPath(import.meta.url));
1669
1973
  var pkg = JSON.parse(
@@ -1721,6 +2025,9 @@ function createCli() {
1721
2025
  program.command("entity [subcommand] [name]").description("Manage manifest entities: nova entity <add|list|remove> [name]").action(async (subcommand, name) => {
1722
2026
  await entityCommand(subcommand, name);
1723
2027
  });
2028
+ program.command("bible [subcommand]").description("Read the Ambient Development manifesto: nova bible [--read]").action(async (subcommand) => {
2029
+ await bibleCommand(subcommand);
2030
+ });
1724
2031
  return program;
1725
2032
  }
1726
2033
  var BANNER = `\x1B[96m
@@ -0,0 +1,13 @@
1
+ import {
2
+ DevServerRunner,
3
+ ProjectMapApi,
4
+ ProxyServer,
5
+ WebSocketServer
6
+ } from "./chunk-4AQQAQBM.js";
7
+ import "./chunk-3RG5ZIWI.js";
8
+ export {
9
+ DevServerRunner,
10
+ ProjectMapApi,
11
+ ProxyServer,
12
+ WebSocketServer
13
+ };