@codemcp/ade 0.2.6 → 0.4.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 (35) hide show
  1. package/.agentskills/skills/conventional-commits/SKILL.md +36 -0
  2. package/.beads/issues.jsonl +10 -0
  3. package/.beads/last-touched +1 -1
  4. package/.kiro/agents/ade.json +9 -2
  5. package/.opencode/agents/ade.md +9 -18
  6. package/.vibe/beads-state-ade-better-in-cli-explanation-b8jcv3.json +24 -0
  7. package/.vibe/beads-state-ade-fix-no-git-k396xs.json +34 -0
  8. package/.vibe/development-plan-better-in-cli-explanation.md +64 -0
  9. package/.vibe/development-plan-fix-no-git.md +76 -0
  10. package/AGENTS.md +27 -0
  11. package/config.lock.yaml +33 -9
  12. package/config.yaml +3 -0
  13. package/package.json +1 -1
  14. package/packages/cli/dist/index.js +190 -50
  15. package/packages/cli/package.json +1 -1
  16. package/packages/cli/src/commands/conventions.integration.spec.ts +8 -1
  17. package/packages/cli/src/commands/install.integration.spec.ts +1 -0
  18. package/packages/cli/src/commands/install.ts +19 -1
  19. package/packages/cli/src/commands/knowledge.integration.spec.ts +1 -0
  20. package/packages/cli/src/commands/setup.integration.spec.ts +2 -0
  21. package/packages/cli/src/commands/setup.spec.ts +2 -1
  22. package/packages/cli/src/commands/setup.ts +61 -6
  23. package/packages/cli/src/index.ts +8 -1
  24. package/packages/core/package.json +1 -1
  25. package/packages/core/src/catalog/catalog.spec.ts +1 -1
  26. package/packages/core/src/catalog/facets/autonomy.ts +9 -9
  27. package/packages/core/src/catalog/facets/backpressure.ts +1 -1
  28. package/packages/core/src/catalog/facets/practices.ts +1 -2
  29. package/packages/core/src/catalog/facets/process.ts +1 -1
  30. package/packages/harnesses/package.json +2 -1
  31. package/packages/harnesses/src/util.spec.ts +97 -0
  32. package/packages/harnesses/src/util.ts +21 -4
  33. package/packages/harnesses/src/writers/opencode.spec.ts +4 -6
  34. package/packages/harnesses/src/writers/opencode.ts +23 -27
  35. package/skills-lock.json +6 -1
@@ -11222,6 +11222,23 @@ var B = class {
11222
11222
  }
11223
11223
  }
11224
11224
  };
11225
+ var kt = class extends B {
11226
+ get cursor() {
11227
+ return this.value ? 0 : 1;
11228
+ }
11229
+ get _value() {
11230
+ return this.cursor === 0;
11231
+ }
11232
+ constructor(e) {
11233
+ super(e, false), this.value = !!e.initialValue, this.on("userInput", () => {
11234
+ this.value = this._value;
11235
+ }), this.on("confirm", (s) => {
11236
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
11237
+ }), this.on("cursor", () => {
11238
+ this.value = !this.value;
11239
+ });
11240
+ }
11241
+ };
11225
11242
  var Lt = class extends B {
11226
11243
  options;
11227
11244
  cursor = 0;
@@ -11556,6 +11573,33 @@ var X2 = ({ cursor: e, options: r2, style: s, output: i = process.stdout, maxIte
11556
11573
  for (const A3 of d3) for (const b of A3) C2.push(b);
11557
11574
  return $2 && C2.push(c), C2;
11558
11575
  };
11576
+ var Rt = (e) => {
11577
+ const r2 = e.active ?? "Yes", s = e.inactive ?? "No";
11578
+ return new kt({ active: r2, inactive: s, signal: e.signal, input: e.input, output: e.output, initialValue: e.initialValue ?? true, render() {
11579
+ const i = e.withGuide ?? _.withGuide, a = `${i ? `${t("gray", h)}
11580
+ ` : ""}${W2(this.state)} ${e.message}
11581
+ `, o2 = this.value ? r2 : s;
11582
+ switch (this.state) {
11583
+ case "submit": {
11584
+ const u2 = i ? `${t("gray", h)} ` : "";
11585
+ return `${a}${u2}${t("dim", o2)}`;
11586
+ }
11587
+ case "cancel": {
11588
+ const u2 = i ? `${t("gray", h)} ` : "";
11589
+ return `${a}${u2}${t(["strikethrough", "dim"], o2)}${i ? `
11590
+ ${t("gray", h)}` : ""}`;
11591
+ }
11592
+ default: {
11593
+ const u2 = i ? `${t("cyan", h)} ` : "", l = i ? t("cyan", x2) : "";
11594
+ return `${a}${u2}${this.value ? `${t("green", z2)} ${r2}` : `${t("dim", H2)} ${t("dim", r2)}`}${e.vertical ? i ? `
11595
+ ${t("cyan", h)} ` : `
11596
+ ` : ` ${t("dim", "/")} `}${this.value ? `${t("dim", H2)} ${t("dim", s)}` : `${t("green", z2)} ${s}`}
11597
+ ${l}
11598
+ `;
11599
+ }
11600
+ }
11601
+ } }).prompt();
11602
+ };
11559
11603
  var R2 = { message: (e = [], { symbol: r2 = t("gray", h), secondarySymbol: s = t("gray", h), output: i = process.stdout, spacing: a = 1, withGuide: o2 } = {}) => {
11560
11604
  const u2 = [], l = o2 ?? _.withGuide, n = l ? s : "", c = l ? `${r2} ` : "", p2 = l ? `${s} ` : "";
11561
11605
  for (let g2 = 0; g2 < a; g2++) u2.push(n);
@@ -11653,6 +11697,25 @@ ${t("cyan", x2)}
11653
11697
  }
11654
11698
  } }).prompt();
