@forgeailab/spark 0.1.3 → 0.3.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.
Files changed (146) hide show
  1. package/README.md +1 -15
  2. package/package.json +8 -9
  3. package/src/commands/add.ts +6 -17
  4. package/src/commands/check.ts +6 -37
  5. package/src/commands/info.ts +1 -8
  6. package/src/commands/preset.ts +1 -0
  7. package/src/internal/board.ts +503 -0
  8. package/src/internal/skill-utils.ts +233 -0
  9. package/src/internal/state.ts +89 -0
  10. package/src/io/board.ts +32 -68
  11. package/src/io/env.ts +8 -3
  12. package/src/io/files.ts +22 -7
  13. package/src/io/skills.ts +1 -1
  14. package/src/io/state.ts +1 -1
  15. package/packs/README.md +0 -132
  16. package/packs/ai-anthropic/files/app/api/ai/route.ts +0 -57
  17. package/packs/ai-anthropic/files/lib/anthropic.ts +0 -15
  18. package/packs/ai-anthropic/pack.toml +0 -32
  19. package/packs/ai-anthropic/skills/ai-feature-patterns/SKILL.md +0 -87
  20. package/packs/ai-anthropic/tasks.yaml +0 -9
  21. package/packs/ai-openai/files/app/api/ai-openai/route.ts +0 -55
  22. package/packs/ai-openai/files/lib/openai.ts +0 -21
  23. package/packs/ai-openai/pack.toml +0 -30
  24. package/packs/ai-openai/tasks.yaml +0 -9
  25. package/packs/analytics-posthog/files/components/PostHogProvider.tsx +0 -19
  26. package/packs/analytics-posthog/files/lib/posthog/client.ts +0 -20
  27. package/packs/analytics-posthog/files/lib/posthog/server.ts +0 -24
  28. package/packs/analytics-posthog/pack.toml +0 -35
  29. package/packs/analytics-posthog/tasks.yaml +0 -15
  30. package/packs/auth-better-auth/files/app/(auth)/login/page.tsx +0 -58
  31. package/packs/auth-better-auth/files/app/api/auth/[...all]/route.ts +0 -4
  32. package/packs/auth-better-auth/files/lib/auth.ts +0 -21
  33. package/packs/auth-better-auth/pack.toml +0 -32
  34. package/packs/auth-better-auth/tasks.yaml +0 -10
  35. package/packs/auth-better-auth-pg/files/app/api/auth/[...all]/route.ts +0 -4
  36. package/packs/auth-better-auth-pg/files/lib/auth.ts +0 -86
  37. package/packs/auth-better-auth-pg/pack.toml +0 -32
  38. package/packs/auth-better-auth-pg/tasks.yaml +0 -17
  39. package/packs/auth-supabase/files/app/(auth)/login/page.tsx +0 -64
  40. package/packs/auth-supabase/files/app/auth/callback/route.ts +0 -15
  41. package/packs/auth-supabase/files/middleware.ts +0 -41
  42. package/packs/auth-supabase/pack.toml +0 -34
  43. package/packs/auth-supabase/tasks.yaml +0 -10
  44. package/packs/db-postgres/files/compose/postgres.yml +0 -28
  45. package/packs/db-postgres/files/docker-compose.include.yml +0 -1
  46. package/packs/db-postgres/files/docker-compose.yml +0 -6
  47. package/packs/db-postgres/files/drizzle.config.ts +0 -10
  48. package/packs/db-postgres/files/lib/db/index.ts +0 -10
  49. package/packs/db-postgres/files/lib/db/schema.ts +0 -11
  50. package/packs/db-postgres/pack.toml +0 -53
  51. package/packs/db-postgres/tasks.yaml +0 -11
  52. package/packs/db-sqlite/files/drizzle.config.ts +0 -10
  53. package/packs/db-sqlite/files/lib/db.ts +0 -8
  54. package/packs/db-sqlite/files/lib/schema.ts +0 -13
  55. package/packs/db-sqlite/pack.toml +0 -34
  56. package/packs/db-sqlite/tasks.yaml +0 -6
  57. package/packs/db-supabase/files/lib/supabase/client.ts +0 -8
  58. package/packs/db-supabase/files/lib/supabase/server.ts +0 -27
  59. package/packs/db-supabase/pack.toml +0 -32
  60. package/packs/db-supabase/skills/supabase-patterns/SKILL.md +0 -82
  61. package/packs/db-supabase/tasks.yaml +0 -6
  62. package/packs/deploy-vercel/files/docs/deploy.md +0 -21
  63. package/packs/deploy-vercel/files/vercel.json +0 -4
  64. package/packs/deploy-vercel/pack.toml +0 -30
  65. package/packs/deploy-vercel/tasks.yaml +0 -14
  66. package/packs/docker-compose-dev/files/.env.docker.example +0 -2
  67. package/packs/docker-compose-dev/files/compose/redis.yml +0 -17
  68. package/packs/docker-compose-dev/files/docker-compose.include.yml +0 -1
  69. package/packs/docker-compose-dev/files/docker-compose.yml +0 -6
  70. package/packs/docker-compose-dev/pack.toml +0 -38
  71. package/packs/docker-compose-dev/tasks.yaml +0 -9
  72. package/packs/email-resend/files/app/api/email/test/route.ts +0 -38
  73. package/packs/email-resend/files/emails/welcome.tsx +0 -66
  74. package/packs/email-resend/files/lib/email.ts +0 -40
  75. package/packs/email-resend/pack.toml +0 -34
  76. package/packs/email-resend/tasks.yaml +0 -9
  77. package/packs/example/pack.toml +0 -69
  78. package/packs/payments-stripe/files/app/api/billing-portal/route.ts +0 -24
  79. package/packs/payments-stripe/files/app/api/checkout/route.ts +0 -58
  80. package/packs/payments-stripe/files/app/api/webhooks/stripe/route.ts +0 -84
  81. package/packs/payments-stripe/files/lib/stripe.ts +0 -60
  82. package/packs/payments-stripe/pack.toml +0 -49
  83. package/packs/payments-stripe/skills/stripe-patterns/SKILL.md +0 -93
  84. package/packs/payments-stripe/tasks.yaml +0 -16
  85. package/packs/sync-zero/files/components/ZeroProvider.tsx +0 -3
  86. package/packs/sync-zero/files/compose/zero-cache.yml +0 -26
  87. package/packs/sync-zero/files/docker-compose.include.yml +0 -1
  88. package/packs/sync-zero/files/docker-compose.yml +0 -6
  89. package/packs/sync-zero/files/lib/zero/client.ts +0 -18
  90. package/packs/sync-zero/files/lib/zero/schema.ts +0 -17
  91. package/packs/sync-zero/files/zero.config.ts +0 -26
  92. package/packs/sync-zero/pack.toml +0 -61
  93. package/packs/sync-zero/skills/zero-patterns/SKILL.md +0 -69
  94. package/packs/sync-zero/tasks.yaml +0 -16
  95. package/packs/testing-playwright/files/e2e/example.spec.ts +0 -7
  96. package/packs/testing-playwright/files/playwright.config.ts +0 -33
  97. package/packs/testing-playwright/pack.toml +0 -25
  98. package/packs/testing-playwright/tasks.yaml +0 -9
  99. package/packs/ui-shadcn/files/app/globals.css +0 -56
  100. package/packs/ui-shadcn/files/components/ui/button.tsx +0 -47
  101. package/packs/ui-shadcn/files/components/ui/card.tsx +0 -33
  102. package/packs/ui-shadcn/files/lib/utils.ts +0 -6
  103. package/packs/ui-shadcn/files/postcss.config.mjs +0 -7
  104. package/packs/ui-shadcn/files/tailwind.config.ts +0 -57
  105. package/packs/ui-shadcn/pack.toml +0 -44
  106. package/packs/ui-shadcn/skills/shadcn-dashboard-patterns/SKILL.md +0 -85
  107. package/packs/ui-shadcn/tasks.yaml +0 -6
  108. package/presets/docs-site.toml +0 -4
  109. package/presets/internal-tool.toml +0 -4
  110. package/presets/lean-saas.toml +0 -4
  111. package/presets/local-ai-mvp.toml +0 -4
  112. package/presets/saas-classic.toml +0 -4
  113. package/src/runtime-package.ts +0 -132
  114. package/templates/README.md +0 -43
  115. package/templates/astro/README.md +0 -3
  116. package/templates/astro/template.toml +0 -4
  117. package/templates/astro-starlight/README.md +0 -3
  118. package/templates/astro-starlight/template.toml +0 -4
  119. package/templates/nextjs/.ai/architecture.md +0 -13
  120. package/templates/nextjs/.ai/board.md +0 -7
  121. package/templates/nextjs/.ai/product-spec.md +0 -11
  122. package/templates/nextjs/.claude/skills/.gitkeep +0 -0
  123. package/templates/nextjs/.codex/skills/.gitkeep +0 -0
  124. package/templates/nextjs/AGENTS.md +0 -95
  125. package/templates/nextjs/CLAUDE.md +0 -3
  126. package/templates/nextjs/README.md +0 -20
  127. package/templates/nextjs/app/(app)/home/page.tsx +0 -43
  128. package/templates/nextjs/app/(app)/home/posts-panel.tsx +0 -83
  129. package/templates/nextjs/app/(app)/layout.tsx +0 -12
  130. package/templates/nextjs/app/(auth)/login/page.tsx +0 -97
  131. package/templates/nextjs/app/globals.css +0 -23
  132. package/templates/nextjs/app/layout.tsx +0 -20
  133. package/templates/nextjs/app/page.tsx +0 -39
  134. package/templates/nextjs/lib/auth-placeholder.ts +0 -21
  135. package/templates/nextjs/lib/posts-placeholder.ts +0 -30
  136. package/templates/nextjs/next.config.ts +0 -5
  137. package/templates/nextjs/package.json +0 -26
  138. package/templates/nextjs/postcss.config.mjs +0 -7
  139. package/templates/nextjs/spark.config.json +0 -4
  140. package/templates/nextjs/template.toml +0 -4
  141. package/templates/nextjs/tsconfig.json +0 -27
  142. package/templates/nextjs/types/post.ts +0 -13
  143. package/templates/one/README.md +0 -5
  144. package/templates/one/template.toml +0 -4
  145. package/templates/vite-react/README.md +0 -3
  146. package/templates/vite-react/template.toml +0 -4
