@arvoretech/hub 0.18.1 → 0.19.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.
@@ -117,7 +117,7 @@ async function checkAndAutoRegenerate(hubDir) {
117
117
  return;
118
118
  }
119
119
  console.log(chalk.yellow("\n Detected outdated configs, auto-regenerating..."));
120
- const { generators: generators2 } = await import("./generate-HJ5FRZSI.js");
120
+ const { generators: generators2 } = await import("./generate-J6FNJF7F.js");
121
121
  const generator = generators2[result.editor];
122
122
  if (!generator) {
123
123
  console.log(chalk.red(` Unknown editor '${result.editor}' in cache. Run 'hub generate' manually.`));
@@ -446,7 +446,11 @@ var STEP_INFO = {
446
446
  role: { title: "What's your role?", subtitle: "e.g. CEO, Product Manager, Designer, Backend Dev, QA Engineer..." },
447
447
  technical_level: { title: "How technical are you?", subtitle: "This changes how the AI explains things to you." },
448
448
  context: { title: "Anything else the AI should know about you?", subtitle: `e.g. "I focus on business metrics", "I only work on the mobile app", "I review PRs but don't code"` },
449
- language: { title: "What language should the AI use?", subtitle: "e.g. English, Portugu\xEAs, Espa\xF1ol..." }
449
+ language: { title: "What language should the AI use?", subtitle: "e.g. English, Portugu\xEAs, Espa\xF1ol..." },
450
+ github_username: { title: "What's your GitHub username?", subtitle: "Used for branch naming and PR assignments. (optional \u2014 press Enter to skip)" },
451
+ aws_profiles: { title: "What AWS profiles do you use?", subtitle: 'Format: profile-name:description (one per line). e.g. "my-company-prd:Production account". Press Enter to skip.' },
452
+ focus_areas: { title: "What areas do you focus on?", subtitle: 'e.g. "infrastructure and costs", "frontend UX", "backend APIs", "mobile app". (optional)' },
453
+ timezone: { title: "What's your timezone?", subtitle: "e.g. America/Sao_Paulo, US/Eastern, Europe/London. (optional)" }
450
454
  };
451
455
  function PersonaApp({ existing, onComplete }) {
452
456
  const { stdout } = useStdout();
@@ -459,7 +463,12 @@ function PersonaApp({ existing, onComplete }) {
459
463
  role: existing?.role || "",
460
464
  context: existing?.context || "",
461
465
  technical_level: existing?.technical_level || "intermediate",
462
- language: existing?.language || "English"
466
+ language: existing?.language || "English",
467
+ aws_profiles: existing?.aws_profiles || [],
468
+ github_username: existing?.github_username || "",
469
+ slack_display_name: existing?.slack_display_name || "",
470
+ focus_areas: existing?.focus_areas || "",
471
+ timezone: existing?.timezone || ""
463
472
  });
464
473
  const [inputValue, setInputValue] = useState(existing?.name || "");
465
474
  const [levelCursor, setLevelCursor] = useState(
@@ -487,7 +496,13 @@ function PersonaApp({ existing, onComplete }) {
487
496
  }
488
497
  if (step === "review") {
489
498
  if (key.return || input === "y") {
490
- onComplete(data);
499
+ const cleanedData = { ...data };
500
+ if (!cleanedData.github_username) delete cleanedData.github_username;
501
+ if (!cleanedData.slack_display_name) delete cleanedData.slack_display_name;
502
+ if (!cleanedData.focus_areas) delete cleanedData.focus_areas;
503
+ if (!cleanedData.timezone) delete cleanedData.timezone;
504
+ if (!cleanedData.aws_profiles?.length) delete cleanedData.aws_profiles;
505
+ onComplete(cleanedData);
491
506
  goTo("done");
492
507
  }
493
508
  if (input === "b" || key.leftArrow) {
@@ -580,19 +595,91 @@ function PersonaApp({ existing, onComplete }) {
580
595
  value: inputValue,
581
596
  onChange: setInputValue,
582
597
  onSubmit: (v) => {
583
- handleTextSubmit(v || "English", "language", "review");
598
+ handleTextSubmit(v || "English", "language", "github_username");
584
599
  },
585
600
  placeholder: "English"
586
601
  }
587
602
  )
588
603
  ] }),
