@jayjiang/byoao 0.2.0

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 (76) hide show
  1. package/dist/cli/cli-program.js +241 -0
  2. package/dist/cli/cli-program.js.map +1 -0
  3. package/dist/cli/installer.js +226 -0
  4. package/dist/cli/installer.js.map +1 -0
  5. package/dist/cli/ui.js +157 -0
  6. package/dist/cli/ui.js.map +1 -0
  7. package/dist/hooks/idle-suggestions.js +14 -0
  8. package/dist/hooks/idle-suggestions.js.map +1 -0
  9. package/dist/hooks/system-transform.js +34 -0
  10. package/dist/hooks/system-transform.js.map +1 -0
  11. package/dist/index.js +43 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/plugin-config.js +57 -0
  14. package/dist/plugin-config.js.map +1 -0
  15. package/dist/tools/add-glossary-term.js +19 -0
  16. package/dist/tools/add-glossary-term.js.map +1 -0
  17. package/dist/tools/add-member.js +21 -0
  18. package/dist/tools/add-member.js.map +1 -0
  19. package/dist/tools/add-project.js +24 -0
  20. package/dist/tools/add-project.js.map +1 -0
  21. package/dist/tools/init-vault.js +82 -0
  22. package/dist/tools/init-vault.js.map +1 -0
  23. package/dist/tools/vault-doctor.js +38 -0
  24. package/dist/tools/vault-doctor.js.map +1 -0
  25. package/dist/tools/vault-status.js +18 -0
  26. package/dist/tools/vault-status.js.map +1 -0
  27. package/dist/vault/create.js +230 -0
  28. package/dist/vault/create.js.map +1 -0
  29. package/dist/vault/doctor.js +148 -0
  30. package/dist/vault/doctor.js.map +1 -0
  31. package/dist/vault/glossary.js +33 -0
  32. package/dist/vault/glossary.js.map +1 -0
  33. package/dist/vault/member.js +67 -0
  34. package/dist/vault/member.js.map +1 -0
  35. package/dist/vault/obsidian-check.js +125 -0
  36. package/dist/vault/obsidian-check.js.map +1 -0
  37. package/dist/vault/obsidian-cli.js +47 -0
  38. package/dist/vault/obsidian-cli.js.map +1 -0
  39. package/dist/vault/opencode-check.js +65 -0
  40. package/dist/vault/opencode-check.js.map +1 -0
  41. package/dist/vault/preset.js +48 -0
  42. package/dist/vault/preset.js.map +1 -0
  43. package/dist/vault/project.js +68 -0
  44. package/dist/vault/project.js.map +1 -0
  45. package/dist/vault/status.js +135 -0
  46. package/dist/vault/status.js.map +1 -0
  47. package/dist/vault/template.js +14 -0
  48. package/dist/vault/template.js.map +1 -0
  49. package/package.json +58 -0
  50. package/src/assets/obsidian-skills/defuddle.md +41 -0
  51. package/src/assets/obsidian-skills/json-canvas.md +244 -0
  52. package/src/assets/obsidian-skills/obsidian-bases.md +497 -0
  53. package/src/assets/obsidian-skills/obsidian-cli.md +106 -0
  54. package/src/assets/obsidian-skills/obsidian-markdown.md +196 -0
  55. package/src/assets/presets/common/AGENT.md.hbs +29 -0
  56. package/src/assets/presets/common/Glossary.md.hbs +44 -0
  57. package/src/assets/presets/common/Start Here.md.hbs +95 -0
  58. package/src/assets/presets/common/obsidian/core-plugins.json +33 -0
  59. package/src/assets/presets/common/obsidian/daily-notes.json +5 -0
  60. package/src/assets/presets/common/obsidian/templates.json +3 -0
  61. package/src/assets/presets/common/templates/Daily Note.md +19 -0
  62. package/src/assets/presets/common/templates/Decision Record.md +32 -0
  63. package/src/assets/presets/common/templates/Investigation.md +34 -0
  64. package/src/assets/presets/common/templates/Meeting Notes.md +25 -0
  65. package/src/assets/presets/pm-tpm/agent-section.hbs +15 -0
  66. package/src/assets/presets/pm-tpm/preset.json +12 -0
  67. package/src/assets/presets/pm-tpm/templates/Feature Doc.md +45 -0
  68. package/src/assets/presets/pm-tpm/templates/Sprint Handoff.md +38 -0
  69. package/src/assets/web-clipper/confluence-page.json +63 -0
  70. package/src/assets/web-clipper/general-article.json +53 -0
  71. package/src/assets/web-clipper/jira-issue.json +68 -0
  72. package/src/assets/web-clipper/meeting-notes.json +53 -0
  73. package/src/skills/enrich-document.md +52 -0
  74. package/src/skills/init-knowledge-base.md +85 -0
  75. package/src/skills/system-explainer.md +58 -0
  76. package/src/skills/vault-doctor.md +73 -0
