claude-autopm 3.24.2 → 3.25.2
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/commands/plugin.js
CHANGED
|
@@ -266,8 +266,10 @@ async function handleInstall(manager, pluginName, argv) {
|
|
|
266
266
|
console.log(chalk.gray(`npm package already installed: @claudeautopm/${fullPluginName}`));
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
//
|
|
270
|
-
console.log(chalk.gray('
|
|
269
|
+
// Discover and install plugin resources
|
|
270
|
+
console.log(chalk.gray('Discovering plugins...'));
|
|
271
|
+
await manager.initialize();
|
|
272
|
+
console.log(chalk.gray('Installing plugin resources...'));
|
|
271
273
|
const result = await manager.installPlugin(fullPluginName);
|
|
272
274
|
|
|
273
275
|
console.log(chalk.green(`\n✓ Plugin installed successfully!`));
|
|
@@ -131,23 +131,64 @@ class PluginManager extends EventEmitter {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* Discover all installed plugins in node_modules
|
|
134
|
+
* Discover all installed plugins in node_modules (local, global, and bundled)
|
|
135
135
|
* Based on npm workspaces pattern from Context7
|
|
136
136
|
*/
|
|
137
137
|
async discoverPlugins() {
|
|
138
138
|
const pluginPattern = `${this.options.scopePrefix}/plugin-`;
|
|
139
|
-
const nodeModulesPath = this.options.pluginDir;
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const scopePath = path.join(nodeModulesPath, this.options.scopePrefix);
|
|
140
|
+
// Check multiple locations: local node_modules, global npm prefix, bundled packages/
|
|
141
|
+
const searchPaths = [this.options.pluginDir];
|
|
144
142
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
// Add global npm path
|
|
144
|
+
try {
|
|
145
|
+
const { execSync } = require('child_process');
|
|
146
|
+
const globalPath = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
147
|
+
if (globalPath && !searchPaths.includes(globalPath)) {
|
|
148
|
+
searchPaths.push(globalPath);
|
|
148
149
|
}
|
|
150
|
+
} catch {
|
|
151
|
+
// npm not available or error — skip global
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Add bundled packages/ directory (for development / autopm source repo)
|
|
155
|
+
const bundledPath = path.join(this.options.projectRoot, 'packages');
|
|
156
|
+
if (fs.existsSync(bundledPath)) {
|
|
157
|
+
// Bundled plugins are at packages/plugin-*/plugin.json (not under @claudeautopm scope)
|
|
158
|
+
try {
|
|
159
|
+
const entries = fs.readdirSync(bundledPath);
|
|
160
|
+
for (const entry of entries) {
|
|
161
|
+
if (!entry.startsWith('plugin-')) continue;
|
|
162
|
+
const pluginJsonPath = path.join(bundledPath, entry, 'plugin.json');
|
|
163
|
+
if (!fs.existsSync(pluginJsonPath)) continue;
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const metadata = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf-8'));
|
|
167
|
+
const fullName = `${this.options.scopePrefix}/${entry}`;
|
|
168
|
+
if (!this.plugins.has(fullName)) {
|
|
169
|
+
this.plugins.set(fullName, {
|
|
170
|
+
name: fullName,
|
|
171
|
+
path: path.join(bundledPath, entry),
|
|
172
|
+
metadata,
|
|
173
|
+
loaded: false
|
|
174
|
+
});
|
|
175
|
+
this.emit('discover:found', { name: fullName, source: 'bundled' });
|
|
176
|
+
}
|
|
177
|
+
} catch { /* skip invalid */ }
|
|
178
|
+
}
|
|
179
|
+
} catch { /* skip unreadable */ }
|
|
180
|
+
}
|
|
149
181
|
|
|
150
|
-
|
|
182
|
+
for (const nodeModulesPath of searchPaths) {
|
|
183
|
+
try {
|
|
184
|
+
// Check if scoped directory exists
|
|
185
|
+
const scopePath = path.join(nodeModulesPath, this.options.scopePrefix);
|
|
186
|
+
|
|
187
|
+
if (!fs.existsSync(scopePath)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const scopedPackages = fs.readdirSync(scopePath);
|
|
151
192
|
|
|
152
193
|
for (const packageName of scopedPackages) {
|
|
153
194
|
if (!packageName.startsWith('plugin-')) {
|
|
@@ -186,8 +227,9 @@ class PluginManager extends EventEmitter {
|
|
|
186
227
|
}
|
|
187
228
|
} catch (error) {
|
|
188
229
|
this.emit('discover:error', { error: error.message });
|
|
189
|
-
|
|
230
|
+
// Continue to next search path instead of failing
|
|
190
231
|
}
|
|
232
|
+
} // end for searchPaths
|
|
191
233
|
}
|
|
192
234
|
|
|
193
235
|
/**
|
|
@@ -223,7 +265,10 @@ class PluginManager extends EventEmitter {
|
|
|
223
265
|
* Implements factory pattern from unplugin Context7 research
|
|
224
266
|
*/
|
|
225
267
|
async loadPlugin(pluginName) {
|
|
226
|
-
|
|
268
|
+
let plugin = this.plugins.get(pluginName);
|
|
269
|
+
if (!plugin && !pluginName.includes('/')) {
|
|
270
|
+
plugin = this.plugins.get(`${this.options.scopePrefix}/${pluginName}`);
|
|
271
|
+
}
|
|
227
272
|
|
|
228
273
|
if (!plugin) {
|
|
229
274
|
throw new Error(`Plugin not found: ${pluginName}`);
|
|
@@ -252,7 +297,7 @@ class PluginManager extends EventEmitter {
|
|
|
252
297
|
|
|
253
298
|
this.emit('load:complete', {
|
|
254
299
|
name: pluginName,
|
|
255
|
-
agentCount: plugin.metadata.agents.length
|
|
300
|
+
agentCount: (plugin.metadata.agents || []).length
|
|
256
301
|
});
|
|
257
302
|
|
|
258
303
|
return plugin;
|
|
@@ -268,7 +313,7 @@ class PluginManager extends EventEmitter {
|
|
|
268
313
|
async registerAgents(plugin) {
|
|
269
314
|
const { metadata, path: pluginPath } = plugin;
|
|
270
315
|
|
|
271
|
-
for (const agentMeta of metadata.agents) {
|
|
316
|
+
for (const agentMeta of (metadata.agents || [])) {
|
|
272
317
|
const agentId = `${plugin.name}:${agentMeta.name}`;
|
|
273
318
|
const agentFilePath = path.join(pluginPath, agentMeta.file);
|
|
274
319
|
|
|
@@ -302,7 +347,11 @@ class PluginManager extends EventEmitter {
|
|
|
302
347
|
* Supports: agents, commands, rules, hooks, scripts (Schema v2.0)
|
|
303
348
|
*/
|
|
304
349
|
async installPlugin(pluginName) {
|
|
305
|
-
|
|
350
|
+
// Normalize name: accept both "plugin-obsidian" and "@claudeautopm/plugin-obsidian"
|
|
351
|
+
let plugin = this.plugins.get(pluginName);
|
|
352
|
+
if (!plugin && !pluginName.includes('/')) {
|
|
353
|
+
plugin = this.plugins.get(`${this.options.scopePrefix}/${pluginName}`);
|
|
354
|
+
}
|
|
306
355
|
|
|
307
356
|
if (!plugin) {
|
|
308
357
|
throw new Error(`Plugin not found: ${pluginName}`);
|
|
@@ -451,6 +500,26 @@ class PluginManager extends EventEmitter {
|
|
|
451
500
|
const installed = [];
|
|
452
501
|
|
|
453
502
|
for (const command of metadata.commands) {
|
|
503
|
+
// Handle auto-discovery from subdirectory
|
|
504
|
+
if (command.subdirectory && command.discovery === 'auto') {
|
|
505
|
+
const commandsSourceDir = path.join(pluginPath, command.subdirectory);
|
|
506
|
+
if (fs.existsSync(commandsSourceDir)) {
|
|
507
|
+
const files = fs.readdirSync(commandsSourceDir).filter(f => f.endsWith('.md'));
|
|
508
|
+
for (const file of files) {
|
|
509
|
+
const sourcePath = path.join(commandsSourceDir, file);
|
|
510
|
+
const targetPath = path.join(targetDir, file);
|
|
511
|
+
if (!fs.existsSync(targetPath)) {
|
|
512
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
513
|
+
installed.push({ name: file.replace('.md', ''), file: targetPath });
|
|
514
|
+
this.emit('install:command', { command: file, path: targetPath });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Handle individual command files
|
|
522
|
+
if (!command.file) continue;
|
|
454
523
|
const sourcePath = path.join(pluginPath, command.file);
|
|
455
524
|
const targetPath = path.join(targetDir, path.basename(command.file));
|
|
456
525
|
|
package/package.json
CHANGED
|
@@ -75,6 +75,15 @@ autopm obsidian sync --check # Dry-run (show what would sync)
|
|
|
75
75
|
autopm obsidian sync --safe-mode # Don't delete vault files
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
### `/obsidian:init` (Claude Code)
|
|
79
|
+
|
|
80
|
+
Generate project-aware vault files by reading your actual project structure. Unlike generic templates, this reads your real issues, epics, agents, and code to create tailored MOC, Dashboard, diagrams, and templates.
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
/obsidian:init # Generate vault files
|
|
84
|
+
/obsidian:init --force # Overwrite existing files
|
|
85
|
+
```
|
|
86
|
+
|
|
78
87
|
### `autopm obsidian doctor`
|
|
79
88
|
|
|
80
89
|
Diagnose common integration problems:
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
---
|
|
2
|
+
allowed-tools: Bash, Read, Write, Glob, Grep, LS
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Obsidian Init
|
|
6
|
+
|
|
7
|
+
Generate project-aware Obsidian vault files by reading the actual project structure.
|
|
8
|
+
|
|
9
|
+
Unlike generic templates, this command reads your project's real content — issues, epics, PRDs, agents, rules, code structure — and generates MOC, Dashboard, templates, and diagrams tailored to what actually exists.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
/obsidian:init [--force]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
- `--force` — Overwrite existing vault files (default: skip if they exist)
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
Run `autopm obsidian setup --vault-path "<path>"` first. This command reads the vault config from `.claude/config.json`.
|
|
23
|
+
|
|
24
|
+
## Instructions
|
|
25
|
+
|
|
26
|
+
### 1. Read Configuration
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Get vault config
|
|
30
|
+
node -e "
|
|
31
|
+
const c = JSON.parse(require('fs').readFileSync('.claude/config.json','utf8'));
|
|
32
|
+
if (!c.obsidian || !c.obsidian.vault_path) { console.error('ERROR: Run autopm obsidian setup first'); process.exit(1); }
|
|
33
|
+
console.log(JSON.stringify(c.obsidian));
|
|
34
|
+
"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Extract `vault_path` and `vault_prefix` from the output. The target directory for all generated files is `{vault_path}/{vault_prefix}/`.
|
|
38
|
+
|
|
39
|
+
### 2. Discover Project Structure
|
|
40
|
+
|
|
41
|
+
Read the project to understand what exists. Gather:
|
|
42
|
+
|
|
43
|
+
**Issues/Tasks:**
|
|
44
|
+
- Glob for `issues/**/*.md` and `.claude/epics/**/*.md`
|
|
45
|
+
- Read frontmatter of each: extract `name`, `status`, `type`, `tags`, `github`
|
|
46
|
+
- Count by status: open, in-progress, closed
|
|
47
|
+
|
|
48
|
+
**PRDs:**
|
|
49
|
+
- Glob for `.claude/prds/**/*.md` or `prds/**/*.md`
|
|
50
|
+
- Read frontmatter: `name`, `status`
|
|
51
|
+
|
|
52
|
+
**Epics:**
|
|
53
|
+
- Glob for `.claude/epics/*/epic.md`
|
|
54
|
+
- Read frontmatter: `name`, `status`, `progress`
|
|
55
|
+
|
|
56
|
+
**Agents:**
|
|
57
|
+
- Glob for `.claude/agents/**/*.md` (exclude README, REGISTRY)
|
|
58
|
+
- Extract agent names and categories from directory structure
|
|
59
|
+
|
|
60
|
+
**Rules:**
|
|
61
|
+
- Glob for `.claude/rules/*.md`
|
|
62
|
+
- Extract rule names
|
|
63
|
+
|
|
64
|
+
**Commands:**
|
|
65
|
+
- Glob for `.claude/commands/*.md`
|
|
66
|
+
- Extract command names (the `pm:*`, `obsidian:*` prefixes)
|
|
67
|
+
|
|
68
|
+
**Code structure:**
|
|
69
|
+
- Check for common project indicators: `package.json`, `requirements.txt`, `go.mod`, `Cargo.toml`, `pom.xml`
|
|
70
|
+
- Read `package.json` if exists: extract `name`, `description`, `scripts`, main dependencies
|
|
71
|
+
- Check for `src/`, `lib/`, `app/`, `api/` directories
|
|
72
|
+
- Check for test directories: `tests/`, `test/`, `__tests__/`, `spec/`
|
|
73
|
+
|
|
74
|
+
### 3. Generate MOC.md
|
|
75
|
+
|
|
76
|
+
Write `{vault_path}/{vault_prefix}/MOC.md` with content based on what was discovered.
|
|
77
|
+
|
|
78
|
+
The MOC (Map of Content) should be a navigation hub. Structure it as:
|
|
79
|
+
|
|
80
|
+
```markdown
|
|
81
|
+
---
|
|
82
|
+
type: moc
|
|
83
|
+
title: "{project_name} — Map of Content"
|
|
84
|
+
created: "{current_iso_date}"
|
|
85
|
+
updated: "{current_iso_date}"
|
|
86
|
+
tags: [moc, navigation]
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
# {project_name} — Map of Content
|
|
90
|
+
|
|
91
|
+
> Auto-generated from project structure. Regenerate with `/obsidian:init`.
|
|
92
|
+
|
|
93
|
+
## Project Overview
|
|
94
|
+
|
|
95
|
+
{One paragraph summary based on package.json description or README first paragraph}
|
|
96
|
+
|
|
97
|
+
Tech stack: {detected languages/frameworks}
|
|
98
|
+
|
|
99
|
+
## Epics
|
|
100
|
+
|
|
101
|
+
{If epics exist, create a Dataview TABLE query. If no epics, write "No epics yet."}
|
|
102
|
+
|
|
103
|
+
```dataview
|
|
104
|
+
TABLE status, progress
|
|
105
|
+
FROM "{prefix}/epics"
|
|
106
|
+
WHERE type = "epic"
|
|
107
|
+
SORT status ASC
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Active Epics
|
|
111
|
+
{List each discovered epic with a direct link: `- [[{prefix}/epics/{name}/epic|{title}]] — {status}`}
|
|
112
|
+
|
|
113
|
+
## Issues & Tasks
|
|
114
|
+
|
|
115
|
+
{If issues exist, create Dataview queries grouped by status}
|
|
116
|
+
|
|
117
|
+
```dataview
|
|
118
|
+
TABLE status, tags
|
|
119
|
+
FROM "{prefix}/issues"
|
|
120
|
+
WHERE status = "open" OR status = "in-progress"
|
|
121
|
+
SORT status ASC, file.mtime DESC
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Summary:** {X} open, {Y} in-progress, {Z} closed
|
|
125
|
+
|
|
126
|
+
## PRDs
|
|
127
|
+
|
|
128
|
+
{If PRDs exist, list them with links. Otherwise "No PRDs yet."}
|
|
129
|
+
|
|
130
|
+
```dataview
|
|
131
|
+
TABLE status, created
|
|
132
|
+
FROM "{prefix}/prds"
|
|
133
|
+
SORT created DESC
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Agents ({count} available)
|
|
137
|
+
|
|
138
|
+
{Group agents by category with links}
|
|
139
|
+
|
|
140
|
+
### Core
|
|
141
|
+
{List core agents}
|
|
142
|
+
|
|
143
|
+
### Languages
|
|
144
|
+
{List language agents}
|
|
145
|
+
|
|
146
|
+
### {Other categories}
|
|
147
|
+
{List other agents}
|
|
148
|
+
|
|
149
|
+
## Rules ({count} active)
|
|
150
|
+
|
|
151
|
+
{List rules as a simple bullet list with links}
|
|
152
|
+
|
|
153
|
+
## Commands ({count} available)
|
|
154
|
+
|
|
155
|
+
{Group commands by prefix: pm:*, obsidian:*, etc.}
|
|
156
|
+
|
|
157
|
+
## Recent Changes
|
|
158
|
+
|
|
159
|
+
```dataview
|
|
160
|
+
TABLE file.mtime as "Modified"
|
|
161
|
+
FROM "{prefix}"
|
|
162
|
+
SORT file.mtime DESC
|
|
163
|
+
LIMIT 15
|
|
164
|
+
```
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 4. Generate DASHBOARD.md
|
|
168
|
+
|
|
169
|
+
Write `{vault_path}/{vault_prefix}/DASHBOARD.md` with project status overview:
|
|
170
|
+
|
|
171
|
+
```markdown
|
|
172
|
+
---
|
|
173
|
+
type: dashboard
|
|
174
|
+
title: "{project_name} Dashboard"
|
|
175
|
+
created: "{current_iso_date}"
|
|
176
|
+
updated: "{current_iso_date}"
|
|
177
|
+
tags: [dashboard, overview]
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
# {project_name} Dashboard
|
|
181
|
+
|
|
182
|
+
> Auto-generated. Regenerate with `/obsidian:init`.
|
|
183
|
+
|
|
184
|
+
## Status at a Glance
|
|
185
|
+
|
|
186
|
+
| Metric | Count |
|
|
187
|
+
|--------|-------|
|
|
188
|
+
| Open Issues | {actual_count} |
|
|
189
|
+
| In Progress | {actual_count} |
|
|
190
|
+
| Closed | {actual_count} |
|
|
191
|
+
| Epics | {actual_count} |
|
|
192
|
+
| PRDs | {actual_count} |
|
|
193
|
+
| Agents | {actual_count} |
|
|
194
|
+
| Rules | {actual_count} |
|
|
195
|
+
|
|
196
|
+
## Open Work
|
|
197
|
+
|
|
198
|
+
```dataview
|
|
199
|
+
TABLE status, tags, file.mtime as "Updated"
|
|
200
|
+
FROM "{prefix}/issues" OR "{prefix}/epics"
|
|
201
|
+
WHERE status = "open" OR status = "in-progress"
|
|
202
|
+
SORT status ASC, file.mtime DESC
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## In Progress
|
|
206
|
+
|
|
207
|
+
```dataview
|
|
208
|
+
LIST
|
|
209
|
+
FROM "{prefix}"
|
|
210
|
+
WHERE status = "in-progress"
|
|
211
|
+
SORT file.mtime DESC
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Recently Completed
|
|
215
|
+
|
|
216
|
+
```dataview
|
|
217
|
+
TABLE status, file.mtime as "Completed"
|
|
218
|
+
FROM "{prefix}"
|
|
219
|
+
WHERE status = "closed" OR status = "completed"
|
|
220
|
+
SORT file.mtime DESC
|
|
221
|
+
LIMIT 10
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## By Type
|
|
225
|
+
|
|
226
|
+
```dataview
|
|
227
|
+
TABLE WITHOUT ID
|
|
228
|
+
type as "Type",
|
|
229
|
+
length(rows) as "Count"
|
|
230
|
+
FROM "{prefix}"
|
|
231
|
+
WHERE type
|
|
232
|
+
GROUP BY type
|
|
233
|
+
SORT length(rows) DESC
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Quick Links
|
|
237
|
+
|
|
238
|
+
- [[{prefix}/MOC|Map of Content]]
|
|
239
|
+
- [[{prefix}/agents|Agents]]
|
|
240
|
+
- [[{prefix}/rules|Rules]]
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### 5. Generate _templates/
|
|
244
|
+
|
|
245
|
+
Create Obsidian Templater-compatible templates based on the project's actual frontmatter conventions.
|
|
246
|
+
|
|
247
|
+
Read 2-3 existing issue files to understand what fields are used. Then generate templates that match.
|
|
248
|
+
|
|
249
|
+
Write to `{vault_path}/{vault_prefix}/_templates/`:
|
|
250
|
+
|
|
251
|
+
**issue.md** — based on actual issue frontmatter fields found:
|
|
252
|
+
```markdown
|
|
253
|
+
---
|
|
254
|
+
type: issue
|
|
255
|
+
title: "<% tp.file.title %>"
|
|
256
|
+
status: open
|
|
257
|
+
created: "<% tp.date.now('YYYY-MM-DDTHH:mm:ss') %>Z"
|
|
258
|
+
updated: "<% tp.date.now('YYYY-MM-DDTHH:mm:ss') %>Z"
|
|
259
|
+
tags: [issue]
|
|
260
|
+
{any_other_fields_found_in_existing_issues}
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
# <% tp.file.title %>
|
|
264
|
+
|
|
265
|
+
## Description
|
|
266
|
+
|
|
267
|
+
## Acceptance Criteria
|
|
268
|
+
|
|
269
|
+
- [ ]
|
|
270
|
+
|
|
271
|
+
## Technical Details
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**prd.md** and **epic.md** — same approach, based on actual frontmatter conventions found.
|
|
275
|
+
|
|
276
|
+
### 6. Generate Architecture Diagram
|
|
277
|
+
|
|
278
|
+
Write `{vault_path}/{vault_prefix}/diagrams/01-architecture.md` with a Mermaid diagram reflecting the actual project structure:
|
|
279
|
+
|
|
280
|
+
```markdown
|
|
281
|
+
---
|
|
282
|
+
type: diagram
|
|
283
|
+
title: "{project_name} Architecture"
|
|
284
|
+
tags: [diagram, mermaid, architecture]
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
# {project_name} Architecture
|
|
288
|
+
|
|
289
|
+
> Auto-generated from project structure. Edit to match your actual architecture.
|
|
290
|
+
|
|
291
|
+
```mermaid
|
|
292
|
+
graph TB
|
|
293
|
+
subgraph "Project: {project_name}"
|
|
294
|
+
{Generate nodes based on discovered directories}
|
|
295
|
+
{e.g., if src/api/ exists: API[API Layer]}
|
|
296
|
+
{e.g., if src/models/ exists: Models[Data Models]}
|
|
297
|
+
{Connect related components}
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
subgraph "ClaudeAutoPM"
|
|
301
|
+
PM[PM System]
|
|
302
|
+
Agents["{agent_count} Agents"]
|
|
303
|
+
Rules["{rule_count} Rules"]
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
subgraph "Obsidian Vault"
|
|
307
|
+
MOC[MOC.md]
|
|
308
|
+
Dashboard[DASHBOARD.md]
|
|
309
|
+
Issues["{issue_count} Issues"]
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
PM -->|sync| Issues
|
|
313
|
+
Agents -->|documented in| MOC
|
|
314
|
+
```
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
If the project is simple (no src/ structure), generate a simpler diagram showing just the ClaudeAutoPM + Obsidian relationship.
|
|
318
|
+
|
|
319
|
+
### 7. Generate Excalidraw Whiteboard
|
|
320
|
+
|
|
321
|
+
Write `{vault_path}/{vault_prefix}/diagrams/pizarra.excalidraw.md`:
|
|
322
|
+
|
|
323
|
+
```markdown
|
|
324
|
+
---
|
|
325
|
+
type: diagram
|
|
326
|
+
title: Whiteboard
|
|
327
|
+
tags: [diagram, excalidraw, whiteboard]
|
|
328
|
+
excalidraw-plugin: parsed
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
# Whiteboard
|
|
332
|
+
|
|
333
|
+
Open this file in Excalidraw to start drawing.
|
|
334
|
+
|
|
335
|
+
%%
|
|
336
|
+
# Excalidraw Data
|
|
337
|
+
|
|
338
|
+
## Drawing
|
|
339
|
+
```json
|
|
340
|
+
{
|
|
341
|
+
"type": "excalidraw",
|
|
342
|
+
"version": 2,
|
|
343
|
+
"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin",
|
|
344
|
+
"elements": [],
|
|
345
|
+
"appState": {
|
|
346
|
+
"gridSize": null,
|
|
347
|
+
"viewBackgroundColor": "#ffffff"
|
|
348
|
+
},
|
|
349
|
+
"files": {}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
%%
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 8. Run Sync
|
|
356
|
+
|
|
357
|
+
After generating all files, run a sync to push everything to the vault:
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
# Find and run sync script
|
|
361
|
+
if [ -f ".claude/scripts/obsidian/sync-to-obsidian.sh" ]; then
|
|
362
|
+
bash .claude/scripts/obsidian/sync-to-obsidian.sh
|
|
363
|
+
elif [ -f "packages/plugin-obsidian/scripts/obsidian/sync-to-obsidian.sh" ]; then
|
|
364
|
+
bash packages/plugin-obsidian/scripts/obsidian/sync-to-obsidian.sh
|
|
365
|
+
fi
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 9. Output Summary
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
Obsidian vault initialized for {project_name}
|
|
372
|
+
|
|
373
|
+
Generated:
|
|
374
|
+
MOC.md — {X} sections, {Y} linked items
|
|
375
|
+
DASHBOARD.md — {open} open, {in_progress} in progress, {closed} closed
|
|
376
|
+
_templates/ — {N} templates based on project conventions
|
|
377
|
+
diagrams/ — architecture + whiteboard
|
|
378
|
+
|
|
379
|
+
Vault: {vault_path}/{prefix}/
|
|
380
|
+
|
|
381
|
+
Discovered:
|
|
382
|
+
Issues: {count} ({open} open, {in_progress} in progress, {closed} closed)
|
|
383
|
+
Epics: {count}
|
|
384
|
+
PRDs: {count}
|
|
385
|
+
Agents: {count} across {categories} categories
|
|
386
|
+
Rules: {count}
|
|
387
|
+
Commands: {count}
|
|
388
|
+
|
|
389
|
+
Next: Open the vault in Obsidian. Install Dataview plugin for live queries.
|
|
390
|
+
Resync: autopm obsidian sync
|
|
391
|
+
Regenerate: /obsidian:init --force
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Important Notes
|
|
395
|
+
|
|
396
|
+
- All Dataview queries use the vault prefix from `.claude/config.json`
|
|
397
|
+
- Generated files go directly to the vault path, not the project
|
|
398
|
+
- Sync runs after generation to ensure vault has latest project content
|
|
399
|
+
- Use `--force` to regenerate if project structure changes significantly
|
|
400
|
+
- The command is idempotent — safe to run multiple times
|