@intelligo-dev/cli 1.0.0-beta.13 → 1.0.0-beta.15

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.
Files changed (188) hide show
  1. package/README.md +65 -2
  2. package/dist/args.d.ts +10 -0
  3. package/dist/args.d.ts.map +1 -0
  4. package/dist/args.js +20 -0
  5. package/dist/args.js.map +1 -0
  6. package/dist/bin.js +103 -22
  7. package/dist/bin.js.map +1 -1
  8. package/dist/commands/add.d.ts +4 -0
  9. package/dist/commands/add.d.ts.map +1 -1
  10. package/dist/commands/add.js +28 -2
  11. package/dist/commands/add.js.map +1 -1
  12. package/dist/commands/create-flow.d.ts +8 -1
  13. package/dist/commands/create-flow.d.ts.map +1 -1
  14. package/dist/commands/create-flow.js +101 -34
  15. package/dist/commands/create-flow.js.map +1 -1
  16. package/dist/commands/create.d.ts +24 -4
  17. package/dist/commands/create.d.ts.map +1 -1
  18. package/dist/commands/create.js +57 -14
  19. package/dist/commands/create.js.map +1 -1
  20. package/dist/commands/doctor.d.ts +2 -0
  21. package/dist/commands/doctor.d.ts.map +1 -1
  22. package/dist/commands/doctor.js +123 -35
  23. package/dist/commands/doctor.js.map +1 -1
  24. package/dist/commands/migrate-check.d.ts +20 -1
  25. package/dist/commands/migrate-check.d.ts.map +1 -1
  26. package/dist/commands/migrate-check.js +35 -6
  27. package/dist/commands/migrate-check.js.map +1 -1
  28. package/dist/commands/migrate.d.ts.map +1 -1
  29. package/dist/commands/migrate.js +2 -8
  30. package/dist/commands/migrate.js.map +1 -1
  31. package/dist/commands/sync-messages.d.ts +37 -0
  32. package/dist/commands/sync-messages.d.ts.map +1 -0
  33. package/dist/commands/sync-messages.js +88 -0
  34. package/dist/commands/sync-messages.js.map +1 -0
  35. package/dist/commands/sync-scaffold.d.ts +36 -0
  36. package/dist/commands/sync-scaffold.d.ts.map +1 -0
  37. package/dist/commands/sync-scaffold.js +53 -0
  38. package/dist/commands/sync-scaffold.js.map +1 -0
  39. package/dist/commands/sync.d.ts +109 -0
  40. package/dist/commands/sync.d.ts.map +1 -0
  41. package/dist/commands/sync.js +392 -0
  42. package/dist/commands/sync.js.map +1 -0
  43. package/dist/commands/upgrade-check.d.ts.map +1 -1
  44. package/dist/commands/upgrade-check.js +4 -1
  45. package/dist/commands/upgrade-check.js.map +1 -1
  46. package/dist/env-files.d.ts +11 -0
  47. package/dist/env-files.d.ts.map +1 -1
  48. package/dist/env-files.js +28 -9
  49. package/dist/env-files.js.map +1 -1
  50. package/dist/manifest.d.ts +27 -0
  51. package/dist/manifest.d.ts.map +1 -1
  52. package/dist/manifest.js +30 -3
  53. package/dist/manifest.js.map +1 -1
  54. package/dist/module-exports.d.ts +63 -0
  55. package/dist/module-exports.d.ts.map +1 -0
  56. package/dist/module-exports.js +231 -0
  57. package/dist/module-exports.js.map +1 -0
  58. package/dist/registry-bundle.d.ts +43 -0
  59. package/dist/registry-bundle.d.ts.map +1 -0
  60. package/dist/registry-bundle.js +74 -0
  61. package/dist/registry-bundle.js.map +1 -0
  62. package/dist/registry-items.d.ts +41 -13
  63. package/dist/registry-items.d.ts.map +1 -1
  64. package/dist/registry-items.js +117 -33
  65. package/dist/registry-items.js.map +1 -1
  66. package/package.json +1 -1
  67. package/src/args.ts +25 -0
  68. package/src/bin.ts +124 -22
  69. package/src/commands/add.ts +39 -12
  70. package/src/commands/create-flow.ts +119 -29
  71. package/src/commands/create.ts +82 -14
  72. package/src/commands/doctor.ts +156 -47
  73. package/src/commands/migrate-check.ts +51 -10
  74. package/src/commands/migrate.ts +2 -8
  75. package/src/commands/sync-messages.ts +114 -0
  76. package/src/commands/sync-scaffold.ts +83 -0
  77. package/src/commands/sync.ts +574 -0
  78. package/src/commands/upgrade-check.ts +4 -1
  79. package/src/env-files.ts +28 -7
  80. package/src/manifest.ts +57 -3
  81. package/src/module-exports.ts +266 -0
  82. package/src/registry-bundle.ts +103 -0
  83. package/src/registry-items.ts +141 -36
  84. package/templates/admin-page/admin-page.tsx.tpl +96 -24
  85. package/templates/app-scaffold/gitignore.tpl +25 -0
  86. package/templates/app-scaffold/intelligo.ts.tpl +6 -47
  87. package/templates/app-scaffold/next.config.mjs.tpl +31 -2
  88. package/templates/app-scaffold/package.json.tpl +2 -2
  89. package/templates/manifest.json +40 -3
  90. package/templates/pnpm-standalone/npmrc.tpl +6 -0
  91. package/templates/pnpm-standalone/pnpm-workspace.yaml.tpl +10 -0
  92. package/templates/registry/ai-agent-activity.json +23 -0
  93. package/templates/registry/ai-agent-progress.json +21 -0
  94. package/templates/registry/ai-approval-card.json +27 -0
  95. package/templates/registry/ai-artifact.json +22 -0
  96. package/templates/registry/ai-branch.json +21 -0
  97. package/templates/registry/ai-citations.json +24 -0
  98. package/templates/registry/ai-code-block.json +23 -0
  99. package/templates/registry/ai-composer-menu.json +19 -0
  100. package/templates/registry/ai-file-diff.json +25 -0
  101. package/templates/registry/ai-image-generation.json +23 -0
  102. package/templates/registry/ai-markdown.json +23 -0
  103. package/templates/registry/ai-message-bubble.json +23 -0
  104. package/templates/registry/ai-message-scroller.json +22 -0
  105. package/templates/registry/ai-message.json +21 -0
  106. package/templates/registry/ai-motion.json +19 -0
  107. package/templates/registry/ai-prompt-input.json +28 -0
  108. package/templates/registry/ai-reasoning-text.json +22 -0
  109. package/templates/registry/ai-reasoning.json +22 -0
  110. package/templates/registry/ai-shimmer-text.json +19 -0
  111. package/templates/registry/ai-sidebar.json +25 -0
  112. package/templates/registry/ai-speech-input.json +21 -0
  113. package/templates/registry/ai-streaming-response.json +24 -0
  114. package/templates/registry/ai-suggestion.json +19 -0
  115. package/templates/registry/ai-todo-list.json +22 -0
  116. package/templates/registry/ai-tool-approval.json +26 -0
  117. package/templates/registry/ai-tool-result.json +25 -0
  118. package/templates/registry/alert-dialog.json +23 -0
  119. package/templates/registry/animated-list.json +21 -0
  120. package/templates/registry/app-shell.json +93 -0
  121. package/templates/registry/artifacts.json +82 -0
  122. package/templates/registry/attachment.json +22 -0
  123. package/templates/registry/auth-email-verification.json +39 -0
  124. package/templates/registry/auth-login.json +72 -0
  125. package/templates/registry/auth-password-reset.json +53 -0
  126. package/templates/registry/auth-signup.json +41 -0
  127. package/templates/registry/billing-settings.json +66 -0
  128. package/templates/registry/button.json +22 -0
  129. package/templates/registry/chat-eve.json +19 -0
  130. package/templates/registry/chat-panel.json +30 -0
  131. package/templates/registry/chat-share.json +42 -0
  132. package/templates/registry/chat-widget.json +34 -0
  133. package/templates/registry/chat.json +326 -0
  134. package/templates/registry/checkbox.json +22 -0
  135. package/templates/registry/checkout.json +40 -0
  136. package/templates/registry/collapsible.json +19 -0
  137. package/templates/registry/command.json +23 -0
  138. package/templates/registry/copy-button.json +21 -0
  139. package/templates/registry/dashboard.json +69 -0
  140. package/templates/registry/dialog.json +24 -0
  141. package/templates/registry/document-viewer.json +22 -0
  142. package/templates/registry/dropdown-menu.json +23 -0
  143. package/templates/registry/expandable-tabs.json +22 -0
  144. package/templates/registry/feature-gating.json +73 -0
  145. package/templates/registry/file-upload.json +24 -0
  146. package/templates/registry/hold-action-button.json +22 -0
  147. package/templates/registry/input-group.json +23 -0
  148. package/templates/registry/input.json +19 -0
  149. package/templates/registry/intelligo.json +187 -0
  150. package/templates/registry/invitation-accept.json +64 -0
  151. package/templates/registry/language-switcher.json +29 -0
  152. package/templates/registry/morphing-modal.json +24 -0
  153. package/templates/registry/notification-stack.json +24 -0
  154. package/templates/registry/notifications.json +87 -0
  155. package/templates/registry/onboarding.json +83 -0
  156. package/templates/registry/otp-input.json +22 -0
  157. package/templates/registry/page-header.json +15 -0
  158. package/templates/registry/payment-poll.json +58 -0
  159. package/templates/registry/popover-morph.json +22 -0
  160. package/templates/registry/popover.json +22 -0
  161. package/templates/registry/pricing.json +117 -0
  162. package/templates/registry/privacy-settings.json +65 -0
  163. package/templates/registry/profile-settings.json +68 -0
  164. package/templates/registry/progress.json +19 -0
  165. package/templates/registry/radio-group.json +22 -0
  166. package/templates/registry/registry.json +2960 -0
  167. package/templates/registry/route-error.json +65 -0
  168. package/templates/registry/select-morph.json +23 -0
  169. package/templates/registry/select.json +23 -0
  170. package/templates/registry/settings-shell.json +47 -0
  171. package/templates/registry/sheet.json +24 -0
  172. package/templates/registry/sidebar.json +28 -0
  173. package/templates/registry/smoke.json +34 -0
  174. package/templates/registry/spinner.json +16 -0
  175. package/templates/registry/stat-card.json +18 -0
  176. package/templates/registry/status-badge.json +18 -0
  177. package/templates/registry/switch.json +20 -0
  178. package/templates/registry/tabs.json +23 -0
  179. package/templates/registry/team-settings.json +97 -0
  180. package/templates/registry/textarea.json +16 -0
  181. package/templates/registry/tooltip.json +22 -0
  182. package/templates/registry/trial-banner.json +43 -0
  183. package/templates/registry/usage.json +84 -0
  184. package/templates/registry/workspace-settings.json +71 -0
  185. package/templates/registry-items.json +3 -3
  186. package/templates/registry-requires.json +30 -4
  187. package/templates/vitest/server-only.ts.tpl +7 -0
  188. package/templates/vitest/vitest.config.ts.tpl +37 -0