@@ -0,0 +1,230 @@
1
+ import fs from "fs-extra";
2
+ import path from "node:path";
3
+ import { renderTemplate, today } from "./template.js";
4
+ import { loadPreset, getCommonDir } from "./preset.js";
5
+ // Common directories shared by all presets
6
+ const COMMON_DIRECTORIES = [
7
+ "Inbox",
8
+ "Knowledge",
9
+ "Knowledge/concepts",
10
+ "Knowledge/templates",
11
+ "People",
12
+ "Systems",
13
+ "Archive",
14
+ "Daily",
15
+ ];
16
+ export async function createVault(config) {
17
+ const { teamName, vaultPath, members, projects, glossaryEntries, jiraHost, jiraProject, preset } = config;
18
+ const presetName = preset ?? "pm-tpm"; // Fallback if config wasn't parsed through Zod
19
+ const { config: presetConfig, presetsDir } = loadPreset(presetName);
20
+ const commonDir = getCommonDir();
21
+ const presetDir = path.join(presetsDir, presetName);
22
+ let filesCreated = 0;
23
+ let wikilinksCreated = 0;
24
+ // Merge directories: common + preset-specific
25
+ const allDirectories = [...COMMON_DIRECTORIES, ...presetConfig.directories];
26
+ // 1. Create directories
27
+ for (const dir of allDirectories) {
28
+ await fs.ensureDir(path.join(vaultPath, dir));
29
+ }
30
+ // 2. Copy .obsidian config from common
31
+ await fs.ensureDir(path.join(vaultPath, ".obsidian"));
32
+ const obsidianSrc = path.join(commonDir, "obsidian");
33
+ if (await fs.pathExists(obsidianSrc)) {
34
+ await fs.copy(obsidianSrc, path.join(vaultPath, ".obsidian"), { overwrite: false });
35
+ filesCreated += (await fs.readdir(obsidianSrc)).length;
36
+ }
37
+ // 3. Copy note templates: common first, then preset overlay
38
+ const templateDest = path.join(vaultPath, "Knowledge/templates");
39
+ const allTemplateNames = [];
40
+ // Common templates
41
+ const commonTemplatesDir = path.join(commonDir, "templates");
42
+ if (await fs.pathExists(commonTemplatesDir)) {
43
+ const files = await fs.readdir(commonTemplatesDir);
44
+ for (const file of files) {
45
+ await fs.copy(path.join(commonTemplatesDir, file), path.join(templateDest, file), { overwrite: false });
46
+ allTemplateNames.push(file.replace(/\.md$/, ""));
47
+ filesCreated++;
48
+ }
49
+ }
50
+ // Preset templates
51
+ const presetTemplatesDir = path.join(presetDir, "templates");
52
+ if (await fs.pathExists(presetTemplatesDir)) {
53
+ const files = await fs.readdir(presetTemplatesDir);
54
+ for (const file of files) {
55
+ await fs.copy(path.join(presetTemplatesDir, file), path.join(templateDest, file), { overwrite: false });
56
+ allTemplateNames.push(file.replace(/\.md$/, ""));
57
+ filesCreated++;
58
+ }
59
+ }
60
+ // 4. Generate Glossary.md
61
+ const glossaryTemplate = await fs.readFile(path.join(commonDir, "Glossary.md.hbs"), "utf-8");
62
+ let glossaryRows = "";
63
+ if (glossaryEntries.length > 0) {
64
+ glossaryRows = glossaryEntries
65
+ .map((e) => `| **${e.term}** | ${e.definition} |`)
66
+ .join("\n");
67
+ }
68
+ const glossaryContent = renderTemplate(glossaryTemplate, {
69
+ TEAM_NAME: teamName,
70
+ date: today(),
71
+ GLOSSARY_ENTRIES: glossaryRows,
72
+ });
73
+ await fs.writeFile(path.join(vaultPath, "Knowledge/Glossary.md"), glossaryContent);
74
+ filesCreated++;
75
+ // 5. Generate Start Here.md
76
+ const startHereTemplate = await fs.readFile(path.join(commonDir, "Start Here.md.hbs"), "utf-8");
77
+ const startHereContent = renderTemplate(startHereTemplate, { TEAM_NAME: teamName });
78
+ await fs.writeFile(path.join(vaultPath, "Start Here.md"), startHereContent);
79
+ filesCreated++;
80
+ // 6. Generate AGENT.md (two-layer: common skeleton + preset section)
81
+ const agentSkeletonTemplate = await fs.readFile(path.join(commonDir, "AGENT.md.hbs"), "utf-8");
82
+ // Render preset agent-section
83
+ let roleSection = "";
84
+ const agentSectionPath = path.join(presetDir, "agent-section.hbs");
85
+ if (await fs.pathExists(agentSectionPath)) {
86
+ const agentSectionTemplate = await fs.readFile(agentSectionPath, "utf-8");
87
+ let projectsList;
88
+ if (projects.length > 0) {
89
+ projectsList = projects
90
+ .map((p) => `- [[${p.name}]] — ${p.description}`)
91
+ .join("\n");
92
+ wikilinksCreated += projects.length;
93
+ }
94
+ else {
95
+ projectsList = "(No projects added yet — create notes in Projects/)";
96
+ }
97
+ roleSection = renderTemplate(agentSectionTemplate, {
98
+ PROJECTS: projectsList,
99
+ JIRA_HOST: jiraHost,
100
+ JIRA_PROJECT: jiraProject,
101
+ HAS_JIRA: jiraHost && jiraProject,
102
+ });
103
+ }
104
+ // Build team table
105
+ let teamTable;
106
+ if (members.length > 0) {
107
+ const rows = members.map((m) => `| [[${m.name}]] | ${m.role} |`).join("\n");
108
+ teamTable = `| Name | Role |\n|------|------|\n${rows}`;
109
+ wikilinksCreated += members.length;
110
+ }
111
+ else {
112
+ teamTable = "(No members added yet — create notes in People/)";
113
+ }
114
+ // Build template list string
115
+ const templateList = allTemplateNames.map((t) => `- ${t}`).join("\n");
116
+ const agentContent = renderTemplate(agentSkeletonTemplate, {
117
+ TEAM_NAME: teamName,
118
+ AGENT_DESCRIPTION: presetConfig.agentDescription,
119
+ ROLE_SECTION: roleSection,
120
+ TEAM_TABLE: teamTable,
121
+ TEMPLATE_LIST: templateList,
122
+ });
123
+ await fs.writeFile(path.join(vaultPath, "AGENT.md"), agentContent);
124
+ await fs.writeFile(path.join(vaultPath, "CLAUDE.md"), agentContent);
125
+ filesCreated += 2;
126
+ // 7. Create people notes
127
+ for (const member of members) {
128
+ const content = `---
129
+ title: "${member.name}"
130
+ type: person
131
+ team: "${teamName}"
132
+ role: "${member.role}"
133
+ status: active
134
+ tags: [person]
135
+ ---
136
+
137
+ # ${member.name}
138
+
139
+ **Role**: ${member.role}
140
+ **Team**: ${teamName}
141
+ `;
142
+ await fs.writeFile(path.join(vaultPath, `People/${member.name}.md`), content);
143
+ filesCreated++;
144
+ }
145
+ // 8. Create project notes
146
+ for (const project of projects) {
147
+ const content = `---
148
+ title: "${project.name}"
149
+ type: feature
150
+ status: active
151
+ date: ${today()}
152
+ team: "${teamName}"
153
+ jira: ""
154
+ stakeholders: []
155
+ priority: ""
156
+ tags: [project]
157
+ ---
158
+
159
+ # ${project.name}
160
+
161
+ ${project.description}
162
+ `;
163
+ await fs.writeFile(path.join(vaultPath, `Projects/${project.name}.md`), content);
164
+ filesCreated++;
165
+ }
166
+ // 9. Create team index (always, since Start Here.md links to it)
167
+ {
168
+ let teamIndexContent = `---
169
+ title: "${teamName} Team"
170
+ type: reference
171
+ team: "${teamName}"
172
+ tags: [team]
173
+ ---
174
+
175
+ # ${teamName} Team
176
+
177
+ ## Members
178
+
179
+ `;
180
+ if (members.length > 0) {
181
+ teamIndexContent += "| Name | Role |\n|------|------|\n";
182
+ teamIndexContent += members.map((m) => `| [[${m.name}]] | ${m.role} |`).join("\n");
183
+ wikilinksCreated += members.length;
184
+ }
185
+ else {
186
+ teamIndexContent += "(No members added yet)";
187
+ }
188
+ teamIndexContent += "\n\n## Active Projects\n\n";
189
+ if (projects.length > 0) {
190
+ teamIndexContent += projects
191
+ .map((p) => `- [[${p.name}]] — ${p.description}`)
192
+ .join("\n");
193
+ wikilinksCreated += projects.length;
194
+ }
195
+ else {
196
+ teamIndexContent += "(No projects added yet)";
197
+ }
198
+ teamIndexContent += "\n";
199
+ await fs.writeFile(path.join(vaultPath, `People/${teamName} Team.md`), teamIndexContent);
200
+ filesCreated++;
201
+ }
202
+ // 10. Create .gitkeep in empty directories
203
+ const dirsNeedingGitkeep = [
204
+ "Inbox",
205
+ "Knowledge/concepts",
206
+ "Systems",
207
+ "Archive",
208
+ "Daily",
209
+ ];
210
+ // Add preset-specific dirs if they're empty
211
+ for (const dir of presetConfig.directories) {
212
+ dirsNeedingGitkeep.push(dir);
213
+ }
214
+ for (const dir of dirsNeedingGitkeep) {
215
+ const dirPath = path.join(vaultPath, dir);
216
+ if (await fs.pathExists(dirPath)) {
217
+ const files = await fs.readdir(dirPath);
218
+ if (files.length === 0) {
219
+ await fs.writeFile(path.join(dirPath, ".gitkeep"), "");
220
+ }
221
+ }
222
+ }
223
+ return {
224
+ vaultPath,
225
+ filesCreated,
226
+ wikilinksCreated,
227
+ directories: allDirectories,
228
+ };
229
+ }
230
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/vault/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,148 @@
1
+ import fs from "fs-extra";
2
+ import path from "node:path";
3
+ import matter from "gray-matter";
4
+ /** Recursively collect all .md files (excluding .obsidian) */
5
+ async function collectMarkdownFiles(dir) {
6
+ const results = [];
7
+ const entries = await fs.readdir(dir, { withFileTypes: true });
8
+ for (const entry of entries) {
9
+ const fullPath = path.join(dir, entry.name);
10
+ if (entry.isDirectory()) {
11
+ if (entry.name === ".obsidian" || entry.name === ".git")
12
+ continue;
13
+ results.push(...(await collectMarkdownFiles(fullPath)));
14
+ }
15
+ else if (entry.name.endsWith(".md")) {
16
+ results.push(fullPath);
17
+ }
18
+ }
19
+ return results;
20
+ }
21
+ /** Extract wikilink targets from content (skip code blocks) */
22
+ function extractWikilinks(content) {
23
+ const stripped = content
24
+ .replace(/```[\s\S]*?```/g, "")
25
+ .replace(/`[^`]+`/g, "");
26
+ const matches = stripped.match(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g);
27
+ if (!matches)
28
+ return [];
29
+ return matches.map((m) => m.replace(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/, "$1"));
30
+ }
31
+ export async function getVaultDiagnosis(vaultPath) {
32
+ const issues = [];
33
+ const allFiles = await collectMarkdownFiles(vaultPath);
34
+ const noteNames = new Set(allFiles.map((f) => path.basename(f, ".md")));
35
+ let healthyNotes = 0;
36
+ // Check 1 & 2: Frontmatter — missing type or tags
37
+ for (const filePath of allFiles) {
38
+ const relativePath = path.relative(vaultPath, filePath);
39
+ // Skip template files
40
+ if (relativePath.startsWith("Knowledge/templates/"))
41
+ continue;
42
+ const content = await fs.readFile(filePath, "utf-8");
43
+ const { data } = matter(content);
44
+ let hasIssue = false;
45
+ if (!data || Object.keys(data).length === 0) {
46
+ issues.push({
47
+ severity: "warning",
48
+ category: "frontmatter",
49
+ file: relativePath,
50
+ message: "Missing frontmatter — no metadata defined",
51
+ });
52
+ hasIssue = true;
53
+ }
54
+ else {
55
+ if (!data.type) {
56
+ issues.push({
57
+ severity: "warning",
58
+ category: "frontmatter",
59
+ file: relativePath,
60
+ message: "Missing `type` in frontmatter",
61
+ });
62
+ hasIssue = true;
63
+ }
64
+ if (!data.tags || (Array.isArray(data.tags) && data.tags.length === 0)) {
65
+ issues.push({
66
+ severity: "info",
67
+ category: "frontmatter",
68
+ file: relativePath,
69
+ message: "Missing `tags` in frontmatter",
70
+ });
71
+ hasIssue = true;
72
+ }
73
+ }
74
+ if (!hasIssue)
75
+ healthyNotes++;
76
+ }
77
+ // Check 3: AGENT.md drift
78
+ const agentPath = path.join(vaultPath, "AGENT.md");
79
+ if (await fs.pathExists(agentPath)) {
80
+ const agentContent = await fs.readFile(agentPath, "utf-8");
81
+ const agentLinks = extractWikilinks(agentContent);
82
+ for (const linkTarget of agentLinks) {
83
+ if (!noteNames.has(linkTarget)) {
84
+ issues.push({
85
+ severity: "warning",
86
+ category: "agent-drift",
87
+ message: `AGENT.md links to [[${linkTarget}]] but no matching note found`,
88
+ });
89
+ }
90
+ }
91
+ }
92
+ // Check 4: Orphan notes (no incoming or outgoing wikilinks)
93
+ const allLinksMap = new Map();
94
+ const incomingLinks = new Set();
95
+ for (const filePath of allFiles) {
96
+ const content = await fs.readFile(filePath, "utf-8");
97
+ const links = extractWikilinks(content);
98
+ const name = path.basename(filePath, ".md");
99
+ allLinksMap.set(name, new Set(links));
100
+ for (const link of links) {
101
+ incomingLinks.add(link);
102
+ }
103
+ }
104
+ for (const filePath of allFiles) {
105
+ const relativePath = path.relative(vaultPath, filePath);
106
+ if (relativePath.startsWith("Knowledge/templates/"))
107
+ continue;
108
+ if (relativePath === "AGENT.md" || relativePath === "CLAUDE.md")
109
+ continue;
110
+ const name = path.basename(filePath, ".md");
111
+ const outgoing = allLinksMap.get(name) || new Set();
112
+ const hasIncoming = incomingLinks.has(name);
113
+ const hasOutgoing = outgoing.size > 0;
114
+ if (!hasIncoming && !hasOutgoing) {
115
+ issues.push({
116
+ severity: "info",
117
+ category: "orphan",
118
+ file: relativePath,
119
+ message: "Orphan note — no incoming or outgoing wikilinks",
120
+ });
121
+ }
122
+ }
123
+ // Check 5: Broken wikilinks
124
+ for (const filePath of allFiles) {
125
+ const relativePath = path.relative(vaultPath, filePath);
126
+ const content = await fs.readFile(filePath, "utf-8");
127
+ const links = extractWikilinks(content);
128
+ for (const link of links) {
129
+ if (!noteNames.has(link)) {
130
+ issues.push({
131
+ severity: "warning",
132
+ category: "broken-link",
133
+ file: relativePath,
134
+ message: `Broken wikilink: [[${link}]]`,
135
+ });
136
+ }
137
+ }
138
+ }
139
+ return {
140
+ summary: {
141
+ totalNotes: allFiles.length,
142
+ healthyNotes,
143
+ issueCount: issues.length,
144
+ },
145
+ issues,
146
+ };
147
+ }
148
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/vault/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import fs from "fs-extra";
2
+ import path from "node:path";
3
+ export async function addGlossaryTerm(input) {
4
+ const { vaultPath, term, definition } = input;
5
+ const glossaryPath = path.join(vaultPath, "30-Knowledge/Glossary.md");
6
+ if (!(await fs.pathExists(glossaryPath))) {
7
+ throw new Error(`Glossary not found at: ${glossaryPath}`);
8
+ }
9
+ let content = await fs.readFile(glossaryPath, "utf-8");
10
+ // Find the Core Terms table and append a new row
11
+ const newRow = `| **${term}** | ${definition} |`;
12
+ // Find the last row in the Core Terms table (after |------|-----------|)
13
+ const tableMatch = content.match(/(## Core Terms\n\n\| Term \| Definition \|\n\|------|-----------\|\n)([\s\S]*?)(\n\n---|\n\n##|\n*$)/);
14
+ if (tableMatch) {
15
+ const [fullMatch, tableHeader, existingRows, tableEnd] = tableMatch;
16
+ const updatedRows = existingRows.trim()
17
+ ? `${existingRows.trim()}\n${newRow}`
18
+ : newRow;
19
+ content = content.replace(fullMatch, `${tableHeader}${updatedRows}\n${tableEnd}`);
20
+ }
21
+ else {
22
+ // Fallback: just append before the "How to Add" section
23
+ if (content.includes("## How to Add a New Term")) {
24
+ content = content.replace("## How to Add a New Term", `${newRow}\n\n## How to Add a New Term`);
25
+ }
26
+ else {
27
+ content += `\n${newRow}\n`;
28
+ }
29
+ }
30
+ await fs.writeFile(glossaryPath, content);
31
+ return { glossaryPath, termAdded: term };
32
+ }
33
+ //# sourceMappingURL=glossary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary.js","sourceRoot":"","sources":["../../src/vault/glossary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAQ7B,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAA2B;IAE3B,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAE9C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC;IACtE,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,0BAA0B,YAAY,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAEvD,iDAAiD;IACjD,MAAM,MAAM,GAAG,OAAO,IAAI,QAAQ,UAAU,IAAI,CAAC;IAEjD,yEAAyE;IACzE,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAC9B,sGAAsG,CACvG,CAAC;IAEF,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC;QACpE,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,EAAE;YACrC,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,KAAK,MAAM,EAAE;YACrC,CAAC,CAAC,MAAM,CAAC;QACX,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,SAAS,EACT,GAAG,WAAW,GAAG,WAAW,KAAK,QAAQ,EAAE,CAC5C,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,wDAAwD;QACxD,IAAI,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAC,EAAE,CAAC;YACjD,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,0BAA0B,EAC1B,GAAG,MAAM,8BAA8B,CACxC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,KAAK,MAAM,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAE1C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,67 @@
1
+ import fs from "fs-extra";
2
+ import path from "node:path";
3
+ export async function addMember(input) {
4
+ const { vaultPath, name, role, team } = input;
5
+ let wikilinksAdded = 0;
6
+ // 1. Create person note
7
+ const personPath = path.join(vaultPath, `40-People/${name}.md`);
8
+ if (await fs.pathExists(personPath)) {
9
+ throw new Error(`Person note already exists: ${personPath}`);
10
+ }
11
+ const content = `---
12
+ title: "${name}"
13
+ type: person
14
+ team: "${team}"
15
+ role: "${role}"
16
+ status: active
17
+ tags: [person]
18
+ ---
19
+
20
+ # ${name}
21
+
22
+ **Role**: ${role}
23
+ **Team**: ${team}
24
+ `;
25
+ await fs.writeFile(personPath, content);
26
+ // 2. Update team index if it exists
27
+ const teamFiles = await fs.readdir(path.join(vaultPath, "40-People"));
28
+ const teamIndexFile = teamFiles.find((f) => f.endsWith("Team.md"));
29
+ if (teamIndexFile) {
30
+ const teamIndexPath = path.join(vaultPath, "40-People", teamIndexFile);
31
+ let teamContent = await fs.readFile(teamIndexPath, "utf-8");
32
+ // Add to members table
33
+ const tableRow = `| [[${name}]] | ${role} |`;
34
+ if (teamContent.includes("|------|------|")) {
35
+ // Insert after the table header separator
36
+ teamContent = teamContent.replace(/(^\|------|------\|$)/m, `$1\n${tableRow}`);
37
+ wikilinksAdded++;
38
+ }
39
+ await fs.writeFile(teamIndexPath, teamContent);
40
+ }
41
+ // 3. Update AGENT.md wikilinks
42
+ for (const agentFile of ["AGENT.md", "CLAUDE.md"]) {
43
+ const agentPath = path.join(vaultPath, agentFile);
44
+ if (await fs.pathExists(agentPath)) {
45
+ let agentContent = await fs.readFile(agentPath, "utf-8");
46
+ // Replace placeholder if present
47
+ if (agentContent.includes("(No members added yet")) {
48
+ const table = `| Name | Role |\n|------|------|\n| [[${name}]] | ${role} |`;
49
+ agentContent = agentContent.replace(/\(No members added yet[^)]*\)/, table);
50
+ wikilinksAdded++;
51
+ }
52
+ else if (agentContent.includes("|------|------|")) {
53
+ // Find the team table section and add a row
54
+ const teamSectionMatch = agentContent.match(/## Team[^\n]*\n[\s\S]*?(\|------|------\|[^\n]*(?:\n\|[^\n]*)*)/);
55
+ if (teamSectionMatch) {
56
+ const existingTable = teamSectionMatch[1];
57
+ const newTable = existingTable + `\n| [[${name}]] | ${role} |`;
58
+ agentContent = agentContent.replace(existingTable, newTable);
59
+ wikilinksAdded++;
60
+ }
61
+ }
62
+ await fs.writeFile(agentPath, agentContent);
63
+ }
64
+ }
65
+ return { filePath: personPath, wikilinksAdded };
66
+ }
67
+ //# sourceMappingURL=member.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"member.js","sourceRoot":"","sources":["../../src/vault/member.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ import { execSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { platform } from "node:os";
4
+ const OBSIDIAN_DOWNLOAD_URL = "https://obsidian.md/download";
5
+ function getMacAppPaths() {
6
+ return [
7
+ "/Applications/Obsidian.app",
8
+ `${process.env.HOME}/Applications/Obsidian.app`,
9
+ ];
10
+ }
11
+ function getLinuxPaths() {
12
+ return [
13
+ "/usr/bin/obsidian",
14
+ "/usr/local/bin/obsidian",
15
+ "/snap/bin/obsidian",
16
+ `${process.env.HOME}/.local/bin/obsidian`,
17
+ ];
18
+ }
19
+ function findInstallPath() {
20
+ const os = platform();
21
+ if (os === "darwin") {
22
+ for (const p of getMacAppPaths()) {
23
+ if (existsSync(p))
24
+ return p;
25
+ }
26
+ // Also check via mdfind (Spotlight)
27
+ try {
28
+ const result = execSync('mdfind "kMDItemCFBundleIdentifier == md.obsidian"', {
29
+ timeout: 5000,
30
+ encoding: "utf-8",
31
+ }).trim();
32
+ if (result)
33
+ return result.split("\n")[0];
34
+ }
35
+ catch {
36
+ // Spotlight may not be available
37
+ }
38
+ }
39
+ else if (os === "linux") {
40
+ for (const p of getLinuxPaths()) {
41
+ if (existsSync(p))
42
+ return p;
43
+ }
44
+ // Check via which
45
+ try {
46
+ const result = execSync("which obsidian", {
47
+ timeout: 3000,
48
+ encoding: "utf-8",
49
+ }).trim();
50
+ if (result)
51
+ return result;
52
+ }
53
+ catch {
54
+ // Not found
55
+ }
56
+ }
57
+ else if (os === "win32") {
58
+ const localAppData = process.env.LOCALAPPDATA || "";
59
+ const winPath = `${localAppData}\\Obsidian\\Obsidian.exe`;
60
+ if (existsSync(winPath))
61
+ return winPath;
62
+ }
63
+ return null;
64
+ }
65
+ function isRunning() {
66
+ const os = platform();
67
+ try {
68
+ if (os === "darwin") {
69
+ const result = execSync("pgrep -x Obsidian", {
70
+ timeout: 3000,
71
+ encoding: "utf-8",
72
+ }).trim();
73
+ return result.length > 0;
74
+ }
75
+ else if (os === "linux") {
76
+ const result = execSync("pgrep -x obsidian", {
77
+ timeout: 3000,
78
+ encoding: "utf-8",
79
+ }).trim();
80
+ return result.length > 0;
81
+ }
82
+ else if (os === "win32") {
83
+ const result = execSync("tasklist /FI \"IMAGENAME eq Obsidian.exe\" /NH", {
84
+ timeout: 3000,
85
+ encoding: "utf-8",
86
+ });
87
+ return result.includes("Obsidian.exe");
88
+ }
89
+ }
90
+ catch {
91
+ // pgrep returns exit code 1 if no match — that's expected
92
+ }
93
+ return false;
94
+ }
95
+ export function checkObsidian() {
96
+ const appPath = findInstallPath();
97
+ return {
98
+ installed: appPath !== null,
99
+ running: isRunning(),
100
+ appPath,
101
+ downloadUrl: OBSIDIAN_DOWNLOAD_URL,
102
+ };
103
+ }
104
+ export function formatObsidianStatus(status) {
105
+ const lines = [];
106
+ if (!status.installed) {
107
+ lines.push("⚠️ Obsidian is NOT installed.");
108
+ lines.push(` Download it from: ${status.downloadUrl}`);
109
+ lines.push(" After installing, open Obsidian and create or open a vault.");
110
+ return lines.join("\n");
111
+ }
112
+ lines.push(`✓ Obsidian is installed at: ${status.appPath}`);
113
+ if (!status.running) {
114
+ lines.push("⚠️ Obsidian is NOT currently running.");
115
+ lines.push(" Please open Obsidian before working with vaults.");
116
+ if (status.appPath && platform() === "darwin") {
117
+ lines.push(` Run: open "${status.appPath}"`);
118
+ }
119
+ }
120
+ else {
121
+ lines.push("✓ Obsidian is running.");
122
+ }
123
+ return lines.join("\n");
124
+ }
125
+ //# sourceMappingURL=obsidian-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"obsidian-check.js","sourceRoot":"","sources":["../../src/vault/obsidian-check.ts"],"names":[],"mappings":"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"}