claude-autopm 3.23.2 → 3.24.1

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/bin/autopm.js CHANGED
@@ -208,6 +208,8 @@ function main() {
208
208
  .command(require('../lib/cli/commands/agent'))
209
209
  // Context management command (STANDALONE)
210
210
  .command(require('../lib/cli/commands/context'))
211
+ // Obsidian vault integration (STANDALONE)
212
+ .command(require('../lib/cli/commands/obsidian'))
211
213
  // Validation command
212
214
  .command('validate', 'Validate ClaudeAutoPM configuration and setup',
213
215
  (yargs) => {
@@ -127,6 +127,44 @@ class Installer {
127
127
  console.log(`${this.colors.RED}✗${this.colors.NC} ${msg}`);
128
128
  }
129
129
 
130
+ /**
131
+ * Print scenario-specific next steps after installation
132
+ */
133
+ printScenarioNextSteps() {
134
+ const scenario = this.currentScenario;
135
+ if (!scenario) return;
136
+
137
+ console.log(`${this.colors.CYAN}📋 Next Steps for "${scenario}" scenario:${this.colors.NC}`);
138
+ console.log('');
139
+
140
+ if (scenario === 'obsidian') {
141
+ console.log(' 1. Configure your Obsidian vault:');
142
+ console.log(` ${this.colors.BOLD}autopm obsidian setup --vault-path "<your-vault-path>"${this.colors.NC}`);
143
+ console.log('');
144
+ console.log(` ${this.colors.DIM}Use quotes around the path. Examples per platform:${this.colors.NC}`);
145
+ console.log(` ${this.colors.DIM} WSL: --vault-path "/mnt/c/Users/You/Documents/My Vault"${this.colors.NC}`);
146
+ console.log(` ${this.colors.DIM} macOS: --vault-path "/Users/you/Documents/My Vault"${this.colors.NC}`);
147
+ console.log(` ${this.colors.DIM} Linux: --vault-path "/home/you/Obsidian/My Vault"${this.colors.NC}`);
148
+ console.log('');
149
+ console.log(' 2. Open the vault folder in Obsidian and install recommended plugins');
150
+ console.log(` ${this.colors.DIM}(Dataview, Templater, Excalidraw, Mermaid Tools)${this.colors.NC}`);
151
+ console.log('');
152
+ console.log(' 3. For continuous sync:');
153
+ console.log(` ${this.colors.BOLD}autopm obsidian sync --watch${this.colors.NC}`);
154
+ console.log('');
155
+ console.log(` ${this.colors.DIM}Verify setup: autopm validate${this.colors.NC}`);
156
+ console.log(` ${this.colors.DIM}Troubleshooting: autopm obsidian doctor${this.colors.NC}`);
157
+ console.log('');
158
+ } else {
159
+ console.log(' 1. Open your project in Claude Code:');
160
+ console.log(` ${this.colors.BOLD}cd your-project && claude${this.colors.NC}`);
161
+ console.log('');
162
+ console.log(' 2. Start working! Slash commands are available inside Claude Code.');
163
+ console.log(` ${this.colors.DIM}Example: /pm:status, /pm:next, /pm:help${this.colors.NC}`);
164
+ console.log('');
165
+ }
166
+ }
167
+
130
168
  async confirm(prompt) {
131
169
  // In test mode or auto-accept mode, auto-answer yes
132
170
  if (process.env.AUTOPM_TEST_MODE === '1' || process.env.AUTOPM_AUTO_ACCEPT === '1') {
@@ -561,6 +599,7 @@ ${this.colors.BOLD}Select installation scenario:${this.colors.NC}
561
599
  • Core + PM + Obsidian vault sync
562
600
  • Unidirectional project → vault mirroring
563
601
  • Best for: Knowledge management, documentation-heavy projects
602
+ • After install, run: ${this.colors.BOLD}autopm obsidian setup --vault-path "<path>"${this.colors.NC}
564
603
  ${this.colors.DIM}• Plugins: core, pm, obsidian (3 plugins)${this.colors.NC}
565
604
  `);
566
605
 
@@ -1525,6 +1564,9 @@ See: https://github.com/rafeekpro/ClaudeAutoPM
1525
1564
  this.printMsg('GREEN', '╚══════════════════════════════════════════╝');
1526
1565
  console.log('');
1527
1566
 
1567
+ // Scenario-specific next steps
1568
+ this.printScenarioNextSteps();
1569
+
1528
1570
  // Run post-installation configuration check
1529
1571
  await this.runPostInstallCheck();
1530
1572
 
@@ -343,7 +343,7 @@ class PostInstallChecker {
343
343
  checkObsidianVault() {
344
344
  const configPath = path.join(this.projectRoot, '.claude', 'config.json');
345
345
  let configured = false;
346
- let message = 'Not configured — run: obsidian:setup';
346
+ let message = 'Not configured — run: autopm obsidian setup --vault-path "<path>"';
347
347
 
348
348
  if (fs.existsSync(configPath)) {
349
349
  try {
@@ -0,0 +1,220 @@
1
+ /**
2
+ * CLI Obsidian Commands
3
+ * Manage Obsidian vault integration — setup, sync, diagnostics
4
+ */
5
+
6
+ const { spawn } = require('child_process');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+
10
+ /**
11
+ * Find project root by walking up from cwd
12
+ */
13
+ function findProjectRoot() {
14
+ let dir = process.cwd();
15
+ while (dir !== path.dirname(dir)) {
16
+ if (fs.existsSync(path.join(dir, '.claude', 'config.json')) ||
17
+ fs.existsSync(path.join(dir, 'CLAUDE.md'))) {
18
+ return dir;
19
+ }
20
+ dir = path.dirname(dir);
21
+ }
22
+ return process.cwd();
23
+ }
24
+
25
+ /**
26
+ * Find scripts directory — checks installed location first, then source repo
27
+ */
28
+ function findScriptsDir(root) {
29
+ const installed = path.join(root, '.claude', 'scripts', 'obsidian');
30
+ if (fs.existsSync(installed)) return installed;
31
+
32
+ const source = path.join(root, 'packages', 'plugin-obsidian', 'scripts', 'obsidian');
33
+ if (fs.existsSync(source)) return source;
34
+
35
+ return null;
36
+ }
37
+
38
+ /**
39
+ * Check if plugin-obsidian is installed, return scripts dir
40
+ */
41
+ function checkPlugin(root) {
42
+ const scriptsDir = findScriptsDir(root);
43
+ if (!scriptsDir) {
44
+ console.error('❌ plugin-obsidian not installed.');
45
+ console.error(' Run: autopm install --scenario=obsidian');
46
+ process.exit(1);
47
+ }
48
+ return scriptsDir;
49
+ }
50
+
51
+ /**
52
+ * autopm obsidian setup
53
+ */
54
+ async function obsidianSetup(argv) {
55
+ const root = findProjectRoot();
56
+ const scriptsDir = checkPlugin(root);
57
+
58
+ const scriptPath = path.join(scriptsDir, 'setup.js');
59
+ if (!fs.existsSync(scriptPath)) {
60
+ console.error('❌ Setup script not found:', scriptPath);
61
+ process.exit(1);
62
+ }
63
+
64
+ const args = [];
65
+ if (argv.vaultPath) args.push('--vault-path', argv.vaultPath);
66
+ if (argv.prefix) args.push('--prefix', argv.prefix);
67
+ if (argv.watch === true) args.push('--watch');
68
+ if (argv.watch === false) args.push('--no-watch');
69
+ args.push('--project-root', root);
70
+
71
+ const child = spawn('node', [scriptPath, ...args], {
72
+ stdio: 'inherit',
73
+ cwd: root
74
+ });
75
+
76
+ child.on('close', (code) => process.exit(code || 0));
77
+ }
78
+
79
+ /**
80
+ * autopm obsidian sync
81
+ */
82
+ async function obsidianSync(argv) {
83
+ const root = findProjectRoot();
84
+ const scriptsDir = checkPlugin(root);
85
+
86
+ // Prefer shell script, fall back to Node
87
+ const shPath = path.join(scriptsDir, 'sync-to-obsidian.sh');
88
+ const jsPath = path.join(scriptsDir, 'sync-to-obsidian.js');
89
+
90
+ const args = [];
91
+ if (argv.watch) args.push('--watch');
92
+ if (argv.check) args.push('--check');
93
+ if (argv.safeMode) args.push('--safe-mode');
94
+ args.push('--project-root', root);
95
+
96
+ let cmd, cmdArgs;
97
+ if (fs.existsSync(shPath)) {
98
+ cmd = 'bash';
99
+ cmdArgs = [shPath, ...args];
100
+ } else if (fs.existsSync(jsPath)) {
101
+ cmd = 'node';
102
+ cmdArgs = [jsPath, ...args];
103
+ } else {
104
+ console.error('❌ Sync script not found. Reinstall plugin-obsidian.');
105
+ process.exit(1);
106
+ }
107
+
108
+ const child = spawn(cmd, cmdArgs, {
109
+ stdio: 'inherit',
110
+ cwd: root
111
+ });
112
+
113
+ child.on('close', (code) => process.exit(code || 0));
114
+ }
115
+
116
+ /**
117
+ * autopm obsidian doctor
118
+ */
119
+ async function obsidianDoctor(argv) {
120
+ const root = findProjectRoot();
121
+ const scriptsDir = checkPlugin(root);
122
+
123
+ const scriptPath = path.join(scriptsDir, 'doctor.js');
124
+ if (!fs.existsSync(scriptPath)) {
125
+ console.error('❌ Doctor script not found:', scriptPath);
126
+ process.exit(1);
127
+ }
128
+
129
+ const args = ['--project-root', root];
130
+
131
+ const child = spawn('node', [scriptPath, ...args], {
132
+ stdio: 'inherit',
133
+ cwd: root
134
+ });
135
+
136
+ child.on('close', (code) => process.exit(code || 0));
137
+ }
138
+
139
+ /**
140
+ * Command builder — registers subcommands
141
+ */
142
+ function builder(yargs) {
143
+ return yargs
144
+ .command(
145
+ 'setup',
146
+ 'Configure Obsidian vault integration',
147
+ (yargs) => {
148
+ return yargs
149
+ .option('vault-path', {
150
+ describe: 'Path to your Obsidian vault folder (use quotes if path has spaces)',
151
+ type: 'string',
152
+ demandOption: true
153
+ })
154
+ .option('prefix', {
155
+ describe: 'Subfolder name in vault (default: project directory name)',
156
+ type: 'string'
157
+ })
158
+ .option('watch', {
159
+ describe: 'Enable continuous sync after setup',
160
+ type: 'boolean'
161
+ })
162
+ .example('autopm obsidian setup --vault-path "/mnt/c/Users/You/My Vault"', 'WSL')
163
+ .example('autopm obsidian setup --vault-path "/Users/you/My Vault"', 'macOS')
164
+ .example('autopm obsidian setup --vault-path "/home/you/My Vault" --prefix my-project', 'Linux with prefix');
165
+ },
166
+ obsidianSetup
167
+ )
168
+ .command(
169
+ 'sync',
170
+ 'Sync project files to Obsidian vault',
171
+ (yargs) => {
172
+ return yargs
173
+ .option('watch', {
174
+ describe: 'Continuous sync on file changes',
175
+ type: 'boolean',
176
+ default: false
177
+ })
178
+ .option('check', {
179
+ describe: 'Dry-run: show what would be synced',
180
+ type: 'boolean',
181
+ default: false
182
+ })
183
+ .option('safe-mode', {
184
+ describe: 'Never delete vault files (omit --delete from rsync)',
185
+ type: 'boolean',
186
+ default: false
187
+ })
188
+ .example('autopm obsidian sync', 'One-shot sync')
189
+ .example('autopm obsidian sync --watch', 'Continuous sync')
190
+ .example('autopm obsidian sync --check', 'Dry-run');
191
+ },
192
+ obsidianSync
193
+ )
194
+ .command(
195
+ 'doctor',
196
+ 'Diagnose common Obsidian integration issues',
197
+ (yargs) => {
198
+ return yargs
199
+ .example('autopm obsidian doctor', 'Run all diagnostic checks');
200
+ },
201
+ obsidianDoctor
202
+ )
203
+ .demandCommand(1, 'You must specify an obsidian command')
204
+ .strictCommands()
205
+ .help();
206
+ }
207
+
208
+ module.exports = {
209
+ command: 'obsidian',
210
+ describe: 'Manage Obsidian vault integration (setup, sync, diagnostics)',
211
+ builder,
212
+ handler: (argv) => {
213
+ console.log('\nUsage: autopm obsidian <command>\n');
214
+ console.log('Commands:');
215
+ console.log(' setup Configure Obsidian vault integration');
216
+ console.log(' sync Sync project files to Obsidian vault');
217
+ console.log(' doctor Diagnose common integration issues');
218
+ console.log('\nRun autopm obsidian <command> --help for details\n');
219
+ }
220
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-autopm",
3
- "version": "3.23.2",
3
+ "version": "3.24.1",
4
4
  "description": "Autonomous Project Management Framework for Claude Code - Advanced AI-powered development automation",
5
5
  "main": "bin/autopm.js",
6
6
  "workspaces": [
@@ -50,26 +50,32 @@ autopm install
50
50
 
51
51
  ## Commands
52
52
 
53
- ### `obsidian:setup`
53
+ Available from your terminal via `autopm obsidian <command>` and as `/obsidian:<command>` slash commands inside Claude Code.
54
54
 
55
- Interactive vault configuration wizard. Run once after install.
55
+ ### `autopm obsidian setup`
56
+
57
+ Configure your Obsidian vault. Run once after install:
56
58
 
57
59
  ```bash
58
- /obsidian:setup --vault-path /path/to/vault --prefix my-project
60
+ autopm obsidian setup --vault-path "/path/to/your vault" --prefix my-project
61
+
62
+ # WSL: --vault-path "/mnt/c/Users/You/Documents/My Vault"
63
+ # macOS: --vault-path "/Users/you/Documents/My Vault"
64
+ # Linux: --vault-path "/home/you/Obsidian/My Vault"
59
65
  ```
60
66
 
61
- ### `obsidian:sync`
67
+ ### `autopm obsidian sync`
62
68
 
63
69
  Sync project files to the Obsidian vault.
64
70
 
65
71
  ```bash
66
- /obsidian:sync # One-shot sync
67
- /obsidian:sync --watch # Continuous sync on file changes
68
- /obsidian:sync --check # Dry-run (show what would sync)
69
- /obsidian:sync --safe-mode # Don't delete vault files
72
+ autopm obsidian sync # One-shot sync
73
+ autopm obsidian sync --watch # Continuous sync on file changes
74
+ autopm obsidian sync --check # Dry-run (show what would sync)
75
+ autopm obsidian sync --safe-mode # Don't delete vault files
70
76
  ```
71
77
 
72
- ### `obsidian:doctor`
78
+ ### `autopm obsidian doctor`
73
79
 
74
80
  Diagnose common integration problems:
75
81
 
@@ -20,6 +20,24 @@
20
20
  "tags": ["obsidian", "vault", "sync", "dataview", "integration"]
21
21
  },
22
22
  "scripts": [
23
+ {
24
+ "name": "setup",
25
+ "file": "scripts/obsidian/setup.js",
26
+ "description": "Interactive vault configuration wizard",
27
+ "type": "workflow",
28
+ "executable": true,
29
+ "category": "setup",
30
+ "tags": ["setup", "wizard", "vault"]
31
+ },
32
+ {
33
+ "name": "doctor",
34
+ "file": "scripts/obsidian/doctor.js",
35
+ "description": "Diagnostic checks for common integration issues",
36
+ "type": "utility",
37
+ "executable": true,
38
+ "category": "diagnostics",
39
+ "tags": ["doctor", "diagnostics", "vault"]
40
+ },
23
41
  {
24
42
  "name": "sync-to-obsidian",
25
43
  "file": "scripts/obsidian/sync-to-obsidian.sh",
@@ -158,7 +158,12 @@ function substituteTemplate(content, vars) {
158
158
  // ─── Template generation ────────────────────────────────────────────
159
159
 
160
160
  function generateTemplates(vaultDest, prefix, projectRoot) {
161
- const templatesDir = join(__dirname, '..', '..', 'templates');
161
+ // Find templates: check relative to script first, then source repo from project root
162
+ const candidates = [
163
+ join(__dirname, '..', '..', 'templates'),
164
+ join(projectRoot, 'packages', 'plugin-obsidian', 'templates'),
165
+ ];
166
+ const templatesDir = candidates.find(d => existsSync(join(d, 'MOC.md.tmpl'))) || candidates[0];
162
167
  const createdDate = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
163
168
  const projectName = prefix;
164
169
 
@@ -36,27 +36,27 @@ describe('plugin-obsidian documentation', () => {
36
36
  describe('packages/plugin-obsidian/README.md', () => {
37
37
  const readmePath = resolve(root, 'packages', 'plugin-obsidian', 'README.md');
38
38
 
39
- it('mentions obsidian:setup', () => {
39
+ it('mentions obsidian setup command', () => {
40
40
  const content = readFileSync(readmePath, 'utf8');
41
41
  assert.ok(
42
- content.includes('obsidian:setup'),
43
- 'README must mention obsidian:setup'
42
+ content.includes('obsidian setup'),
43
+ 'README must mention obsidian setup'
44
44
  );
45
45
  });
46
46
 
47
- it('mentions obsidian:sync', () => {
47
+ it('mentions obsidian sync command', () => {
48
48
  const content = readFileSync(readmePath, 'utf8');
49
49
  assert.ok(
50
- content.includes('obsidian:sync'),
51
- 'README must mention obsidian:sync'
50
+ content.includes('obsidian sync'),
51
+ 'README must mention obsidian sync'
52
52
  );
53
53
  });
54
54
 
55
- it('mentions obsidian:doctor', () => {
55
+ it('mentions obsidian doctor command', () => {
56
56
  const content = readFileSync(readmePath, 'utf8');
57
57
  assert.ok(
58
- content.includes('obsidian:doctor'),
59
- 'README must mention obsidian:doctor'
58
+ content.includes('obsidian doctor'),
59
+ 'README must mention obsidian doctor'
60
60
  );
61
61
  });
62
62
  });
@@ -64,21 +64,11 @@ describe('plugin-obsidian documentation', () => {
64
64
  describe('CHANGELOG.md', () => {
65
65
  const changelogPath = resolve(root, 'CHANGELOG.md');
66
66
 
67
- it('mentions plugin-obsidian under Unreleased', () => {
67
+ it('mentions plugin-obsidian', () => {
68
68
  const content = readFileSync(changelogPath, 'utf8');
69
- const unreleasedIdx = content.indexOf('## [Unreleased]');
70
- assert.ok(unreleasedIdx !== -1, 'CHANGELOG must have an [Unreleased] section');
71
-
72
- // Find the next version heading after Unreleased
73
- const afterUnreleased = content.slice(unreleasedIdx + '## [Unreleased]'.length);
74
- const nextVersionIdx = afterUnreleased.indexOf('\n## [');
75
- const unreleasedSection = nextVersionIdx !== -1
76
- ? afterUnreleased.slice(0, nextVersionIdx)
77
- : afterUnreleased;
78
-
79
69
  assert.ok(
80
- unreleasedSection.includes('plugin-obsidian'),
81
- 'Unreleased section must mention plugin-obsidian'
70
+ content.includes('plugin-obsidian'),
71
+ 'CHANGELOG must mention plugin-obsidian'
82
72
  );
83
73
  });
84
74
  });