@groupby/ai-dev 0.2.1 → 0.3.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 (45) hide show
  1. package/README.md +3 -1
  2. package/dist/index.js +86 -19
  3. package/package.json +4 -4
  4. package/skills/library/implementation-plan/README.md +19 -0
  5. package/skills/library/implementation-plan/SKILL.md +337 -0
  6. package/skills/library/jira-plan/README.md +17 -0
  7. package/skills/library/jira-plan/SKILL.md +163 -0
  8. package/toolsets/rzlv-flow/README.md +54 -9
  9. package/toolsets/rzlv-flow/resources/confluence-file-structure.md +129 -23
  10. package/toolsets/rzlv-flow/resources/sync-state-format.md +109 -0
  11. package/toolsets/rzlv-flow/skills/jira-daily-triage/SKILL.md +13 -5
  12. package/toolsets/rzlv-flow/skills/jira-sprint-status/SKILL.md +28 -1
  13. package/toolsets/rzlv-flow/skills/jira-ticket-focus/SKILL.md +6 -1
  14. package/toolsets/rzlv-flow/skills/jira-wrap-sync/SKILL.md +41 -8
  15. package/skills/skills/README.md +0 -61
  16. package/skills/skills/archived/README.md +0 -3
  17. package/skills/skills/library/README.md +0 -3
  18. package/skills/skills/library/frontend-design/LICENSE.txt +0 -177
  19. package/skills/skills/library/frontend-design/SKILL.md +0 -42
  20. package/teams/teams/brain-studio/skills/code-review/SKILL.md +0 -46
  21. package/toolsets/toolsets/rzlv-flow/README.md +0 -102
  22. package/toolsets/toolsets/rzlv-flow/docs/mcp-setup.md +0 -126
  23. package/toolsets/toolsets/rzlv-flow/resources/README.md +0 -16
  24. package/toolsets/toolsets/rzlv-flow/resources/confluence-file-structure.md +0 -285
  25. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/README.md +0 -19
  26. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/decisions.md +0 -36
  27. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/initiative-overview.md +0 -40
  28. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/strategic-context.md +0 -44
  29. package/toolsets/toolsets/rzlv-flow/resources/confluence-page-templates/technical-architecture.md +0 -48
  30. package/toolsets/toolsets/rzlv-flow/resources/fcmp-protocol.md +0 -331
  31. package/toolsets/toolsets/rzlv-flow/resources/jira-file-structure.md +0 -177
  32. package/toolsets/toolsets/rzlv-flow/resources/sync-state-format.md +0 -318
  33. package/toolsets/toolsets/rzlv-flow/skills/atlassian-orchestrator/SKILL.md +0 -643
  34. package/toolsets/toolsets/rzlv-flow/skills/context-analyst/SKILL.md +0 -265
  35. package/toolsets/toolsets/rzlv-flow/skills/jira-comment/SKILL.md +0 -89
  36. package/toolsets/toolsets/rzlv-flow/skills/jira-daily-triage/SKILL.md +0 -143
  37. package/toolsets/toolsets/rzlv-flow/skills/jira-sprint-status/SKILL.md +0 -143
  38. package/toolsets/toolsets/rzlv-flow/skills/jira-status/SKILL.md +0 -97
  39. package/toolsets/toolsets/rzlv-flow/skills/jira-sync/SKILL.md +0 -148
  40. package/toolsets/toolsets/rzlv-flow/skills/jira-ticket-focus/SKILL.md +0 -245
  41. package/toolsets/toolsets/rzlv-flow/skills/jira-ticket-trace/SKILL.md +0 -112
  42. package/toolsets/toolsets/rzlv-flow/skills/jira-wrap-sync/SKILL.md +0 -260
  43. /package/toolsets/{toolsets/rzlv-flow → rzlv-flow}/docs/getting-started.md +0 -0
  44. /package/toolsets/{toolsets/rzlv-flow → rzlv-flow}/skills/confluence-fetch/SKILL.md +0 -0
  45. /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.1",
3
+ "version": "0.3.0",
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",
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "git+https://github.com/rezolved/ai-dev-shared.git",
25
+ "url": "git+https://github.com/groupby/ai-dev-shared.git",
26
26
  "directory": "cli"
27
27
  },
28
28
  "engines": {
29
- "node": ">=18"
29
+ "node": ">=20"
30
30
  },
31
31
  "publishConfig": {
32
32
  "access": "public"
@@ -0,0 +1,19 @@
1
+ # implementation-plan
2
+
3
+ Generate structured implementation plans from any written input — documents, notes, specs, or descriptions.
4
+
5
+ Produces phased checklists with architecture justification, a clarifications file for surfacing and resolving ambiguities before planning, and self-contained sub-plan files for larger work.
6
+
7
+ ## Output
8
+
9
+ Plans are written to `docs/planning/<story-name>/` (folder) or `docs/planning/<story-name>.md` (single file), depending on scope. A `clarifications.md` file is always generated alongside the plan.
10
+
11
+ ## Example prompts
12
+
13
+ - `/implementation-plan` with a pasted feature description
14
+ - `/implementation-plan` pointing to `docs/planning/tasks/configurable-install-path.md`
15
+ - "Plan the implementation for adding OAuth support"
16
+
17
+ ## Related
18
+
19
+ - [`jira-plan`](../jira-plan/) — wrapper that fetches Jira ticket context and delegates to this skill for plan generation