11655
11699
  };
11700
+ var jt = (e) => t("dim", e);
11701
+ var kt2 = (e, r2, s) => {
11702
+ const i = { hard: true, trim: false }, a = J2(e, r2, i).split(`
11703
+ `), o2 = a.reduce((n, c) => Math.max(D2(c), n), 0), u2 = a.map(s).reduce((n, c) => Math.max(D2(c), n), 0), l = r2 - (u2 - o2);
11704
+ return J2(e, l, i);
11705
+ };
11706
+ var Vt2 = (e = "", r2 = "", s) => {
11707
+ const i = s?.output ?? N2.stdout, a = s?.withGuide ?? _.withGuide, o2 = s?.format ?? jt, u2 = ["", ...kt2(e, rt(i) - 6, o2).split(`
11708
+ `).map(o2), ""], l = D2(r2), n = Math.max(u2.reduce((g2, E) => {
11709
+ const $2 = D2(E);
11710
+ return $2 > g2 ? $2 : g2;
11711
+ }, 0), l) + 2, c = u2.map((g2) => `${t("gray", h)} ${g2}${" ".repeat(n - D2(g2))}${t("gray", h)}`).join(`
11712
+ `), p2 = a ? `${t("gray", h)}
11713
+ ` : "", f = a ? We : ge;
11714
+ i.write(`${p2}${t("green", V)} ${t("reset", r2)} ${t("gray", se.repeat(Math.max(n - l - 1, 1)) + pe)}
11715
+ ${c}
11716
+ ${t("gray", f + se.repeat(n + 2) + me)}
11717
+ `);
11718
+ };
11656
11719
  var ze = { light: I2("\u2500", "-"), heavy: I2("\u2501", "="), block: I2("\u2588", "#") };