@@ -3,11 +3,10 @@
3
3
  * is, what else it needs installed first, and the commands that install
4
4
  * a selection into a fresh app.
5
5
  *
6
- * Installing is the shadcn CLI's job, not this package's — the items
7
- * are shadcn-schema JSON served at intelligo.dev/r, and the scaffold's
8
- * components.json already names that registry as `@intelligo`. What the
9
- * CLI adds is the order: an item that imports a sibling's files
10
- * (requires.json `items`) has to land after it.
6
+ * Installing goes through `intelligo sync` (commands/sync.ts): one
7
+ * `shadcn add` per item from the registry bundled with this CLI, in the
8
+ * order requires.json `items` implies — an item that imports a
9
+ * sibling's files lands after it.
11
10
  */
12
11
  import { existsSync, readFileSync } from "node:fs";
13
12
  import path from "node:path";
@@ -56,8 +55,16 @@ export function detectPackageManager(userAgent = process.env.npm_config_user_age
56
55
  const name = userAgent?.split("/")[0];
57
56
  return name === "npm" || name === "yarn" || name === "bun" ? name : "pnpm";
58
57
  }
59
- export function formatCommand(c) {
60
- return [c.command, ...c.args].join(" ");
58
+ /**
59
+ * The command as a reader pastes it from `from` (the app's directory):
60
+ * a command that runs elsewhere is wrapped in a `cd`.
61
+ */
62
+ export function formatCommand(c, from) {
63
+ const line = [c.command, ...c.args].join(" ");
64
+ if (!c.cwd || !from || path.resolve(c.cwd) === path.resolve(from)) {
65
+ return line;
66
+ }
67
+ return `(cd ${path.relative(from, c.cwd) || "."} && ${line})`;
61
68
  }
62
69
  const EXEC = {
63
70
  pnpm: ["pnpm", "exec"],
@@ -69,35 +76,112 @@ const EXEC = {
69
76
  export function hasLocalShadcn(appRoot) {
70
77
  return existsSync(path.join(appRoot, "node_modules", ".bin", "shadcn"));
71
78
  }
79
+ /** The `packages:` globs of a pnpm-workspace.yaml (`!` exclusions kept). */
80
+ export function workspaceGlobs(yaml) {
81
+ const globs = [];
82
+ let inPackages = false;
83
+ for (const raw of yaml.split(/\r?\n/)) {
84
+ const line = raw.replace(/\s+#.*$/, "");
85
+ const key = /^packages\s*:\s*(.*)$/.exec(line);
86
+ if (key) {
87
+ const flow = /^\[(.*)\]$/.exec(key[1].trim());
88
+ if (flow) {
89
+ for (const g of flow[1].split(",")) {
90
+ const glob = g.trim().replace(/^["']|["']$/g, "");
91
+ if (glob)
92
+ globs.push(glob);
93
+ }
94
+ }
95
+ else
96
+ inPackages = true;
97
+ continue;
98
+ }
99
+ if (!inPackages)
100
+ continue;
101
+ const entry = /^\s+-\s*(.+?)\s*$/.exec(line);
102
+ if (entry)
103
+ globs.push(entry[1].replace(/^["']|["']$/g, ""));
104
+ else if (/^\S/.test(line))
105
+ inPackages = false;
106
+ }
107
+ return globs;
108
+ }
109
+ function globToRegExp(glob) {
110
+ const pattern = glob
111
+ .replace(/^\.\//, "")
112
+ .replace(/\/+$/, "")
113
+ .split(/(\*\*|\*|\?)/)
114
+ .map((part) => part === "**"
115
+ ? ".*"
116
+ : part === "*"
117
+ ? "[^/]*"
118
+ : part === "?"
119
+ ? "[^/]"
120
+ : part.replace(/[.+^${}()|[\]\\]/g, "\\$&"))
121
+ .join("");
122
+ return new RegExp(`^${pattern}$`);
123
+ }
72
124
  /**
73
- * The commands that put `items` into the app at `appRoot`: install the
74
- * dependencies when shadcn is not there yet (the scaffold declares it),
75
- * then the design-system base, then the items in dependency order.
76
- * `--overwrite` because the base replaces the scaffold's globals.css,
77
- * and items that share a file agree on it — the same flags the
78
- * reference app is regenerated with.
125
+ * The root of the pnpm workspace that has `appRoot` as a member, if
126
+ * any: the nearest pnpm-workspace.yaml above the app, when one of its
127
+ * `packages` globs matches the app's path and no `!` glob excludes it.
128
+ * Installing from there updates the workspace's one lockfile instead of
129
+ * starting a nested one in the app.
130
+ */
131
+ export function findWorkspaceRoot(appRoot) {
132
+ const app = path.resolve(appRoot);
133
+ let dir = path.dirname(app);
134
+ for (;;) {
135
+ const file = path.join(dir, "pnpm-workspace.yaml");
136
+ if (existsSync(file)) {
137
+ const rel = path.relative(dir, app).split(path.sep).join("/");
138
+ const globs = workspaceGlobs(readFileSync(file, "utf8"));
139
+ const included = globs
140
+ .filter((g) => !g.startsWith("!"))
141
+ .some((g) => globToRegExp(g).test(rel));
142
+ const excluded = globs
143
+ .filter((g) => g.startsWith("!"))
144
+ .some((g) => globToRegExp(g.slice(1)).test(rel));
145
+ return included && !excluded ? dir : null;
146
+ }
147
+ const parent = path.dirname(dir);
148
+ if (parent === dir)
149
+ return null;
150
+ dir = parent;
151
+ }
152
+ }
153
+ /** The design-system base, installed before any page. */
154
+ const BASE = "intelligo";
155
+ /**
156
+ * How `items` get into the app at `appRoot`: the dependencies first
157
+ * when shadcn is missing, then `intelligo sync` of the base and every
158
+ * item — one `shadcn add` per item in dependency order, from the
159
+ * registry bundled with this CLI, with `--force` because a fresh
160
+ * scaffold's files (globals.css, the theme provider) are the registry's
161
+ * to replace. Null when nothing was picked.
79
162
  */
80
163
  export function installPlan(items, options) {
81
164
  if (items.length === 0)
82
- return [];
83
- const [command, ...exec] = EXEC[options.packageManager];
84
- const add = (names) => ({
85
- command: command,
86
- args: [
87
- ...exec,
88
- "shadcn",
89
- "add",
90
- ...names.map((n) => `@intelligo/${n}`),
91
- "--yes",
92
- "--overwrite",
93
- ],
94
- });
95
- return [
96
- ...(hasLocalShadcn(options.appRoot)
97
- ? []
98
- : [{ command: options.packageManager, args: ["install"] }]),
99
- add(["intelligo"]),
100
- add([...items]),
101
- ];
165
+ return null;
166
+ const packageManager = options.workspaceRoot
167
+ ? "pnpm"
168
+ : options.packageManager;
169
+ const [command, ...exec] = EXEC[packageManager];
170
+ const synced = [BASE, ...items.filter((n) => n !== BASE)];
171
+ return {
172
+ install: hasLocalShadcn(options.appRoot)
173
+ ? null
174
+ : {
175
+ command: packageManager,
176
+ args: ["install"],
177
+ cwd: options.workspaceRoot ?? options.appRoot,
178
+ },
179
+ items: synced,
180
+ sync: {
181
+ command: command,
182
+ args: [...exec, "intelligo", "sync", ...synced, "--force"],
183
+ cwd: options.appRoot,
184
+ },
185
+ };
102
186
  }
103
187
  //# sourceMappingURL=registry-items.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry-items.js","sourceRoot":"","sources":["../src/registry-items.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry-items.js","sourceRoot":"","sources":["../src/registry-items.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intelligo-dev/cli",
3
- "version": "1.0.0-beta.13",
3
+ "version": "1.0.0-beta.15",
4
4
  "description": "The Intelligo CLI: scaffold an AI SaaS app, generate owned source, and check its migrations and upgrades.",
5
5
  "keywords": [
6
6
  "intelligo",
package/src/args.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The flags a command accepts. A flag it does not know is an error
3
+ * rather than ignored: `sync --chek` must not apply, and `add --all`
4
+ * must not look as if it did something.
5
+ */
6
+
7
+ export function unknownFlags(
8
+ args: readonly string[],
9
+ allowed: readonly string[]
10
+ ): string[] {
11
+ return args.filter((arg) => arg.startsWith("-") && !allowed.includes(arg));
12
+ }
13
+
14
+ export function wantsHelp(args: readonly string[]): boolean {
15
+ return args.includes("--help") || args.includes("-h");
16
+ }
17
+
18
+ /** `a,b c` → `["a", "b", "c"]`: items as `create --items` takes them, or space-separated. */
19
+ export function itemNames(args: readonly string[]): string[] {
20
+ return args
21
+ .filter((arg) => !arg.startsWith("-"))
22
+ .flatMap((arg) => arg.split(","))
23
+ .map((name) => name.trim())
24
+ .filter(Boolean);
25
+ }
package/src/bin.ts CHANGED
@@ -35,13 +35,26 @@ import {
35
35
  applyMigrations,
36
36
  formatApplyResult,
37
37
  } from "./commands/migrate.js";
38
+ import {
39
+ formatSyncReport,
40
+ installedFrameworkVersion,
41
+ selectItems,
42
+ syncApply,
43
+ syncCheck,
44
+ syncCheckExitCode,
45
+ type SyncContext,
46
+ } from "./commands/sync.js";
38
47
  import {
39
48
  formatUpgradeReport,
40
49
  upgradeCheck,
41
50
  upgradeCheckExitCode,
42
51
  } from "./commands/upgrade-check.js";
43
52
 
53
+ import { itemNames, unknownFlags, wantsHelp } from "./args.js";
44
54
  import { loadAppEnv } from "./env-files.js";
55
+ import { workspaceRootVariable } from "./commands/create.js";
56
+ import { resolveRegistryDir } from "./registry-bundle.js";
57
+ import { findWorkspaceRoot, readRegistryCatalogue } from "./registry-items.js";
45
58
  import { MIGRATION_LOCATIONS, resolveMigrationsDir } from "./migrations-dir.js";
46
59
 
47
60
  /**
@@ -74,7 +87,7 @@ function usage(): string {
74
87
  "intelligo <command>",
75
88
  "",
76
89
  " create [dir] Scaffold an app, then install the registry pages you pick",
77
- " (--items a,b | --all, --yes, --no-install)",
90
+ " (--items a,b | --all, --yes, --no-install, --name <name>)",
78
91
  " doctor Report configuration and migration-chain problems",
79
92
  " migrate Apply the framework's migration chain to DATABASE_URL",
80
93
  " migrate --check Compare the framework's migrations to a database",
@@ -82,6 +95,10 @@ function usage(): string {
82
95
  " fresh | ahead | unmanaged | legacy)",
83
96
  " add <feature> Generate consumer-owned source (--force to overwrite)",
84
97
  " upgrade --check Show what a template upgrade would change",
98
+ " sync [items…] Install registry pages from this release's registry",
99
+ " (names space- or comma-separated),",
100
+ " keeping seams and merging messages (--force replaces",
101
+ " hand-edited files; --check only reports, exit 1 on drift)",
85
102
  "",
86
103
  ].join("\n");
87
104
  }
@@ -164,17 +181,72 @@ async function runMigrate(
164
181
  }
165
182
  }
166
183
 
184
+ function flagsOk(
185
+ command: string,
186
+ args: readonly string[],
187
+ allowed: readonly string[]
188
+ ): boolean {
189
+ const unknown = unknownFlags(args, allowed);
190
+ if (unknown.length === 0) return true;
191
+ console.error(
192
+ `intelligo ${command}: unknown argument ${unknown.join(" ")}` +
193
+ (allowed.length ? ` (it takes ${allowed.join(", ")}).` : ".")
194
+ );
195
+ return false;
196
+ }
197
+
198
+ function runAdd(rest: readonly string[]): number {
199
+ if (!flagsOk("add", rest, ["--force"])) return 2;
200
+ const feature = rest.find((a) => !a.startsWith("--"));
201
+ if (!feature) {
202
+ const catalogue = readCatalogue(TEMPLATES_DIR);
203
+ console.error("Usage: intelligo add <feature>\n");
204
+ for (const [name, spec] of Object.entries(catalogue)) {
205
+ console.error(` ${name.padEnd(16)} ${spec.description}`);
206
+ }
207
+ return 1;
208
+ }
209
+ if (feature === "pnpm-standalone" && findWorkspaceRoot(process.cwd())) {
210
+ console.error(
211
+ "intelligo add pnpm-standalone: this app is a member of a pnpm workspace, and a " +
212
+ "workspace file of its own would take it out; the workspace root's settings apply."
213
+ );
214
+ return 1;
215
+ }
216
+ const result = addFeature(feature, {
217
+ appRoot: process.cwd(),
218
+ templatesDir: TEMPLATES_DIR,
219
+ frameworkVersion: FRAMEWORK_VERSION,
220
+ force: rest.includes("--force"),
221
+ // Computed, not recorded: an app made before the variable existed
222
+ // has no value for it.
223
+ variables:
224
+ feature === "app-scaffold"
225
+ ? workspaceRootVariable(process.cwd())
226
+ : undefined,
227
+ });
228
+ console.log(formatAddResult(result));
229
+ return addExitCode(result);
230
+ }
231
+
167
232
  async function main(): Promise<number> {
168
233
  const [, , command = "help", ...rest] = process.argv;
169
234
 
235
+ if (wantsHelp(rest)) {
236
+ console.log(usage());
237
+ return 0;
238
+ }
239
+
170
240
  // The commands that read the app's configuration see what the app
171
- // itself would: its .env.local and .env, under anything the shell set.
241
+ // itself would: its .env.local and .env, then the pnpm workspace
242
+ // root's, under anything the shell set.
172
243
  if (command === "doctor" || command === "migrate" || command === "upgrade") {
173
244
  loadAppEnv(process.cwd());
174
245
  }
175
246
 
176
247
  switch (command) {
177
248
  case "doctor": {
249
+ if (!flagsOk("doctor", rest, [])) return 2;
178
250
  const results = runChecks();
179
251
  console.log(formatResults(results));
180
252
  return exitCodeFor(results);
@@ -203,32 +275,24 @@ async function main(): Promise<number> {
203
275
  // command that converses.
204
276
  const { parseCreateFlags, runCreate } =
205
277
  await import("./commands/create-flow.js");
206
- return runCreate(parseCreateFlags(rest), {
207
- templatesDir: TEMPLATES_DIR,
208
- frameworkVersion: FRAMEWORK_VERSION,
209
- interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
210
- });
211
- }
212
- case "add": {
213
- const feature = rest.find((a) => !a.startsWith("--"));
214
- if (!feature) {
215
- const catalogue = readCatalogue(TEMPLATES_DIR);
216
- console.error("Usage: intelligo add <feature>\n");
217
- for (const [name, spec] of Object.entries(catalogue)) {
218
- console.error(` ${name.padEnd(16)} ${spec.description}`);
219
- }
220
- return 1;
278
+ const flags = parseCreateFlags(rest);
279
+ if (flags.unknown.length > 0) {
280
+ console.error(
281
+ `intelligo create: unknown argument ${flags.unknown.join(" ")}.\n`
282
+ );
283
+ console.error(usage());
284
+ return 2;
221
285
  }
222
- const result = addFeature(feature, {
223
- appRoot: process.cwd(),
286
+ return runCreate(flags, {
224
287
  templatesDir: TEMPLATES_DIR,
225
288
  frameworkVersion: FRAMEWORK_VERSION,
226
- force: rest.includes("--force"),
289
+ interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
227
290
  });
228
- console.log(formatAddResult(result));
229
- return addExitCode(result);
230
291
  }
292
+ case "add":
293
+ return runAdd(rest);
231
294
  case "upgrade": {
295
+ if (!flagsOk("upgrade", rest, ["--check"])) return 2;
232
296
  if (!rest.includes("--check")) {
233
297
  console.error("Only `upgrade --check` is implemented.");
234
298
  console.error(
@@ -245,7 +309,45 @@ async function main(): Promise<number> {
245
309
  console.log(formatUpgradeReport(report));
246
310
  return upgradeCheckExitCode(report);
247
311
  }
312
+ case "sync": {
313
+ if (!flagsOk("sync", rest, ["--check", "--force"])) return 2;
314
+ const registryDir = resolveRegistryDir(TEMPLATES_DIR);
315
+ if (!registryDir) {
316
+ console.error(
317
+ "This CLI has no bundled registry — in the framework repository, run `pnpm registry:build` first."
318
+ );
319
+ return 1;
320
+ }
321
+ const context: SyncContext = {
322
+ appRoot: process.cwd(),
323
+ registryDir,
324
+ requires: readRegistryCatalogue(TEMPLATES_DIR).requires,
325
+ frameworkVersion: FRAMEWORK_VERSION,
326
+ };
327
+ const installed = installedFrameworkVersion(context.appRoot);
328
+ if (installed && installed !== FRAMEWORK_VERSION) {
329
+ console.error(
330
+ `! @intelligo-dev/core is ${installed} but this CLI carries the ${FRAMEWORK_VERSION} registry — ` +
331
+ "run the CLI of the same version (`pnpm exec intelligo`), or pages and packages will disagree."
332
+ );
333
+ }
334
+ const selection = selectItems(itemNames(rest), context);
335
+ if (!selection.ok) {
336
+ console.error(selection.message);
337
+ return 1;
338
+ }
339
+ if (rest.includes("--check")) {
340
+ const report = syncCheck(selection.items, context);
341
+ console.log(formatSyncReport(report));
342
+ return syncCheckExitCode(report);
343
+ }
344
+ return syncApply(selection.items, context, {
345
+ force: rest.includes("--force"),
346
+ });
347
+ }
248
348
  case "help":
349
+ case "--help":
350
+ case "-h":
249
351
  console.log(usage());
250
352
  return 0;
251
353
  default:
@@ -30,6 +30,8 @@ export type TemplateFeature = {
30
30
  deprecated?: string;
31
31
  files: TemplateFile[];
32
32
  cron?: TemplateCron;
33
+ /** What the developer still does by hand once the files are written. */
34
+ nextSteps?: string[];
33
35
  };
34
36
  export type TemplateCatalogue = Record<string, TemplateFeature>;
35
37
 
@@ -54,6 +56,8 @@ export type AddResult = {
54
56
  * app has a vercel.json without it, which is the consumer's to edit.
55
57
  */
56
58
  cron?: TemplateCron & { status: "written" | "present" | "manual" };
59
+ /** The catalogue's steps left to the developer, printed after the files. */
60
+ nextSteps?: string[];
57
61
  };
58
62
 
59
63
  export type AddOptions = {
@@ -81,6 +85,9 @@ export function substitute(
81
85
  );
82
86
  }
83
87
 
88
+ /** A `__NAME__` placeholder the variables did not fill. */
89
+ const PLACEHOLDER = /__[A-Z][A-Z0-9_]*__/;
90
+
84
91
  const VERCEL_CONFIG = "vercel.json";
85
92
 
86
93
  /**
@@ -137,16 +144,39 @@ export function addFeature(feature: string, options: AddOptions): AddResult {
137
144
  skippedUnknown: [],
138
145
  };
139
146
  if (spec.deprecated) result.deprecated = spec.deprecated;
147
+ if (spec.nextSteps?.length) result.nextSteps = spec.nextSteps;
140
148
  const recorded: GeneratedFile[] = [];
149
+ const handedOver = new Set(previous?.handedOver ?? []);
150
+
151
+ // Re-generating a feature substitutes what it was generated with, so
152
+ // `add app-scaffold` in an app `create` made takes the app's own name;
153
+ // values passed now fill in or replace them.
154
+ const variables =
155
+ previous?.variables || options.variables
156
+ ? { ...previous?.variables, ...options.variables }
157
+ : undefined;
141
158
 
159
+ // Checked before anything is written, so a refusal leaves no half-made
160
+ // feature behind.
142
161
  for (const file of spec.files) {
162
+ const unfilled = substitute(
163
+ readFileSync(path.join(options.templatesDir, file.template), "utf8"),
164
+ variables
165
+ ).match(PLACEHOLDER);
166
+ if (unfilled) {
167
+ throw new Error(
168
+ `${feature} needs a value for ${unfilled[0]} that only \`intelligo create\` supplies — ` +
169
+ "run `intelligo create <directory>`, or `intelligo create .` in an empty one."
170
+ );
171
+ }
172
+ }
173
+
174
+ for (const file of spec.files) {
175
+ // A registry item replaced it; `intelligo sync` keeps it now.
176
+ if (handedOver.has(file.target)) continue;
143
177
  const source = path.join(options.templatesDir, file.template);
144
178
  const target = path.join(options.appRoot, file.target);
145
- const contents = substitute(
146
- readFileSync(source, "utf8"),
147
- options.variables
148
- );
149
-
179
+ const contents = substitute(readFileSync(source, "utf8"), variables);
150
180
  if (existsSync(target) && !options.force) {
151
181
  const current = hashContents(readFileSync(target, "utf8"));
152
182
  const known = previousByPath.get(file.target);
@@ -182,13 +212,7 @@ export function addFeature(feature: string, options: AddOptions): AddResult {
182
212
 
183
213
  writeManifest(
184
214
  options.appRoot,
185
- recordFeature(
186
- manifest,
187
- feature,
188
- spec.templateVersion,
189
- recorded,
190
- options.variables
191
- )
215
+ recordFeature(manifest, feature, spec.templateVersion, recorded, variables)
192
216
  );
193
217
 
194
218
  return result;
@@ -212,6 +236,9 @@ export function formatAddResult(r: AddResult): string {
212
236
  )
213
237
  lines.push(" nothing to do");
214
238
  if (r.cron) lines.push(...cronNextSteps(r.cron));
239
+ if (r.nextSteps?.length) {
240
+ lines.push("", " Next:", ...r.nextSteps.map((step) => ` - ${step}`));
241
+ }
215
242
  return lines.join("\n");
216
243
  }
217
244