package/README.md CHANGED
@@ -36,13 +36,6 @@ Print everything the install would do — files touched, env vars added, deps in
36
36
  spark info payments-stripe
37
37
  ```
38
38
 
39
- For **hybrid** packs (those with a `[runtime_package]` block in their manifest), `info` also prints:
40
-
41
- - `Install mode: hybrid` (vs `copy` for the rest)
42
- - `Runtime helper: <package> (range <X>, resolved <Y>)` — the npm name + manifest version range + the version currently installed in the project's `package.json` (or `not installed`).
43
-
44
- For copy packs `info` prints `Install mode: copy` and omits the helper line.
45
-
46
39
  ### `add <pack...> [--dry-run]`
47
40
 
48
41
  Resolve the requested packs against the registry + installed set + active scaffold, then apply. Idempotent: running it twice with the same args is a no-op.
@@ -58,13 +51,6 @@ The resolver enforces:
58
51
  - **Exclusivity.** Capabilities classified exclusive (`db`, `auth`, `payments`, `ui-kit`, `sync`) reject double-installs. Non-exclusive caps (`ai-sdk`, `analytics`, `email`, `blob-storage`, `e2e`, `deploy-target`, `local-runtime`) coexist.
59
52
  - **Scaffold compatibility.** A pack with `compatible_scaffolds = ["nextjs"]` refuses to install on a `vite-react` project.
60
53
 
61
- **Hybrid pack install.** When a pack declares `[runtime_package]`, the CLI adds the helper package to the same `bun add` batch as the pack's explicit runtime deps. Two resolution modes:
62
-
63
- - **Dev mode** — `SPARK_ROOT` is set and `${SPARK_ROOT}/libs/<helper>/` exists. The helper is linked via `file:` to the workspace path. Used by the reference app and `/tmp/spark-validate` smoke installs.
64
- - **Published mode** — otherwise. The helper is installed by `<npm-name>@<range>` from the manifest's `[runtime_package].version`.
65
-
66
- Either way the pack's `[dependencies].runtime` array MUST NOT also list the helper — the CLI handles it implicitly (see Decision 6 in the runtime-packages design doc).
67
-
68
54
  ### `preset <name>`
69
55
 
70
56
  Apply a named bundle from `presets/<name>.toml`. Refuses if the active scaffold is not in the preset's `compatible_scaffolds`.
@@ -75,7 +61,7 @@ spark preset lean-saas
75
61
 
76
62
  ### `check`
77
63
 
78
- Audit `.spark/state.json` against the filesystem. Reports missing files, missing env vars in `.env.local`, deleted seeded tasks. For each installed **hybrid** pack, also verifies the helper package is still listed in the consumer's `package.json` (under `dependencies` or `devDependencies`) — surfaces a `drift: helper packages` section when a helper has been removed manually. **Does not repair.**
64
+ Audit `.spark/state.json` against the filesystem. Reports missing files, missing env vars in `.env.local`, and deleted seeded tasks. **Does not repair.**
79
65
 
80
66
  ```bash
