@jayjiang/byoao 0.5.0 → 0.6.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 (50) hide show
  1. package/dist/__tests__/plugin-config.test.js +11 -8
  2. package/dist/__tests__/plugin-config.test.js.map +1 -1
  3. package/dist/cli/cli-program.js +136 -158
  4. package/dist/cli/cli-program.js.map +1 -1
  5. package/dist/cli/installer.js +4 -11
  6. package/dist/cli/installer.js.map +1 -1
  7. package/dist/hooks/idle-suggestions.js +3 -3
  8. package/dist/hooks/idle-suggestions.js.map +1 -1
  9. package/dist/hooks/system-transform.js +37 -14
  10. package/dist/hooks/system-transform.js.map +1 -1
  11. package/dist/index.js +2 -2
  12. package/dist/plugin-config.js +5 -2
  13. package/dist/plugin-config.js.map +1 -1
  14. package/dist/tools/add-glossary-term.js +2 -0
  15. package/dist/tools/add-glossary-term.js.map +1 -1
  16. package/dist/tools/add-person.js +21 -0
  17. package/dist/tools/add-person.js.map +1 -0
  18. package/dist/tools/init-vault.js +11 -9
  19. package/dist/tools/init-vault.js.map +1 -1
  20. package/dist/vault/__tests__/create.test.js +96 -42
  21. package/dist/vault/__tests__/create.test.js.map +1 -1
  22. package/dist/vault/__tests__/glossary.test.js +25 -14
  23. package/dist/vault/__tests__/glossary.test.js.map +1 -1
  24. package/dist/vault/__tests__/member.test.js +2 -4
  25. package/dist/vault/__tests__/member.test.js.map +1 -1
  26. package/dist/vault/create.js +205 -157
  27. package/dist/vault/create.js.map +1 -1
  28. package/dist/vault/doctor.js +1 -1
  29. package/dist/vault/doctor.js.map +1 -1
  30. package/dist/vault/glossary.js +8 -14
  31. package/dist/vault/glossary.js.map +1 -1
  32. package/dist/vault/member.js +1 -1
  33. package/dist/vault/member.js.map +1 -1
  34. package/dist/vault/project.js +1 -1
  35. package/dist/vault/project.js.map +1 -1
  36. package/dist/vault/vault-detect.js +30 -0
  37. package/dist/vault/vault-detect.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/assets/presets/common/AGENT.md.hbs +34 -67
  40. package/src/assets/presets/common/Glossary.md.hbs +7 -35
  41. package/src/assets/presets/common/Start Here.md.hbs +32 -64
  42. package/src/assets/presets/minimal/preset.json +28 -0
  43. package/src/skills/{vault-doctor.md → diagnose.md} +12 -12
  44. package/src/skills/{system-explainer.md → explain.md} +8 -8
  45. package/src/skills/weave.md +240 -0
  46. package/src/assets/web-clipper/confluence-page.json +0 -63
  47. package/src/assets/web-clipper/general-article.json +0 -53
  48. package/src/assets/web-clipper/jira-issue.json +0 -68
  49. package/src/assets/web-clipper/meeting-notes.json +0 -53
  50. package/src/skills/enrich-document.md +0 -52
@@ -12,45 +12,56 @@ afterEach(async () => {
12
12
  await fs.remove(tmpDir);
13
13
  });
14
14
  const GLOSSARY_TEMPLATE = `---
15
- title: "Test Glossary"
15
+ title: Glossary
16
16
  type: reference
17
- tags: [glossary]
17
+ status: active
18
+ tags: [glossary, reference]
18
19
  ---
19
20
 
20
- # Test Glossary
21
+ # Glossary
21
22
 
22
- ## Core Terms
23
+ Domain terms and key concepts in this knowledge base.
24
+ Maintained by /weave — run it to discover and add new terms.
23
25
 
24
- | Term | Definition |
25
- |------|-----------|
26
- | **API** | Application programming interface |
27
-
28
- ---
29
-
30
- ## How to Add a New Term
31
-
32
- Just add it.
26
+ | Term | Definition | Domain |
27
+ |------|-----------|--------|
28
+ | **API** | Application programming interface | engineering |
33
29
  `;