604
+ step === "github_username" && /* @__PURE__ */ jsxs(Box, { children: [
605
+ /* @__PURE__ */ jsx(Text, { color: colors.brand, bold: true, children: "\u276F " }),
606
+ /* @__PURE__ */ jsx(
607
+ TextInput,
608
+ {
609
+ value: inputValue,
610
+ onChange: setInputValue,
611
+ onSubmit: (v) => {
612
+ setData((prev) => ({ ...prev, github_username: v.trim() }));
613
+ setInputValue(data.aws_profiles?.map((p) => `${p.name}:${p.description}`).join(", ") || "");
614
+ goTo("aws_profiles");
615
+ },
616
+ placeholder: "(optional \u2014 press Enter to skip)"
617
+ }
618
+ )
619
+ ] }),
620
+ step === "aws_profiles" && /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { children: [
621
+ /* @__PURE__ */ jsx(Text, { color: colors.brand, bold: true, children: "\u276F " }),
622
+ /* @__PURE__ */ jsx(
623
+ TextInput,
624
+ {
625
+ value: inputValue,
626
+ onChange: setInputValue,
627
+ onSubmit: (v) => {
628
+ const profiles = v.trim() ? v.split(",").map((p) => {
629
+ const [name, ...descParts] = p.trim().split(":");
630
+ return { name: name.trim(), description: descParts.join(":").trim() || name.trim() };
631
+ }).filter((p) => p.name) : [];
632
+ setData((prev) => ({ ...prev, aws_profiles: profiles }));
633
+ setInputValue(data.focus_areas || "");
634
+ goTo("focus_areas");
635
+ },
636
+ placeholder: "my-prd:Production, my-stg:Staging (optional)"
637
+ }
638
+ )
639
+ ] }) }),
640
+ step === "focus_areas" && /* @__PURE__ */ jsxs(Box, { children: [
641
+ /* @__PURE__ */ jsx(Text, { color: colors.brand, bold: true, children: "\u276F " }),
642
+ /* @__PURE__ */ jsx(
643
+ TextInput,
644
+ {
645
+ value: inputValue,
646
+ onChange: setInputValue,
647
+ onSubmit: (v) => {
648
+ setData((prev) => ({ ...prev, focus_areas: v.trim() }));
649
+ setInputValue(data.timezone || "");
650
+ goTo("timezone");
651
+ },
652
+ placeholder: "(optional \u2014 press Enter to skip)"
653
+ }
654
+ )
655
+ ] }),
656
+ step === "timezone" && /* @__PURE__ */ jsxs(Box, { children: [
657
+ /* @__PURE__ */ jsx(Text, { color: colors.brand, bold: true, children: "\u276F " }),
658
+ /* @__PURE__ */ jsx(
659
+ TextInput,
660
+ {
661
+ value: inputValue,
662
+ onChange: setInputValue,
663
+ onSubmit: (v) => {
664
+ setData((prev) => ({ ...prev, timezone: v.trim() }));
665
+ setInputValue("");
666
+ goTo("review");
667
+ },
668
+ placeholder: "America/Sao_Paulo (optional)"
669
+ }
670
+ )
671
+ ] }),
589
672
  step === "review" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
590
673
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 1, borderStyle: "round", borderColor: colors.dim, paddingRight: 1, children: [
591
674
  /* @__PURE__ */ jsx(ReviewRow, { label: "Name", value: data.name }),
592
675
  /* @__PURE__ */ jsx(ReviewRow, { label: "Role", value: data.role }),
593
676
  /* @__PURE__ */ jsx(ReviewRow, { label: "Level", value: TECHNICAL_LEVELS.find((l) => l.value === data.technical_level)?.label || data.technical_level }),
594
677
  data.context && /* @__PURE__ */ jsx(ReviewRow, { label: "Context", value: data.context }),
595
- /* @__PURE__ */ jsx(ReviewRow, { label: "Language", value: data.language })
678
+ /* @__PURE__ */ jsx(ReviewRow, { label: "Language", value: data.language }),
679
+ data.github_username && /* @__PURE__ */ jsx(ReviewRow, { label: "GitHub", value: data.github_username }),
680
+ data.aws_profiles && data.aws_profiles.length > 0 && /* @__PURE__ */ jsx(ReviewRow, { label: "AWS", value: data.aws_profiles.map((p) => `${p.name} (${p.description})`).join(", ") }),
681
+ data.focus_areas && /* @__PURE__ */ jsx(ReviewRow, { label: "Focus", value: data.focus_areas }),
682
+ data.timezone && /* @__PURE__ */ jsx(ReviewRow, { label: "Timezone", value: data.timezone })
596
683
  ] }),