11657
11720
  var oe = (e, r2) => e.includes(`
11658
11721
  `) ? e.split(`
@@ -11837,7 +11900,7 @@ function createDefaultRegistry() {
11837
11900
  var processFacet = {
11838
11901
  id: "process",
11839
11902
  label: "Process",
11840
- description: "How your AI agent receives and executes tasks",
11903
+ description: "How will you guide your agent",
11841
11904
  required: true,
11842
11905
  options: [
11843
11906
  {
@@ -12312,7 +12375,7 @@ var architectureFacet = {
12312
12375
  var practicesFacet = {
12313
12376
  id: "practices",
12314
12377
  label: "Practices",
12315
- description: "Composable development practices \u2014 mix and match regardless of stack",
12378
+ description: "Development practices \u2014 mix and match regardless of stack",
12316
12379
  required: false,
12317
12380
  multiSelect: true,
12318
12381
  options: [
@@ -12544,7 +12607,7 @@ var JAVA_UNIT_TEST_RECIPE = [
12544
12607
  var backpressureFacet = {
12545
12608
  id: "backpressure",
12546
12609
  label: "Backpressure",
12547
- description: "Install git hooks that enforce quality gates \u2014 silent on success, surface only relevant failures",
12610
+ description: "Which automated quality gates to put in place. Translates to git hooks",
12548
12611
  required: false,
12549
12612
  multiSelect: true,
12550
12613
  dependsOn: ["architecture"],
@@ -12603,31 +12666,31 @@ var autonomyFacet = {
12603
12666
  multiSelect: false,
12604
12667
  options: [
12605
12668
  {
12606
- id: "rigid",
12607
- label: "Rigid",
12608
- description: "Keep built-in capabilities approval-gated and require confirmation before acting",
12669
+ id: "sensible-defaults",
12670
+ label: "Sensible defaults",
12671
+ description: "Allow a curated built-in capabilities set while keeping potentially risky actions approval-gated",
12609
12672
  recipe: [
12610
12673
  {
12611
12674
  writer: "permission-policy",
12612
- config: { profile: "rigid" }
12675
+ config: { profile: "sensible-defaults" }
12613
12676
  }
12614
12677
  ]
12615
12678
  },
12616
12679
  {
12617
- id: "sensible-defaults",
12618
- label: "Sensible defaults",
12619
- description: "Allow a curated low-risk built-in capability set while keeping web access approval-gated",
12680
+ id: "rigid",
12681
+ label: "Rigid",
12682
+ description: "Keep built-in capabilities approval-gated and require confirmation before acting",
12620
12683
  recipe: [
12621
12684
  {
12622
12685
  writer: "permission-policy",
12623
- config: { profile: "sensible-defaults" }
12686
+ config: { profile: "rigid" }
12624
12687
  }
12625
12688
  ]
12626
12689
  },
12627
12690
  {
12628
12691
  id: "max-autonomy",
12629
12692
  label: "Max autonomy",
12630
- description: "Allow broad local built-in autonomy while keeping web access approval-gated",
12693
+ description: "Allow broad local built-in autonomy. Recommended for sandboxed environments only",
12631
12694
  recipe: [
12632
12695
  {
12633
12696
  writer: "permission-policy",
@@ -21824,7 +21887,7 @@ async function installSkills(skills, projectRoot) {
21824
21887
  }
21825
21888
 
21826
21889
  // ../harnesses/dist/util.js
21827
- import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
21890
+ import { access as access2, mkdir as mkdir3, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
21828
21891
  import { dirname as dirname5, join as join9 } from "path";
21829
21892
  async function readJsonOrEmpty(path2) {
21830
21893
  try {
@@ -21891,10 +21954,19 @@ async function writeAgentMd(config, opts) {
21891
21954
  await writeFile4(opts.path, content, "utf-8");
21892
21955
  }
21893
21956
  async function writeGitHooks(hooks, projectRoot) {
21894
- if (!hooks)
21957
+ if (!hooks || hooks.length === 0)
21958
+ return;
21959
+ const gitDir = join9(projectRoot, ".git");
21960
+ try {
21961
+ await access2(gitDir);
21962
+ } catch {
21963
+ R2.warn("Git hooks were configured but could not be installed: the project is not a git repository.\nRun `git init` and re-run setup to install the hooks.");
21895
21964
  return;
21965
+ }
21966
+ const hooksDir = join9(gitDir, "hooks");
21967
+ await mkdir3(hooksDir, { recursive: true });
21896
21968
  for (const hook of hooks) {
21897
- const hookPath = join9(projectRoot, ".git", "hooks", hook.phase);
21969
+ const hookPath = join9(hooksDir, hook.phase);
21898
21970
  await writeFile4(hookPath, hook.script, { mode: 493 });
21899
21971
  }
21900
21972
  }
@@ -22434,7 +22506,27 @@ function getKiroAgentMcpServers(servers) {
22434
22506
 
22435
22507
  // ../harnesses/dist/writers/opencode.js
22436
22508
  import { join as join18 } from "path";
22509
+ var APPLICABLE_TO_ALL = {
22510
+ read: {
22511
+ "*": "allow",
22512
+ "*.env": "deny",
22513
+ "*.env.*": "deny",
22514
+ "*.env.example": "allow"
22515
+ },
22516
+ skill: "deny",
22517
+ //we're using an own skills-mcp
22518
+ todoread: "deny",
22519
+ //no agent-proprieatry todo tools
22520
+ todowrite: "deny",
22521
+ task: "deny",
22522
+ lsp: "allow",
22523
+ glob: "allow",
22524
+ grep: "allow",
22525
+ list: "allow",
22526
+ external_directory: "ask"
22527
+ };
22437
22528
  var RIGID_RULES = {
22529
+ ...APPLICABLE_TO_ALL,
22438
22530
  "*": "ask",
22439
22531
  webfetch: "ask",
22440
22532
  websearch: "ask",
@@ -22443,31 +22535,17 @@ var RIGID_RULES = {
22443
22535
  doom_loop: "deny"
22444
22536
  };
22445
22537
  var SENSIBLE_DEFAULTS_RULES = {
22446
- read: {
22447
- "*": "allow",
22448
- "*.env": "deny",
22449
- "*.env.*": "deny",
22450
- "*.env.example": "allow"
22451
- },
22538
+ ...APPLICABLE_TO_ALL,
22452
22539
  edit: "allow",
22453
- glob: "allow",
22454
- grep: "allow",
22455
- list: "allow",
22456
- lsp: "allow",
22457
- task: "allow",
22458
- todoread: "deny",
22459
- todowrite: "deny",
22460
- skill: "deny",
22461
22540
  webfetch: "ask",
22462
22541
  websearch: "ask",
22463
22542
  codesearch: "ask",
22464
22543
  bash: {
22465
- "*": "deny",
22544
+ "*": "ask",
22466
22545
  "grep *": "allow",
22467
22546
  "rg *": "allow",
22468
22547
  "find *": "allow",
22469
22548
  "fd *": "allow",
22470
- ls: "allow",
22471
22549
  "ls *": "allow",
22472
22550
  "cat *": "allow",
22473
22551
  "head *": "allow",
@@ -22502,14 +22580,7 @@ var SENSIBLE_DEFAULTS_RULES = {
22502
22580
  "yq *": "allow",
22503
22581
  "mkdir *": "allow",
22504
22582
  "touch *": "allow",
22505
- "cp *": "ask",
22506
- "mv *": "ask",
22507
- "ln *": "ask",
22508
- "npm *": "ask",
22509
- "node *": "ask",
22510
- "pip *": "ask",
22511
- "python *": "ask",
22512
- "python3 *": "ask",
22583
+ "kill *": "ask",
22513
22584
  "rm *": "deny",
22514
22585
  "rmdir *": "deny",
22515
22586
  "curl *": "deny",
@@ -22530,7 +22601,6 @@ var SENSIBLE_DEFAULTS_RULES = {
22530
22601
  "mkfs *": "deny",
22531
22602
  "mount *": "deny",
22532
22603
  "umount *": "deny",
22533
- "kill *": "deny",
22534
22604
  "killall *": "deny",
22535
22605
  "pkill *": "deny",
22536
22606
  "nc *": "deny",
@@ -22550,15 +22620,14 @@ var SENSIBLE_DEFAULTS_RULES = {
22550
22620
  "userdel *": "deny",
22551
22621
  "iptables *": "deny"
22552
22622
  },
22553
- external_directory: "deny",
22554
22623
  doom_loop: "deny"
22555
22624
  };
22556
22625
  var MAX_AUTONOMY_RULES = {
22626
+ ...APPLICABLE_TO_ALL,
22557
22627
  "*": "allow",
22558
22628
  webfetch: "ask",
22559
22629
  websearch: "ask",
22560
22630
  codesearch: "ask",
22561
- external_directory: "deny",
22562
22631
  doom_loop: "deny"
22563
22632
  };
22564
22633
  function getPermissionRules(profile) {
@@ -22631,7 +22700,35 @@ function getHarnessIds() {
22631
22700
 
22632
22701
  // src/commands/setup.ts
22633
22702
  async function runSetup(projectRoot, catalog) {
22634
- Wt2("ade setup");
22703
+ let lineIndex = 0;
22704
+ const LOGO_LINES = [
22705
+ "\n",
22706
+ " \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
22707
+ "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557",
22708
+ "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D",
22709
+ "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D ",
22710
+ "\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 ",
22711
+ "\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D ",
22712
+ "\n"
22713
+ ];
22714
+ for (const line of LOGO_LINES) {
22715
+ lineIndex++;
22716
+ if (lineIndex === 1) {
22717
+ Wt2(line);
22718
+ } else {
22719
+ console.log(`\u2502 ${line}`);
22720
+ }
22721
+ }
22722
+ Vt2(
22723
+ [
22724
+ "You're about to define how your team works with coding agents.",
22725
+ "",
22726
+ "Pick your facets \u2014 architecture, practices, process \u2014 and ADE",
22727
+ "translates them into a shared information hierarchy your agents",
22728
+ "read from the repo. One setup, consistent across the whole team."
22729
+ ].join("\n"),
22730
+ "ADE \u2014 Agentic Development Environment"
22731
+ );
22635
22732
  const existingConfig = await readUserConfig(projectRoot);
22636
22733
  const existingChoices = existingConfig?.choices ?? {};
22637
22734
  for (const [facetId, value] of Object.entries(existingChoices)) {
@@ -22676,7 +22773,7 @@ async function runSetup(projectRoot, catalog) {
22676
22773
  let excludedDocsets;
22677
22774
  if (impliedDocsets.length > 0) {
22678
22775
  const selected = await Lt2({
22679
- message: "Documentation \u2014 deselect any you don't need",
22776
+ message: "Documentation sources \u2014 Those will be pulled to your local disk for browsing on demand",
22680
22777
  options: impliedDocsets.map((d3) => ({
22681
22778
  value: d3.id,
22682
22779
  label: d3.label,
@@ -22705,7 +22802,7 @@ async function runSetup(projectRoot, catalog) {
22705
22802
  (h3) => allHarnessWriters.some((w2) => w2.id === h3)
22706
22803
  );
22707
22804
  const selectedHarnesses = await Lt2({
22708
- message: "Harnesses \u2014 which coding agents should receive config?",
22805
+ message: "Which coding agents should receive config?\nADE generates config files for each agent you select.\n",
22709
22806
  options: harnessOptions,
22710
22807
  initialValues: validExistingHarnesses && validExistingHarnesses.length > 0 ? validExistingHarnesses : ["universal"],
22711
22808
  required: false
@@ -22746,7 +22843,27 @@ async function runSetup(projectRoot, catalog) {
22746
22843
  To use the latest defaults, remove .ade/skills/ and re-run setup.`
22747
22844
  );