34
30
  describe("addGlossaryTerm", () => {
35
- it("appends term to existing Core Terms table", async () => {
31
+ it("appends term to existing glossary table", async () => {
36
32
  await fs.writeFile(path.join(tmpDir, "Knowledge/Glossary.md"), GLOSSARY_TEMPLATE);
37
33
  const result = await addGlossaryTerm({
38
34
  vaultPath: tmpDir,
39
35
  term: "SDK",
40
36
  definition: "Software development kit",
37
+ domain: "engineering",
41
38
  });
42
39
  expect(result.termAdded).toBe("SDK");
43
40
  const content = await fs.readFile(path.join(tmpDir, "Knowledge/Glossary.md"), "utf-8");
44
41
  expect(content).toContain("**SDK**");
45
42
  expect(content).toContain("Software development kit");
43
+ expect(content).toContain("| engineering |");
46
44
  // Original term still present
47
45
  expect(content).toContain("**API**");
48
46
  });
47
+ it("appends term with empty domain when domain not provided", async () => {
48
+ await fs.writeFile(path.join(tmpDir, "Knowledge/Glossary.md"), GLOSSARY_TEMPLATE);
49
+ const result = await addGlossaryTerm({
50
+ vaultPath: tmpDir,
51
+ term: "TDD",
52
+ definition: "Test-driven development",
53
+ domain: "",
54
+ });
55
+ expect(result.termAdded).toBe("TDD");
56
+ const content = await fs.readFile(path.join(tmpDir, "Knowledge/Glossary.md"), "utf-8");
57
+ expect(content).toContain("**TDD**");
58
+ });
49
59
  it("throws when Glossary.md does not exist", async () => {
50
60
  await expect(addGlossaryTerm({
51
61
  vaultPath: tmpDir,
52
62
  term: "Test",
53
63
  definition: "A test",
64
+ domain: "",
54
65
  })).rejects.toThrow("Glossary not found");
55
66
  });
56
67
  });
@@ -1 +1 @@
1
- {"version":3,"file":"glossary.test.js","sourceRoot":"","sources":["../../../src/vault/__tests__/glossary.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,IAAI,MAAc,CAAC;AAEnB,UAAU,CAAC,KAAK,IAAI,EAAE;IACpB,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACrE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;CAmBzB,CAAC;AAEF,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAC1C,iBAAiB,CAClB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC;YACnC,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,KAAK;YACX,UAAU,EAAE,0BAA0B;SACvC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAErC,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAC1C,OAAO,CACR,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;QACtD,8BAA8B;QAC9B,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,MAAM,CACV,eAAe,CAAC;YACd,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,QAAQ;SACrB,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"glossary.test.js","sourceRoot":"","sources":["../../../src/vault/__tests__/glossary.test.ts"],"names":[],"mappings":"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"}
@@ -53,8 +53,7 @@ type: reference
53
53
  expect(updated).toContain("[[Bob]]");
54
54
  expect(updated).toContain("PM");
55
55
  });
56
- it("updates AGENT.md and CLAUDE.md wikilinks", async () => {
57
- // Create AGENT.md and CLAUDE.md with placeholder
56
+ it("updates AGENT.md wikilinks", async () => {
58
57
  const agentContent = `# Agent
59
58
 
60
59
  ## Team
@@ -62,14 +61,13 @@ type: reference
62
61
  (No members added yet — create notes in People/)
63
62
  `;
64
63
  await fs.writeFile(path.join(tmpDir, "AGENT.md"), agentContent);
65
- await fs.writeFile(path.join(tmpDir, "CLAUDE.md"), agentContent);
66
64
  const result = await addMember({
67
65
  vaultPath: tmpDir,
68
66
  name: "Carol",
69
67
  role: "Designer",
70
68
  team: "Platform",
71
69
  });
72
- expect(result.wikilinksAdded).toBeGreaterThanOrEqual(2);
70
+ expect(result.wikilinksAdded).toBeGreaterThanOrEqual(1);
73
71
  const agent = await fs.readFile(path.join(tmpDir, "AGENT.md"), "utf-8");
74
72
  expect(agent).toContain("[[Carol]]");
75
73
  expect(agent).not.toContain("No members added yet");
@@ -1 +1 @@
1
- {"version":3,"file":"member.test.js","sourceRoot":"","sources":["../../../src/vault/__tests__/member.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"member.test.js","sourceRoot":"","sources":["../../../src/vault/__tests__/member.test.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ import { configureMcp } from "./mcp.js";
6
6
  import { configureObsidianPlugins } from "./obsidian-plugins.js";
7
7
  import { configureProvider } from "./provider.js";
8
8
  import { writeManifest } from "./manifest.js";
9
+ import { detectInitMode } from "./vault-detect.js";
9
10
  function countWikilinks(content) {
10
11
  const stripped = content
11
12
  .replace(/```[\s\S]*?```/g, "")
@@ -13,100 +14,97 @@ function countWikilinks(content) {
13
14
  const matches = stripped.match(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g);
14
15
  return matches ? matches.length : 0;
15
16
  }
16
- // Common directories shared by all presets
17
- const COMMON_DIRECTORIES = [
18
- "Inbox",
17
+ // Minimal directories: the core of every knowledge base
18
+ const MINIMAL_DIRECTORIES = [
19
+ "Daily",
19
20
  "Knowledge",
20
- "Knowledge/concepts",
21
21
  "Knowledge/templates",
22
- "People",
23
- "Systems",
24
- "Archive",
25
- "Daily",
26
22
  ];
27
- export async function createVault(config) {
28
- const { teamName, vaultPath, members, projects, glossaryEntries, jiraHost, jiraProject, preset } = config;
29
- const presetName = preset ?? "pm-tpm"; // Fallback if config wasn't parsed through Zod
30
- const { config: presetConfig, presetsDir } = loadPreset(presetName);
31
- const commonDir = getCommonDir();
32
- const presetDir = path.join(presetsDir, presetName);
33
- let filesCreated = 0;
34
- const installedFiles = {
35
- skills: [],
36
- commands: [],
37
- obsidianConfig: [],
38
- templates: [],
23
+ function makeContext(vaultPath, kbName, preserveObsidian = false) {
24
+ return {
25
+ vaultPath,
26
+ kbName,
27
+ commonDir: getCommonDir(),
28
+ preserveObsidian,
29
+ filesCreated: 0,
30
+ directories: [],
31
+ installedFiles: {
32
+ skills: [],
33
+ commands: [],
34
+ obsidianConfig: [],
35
+ templates: [],
36
+ },
39
37
  };
40
- // Merge directories: common + preset-specific
41
- const allDirectories = [...COMMON_DIRECTORIES, ...presetConfig.directories];
42
- // 1. Create directories
43
- for (const dir of allDirectories) {
44
- await fs.ensureDir(path.join(vaultPath, dir));
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // Composable creation functions
41
+ // ---------------------------------------------------------------------------
42
+ /**
43
+ * Create the minimal core: directories, .obsidian/ config, common templates,
44
+ * Glossary.md, Start Here.md.
45
+ */
46
+ export async function createMinimalCore(ctx, glossaryEntries = []) {
47
+ // 1. Create minimal directories
48
+ for (const dir of MINIMAL_DIRECTORIES) {
49
+ await fs.ensureDir(path.join(ctx.vaultPath, dir));
45
50
  }
46
- // 2. Copy .obsidian config from common
47
- await fs.ensureDir(path.join(vaultPath, ".obsidian"));
48
- const obsidianSrc = path.join(commonDir, "obsidian");
49
- if (await fs.pathExists(obsidianSrc)) {
50
- await fs.copy(obsidianSrc, path.join(vaultPath, ".obsidian"), { overwrite: false });
51
- filesCreated += (await fs.readdir(obsidianSrc)).length;
52
- const obsidianFiles = await fs.readdir(obsidianSrc);
53
- for (const f of obsidianFiles) {
54
- installedFiles.obsidianConfig.push(`.obsidian/${f}`);
51
+ ctx.directories.push(...MINIMAL_DIRECTORIES);
52
+ // 2. Copy .obsidian config from common (skip if preserving existing vault config)
53
+ if (!ctx.preserveObsidian) {
54
+ await fs.ensureDir(path.join(ctx.vaultPath, ".obsidian"));
55
+ const obsidianSrc = path.join(ctx.commonDir, "obsidian");
56
+ if (await fs.pathExists(obsidianSrc)) {
57
+ await fs.copy(obsidianSrc, path.join(ctx.vaultPath, ".obsidian"), { overwrite: false });
58
+ const obsidianFiles = await fs.readdir(obsidianSrc);
59
+ ctx.filesCreated += obsidianFiles.length;
60
+ for (const f of obsidianFiles) {
61
+ ctx.installedFiles.obsidianConfig.push(`.obsidian/${f}`);
62
+ }
55
63
  }
56
64
  }
57
- // 3. Copy note templates: common first, then preset overlay
58
- const templateDest = path.join(vaultPath, "Knowledge/templates");
59
- const allTemplateNames = [];
60
- // Common templates
61
- const commonTemplatesDir = path.join(commonDir, "templates");
65
+ // 3. Copy common templates
66
+ const templateDest = path.join(ctx.vaultPath, "Knowledge/templates");
67
+ const commonTemplatesDir = path.join(ctx.commonDir, "templates");
62
68
  if (await fs.pathExists(commonTemplatesDir)) {
63
69
  const files = await fs.readdir(commonTemplatesDir);
64
70
  for (const file of files) {
65
71
  await fs.copy(path.join(commonTemplatesDir, file), path.join(templateDest, file), { overwrite: false });
66
- allTemplateNames.push(file.replace(/\.md$/, ""));
67
- filesCreated++;
68
- installedFiles.templates.push(`Knowledge/templates/${file}`);
69
- }
70
- }
71
- // Preset templates
72
- const presetTemplatesDir = path.join(presetDir, "templates");
73
- if (await fs.pathExists(presetTemplatesDir)) {
74
- const files = await fs.readdir(presetTemplatesDir);
75
- for (const file of files) {
76
- await fs.copy(path.join(presetTemplatesDir, file), path.join(templateDest, file), { overwrite: false });
77
- allTemplateNames.push(file.replace(/\.md$/, ""));
78
- filesCreated++;
79
- installedFiles.templates.push(`Knowledge/templates/${file}`);
72
+ ctx.filesCreated++;
73
+ ctx.installedFiles.templates.push(`Knowledge/templates/${file}`);
80
74
  }
81
75
  }
82
76
  // 4. Generate Glossary.md
83
- const glossaryTemplate = await fs.readFile(path.join(commonDir, "Glossary.md.hbs"), "utf-8");
77
+ const glossaryTemplate = await fs.readFile(path.join(ctx.commonDir, "Glossary.md.hbs"), "utf-8");
84
78
  let glossaryRows = "";
85
79
  if (glossaryEntries.length > 0) {
86
80
  glossaryRows = glossaryEntries
87
- .map((e) => `| **${e.term}** | ${e.definition} |`)
81
+ .map((e) => `| **${e.term}** | ${e.definition} | ${e.domain} |`)
88
82
  .join("\n");
89
83
  }
90
84
  const glossaryContent = renderTemplate(glossaryTemplate, {
91
- TEAM_NAME: teamName,
85
+ KB_NAME: ctx.kbName,
92
86
  date: today(),
93
87
  GLOSSARY_ENTRIES: glossaryRows,
94
88
  });
95
- const glossaryPath = path.join(vaultPath, "Knowledge/Glossary.md");
89
+ const glossaryPath = path.join(ctx.vaultPath, "Knowledge/Glossary.md");
96
90
  if (!(await fs.pathExists(glossaryPath))) {
97
91
  await fs.writeFile(glossaryPath, glossaryContent);
98
- filesCreated++;
92
+ ctx.filesCreated++;
99
93
  }
100
94
  // 5. Generate Start Here.md
101
- const startHereTemplate = await fs.readFile(path.join(commonDir, "Start Here.md.hbs"), "utf-8");
102
- const startHereContent = renderTemplate(startHereTemplate, { TEAM_NAME: teamName });
103
- const startHerePath = path.join(vaultPath, "Start Here.md");
95
+ const startHereTemplate = await fs.readFile(path.join(ctx.commonDir, "Start Here.md.hbs"), "utf-8");
96
+ const startHereContent = renderTemplate(startHereTemplate, { KB_NAME: ctx.kbName });
97
+ const startHerePath = path.join(ctx.vaultPath, "Start Here.md");
104
98
  if (!(await fs.pathExists(startHerePath))) {
105
99
  await fs.writeFile(startHerePath, startHereContent);
106
- filesCreated++;
100
+ ctx.filesCreated++;
107
101
  }
108
- // 6. Generate AGENT.md (two-layer: common skeleton + preset section)
109
- const agentSkeletonTemplate = await fs.readFile(path.join(commonDir, "AGENT.md.hbs"), "utf-8");
102
+ }
103
+ /**
104
+ * Generate AGENT.md from the common skeleton + preset section.
105
+ */
106
+ export async function createAgentMd(ctx, ownerName, presetConfig, presetDir, projects, jiraHost, jiraProject) {
107
+ const agentSkeletonTemplate = await fs.readFile(path.join(ctx.commonDir, "AGENT.md.hbs"), "utf-8");
110
108
  // Render preset agent-section
111
109
  let roleSection = "";
112
110
  const agentSectionPath = path.join(presetDir, "agent-section.hbs");
@@ -128,42 +126,68 @@ export async function createVault(config) {
128
126
  HAS_JIRA: !!(jiraHost && jiraProject),
129
127
  });
130
128
  }
131
- // Build team table
132
- let teamTable;
133
- if (members.length > 0) {
134
- const rows = members.map((m) => `| [[${m.name}]] | ${m.role} |`).join("\n");
135
- teamTable = `| Name | Role |\n|------|------|\n${rows}`;
136
- }
137
- else {
138
- teamTable = "(No members added yet — create notes in People/)";
139
- }
140
- // Build template list string
141
- const templateList = allTemplateNames.map((t) => `- ${t}`).join("\n");
142
129
  const agentContent = renderTemplate(agentSkeletonTemplate, {
143
- TEAM_NAME: teamName,
144
- AGENT_DESCRIPTION: presetConfig.agentDescription,
130
+ KB_NAME: ctx.kbName,
131
+ OWNER_NAME: ownerName,
145
132
  ROLE_SECTION: roleSection,
146
- TEAM_TABLE: teamTable,
147
- TEMPLATE_LIST: templateList,
148
- JIRA_PROJECT: jiraProject,
149
- HAS_JIRA: !!(jiraHost && jiraProject),
150
133
  });
151
- const agentMdPath = path.join(vaultPath, "AGENT.md");
152
- const claudeMdPath = path.join(vaultPath, "CLAUDE.md");
134
+ const agentMdPath = path.join(ctx.vaultPath, "AGENT.md");
153
135
  if (!(await fs.pathExists(agentMdPath))) {
154
136
  await fs.writeFile(agentMdPath, agentContent);
155
- filesCreated++;
137
+ ctx.filesCreated++;
138
+ }
139
+ }
140
+ /**
141
+ * Apply preset overlay: create preset-specific directories and copy preset templates.
142
+ * Returns the list of all template names (common + preset) for AGENT.md generation.
143
+ */
144
+ export async function applyPresetOverlay(ctx, presetConfig, presetDir) {
145
+ // Create preset-specific directories
146
+ for (const dir of presetConfig.directories) {
147
+ await fs.ensureDir(path.join(ctx.vaultPath, dir));
148
+ }
149
+ ctx.directories.push(...presetConfig.directories);
150
+ // Collect common template names already installed
151
+ const templateDest = path.join(ctx.vaultPath, "Knowledge/templates");
152
+ const allTemplateNames = [];
153
+ // Read existing common templates (already copied by createMinimalCore)
154
+ if (await fs.pathExists(templateDest)) {
155
+ const existing = await fs.readdir(templateDest);
156
+ for (const file of existing) {
157
+ if (file.endsWith(".md")) {
158
+ allTemplateNames.push(file.replace(/\.md$/, ""));
159
+ }
160
+ }
161
+ }
162
+ // Copy preset templates
163
+ const presetTemplatesDir = path.join(presetDir, "templates");
164
+ if (await fs.pathExists(presetTemplatesDir)) {
165
+ const files = await fs.readdir(presetTemplatesDir);
166
+ for (const file of files) {
167
+ await fs.copy(path.join(presetTemplatesDir, file), path.join(templateDest, file), { overwrite: false });
168
+ allTemplateNames.push(file.replace(/\.md$/, ""));
169
+ ctx.filesCreated++;
170
+ ctx.installedFiles.templates.push(`Knowledge/templates/${file}`);
171
+ }
156
172
  }
157
- if (!(await fs.pathExists(claudeMdPath))) {
158
- await fs.writeFile(claudeMdPath, agentContent);
159
- filesCreated++;
173
+ return allTemplateNames;
174
+ }
175
+ /**
176
+ * Create people notes from members config.
177
+ * Ensures People/ directory exists.
178
+ */
179
+ export async function createPeopleNotes(ctx, members) {
180
+ if (members.length === 0)
181
+ return;
182
+ await fs.ensureDir(path.join(ctx.vaultPath, "People"));
183
+ if (!ctx.directories.includes("People")) {
184
+ ctx.directories.push("People");
160
185
  }
161
- // 7. Create people notes
162
186
  for (const member of members) {
163
187
  const content = `---
164
188
  title: "${member.name}"
165
189
  type: person
166
- team: "${teamName}"
190
+ team: "${ctx.kbName}"
167
191
  role: "${member.role}"
168
192
  status: active
169
193
  tags: [person]
@@ -172,22 +196,33 @@ tags: [person]
172
196
  # ${member.name}
173
197
 
174
198
  **Role**: ${member.role}
175
- **Team**: ${teamName}
199
+ **Team**: ${ctx.kbName}
176
200
  `;
177
- const memberPath = path.join(vaultPath, `People/${member.name}.md`);
201
+ const memberPath = path.join(ctx.vaultPath, `People/${member.name}.md`);
178
202
  if (!(await fs.pathExists(memberPath))) {
179
203
  await fs.writeFile(memberPath, content);
180
- filesCreated++;
204
+ ctx.filesCreated++;
181
205
  }
182
206
  }
183
- // 8. Create project notes
207
+ }
208
+ /**
209
+ * Create project notes from projects config.
210
+ * Ensures Projects/ directory exists.
211
+ */
212
+ export async function createProjectNotes(ctx, projects) {
213
+ if (projects.length === 0)
214
+ return;
215
+ await fs.ensureDir(path.join(ctx.vaultPath, "Projects"));
216
+ if (!ctx.directories.includes("Projects")) {
217
+ ctx.directories.push("Projects");
218
+ }
184
219
  for (const project of projects) {
185
220
  const content = `---
186
221
  title: "${project.name}"
187
222
  type: feature
188
223
  status: active
189
224
  date: ${today()}
190
- team: "${teamName}"
225
+ team: "${ctx.kbName}"
191
226
  jira: ""
192
227
  stakeholders: []
193
228
  priority: ""
@@ -198,98 +233,111 @@ tags: [project]
198
233
 
199
234
  ${project.description}
200
235
  `;
201
- const projectPath = path.join(vaultPath, `Projects/${project.name}.md`);
236
+ const projectPath = path.join(ctx.vaultPath, `Projects/${project.name}.md`);
202
237
  if (!(await fs.pathExists(projectPath))) {
203
238
  await fs.writeFile(projectPath, content);
204
- filesCreated++;
239
+ ctx.filesCreated++;
205
240
  }
206
241
  }
207
- // 9. Create team index (always, since Start Here.md links to it)
208
- {
209
- let teamIndexContent = `---
210
- title: "${teamName} Team"
242
+ }
243
+ /**
244
+ * Create team index note in People/.
245
+ * Only call when the vault has a People/ directory (preset includes it or members exist).
246
+ */
247
+ export async function createTeamIndex(ctx, members, projects) {
248
+ await fs.ensureDir(path.join(ctx.vaultPath, "People"));
249
+ if (!ctx.directories.includes("People")) {
250
+ ctx.directories.push("People");
251
+ }
252
+ let teamIndexContent = `---
253
+ title: "${ctx.kbName} Team"
211
254
  type: reference
212
- team: "${teamName}"
255
+ team: "${ctx.kbName}"
213
256
  tags: [team]
214
257
  ---
215
258
 
216
- # ${teamName} Team
259
+ # ${ctx.kbName} Team
217
260
 
218
261
  ## Members
219
262
 
220
263
  `;
221
- if (members.length > 0) {
222
- teamIndexContent += "| Name | Role |\n|------|------|\n";
223
- teamIndexContent += members.map((m) => `| [[${m.name}]] | ${m.role} |`).join("\n");
224
- }
225
- else {
226
- teamIndexContent += "(No members added yet)";
227
- }
228
- teamIndexContent += "\n\n## Active Projects\n\n";
229
- if (projects.length > 0) {
230
- teamIndexContent += projects
231
- .map((p) => `- [[${p.name}]] — ${p.description}`)
232
- .join("\n");
233
- }
234
- else {
235
- teamIndexContent += "(No projects added yet)";
236
- }
237
- teamIndexContent += "\n";
238
- const teamIndexPath = path.join(vaultPath, `People/${teamName} Team.md`);
239
- if (!(await fs.pathExists(teamIndexPath))) {
240
- await fs.writeFile(teamIndexPath, teamIndexContent);
241
- filesCreated++;
242
- }
264
+ if (members.length > 0) {
265
+ teamIndexContent += "| Name | Role |\n|------|------|\n";
266
+ teamIndexContent += members.map((m) => `| [[${m.name}]] | ${m.role} |`).join("\n");
243
267
  }
244
- // 10. Create .gitkeep in empty directories
245
- const dirsNeedingGitkeep = [
246
- "Inbox",
247
- "Knowledge/concepts",
248
- "Systems",
249
- "Archive",
250
- "Daily",
251
- ];
252
- // Add preset-specific dirs if they're empty
253
- for (const dir of presetConfig.directories) {
254
- dirsNeedingGitkeep.push(dir);
268
+ else {
269
+ teamIndexContent += "(No members added yet)";
255
270
  }
256
- for (const dir of dirsNeedingGitkeep) {
257
- const dirPath = path.join(vaultPath, dir);
258
- if (await fs.pathExists(dirPath)) {
259
- const files = await fs.readdir(dirPath);
260
- if (files.length === 0) {
261
- await fs.writeFile(path.join(dirPath, ".gitkeep"), "");
262
- }
263
- }
271
+ teamIndexContent += "\n\n## Active Projects\n\n";
272
+ if (projects.length > 0) {
273
+ teamIndexContent += projects
274
+ .map((p) => `- [[${p.name}]] — ${p.description}`)
275
+ .join("\n");
264
276
  }
265
- // 11. Configure vault as OpenCode project (plugin + skills + commands)
266
- await configureOpenCodeProject(vaultPath, installedFiles);
267
- // 12. Configure MCP servers in global OpenCode config
277
+ else {
278
+ teamIndexContent += "(No projects added yet)";
279
+ }
280
+ teamIndexContent += "\n";
281
+ const teamIndexPath = path.join(ctx.vaultPath, `People/${ctx.kbName} Team.md`);
282
+ if (!(await fs.pathExists(teamIndexPath))) {
283
+ await fs.writeFile(teamIndexPath, teamIndexContent);
284
+ ctx.filesCreated++;
285
+ }
286
+ }
287
+ // ---------------------------------------------------------------------------
288
+ // Main orchestrator
289
+ // ---------------------------------------------------------------------------
290
+ export async function createVault(config) {
291
+ const { kbName, vaultPath, members, projects, glossaryEntries, jiraHost, jiraProject, preset } = config;
292
+ const presetName = preset ?? "pm-tpm"; // Fallback if config wasn't parsed through Zod
293
+ const { config: presetConfig, presetsDir } = loadPreset(presetName);
294
+ const presetDir = path.join(presetsDir, presetName);
295
+ const initMode = detectInitMode(vaultPath);
296
+ const preserveObsidian = initMode === "obsidian-vault";
297
+ const ctx = makeContext(vaultPath, kbName, preserveObsidian);
298
+ // 1. Create minimal core (dirs, .obsidian, common templates, Glossary, Start Here)
299
+ await createMinimalCore(ctx, glossaryEntries);
300
+ // 2. Apply preset overlay (preset dirs + preset templates)
301
+ const allTemplateNames = await applyPresetOverlay(ctx, presetConfig, presetDir);
302
+ // 3. Generate AGENT.md
303
+ await createAgentMd(ctx, config.ownerName, presetConfig, presetDir, projects, jiraHost, jiraProject);
304
+ // 4. Create people notes (conditional)
305
+ await createPeopleNotes(ctx, members);
306
+ // 5. Create project notes (conditional)
307
+ await createProjectNotes(ctx, projects);
308
+ // 6. Create team index (only when preset declares People/ or members exist)
309
+ const hasPeopleDir = presetConfig.directories.includes("People") || members.length > 0;
310
+ if (hasPeopleDir) {
311
+ await createTeamIndex(ctx, members, projects);
312
+ }
313
+ // 7. Configure vault as OpenCode project (plugin + skills + commands)
314
+ await configureOpenCodeProject(ctx.vaultPath, ctx.installedFiles);
315
+ // 8. Configure MCP servers in global OpenCode config
268
316
  const mcpResult = await configureMcp(presetConfig);
269
- // 13. Install Obsidian community plugins from preset
270
- const pluginsResult = await configureObsidianPlugins(vaultPath, presetConfig);
271
- // 14. Configure AI provider in global OpenCode config
317
+ // 9. Install Obsidian community plugins from preset
318
+ const pluginsResult = await configureObsidianPlugins(ctx.vaultPath, presetConfig);
319
+ // 10. Configure AI provider in global OpenCode config
272
320
  let providerResult = null;
273
321
  if (config.provider && config.provider !== "skip") {
274
322
  providerResult = await configureProvider(config.provider, config.provider === "gemini" ? config.gcpProjectId : undefined);
275
323
  }
276
- // 15. Write BYOAO manifest
277
- await writeManifest(vaultPath, presetName, installedFiles);
278
- // 16. Count wikilinks from all generated markdown files
324
+ // 11. Write BYOAO manifest
325
+ await writeManifest(ctx.vaultPath, presetName, ctx.installedFiles);
326
+ // 12. Count wikilinks from all generated markdown files
279
327
  let wikilinksCreated = 0;
280
- const entries = await fs.readdir(vaultPath, { recursive: true });
328
+ const entries = await fs.readdir(ctx.vaultPath, { recursive: true });
281
329
  for (const entry of entries) {
282
330
  const entryStr = String(entry);
283
331
  if (entryStr.endsWith(".md") && !entryStr.startsWith(".obsidian")) {
284
- const content = await fs.readFile(path.join(vaultPath, entryStr), "utf-8");
332
+ const content = await fs.readFile(path.join(ctx.vaultPath, entryStr), "utf-8");
285
333
  wikilinksCreated += countWikilinks(content);
286
334
  }
287
335
  }
288
336
  return {
289
- vaultPath,
290
- filesCreated,
337
+ vaultPath: ctx.vaultPath,
338
+ filesCreated: ctx.filesCreated,
291
339
  wikilinksCreated,
292
- directories: allDirectories,
340
+ directories: ctx.directories,
293
341
  mcpResult,
294
342
  pluginsResult,
295
343
  providerResult,