597
684
  /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: colors.dim, children: [
598
685
  /* @__PURE__ */ jsx(Text, { color: colors.brand, children: "enter" }),
@@ -684,6 +771,28 @@ ${persona.name} is an experienced developer. Communicate directly:
684
771
  - Be concise and technical. Skip basic explanations.
685
772
  - Focus on trade-offs, edge cases, and non-obvious implications.
686
773
  - Show code directly without hand-holding.`);
774
+ }
775
+ if (persona.focus_areas) {
776
+ lines.push(`
777
+ ${persona.name} focuses on: ${persona.focus_areas}. Prioritize these areas in suggestions and discussions.`);
778
+ }
779
+ if (persona.aws_profiles?.length) {
780
+ lines.push(`
781
+ ### AWS Profiles
782
+ `);
783
+ for (const profile of persona.aws_profiles) {
784
+ lines.push(`- \`${profile.name}\`: ${profile.description}`);
785
+ }
786
+ lines.push(`
787
+ When running AWS commands, ask which environment if not clear from context.`);
788
+ }
789
+ if (persona.github_username) {
790
+ lines.push(`
791
+ GitHub username: **${persona.github_username}**`);
792
+ }
793
+ if (persona.timezone) {
794
+ lines.push(`
795
+ Timezone: ${persona.timezone}`);
687
796
  }
688
797
  if (persona.context) {
689
798
  lines.push(`
@@ -695,6 +804,32 @@ Always communicate with ${persona.name} in **${persona.language}**.`);
695
804
  }
696
805
  return lines.join("\n");
697
806
  }