22748
22845
  }
22749
- await installSkills(logicalConfig.skills, projectRoot);
22846
+ if (logicalConfig.skills.length > 0) {
22847
+ const skillNames = logicalConfig.skills.map((s) => ` \u2022 ${s.name}`).join("\n");
22848
+ const confirmInstall = await Rt({
22849
+ message: `Install ${logicalConfig.skills.length} skill(s) now?
22850
+ ` + skillNames + `
22851
+ You can also install them later with:
22852
+ npx @codemcp/skills experimental_install`,
22853
+ initialValue: true
22854
+ });
22855
+ if (typeof confirmInstall === "symbol") {
22856
+ Nt("Setup cancelled.");
22857
+ return;
22858
+ }
22859
+ if (confirmInstall) {
22860
+ await installSkills(logicalConfig.skills, projectRoot);
22861
+ } else {
22862
+ R2.info(
22863
+ "Skills not installed. Run manually when ready:\n npx @codemcp/skills experimental_install"
22864
+ );
22865
+ }
22866
+ }
22750
22867
  if (logicalConfig.knowledge_sources.length > 0) {
22751
22868
  R2.info(
22752
22869
  "Knowledge sources selected. Initialize them separately:\n npx @codemcp/knowledge init"
@@ -22779,7 +22896,7 @@ function promptSelect(facet, existingChoices) {
22779
22896
  }
22780
22897
  const initialValue = getValidInitialValue(facet, existingChoices);
22781
22898
  return Jt({
22782
- message: facet.label,
22899
+ message: `${facet.label} \u2014 ${facet.description}`,
22783
22900
  options: options2,
22784
22901
  ...initialValue !== void 0 && { initialValue }
22785
22902
  });
@@ -22792,7 +22909,7 @@ function promptMultiSelect(facet, existingChoices) {
22792
22909
  }));
