@nextsparkjs/ai-workflow 0.1.0-beta.100

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/claude/_docs/workflows-optimizations.md +359 -0
  4. package/claude/agents/api-tester.md +634 -0
  5. package/claude/agents/architecture-supervisor.md +1351 -0
  6. package/claude/agents/backend-developer.md +997 -0
  7. package/claude/agents/backend-validator.md +417 -0
  8. package/claude/agents/bdd-docs-writer.md +737 -0
  9. package/claude/agents/block-developer.md +677 -0
  10. package/claude/agents/code-reviewer.md +1432 -0
  11. package/claude/agents/db-developer.md +721 -0
  12. package/claude/agents/db-validator.md +407 -0
  13. package/claude/agents/demo-video-generator.md +493 -0
  14. package/claude/agents/documentation-writer.md +1268 -0
  15. package/claude/agents/frontend-developer.md +1234 -0
  16. package/claude/agents/frontend-validator.md +777 -0
  17. package/claude/agents/functional-validator.md +630 -0
  18. package/claude/agents/mock-analyst.md +387 -0
  19. package/claude/agents/product-manager.md +963 -0
  20. package/claude/agents/qa-automation.md +1762 -0
  21. package/claude/agents/release-manager.md +634 -0
  22. package/claude/agents/selectors-translator.md +262 -0
  23. package/claude/agents/unit-test-writer.md +785 -0
  24. package/claude/agents/visual-comparator.md +329 -0
  25. package/claude/agents/workflow-maintainer.md +352 -0
  26. package/claude/commands/do/README.md +88 -0
  27. package/claude/commands/do/create-api.md +64 -0
  28. package/claude/commands/do/create-entity.md +66 -0
  29. package/claude/commands/do/create-migration.md +64 -0
  30. package/claude/commands/do/create-plugin.md +56 -0
  31. package/claude/commands/do/create-theme.md +70 -0
  32. package/claude/commands/do/mock-data.md +67 -0
  33. package/claude/commands/do/reset-db.md +71 -0
  34. package/claude/commands/do/setup-scheduled-action.md +75 -0
  35. package/claude/commands/do/sync-code-review.md +117 -0
  36. package/claude/commands/do/update-selectors.md +112 -0
  37. package/claude/commands/do/use-skills.md +90 -0
  38. package/claude/commands/do/validate-blocks.md +69 -0
  39. package/claude/commands/how-to/README.md +261 -0
  40. package/claude/commands/how-to/add-metadata.md +692 -0
  41. package/claude/commands/how-to/add-taxonomies.md +806 -0
  42. package/claude/commands/how-to/add-translations.md +571 -0
  43. package/claude/commands/how-to/create-api.md +577 -0
  44. package/claude/commands/how-to/create-block.md +575 -0
  45. package/claude/commands/how-to/create-child-entities.md +771 -0
  46. package/claude/commands/how-to/create-entity.md +597 -0
  47. package/claude/commands/how-to/create-migrations.md +605 -0
  48. package/claude/commands/how-to/create-plugin.md +654 -0
  49. package/claude/commands/how-to/customize-app.md +481 -0
  50. package/claude/commands/how-to/customize-dashboard.md +553 -0
  51. package/claude/commands/how-to/customize-theme.md +438 -0
  52. package/claude/commands/how-to/define-features-flows.md +632 -0
  53. package/claude/commands/how-to/deploy.md +507 -0
  54. package/claude/commands/how-to/handle-file-uploads.md +746 -0
  55. package/claude/commands/how-to/implement-search.md +1001 -0
  56. package/claude/commands/how-to/install-plugins.md +352 -0
  57. package/claude/commands/how-to/manage-test-coverage.md +984 -0
  58. package/claude/commands/how-to/run-tests.md +400 -0
  59. package/claude/commands/how-to/set-app-languages.md +601 -0
  60. package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
  61. package/claude/commands/how-to/set-scheduled-actions.md +527 -0
  62. package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
  63. package/claude/commands/how-to/setup-authentication.md +388 -0
  64. package/claude/commands/how-to/setup-claude-code.md +440 -0
  65. package/claude/commands/how-to/setup-database.md +274 -0
  66. package/claude/commands/how-to/setup-email-providers.md +598 -0
  67. package/claude/commands/how-to/setup-mobile-dev.md +627 -0
  68. package/claude/commands/how-to/start.md +500 -0
  69. package/claude/commands/how-to/use-devtools.md +639 -0
  70. package/claude/commands/how-to/use-superadmin.md +622 -0
  71. package/claude/commands/session/README.md +193 -0
  72. package/claude/commands/session/block-create.md +190 -0
  73. package/claude/commands/session/block-list.md +203 -0
  74. package/claude/commands/session/block-update.md +192 -0
  75. package/claude/commands/session/block-validate.md +218 -0
  76. package/claude/commands/session/changelog.md +115 -0
  77. package/claude/commands/session/close.md +225 -0
  78. package/claude/commands/session/commit.md +174 -0
  79. package/claude/commands/session/db-entity.md +206 -0
  80. package/claude/commands/session/db-fix.md +212 -0
  81. package/claude/commands/session/db-sample.md +206 -0
  82. package/claude/commands/session/demo.md +178 -0
  83. package/claude/commands/session/doc-bdd.md +207 -0
  84. package/claude/commands/session/doc-feature.md +218 -0
  85. package/claude/commands/session/doc-read.md +225 -0
  86. package/claude/commands/session/execute.md +204 -0
  87. package/claude/commands/session/explain.md +202 -0
  88. package/claude/commands/session/fix-bug.md +210 -0
  89. package/claude/commands/session/fix-build.md +182 -0
  90. package/claude/commands/session/fix-test.md +189 -0
  91. package/claude/commands/session/pending.md +232 -0
  92. package/claude/commands/session/refine.md +188 -0
  93. package/claude/commands/session/resume.md +192 -0
  94. package/claude/commands/session/review.md +192 -0
  95. package/claude/commands/session/scope-change.md +181 -0
  96. package/claude/commands/session/start-blocks.md +347 -0
  97. package/claude/commands/session/start.md +604 -0
  98. package/claude/commands/session/status.md +169 -0
  99. package/claude/commands/session/test-fix.md +221 -0
  100. package/claude/commands/session/test-run.md +203 -0
  101. package/claude/commands/session/test-write.md +242 -0
  102. package/claude/commands/session/validate.md +162 -0
  103. package/claude/config/context.json +40 -0
  104. package/claude/config/github.json +69 -0
  105. package/claude/config/github.schema.json +106 -0
  106. package/claude/config/team.json +46 -0
  107. package/claude/config/team.schema.json +106 -0
  108. package/claude/config/workspace.json +43 -0
  109. package/claude/config/workspace.schema.json +75 -0
  110. package/claude/skills/README.md +228 -0
  111. package/claude/skills/accessibility/SKILL.md +573 -0
  112. package/claude/skills/api-bypass-layers/SKILL.md +550 -0
  113. package/claude/skills/asana-integration/SKILL.md +499 -0
  114. package/claude/skills/better-auth/SKILL.md +666 -0
  115. package/claude/skills/billing-subscriptions/SKILL.md +660 -0
  116. package/claude/skills/block-decision-matrix/SKILL.md +359 -0
  117. package/claude/skills/clickup-integration/SKILL.md +434 -0
  118. package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
  119. package/claude/skills/create-plugin/SKILL.md +425 -0
  120. package/claude/skills/create-theme/SKILL.md +331 -0
  121. package/claude/skills/cypress-api/SKILL.md +511 -0
  122. package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
  123. package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
  124. package/claude/skills/cypress-e2e/SKILL.md +526 -0
  125. package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
  126. package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
  127. package/claude/skills/cypress-selectors/SKILL.md +309 -0
  128. package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
  129. package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
  130. package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
  131. package/claude/skills/database-migrations/SKILL.md +335 -0
  132. package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
  133. package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
  134. package/claude/skills/design-system/SKILL.md +682 -0
  135. package/claude/skills/documentation/SKILL.md +540 -0
  136. package/claude/skills/entity-api/SKILL.md +482 -0
  137. package/claude/skills/entity-system/SKILL.md +635 -0
  138. package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
  139. package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
  140. package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
  141. package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
  142. package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
  143. package/claude/skills/github/SKILL.md +467 -0
  144. package/claude/skills/i18n-nextintl/SKILL.md +302 -0
  145. package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
  146. package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
  147. package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
  148. package/claude/skills/impact-analysis/SKILL.md +203 -0
  149. package/claude/skills/jest-unit/SKILL.md +306 -0
  150. package/claude/skills/jest-unit/references/component-testing.md +371 -0
  151. package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
  152. package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
  153. package/claude/skills/jira-integration/SKILL.md +539 -0
  154. package/claude/skills/media-library/SKILL.md +743 -0
  155. package/claude/skills/mock-analysis/SKILL.md +276 -0
  156. package/claude/skills/monorepo-architecture/SKILL.md +162 -0
  157. package/claude/skills/nextjs-api-development/SKILL.md +364 -0
  158. package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
  159. package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
  160. package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
  161. package/claude/skills/notion-integration/SKILL.md +641 -0
  162. package/claude/skills/npm-development-workflow/SKILL.md +480 -0
  163. package/claude/skills/page-builder-blocks/SKILL.md +530 -0
  164. package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
  165. package/claude/skills/permissions-system/SKILL.md +619 -0
  166. package/claude/skills/plugins/SKILL.md +340 -0
  167. package/claude/skills/plugins/references/plugin-templates.md +414 -0
  168. package/claude/skills/plugins/references/plugin-testing.md +353 -0
  169. package/claude/skills/plugins/references/plugin-types.md +198 -0
  170. package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
  171. package/claude/skills/pom-patterns/SKILL.md +452 -0
  172. package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
  173. package/claude/skills/rate-limiting/SKILL.md +342 -0
  174. package/claude/skills/react-best-practices/AGENTS.md +2410 -0
  175. package/claude/skills/react-best-practices/README.md +123 -0
  176. package/claude/skills/react-best-practices/SKILL.md +125 -0
  177. package/claude/skills/react-best-practices/metadata.json +15 -0
  178. package/claude/skills/react-best-practices/rules/_sections.md +46 -0
  179. package/claude/skills/react-best-practices/rules/_template.md +28 -0
  180. package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  181. package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  182. package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
  183. package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
  184. package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
  185. package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
  186. package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  187. package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  188. package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  189. package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  190. package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  191. package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
  192. package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  193. package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  194. package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  195. package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  196. package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  197. package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  198. package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  199. package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  200. package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  201. package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
  202. package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  203. package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
  204. package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  205. package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  206. package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  207. package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  208. package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
  209. package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  210. package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  211. package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  212. package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  213. package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  214. package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  215. package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  216. package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  217. package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  218. package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  219. package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  220. package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
  221. package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  222. package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  223. package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  224. package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
  225. package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  226. package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
  227. package/claude/skills/react-patterns/SKILL.md +688 -0
  228. package/claude/skills/registry-system/SKILL.md +331 -0
  229. package/claude/skills/scheduled-actions/SKILL.md +671 -0
  230. package/claude/skills/scope-enforcement/SKILL.md +542 -0
  231. package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
  232. package/claude/skills/server-actions/SKILL.md +493 -0
  233. package/claude/skills/service-layer/SKILL.md +587 -0
  234. package/claude/skills/session-management/SKILL.md +266 -0
  235. package/claude/skills/session-management/scripts/create-session.py +166 -0
  236. package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
  237. package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
  238. package/claude/skills/session-management/scripts/session-archive.sh +87 -0
  239. package/claude/skills/session-management/scripts/session-close.sh +133 -0
  240. package/claude/skills/session-management/scripts/session-init.sh +225 -0
  241. package/claude/skills/session-management/scripts/session-list.sh +163 -0
  242. package/claude/skills/session-management/scripts/split-plan.sh +116 -0
  243. package/claude/skills/shadcn-components/SKILL.md +586 -0
  244. package/claude/skills/shadcn-theming/SKILL.md +446 -0
  245. package/claude/skills/suspense-loading/SKILL.md +280 -0
  246. package/claude/skills/tailwind-theming/SKILL.md +507 -0
  247. package/claude/skills/tanstack-query/SKILL.md +608 -0
  248. package/claude/skills/test-coverage/SKILL.md +239 -0
  249. package/claude/skills/web-design-guidelines/SKILL.md +39 -0
  250. package/claude/skills/zod-validation/SKILL.md +537 -0
  251. package/claude/templates/blocks/progress.md +86 -0
  252. package/claude/templates/iteration/changes.md +61 -0
  253. package/claude/templates/iteration/progress.md +55 -0
  254. package/claude/templates/log.md +31 -0
  255. package/claude/templates/story/context.md +77 -0
  256. package/claude/templates/story/pendings.md +37 -0
  257. package/claude/templates/story/plan.md +299 -0
  258. package/claude/templates/story/requirements.md +109 -0
  259. package/claude/templates/story/scope.json +10 -0
  260. package/claude/templates/story/tests.md +91 -0
  261. package/claude/templates/task/progress.md +58 -0
  262. package/claude/templates/task/requirements.md +54 -0
  263. package/claude/workflows/README.md +154 -0
  264. package/claude/workflows/blocks.md +614 -0
  265. package/claude/workflows/story.md +1207 -0
  266. package/claude/workflows/task.md +927 -0
  267. package/claude/workflows/tweak.md +527 -0
  268. package/cursor/.gitkeep +0 -0
  269. package/package.json +35 -0
  270. package/scripts/postinstall.mjs +198 -0
  271. package/scripts/setup.mjs +282 -0
  272. package/scripts/sync.mjs +209 -0
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @nextsparkjs/ai-workflow postinstall hook
4
+ *
5
+ * Automatically syncs AI workflow files (.claude/, .cursor/, etc.) when the package is updated.
6
+ * Only runs in NextSpark projects that already have AI workflow set up.
7
+ *
8
+ * Debug mode: Set NEXTSPARK_DEBUG=1 to see detailed logs
9
+ */
10
+
11
+ import { execSync } from 'child_process';
12
+ import { existsSync, readFileSync } from 'fs';
13
+ import { join, dirname, resolve, isAbsolute } from 'path';
14
+ import { fileURLToPath } from 'url';
15
+
16
+ const DEBUG = process.env.NEXTSPARK_DEBUG === '1';
17
+
18
+ function debug(message) {
19
+ if (DEBUG) {
20
+ console.log(` [DEBUG] ${message}`);
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Find the project root by walking up from the current directory
26
+ * until we find a package.json that isn't inside node_modules
27
+ */
28
+ function findProjectRoot() {
29
+ // When running postinstall, we're in node_modules/@nextsparkjs/ai-workflow
30
+ // We need to find the project root (where the user's package.json is)
31
+ let current = process.cwd();
32
+
33
+ // If we're inside node_modules, walk up to find the project root
34
+ if (current.includes('node_modules')) {
35
+ // Go up until we're out of node_modules
36
+ const parts = current.split('node_modules');
37
+ current = parts[0].replace(/[/\\]$/, ''); // Remove trailing slash
38
+ }
39
+
40
+ return current;
41
+ }
42
+
43
+ /**
44
+ * Validate that the project root path is safe to use
45
+ */
46
+ function validateProjectRoot(projectRoot) {
47
+ // Must be an absolute path
48
+ if (!isAbsolute(projectRoot)) {
49
+ debug(`Invalid path: not absolute - ${projectRoot}`);
50
+ return false;
51
+ }
52
+
53
+ // Must exist
54
+ if (!existsSync(projectRoot)) {
55
+ debug(`Invalid path: does not exist - ${projectRoot}`);
56
+ return false;
57
+ }
58
+
59
+ // Resolve to canonical path and verify it doesn't escape
60
+ const resolved = resolve(projectRoot);
61
+ if (resolved !== projectRoot && !projectRoot.startsWith(resolved)) {
62
+ debug(`Invalid path: resolution mismatch - ${projectRoot} vs ${resolved}`);
63
+ return false;
64
+ }
65
+
66
+ return true;
67
+ }
68
+
69
+ /**
70
+ * Detect if we're in the NextSpark development monorepo.
71
+ * Consumer projects may also have pnpm-workspace.yaml (for themes/plugins),
72
+ * so we check for packages/core/ which only exists in the dev monorepo.
73
+ */
74
+ function isDevMonorepo(projectRoot) {
75
+ const isMonorepo = existsSync(join(projectRoot, 'packages', 'core', 'package.json'));
76
+ if (isMonorepo) {
77
+ debug('Skipping: NextSpark development monorepo detected');
78
+ }
79
+ return isMonorepo;
80
+ }
81
+
82
+ /**
83
+ * Detect if we're in a NextSpark consumer project
84
+ */
85
+ function isNextSparkProject(projectRoot) {
86
+ // If we're in the dev monorepo, skip
87
+ if (isDevMonorepo(projectRoot)) {
88
+ return false;
89
+ }
90
+
91
+ // Check for package.json with nextspark dependency
92
+ const pkgPath = join(projectRoot, 'package.json');
93
+ if (!existsSync(pkgPath)) {
94
+ debug('Skipping: no package.json found');
95
+ return false;
96
+ }
97
+
98
+ try {
99
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
100
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
101
+
102
+ const hasNextSpark = !!(deps['@nextsparkjs/core'] || deps['@nextsparkjs/cli']);
103
+ if (!hasNextSpark) {
104
+ debug('Skipping: no NextSpark dependencies found');
105
+ }
106
+ return hasNextSpark;
107
+ } catch {
108
+ debug('Skipping: failed to parse package.json');
109
+ return false;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Find the .claude/ folder location.
115
+ * In workspace setups, .claude/ may be in the parent directory (e.g., web/ is a
116
+ * workspace member but .claude/ lives at the monorepo root alongside web/).
117
+ * Returns the directory containing .claude/, or null if not found.
118
+ */
119
+ function findClaudeRoot(projectRoot) {
120
+ // Check project root itself
121
+ if (existsSync(join(projectRoot, '.claude'))) {
122
+ debug(`.claude/ found at ${projectRoot}`);
123
+ return projectRoot;
124
+ }
125
+
126
+ // Check parent directory (workspace pattern: my-project/web/ has package.json,
127
+ // but .claude/ is at my-project/)
128
+ const parentDir = resolve(projectRoot, '..');
129
+ if (existsSync(join(parentDir, '.claude'))) {
130
+ debug(`.claude/ found at parent: ${parentDir}`);
131
+ return parentDir;
132
+ }
133
+
134
+ debug('Skipping: no .claude/ folder found (AI workflow not set up yet)');
135
+ return null;
136
+ }
137
+
138
+ /**
139
+ * Find the setup.mjs script path relative to this postinstall script
140
+ */
141
+ function getSetupScriptPath() {
142
+ // This script is at packages/ai-workflow/scripts/postinstall.mjs
143
+ // setup.mjs is at packages/ai-workflow/scripts/setup.mjs
144
+ const __filename = fileURLToPath(import.meta.url);
145
+ const __dirname = dirname(__filename);
146
+ const setupPath = join(__dirname, 'setup.mjs');
147
+
148
+ if (existsSync(setupPath)) {
149
+ return setupPath;
150
+ }
151
+
152
+ debug(`Setup script not found at ${setupPath}`);
153
+ return null;
154
+ }
155
+
156
+ // Main execution
157
+ try {
158
+ const projectRoot = findProjectRoot();
159
+ debug(`Project root: ${projectRoot}`);
160
+
161
+ // Validate project root path
162
+ if (!validateProjectRoot(projectRoot)) {
163
+ debug('Skipping: invalid project root path');
164
+ process.exit(0);
165
+ }
166
+
167
+ // Only run in NextSpark projects with an existing .claude/ folder
168
+ if (isNextSparkProject(projectRoot)) {
169
+ const claudeRoot = findClaudeRoot(projectRoot);
170
+ if (claudeRoot) {
171
+ console.log('\n 📦 @nextsparkjs/ai-workflow updated - syncing AI workflow files...\n');
172
+
173
+ const setupScript = getSetupScriptPath();
174
+ if (setupScript) {
175
+ try {
176
+ execSync(`node "${setupScript}" claude`, {
177
+ stdio: 'inherit',
178
+ cwd: claudeRoot,
179
+ });
180
+ } catch (syncError) {
181
+ console.warn('\n ⚠️ Auto-sync failed. Run manually: npx nextspark sync:ai\n');
182
+ debug(`Sync error: ${syncError.message}`);
183
+ }
184
+ } else {
185
+ debug('Skipping: setup.mjs script not found');
186
+ }
187
+ }
188
+ }
189
+ } catch (error) {
190
+ // Postinstall hooks should not break installations
191
+ // The user can always run the sync manually
192
+ if (DEBUG) {
193
+ console.warn('\n [DEBUG] Postinstall error:', error.message);
194
+ if (error.stack) {
195
+ console.warn(error.stack);
196
+ }
197
+ }
198
+ }
@@ -0,0 +1,282 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @nextsparkjs/ai-workflow - Setup Script
5
+ *
6
+ * Copies AI workflow templates to the consumer's project directory.
7
+ * Uses file-by-file copy to preserve user-created files.
8
+ *
9
+ * Usage:
10
+ * node node_modules/@nextsparkjs/ai-workflow/scripts/setup.mjs [editor]
11
+ * nextspark setup:ai --editor claude
12
+ *
13
+ * Editor options:
14
+ * claude Setup Claude Code (.claude/)
15
+ * cursor Setup Cursor (coming soon)
16
+ * antigravity Setup Antigravity (coming soon)
17
+ * all Setup all available editors
18
+ * (no arg) Defaults to "claude"
19
+ *
20
+ * Copy strategy:
21
+ * agents/, commands/, skills/, templates/, workflows/, _docs/
22
+ * → Overwrite matching files, preserve user-created files
23
+ * config/*.schema.json
24
+ * → Always overwrite (schema updates)
25
+ * config/*.json (non-schema)
26
+ * → Copy only if not exists (user config — never overwrite)
27
+ * sessions/
28
+ * → Create directory if not exists, never copy content
29
+ */
30
+
31
+ import fs from 'node:fs'
32
+ import path from 'node:path'
33
+ import { fileURLToPath } from 'node:url'
34
+
35
+ const __filename = fileURLToPath(import.meta.url)
36
+ const __dirname = path.dirname(__filename)
37
+
38
+ // Package root = where this script lives (packages/ai-workflow/ or node_modules/@nextsparkjs/ai-workflow/)
39
+ const PACKAGE_ROOT = path.resolve(__dirname, '..')
40
+
41
+ // Consumer project root = where the user runs from
42
+ const PROJECT_ROOT = process.cwd()
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Editor definitions
46
+ // ---------------------------------------------------------------------------
47
+
48
+ const EDITORS = {
49
+ claude: {
50
+ enabled: true,
51
+ source: 'claude', // subdirectory in package
52
+ target: '.claude', // subdirectory in consumer project
53
+ },
54
+ cursor: {
55
+ enabled: false,
56
+ source: 'cursor',
57
+ target: '.cursor',
58
+ },
59
+ antigravity: {
60
+ enabled: false,
61
+ source: 'antigravity',
62
+ target: '.antigravity',
63
+ },
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // Copy strategies per directory
68
+ // ---------------------------------------------------------------------------
69
+
70
+ /** Directories where matching files are overwritten */
71
+ const OVERWRITE_DIRS = new Set([
72
+ 'agents',
73
+ 'commands',
74
+ 'skills',
75
+ 'templates',
76
+ 'workflows',
77
+ '_docs',
78
+ ])
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // Helpers
82
+ // ---------------------------------------------------------------------------
83
+
84
+ const colors = {
85
+ reset: '\x1b[0m',
86
+ cyan: '\x1b[36m',
87
+ green: '\x1b[32m',
88
+ yellow: '\x1b[33m',
89
+ blue: '\x1b[34m',
90
+ red: '\x1b[31m',
91
+ dim: '\x1b[2m',
92
+ bold: '\x1b[1m',
93
+ }
94
+
95
+ function log(msg, color = 'reset') {
96
+ console.log(`${colors[color]}${msg}${colors.reset}`)
97
+ }
98
+
99
+ /**
100
+ * Recursively get all files in a directory (relative paths).
101
+ */
102
+ function getAllFiles(dir, baseDir = dir) {
103
+ const results = []
104
+ if (!fs.existsSync(dir)) return results
105
+
106
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
107
+ const fullPath = path.join(dir, entry.name)
108
+ if (entry.isDirectory()) {
109
+ results.push(...getAllFiles(fullPath, baseDir))
110
+ } else if (entry.name !== '.gitkeep') {
111
+ results.push(path.relative(baseDir, fullPath))
112
+ }
113
+ }
114
+ return results
115
+ }
116
+
117
+ /**
118
+ * Copy a single file, creating parent directories as needed.
119
+ */
120
+ function copyFile(src, dest) {
121
+ const destDir = path.dirname(dest)
122
+ if (!fs.existsSync(destDir)) {
123
+ fs.mkdirSync(destDir, { recursive: true })
124
+ }
125
+ fs.copyFileSync(src, dest)
126
+ }
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Setup logic for a single editor
130
+ // ---------------------------------------------------------------------------
131
+
132
+ function setupEditor(name, cfg) {
133
+ const sourceRoot = path.join(PACKAGE_ROOT, cfg.source)
134
+ const targetRoot = path.join(PROJECT_ROOT, cfg.target)
135
+
136
+ if (!fs.existsSync(sourceRoot)) {
137
+ log(` Source not found: ${sourceRoot}`, 'red')
138
+ return { overwritten: 0, created: 0, preserved: 0 }
139
+ }
140
+
141
+ // Ensure target root exists
142
+ if (!fs.existsSync(targetRoot)) {
143
+ fs.mkdirSync(targetRoot, { recursive: true })
144
+ }
145
+
146
+ let overwritten = 0
147
+ let created = 0
148
+ let preserved = 0
149
+
150
+ // 1. Process overwrite directories (agents, commands, skills, etc.)
151
+ for (const dirName of OVERWRITE_DIRS) {
152
+ const srcDir = path.join(sourceRoot, dirName)
153
+ if (!fs.existsSync(srcDir)) continue
154
+
155
+ const files = getAllFiles(srcDir)
156
+ for (const relPath of files) {
157
+ const srcFile = path.join(srcDir, relPath)
158
+ const destFile = path.join(targetRoot, dirName, relPath)
159
+ const existed = fs.existsSync(destFile)
160
+
161
+ copyFile(srcFile, destFile)
162
+
163
+ if (existed) {
164
+ overwritten++
165
+ } else {
166
+ created++
167
+ }
168
+ }
169
+
170
+ if (files.length > 0) {
171
+ log(` ${dirName}/: ${files.length} files`, 'green')
172
+ }
173
+ }
174
+
175
+ // 2. Process config/ directory with mixed strategy
176
+ const srcConfig = path.join(sourceRoot, 'config')
177
+ const destConfig = path.join(targetRoot, 'config')
178
+
179
+ if (fs.existsSync(srcConfig)) {
180
+ if (!fs.existsSync(destConfig)) {
181
+ fs.mkdirSync(destConfig, { recursive: true })
182
+ }
183
+
184
+ let schemasCopied = 0
185
+ let configsCreated = 0
186
+ let configsPreserved = 0
187
+
188
+ for (const file of fs.readdirSync(srcConfig)) {
189
+ const srcFile = path.join(srcConfig, file)
190
+ const destFile = path.join(destConfig, file)
191
+
192
+ // Skip non-files and .gitkeep
193
+ if (!fs.statSync(srcFile).isFile() || file === '.gitkeep') continue
194
+
195
+ const isSchema = file.endsWith('.schema.json')
196
+
197
+ if (isSchema) {
198
+ // Schema files: always overwrite
199
+ copyFile(srcFile, destFile)
200
+ schemasCopied++
201
+ overwritten++
202
+ } else {
203
+ // Config files: copy only if not exists
204
+ if (fs.existsSync(destFile)) {
205
+ configsPreserved++
206
+ preserved++
207
+ } else {
208
+ copyFile(srcFile, destFile)
209
+ configsCreated++
210
+ created++
211
+ }
212
+ }
213
+ }
214
+
215
+ if (schemasCopied > 0) {
216
+ log(` config/*.schema.json: ${schemasCopied} schemas updated`, 'green')
217
+ }
218
+ if (configsCreated > 0) {
219
+ log(` config/*.json: ${configsCreated} new configs created`, 'green')
220
+ }
221
+ if (configsPreserved > 0) {
222
+ log(` config/*.json: ${configsPreserved} user configs preserved`, 'blue')
223
+ }
224
+ }
225
+
226
+ // 3. Ensure sessions/ directory exists (never copy content)
227
+ const sessionsDir = path.join(targetRoot, 'sessions')
228
+ if (!fs.existsSync(sessionsDir)) {
229
+ fs.mkdirSync(sessionsDir, { recursive: true })
230
+ log(` sessions/: created (empty)`, 'green')
231
+ } else {
232
+ log(` sessions/: exists (preserved)`, 'blue')
233
+ }
234
+
235
+ return { overwritten, created, preserved }
236
+ }
237
+
238
+ // ---------------------------------------------------------------------------
239
+ // Entry point
240
+ // ---------------------------------------------------------------------------
241
+
242
+ const editorArg = process.argv[2] || 'claude'
243
+
244
+ log(`\n${colors.bold}@nextsparkjs/ai-workflow — Setup${colors.reset}\n`, 'cyan')
245
+ log(`Package: ${PACKAGE_ROOT}`, 'dim')
246
+ log(`Project: ${PROJECT_ROOT}\n`, 'dim')
247
+
248
+ if (editorArg === 'all') {
249
+ for (const [name, cfg] of Object.entries(EDITORS)) {
250
+ if (cfg.enabled) {
251
+ log(`Setting up ${name}:`, 'cyan')
252
+ const stats = setupEditor(name, cfg)
253
+ log(` Total: ${stats.overwritten} updated, ${stats.created} created, ${stats.preserved} preserved\n`, 'dim')
254
+ } else {
255
+ log(`${name}: coming soon\n`, 'yellow')
256
+ }
257
+ }
258
+ } else {
259
+ const cfg = EDITORS[editorArg]
260
+
261
+ if (!cfg) {
262
+ log(`Unknown editor: ${editorArg}`, 'red')
263
+ log(`Available: ${Object.keys(EDITORS).join(', ')}, all`, 'dim')
264
+ process.exit(1)
265
+ }
266
+
267
+ if (!cfg.enabled) {
268
+ log(`${editorArg}: coming soon`, 'yellow')
269
+ log(`Currently supported: claude`, 'dim')
270
+ process.exit(0)
271
+ }
272
+
273
+ log(`Setting up ${editorArg}:`, 'cyan')
274
+ const stats = setupEditor(editorArg, cfg)
275
+
276
+ log('')
277
+ log(`Setup complete!`, 'green')
278
+ log(` ${stats.overwritten} files updated`, 'dim')
279
+ log(` ${stats.created} files created`, 'dim')
280
+ log(` ${stats.preserved} files preserved`, 'dim')
281
+ log('')
282
+ }
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @nextsparkjs/ai-workflow - Sync Script
5
+ *
6
+ * Syncs the monorepo's working .claude/ directory into the publishable
7
+ * packages/ai-workflow/claude/ directory. Run manually before npm publish.
8
+ *
9
+ * Usage:
10
+ * node packages/ai-workflow/scripts/sync.mjs
11
+ * # or from package dir:
12
+ * pnpm sync
13
+ */
14
+
15
+ import fs from 'node:fs'
16
+ import path from 'node:path'
17
+ import { fileURLToPath } from 'node:url'
18
+
19
+ const __filename = fileURLToPath(import.meta.url)
20
+ const __dirname = path.dirname(__filename)
21
+
22
+ // Package root = packages/ai-workflow/
23
+ const PACKAGE_ROOT = path.resolve(__dirname, '..')
24
+ // Monorepo root = repo/
25
+ const MONOREPO_ROOT = path.resolve(PACKAGE_ROOT, '../..')
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Editor definitions (future-proof)
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const EDITORS = {
32
+ claude: {
33
+ sourceDir: '.claude',
34
+ targetDir: 'claude',
35
+ enabled: true,
36
+ },
37
+ cursor: {
38
+ sourceDir: '.cursor',
39
+ targetDir: 'cursor',
40
+ enabled: false,
41
+ },
42
+ antigravity: {
43
+ sourceDir: '.antigravity',
44
+ targetDir: 'antigravity',
45
+ enabled: false,
46
+ },
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Sync mapping: directories and files to copy from .claude/ to claude/
51
+ // ---------------------------------------------------------------------------
52
+
53
+ /** Directories to sync (cleaned before copy) */
54
+ const SYNC_DIRS = [
55
+ 'agents',
56
+ 'commands',
57
+ 'skills',
58
+ 'templates',
59
+ 'workflows',
60
+ '_docs',
61
+ ]
62
+
63
+ /** Subdirectories to exclude from sync (monorepo-only content) */
64
+ const EXCLUDE_DIRS = new Set(['_monorepo'])
65
+
66
+ /** File globs within config/ to sync (only schemas) */
67
+ const CONFIG_SYNC_PATTERN = /\.schema\.json$/
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Helpers
71
+ // ---------------------------------------------------------------------------
72
+
73
+ const colors = {
74
+ reset: '\x1b[0m',
75
+ cyan: '\x1b[36m',
76
+ green: '\x1b[32m',
77
+ yellow: '\x1b[33m',
78
+ red: '\x1b[31m',
79
+ dim: '\x1b[2m',
80
+ }
81
+
82
+ function log(msg, color = 'reset') {
83
+ console.log(`${colors[color]}${msg}${colors.reset}`)
84
+ }
85
+
86
+ function getAllFiles(dir, baseDir = dir) {
87
+ const results = []
88
+ if (!fs.existsSync(dir)) return results
89
+
90
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
91
+ if (EXCLUDE_DIRS.has(entry.name)) continue
92
+ const fullPath = path.join(dir, entry.name)
93
+ if (entry.isDirectory()) {
94
+ results.push(...getAllFiles(fullPath, baseDir))
95
+ } else {
96
+ results.push(path.relative(baseDir, fullPath))
97
+ }
98
+ }
99
+ return results
100
+ }
101
+
102
+ function removeDir(dir) {
103
+ if (fs.existsSync(dir)) {
104
+ fs.rmSync(dir, { recursive: true, force: true })
105
+ }
106
+ }
107
+
108
+ function copyFile(src, dest) {
109
+ const destDir = path.dirname(dest)
110
+ if (!fs.existsSync(destDir)) {
111
+ fs.mkdirSync(destDir, { recursive: true })
112
+ }
113
+ fs.copyFileSync(src, dest)
114
+ }
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // Main sync logic
118
+ // ---------------------------------------------------------------------------
119
+
120
+ function syncEditor(name, cfg) {
121
+ const sourceRoot = path.join(MONOREPO_ROOT, cfg.sourceDir)
122
+ const targetRoot = path.join(PACKAGE_ROOT, cfg.targetDir)
123
+
124
+ if (!fs.existsSync(sourceRoot)) {
125
+ log(` Source not found: ${sourceRoot}`, 'red')
126
+ return { copied: 0, removed: 0, skipped: 0 }
127
+ }
128
+
129
+ let copied = 0
130
+ let removed = 0
131
+ let skipped = 0
132
+
133
+ // 1. Sync directories: clean target then copy
134
+ for (const dirName of SYNC_DIRS) {
135
+ const srcDir = path.join(sourceRoot, dirName)
136
+ const destDir = path.join(targetRoot, dirName)
137
+
138
+ if (!fs.existsSync(srcDir)) {
139
+ skipped++
140
+ continue
141
+ }
142
+
143
+ // Clean target directory (remove stale files)
144
+ if (fs.existsSync(destDir)) {
145
+ const oldFiles = getAllFiles(destDir)
146
+ removeDir(destDir)
147
+ removed += oldFiles.length
148
+ }
149
+
150
+ // Copy all files
151
+ const files = getAllFiles(srcDir)
152
+ for (const relPath of files) {
153
+ copyFile(
154
+ path.join(srcDir, relPath),
155
+ path.join(destDir, relPath)
156
+ )
157
+ copied++
158
+ }
159
+
160
+ log(` ${dirName}/: ${files.length} files`, 'green')
161
+ }
162
+
163
+ // 2. Sync config/ schemas only
164
+ const srcConfig = path.join(sourceRoot, 'config')
165
+ const destConfig = path.join(targetRoot, 'config')
166
+
167
+ if (fs.existsSync(srcConfig)) {
168
+ let schemaCount = 0
169
+ for (const file of fs.readdirSync(srcConfig)) {
170
+ if (CONFIG_SYNC_PATTERN.test(file)) {
171
+ copyFile(
172
+ path.join(srcConfig, file),
173
+ path.join(destConfig, file)
174
+ )
175
+ schemaCount++
176
+ copied++
177
+ }
178
+ }
179
+ log(` config/*.schema.json: ${schemaCount} files`, 'green')
180
+ log(` config/*.json (templates): preserved`, 'dim')
181
+ }
182
+
183
+ return { copied, removed, skipped }
184
+ }
185
+
186
+ // ---------------------------------------------------------------------------
187
+ // Entry point
188
+ // ---------------------------------------------------------------------------
189
+
190
+ log('\n@nextsparkjs/ai-workflow — Sync\n', 'cyan')
191
+ log(`Source: ${MONOREPO_ROOT}/.claude/`, 'dim')
192
+ log(`Target: ${PACKAGE_ROOT}/claude/\n`, 'dim')
193
+
194
+ let totalCopied = 0
195
+ let totalRemoved = 0
196
+
197
+ for (const [name, cfg] of Object.entries(EDITORS)) {
198
+ if (!cfg.enabled) {
199
+ log(`${name}: coming soon (skipped)`, 'dim')
200
+ continue
201
+ }
202
+
203
+ log(`Syncing ${name}:`, 'cyan')
204
+ const stats = syncEditor(name, cfg)
205
+ totalCopied += stats.copied
206
+ totalRemoved += stats.removed
207
+ }
208
+
209
+ log(`\nDone: ${totalCopied} files copied, ${totalRemoved} stale files removed.\n`, 'green')