@groupby/ai-dev 0.2.1 → 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.
- package/README.md +3 -1
- package/dist/index.js +86 -19
- package/package.json +2 -2
- package/toolsets/rzlv-flow/README.md +54 -9
- package/toolsets/rzlv-flow/resources/confluence-file-structure.md +129 -23
- package/toolsets/rzlv-flow/resources/sync-state-format.md +109 -0
- package/toolsets/rzlv-flow/skills/jira-daily-triage/SKILL.md +13 -5
- package/toolsets/rzlv-flow/skills/jira-sprint-status/SKILL.md +28 -1
- package/toolsets/rzlv-flow/skills/jira-ticket-focus/SKILL.md +6 -1
- package/toolsets/rzlv-flow/skills/jira-wrap-sync/SKILL.md +41 -8
- package/skills/skills/README.md +0 -61
- package/skills/skills/archived/README.md +0 -3
- package/skills/skills/library/README.md +0 -3
- package/skills/skills/library/frontend-design/LICENSE.txt +0 -177
- package/skills/skills/library/frontend-design/SKILL.md +0 -42
- package/teams/teams/brain-studio/skills/code-review/SKILL.md +0 -46
- package/toolsets/toolsets/rzlv-flow/README.md +0 -102
- package/toolsets/toolsets/rzlv-flow/docs/mcp-setup.md +0 -126
- package/toolsets/toolsets/rzlv-flow/resources/README.md +0 -16
- package/toolsets/toolsets/rzlv-flow/resources/confluence-file-structure.md +0 -285
- package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/README.md +0 -19
- package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/decisions.md +0 -36
- package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/initiative-overview.md +0 -40
- package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/strategic-context.md +0 -44
- package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/technical-architecture.md +0 -48
- package/toolsets/toolsets/rzlv-flow/resources/fcmp-protocol.md +0 -331
- package/toolsets/toolsets/rzlv-flow/resources/jira-file-structure.md +0 -177
- package/toolsets/toolsets/rzlv-flow/resources/sync-state-format.md +0 -318
- package/toolsets/toolsets/rzlv-flow/skills/atlassian-orchestrator/SKILL.md +0 -643
- package/toolsets/toolsets/rzlv-flow/skills/context-analyst/SKILL.md +0 -265
- package/toolsets/toolsets/rzlv-flow/skills/jira-comment/SKILL.md +0 -89
- package/toolsets/toolsets/rzlv-flow/skills/jira-daily-triage/SKILL.md +0 -143
- package/toolsets/toolsets/rzlv-flow/skills/jira-sprint-status/SKILL.md +0 -143
- package/toolsets/toolsets/rzlv-flow/skills/jira-status/SKILL.md +0 -97
- package/toolsets/toolsets/rzlv-flow/skills/jira-sync/SKILL.md +0 -148
- package/toolsets/toolsets/rzlv-flow/skills/jira-ticket-focus/SKILL.md +0 -245
- package/toolsets/toolsets/rzlv-flow/skills/jira-ticket-trace/SKILL.md +0 -112
- package/toolsets/toolsets/rzlv-flow/skills/jira-wrap-sync/SKILL.md +0 -260
- /package/toolsets/{toolsets/rzlv-flow → rzlv-flow}/docs/getting-started.md +0 -0
- /package/toolsets/{toolsets/rzlv-flow → rzlv-flow}/skills/confluence-fetch/SKILL.md +0 -0
- /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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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",
|
|
@@ -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 |
|
|
12
|
-
| `jira-ticket-focus` | Deep-load a ticket with full context |
|
|
13
|
-
| `jira-wrap-sync` | Wrap up work, draft comment, multi-action sync |
|
|
14
|
-
| `jira-ticket-trace` | Trace code back to Jira requirements |
|
|
15
|
-
| `jira-comment` | Add a comment to a Jira ticket with FCMP safety |
|
|
16
|
-
| `jira-status` | Change ticket status with transition validation |
|
|
17
|
-
| `jira-sprint-status` | Sprint status report with completion metrics |
|
|
18
|
-
| `jira-sync` | Force-sync local Jira context with remote state |
|
|
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
|
-
|
|
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:**
|
|
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
|
|
41
|
-
|
|
42
|
-
├──
|
|
43
|
-
|
|
44
|
-
├──
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
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}
|
|
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
|
-
|
|
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
|
-
|
|
285
|
+
See `sync-state-format.md` for the full frontmatter schema and worked examples.
|