@3lineas/grooveskills 1.1.0 → 1.3.1

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.
@@ -29,20 +29,38 @@ function detectAgents(targetPath) {
29
29
  import { readFileSync, existsSync } from "fs";
30
30
  import { join } from "path";
31
31
  function detectDependencies(targetPath) {
32
+ const detected = /* @__PURE__ */ new Set();
33
+ let hasManifest = false;
32
34
  const packageJsonPath = join(targetPath, "package.json");
33
- if (!existsSync(packageJsonPath)) {
34
- return { dependencies: [], exists: false };
35
+ if (existsSync(packageJsonPath)) {
36
+ hasManifest = true;
37
+ try {
38
+ const raw = readFileSync(packageJsonPath, "utf-8");
39
+ const parsed = JSON.parse(raw);
40
+ for (const dep of Object.keys(parsed.dependencies ?? {})) {
41
+ detected.add(dep);
42
+ }
43
+ for (const dep of Object.keys(parsed.devDependencies ?? {})) {
44
+ detected.add(dep);
45
+ }
46
+ } catch {
47
+ }
35
48
  }
36
- try {
37
- const raw = readFileSync(packageJsonPath, "utf-8");
38
- const parsed = JSON.parse(raw);
39
- const direct = Object.keys(parsed.dependencies ?? {});
40
- const dev = Object.keys(parsed.devDependencies ?? {});
41
- const all = Array.from(/* @__PURE__ */ new Set([...direct, ...dev]));
42
- return { dependencies: all, exists: true };
43
- } catch {
44
- return { dependencies: [], exists: true };
49
+ const cargoPath = join(targetPath, "Cargo.toml");
50
+ if (existsSync(cargoPath)) {
51
+ hasManifest = true;
52
+ detected.add("rust");
53
+ }
54
+ const pyprojectPath = join(targetPath, "pyproject.toml");
55
+ const requirementsPath = join(targetPath, "requirements.txt");
56
+ if (existsSync(pyprojectPath) || existsSync(requirementsPath)) {
57
+ hasManifest = true;
58
+ detected.add("python");
45
59
  }
60
+ return {
61
+ dependencies: Array.from(detected),
62
+ exists: hasManifest
63
+ };
46
64
  }
47
65
 
48
66
  // src/domains/registry/skill-catalog.ts
@@ -50,10 +68,17 @@ var SKILL_CATALOG = [
50
68
  {
51
69
  id: "groove-harness",
52
70
  name: "GrooveSkills Harness",
53
- description: "Lightweight project harness, context memory, and 3-lane task execution (Answer, Task, Spec) for AI coding agents",
71
+ description: "Lightweight project harness, context memory, and ODD 3-lane task execution (Answer, Task, Spec) for AI coding agents",
54
72
  dependencies: [],
55
73
  directoryName: "groove-harness"
56
74
  },
75
+ {
76
+ id: "odd-ai",
77
+ name: "Organic & Outcome-Driven Development (ODD)",
78
+ description: "7-step ODD protocol for AI agents: authorize, explore, classify, outcome hypotheses, and verifiable evidence receipts",
79
+ dependencies: [],
80
+ directoryName: "odd-ai"
81
+ },
57
82
  {
58
83
  id: "best-practices",
59
84
  name: "Universal Best Practices",
@@ -61,6 +86,62 @@ var SKILL_CATALOG = [
61
86
  dependencies: [],
62
87
  directoryName: "best-practices"
63
88
  },
89
+ {
90
+ id: "ux-writing",
91
+ name: "UX Writing & Microcopy Standards",
92
+ description: "Persuasive, empathetic, professional bilingual microcopy (ES/EN) for error messages, forms, CTAs, toasts, and empty states",
93
+ dependencies: [],
94
+ directoryName: "ux-writing"
95
+ },
96
+ {
97
+ id: "typescript-clean",
98
+ name: "TypeScript Clean Code (TS 5.5+)",
99
+ description: "Advanced strict TypeScript, discriminated unions, satisfies, const generics, unknown over any, and zero unsafe casts",
100
+ dependencies: ["typescript"],
101
+ directoryName: "typescript-clean"
102
+ },
103
+ {
104
+ id: "rust-clean",
105
+ name: "Rust Clean Code (Rust 2024)",
106
+ description: "Idiomatic Rust clean code: let-else guard clauses, no unwrap in production, thiserror typed errors, Newtypes, zero barrel files, <200 lines",
107
+ dependencies: ["rust"],
108
+ directoryName: "rust-clean"
109
+ },
110
+ {
111
+ id: "python-clean",
112
+ name: "Python Clean Code (Python 3.12+)",
113
+ description: "Modern Python standards: guard clauses (no nested ifs), strict typing (zero Any), uv, Ruff, Pyright, Pydantic v2 boundaries, <200 lines",
114
+ dependencies: ["python"],
115
+ directoryName: "python-clean"
116
+ },
117
+ {
118
+ id: "error-handling-logging",
119
+ name: "Error Handling & Structured Logging",
120
+ description: "Deterministic error management with AppError, Result/ActionResponse contracts, and structured context logging",
121
+ dependencies: [],
122
+ directoryName: "error-handling-logging"
123
+ },
124
+ {
125
+ id: "valibot-validation",
126
+ name: "Valibot Schema Validation",
127
+ description: "Boundary schema validation with Valibot (* as v from 'valibot'), strict type inference, and friendly error messages",
128
+ dependencies: ["valibot"],
129
+ directoryName: "valibot-validation"
130
+ },
131
+ {
132
+ id: "clean-architecture-ddd",
133
+ name: "Clean Architecture & DDD",
134
+ description: "Domain-driven design, isolated single-purpose Actions, pure functions, immutability, zero barrel files, <200 lines per file",
135
+ dependencies: [],
136
+ directoryName: "clean-architecture-ddd"
137
+ },
138
+ {
139
+ id: "testing-vitest",
140
+ name: "Vitest Testing Standards",
141
+ description: "Clean unit and integration testing with Vitest: AAA pattern, behavioral testing over mocks, and boundary edge cases",
142
+ dependencies: ["vitest"],
143
+ directoryName: "testing-vitest"
144
+ },
64
145
  {
65
146
  id: "hono",
66
147
  name: "Hono Architecture (Valibot)",
@@ -95,7 +176,13 @@ var SKILL_CATALOG = [
95
176
  function detectProjectContext(targetPath) {
96
177
  const detectedAgents = detectAgents(targetPath);
97
178
  const { dependencies, exists } = detectDependencies(targetPath);
98
- const suggestedSkillIds = ["best-practices"];
179
+ const suggestedSkillIds = [
180
+ "odd-ai",
181
+ "best-practices",
182
+ "ux-writing",
183
+ "error-handling-logging",
184
+ "clean-architecture-ddd"
185
+ ];
99
186
  for (const skill of SKILL_CATALOG) {
100
187
  if (skill.dependencies.length === 0) {
101
188
  continue;
@@ -117,7 +204,7 @@ function detectProjectContext(targetPath) {
117
204
  }
118
205
 
119
206
  // src/domains/ui/ui-orchestrator.ts
120
- import * as p5 from "@clack/prompts";
207
+ import * as p6 from "@clack/prompts";
121
208
  import pc2 from "picocolors";
122
209
 
123
210
  // src/domains/ui/banners.ts
@@ -180,10 +267,28 @@ async function promptHarness(harnessExists) {
180
267
  return confirm3;
181
268
  }
182
269
 
183
- // src/domains/ui/prompt-confirm-overwrite.ts
270
+ // src/domains/ui/prompt-language.ts
184
271
  import * as p4 from "@clack/prompts";
272
+ async function promptLanguage(defaultLanguage = "es") {
273
+ const options = [
274
+ { value: "es", label: "Espa\xF1ol (Spanish)", hint: "Recomendado para proyectos en espa\xF1ol" },
275
+ { value: "en", label: "English", hint: "Standard international English" },
276
+ { value: "pt", label: "Portugu\xEAs (Portuguese)", hint: "Portugu\xEAs para o Brasil e Portugal" },
277
+ { value: "it", label: "Italiano (Italian)", hint: "Comunicazione in lingua italiana" },
278
+ { value: "fr", label: "Fran\xE7ais (French)", hint: "Communication en langue fran\xE7aise" }
279
+ ];
280
+ const selected = await p4.select({
281
+ message: "Which language should AI agents use for user-facing copy and communication?",
282
+ options,
283
+ initialValue: defaultLanguage
284
+ });
285
+ return selected;
286
+ }
287
+
288
+ // src/domains/ui/prompt-confirm-overwrite.ts
289
+ import * as p5 from "@clack/prompts";
185
290
  async function promptConfirmOverwrite() {
186
- return await p4.confirm({
291
+ return await p5.confirm({
187
292
  message: "Some skills already exist in this project. Would you like to overwrite them with the latest version?",
188
293
  initialValue: true
189
294
  });
@@ -418,9 +523,44 @@ alwaysApply: false
418
523
  import { existsSync as existsSync10, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3 } from "fs";
419
524
  import { join as join12 } from "path";
420
525
 
526
+ // src/domains/harness/harness-languages.ts
527
+ var SUPPORTED_LANGUAGES = {
528
+ es: {
529
+ code: "es",
530
+ name: "Spanish",
531
+ nativeName: "Espa\xF1ol",
532
+ instruction: "Todos los mensajes, validaciones, errores, CTAs y comunicaciones con el usuario deben redactarse estrictamente en Espa\xF1ol. El tono debe ser emp\xE1tico, amable, persuasivo y profesional."
533
+ },
534
+ en: {
535
+ code: "en",
536
+ name: "English",
537
+ nativeName: "English",
538
+ instruction: "All user-facing messages, form validations, errors, CTAs, and notifications must be written strictly in English. Tone must be empathetic, friendly, persuasive, and professional."
539
+ },
540
+ pt: {
541
+ code: "pt",
542
+ name: "Portuguese",
543
+ nativeName: "Portugu\xEAs",
544
+ instruction: "Todas as mensagens, valida\xE7\xF5es, erros, CTAs e comunica\xE7\xF5es voltadas para o usu\xE1rio devem ser redigidas estritamente em Portugu\xEAs. O tom deve ser emp\xE1tico, amig\xE1vel, persuasivo e profissional."
545
+ },
546
+ it: {
547
+ code: "it",
548
+ name: "Italian",
549
+ nativeName: "Italiano",
550
+ instruction: "Tutti i messaggi, le convalide, gli errori, i pulsanti/CTA e le comunicazioni rivolte all'utente devono essere scritti rigorosamente in Italiano. Il tono deve essere empatico, cordiale, persuasivo e professionale."
551
+ },
552
+ fr: {
553
+ code: "fr",
554
+ name: "French",
555
+ nativeName: "Fran\xE7ais",
556
+ instruction: "Tous les messages, validations, erreurs, boutons d'appel \xE0 l'action (CTA) et notifications destin\xE9s \xE0 l'utilisateur doivent \xEAtre r\xE9dig\xE9s strictement en Fran\xE7ais. Le ton doit \xEAtre empathique, courtois, persuasif et professionnel."
557
+ }
558
+ };
559
+
421
560
  // src/domains/harness/harness-templates.ts
422
- function buildContextTemplate(detectedDependencies) {
561
+ function buildContextTemplate(detectedDependencies, language = "es") {
423
562
  const depsFormatted = detectedDependencies.length > 0 ? detectedDependencies.map((d) => `- \`${d}\``).join("\n") : "- (No external dependencies detected)";
563
+ const langConfig = SUPPORTED_LANGUAGES[language];
424
564
  return `# Project Context & Architecture
425
565
 
426
566
  This file provides AI agents with instant architectural orientation for this project.
@@ -433,21 +573,26 @@ ${depsFormatted}
433
573
  - Maintain direct imports with path aliases; avoid barrel files.
434
574
  - Keep domain boundaries explicit and clean.
435
575
 
436
- ## 3. Essential Commands
576
+ ## 3. Communication & User Copy Language
577
+ - **Configured Language**: ${langConfig.nativeName} (\`${language}\`).
578
+ - **Policy**: ${langConfig.instruction}
579
+
580
+ ## 4. Essential Commands
437
581
  - Build: Refer to \`package.json\` scripts.
438
582
  - Typecheck: Refer to TypeScript configuration.
439
583
  - Tests: Refer to test runner configuration.
440
584
  `;
441
585
  }
442
- function buildDecisionsTemplate() {
586
+ function buildDecisionsTemplate(language = "es") {
443
587
  const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
588
+ const langConfig = SUPPORTED_LANGUAGES[language];
444
589
  return `# Architecture Decision Records (ADR)
445
590
 
446
591
  An append-only log of significant technical and architectural choices.
447
592
 
448
593
  | Date | Title | Decision | Reason & Consequences |
449
594
  | :--- | :--- | :--- | :--- |
450
- | ${now} | Adopt GrooveSkills Harness | Initialized \`.grooveskills/\` control plane | Clean, encapsulated agent memory and 3-lane execution flow |
595
+ | ${now} | Adopt GrooveSkills Harness | Initialized \`.grooveskills/\` control plane with ODD | Clean, encapsulated agent memory, 3-lane ODD flow, and ${langConfig.nativeName} copy standard |
451
596
  `;
452
597
  }
453
598
  function buildTaskTemplate() {
@@ -458,8 +603,9 @@ createdAt: \${CURRENT_DATE}
458
603
 
459
604
  # Feature / Task Title
460
605
 
461
- ## Intent
462
- Describe what changes are being made and why they are necessary.
606
+ ## Outcome Hypothesis
607
+ Describe the expected behavioral outcome:
608
+ By [Output / Technical Change], [Outcome / Verifiable Behavior] will occur, proven by [Evidence / Test / Build Command].
463
609
 
464
610
  ## Scope
465
611
  - In scope:
@@ -476,18 +622,24 @@ Describe what changes are being made and why they are necessary.
476
622
  - Next concrete step if interrupted.
477
623
  `;
478
624
  }
479
- function buildAgentHarnessBlock() {
625
+ function buildAgentHarnessBlock(language = "es") {
626
+ const langConfig = SUPPORTED_LANGUAGES[language];
480
627
  return `<!-- grooveskills:harness:start -->
481
628
  # GrooveSkills Harness \u2014 Agent Rules & Memory
482
629
 
483
630
  This project uses the **GrooveSkills Harness** located in \`.grooveskills/\`.
484
631
 
485
- ## 1. Three-Lane Rule
486
- - **Answer**: Explanations, audits, or questions. Read-only; write zero code.
487
- - **Task**: Direct implementation. Record intent, scope, and checklist in \`.grooveskills/memory/tasks/<slug>.md\`. Only check off tasks with observed evidence under \`Evidence\`.
488
- - **Spec**: Ambiguous or high-impact architecture. Clarify and validate the spec in \`.grooveskills/memory/tasks/<slug>.md\` before writing code.
632
+ ## 1. Three-Lane ODD Protocol
633
+ - **Authorize & Explore**: Follow the 7-step ODD protocol (Authorize, Explore, Resolve, Classify, Track & Hypothesize, Execute, Verify).
634
+ - **Lane 1 (Answer)**: Explanations, audits, or questions. Read-only; write zero code.
635
+ - **Lane 2 (Task)**: Direct implementation. Declare Outcome Hypothesis and checklist in \`.grooveskills/memory/tasks/<slug>.md\`. Only check off tasks with observed evidence under \`Evidence\`.
636
+ - **Lane 3 (Spec)**: Ambiguous or high-impact architecture. Clarify and validate the spec in \`.grooveskills/memory/tasks/<slug>.md\` before writing code.
637
+
638
+ ## 2. User Communication Language (${langConfig.nativeName})
639
+ - **Language**: \`${language}\` (${langConfig.nativeName}).
640
+ - **Requirement**: ${langConfig.instruction}
489
641
 
490
- ## 2. Memory Locations
642
+ ## 3. Memory Locations
491
643
  - Architecture context: \`.grooveskills/memory/context.md\`
492
644
  - Technical decisions (ADR): \`.grooveskills/memory/decisions.md\`
493
645
  - Active tasks & evidence: \`.grooveskills/memory/tasks/\`
@@ -499,8 +651,8 @@ import { existsSync as existsSync9, readFileSync as readFileSync3, writeFileSync
499
651
  import { join as join11 } from "path";
500
652
  var START_MARKER = "<!-- grooveskills:harness:start -->";
501
653
  var END_MARKER = "<!-- grooveskills:harness:end -->";
502
- function applyHarnessBlock(filePath) {
503
- const block = buildAgentHarnessBlock();
654
+ function applyHarnessBlock(filePath, language = "es") {
655
+ const block = buildAgentHarnessBlock(language);
504
656
  if (!existsSync9(filePath)) {
505
657
  writeFileSync2(filePath, `${block}
506
658
  `, "utf-8");
@@ -517,14 +669,14 @@ function applyHarnessBlock(filePath) {
517
669
  writeFileSync2(filePath, `${existingContent}${separator}${block}
518
670
  `, "utf-8");
519
671
  }
520
- function upsertAgentInstructions(targetPath, selectedAgents) {
672
+ function upsertAgentInstructions(targetPath, selectedAgents, language = "es") {
521
673
  const updatedFiles = [];
522
674
  const agentsMdPath = join11(targetPath, "AGENTS.md");
523
- applyHarnessBlock(agentsMdPath);
675
+ applyHarnessBlock(agentsMdPath, language);
524
676
  updatedFiles.push(agentsMdPath);
525
677
  if (selectedAgents.includes("claude")) {
526
678
  const claudeMdPath = join11(targetPath, "CLAUDE.md");
527
- applyHarnessBlock(claudeMdPath);
679
+ applyHarnessBlock(claudeMdPath, language);
528
680
  updatedFiles.push(claudeMdPath);
529
681
  }
530
682
  return updatedFiles;
@@ -534,18 +686,19 @@ function upsertAgentInstructions(targetPath, selectedAgents) {
534
686
  function installHarness(options) {
535
687
  const memoryDir = getProjectMemoryDir(options.targetPath);
536
688
  const tasksDir = getProjectTasksDir(options.targetPath);
689
+ const language = options.language ?? "es";
537
690
  if (!existsSync10(tasksDir)) {
538
691
  mkdirSync6(tasksDir, { recursive: true });
539
692
  }
540
693
  const createdFiles = [];
541
694
  const contextPath = join12(memoryDir, "context.md");
542
695
  if (!existsSync10(contextPath)) {
543
- writeFileSync3(contextPath, buildContextTemplate(options.detectedDependencies), "utf-8");
696
+ writeFileSync3(contextPath, buildContextTemplate(options.detectedDependencies, language), "utf-8");
544
697
  createdFiles.push(contextPath);
545
698
  }
546
699
  const decisionsPath = join12(memoryDir, "decisions.md");
547
700
  if (!existsSync10(decisionsPath)) {
548
- writeFileSync3(decisionsPath, buildDecisionsTemplate(), "utf-8");
701
+ writeFileSync3(decisionsPath, buildDecisionsTemplate(language), "utf-8");
549
702
  createdFiles.push(decisionsPath);
550
703
  }
551
704
  const templateTaskPath = join12(tasksDir, "_template.md");
@@ -553,38 +706,46 @@ function installHarness(options) {
553
706
  writeFileSync3(templateTaskPath, buildTaskTemplate(), "utf-8");
554
707
  createdFiles.push(templateTaskPath);
555
708
  }
556
- const instructions = upsertAgentInstructions(options.targetPath, options.selectedAgents);
709
+ const instructions = upsertAgentInstructions(options.targetPath, options.selectedAgents, language);
557
710
  createdFiles.push(...instructions);
558
711
  return createdFiles;
559
712
  }
560
713
 
561
714
  // src/domains/harness/write-project-config.ts
562
715
  import { existsSync as existsSync11, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
563
- function writeProjectConfig(targetPath, newSkills, newAgents, harnessEnabled) {
564
- const configPath = getProjectConfigPath(targetPath);
565
- let existingSkills = [];
566
- let existingAgents = [];
567
- let existingHarness = false;
568
- if (existsSync11(configPath)) {
569
- try {
570
- const parsed = JSON.parse(readFileSync4(configPath, "utf-8"));
571
- if (Array.isArray(parsed.skills)) {
572
- existingSkills = parsed.skills;
573
- }
574
- if (Array.isArray(parsed.agents)) {
575
- existingAgents = parsed.agents;
576
- }
577
- if (typeof parsed.harness === "boolean") {
578
- existingHarness = parsed.harness;
579
- }
580
- } catch {
581
- }
716
+ function readExistingConfig(configPath) {
717
+ const fallback = {
718
+ skills: [],
719
+ agents: [],
720
+ harness: false
721
+ };
722
+ if (!existsSync11(configPath)) {
723
+ return fallback;
582
724
  }
583
- const mergedSkills = Array.from(/* @__PURE__ */ new Set([...existingSkills, ...newSkills]));
584
- const mergedAgents = Array.from(/* @__PURE__ */ new Set([...existingAgents, ...newAgents]));
725
+ try {
726
+ const raw = readFileSync4(configPath, "utf-8");
727
+ const parsed = JSON.parse(raw);
728
+ return {
729
+ skills: Array.isArray(parsed.skills) ? parsed.skills : [],
730
+ agents: Array.isArray(parsed.agents) ? parsed.agents : [],
731
+ harness: typeof parsed.harness === "boolean" ? parsed.harness : false,
732
+ language: typeof parsed.language === "string" ? parsed.language : void 0
733
+ };
734
+ } catch {
735
+ return fallback;
736
+ }
737
+ }
738
+ function writeProjectConfig(targetPath, newSkills, newAgents, harnessEnabled, language) {
739
+ const configPath = getProjectConfigPath(targetPath);
740
+ const existing = readExistingConfig(configPath);
741
+ const mergedSkills = Array.from(/* @__PURE__ */ new Set([...existing.skills, ...newSkills]));
742
+ const mergedAgents = Array.from(/* @__PURE__ */ new Set([...existing.agents, ...newAgents]));
743
+ const resolvedLanguage = language ?? existing.language ?? "es";
744
+ const isHarnessActive = existing.harness || harnessEnabled;
585
745
  const config = {
586
746
  version: 1,
587
- harness: existingHarness || harnessEnabled,
747
+ harness: isHarnessActive,
748
+ ...isHarnessActive ? { language: resolvedLanguage } : {},
588
749
  agents: mergedAgents,
589
750
  skills: mergedSkills,
590
751
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -606,6 +767,9 @@ async function installSkills(options) {
606
767
  if (options.enableHarness && !skillsToInstall.includes("groove-harness")) {
607
768
  skillsToInstall.push("groove-harness");
608
769
  }
770
+ if (options.enableHarness && !skillsToInstall.includes("odd-ai")) {
771
+ skillsToInstall.push("odd-ai");
772
+ }
609
773
  syncProjectStore(options.targetPath, skillsToInstall, options.selectedAgents);
610
774
  const projectSkillsDir = getProjectSkillsDir(options.targetPath);
611
775
  const createdFiles = [];
@@ -630,7 +794,8 @@ async function installSkills(options) {
630
794
  targetPath: options.targetPath,
631
795
  selectedAgents: options.selectedAgents,
632
796
  selectedSkillIds: skillsToInstall,
633
- detectedDependencies: options.detectedDependencies || []
797
+ detectedDependencies: options.detectedDependencies || [],
798
+ language: options.language
634
799
  });
635
800
  createdFiles.push(...harnessFiles);
636
801
  harnessInitialized = true;
@@ -639,75 +804,98 @@ async function installSkills(options) {
639
804
  options.targetPath,
640
805
  skillsToInstall,
641
806
  options.selectedAgents,
642
- Boolean(options.enableHarness)
807
+ Boolean(options.enableHarness),
808
+ options.language
643
809
  );
644
810
  createdFiles.push(configPath);
645
811
  return {
646
812
  installedSkills: skillsToInstall,
647
813
  configuredAgents: options.selectedAgents,
648
814
  createdFiles,
649
- harnessInitialized
815
+ harnessInitialized,
816
+ language: options.language
650
817
  };
651
818
  }
652
819
 
653
820
  // src/domains/ui/ui-orchestrator.ts
821
+ async function resolveLanguageSelection(harnessEnabled) {
822
+ if (!harnessEnabled) {
823
+ return "es";
824
+ }
825
+ const langChoice = await promptLanguage("es");
826
+ if (p6.isCancel(langChoice)) {
827
+ p6.cancel("Installation cancelled. Have a wonderful day!");
828
+ process.exit(0);
829
+ }
830
+ return langChoice;
831
+ }
832
+ async function resolveOverwriteConfirmation(targetPath, selectedSkills, selectedAgents) {
833
+ const anyExists = selectedSkills.some((skillId) => {
834
+ const meta = SKILL_CATALOG.find((s) => s.id === skillId);
835
+ return meta ? verifySkillExists(targetPath, meta.directoryName, selectedAgents) : false;
836
+ });
837
+ if (!anyExists) {
838
+ return true;
839
+ }
840
+ const confirm3 = await promptConfirmOverwrite();
841
+ if (p6.isCancel(confirm3) || !confirm3) {
842
+ p6.cancel("Installation stopped to protect existing skills.");
843
+ process.exit(0);
844
+ }
845
+ return Boolean(confirm3);
846
+ }
654
847
  async function runInteractiveUi(context) {
655
848
  console.log(renderBanner());
656
- p5.intro(pc2.magenta("Welcome to GrooveSkills! Let's tailor your AI agents with clean architectural standards."));
849
+ p6.intro(pc2.magenta("Welcome to GrooveSkills! Let's tailor your AI agents with clean architectural standards."));
657
850
  if (context.detectedDependencies.length > 0) {
658
- p5.note(
851
+ p6.note(
659
852
  `Detected dependencies: ${context.detectedDependencies.map((d) => pc2.bold(d)).join(", ")}
660
853
  Active AI folders: ${context.detectedAgents.map((a) => pc2.green(a)).join(", ") || "None (will be created)"}`,
661
854
  "Project Environment"
662
855
  );
663
856
  }
664
857
  const selectedSkills = await promptSkills(context.suggestedSkillIds);
665
- if (p5.isCancel(selectedSkills)) {
666
- p5.cancel("Installation cancelled. Have a wonderful day!");
858
+ if (p6.isCancel(selectedSkills)) {
859
+ p6.cancel("Installation cancelled. Have a wonderful day!");
667
860
  process.exit(0);
668
861
  }
669
862
  const selectedAgents = await promptAgents(context.detectedAgents);
670
- if (p5.isCancel(selectedAgents)) {
671
- p5.cancel("Installation cancelled. Have a wonderful day!");
863
+ if (p6.isCancel(selectedAgents)) {
864
+ p6.cancel("Installation cancelled. Have a wonderful day!");
672
865
  process.exit(0);
673
866
  }
674
867
  const harnessChoice = await promptHarness(context.harnessExists);
675
- if (p5.isCancel(harnessChoice)) {
676
- p5.cancel("Installation cancelled. Have a wonderful day!");
868
+ if (p6.isCancel(harnessChoice)) {
869
+ p6.cancel("Installation cancelled. Have a wonderful day!");
677
870
  process.exit(0);
678
871
  }
679
- let forceOverwrite = true;
680
- const anyExists = selectedSkills.some((skillId) => {
681
- const meta = SKILL_CATALOG.find((s2) => s2.id === skillId);
682
- return meta ? verifySkillExists(context.targetPath, meta.directoryName, selectedAgents) : false;
683
- });
684
- if (anyExists) {
685
- const confirm3 = await promptConfirmOverwrite();
686
- if (p5.isCancel(confirm3) || !confirm3) {
687
- p5.cancel("Installation stopped to protect existing skills.");
688
- process.exit(0);
689
- }
690
- forceOverwrite = Boolean(confirm3);
691
- }
692
- const s = p5.spinner();
872
+ const harnessEnabled = Boolean(harnessChoice);
873
+ const selectedLanguage = await resolveLanguageSelection(harnessEnabled);
874
+ const forceOverwrite = await resolveOverwriteConfirmation(
875
+ context.targetPath,
876
+ selectedSkills,
877
+ selectedAgents
878
+ );
879
+ const s = p6.spinner();
693
880
  s.start("Installing and adapting skills across your configured AI agents...");
694
881
  const result = await installSkills({
695
882
  targetPath: context.targetPath,
696
883
  selectedSkillIds: selectedSkills,
697
884
  selectedAgents,
698
885
  forceOverwrite,
699
- enableHarness: Boolean(harnessChoice),
700
- detectedDependencies: context.detectedDependencies
886
+ enableHarness: harnessEnabled,
887
+ detectedDependencies: context.detectedDependencies,
888
+ language: selectedLanguage
701
889
  });
702
890
  s.stop(pc2.green("All skills and harness have been successfully configured!"));
703
- const harnessStatus = result.harnessInitialized ? pc2.green("Initialized in .grooveskills/") : pc2.dim("Skipped");
704
- p5.note(
891
+ const harnessStatus = result.harnessInitialized ? pc2.green(`Initialized in .grooveskills/ (Language: ${selectedLanguage})`) : pc2.dim("Skipped");
892
+ p6.note(
705
893
  `Configured skills: ${result.installedSkills.map((s2) => pc2.bold(s2)).join(", ")}
706
894
  Target agents: ${result.configuredAgents.map((a) => pc2.cyan(a)).join(", ")}
707
895
  GrooveSkills Harness: ${harnessStatus}`,
708
896
  "Installation Summary"
709
897
  );
710
- p5.outro(pc2.green("Your AI programming pair is now empowered with your team's architectural habits! \u{1F680}"));
898
+ p6.outro(pc2.green("Your AI programming pair is now empowered with your team's architectural habits! \u{1F680}"));
711
899
  }
712
900
 
713
901
  // src/main.ts
@@ -724,7 +912,8 @@ async function runGrooveSkills(options = {}) {
724
912
  selectedAgents: options.agents || [],
725
913
  forceOverwrite: Boolean(options.yes),
726
914
  enableHarness: options.harness,
727
- detectedDependencies: context.detectedDependencies
915
+ detectedDependencies: context.detectedDependencies,
916
+ language: options.language
728
917
  });
729
918
  return;
730
919
  }
@@ -734,4 +923,4 @@ async function runGrooveSkills(options = {}) {
734
923
  export {
735
924
  runGrooveSkills
736
925
  };
737
- //# sourceMappingURL=chunk-5Q6O3BRA.js.map
926
+ //# sourceMappingURL=chunk-M7ZPBGQE.js.map