81
67
  spark check
package/package.json CHANGED
@@ -1,17 +1,19 @@
1
1
  {
2
2
  "name": "@forgeailab/spark",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for managing feature packs in an spark-scaffolded project.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/ForgeAILab/spark.git",
8
+ "directory": "packages/spark"
9
+ },
5
10
  "type": "module",
6
11
  "publishConfig": {
7
12
  "access": "public"
8
13
  },
9
14
  "files": [
10
15
  "src",
11
- "README.md",
12
- "packs",
13
- "presets",
14
- "templates"
16
+ "README.md"
15
17
  ],
16
18
  "bin": {
17
19
  "spark": "./src/cli.ts"
@@ -21,10 +23,7 @@
21
23
  "typecheck": "tsc --noEmit"
22
24
  },
23
25
  "dependencies": {
24
- "@forgeailab/spark-schema": "^0.1.3",
25
- "@forgeailab/spark-state": "^0.1.3",
26
- "@forgeailab/spark-skill-utils": "^0.1.3",
27
- "@forgeailab/spark-board": "^0.1.3",
26
+ "@forgeailab/spark-schema": "0.3.0",
28
27
  "@clack/prompts": "latest",
29
28
  "citty": "latest",
30
29
  "picocolors": "latest",
@@ -17,7 +17,6 @@ import { readRegistry, type Registry } from '../io/registry.ts';
17
17
  import { addInstalledPack, installedPackNames, readState, writeState } from '../io/state.ts';
18
18
  import { seedBoardTasks } from '../io/board.ts';
19
19
  import { copyPackSkills } from '../io/skills.ts';
20
- import { assertRuntimeHelperNotRedeclared, resolveRuntimeHelper } from '../runtime-package.ts';
21
20
 
22
21
  type AddOutput = Pick<Console, 'log' | 'error'>;
23
22
 
@@ -66,20 +65,8 @@ function formatResolverError(error: ResolverError): string {
66
65
  return `Unknown pack: ${error.pack}`;
67
66
  }
68
67
 
69
- async function packRuntimeDependencies(plan: InstallPlan): Promise<string[]> {
70
- const dependencies: string[] = [];
71
-
72
- for (const pack of plan.packs) {
73
- assertRuntimeHelperNotRedeclared(pack.name, pack.manifest);
74
- dependencies.push(...(pack.manifest.dependencies?.runtime ?? []));
75
-
76
- const helper = await resolveRuntimeHelper(pack.manifest);
77
- if (helper) {
78
- dependencies.push(helper);
79
- }
80
- }
81
-
82
- return dependencies;
68
+ function packRuntimeDependencies(plan: InstallPlan): string[] {
69
+ return plan.packs.flatMap((pack) => pack.manifest.dependencies?.runtime ?? []);
83
70
  }
84
71
 
85
72
  function packDevDependencies(plan: InstallPlan): string[] {
@@ -182,11 +169,13 @@ function stateEntryForPack(
182
169
  skillFiles: readonly string[],
183
170
  touchedEnvFiles: readonly string[],
184
171
  ): StateInstalledPack {
172
+ const now = new Date();
173
+ const packInstallChange = `pack-install-${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, '0')}-${String(now.getUTCDate()).padStart(2, '0')}`;
185
174
  const fileTargets = [
186
175
  ...fileRecords.map((record) => record.to),
187
176
  ...skillFiles,
188
177
  ...touchedEnvFiles,
189
- ...(taskIds.length > 0 ? ['.ai/board.md'] : []),
178
+ ...(taskIds.length > 0 ? [`docs/spark/changes/${packInstallChange}/tasks.md`] : []),
190
179
  ];
191
180
 
192
181
  return {
@@ -235,7 +224,7 @@ export async function runAdd(requestedPacks: readonly string[], options: AddOpti
235
224
  };
236
225
  }
237
226
 
238
- const runtimeDependencies = await packRuntimeDependencies(plan);
227
+ const runtimeDependencies = packRuntimeDependencies(plan);
239
228
  const devDependencies = packDevDependencies(plan);
240
229
 
241
230
  if (options.dryRun) {
@@ -2,11 +2,10 @@ import { readFile, stat } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { defineCommand } from 'citty';
4
4
  import pc from 'picocolors';
5
- import { readConfig, type AppSkillsConfig } from '../config.ts';
5
+ import type { AppSkillsConfig } from '../config.ts';
6
6
  import { missingBoardTasks } from '../io/board.ts';
7
- import { readRegistry, type Registry } from '../io/registry.ts';
7
+ import type { Registry } from '../io/registry.ts';
8
8
  import { readState } from '../io/state.ts';
9
- import { installedRuntimeHelperSpecifier } from '../runtime-package.ts';
10
9
 
11
10
  type CheckOutput = Pick<Console, 'log' | 'error'>;
12
11
 
@@ -14,7 +13,6 @@ export type DriftReport = {
14
13
  missingFiles: string[];
15
14
  missingEnv: string[];
16
15
  missingTasks: string[];
17
- missingHelpers: string[];
18
16
  };
19
17
 
20
18
  export type CheckOptions = {
@@ -56,13 +54,9 @@ async function readEnvLocal(projectRoot: string): Promise<string> {
56
54
  export async function runCheck(
57
55
  projectRoot = process.cwd(),
58
56
  output: CheckOutput = console,
59
- options: CheckOptions = {},
57
+ _options: CheckOptions = {},
60
58
  ): Promise<DriftReport> {
61
- const [, registry, state] = await Promise.all([
62
- options.config ? Promise.resolve(options.config) : readConfig(projectRoot),
63
- options.registry ? Promise.resolve(options.registry) : readRegistry(projectRoot),
64
- readState(projectRoot),
65
- ]);
59
+ const state = await readState(projectRoot);
66
60
  const recordedFiles = [
67
61
  ...new Set(state.installed_packs.flatMap((pack) => pack.files)),
68
62
  ].sort();
@@ -79,33 +73,17 @@ export async function runCheck(
79
73
  const envLocal = await readEnvLocal(projectRoot);
80
74
  const missingEnv = recordedEnv.filter((key) => !hasEnvVar(envLocal, key));
81
75
  const missingTasks = await missingBoardTasks(projectRoot, recordedTasks);
82
- const missingHelpers: string[] = [];
83
-
84
- for (const pack of state.installed_packs) {
85
- const runtimePackage = registry.packs.get(pack.name)?.manifest.runtime_package;
86
- if (!runtimePackage) {
87
- continue;
88
- }
89
-
90
- if (!(await installedRuntimeHelperSpecifier(projectRoot, runtimePackage))) {
91
- missingHelpers.push(
92
- `${pack.name}: helper package ${runtimePackage.package} missing from package.json`,
93
- );
94
- }
95
- }
96
76
 
97
77
  if (
98
78
  missingFiles.length === 0 &&
99
79
  missingEnv.length === 0 &&
100
- missingTasks.length === 0 &&
101
- missingHelpers.length === 0
80
+ missingTasks.length === 0
102
81
  ) {
103
82
  output.log(pc.green('OK: spark state matches the project filesystem.'));
104
83
  return {
105
84
  missingFiles,
106
85
  missingEnv,
107
86
  missingTasks,
108
- missingHelpers,
109
87
  };
110
88
  }
111
89
 
@@ -132,18 +110,10 @@ export async function runCheck(
132
110
  }
133
111
  }
134
112
 
135
- if (missingHelpers.length > 0) {
136
- output.error('drift: helper packages');
137
- for (const helper of missingHelpers) {
138
- output.error(` ${helper}`);
139
- }
140
- }
141
-
142
113
  return {
143
114
  missingFiles,
144
115
  missingEnv,
145
116
  missingTasks,
146
- missingHelpers,
147
117
  };
148
118
  }
149
119
 
@@ -157,8 +127,7 @@ export const checkCommand = defineCommand({
157
127
  if (
158
128
  report.missingFiles.length > 0 ||
159
129
  report.missingEnv.length > 0 ||
160
- report.missingTasks.length > 0 ||
161
- report.missingHelpers.length > 0
130
+ report.missingTasks.length > 0
162
131
  ) {
163
132
  process.exitCode = 1;
164
133
  }
@@ -3,7 +3,6 @@ import pc from 'picocolors';
3
3
  import { readConfig, type AppSkillsConfig } from '../config.ts';
4
4
  import { readRegistry, type Registry } from '../io/registry.ts';
5
5
  import { installedPackNames, readState } from '../io/state.ts';
6
- import { formatResolvedRuntimeHelper } from '../runtime-package.ts';
7
6
 
8
7
  type InfoOutput = Pick<Console, 'log'>;
9
8
 
@@ -50,13 +49,7 @@ export async function runInfo(
50
49
  output.log(pc.bold(`${manifest.name}@${manifest.version}`));
51
50
  output.log(manifest.description ?? '');
52
51
  output.log(`status: ${installed ? 'installed' : 'available'}`);
53
- output.log(`Install mode: ${manifest.runtime_package ? 'hybrid' : 'copy'}`);
54
- if (manifest.runtime_package) {
55
- const resolved = await formatResolvedRuntimeHelper(projectRoot, manifest.runtime_package);
56
- output.log(
57
- `Runtime helper: ${manifest.runtime_package.package} (range ${manifest.runtime_package.version}, resolved ${resolved})`,
58
- );
59
- }
52
+ output.log('Install mode: copy');
60
53
  output.log(`category: ${manifest.category}`);
61
54
  output.log(`provides: ${formatList(manifest.provides)}`);
62
55
  output.log(`requires: ${formatList(manifest.requires)}`);
@@ -27,6 +27,7 @@ export async function runPreset(
27
27
  projectRoot,
28
28
  registry,
29
29
  config,
30
+ yes: true,
30
31
  });
31
32
  }
32
33