@nextsparkjs/ai-workflow 0.1.0-beta.88 → 0.1.0-beta.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextsparkjs/ai-workflow",
3
- "version": "0.1.0-beta.88",
3
+ "version": "0.1.0-beta.89",
4
4
  "description": "AI workflow templates for NextSpark - Claude Code agents, commands, skills, and multi-editor support",
5
5
  "license": "MIT",
6
6
  "author": "NextSpark <hello@nextspark.dev>",
@@ -29,6 +29,7 @@
29
29
  ],
30
30
  "scripts": {
31
31
  "setup": "node scripts/setup.mjs",
32
- "sync": "node scripts/sync.mjs"
32
+ "sync": "node scripts/sync.mjs",
33
+ "postinstall": "node scripts/postinstall.mjs"
33
34
  }
34
35
  }
@@ -0,0 +1,170 @@
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 a NextSpark project (not the monorepo)
71
+ */
72
+ function isNextSparkProject(projectRoot) {
73
+ // If we're in the monorepo, skip
74
+ const pnpmWorkspace = join(projectRoot, 'pnpm-workspace.yaml');
75
+ if (existsSync(pnpmWorkspace)) {
76
+ debug('Skipping: monorepo detected (pnpm-workspace.yaml exists)');
77
+ return false;
78
+ }
79
+
80
+ // Check for package.json with nextspark dependency
81
+ const pkgPath = join(projectRoot, 'package.json');
82
+ if (!existsSync(pkgPath)) {
83
+ debug('Skipping: no package.json found');
84
+ return false;
85
+ }
86
+
87
+ try {
88
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
89
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
90
+
91
+ const hasNextSpark = !!(deps['@nextsparkjs/core'] || deps['@nextsparkjs/cli']);
92
+ if (!hasNextSpark) {
93
+ debug('Skipping: no NextSpark dependencies found');
94
+ }
95
+ return hasNextSpark;
96
+ } catch {
97
+ debug('Skipping: failed to parse package.json');
98
+ return false;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Check if the project has a .claude/ folder (AI workflow already set up)
104
+ */
105
+ function hasAIWorkflowSetup(projectRoot) {
106
+ const hasClaudeDir = existsSync(join(projectRoot, '.claude'));
107
+ if (!hasClaudeDir) {
108
+ debug('Skipping: no .claude/ folder found (AI workflow not set up yet)');
109
+ }
110
+ return hasClaudeDir;
111
+ }
112
+
113
+ /**
114
+ * Find the setup.mjs script path relative to this postinstall script
115
+ */
116
+ function getSetupScriptPath() {
117
+ // This script is at packages/ai-workflow/scripts/postinstall.mjs
118
+ // setup.mjs is at packages/ai-workflow/scripts/setup.mjs
119
+ const __filename = fileURLToPath(import.meta.url);
120
+ const __dirname = dirname(__filename);
121
+ const setupPath = join(__dirname, 'setup.mjs');
122
+
123
+ if (existsSync(setupPath)) {
124
+ return setupPath;
125
+ }
126
+
127
+ debug(`Setup script not found at ${setupPath}`);
128
+ return null;
129
+ }
130
+
131
+ // Main execution
132
+ try {
133
+ const projectRoot = findProjectRoot();
134
+ debug(`Project root: ${projectRoot}`);
135
+
136
+ // Validate project root path
137
+ if (!validateProjectRoot(projectRoot)) {
138
+ debug('Skipping: invalid project root path');
139
+ process.exit(0);
140
+ }
141
+
142
+ // Only run in NextSpark projects with an existing .claude/ folder
143
+ if (isNextSparkProject(projectRoot) && hasAIWorkflowSetup(projectRoot)) {
144
+ console.log('\n 📦 @nextsparkjs/ai-workflow updated - syncing AI workflow files...\n');
145
+
146
+ const setupScript = getSetupScriptPath();
147
+ if (setupScript) {
148
+ try {
149
+ execSync(`node "${setupScript}" claude`, {
150
+ stdio: 'inherit',
151
+ cwd: projectRoot,
152
+ });
153
+ } catch (syncError) {
154
+ console.warn('\n ⚠️ Auto-sync failed. Run manually: npx nextspark sync:ai\n');
155
+ debug(`Sync error: ${syncError.message}`);
156
+ }
157
+ } else {
158
+ debug('Skipping: setup.mjs script not found');
159
+ }
160
+ }
161
+ } catch (error) {
162
+ // Postinstall hooks should not break installations
163
+ // The user can always run the sync manually
164
+ if (DEBUG) {
165
+ console.warn('\n [DEBUG] Postinstall error:', error.message);
166
+ if (error.stack) {
167
+ console.warn(error.stack);
168
+ }
169
+ }
170
+ }