@3lineas/grooveskills 1.4.0 → 1.6.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.
@@ -70,7 +70,8 @@ var SKILL_CATALOG = [
70
70
  name: "GrooveSkills Harness",
71
71
  description: "Lightweight project harness, context memory, and ODD 3-lane task execution (Answer, Task, Spec) for AI coding agents",
72
72
  dependencies: [],
73
- directoryName: "groove-harness"
73
+ directoryName: "groove-harness",
74
+ isCoreHarness: true
74
75
  },
75
76
  {
76
77
  id: "odd-ai",
@@ -135,6 +136,55 @@ var SKILL_CATALOG = [
135
136
  dependencies: [],
136
137
  directoryName: "clean-architecture-ddd"
137
138
  },
139
+ {
140
+ id: "external-service-adapters",
141
+ name: "External Service Adapters & Wrappers",
142
+ description: "Standardized adapter and wrapper architecture for external services (payment gateways, emails, SMS, cloud storage)",
143
+ dependencies: [],
144
+ directoryName: "external-service-adapters"
145
+ },
146
+ {
147
+ id: "storage-file-adapters",
148
+ name: "Storage & File Adapters (S3, R2, Supabase)",
149
+ description: "Standardized wrapper & adapter architecture for object storage, uploads, presigned URLs, and streaming without repositories",
150
+ dependencies: [],
151
+ directoryName: "storage-file-adapters"
152
+ },
153
+ {
154
+ id: "auth-rbac-policies",
155
+ name: "Auth Adapters & RBAC/ABAC Policies",
156
+ description: "Authentication adapter architecture and pure domain authorization policies (RBAC/ABAC) with AuthenticatedActor contract",
157
+ dependencies: [],
158
+ directoryName: "auth-rbac-policies"
159
+ },
160
+ {
161
+ id: "controller-orchestration-queues",
162
+ name: "Controller Orchestration & Queue Adapters",
163
+ description: "Strict orchestration rules for Controllers, atomic Actions, DB Transactions, and background Queue Adapters (Cloudflare, BullMQ, SQS)",
164
+ dependencies: [],
165
+ directoryName: "controller-orchestration-queues"
166
+ },
167
+ {
168
+ id: "webhook-idempotency",
169
+ name: "Webhook Verifiers & Idempotency Guards",
170
+ description: "Incoming webhook HMAC verification adapters and atomic idempotency guards guaranteeing at-most-once processing",
171
+ dependencies: [],
172
+ directoryName: "webhook-idempotency"
173
+ },
174
+ {
175
+ id: "caching-rate-limiting",
176
+ name: "Cache-Aside & Rate Limiting Adapters",
177
+ description: "Cache-aside remember wrapper and interchangeable sliding-window rate limiter adapters (Redis, Cloudflare KV, In-Memory)",
178
+ dependencies: [],
179
+ directoryName: "caching-rate-limiting"
180
+ },
181
+ {
182
+ id: "env-feature-flags",
183
+ name: "Fail-Fast Env & Feature Flags Adapters",
184
+ description: "Fail-fast environment schema validation with Valibot and decoupled feature flags adapters (PostHog, LaunchDarkly, Unleash)",
185
+ dependencies: [],
186
+ directoryName: "env-feature-flags"
187
+ },
138
188
  {
139
189
  id: "testing-vitest",
140
190
  name: "Vitest Testing Standards",
@@ -194,7 +244,14 @@ function detectProjectContext(targetPath) {
194
244
  "best-practices",
195
245
  "ux-writing",
196
246
  "error-handling-logging",
197
- "clean-architecture-ddd"
247
+ "clean-architecture-ddd",
248
+ "external-service-adapters",
249
+ "storage-file-adapters",
250
+ "auth-rbac-policies",
251
+ "controller-orchestration-queues",
252
+ "webhook-idempotency",
253
+ "caching-rate-limiting",
254
+ "env-feature-flags"
198
255
  ];
199
256
  for (const skill of SKILL_CATALOG) {
200
257
  if (skill.dependencies.length === 0) {
@@ -220,7 +277,7 @@ function detectProjectContext(targetPath) {
220
277
 
221
278
  // src/domains/ui/ui-orchestrator.ts
222
279
  import * as p5 from "@clack/prompts";
223
- import pc2 from "picocolors";
280
+ import pc3 from "picocolors";
224
281
 
225
282
  // src/domains/ui/banners.ts
226
283
  import pc from "picocolors";
@@ -235,7 +292,8 @@ function renderBanner() {
235
292
  // src/domains/ui/prompt-skills.ts
236
293
  import * as p from "@clack/prompts";
237
294
  async function promptSkills(suggestedSkillIds) {
238
- const options = SKILL_CATALOG.map((skill) => {
295
+ const selectableSkills = SKILL_CATALOG.filter((skill) => !skill.isCoreHarness);
296
+ const options = selectableSkills.map((skill) => {
239
297
  const isSuggested = suggestedSkillIds.includes(skill.id);
240
298
  return {
241
299
  value: skill.id,
@@ -321,7 +379,7 @@ function verifySkillExists(targetPath, skillDirectoryName, agents) {
321
379
  }
322
380
 
323
381
  // src/domains/installer/install-orchestrator.ts
324
- import { join as join10 } from "path";
382
+ import { join as join11 } from "path";
325
383
 
326
384
  // src/domains/adapters/skill-reference-builder.ts
327
385
  import { existsSync as existsSync3, lstatSync as lstatSync2, mkdirSync, readFileSync as readFileSync3, rmSync, unlinkSync, writeFileSync } from "fs";
@@ -547,8 +605,8 @@ function syncProjectStore(targetPath, selectedSkillIds, _selectedAgents) {
547
605
  }
548
606
 
549
607
  // src/domains/harness/install-harness.ts
550
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
551
- import { join as join9 } from "path";
608
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
609
+ import { join as join10 } from "path";
552
610
 
553
611
  // src/domains/harness/harness-languages.ts
554
612
  var SUPPORTED_LANGUAGES = {
@@ -556,58 +614,98 @@ var SUPPORTED_LANGUAGES = {
556
614
  code: "es",
557
615
  name: "Spanish",
558
616
  nativeName: "Espa\xF1ol",
559
- 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."
617
+ 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.",
618
+ onboardingPromptTitle: "Prompt de inicializaci\xF3n para tu Agente IA (Copia y pega en el chat):",
619
+ onboardingPrompt: "Hola. Acabo de configurar GrooveSkills Harness en este proyecto. Por favor lee `.grooveskills/memory/tasks/00-onboarding.md`, `.grooveskills/memory/context.md` y las skills instaladas en `.agents/skills/`. Hazme preguntas interactivas paso a paso para detectar vac\xEDos de arquitectura, aclarar objetivos y adaptar el proyecto a mis necesidades antes de continuar."
560
620
  },
561
621
  en: {
562
622
  code: "en",
563
623
  name: "English",
564
624
  nativeName: "English",
565
- 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."
625
+ 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.",
626
+ onboardingPromptTitle: "Initialization prompt for your AI Agent (Copy and paste into your chat):",
627
+ onboardingPrompt: "Hello. I just configured GrooveSkills Harness in this project. Please review `.grooveskills/memory/tasks/00-onboarding.md`, `.grooveskills/memory/context.md`, and the skills installed in `.agents/skills/`. Ask me step-by-step interactive questions to identify architectural gaps, clarify priorities, and adapt the project to my needs before continuing."
566
628
  },
567
629
  pt: {
568
630
  code: "pt",
569
631
  name: "Portuguese",
570
632
  nativeName: "Portugu\xEAs",
571
- 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."
633
+ 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.",
634
+ onboardingPromptTitle: "Prompt de inicializa\xE7\xE3o para o seu Agente de IA (Copie e cole no chat):",
635
+ onboardingPrompt: "Ol\xE1. Acabei de configurar o GrooveSkills Harness neste projeto. Por favor leia `.grooveskills/memory/tasks/00-onboarding.md`, `.grooveskills/memory/context.md` e as skills instaladas em `.agents/skills/`. Fa\xE7a-me perguntas interativas passo a passo para mapear lacunas de arquitetura, alinhar prioridades e adaptar o projeto \xE0s minhas necessidades antes de prosseguir."
572
636
  },
573
637
  it: {
574
638
  code: "it",
575
639
  name: "Italian",
576
640
  nativeName: "Italiano",
577
- 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."
641
+ 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.",
642
+ onboardingPromptTitle: "Prompt di inizializzazione per il tuo Agente AI (Copia e incolla nella chat):",
643
+ onboardingPrompt: "Ciao. Ho appena configurato GrooveSkills Harness in questo progetto. Ti prego di leggere `.grooveskills/memory/tasks/00-onboarding.md`, `.grooveskills/memory/context.md` e le skills installate in `.agents/skills/`. Fammi domande interattive passo dopo passo per identificare lacune architetturali, allineare le priorit\xE0 e adattare il progetto alle mie esigenze prima di continuare."
578
644
  },
579
645
  fr: {
580
646
  code: "fr",
581
647
  name: "French",
582
648
  nativeName: "Fran\xE7ais",
583
- 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."
649
+ 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.",
650
+ onboardingPromptTitle: "Prompt d'initialisation pour votre Agent IA (Copiez et collez dans le chat) :",
651
+ onboardingPrompt: "Bonjour. Je viens de configurer GrooveSkills Harness sur ce projet. Veuillez consulter `.grooveskills/memory/tasks/00-onboarding.md`, `.grooveskills/memory/context.md` et les skills install\xE9es dans `.agents/skills/`. Posez-moi des questions interactives \xE9tape par \xE9tape pour identifier les lacunes d'architecture, pr\xE9ciser les priorit\xE9s et adapter le projet \xE0 mes besoins avant de continuer."
584
652
  }
585
653
  };
586
654
 
587
655
  // src/domains/harness/harness-templates.ts
588
- function buildContextTemplate(detectedDependencies, language = "es") {
656
+ function buildContextTemplate(detectedDependencies, language = "es", profile) {
589
657
  const depsFormatted = detectedDependencies.length > 0 ? detectedDependencies.map((d) => `- \`${d}\``).join("\n") : "- (No external dependencies detected)";
590
658
  const langConfig = SUPPORTED_LANGUAGES[language];
591
- return `# Project Context & Architecture
659
+ const projectName = profile?.projectName ?? "Project";
660
+ const pkgManager = profile?.packageManager ?? "npm";
661
+ const commandLines = [];
662
+ if (profile?.commands) {
663
+ for (const [key, cmd] of Object.entries(profile.commands)) {
664
+ commandLines.push(`- **${key}**: \`${cmd}\``);
665
+ }
666
+ }
667
+ const commandsSection = commandLines.length > 0 ? commandLines.join("\n") : `- Build: Refer to \`package.json\` scripts.
668
+ - Typecheck: Refer to TypeScript configuration.
669
+ - Tests: Refer to test runner configuration.`;
670
+ const aliasLines = [];
671
+ if (profile?.pathAliases) {
672
+ for (const [alias, target] of Object.entries(profile.pathAliases)) {
673
+ aliasLines.push(`- \`${alias}\` -> \`${target}\``);
674
+ }
675
+ }
676
+ const aliasesSection = aliasLines.length > 0 ? `
677
+ ### Path Aliases
678
+ ${aliasLines.join("\n")}
679
+ ` : "";
680
+ const dirLines = [];
681
+ if (profile?.sourceDirectories && profile.sourceDirectories.length > 0) {
682
+ for (const dir of profile.sourceDirectories) {
683
+ dirLines.push(`- \`${dir}/\``);
684
+ }
685
+ }
686
+ const dirsSection = dirLines.length > 0 ? `
687
+ ### Source Modules
688
+ ${dirLines.join("\n")}
689
+ ` : "";
690
+ return `# Project Context & Architecture: ${projectName}
592
691
 
593
692
  This file provides AI agents with instant architectural orientation for this project.
594
693
 
595
694
  ## 1. Stack & Dependencies
695
+ - **Package Manager**: \`${pkgManager}\`
596
696
  ${depsFormatted}
597
697
 
598
698
  ## 2. Directory Structure Conventions
599
699
  - Follow single-responsibility per file (<200 lines).
600
700
  - Maintain direct imports with path aliases; avoid barrel files.
601
701
  - Keep domain boundaries explicit and clean.
602
-
702
+ ${aliasesSection}${dirsSection}
603
703
  ## 3. Communication & User Copy Language
604
704
  - **Configured Language**: ${langConfig.nativeName} (\`${language}\`).
605
705
  - **Policy**: ${langConfig.instruction}
606
706
 
607
707
  ## 4. Essential Commands
608
- - Build: Refer to \`package.json\` scripts.
609
- - Typecheck: Refer to TypeScript configuration.
610
- - Tests: Refer to test runner configuration.
708
+ ${commandsSection}
611
709
  `;
612
710
  }
613
711
  function buildDecisionsTemplate(language = "es") {
@@ -622,6 +720,38 @@ An append-only log of significant technical and architectural choices.
622
720
  | ${now} | Adopt GrooveSkills Harness | Initialized \`.grooveskills/\` control plane with ODD | Clean, encapsulated agent memory, 3-lane ODD flow, and ${langConfig.nativeName} copy standard |
623
721
  `;
624
722
  }
723
+ function buildOnboardingTaskTemplate(projectName = "Project") {
724
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
725
+ return `---
726
+ status: pending
727
+ createdAt: ${now}
728
+ ---
729
+
730
+ # 00 \u2014 Initial Project Onboarding & Architecture Exploration: ${projectName}
731
+
732
+ ## Outcome Hypothesis
733
+ By scanning dependencies, entry points, configuration manifests, and module layout, the AI agent will gain a verified architectural understanding of \`${projectName}\`, proven by filling out \`.grooveskills/memory/context.md\`, formulating interactive alignment questions with the user, and validating the essential build/typecheck commands.
734
+
735
+ ## Scope
736
+ - In scope: Inspect project manifests, entry points, directory conventions, ask interactive alignment questions, and verify script execution.
737
+ - Out of scope: Refactoring business logic or modifying functional code before alignment.
738
+
739
+ ## Checklist
740
+ - [ ] Verify package manager and essential scripts (\`build\`, \`test\`, \`typecheck\`).
741
+ - [ ] Map high-level module architecture and data flow in \`.grooveskills/memory/context.md\`.
742
+ - [ ] Ask the user step-by-step interactive questions to clarify goals, domain scope, and adapt project conventions.
743
+ - [ ] Document agreed technical decisions in \`.grooveskills/memory/decisions.md\`.
744
+ - [ ] Verify test runner or static checks pass cleanly.
745
+ - [ ] Switch status to \`completed\` once project context is fully verified.
746
+
747
+ ## Evidence
748
+ - Observed command runs and outputs during the initial onboarding pass.
749
+ - User answers to alignment questions captured in memory.
750
+
751
+ ## Next
752
+ - Once onboarding is marked completed, proceed with domain tasks following the 3-lane ODD protocol.
753
+ `;
754
+ }
625
755
  function buildTaskTemplate() {
626
756
  return `---
627
757
  status: in-progress
@@ -657,6 +787,7 @@ function buildAgentHarnessBlock(language = "es") {
657
787
  This project uses the **GrooveSkills Harness** located in \`.grooveskills/\`.
658
788
 
659
789
  ## 1. Three-Lane ODD Protocol
790
+ - **Onboarding Check**: If \`.grooveskills/memory/tasks/00-onboarding.md\` has \`status: pending\`, complete its checklist to enrich \`context.md\` and set its status to \`completed\`.
660
791
  - **Authorize & Explore**: Follow the 7-step ODD protocol (Authorize, Explore, Resolve, Classify, Track & Hypothesize, Execute, Verify).
661
792
  - **Lane 1 (Answer)**: Explanations, audits, or questions. Read-only; write zero code.
662
793
  - **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\`.
@@ -673,19 +804,162 @@ This project uses the **GrooveSkills Harness** located in \`.grooveskills/\`.
673
804
  <!-- grooveskills:harness:end -->`;
674
805
  }
675
806
 
807
+ // src/domains/harness/scan-project-onboarding.ts
808
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
809
+ import { basename, join as join8 } from "path";
810
+ function detectProjectName(targetPath) {
811
+ const packageJsonPath = join8(targetPath, "package.json");
812
+ if (existsSync6(packageJsonPath)) {
813
+ try {
814
+ const raw = readFileSync4(packageJsonPath, "utf-8");
815
+ const parsed = JSON.parse(raw);
816
+ if (typeof parsed.name === "string" && parsed.name.trim().length > 0) {
817
+ return parsed.name.trim();
818
+ }
819
+ } catch {
820
+ }
821
+ }
822
+ const cargoPath = join8(targetPath, "Cargo.toml");
823
+ if (existsSync6(cargoPath)) {
824
+ try {
825
+ const cargoRaw = readFileSync4(cargoPath, "utf-8");
826
+ const nameMatch = cargoRaw.match(/name\s*=\s*"([^"]+)"/);
827
+ if (nameMatch?.[1]) {
828
+ return nameMatch[1];
829
+ }
830
+ } catch {
831
+ }
832
+ }
833
+ const pyprojectPath = join8(targetPath, "pyproject.toml");
834
+ if (existsSync6(pyprojectPath)) {
835
+ try {
836
+ const pyRaw = readFileSync4(pyprojectPath, "utf-8");
837
+ const pyMatch = pyRaw.match(/name\s*=\s*"([^"]+)"/);
838
+ if (pyMatch?.[1]) {
839
+ return pyMatch[1];
840
+ }
841
+ } catch {
842
+ }
843
+ }
844
+ return basename(targetPath) || "project";
845
+ }
846
+ function detectPackageManager(targetPath) {
847
+ if (existsSync6(join8(targetPath, "pnpm-lock.yaml"))) {
848
+ return "pnpm";
849
+ }
850
+ if (existsSync6(join8(targetPath, "bun.lockb")) || existsSync6(join8(targetPath, "bun.lock"))) {
851
+ return "bun";
852
+ }
853
+ if (existsSync6(join8(targetPath, "yarn.lock"))) {
854
+ return "yarn";
855
+ }
856
+ if (existsSync6(join8(targetPath, "package-lock.json"))) {
857
+ return "npm";
858
+ }
859
+ if (existsSync6(join8(targetPath, "Cargo.lock")) || existsSync6(join8(targetPath, "Cargo.toml"))) {
860
+ return "cargo";
861
+ }
862
+ if (existsSync6(join8(targetPath, "uv.lock"))) {
863
+ return "uv";
864
+ }
865
+ if (existsSync6(join8(targetPath, "poetry.lock"))) {
866
+ return "poetry";
867
+ }
868
+ return "npm";
869
+ }
870
+ function detectCommands(targetPath, pkgManager) {
871
+ const packageJsonPath = join8(targetPath, "package.json");
872
+ if (!existsSync6(packageJsonPath)) {
873
+ return {};
874
+ }
875
+ try {
876
+ const raw = readFileSync4(packageJsonPath, "utf-8");
877
+ const parsed = JSON.parse(raw);
878
+ const scripts = parsed.scripts ?? {};
879
+ const commands = {};
880
+ const runner = pkgManager === "npm" ? "npm run" : pkgManager;
881
+ if (scripts.build) {
882
+ commands.build = `${runner} build`;
883
+ }
884
+ if (scripts["check-types"]) {
885
+ commands.typecheck = `${runner} check-types`;
886
+ } else if (scripts.typecheck) {
887
+ commands.typecheck = `${runner} typecheck`;
888
+ } else if (scripts.tsc) {
889
+ commands.typecheck = `${runner} tsc`;
890
+ }
891
+ if (scripts.test) {
892
+ commands.test = `${runner} test`;
893
+ }
894
+ if (scripts.lint) {
895
+ commands.lint = `${runner} lint`;
896
+ }
897
+ if (scripts.dev) {
898
+ commands.dev = `${runner} dev`;
899
+ }
900
+ return commands;
901
+ } catch {
902
+ return {};
903
+ }
904
+ }
905
+ function detectPathAliases(targetPath) {
906
+ const tsconfigPath = join8(targetPath, "tsconfig.json");
907
+ if (!existsSync6(tsconfigPath)) {
908
+ return {};
909
+ }
910
+ try {
911
+ const raw = readFileSync4(tsconfigPath, "utf-8");
912
+ const parsed = JSON.parse(raw);
913
+ const paths = parsed.compilerOptions?.paths ?? {};
914
+ const aliases = {};
915
+ for (const [key, targets] of Object.entries(paths)) {
916
+ if (Array.isArray(targets) && typeof targets[0] === "string") {
917
+ aliases[key] = targets[0];
918
+ }
919
+ }
920
+ return aliases;
921
+ } catch {
922
+ return {};
923
+ }
924
+ }
925
+ function detectSourceDirectories(targetPath) {
926
+ const srcPath = join8(targetPath, "src");
927
+ const scanTarget = existsSync6(srcPath) ? srcPath : targetPath;
928
+ try {
929
+ const entries = readdirSync2(scanTarget, { withFileTypes: true });
930
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !name.startsWith(".") && name !== "node_modules" && name !== "dist");
931
+ } catch {
932
+ return [];
933
+ }
934
+ }
935
+ function scanProjectOnboarding(targetPath) {
936
+ const projectName = detectProjectName(targetPath);
937
+ const packageManager = detectPackageManager(targetPath);
938
+ const commands = detectCommands(targetPath, packageManager);
939
+ const pathAliases = detectPathAliases(targetPath);
940
+ const sourceDirectories = detectSourceDirectories(targetPath);
941
+ return {
942
+ projectName,
943
+ packageManager,
944
+ commands,
945
+ pathAliases,
946
+ sourceDirectories
947
+ };
948
+ }
949
+
676
950
  // src/domains/harness/upsert-agent-instructions.ts
677
- import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
678
- import { join as join8 } from "path";
951
+ import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
952
+ import { join as join9 } from "path";
679
953
  var START_MARKER = "<!-- grooveskills:harness:start -->";
680
954
  var END_MARKER = "<!-- grooveskills:harness:end -->";
681
955
  function applyHarnessBlock(filePath, language = "es") {
682
956
  const block = buildAgentHarnessBlock(language);
683
- if (!existsSync6(filePath)) {
957
+ if (!existsSync7(filePath)) {
684
958
  writeFileSync2(filePath, `${block}
685
959
  `, "utf-8");
686
960
  return;
687
961
  }
688
- const existingContent = readFileSync4(filePath, "utf-8");
962
+ const existingContent = readFileSync5(filePath, "utf-8");
689
963
  if (existingContent.includes(START_MARKER) && existingContent.includes(END_MARKER)) {
690
964
  const regex = new RegExp(`${START_MARKER}[\\s\\S]*?${END_MARKER}`, "g");
691
965
  const updated = existingContent.replace(regex, block);
@@ -698,11 +972,11 @@ function applyHarnessBlock(filePath, language = "es") {
698
972
  }
699
973
  function upsertAgentInstructions(targetPath, selectedAgents, language = "es") {
700
974
  const updatedFiles = [];
701
- const agentsMdPath = join8(targetPath, "AGENTS.md");
975
+ const agentsMdPath = join9(targetPath, "AGENTS.md");
702
976
  applyHarnessBlock(agentsMdPath, language);
703
977
  updatedFiles.push(agentsMdPath);
704
978
  if (selectedAgents.includes("claude")) {
705
- const claudeMdPath = join8(targetPath, "CLAUDE.md");
979
+ const claudeMdPath = join9(targetPath, "CLAUDE.md");
706
980
  applyHarnessBlock(claudeMdPath, language);
707
981
  updatedFiles.push(claudeMdPath);
708
982
  }
@@ -714,43 +988,65 @@ function installHarness(options) {
714
988
  const memoryDir = getProjectMemoryDir(options.targetPath);
715
989
  const tasksDir = getProjectTasksDir(options.targetPath);
716
990
  const language = options.language ?? "es";
717
- if (!existsSync7(tasksDir)) {
991
+ const forceOnboarding = Boolean(options.forceOnboarding);
992
+ if (!existsSync8(tasksDir)) {
718
993
  mkdirSync3(tasksDir, { recursive: true });
719
994
  }
720
995
  const createdFiles = [];
721
- const contextPath = join9(memoryDir, "context.md");
722
- if (!existsSync7(contextPath)) {
723
- writeFileSync3(contextPath, buildContextTemplate(options.detectedDependencies, language), "utf-8");
996
+ const contextPath = join10(memoryDir, "context.md");
997
+ const shouldWriteContext = !existsSync8(contextPath) || forceOnboarding;
998
+ if (shouldWriteContext) {
999
+ const profile = scanProjectOnboarding(options.targetPath);
1000
+ const contextContent = buildContextTemplate(
1001
+ options.detectedDependencies,
1002
+ language,
1003
+ profile
1004
+ );
1005
+ writeFileSync3(contextPath, contextContent, "utf-8");
724
1006
  createdFiles.push(contextPath);
1007
+ const onboardingTaskPath = join10(tasksDir, "00-onboarding.md");
1008
+ writeFileSync3(
1009
+ onboardingTaskPath,
1010
+ buildOnboardingTaskTemplate(profile.projectName),
1011
+ "utf-8"
1012
+ );
1013
+ createdFiles.push(onboardingTaskPath);
725
1014
  }
726
- const decisionsPath = join9(memoryDir, "decisions.md");
727
- if (!existsSync7(decisionsPath)) {
1015
+ const decisionsPath = join10(memoryDir, "decisions.md");
1016
+ if (!existsSync8(decisionsPath)) {
728
1017
  writeFileSync3(decisionsPath, buildDecisionsTemplate(language), "utf-8");
729
1018
  createdFiles.push(decisionsPath);
730
1019
  }
731
- const templateTaskPath = join9(tasksDir, "_template.md");
732
- if (!existsSync7(templateTaskPath)) {
1020
+ const templateTaskPath = join10(tasksDir, "_template.md");
1021
+ if (!existsSync8(templateTaskPath)) {
733
1022
  writeFileSync3(templateTaskPath, buildTaskTemplate(), "utf-8");
734
1023
  createdFiles.push(templateTaskPath);
735
1024
  }
736
- const instructions = upsertAgentInstructions(options.targetPath, options.selectedAgents, language);
1025
+ const instructions = upsertAgentInstructions(
1026
+ options.targetPath,
1027
+ options.selectedAgents,
1028
+ language
1029
+ );
737
1030
  createdFiles.push(...instructions);
738
- return createdFiles;
1031
+ return {
1032
+ createdFiles,
1033
+ onboardingInitialized: shouldWriteContext
1034
+ };
739
1035
  }
740
1036
 
741
1037
  // src/domains/harness/write-project-config.ts
742
- import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
1038
+ import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
743
1039
  function readExistingConfig(configPath) {
744
1040
  const fallback = {
745
1041
  skills: [],
746
1042
  agents: [],
747
1043
  harness: false
748
1044
  };
749
- if (!existsSync8(configPath)) {
1045
+ if (!existsSync9(configPath)) {
750
1046
  return fallback;
751
1047
  }
752
1048
  try {
753
- const raw = readFileSync5(configPath, "utf-8");
1049
+ const raw = readFileSync6(configPath, "utf-8");
754
1050
  const parsed = JSON.parse(raw);
755
1051
  return {
756
1052
  skills: Array.isArray(parsed.skills) ? parsed.skills : [],
@@ -805,7 +1101,7 @@ async function installSkills(options) {
805
1101
  if (!skill) {
806
1102
  continue;
807
1103
  }
808
- const skillSourceDir = join10(projectSkillsDir, skill.directoryName);
1104
+ const skillSourceDir = join11(projectSkillsDir, skill.directoryName);
809
1105
  for (const agentId of options.selectedAgents) {
810
1106
  const adapter = ADAPTER_REGISTRY[agentId];
811
1107
  if (!adapter) {
@@ -815,14 +1111,15 @@ async function installSkills(options) {
815
1111
  createdFiles.push(...files);
816
1112
  }
817
1113
  }
818
- const harnessFiles = installHarness({
1114
+ const harnessResult = installHarness({
819
1115
  targetPath: options.targetPath,
820
1116
  selectedAgents: options.selectedAgents,
821
1117
  selectedSkillIds: skillsToInstall,
822
1118
  detectedDependencies: options.detectedDependencies || [],
823
- language: options.language
1119
+ language: options.language,
1120
+ forceOnboarding: options.forceOnboarding
824
1121
  });
825
- createdFiles.push(...harnessFiles);
1122
+ createdFiles.push(...harnessResult.createdFiles);
826
1123
  const configPath = writeProjectConfig(
827
1124
  options.targetPath,
828
1125
  skillsToInstall,
@@ -835,14 +1132,35 @@ async function installSkills(options) {
835
1132
  installedSkills: skillsToInstall,
836
1133
  configuredAgents: options.selectedAgents,
837
1134
  createdFiles,
838
- harnessInitialized: true,
1135
+ harnessInitialized: harnessResult.onboardingInitialized,
839
1136
  language: options.language
840
1137
  };
841
1138
  }
842
1139
 
1140
+ // src/domains/ui/render-onboarding-prompt.ts
1141
+ import pc2 from "picocolors";
1142
+ function formatOnboardingPrompt(language = "es") {
1143
+ const langConfig = SUPPORTED_LANGUAGES[language] ?? SUPPORTED_LANGUAGES.es;
1144
+ const title = langConfig.onboardingPromptTitle;
1145
+ const prompt = langConfig.onboardingPrompt;
1146
+ const separator = pc2.cyan("\u2500".repeat(60));
1147
+ const formattedOutput = [
1148
+ separator,
1149
+ pc2.bold(pc2.yellow(title)),
1150
+ "",
1151
+ pc2.green(prompt),
1152
+ separator
1153
+ ].join("\n");
1154
+ return {
1155
+ title,
1156
+ prompt,
1157
+ formattedOutput
1158
+ };
1159
+ }
1160
+
843
1161
  // src/domains/ui/ui-orchestrator.ts
844
- async function resolveLanguageSelection(harnessExists, configuredLanguage) {
845
- if (harnessExists) {
1162
+ async function resolveLanguageSelection(harnessExists, configuredLanguage, forceOnboarding) {
1163
+ if (harnessExists && !forceOnboarding) {
846
1164
  return configuredLanguage ?? "es";
847
1165
  }
848
1166
  const langChoice = await promptLanguage(configuredLanguage ?? "es");
@@ -867,14 +1185,14 @@ async function resolveOverwriteConfirmation(targetPath, selectedSkills, selected
867
1185
  }
868
1186
  return Boolean(confirm2);
869
1187
  }
870
- async function runInteractiveUi(context) {
1188
+ async function runInteractiveUi(context, forceOnboarding = false) {
871
1189
  console.log(renderBanner());
872
- p5.intro(pc2.magenta("Welcome to GrooveSkills! Let's tailor your AI agents with clean architectural standards."));
1190
+ p5.intro(pc3.magenta("Welcome to GrooveSkills! Let's tailor your AI agents with clean architectural standards."));
873
1191
  if (context.detectedDependencies.length > 0) {
874
- const harnessInfo = context.harnessExists ? pc2.green(`Active (.grooveskills/memory, lang: ${context.configuredLanguage ?? "es"})`) : pc2.cyan("Will be initialized");
1192
+ const harnessInfo = context.harnessExists && !forceOnboarding ? pc3.green(`Active (.grooveskills/memory, lang: ${context.configuredLanguage ?? "es"})`) : pc3.cyan("Will be initialized");
875
1193
  p5.note(
876
- `Detected dependencies: ${context.detectedDependencies.map((d) => pc2.bold(d)).join(", ")}
877
- Active AI folders: ${context.detectedAgents.map((a) => pc2.green(a)).join(", ") || "None (will be created)"}
1194
+ `Detected dependencies: ${context.detectedDependencies.map((d) => pc3.bold(d)).join(", ")}
1195
+ Active AI folders: ${context.detectedAgents.map((a) => pc3.green(a)).join(", ") || "None (will be created)"}
878
1196
  GrooveSkills Harness: ${harnessInfo}`,
879
1197
  "Project Environment"
880
1198
  );
@@ -891,7 +1209,8 @@ GrooveSkills Harness: ${harnessInfo}`,
891
1209
  }
892
1210
  const selectedLanguage = await resolveLanguageSelection(
893
1211
  context.harnessExists,
894
- context.configuredLanguage
1212
+ context.configuredLanguage,
1213
+ forceOnboarding
895
1214
  );
896
1215
  const forceOverwrite = await resolveOverwriteConfirmation(
897
1216
  context.targetPath,
@@ -907,17 +1226,25 @@ GrooveSkills Harness: ${harnessInfo}`,
907
1226
  forceOverwrite,
908
1227
  enableHarness: true,
909
1228
  detectedDependencies: context.detectedDependencies,
910
- language: selectedLanguage
1229
+ language: selectedLanguage,
1230
+ forceOnboarding
911
1231
  });
912
- s.stop(pc2.green("All skills and harness have been successfully configured!"));
913
- const harnessStatus = result.harnessInitialized ? pc2.green(`Initialized in .grooveskills/ (Language: ${selectedLanguage})`) : pc2.cyan(`Preserved in .grooveskills/ (Language: ${selectedLanguage})`);
1232
+ s.stop(pc3.green("All skills and harness have been successfully configured!"));
1233
+ const harnessStatus = result.harnessInitialized ? pc3.green(`Initialized in .grooveskills/ (Language: ${selectedLanguage})`) : pc3.cyan(`Preserved in .grooveskills/ (Language: ${selectedLanguage})`);
914
1234
  p5.note(
915
- `Configured skills: ${result.installedSkills.map((s2) => pc2.bold(s2)).join(", ")}
916
- Target agents: ${result.configuredAgents.map((a) => pc2.cyan(a)).join(", ")}
1235
+ `Configured skills: ${result.installedSkills.map((s2) => pc3.bold(s2)).join(", ")}
1236
+ Target agents: ${result.configuredAgents.map((a) => pc3.cyan(a)).join(", ")}
917
1237
  GrooveSkills Harness: ${harnessStatus}`,
918
1238
  "Installation Summary"
919
1239
  );
920
- p5.outro(pc2.green("Your AI programming pair is now empowered with your team's architectural habits! \u{1F680}"));
1240
+ if (result.harnessInitialized) {
1241
+ const promptRender = formatOnboardingPrompt(selectedLanguage);
1242
+ p5.note(
1243
+ pc3.green(promptRender.prompt),
1244
+ promptRender.title
1245
+ );
1246
+ }
1247
+ p5.outro(pc3.green("Your AI programming pair is now empowered with your team's architectural habits! \u{1F680}"));
921
1248
  }
922
1249
 
923
1250
  // src/main.ts
@@ -927,23 +1254,31 @@ async function runGrooveSkills(options = {}) {
927
1254
  const hasSkills = options.skills && options.skills.length > 0;
928
1255
  const hasAgents = options.agents && options.agents.length > 0;
929
1256
  const isDirectHarness = Boolean(options.harness) && hasAgents;
930
- if (hasSkills && hasAgents || isDirectHarness) {
1257
+ const isDirectInit = Boolean(options.forceOnboarding) && hasAgents;
1258
+ if (hasSkills && hasAgents || isDirectHarness || isDirectInit) {
931
1259
  const language = options.language ?? context.configuredLanguage ?? "es";
932
- await installSkills({
1260
+ const result = await installSkills({
933
1261
  targetPath,
934
1262
  selectedSkillIds: options.skills || [],
935
1263
  selectedAgents: options.agents || [],
936
1264
  forceOverwrite: Boolean(options.yes),
937
1265
  enableHarness: true,
938
1266
  detectedDependencies: context.detectedDependencies,
939
- language
1267
+ language,
1268
+ forceOnboarding: options.forceOnboarding
940
1269
  });
1270
+ if (result.harnessInitialized) {
1271
+ const promptRender = formatOnboardingPrompt(language);
1272
+ console.log(`
1273
+ ${promptRender.formattedOutput}
1274
+ `);
1275
+ }
941
1276
  return;
942
1277
  }
943
- await runInteractiveUi(context);
1278
+ await runInteractiveUi(context, Boolean(options.forceOnboarding));
944
1279
  }
945
1280
 
946
1281
  export {
947
1282
  runGrooveSkills
948
1283
  };
949
- //# sourceMappingURL=chunk-FN4OEDVV.js.map
1284
+ //# sourceMappingURL=chunk-HUG3MG3P.js.map