@groupby/ai-dev 0.2.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +3 -1
  2. package/dist/index.js +86 -19
  3. package/package.json +3 -3
  4. package/toolsets/rzlv-flow/README.md +54 -9
  5. package/toolsets/rzlv-flow/resources/confluence-file-structure.md +129 -23
  6. package/toolsets/rzlv-flow/resources/sync-state-format.md +109 -0
  7. package/toolsets/rzlv-flow/skills/jira-daily-triage/SKILL.md +13 -5
  8. package/toolsets/rzlv-flow/skills/jira-sprint-status/SKILL.md +28 -1
  9. package/toolsets/rzlv-flow/skills/jira-ticket-focus/SKILL.md +6 -1
  10. package/toolsets/rzlv-flow/skills/jira-wrap-sync/SKILL.md +41 -8
  11. package/skills/skills/README.md +0 -61
  12. package/skills/skills/archived/README.md +0 -3
  13. package/skills/skills/library/README.md +0 -3
  14. package/skills/skills/library/frontend-design/LICENSE.txt +0 -177
  15. package/skills/skills/library/frontend-design/SKILL.md +0 -42
  16. package/teams/teams/brain-studio/skills/code-review/SKILL.md +0 -46
  17. package/toolsets/toolsets/rzlv-flow/README.md +0 -102
  18. package/toolsets/toolsets/rzlv-flow/docs/mcp-setup.md +0 -126
  19. package/toolsets/toolsets/rzlv-flow/resources/README.md +0 -16
  20. package/toolsets/toolsets/rzlv-flow/resources/confluence-file-structure.md +0 -285
  21. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/README.md +0 -19
  22. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/decisions.md +0 -36
  23. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/initiative-overview.md +0 -40
  24. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/strategic-context.md +0 -44
  25. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/technical-architecture.md +0 -48
  26. package/toolsets/toolsets/rzlv-flow/resources/fcmp-protocol.md +0 -331
  27. package/toolsets/toolsets/rzlv-flow/resources/jira-file-structure.md +0 -177
  28. package/toolsets/toolsets/rzlv-flow/resources/sync-state-format.md +0 -318
  29. package/toolsets/toolsets/rzlv-flow/skills/atlassian-orchestrator/SKILL.md +0 -643
  30. package/toolsets/toolsets/rzlv-flow/skills/context-analyst/SKILL.md +0 -265
  31. package/toolsets/toolsets/rzlv-flow/skills/jira-comment/SKILL.md +0 -89
  32. package/toolsets/toolsets/rzlv-flow/skills/jira-daily-triage/SKILL.md +0 -143
  33. package/toolsets/toolsets/rzlv-flow/skills/jira-sprint-status/SKILL.md +0 -143
  34. package/toolsets/toolsets/rzlv-flow/skills/jira-status/SKILL.md +0 -97
  35. package/toolsets/toolsets/rzlv-flow/skills/jira-sync/SKILL.md +0 -148
  36. package/toolsets/toolsets/rzlv-flow/skills/jira-ticket-focus/SKILL.md +0 -245
  37. package/toolsets/toolsets/rzlv-flow/skills/jira-ticket-trace/SKILL.md +0 -112
  38. package/toolsets/toolsets/rzlv-flow/skills/jira-wrap-sync/SKILL.md +0 -260
  39. /package/toolsets/{toolsets/rzlv-flow → rzlv-flow}/docs/getting-started.md +0 -0
  40. /package/toolsets/{toolsets/rzlv-flow → rzlv-flow}/skills/confluence-fetch/SKILL.md +0 -0
  41. /package/toolsets/{toolsets/rzlv-flow → rzlv-flow}/skills/confluence-publish/SKILL.md +0 -0
package/README.md CHANGED
@@ -57,6 +57,7 @@ When installing a team's skills, you will be prompted to optionally include skil
57
57
  | Flag | Description |
58
58
  | --- | --- |
59
59
  | `--target <dir>` | Install into a specific directory (defaults to cwd) |
60
+ | `--ai-dir <path>` | Override the AI skills/resources directory (defaults to `docs/ai`) |
60
61
  | `--dry-run` | Preview what would be installed without writing files |
61
62
  | `--force` | Overwrite existing files without prompting |
62
63
  | `--skip-existing` | Skip files that already exist |
@@ -65,8 +66,9 @@ When installing a team's skills, you will be prompted to optionally include skil
65
66
 
66
67
  1. **Discovery** — The CLI scans bundled `skills/` and `teams/` directories for `SKILL.md` files and reads their YAML frontmatter (name, description, etc.).
67
68
  2. **Client detection** — It checks the target project for known config directories (`.github/`, `.claude/`, `.agents/`) to determine which LLM clients are in use.