22793
22910
  const initialValues = getValidInitialValues(facet, existingChoices);
22794
22911
  return Lt2({
22795
- message: facet.label,
22912
+ message: `${facet.label} \u2014 ${facet.description}`,
22796
22913
  options: options2,
22797
22914
  required: false,
22798
22915
  ...initialValues !== void 0 && { initialValues }
@@ -22831,7 +22948,23 @@ async function runInstall(projectRoot, harnessIds) {
22831
22948
  To use the latest defaults, remove .ade/skills/ and re-run install.`
22832
22949
  );
22833
22950
  }
22834
- await installSkills(logicalConfig.skills, projectRoot);
22951
+ if (logicalConfig.skills.length > 0) {
22952
+ const confirmInstall = await Rt({
22953
+ message: `Install ${logicalConfig.skills.length} skill(s) now?`,
22954
+ initialValue: true
22955
+ });
22956
+ if (typeof confirmInstall === "symbol") {
22957
+ Nt("Install cancelled.");
22958
+ return;
22959
+ }
22960
+ if (confirmInstall) {
22961
+ await installSkills(logicalConfig.skills, projectRoot);
22962
+ } else {
22963
+ R2.info(
22964
+ "Skills not installed. Run manually when ready:\n npx @codemcp/skills experimental_install"
22965
+ );
22966
+ }
22967
+ }
22835
22968
  if (logicalConfig.knowledge_sources.length > 0) {
22836
22969
  R2.info(
22837
22970
  "Knowledge sources configured. Initialize them separately:\n npx @codemcp/knowledge init"
@@ -22861,7 +22994,14 @@ if (command === "setup") {
22861
22994
  console.log(version);
22862
22995
  } else {
22863
22996
  const allIds = getHarnessIds();
22864
- console.log(`ade v${version}`);
22997
+ console.log(`ade v${version} \u2014 Agentic Development Environment`);
22998
+ console.log();
22999
+ console.log(
23000
+ "Define how your team works with coding agents \u2014 pick your facets,"
23001
+ );
23002
+ console.log(
23003
+ "ADE translates them into a shared information hierarchy in your repo."
23004
+ );
22865
23005
  console.log();
22866
23006
  console.log("Usage: ade <command> [options]");
22867
23007
  console.log();
@@ -39,5 +39,5 @@
39
39
  "typescript": "catalog:",
40
40
  "vitest": "catalog:"
41
41
  },
42
- "version": "0.2.6"
42
+ "version": "0.4.0"
43
43
  }
@@ -6,11 +6,18 @@ import { join } from "node:path";
6
6
  vi.mock("@clack/prompts", () => ({
7
7
  intro: vi.fn(),
8
8
  outro: vi.fn(),
9
+ note: vi.fn(),
9
10
  select: vi.fn(),
10
11
  multiselect: vi.fn(),
11
- confirm: vi.fn(),
12
+ confirm: vi.fn().mockResolvedValue(true),
12
13
  isCancel: vi.fn().mockReturnValue(false),
13
14
  cancel: vi.fn(),
15
+ log: {
16
+ info: vi.fn(),
17
+ warn: vi.fn(),
18
+ error: vi.fn(),
19
+ success: vi.fn()
20
+ },
14
21
  spinner: vi.fn().mockReturnValue({ start: vi.fn(), stop: vi.fn() })
15
22
  }));
16
23
 
@@ -7,6 +7,7 @@ import { join } from "node:path";
7
7
  vi.mock("@clack/prompts", () => ({
8
8
  intro: vi.fn(),
9
9
  outro: vi.fn(),
10
+ note: vi.fn(),
10
11
  log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
11
12
  select: vi.fn(),
12
13
  multiselect: vi.fn(),
@@ -51,7 +51,25 @@ export async function runInstall(
51
51
  );
52
52
  }
53
53
 
54
- await installSkills(logicalConfig.skills, projectRoot);
54
+ if (logicalConfig.skills.length > 0) {
55
+ const confirmInstall = await clack.confirm({
56
+ message: `Install ${logicalConfig.skills.length} skill(s) now?`,
57
+ initialValue: true
58
+ });
59
+
60
+ if (typeof confirmInstall === "symbol") {
61
+ clack.cancel("Install cancelled.");
62
+ return;
63
+ }
64
+
65
+ if (confirmInstall) {
66
+ await installSkills(logicalConfig.skills, projectRoot);
67
+ } else {
68
+ clack.log.info(
69
+ "Skills not installed. Run manually when ready:\n npx @codemcp/skills experimental_install"
70
+ );
71
+ }
72
+ }
55
73
 
56
74
  if (logicalConfig.knowledge_sources.length > 0) {
57
75
  clack.log.info(
@@ -6,6 +6,7 @@ import { join } from "node:path";
6
6
  vi.mock("@clack/prompts", () => ({
7
7
  intro: vi.fn(),
8
8
  outro: vi.fn(),
9
+ note: vi.fn(),
9
10
  select: vi.fn(),
10
11
  multiselect: vi.fn(),
11
12
  confirm: vi.fn(),
@@ -7,11 +7,13 @@ import { join } from "node:path";
7
7
  vi.mock("@clack/prompts", () => ({
8
8
  intro: vi.fn(),
9
9
  outro: vi.fn(),
10
+ note: vi.fn(),
10
11
  select: vi.fn(),
11
12
  multiselect: vi.fn(),
12
13
  confirm: vi.fn(),
13
14
  isCancel: vi.fn().mockReturnValue(false),
14
15
  cancel: vi.fn(),
16
+ log: { info: vi.fn(), warn: vi.fn() },
15
17
  spinner: vi.fn().mockReturnValue({ start: vi.fn(), stop: vi.fn() })
16
18
  }));
17
19
 
@@ -6,6 +6,7 @@ import type { Catalog, LogicalConfig } from "@codemcp/ade-core";
6
6
  vi.mock("@clack/prompts", () => ({
7
7
  intro: vi.fn(),
8
8
  outro: vi.fn(),
9
+ note: vi.fn(),
9
10
  select: vi.fn(),
10
11
  multiselect: vi.fn(),
11
12
  confirm: vi.fn(),
@@ -298,7 +299,7 @@ describe("runSetup", () => {
298
299
  expect(clack.multiselect).toHaveBeenCalledTimes(1);
299
300
  expect(clack.multiselect).toHaveBeenCalledWith(
300
301
  expect.objectContaining({
301
- message: expect.stringContaining("Harnesses")
302
+ message: expect.stringContaining("coding agents")
302
303
  })
303
304
  );
304
305
  });
@@ -26,7 +26,35 @@ export async function runSetup(
26
26
  projectRoot: string,
27
27
  catalog: Catalog
28
28
  ): Promise<void> {
29
- clack.intro("ade setup");
29
+ let lineIndex = 0;
30
+ const LOGO_LINES = [
31
+ "\n",
32
+ " █████╗ ██████╗ ███████╗ ███████╗███████╗████████╗██╗ ██╗██████╗ ",
33
+ "██╔══██╗██╔══██╗██╔════╝ ██╔════╝██╔════╝╚══██╔══╝██║ ██║██╔══██╗",
34
+ "███████║██║ ██║█████╗ ███████╗█████╗ ██║ ██║ ██║██████╔╝",
35
+ "██╔══██║██║ ██║██╔══╝ ╚════██║██╔══╝ ██║ ██║ ██║██╔═══╝ ",
36
+ "██║ ██║██████╔╝███████╗ ███████║███████╗ ██║ ╚██████╔╝██║ ",
37
+ "╚═╝ ╚═╝╚═════╝ ╚══════╝ ╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ",
38
+ "\n"
39
+ ];
40
+ for (const line of LOGO_LINES) {
41
+ lineIndex++;
42
+ if (lineIndex === 1) {
43
+ clack.intro(line);
44
+ } else {
45
+ console.log(`│ ${line}`);
46
+ }
47
+ }
48
+ clack.note(
49
+ [
50
+ "You're about to define how your team works with coding agents.",
51
+ "",
52
+ "Pick your facets — architecture, practices, process — and ADE",
53
+ "translates them into a shared information hierarchy your agents",
54
+ "read from the repo. One setup, consistent across the whole team."
55
+ ].join("\n"),
56
+ "ADE — Agentic Development Environment"
57
+ );
30
58
 
31
59
  const existingConfig = await readUserConfig(projectRoot);
32
60
  const existingChoices = existingConfig?.choices ?? {};
@@ -83,7 +111,8 @@ export async function runSetup(
83
111
 
84
112
  if (impliedDocsets.length > 0) {
85
113
  const selected = await clack.multiselect({
86
- message: "Documentation — deselect any you don't need",
114
+ message:
115
+ "Documentation sources — Those will be pulled to your local disk for browsing on demand",
87
116
  options: impliedDocsets.map((d) => ({
88
117
  value: d.id,
89
118
  label: d.label,
@@ -120,7 +149,9 @@ export async function runSetup(
120
149
  );
121
150
 
122
151
  const selectedHarnesses = await clack.multiselect({
123
- message: "Harnesses — which coding agents should receive config?",
152
+ message:
153
+ "Which coding agents should receive config?\n" +
154
+ "ADE generates config files for each agent you select.\n",
124
155
  options: harnessOptions,
125
156
  initialValues:
126
157
  validExistingHarnesses && validExistingHarnesses.length > 0
@@ -172,7 +203,31 @@ export async function runSetup(
172
203
  );
173
204
  }
174
205
 
175
- await installSkills(logicalConfig.skills, projectRoot);
206
+ if (logicalConfig.skills.length > 0) {
207
+ const skillNames = logicalConfig.skills
208
+ .map((s) => ` • ${s.name}`)
209
+ .join("\n");
210
+ const confirmInstall = await clack.confirm({
211
+ message:
212
+ `Install ${logicalConfig.skills.length} skill(s) now?\n` +
213
+ skillNames +
214
+ `\nYou can also install them later with:\n npx @codemcp/skills experimental_install`,
215
+ initialValue: true
216
+ });
217
+
218
+ if (typeof confirmInstall === "symbol") {
219
+ clack.cancel("Setup cancelled.");
220
+ return;
221
+ }
222
+
223
+ if (confirmInstall) {
224
+ await installSkills(logicalConfig.skills, projectRoot);
225
+ } else {
226
+ clack.log.info(
227
+ "Skills not installed. Run manually when ready:\n npx @codemcp/skills experimental_install"
228
+ );
229
+ }
230
+ }
176
231
 
177
232
  if (logicalConfig.knowledge_sources.length > 0) {
178
233
  clack.log.info(
@@ -225,7 +280,7 @@ function promptSelect(
225
280
  const initialValue = getValidInitialValue(facet, existingChoices);
226
281
 
227
282
  return clack.select({
228
- message: facet.label,
283
+ message: `${facet.label} — ${facet.description}`,
229
284
  options,
230
285
  ...(initialValue !== undefined && { initialValue })
231
286
  });
@@ -244,7 +299,7 @@ function promptMultiSelect(
244
299
  const initialValues = getValidInitialValues(facet, existingChoices);
245
300
 
246
301
  return clack.multiselect({
247
- message: facet.label,
302
+ message: `${facet.label} — ${facet.description}`,
248
303
  options,
249
304
  required: false,
250
305
  ...(initialValues !== undefined && { initialValues })
@@ -31,7 +31,14 @@ if (command === "setup") {
31
31
  console.log(version);
32
32
  } else {
33
33
  const allIds = getHarnessIds();
34
- console.log(`ade v${version}`);
34
+ console.log(`ade v${version} — Agentic Development Environment`);
35
+ console.log();
36
+ console.log(
37
+ "Define how your team works with coding agents — pick your facets,"
38
+ );
39
+ console.log(
40
+ "ADE translates them into a shared information hierarchy in your repo."
41
+ );
35
42
  console.log();
36
43
  console.log("Usage: ade <command> [options]");
37
44
  console.log();
@@ -38,5 +38,5 @@
38
38
  "typescript": "catalog:",
39
39
  "vitest": "catalog:"
40
40
  },
41
- "version": "0.2.6"
41
+ "version": "0.4.0"
42
42
  }
@@ -470,8 +470,8 @@ describe("catalog", () => {
470
470
  expect(autonomy!.required).toBe(false);
471
471
  expect(autonomy!.multiSelect).toBe(false);
472
472
  expect(autonomy!.options.map((option) => option.id)).toEqual([
473
- "rigid",
474
473
  "sensible-defaults",
474
+ "rigid",
475
475
  "max-autonomy"
476
476
  ]);
477
477
  });