obsidian-plugin-config 1.7.1 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,393 +1,393 @@
1
- #!/usr/bin/env tsx
2
-
3
- import fs from 'fs';
4
- import path from 'path';
5
- import { execSync } from 'child_process';
6
-
7
- /**
8
- * Generate bin/obsidian-inject.js from template
9
- */
10
- async function generateBinFile(): Promise<void> {
11
- console.log(`\n🔧 Generating bin/obsidian-inject.js...`);
12
-
13
- const binDir = 'bin';
14
- const binPath = path.join(binDir, 'obsidian-inject.js');
15
-
16
- // Ensure bin directory exists
17
- if (!fs.existsSync(binDir)) {
18
- fs.mkdirSync(binDir, { recursive: true });
19
- console.log(` 📁 Created ${binDir} directory`);
20
- }
21
-
22
- // Read package.json for version info
23
- const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
24
-
25
- const binContent = `#!/usr/bin/env node
26
-
27
- /**
28
- * Obsidian Plugin Config - CLI Entry Point
29
- * Global command: obsidian-inject
30
- * Version: ${packageJson.version}
31
- */
32
-
33
- import { execSync } from 'child_process';
34
- import { fileURLToPath } from 'url';
35
- import { dirname, join, isAbsolute, resolve } from 'path';
36
- import fs from 'fs';
37
-
38
- // Get the directory of this script
39
- const __filename = fileURLToPath(import.meta.url);
40
- const __dirname = dirname(__filename);
41
- const packageRoot = dirname(__dirname);
42
-
43
- // Path to the injection script
44
- const injectScriptPath = join(packageRoot, 'scripts', 'inject-path.ts');
45
-
46
- function showHelp() {
47
- console.log(\`
48
- Obsidian Plugin Config - Global CLI
49
- Injection system for autonomous Obsidian plugins
50
-
51
- USAGE:
52
- obsidian-inject # Inject in current directory (with confirmation)
53
- obsidian-inject <path> # Inject by path (with confirmation)
54
- obsidian-inject <path> --no # Inject without confirmation
55
- obsidian-inject --help, -h # Show this help
56
-
57
- OPTIONS:
58
- --no, -n # Skip confirmation prompts (auto-confirm all)
59
- --dry-run # Verification only (no changes)
60
-
61
- EXAMPLES:
62
- cd my-plugin && obsidian-inject
63
- obsidian-inject ../my-other-plugin
64
- obsidian-inject ../my-plugin --no
65
- obsidian-inject "C:\\\\Users\\\\dev\\\\plugins\\\\my-plugin"
66
-
67
- WHAT IS INJECTED:
68
- ✅ Local scripts (esbuild.config.ts, acp.ts, utils.ts, etc.)
69
- ✅ Package.json configuration (scripts, dependencies)
70
- ✅ Config files (tsconfig, eslint, prettier, vscode, github)
71
- ✅ Yarn protection enforced
72
- ✅ Automatic dependency installation
73
-
74
- ARCHITECTURE:
75
- - Plugin becomes AUTONOMOUS with local scripts
76
- - No external dependencies required after injection
77
- - Updates possible via re-injection
78
-
79
- More info: https://github.com/3C0D/obsidian-plugin-config
80
- \`);
81
- }
82
-
83
- function main() {
84
- const args = process.argv.slice(2);
85
-
86
- // Handle help flags
87
- if (args.includes('--help') || args.includes('-h')) {
88
- showHelp();
89
- process.exit(0);
90
- }
91
-
92
- // Check if injection script exists
93
- if (!fs.existsSync(injectScriptPath)) {
94
- console.error(\`❌ Error: Injection script not found at \${injectScriptPath}\`);
95
- console.error(\` Make sure obsidian-plugin-config is properly installed.\`);
96
- process.exit(1);
97
- }
98
-
99
- // Parse arguments
100
- const noConfirm = args.includes('--no') || args.includes('-n');
101
- const dryRun = args.includes('--dry-run');
102
- const pathArg = args.find(arg => !arg.startsWith('-'));
103
-
104
- // Determine target path (resolve relative to user's current directory)
105
- const userCwd = process.cwd();
106
- let targetPath = pathArg || userCwd;
107
-
108
- // If relative path, resolve from user's current directory
109
- if (pathArg && !isAbsolute(pathArg)) {
110
- targetPath = resolve(userCwd, pathArg);
111
- }
112
-
113
- console.log(\`🎯 Obsidian Plugin Config - Global Injection\`);
114
- console.log(\`📁 Target: \${targetPath}\`);
115
- console.log(\`❓ Confirmation: \${noConfirm ? 'Disabled (auto-confirm)' : 'Enabled'}\`);
116
- console.log(\`📦 From: \${packageRoot}\\n\`);
117
-
118
- try {
119
- // Check if target directory has package.json
120
- const targetPackageJson = join(targetPath, 'package.json');
121
- if (!fs.existsSync(targetPackageJson)) {
122
- console.error(\`❌ Error: package.json not found in \${targetPath}\`);
123
- console.error(\` Make sure this is a valid Node.js project.\`);
124
- process.exit(1);
125
- }
126
-
127
- // Prevent injecting into obsidian-plugin-config itself
128
- const pkg = JSON.parse(fs.readFileSync(targetPackageJson, 'utf8'));
129
- if (pkg.name === 'obsidian-plugin-config') {
130
- console.error(\`❌ Cannot inject into obsidian-plugin-config itself.\`);
131
- process.exit(1);
132
- }
133
-
134
- // Clean NPM artifacts if package-lock.json exists
135
- const packageLockPath = join(targetPath, 'package-lock.json');
136
- if (fs.existsSync(packageLockPath)) {
137
- console.log(\`🧹 NPM installation detected, cleaning...\`);
138
-
139
- try {
140
- // Remove package-lock.json
141
- fs.unlinkSync(packageLockPath);
142
- console.log(\` 🗑️ package-lock.json removed\`);
143
-
144
- // Remove node_modules if it exists
145
- const nodeModulesPath = join(targetPath, 'node_modules');
146
- if (fs.existsSync(nodeModulesPath)) {
147
- fs.rmSync(nodeModulesPath, { recursive: true, force: true });
148
- console.log(\` 🗑️ node_modules removed (will be reinstalled with Yarn)\`);
149
- }
150
-
151
- console.log(\` ✅ NPM artifacts cleaned to avoid Yarn conflicts\`);
152
-
153
- } catch (cleanError) {
154
- console.error(\` ❌ Cleanup failed:\`, cleanError.message);
155
- console.log(\` 💡 Manually remove package-lock.json and node_modules\`);
156
- }
157
- }
158
-
159
- // Check if tsx is available locally in target
160
- try {
161
- execSync('npx tsx --version', {
162
- cwd: targetPath,
163
- stdio: 'pipe'
164
- });
165
- console.log(\`✅ tsx available locally\`);
166
- } catch {
167
- console.log(\`⚠️ tsx not found, installing...\`);
168
-
169
- // Install tsx locally in target directory
170
- try {
171
- execSync('yarn add -D tsx', {
172
- cwd: targetPath,
173
- stdio: 'inherit'
174
- });
175
- console.log(\`✅ tsx installed successfully\`);
176
- } catch (installError) {
177
- console.error(\`❌ tsx installation failed:\`, installError.message);
178
- console.error(\` Try installing tsx manually: cd "\${targetPath}" && yarn add -D tsx\`);
179
- process.exit(1);
180
- }
181
- }
182
-
183
- // Execute the injection script with tsx
184
- const yesOption = noConfirm ? ' --yes' : '';
185
- const dryRunOption = dryRun ? ' --dry-run' : '';
186
- const command = \`npx tsx "\${injectScriptPath}" "\${targetPath}"\${yesOption}\${dryRunOption}\`;
187
-
188
- execSync(command, {
189
- stdio: 'inherit',
190
- cwd: targetPath // Use target directory to ensure tsx is available
191
- });
192
-
193
- console.log(\`\\n✅ Injection completed successfully!\`);
194
-
195
- } catch (error) {
196
- console.error(\`\\n❌ Injection error:\`, error.message);
197
- process.exit(1);
198
- }
199
- }
200
-
201
- // Run the CLI
202
- main();
203
- `;
204
-
205
- fs.writeFileSync(binPath, binContent, 'utf8');
206
- console.log(` ✅ Generated ${binPath}`);
207
- }
208
-
209
- /**
210
- * Ensure NPM authentication, prompting login if needed, then verifying success.
211
- */
212
- async function ensureNpmAuth(): Promise<void> {
213
- console.log(`🔐 Checking NPM authentication...`);
214
-
215
- const checkWhoami = (): string | null => {
216
- try {
217
- return execSync('npm whoami --registry https://registry.npmjs.org/', {
218
- stdio: 'pipe',
219
- encoding: 'utf8'
220
- }).trim();
221
- } catch {
222
- return null;
223
- }
224
- };
225
-
226
- const whoami = checkWhoami();
227
- if (whoami) {
228
- console.log(` ✅ Logged in as: ${whoami}\n`);
229
- return;
230
- }
231
-
232
- console.log(` ⚠️ Not logged in to NPM`);
233
- console.log(`🔑 Please login to NPM to publish the package`);
234
- console.log(` Opening browser for authentication...\n`);
235
-
236
- try {
237
- execSync('npm login --auth-type=web --registry https://registry.npmjs.org/', {
238
- stdio: 'inherit'
239
- });
240
- } catch {
241
- console.error(`\n ❌ NPM login failed`);
242
- console.error(` Please run 'npm login' manually and try again`);
243
- throw new Error('NPM authentication required');
244
- }
245
-
246
- // Verify login actually succeeded
247
- const whoamiAfter = checkWhoami();
248
- if (!whoamiAfter) {
249
- console.error(`\n ❌ Login appeared to complete but authentication could not be verified`);
250
- throw new Error('NPM authentication required');
251
- }
252
-
253
- console.log(`\n ✅ Successfully logged in as: ${whoamiAfter}\n`);
254
- }
255
-
256
- /**
257
- * Complete NPM workflow - Version, Commit, Push, Publish
258
- */
259
- async function buildAndPublishNpm(): Promise<void> {
260
- console.log(`🚀 Obsidian Plugin Config - NPM Publish Workflow`);
261
- console.log(`Automation: version → bin → verify → commit → publish\n`);
262
-
263
- try {
264
- // Step 0: Check NPM authentication
265
- await ensureNpmAuth();
266
-
267
- // Step 1: Update version
268
- console.log(`📋 Step 1/5: Updating version...`);
269
- execSync('tsx scripts/update-version-config.ts', { stdio: 'inherit' });
270
-
271
- // Step 2: Generate bin file
272
- console.log(`\n🔧 Step 2/5: Generating bin/obsidian-inject.js...`);
273
- await generateBinFile();
274
-
275
- // Step 3: Verify package
276
- console.log(`\n📋 Step 3/5: Verifying package...`);
277
- verifyPackage();
278
-
279
- // Step 4: Commit and push
280
- console.log(`\n📤 Step 4/5: Committing and pushing changes...`);
281
- try {
282
- // Add all changes
283
- execSync('git add -A', { stdio: 'pipe' });
284
-
285
- // Check if there are changes to commit
286
- const status = execSync('git status --porcelain', { encoding: 'utf8' });
287
- if (status.trim()) {
288
- const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
289
- execSync(`git commit -m "Publish NPM package v${packageJson.version}"`, {
290
- stdio: 'pipe'
291
- });
292
-
293
- // Get current branch and push
294
- const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
295
- encoding: 'utf8'
296
- }).trim();
297
- execSync(`git push origin ${currentBranch}`, { stdio: 'inherit' });
298
- console.log(` ✅ Changes committed and pushed`);
299
- } else {
300
- console.log(` ℹ️ No changes to commit`);
301
- }
302
- } catch (error) {
303
- console.error(
304
- ` ❌ Commit/push failed: ${error instanceof Error ? error.message : String(error)}`
305
- );
306
- throw error;
307
- }
308
-
309
- // Step 5: Publish to NPM
310
- console.log(`\n📤 Step 5/5: Publishing to NPM...`);
311
- execSync('npm publish --registry https://registry.npmjs.org/', {
312
- stdio: 'inherit'
313
- });
314
-
315
- // Optional: Update global CLI automatically
316
- console.log(`\n🌍 Updating global CLI...`);
317
- console.log(` ⏳ Waiting 30s for NPM registry propagation...`);
318
- await new Promise((resolve) => setTimeout(resolve, 30000));
319
- try {
320
- execSync(
321
- 'npm install -g obsidian-plugin-config@latest --force --engine-strict=false',
322
- { stdio: 'inherit' }
323
- );
324
- console.log(` ✅ Global CLI updated`);
325
- } catch {
326
- console.log(` ⚠️ Global CLI update failed (NPM registry may need more time)`);
327
- console.log(
328
- ` 💡 Run manually in a few minutes: npm install -g obsidian-plugin-config@latest --force`
329
- );
330
- }
331
- console.log(`\n🎉 Complete workflow successful!`);
332
- console.log(` Test: cd any-plugin && obsidian-inject`);
333
- } catch (error) {
334
- console.error(
335
- `\n❌ Workflow failed: ${error instanceof Error ? error.message : String(error)}`
336
- );
337
- process.exit(1);
338
- }
339
- }
340
-
341
- /**
342
- * Verify package is ready for publication
343
- */
344
- function verifyPackage(): void {
345
- // Check required scripts
346
- const requiredScripts = [
347
- 'scripts/inject-path.ts',
348
- 'scripts/inject-prompt.ts',
349
- 'scripts/inject-core.ts',
350
- 'scripts/utils.ts',
351
- 'scripts/acp.ts',
352
- 'scripts/update-version-config.ts',
353
- 'scripts/help.ts'
354
- ];
355
-
356
- for (const script of requiredScripts) {
357
- if (!fs.existsSync(script)) {
358
- throw new Error(`Missing required script: ${script}`);
359
- }
360
- }
361
- console.log(` ✅ All required scripts present`);
362
-
363
- // Check package.json
364
- const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
365
- const requiredFields = [
366
- 'name',
367
- 'version',
368
- 'description',
369
- 'bin',
370
- 'repository',
371
- 'author'
372
- ];
373
-
374
- for (const field of requiredFields) {
375
- if (!packageJson[field]) {
376
- throw new Error(`Missing required package.json field: ${field}`);
377
- }
378
- }
379
- console.log(` ✅ Package.json valid (v${packageJson.version})`);
380
-
381
- // Check bin file exists
382
- if (!fs.existsSync('bin/obsidian-inject.js')) {
383
- throw new Error(`Missing bin file: bin/obsidian-inject.js`);
384
- }
385
- console.log(` ✅ Bin file ready`);
386
-
387
- // Quick build test
388
- execSync('tsc --noEmit', { stdio: 'pipe' });
389
- console.log(` ✅ TypeScript check passed`);
390
- }
391
-
392
- // Run the script
393
- await buildAndPublishNpm();
1
+ #!/usr/bin/env tsx
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { execSync } from 'child_process';
6
+
7
+ /**
8
+ * Generate bin/obsidian-inject.js from template
9
+ */
10
+ async function generateBinFile(): Promise<void> {
11
+ console.log(`\n🔧 Generating bin/obsidian-inject.js...`);
12
+
13
+ const binDir = 'bin';
14
+ const binPath = path.join(binDir, 'obsidian-inject.js');
15
+
16
+ // Ensure bin directory exists
17
+ if (!fs.existsSync(binDir)) {
18
+ fs.mkdirSync(binDir, { recursive: true });
19
+ console.log(` 📁 Created ${binDir} directory`);
20
+ }
21
+
22
+ // Read package.json for version info
23
+ const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
24
+
25
+ const binContent = `#!/usr/bin/env node
26
+
27
+ /**
28
+ * Obsidian Plugin Config - CLI Entry Point
29
+ * Global command: obsidian-inject
30
+ * Version: ${packageJson.version}
31
+ */
32
+
33
+ import { execSync } from 'child_process';
34
+ import { fileURLToPath } from 'url';
35
+ import { dirname, join, isAbsolute, resolve } from 'path';
36
+ import fs from 'fs';
37
+
38
+ // Get the directory of this script
39
+ const __filename = fileURLToPath(import.meta.url);
40
+ const __dirname = dirname(__filename);
41
+ const packageRoot = dirname(__dirname);
42
+
43
+ // Path to the injection script
44
+ const injectScriptPath = join(packageRoot, 'scripts', 'inject-path.ts');
45
+
46
+ function showHelp() {
47
+ console.log(\`
48
+ Obsidian Plugin Config - Global CLI
49
+ Injection system for autonomous Obsidian plugins
50
+
51
+ USAGE:
52
+ obsidian-inject # Inject in current directory (with confirmation)
53
+ obsidian-inject <path> # Inject by path (with confirmation)
54
+ obsidian-inject <path> --no # Inject without confirmation
55
+ obsidian-inject --help, -h # Show this help
56
+
57
+ OPTIONS:
58
+ --no, -n # Skip confirmation prompts (auto-confirm all)
59
+ --dry-run # Verification only (no changes)
60
+
61
+ EXAMPLES:
62
+ cd my-plugin && obsidian-inject
63
+ obsidian-inject ../my-other-plugin
64
+ obsidian-inject ../my-plugin --no
65
+ obsidian-inject "C:\\\\Users\\\\dev\\\\plugins\\\\my-plugin"
66
+
67
+ WHAT IS INJECTED:
68
+ ✅ Local scripts (esbuild.config.ts, acp.ts, utils.ts, etc.)
69
+ ✅ Package.json configuration (scripts, dependencies)
70
+ ✅ Config files (tsconfig, eslint, prettier, vscode, github)
71
+ ✅ Yarn protection enforced
72
+ ✅ Automatic dependency installation
73
+
74
+ ARCHITECTURE:
75
+ - Plugin becomes AUTONOMOUS with local scripts
76
+ - No external dependencies required after injection
77
+ - Updates possible via re-injection
78
+
79
+ More info: https://github.com/3C0D/obsidian-plugin-config
80
+ \`);
81
+ }
82
+
83
+ function main() {
84
+ const args = process.argv.slice(2);
85
+
86
+ // Handle help flags
87
+ if (args.includes('--help') || args.includes('-h')) {
88
+ showHelp();
89
+ process.exit(0);
90
+ }
91
+
92
+ // Check if injection script exists
93
+ if (!fs.existsSync(injectScriptPath)) {
94
+ console.error(\`❌ Error: Injection script not found at \${injectScriptPath}\`);
95
+ console.error(\` Make sure obsidian-plugin-config is properly installed.\`);
96
+ process.exit(1);
97
+ }
98
+
99
+ // Parse arguments
100
+ const noConfirm = args.includes('--no') || args.includes('-n');
101
+ const dryRun = args.includes('--dry-run');
102
+ const pathArg = args.find(arg => !arg.startsWith('-'));
103
+
104
+ // Determine target path (resolve relative to user's current directory)
105
+ const userCwd = process.cwd();
106
+ let targetPath = pathArg || userCwd;
107
+
108
+ // If relative path, resolve from user's current directory
109
+ if (pathArg && !isAbsolute(pathArg)) {
110
+ targetPath = resolve(userCwd, pathArg);
111
+ }
112
+
113
+ console.log(\`🎯 Obsidian Plugin Config - Global Injection\`);
114
+ console.log(\`📁 Target: \${targetPath}\`);
115
+ console.log(\`❓ Confirmation: \${noConfirm ? 'Disabled (auto-confirm)' : 'Enabled'}\`);
116
+ console.log(\`📦 From: \${packageRoot}\\n\`);
117
+
118
+ try {
119
+ // Check if target directory has package.json
120
+ const targetPackageJson = join(targetPath, 'package.json');
121
+ if (!fs.existsSync(targetPackageJson)) {
122
+ console.error(\`❌ Error: package.json not found in \${targetPath}\`);
123
+ console.error(\` Make sure this is a valid Node.js project.\`);
124
+ process.exit(1);
125
+ }
126
+
127
+ // Prevent injecting into obsidian-plugin-config itself
128
+ const pkg = JSON.parse(fs.readFileSync(targetPackageJson, 'utf8'));
129
+ if (pkg.name === 'obsidian-plugin-config') {
130
+ console.error(\`❌ Cannot inject into obsidian-plugin-config itself.\`);
131
+ process.exit(1);
132
+ }
133
+
134
+ // Clean NPM artifacts if package-lock.json exists
135
+ const packageLockPath = join(targetPath, 'package-lock.json');
136
+ if (fs.existsSync(packageLockPath)) {
137
+ console.log(\`🧹 NPM installation detected, cleaning...\`);
138
+
139
+ try {
140
+ // Remove package-lock.json
141
+ fs.unlinkSync(packageLockPath);
142
+ console.log(\` 🗑️ package-lock.json removed\`);
143
+
144
+ // Remove node_modules if it exists
145
+ const nodeModulesPath = join(targetPath, 'node_modules');
146
+ if (fs.existsSync(nodeModulesPath)) {
147
+ fs.rmSync(nodeModulesPath, { recursive: true, force: true });
148
+ console.log(\` 🗑️ node_modules removed (will be reinstalled with Yarn)\`);
149
+ }
150
+
151
+ console.log(\` ✅ NPM artifacts cleaned to avoid Yarn conflicts\`);
152
+
153
+ } catch (cleanError) {
154
+ console.error(\` ❌ Cleanup failed:\`, cleanError.message);
155
+ console.log(\` 💡 Manually remove package-lock.json and node_modules\`);
156
+ }
157
+ }
158
+
159
+ // Check if tsx is available locally in target
160
+ try {
161
+ execSync('npx tsx --version', {
162
+ cwd: targetPath,
163
+ stdio: 'pipe'
164
+ });
165
+ console.log(\`✅ tsx available locally\`);
166
+ } catch {
167
+ console.log(\`⚠️ tsx not found, installing...\`);
168
+
169
+ // Install tsx locally in target directory
170
+ try {
171
+ execSync('yarn add -D tsx', {
172
+ cwd: targetPath,
173
+ stdio: 'inherit'
174
+ });
175
+ console.log(\`✅ tsx installed successfully\`);
176
+ } catch (installError) {
177
+ console.error(\`❌ tsx installation failed:\`, installError.message);
178
+ console.error(\` Try installing tsx manually: cd "\${targetPath}" && yarn add -D tsx\`);
179
+ process.exit(1);
180
+ }
181
+ }
182
+
183
+ // Execute the injection script with tsx
184
+ const yesOption = noConfirm ? ' --yes' : '';
185
+ const dryRunOption = dryRun ? ' --dry-run' : '';
186
+ const command = \`npx tsx "\${injectScriptPath}" "\${targetPath}"\${yesOption}\${dryRunOption}\`;
187
+
188
+ execSync(command, {
189
+ stdio: 'inherit',
190
+ cwd: targetPath // Use target directory to ensure tsx is available
191
+ });
192
+
193
+ console.log(\`\\n✅ Injection completed successfully!\`);
194
+
195
+ } catch (error) {
196
+ console.error(\`\\n❌ Injection error:\`, error.message);
197
+ process.exit(1);
198
+ }
199
+ }
200
+
201
+ // Run the CLI
202
+ main();
203
+ `;
204
+
205
+ fs.writeFileSync(binPath, binContent, 'utf8');
206
+ console.log(` ✅ Generated ${binPath}`);
207
+ }
208
+
209
+ /**
210
+ * Ensure NPM authentication, prompting login if needed, then verifying success.
211
+ */
212
+ async function ensureNpmAuth(): Promise<void> {
213
+ console.log(`🔐 Checking NPM authentication...`);
214
+
215
+ const checkWhoami = (): string | null => {
216
+ try {
217
+ return execSync('npm whoami --registry https://registry.npmjs.org/', {
218
+ stdio: 'pipe',
219
+ encoding: 'utf8'
220
+ }).trim();
221
+ } catch {
222
+ return null;
223
+ }
224
+ };
225
+
226
+ const whoami = checkWhoami();
227
+ if (whoami) {
228
+ console.log(` ✅ Logged in as: ${whoami}\n`);
229
+ return;
230
+ }
231
+
232
+ console.log(` ⚠️ Not logged in to NPM`);
233
+ console.log(`🔑 Please login to NPM to publish the package`);
234
+ console.log(` Opening browser for authentication...\n`);
235
+
236
+ try {
237
+ execSync('npm login --auth-type=web --registry https://registry.npmjs.org/', {
238
+ stdio: 'inherit'
239
+ });
240
+ } catch {
241
+ console.error(`\n ❌ NPM login failed`);
242
+ console.error(` Please run 'npm login' manually and try again`);
243
+ throw new Error('NPM authentication required');
244
+ }
245
+
246
+ // Verify login actually succeeded
247
+ const whoamiAfter = checkWhoami();
248
+ if (!whoamiAfter) {
249
+ console.error(`\n ❌ Login appeared to complete but authentication could not be verified`);
250
+ throw new Error('NPM authentication required');
251
+ }
252
+
253
+ console.log(`\n ✅ Successfully logged in as: ${whoamiAfter}\n`);
254
+ }
255
+
256
+ /**
257
+ * Complete NPM workflow - Version, Commit, Push, Publish
258
+ */
259
+ async function buildAndPublishNpm(): Promise<void> {
260
+ console.log(`🚀 Obsidian Plugin Config - NPM Publish Workflow`);
261
+ console.log(`Automation: version → bin → verify → commit → publish\n`);
262
+
263
+ try {
264
+ // Step 0: Check NPM authentication
265
+ await ensureNpmAuth();
266
+
267
+ // Step 1: Update version
268
+ console.log(`📋 Step 1/5: Updating version...`);
269
+ execSync('tsx scripts/update-version-config.ts', { stdio: 'inherit' });
270
+
271
+ // Step 2: Generate bin file
272
+ console.log(`\n🔧 Step 2/5: Generating bin/obsidian-inject.js...`);
273
+ await generateBinFile();
274
+
275
+ // Step 3: Verify package
276
+ console.log(`\n📋 Step 3/5: Verifying package...`);
277
+ verifyPackage();
278
+
279
+ // Step 4: Commit and push
280
+ console.log(`\n📤 Step 4/5: Committing and pushing changes...`);
281
+ try {
282
+ // Add all changes
283
+ execSync('git add -A', { stdio: 'pipe' });
284
+
285
+ // Check if there are changes to commit
286
+ const status = execSync('git status --porcelain', { encoding: 'utf8' });
287
+ if (status.trim()) {
288
+ const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
289
+ execSync(`git commit -m "Publish NPM package v${packageJson.version}"`, {
290
+ stdio: 'pipe'
291
+ });
292
+
293
+ // Get current branch and push
294
+ const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
295
+ encoding: 'utf8'
296
+ }).trim();
297
+ execSync(`git push origin ${currentBranch}`, { stdio: 'inherit' });
298
+ console.log(` ✅ Changes committed and pushed`);
299
+ } else {
300
+ console.log(` ℹ️ No changes to commit`);
301
+ }
302
+ } catch (error) {
303
+ console.error(
304
+ ` ❌ Commit/push failed: ${error instanceof Error ? error.message : String(error)}`
305
+ );
306
+ throw error;
307
+ }
308
+
309
+ // Step 5: Publish to NPM
310
+ console.log(`\n📤 Step 5/5: Publishing to NPM...`);
311
+ execSync('npm publish --registry https://registry.npmjs.org/', {
312
+ stdio: 'inherit'
313
+ });
314
+
315
+ // Optional: Update global CLI automatically
316
+ console.log(`\n🌍 Updating global CLI...`);
317
+ console.log(` ⏳ Waiting 30s for NPM registry propagation...`);
318
+ await new Promise((resolve) => setTimeout(resolve, 30000));
319
+ try {
320
+ execSync(
321
+ 'npm install -g obsidian-plugin-config@latest --force --engine-strict=false',
322
+ { stdio: 'inherit' }
323
+ );
324
+ console.log(` ✅ Global CLI updated`);
325
+ } catch {
326
+ console.log(` ⚠️ Global CLI update failed (NPM registry may need more time)`);
327
+ console.log(
328
+ ` 💡 Run manually in a few minutes: npm install -g obsidian-plugin-config@latest --force`
329
+ );
330
+ }
331
+ console.log(`\n🎉 Complete workflow successful!`);
332
+ console.log(` Test: cd any-plugin && obsidian-inject`);
333
+ } catch (error) {
334
+ console.error(
335
+ `\n❌ Workflow failed: ${error instanceof Error ? error.message : String(error)}`
336
+ );
337
+ process.exit(1);
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Verify package is ready for publication
343
+ */
344
+ function verifyPackage(): void {
345
+ // Check required scripts
346
+ const requiredScripts = [
347
+ 'scripts/inject-path.ts',
348
+ 'scripts/inject-prompt.ts',
349
+ 'scripts/inject-core.ts',
350
+ 'scripts/utils.ts',
351
+ 'scripts/acp.ts',
352
+ 'scripts/update-version-config.ts',
353
+ 'scripts/help.ts'
354
+ ];
355
+
356
+ for (const script of requiredScripts) {
357
+ if (!fs.existsSync(script)) {
358
+ throw new Error(`Missing required script: ${script}`);
359
+ }
360
+ }
361
+ console.log(` ✅ All required scripts present`);
362
+
363
+ // Check package.json
364
+ const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
365
+ const requiredFields = [
366
+ 'name',
367
+ 'version',
368
+ 'description',
369
+ 'bin',
370
+ 'repository',
371
+ 'author'
372
+ ];
373
+
374
+ for (const field of requiredFields) {
375
+ if (!packageJson[field]) {
376
+ throw new Error(`Missing required package.json field: ${field}`);
377
+ }
378
+ }
379
+ console.log(` ✅ Package.json valid (v${packageJson.version})`);
380
+
381
+ // Check bin file exists
382
+ if (!fs.existsSync('bin/obsidian-inject.js')) {
383
+ throw new Error(`Missing bin file: bin/obsidian-inject.js`);
384
+ }
385
+ console.log(` ✅ Bin file ready`);
386
+
387
+ // Quick build test
388
+ execSync('tsc --noEmit', { stdio: 'pipe' });
389
+ console.log(` ✅ TypeScript check passed`);
390
+ }
391
+
392
+ // Run the script
393
+ await buildAndPublishNpm();