807
+ function buildPersonaEditorFile(persona, editor) {
808
+ const content = buildPersonaSection(persona);
809
+ if (editor === "kiro") {
810
+ return `---
811
+ inclusion: always
812
+ name: persona
813
+ ---
814
+
815
+ # Persona \u2014 ${persona.name}
816
+ ${content}
817
+ `;
818
+ }
819
+ if (editor === "cursor") {
820
+ return `---
821
+ description: "Personal AI profile for ${persona.name}"
822
+ alwaysApply: true
823
+ ---
824
+
825
+ # Persona \u2014 ${persona.name}
826
+ ${content}
827
+ `;
828
+ }
829
+ return `# Persona \u2014 ${persona.name}
830
+ ${content}
831
+ `;
832
+ }
698
833
  var personaCommand = new Command("persona").description("Set up your personal AI profile \u2014 adapts how the agent communicates with you").action(async () => {
699
834
  const hubDir = process.cwd();
700
835
  const hubPath = join3(hubDir, ".hub");
@@ -1019,12 +1154,13 @@ async function generateCursor(config, hubDir) {
1019
1154
  const cleanedOrchestratorForAgents = orchestratorRule.replace(/^---[\s\S]*?---\n/m, "").trim();
1020
1155
  const skillsSectionCursor = await buildSkillsSection(hubDir, config);
1021
1156
  const personaCursor = await loadPersona(hubDir);
1022
- const personaSectionCursor = personaCursor ? buildPersonaSection(personaCursor) : "";
1023
- const agentsMdCursor = [cleanedOrchestratorForAgents, skillsSectionCursor, personaSectionCursor].filter(Boolean).join("\n");
1157
+ const agentsMdCursor = [cleanedOrchestratorForAgents, skillsSectionCursor].filter(Boolean).join("\n");
1024
1158
  await writeFile4(join4(hubDir, "AGENTS.md"), agentsMdCursor + "\n", "utf-8");
1025
1159
  console.log(chalk3.green(" Generated AGENTS.md"));
1026
1160
  if (personaCursor) {
1027
- console.log(chalk3.green(` Applied persona: ${personaCursor.name} (${personaCursor.role})`));
1161
+ const personaRuleContent = buildPersonaEditorFile(personaCursor, "cursor");
1162
+ await writeFile4(join4(cursorDir, "rules", "persona.mdc"), personaRuleContent, "utf-8");
1163
+ console.log(chalk3.green(` Generated .cursor/rules/persona.mdc (${personaCursor.name}, ${personaCursor.role})`));
1028
1164
  }
1029
1165
  const hubSteeringDirCursor = resolve2(hubDir, "steering");
1030
1166
  try {
@@ -1918,12 +2054,13 @@ async function generateOpenCode(config, hubDir) {
1918
2054
  });
1919
2055
  const skillsSectionOC = await buildSkillsSection(hubDir, config);
1920
2056
  const personaOC = await loadPersona(hubDir);
1921
- const personaSectionOC = personaOC ? buildPersonaSection(personaOC) : "";
1922
- const agentsMdOC = [orchestratorContent, skillsSectionOC, personaSectionOC].filter(Boolean).join("\n");
2057
+ const agentsMdOC = [orchestratorContent, skillsSectionOC].filter(Boolean).join("\n");
1923
2058
  await writeFile4(join4(hubDir, "AGENTS.md"), agentsMdOC + "\n", "utf-8");
1924
2059
  console.log(chalk3.green(" Generated AGENTS.md"));
1925
2060
  if (personaOC) {
1926
- console.log(chalk3.green(` Applied persona: ${personaOC.name} (${personaOC.role})`));
2061
+ const personaRuleContent = buildPersonaEditorFile(personaOC, "opencode");
2062
+ await writeFile4(join4(opencodeDir, "rules", "persona.md"), personaRuleContent, "utf-8");
2063
+ console.log(chalk3.green(` Generated .opencode/rules/persona.md (${personaOC.name}, ${personaOC.role})`));
1927
2064
  }
1928
2065
  const hubSteeringDirOC = resolve2(hubDir, "steering");
1929
2066
  try {
@@ -2497,8 +2634,7 @@ async function generateClaudeCode(config, hubDir) {
2497
2634
  const cleanedOrchestrator = orchestratorRule.replace(/^---[\s\S]*?---\n/m, "").trim();
2498
2635
  const skillsSectionClaude = await buildSkillsSection(hubDir, config);
2499
2636
  const personaClaude = await loadPersona(hubDir);
2500
- const personaSectionClaude = personaClaude ? buildPersonaSection(personaClaude) : "";
2501
- const agentsMdClaude = [cleanedOrchestrator, skillsSectionClaude, personaSectionClaude].filter(Boolean).join("\n");
2637
+ const agentsMdClaude = [cleanedOrchestrator, skillsSectionClaude].filter(Boolean).join("\n");
2502
2638
  await writeFile4(join4(hubDir, "AGENTS.md"), agentsMdClaude + "\n", "utf-8");
2503
2639
  console.log(chalk3.green(" Generated AGENTS.md"));
2504
2640
  if (personaClaude) {
@@ -2561,6 +2697,10 @@ async function generateClaudeCode(config, hubDir) {
2561
2697
  }
2562
2698
  } catch {
2563
2699
  }
2700
+ if (personaClaude) {
2701
+ claudeMdSections.push(buildPersonaEditorFile(personaClaude, "claude-code"));
2702
+ console.log(chalk3.green(` Generated persona section in CLAUDE.md (${personaClaude.name}, ${personaClaude.role})`));
2703
+ }
2564
2704
  await writeFile4(join4(hubDir, "CLAUDE.md"), claudeMdSections.join("\n\n"), "utf-8");
2565
2705
  console.log(chalk3.green(" Generated CLAUDE.md"));
2566
2706
  if (config.mcps?.length) {
@@ -2658,12 +2798,13 @@ async function generateKiro(config, hubDir) {
2658
2798
  const kiroRule = buildKiroOrchestratorRule(config);
2659
2799
  const skillsSection = await buildSkillsSection(hubDir, config);
2660
2800
  const personaKiro = await loadPersona(hubDir);
2661
- const personaSectionKiro = personaKiro ? buildPersonaSection(personaKiro) : "";
2662
- const kiroRuleWithSkills = [kiroRule, skillsSection, personaSectionKiro].filter(Boolean).join("\n");
2801
+ const kiroRuleWithSkills = [kiroRule, skillsSection].filter(Boolean).join("\n");
2663
2802
  await writeFile4(join4(hubDir, "AGENTS.md"), kiroRuleWithSkills + "\n", "utf-8");
2664
2803
  console.log(chalk3.green(" Generated AGENTS.md"));
2665
2804
  if (personaKiro) {
2666
- console.log(chalk3.green(` Applied persona: ${personaKiro.name} (${personaKiro.role})`));
2805
+ const personaSteeringContent = buildPersonaEditorFile(personaKiro, "kiro");
2806
+ await writeFile4(join4(steeringDir, "persona.md"), personaSteeringContent, "utf-8");
2807
+ console.log(chalk3.green(` Generated .kiro/steering/persona.md (${personaKiro.name}, ${personaKiro.role})`));
2667
2808
  }
2668
2809
  await rm(join4(steeringDir, "orchestrator.md")).catch(() => {
2669
2810
  });
@@ -2963,6 +3104,13 @@ function buildGitignoreLines(config) {
2963
3104
  ".agent-teams/"
2964
3105
  );
2965
3106
  }
3107
+ lines.push(
3108
+ "",
3109
+ "# Persona (personal, not shared)",
3110
+ ".kiro/steering/persona.md",
3111
+ ".cursor/rules/persona.mdc",
3112
+ ".opencode/rules/persona.md"
3113
+ );
2966
3114
  return lines;
2967
3115
  }
2968
3116
  var generators = {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  generateCommand,
3
3
  generators
4
- } from "./chunk-6XBKSCTE.js";
4
+ } from "./chunk-FMBCFOVH.js";
5
5
  import "./chunk-VMN4KGAK.js";
6
6
  export {
7
7
  generateCommand,
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  horizontalLine,
7
7
  personaCommand,
8
8
  symbols
9
- } from "./chunk-6XBKSCTE.js";
9
+ } from "./chunk-FMBCFOVH.js";
10
10
  import {
11
11
  loadHubConfig,
12
12
  resolveConfigPath
@@ -4109,6 +4109,7 @@ async function findUnsyncedAssets(hubDir) {
4109
4109
  for (const file of files) {
4110
4110
  if (!file.endsWith(".md")) continue;
4111
4111
  if (file === "orchestrator.md") continue;
4112
+ if (file === "persona.md") continue;
4112
4113
  const canonicalFile = join18(canonicalSteeringDir, file);
4113
4114
  if (existsSync14(canonicalFile)) continue;
4114
4115
  const key = `steering:${file}`;
@@ -4127,6 +4128,7 @@ async function findUnsyncedAssets(hubDir) {
4127
4128
  for (const file of files) {
4128
4129
  if (!file.endsWith(".mdc")) continue;
4129
4130
  if (file === "orchestrator.mdc") continue;
4131
+ if (file === "persona.mdc") continue;
4130
4132
  const mdName = file.replace(/\.mdc$/, ".md");
4131
4133
  const canonicalFile = join18(canonicalSteeringDir, mdName);
4132
4134
  if (existsSync14(canonicalFile)) continue;
@@ -4146,6 +4148,7 @@ async function findUnsyncedAssets(hubDir) {
4146
4148
  for (const file of files) {
4147
4149
  if (!file.endsWith(".md")) continue;
4148
4150
  if (file === "orchestrator.md") continue;
4151
+ if (file === "persona.md") continue;
4149
4152
  const canonicalFile = join18(canonicalSteeringDir, file);
4150
4153
  if (existsSync14(canonicalFile)) continue;
4151
4154
  const key = `steering:${file}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arvoretech/hub",
3
- "version": "0.18.1",
3
+ "version": "0.19.0",
4
4
  "description": "CLI for managing AI-aware multi-repository workspaces",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",