68
- 3. **Installation** — Each selected skill is copied into `docs/ai/skills/<name>/` and a stub `SKILL.md` is generated in each client's skills directory pointing back to the full skill file.
69
+ 3. **Installation** — Each selected skill is copied into `<ai-dir>/skills/<name>/` (default: `docs/ai/skills/<name>/`) and a stub `SKILL.md` is generated in each client's skills directory pointing back to the full skill file.
69
70
  4. **Conflict handling** — If a file already exists and its content differs, you are prompted to overwrite or skip (unless `--force` or `--skip-existing` is set).
71
+ 5. **Path patching** — When `--ai-dir` is set to a non-default value, any `docs/ai/` references inside installed `.md` files are replaced with the custom path so LLMs can find resource files at runtime.
70
72
 
71
73
  ## Supported clients
72
74
 
package/dist/index.js CHANGED
@@ -22,9 +22,9 @@ function parseFrontmatter(content) {
22
22
  content: parsed.content
23
23
  };
24
24
  }
25
- function generateStub(frontmatter, skillName) {
25
+ function generateStub(frontmatter, skillName, aiDir = "docs/ai") {
26
26
  const body = `
27
- See \`docs/ai/skills/${skillName}/SKILL.md\` and follow closely.
27
+ See \`${aiDir}/skills/${skillName}/SKILL.md\` and follow closely.
28
28
  `;
29
29
  return matter.stringify(body, frontmatter);
30
30
  }
@@ -266,6 +266,7 @@ async function detectClients(targetDir) {
266
266
  // src/lib/installer.ts
267
267
  import path3 from "path";
268
268
  import fs4 from "fs-extra";
269
+ var DEFAULT_AI_DIR = "docs/ai";
269
270
  async function handleFile(srcPath, destPath, options, result, onConflict, contentOverride) {
270
271
  const relativeDest = path3.relative(options.targetDir, destPath);
271
272
  if (await fs4.pathExists(destPath)) {
@@ -309,6 +310,12 @@ async function handleFile(srcPath, destPath, options, result, onConflict, conten
309
310
  result.created.push(relativeDest);
310
311
  }
311
312
  }
313
+ async function patchContent(srcPath, aiDir) {
314
+ if (aiDir === DEFAULT_AI_DIR) return void 0;
315
+ if (!srcPath.endsWith(".md")) return void 0;
316
+ const content = await fs4.readFile(srcPath, "utf-8");
317
+ return content.replaceAll(`${DEFAULT_AI_DIR}/`, `${aiDir}/`);
318
+ }
312
319
  async function installSkill(skill, options, onConflict) {
313
320
  const result = {
314
321
  skill: skill.name,
@@ -316,14 +323,15 @@ async function installSkill(skill, options, onConflict) {
316
323
  skipped: [],
317
324
  overwritten: []
318
325
  };
319
- const destFolder = path3.join(options.targetDir, "docs", "ai", "skills", skill.name);
326
+ const destFolder = path3.join(options.targetDir, options.aiDir, "skills", skill.name);
320
327
  const sourceFiles = await fs4.readdir(skill.sourcePath);
321
328
  for (const file of sourceFiles) {
322
329
  const srcFile = path3.join(skill.sourcePath, file);
323
330
  const stat = await fs4.stat(srcFile);
324
331
  if (stat.isFile()) {
325
332
  const destFile = path3.join(destFolder, file);
326
- await handleFile(srcFile, destFile, options, result, onConflict);
333
+ const contentOverride = await patchContent(srcFile, options.aiDir);
334
+ await handleFile(srcFile, destFile, options, result, onConflict, contentOverride);
327
335
  }
328
336
  }
329
337
  for (const client of options.clients) {
@@ -333,7 +341,7 @@ async function installSkill(skill, options, onConflict) {
333
341
  skill.name,
334
342
  "SKILL.md"
335
343
  );
336
- const stubContent = generateStub(skill.frontmatter, skill.name);
344
+ const stubContent = generateStub(skill.frontmatter, skill.name, options.aiDir);
337
345
  await handleFile("", stubPath, options, result, onConflict, stubContent);
338
346
  }
339
347
  return result;
@@ -355,14 +363,15 @@ async function installResources(toolset, options, onConflict) {
355
363
  if (!toolset.hasResources) return result;
356
364
  const entries = await fs4.readdir(toolset.resourcesPath);
357
365
  const resourceFiles = entries.filter((e) => e !== "README.md");
358
- const destDir = path3.join(options.targetDir, "docs", "ai", "resources");
366
+ const destDir = path3.join(options.targetDir, options.aiDir, "resources");
359
367
  const fileResult = { skill: toolset.name, created: [], skipped: [], overwritten: [] };
360
368
  for (const file of resourceFiles) {
361
369
  const srcFile = path3.join(toolset.resourcesPath, file);
362
370
  const stat = await fs4.stat(srcFile);
363
371
  if (!stat.isFile()) continue;
364
372
  const destFile = path3.join(destDir, file);
365
- await handleFile(srcFile, destFile, options, fileResult, onConflict);
373
+ const contentOverride = await patchContent(srcFile, options.aiDir);
374
+ await handleFile(srcFile, destFile, options, fileResult, onConflict, contentOverride);
366
375
  }
367
376
  result.created = fileResult.created;
368
377
  result.skipped = fileResult.skipped;
@@ -371,7 +380,7 @@ async function installResources(toolset, options, onConflict) {
371
380
  }
372
381
 
373
382
  // src/lib/prompts.ts
374
- import { select, checkbox, confirm } from "@inquirer/prompts";
383
+ import { select, checkbox, confirm, input } from "@inquirer/prompts";
375
384
  import chalk2 from "chalk";
376
385
  async function promptSelectSkills(skills) {
377
386
  const librarySkills = skills.filter((s) => s.sourceType === "library");
@@ -457,12 +466,22 @@ async function promptIncludeLibrary() {
457
466
  default: false
458
467
  });
459
468
  }
460
- async function promptConfirmInstall(skills, clients, targetDir) {
469
+ async function promptInstallDir() {
470
+ const raw = await input({
471
+ message: "Install location for AI skills and resources:",
472
+ default: "docs/ai"
473
+ });
474
+ return raw.replace(/\/+$/, "") || "docs/ai";
475
+ }
476
+ async function promptConfirmInstall(skills, clients, targetDir, aiDir = "docs/ai") {
461
477
  console.log();
462
478
  console.log(chalk2.bold("Install summary:"));
463
479
  console.log(` Skills: ${skills.map((s) => s.name).join(", ")}`);
464
480
  console.log(` Clients: ${clients.map((c) => c.name).join(", ") || chalk2.dim("none")}`);
465
481
  console.log(` Target: ${targetDir}`);
482
+ if (aiDir !== "docs/ai") {
483
+ console.log(` AI directory: ${aiDir}`);
484
+ }
466
485
  console.log();
467
486
  return confirm({ message: "Proceed with installation?", default: true });
468
487
  }
@@ -485,6 +504,14 @@ function formatTeamName2(name) {
485
504
  }
486
505
 
487
506
  // src/commands/install.ts
507
+ function normalizeAiDir(raw) {
508
+ const trimmed = raw.replace(/\/+$/, "") || DEFAULT_AI_DIR;
509
+ if (path4.isAbsolute(trimmed)) {
510
+ console.log(chalk3.red(`--ai-dir must be a relative path, got: ${trimmed}`));
511
+ process.exit(1);
512
+ }
513
+ return trimmed;
514
+ }
488
515
  function printResults(results, options, resourceResult) {
489
516
  const prefix = options.dryRun ? chalk3.yellow("[DRY RUN] ") : "";
490
517
  const createdWord = options.dryRun ? "would create" : "created";
@@ -525,6 +552,7 @@ async function installSkillCmd(name, _opts, cmd) {
525
552
  const targetDir = path4.resolve(
526
553
  parentOpts.target || process.cwd()
527
554
  );
555
+ const aiDir = normalizeAiDir(parentOpts.aiDir || DEFAULT_AI_DIR);
528
556
  const skill = await findSkill(name);
529
557
  if (!skill) {
530
558
  const all = await discoverSkills();
@@ -541,6 +569,7 @@ async function installSkillCmd(name, _opts, cmd) {
541
569
  }
542
570
  const options = {
543
571
  targetDir,
572
+ aiDir,
544
573
  clients,
545
574
  force,
546
575
  skipExisting,
@@ -564,6 +593,7 @@ async function installTeamCmd(name, _opts, cmd) {
564
593
  const targetDir = path4.resolve(
565
594
  parentOpts.target || process.cwd()
566
595
  );
596
+ const aiDir = normalizeAiDir(parentOpts.aiDir || DEFAULT_AI_DIR);
567
597
  const teams = await discoverTeams();
568
598
  const team = teams.find(
569
599
  (t) => t.name === name || t.name.toLowerCase() === name.toLowerCase()
@@ -589,6 +619,7 @@ async function installTeamCmd(name, _opts, cmd) {
589
619
  }
590
620
  const options = {
591
621
  targetDir,
622
+ aiDir,
592
623
  clients,
593
624
  force,
594
625
  skipExisting,
@@ -609,6 +640,7 @@ async function installToolsetCmd(name, _opts, cmd) {
609
640
  const targetDir = path4.resolve(
610
641
  parentOpts.target || process.cwd()
611
642
  );
643
+ const aiDir = normalizeAiDir(parentOpts.aiDir || DEFAULT_AI_DIR);
612
644
  const toolset = await findToolset(name);
613
645
  if (!toolset) {
614
646
  const allToolsets = await discoverToolsets();
@@ -627,6 +659,7 @@ async function installToolsetCmd(name, _opts, cmd) {
627
659
  }
628
660
  const options = {
629
661
  targetDir,
662
+ aiDir,
630
663
  clients,
631
664
  force,
632
665
  skipExisting,
@@ -640,11 +673,18 @@ async function installToolsetCmd(name, _opts, cmd) {
640
673
  const results = await installSkills(toolset.skills, options, conflictHandler);
641
674
  printResults(results, options, resourceResult);
642
675
  }
676
+ var INHERITED_OPTIONS_HELP = `
677
+ Parent options (pass before subcommand):
678
+ --force Overwrite existing files without prompting
679
+ --skip-existing Skip files that already exist without prompting
680
+ --dry-run Show what would be installed without writing files
681
+ --target <dir> Install to a different directory (default: CWD)
682
+ --ai-dir <path> Override the AI skills/resources directory (default: docs/ai)`;
643
683
  function registerInstallCommand(program2) {
644
- const install = program2.command("install").description("Install skills").option("--force", "Overwrite existing files without prompting").option("--skip-existing", "Skip files that already exist without prompting").option("--dry-run", "Show what would be installed without writing files").option("--target <dir>", "Install to a different directory (default: CWD)");
645
- install.command("skill <name>").description("Install a specific skill").action(installSkillCmd);
646
- install.command("team <name>").description("Install all skills for a team").action(installTeamCmd);
647
- install.command("toolset <name>").description("Install all skills and resources for a toolset").action(installToolsetCmd);
684
+ const install = program2.command("install").description("Install skills").option("--force", "Overwrite existing files without prompting").option("--skip-existing", "Skip files that already exist without prompting").option("--dry-run", "Show what would be installed without writing files").option("--target <dir>", "Install to a different directory (default: CWD)").option("--ai-dir <path>", "Override the AI skills/resources directory (default: docs/ai)");
685
+ install.command("skill <name>").description("Install a specific skill").addHelpText("after", INHERITED_OPTIONS_HELP).action(installSkillCmd);
686
+ install.command("team <name>").description("Install all skills for a team").addHelpText("after", INHERITED_OPTIONS_HELP).action(installTeamCmd);
687
+ install.command("toolset <name>").description("Install all skills and resources for a toolset").addHelpText("after", INHERITED_OPTIONS_HELP).action(installToolsetCmd);
648
688
  }
649
689
 
650
690
  // src/commands/interactive.ts
@@ -757,20 +797,32 @@ async function runInteractive() {
757
797
  const selectedSkills = await promptSelectSkills(allSkills);
758
798
  const detected = await detectClients(targetDir);
759
799
  const clients = await promptSelectClients(ALL_CLIENTS, detected);
760
- const confirmed = await promptConfirmInstall(selectedSkills, clients, targetDir);
800
+ const aiDir = await promptInstallDir();
801
+ const confirmed = await promptConfirmInstall(selectedSkills, clients, targetDir, aiDir);
761
802
  if (!confirmed) {
762
803
  console.log(chalk4.dim("Cancelled."));
763
804
  return;
764
805
  }
765
806
  const options = {
766
807
  targetDir,
808
+ aiDir,
767
809
  clients,
768
810
  force: false,
769
811
  skipExisting: false,
770
812
  dryRun: false
771
813
  };
772
814
  const results = await installSkills(selectedSkills, options, promptConflict);
773
- printResults2(results, options);
815
+ let resourceResult;
816
+ const toolsetNames = new Set(
817
+ selectedSkills.filter((s) => s.sourceType === "toolset" && s.toolsetName).map((s) => s.toolsetName)
818
+ );
819
+ for (const tsName of toolsetNames) {
820
+ const toolset = await findToolset(tsName);
821
+ if (toolset && toolset.hasResources) {
822
+ resourceResult = await installResources(toolset, options, promptConflict);
823
+ }
824
+ }
825
+ printResults2(results, options, resourceResult);
774
826
  } else if (action === "install-team") {
775
827
  const teams = await discoverTeams();
776
828
  if (teams.length === 0) {
@@ -787,20 +839,32 @@ async function runInteractive() {
787
839
  }
788
840
  const detected = await detectClients(targetDir);
789
841
  const clients = await promptSelectClients(ALL_CLIENTS, detected);
790
- const confirmed = await promptConfirmInstall(skillsToInstall, clients, targetDir);
842
+ const aiDir = await promptInstallDir();
843
+ const confirmed = await promptConfirmInstall(skillsToInstall, clients, targetDir, aiDir);
791
844
  if (!confirmed) {
792
845
  console.log(chalk4.dim("Cancelled."));
793
846
  return;
794
847
  }
795
848
  const options = {
796
849
  targetDir,
850
+ aiDir,
797
851
  clients,
798
852
  force: false,
799
853
  skipExisting: false,
800
854
  dryRun: false
801
855
  };
802
856
  const results = await installSkills(skillsToInstall, options, promptConflict);
803
- printResults2(results, options);
857
+ let resourceResult;
858
+ const toolsetNames = new Set(
859
+ skillsToInstall.filter((s) => s.sourceType === "toolset" && s.toolsetName).map((s) => s.toolsetName)
860
+ );
861
+ for (const tsName of toolsetNames) {
862
+ const toolset = await findToolset(tsName);
863
+ if (toolset && toolset.hasResources) {
864
+ resourceResult = await installResources(toolset, options, promptConflict);
865
+ }
866
+ }
867
+ printResults2(results, options, resourceResult);
804
868
  } else if (action === "install-toolset") {
805
869
  const toolsets = await discoverToolsets();
806
870
  if (toolsets.length === 0) {
@@ -810,13 +874,15 @@ async function runInteractive() {
810
874
  const toolset = await promptSelectToolset(toolsets);
811
875
  const detected = await detectClients(targetDir);
812
876
  const clients = await promptSelectClients(ALL_CLIENTS, detected);
813
- const confirmed = await promptConfirmInstall(toolset.skills, clients, targetDir);
877
+ const aiDir = await promptInstallDir();
878
+ const confirmed = await promptConfirmInstall(toolset.skills, clients, targetDir, aiDir);
814
879
  if (!confirmed) {
815
880
  console.log(chalk4.dim("Cancelled."));
816
881
  return;
817
882
  }
818
883
  const options = {
819
884
  targetDir,
885
+ aiDir,
820
886
  clients,
821
887
  force: false,
822
888
  skipExisting: false,
@@ -846,6 +912,7 @@ Examples:
846
912
  $ npx @groupby/ai-dev install skill frontend-design Install a single skill
847
913
  $ npx @groupby/ai-dev install team brain-studio Install all team skills
848
914
  $ npx @groupby/ai-dev install toolset rzlv-flow Install all skills + resources for a toolset
849
- $ npx @groupby/ai-dev install team brain-studio --dry-run`
915
+ $ npx @groupby/ai-dev install team brain-studio --dry-run
916
+ $ npx @groupby/ai-dev install skill frontend-design --ai-dir .ai`
850
917
  );
851
918
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groupby/ai-dev",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "Interactive installer for Rezolve Ai development skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,7 @@
13
13
  "toolsets/"
14
14
  ],
15
15
  "scripts": {
16
- "prebuild": "cp -r ../skills ./skills && cp -r ../teams ./teams && cp -r ../toolsets ./toolsets",
16
+ "prebuild": "rm -rf skills teams toolsets && cp -r ../skills ./skills && cp -r ../teams ./teams && cp -r ../toolsets ./toolsets",
17
17
  "build": "tsup",
18
18
  "prepublishOnly": "npm run build",
19
19
  "test": "vitest run",
@@ -29,7 +29,7 @@
29
29
  "node": ">=18"
30
30
  },
31
31
  "publishConfig": {
32
- "access": "restricted"
32
+ "access": "public"
33
33
  },
34
34
  "dependencies": {
35
35
  "@inquirer/prompts": "^7.0.0",
@@ -4,20 +4,41 @@ A toolset of modular, composable skills for Jira and Confluence developer workfl
4
4
 
5
5
  Each skill is standalone and can be installed individually or as a complete suite.
6
6
 
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ # 1. Install all skills + resources
11
+ npx @groupby/ai-dev install toolset rzlv-flow
12
+
13
+ # 2. Configure Atlassian MCP (see docs/mcp-setup.md)
14
+ # Add atlassian-rovo config to .vscode/mcp.json
15
+
16
+ # 3. Use — ask your AI assistant:
17
+ # "Start my day" → daily triage
18
+ # "Focus on PROJ-123" → ticket deep-dive
19
+ # "Wrap up PROJ-123" → wrap & sync
20
+ ```
21
+
22
+ See [docs/getting-started.md](docs/getting-started.md) for a full walkthrough.
23
+
7
24
  ## Skills
8
25
 
9
26
  | Skill | Description | Status |
10
27
  |-------|-------------|--------|
11
- | `jira-daily-triage` | Start-of-day ticket triage with sprint detection | Planned |
12
- | `jira-ticket-focus` | Deep-load a ticket with full context | Planned |
13
- | `jira-wrap-sync` | Wrap up work, draft comment, multi-action sync | Planned |
14
- | `jira-ticket-trace` | Trace code back to Jira requirements | Planned |
15
- | `jira-comment` | Add a comment to a Jira ticket with FCMP safety | Planned |
16
- | `jira-status` | Change ticket status with transition validation | Planned |
17
- | `jira-sprint-status` | Sprint status report with completion metrics | Planned |
18
- | `jira-sync` | Force-sync local Jira context with remote state | Planned |
28
+ | `jira-daily-triage` | Start-of-day ticket triage with sprint detection | Complete |
29
+ | `jira-ticket-focus` | Deep-load a ticket with full context | Complete |
30
+ | `jira-wrap-sync` | Wrap up work, draft comment, multi-action sync | Complete |
31
+ | `jira-ticket-trace` | Trace code back to Jira requirements | Complete |
32
+ | `jira-comment` | Add a comment to a Jira ticket with FCMP safety | Complete |
33
+ | `jira-status` | Change ticket status with transition validation | Complete |
34
+ | `jira-sprint-status` | Sprint status report with completion metrics | Complete |
35
+ | `jira-sync` | Force-sync local Jira context with remote state | Complete |
19
36
  | `atlassian-orchestrator` | Pull surrounding ticket context and create structures | Complete |
20
37
  | `context-analyst` | Transform meeting transcripts into structured docs | Complete |
38
+ | `confluence-publish` | Publish local markdown to Confluence with Leaf Bundle mirrors | Complete |
39
+ | `confluence-fetch` | Pull a Confluence page into a local markdown mirror | Complete |
40
+
41
+ > **Note:** `confluence-publish` provides lightweight ad-hoc publishing to Confluence. For bulk structured sync of `_docs/` folders following epic hierarchy, use `atlassian-orchestrator` Mode 3 instead.
21
42
 
22
43
  ## Prerequisites
23
44
 
@@ -38,7 +59,16 @@ npx @groupby/ai-dev install toolset rzlv-flow # All skills + resources
38
59
  npx @groupby/ai-dev install skill jira-daily-triage # Individual skill
39
60
  ```
40
61
 
41
- > **Note:** CLI toolset support is coming soon. For now, skills can be copied manually from this repository into your project's `docs/ai/skills/` directory, and resources into `docs/ai/resources/`.
62
+ Common options:
63
+
64
+ | Flag | Description |
65
+ |------|-------------|
66
+ | `--target <dir>` | Install into a specific directory (defaults to cwd) |
67
+ | `--dry-run` | Preview what would be installed without writing files |
68
+ | `--force` | Overwrite existing files without prompting |
69
+ | `--skip-existing` | Skip files that already exist |
70
+
71
+ You can also copy skills manually from this repository into `docs/ai/skills/` and resources into `docs/ai/resources/`.
42
72
 
43
73
  ## BMAD Compatibility
44
74
 
@@ -55,3 +85,18 @@ Skills reference shared resource files installed to `docs/ai/resources/` in your
55
85
  | `confluence-file-structure.md` | Leaf Bundle pattern for Confluence page mirrors |
56
86
  | `sync-state-format.md` | YAML frontmatter schema for ticket and page files |
57
87
  | `confluence-page-templates/` | Starter scaffolds for Confluence pages (initiative overview, strategic context, technical architecture, decisions) |
88
+
89
+ ## MCP Setup Automation
90
+
91
+ MCP server configuration is currently manual — see [docs/mcp-setup.md](docs/mcp-setup.md) for step-by-step instructions (~5 minutes). Automated MCP configuration via the CLI is a future enhancement.
92
+
93
+ ## Documentation
94
+
95
+ | Doc | Description |
96
+ |-----|-------------|
97
+ | [docs/getting-started.md](docs/getting-started.md) | Quick walkthrough: install → configure → use |
98
+ | [docs/mcp-setup.md](docs/mcp-setup.md) | Atlassian and GitHub MCP server configuration |
99
+
100
+ ## Contributing
101
+
102
+ See the [architecture documentation](../../docs/architecture/README.md) for repo structure, skill authoring guides, and conventions.
@@ -1,6 +1,6 @@
1
1
  # Confluence File Structure — Local Page Mirrors
2
2
 
3
- **Pattern:** Dual-mode local mirrors for Confluence pages
3
+ **Pattern:** Leaf Bundle local mirrors for Confluence pages
4
4
 
5
5
  ---
6
6
 
@@ -15,6 +15,30 @@ Both modes follow the FCMP protocol for safe sync operations and preserve manual
15
15
 
16
16
  ---
17
17
 
18
+ ## Leaf Bundle Pattern
19
+
20
+ Every Confluence page is stored as a **directory** containing an `index.md` file — never as a standalone `.md` file. This is called the Leaf Bundle pattern.
21
+
22
+ ### Why Folders, Not Files?
23
+
24
+ - **Child pages** — A Confluence page can have child pages. The folder naturally represents this hierarchy; child pages become subdirectories.
25
+ - **Assets** — Pages may include attachments, images, or diagrams. These are stored alongside `index.md` in the same folder.
26
+ - **Hierarchy expansion** — New child pages can be added without restructuring. A flat file cannot grow into a parent.
27
+
28
+ ### DO NOT / DO
29
+
30
+ ```
31
+ ❌ DO NOT create flat files:
32
+ docs/confluence/rezolve/ENG/strategic-context.md
33
+
34
+ ✅ DO create Leaf Bundle directories:
35
+ docs/confluence/rezolve/ENG/strategic-context/index.md
36
+ ```
37
+
38
+ This rule applies to **all** Confluence page mirrors, in both structured and flexible modes.
39
+
40
+ ---
41
+
18
42
  ## Base Path
19
43
 
20
44
  ```
@@ -37,27 +61,34 @@ docs/confluence/{instance}/{space}/
37
61
  └── {initiative-name}/
38
62
  ├── index.md # Initiative Overview
39
63
  └── {epic-name}/
40
- ├── overview.md
41
- ├── technical-architecture.md
42
- ├── decisions.md
43
- ├── strategic-context.md
44
- ├── research-and-findings.md # Created only if relevant content exists
45
- ├── assumptions.md # Created only if relevant content exists
46
- └── implementation-guidance.md
64
+ ├── overview/
65
+ │ └── index.md
66
+ ├── technical-architecture/
67
+ │ └── index.md
68
+ ├── decisions/
69
+ │ └── index.md
70
+ ├── strategic-context/
71
+ │ └── index.md
72
+ ├── research-and-findings/ # Created only if relevant content exists
73
+ │ └── index.md
74
+ ├── assumptions/ # Created only if relevant content exists
75
+ │ └── index.md
76
+ └── implementation-guidance/
77
+ └── index.md
47
78
  ```
48
79
 
49
80
  ### Page Types
50
81
 
51
- | Page Type | Source | Description |
52
- |-----------|--------|-------------|
53
- | Initiative Overview (`index.md` at initiative root) | Initiative metadata | Top-level overview: goals, epic breakdown, navigation |
54
- | `overview.md` | Epic `_docs/` | High-level epic overview and goals |
55
- | `technical-architecture.md` | Epic `_docs/` | Architecture decisions and technical design |
56
- | `decisions.md` | Epic `_docs/` | Decision log and rationale |
57
- | `strategic-context.md` | Epic `_docs/` | Business context and strategic alignment |
58
- | `research-and-findings.md` | Epic `_docs/` | User research, discoveries, and clarifications* |
59
- | `assumptions.md` | Epic `_docs/` | Assumptions with confidence levels and validation status* |
60
- | `implementation-guidance.md` | Epic `_docs/` | High-value technical guides for development |
82
+ | Page Type | Folder | Source | Description |
83
+ |-----------|--------|--------|-------------|
84
+ | Initiative Overview | `index.md` (at initiative root) | Initiative metadata | Top-level overview: goals, epic breakdown, navigation |
85
+ | Overview | `overview/index.md` | Epic `_docs/` | High-level epic overview and goals |
86
+ | Technical Architecture | `technical-architecture/index.md` | Epic `_docs/` | Architecture decisions and technical design |
87
+ | Decisions | `decisions/index.md` | Epic `_docs/` | Decision log and rationale |
88
+ | Strategic Context | `strategic-context/index.md` | Epic `_docs/` | Business context and strategic alignment |
89
+ | Research & Findings | `research-and-findings/index.md` | Epic `_docs/` | User research, discoveries, and clarifications* |
90
+ | Assumptions | `assumptions/index.md` | Epic `_docs/` | Assumptions with confidence levels and validation status* |
91
+ | Implementation Guidance | `implementation-guidance/index.md` | Epic `_docs/` | High-value technical guides for development |
61
92
 
62
93
  \* Created only if the source material includes relevant content.
63
94
 
@@ -84,10 +115,11 @@ Used for ad-hoc documentation, user-specified locations, and non-epic-based cont
84
115
  ```
85
116
  docs/confluence/{instance}/{space}/
86
117
  └── {user-specified-path}/
87
- └── {page-name}.md
118
+ └── {page-name}/
119
+ └── index.md
88
120
  ```
89
121
 
90
- No predefined structure — adapts to the user's needs.
122
+ No predefined structure beyond the Leaf Bundle requirement — adapts to the user's needs.
91
123
 
92
124
  ### When to Use
93
125
 
@@ -141,15 +173,28 @@ Skills using flexible mode should ask:
141
173
 
142
174
  ---
143
175
 
176
+ ## Confluence Link Format
177
+
178
+ When constructing Confluence URLs:
179
+
180
+ - Replace spaces in page titles with `+`
181
+ - Use the `/wiki/display/{SPACE}/{Title}` format for display links
182
+ - Use the `/wiki/spaces/{SPACE}/pages/{pageId}` format for direct links
183
+
184
+ Example: `https://company.atlassian.net/wiki/display/ENG/Technical+Architecture`
185
+
186
+ ---
187
+
144
188
  ## Page File Format
145
189
 
146
- Each local Confluence mirror file uses YAML frontmatter for sync metadata:
190
+ Each local Confluence mirror file (`index.md` inside a Leaf Bundle directory) uses YAML frontmatter for sync metadata:
147
191
 
148
192
  ```yaml
149
193
  ---
150
194
  sync:
151
195
  confluence_page_id: "123456789"
152
196
  confluence_url: "https://company.atlassian.net/wiki/spaces/ENG/pages/123456789"
197
+ confluence_title: "Technical Architecture"
153
198
  space: "ENG"
154
199
  parent_page_id: "987654321"
155
200
  last_synced: "2025-12-11T10:30:00Z"
@@ -163,6 +208,9 @@ source:
163
208
  jira_ticket: "PROJ-120"
164
209
  doc_type: "technical-architecture"
165
210
  generated_from: "docs/jira/rezolve/PROJ/user-authentication/_docs/technical-architecture.md"
211
+ bmad_docs_referenced:
212
+ - "docs/jira/rezolve/PROJ/PROJ-120-user-auth/_docs/technical-architecture.md"
213
+ jira_initiative_key: "PROJ-100"
166
214
 
167
215
  # For flexible mode
168
216
  # origin: "manual"
@@ -174,6 +222,64 @@ source:
174
222
  Page content in markdown, converted to Confluence storage format during sync.
175
223
  ```
176
224
 
177
- See `sync-state-format.md` for full frontmatter schema details.
225
+ ### Frontmatter Field Reference
226
+
227
+ | Field | Type | Required | Description |
228
+ |-------|------|----------|-------------|
229
+ | `sync.confluence_page_id` | string | After first sync | Confluence page ID |
230
+ | `sync.confluence_url` | string | After first sync | Full page URL |
231
+ | `sync.confluence_title` | string | Yes | Exact title as it appears in Confluence (used for page lookups) |
232
+ | `sync.space` | string | Yes | Confluence space key |
233
+ | `sync.parent_page_id` | string | Yes | Parent page ID in Confluence |
234
+ | `sync.last_synced` | ISO8601 | After first sync | Last sync timestamp |
235
+ | `sync.version` | number | After first sync | Confluence page version for optimistic locking |
236
+ | `sync.remote_updated` | ISO8601 | After first sync | Last remote change timestamp |
237
+ | `mode` | enum | Yes | `structured` or `flexible` |
238
+ | `source.jira_ticket` | string | Structured | Associated Jira ticket key |
239
+ | `source.doc_type` | string | Structured | Page type (overview, technical-architecture, decisions, strategic-context) |
240
+ | `source.generated_from` | string | Structured | Path to the `_docs/` source file |
241
+ | `source.bmad_docs_referenced` | string[] | No | BMAD source docs used to generate the page (traceability) |
242
+ | `source.jira_initiative_key` | string | No | Related Jira initiative key (cross-linking) |
243
+ | `source.origin` | string | Flexible | Origin skill or `manual` |
244
+ | `source.linked_jira` | string | Flexible | Optionally linked Jira ticket |
245
+
246
+ > **Note on field naming:** The source rzlv-flow repo uses flat root-level fields (e.g. `confluence_space_key: "ENG"`). This toolset uses nested fields under `sync.*` for consistency with the Jira file schema. Both conventions represent the same data; skills should follow the nested format documented here.
247
+
248
+ ### Post-Sync Frontmatter Example
249
+
250
+ **Before first sync (draft state):**
251
+ ```yaml
252
+ ---
253
+ sync:
254
+ confluence_title: "Technical Architecture"
255
+ space: "ENG"
256
+ parent_page_id: "987654321"
257
+ mode: "structured"
258
+ source:
259
+ jira_ticket: "PROJ-120"
260
+ doc_type: "technical-architecture"
261
+ generated_from: "docs/jira/rezolve/PROJ/PROJ-120-user-auth/_docs/technical-architecture.md"
262
+ ---
263
+ ```
264
+
265
+ **After first sync (populated by sync operation):**
266
+ ```yaml
267
+ ---
268
+ sync:
269
+ confluence_page_id: "123456789"
270
+ confluence_url: "https://company.atlassian.net/wiki/spaces/ENG/pages/123456789"
271
+ confluence_title: "Technical Architecture"
272
+ space: "ENG"
273
+ parent_page_id: "987654321"
274
+ last_synced: "2025-12-11T10:30:00Z"
275
+ version: 1
276
+ remote_updated: "2025-12-11T10:30:00Z"
277
+ mode: "structured"
278
+ source:
279
+ jira_ticket: "PROJ-120"
280
+ doc_type: "technical-architecture"
281
+ generated_from: "docs/jira/rezolve/PROJ/PROJ-120-user-auth/_docs/technical-architecture.md"
282
+ ---
283
+ ```
178
284
 
179
- <!-- TODO: Pull additional detail from full rzlv-flow repo for Leaf Bundle pattern specifics and doc-type-mapping rules if available -->
285
+ See `sync-state-format.md` for the full frontmatter schema and worked examples.