@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.
- package/README.md +34 -25
- package/dist/{chunk-FN4OEDVV.js → chunk-HUG3MG3P.js} +396 -61
- package/dist/chunk-HUG3MG3P.js.map +1 -0
- package/dist/cli.js +6 -4
- package/dist/cli.js.map +1 -1
- package/dist/main.d.ts +1 -0
- package/dist/main.js +1 -1
- package/package.json +1 -1
- package/skills/auth-rbac-policies/SKILL.md +100 -0
- package/skills/auth-rbac-policies/references/auth-adapters-examples.md +99 -0
- package/skills/caching-rate-limiting/SKILL.md +109 -0
- package/skills/caching-rate-limiting/references/cache-rate-limit-examples.md +129 -0
- package/skills/clean-architecture-ddd/SKILL.md +1 -0
- package/skills/controller-orchestration-queues/SKILL.md +109 -0
- package/skills/controller-orchestration-queues/references/controller-queue-examples.md +115 -0
- package/skills/env-feature-flags/SKILL.md +94 -0
- package/skills/env-feature-flags/references/env-feature-flags-examples.md +93 -0
- package/skills/external-service-adapters/SKILL.md +148 -0
- package/skills/external-service-adapters/references/email-and-payment-examples.md +198 -0
- package/skills/groove-harness/SKILL.md +1 -0
- package/skills/storage-file-adapters/SKILL.md +125 -0
- package/skills/storage-file-adapters/references/storage-adapters-examples.md +164 -0
- package/skills/webhook-idempotency/SKILL.md +104 -0
- package/skills/webhook-idempotency/references/webhook-idempotency-examples.md +112 -0
- package/dist/chunk-FN4OEDVV.js.map +0 -1
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
551
|
-
import { join as
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
678
|
-
import { join as
|
|
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 (!
|
|
957
|
+
if (!existsSync7(filePath)) {
|
|
684
958
|
writeFileSync2(filePath, `${block}
|
|
685
959
|
`, "utf-8");
|
|
686
960
|
return;
|
|
687
961
|
}
|
|
688
|
-
const existingContent =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
722
|
-
|
|
723
|
-
|
|
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 =
|
|
727
|
-
if (!
|
|
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 =
|
|
732
|
-
if (!
|
|
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(
|
|
1025
|
+
const instructions = upsertAgentInstructions(
|
|
1026
|
+
options.targetPath,
|
|
1027
|
+
options.selectedAgents,
|
|
1028
|
+
language
|
|
1029
|
+
);
|
|
737
1030
|
createdFiles.push(...instructions);
|
|
738
|
-
return
|
|
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
|
|
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 (!
|
|
1045
|
+
if (!existsSync9(configPath)) {
|
|
750
1046
|
return fallback;
|
|
751
1047
|
}
|
|
752
1048
|
try {
|
|
753
|
-
const raw =
|
|
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 =
|
|
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
|
|
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(...
|
|
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:
|
|
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(
|
|
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 ?
|
|
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) =>
|
|
877
|
-
Active AI folders: ${context.detectedAgents.map((a) =>
|
|
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(
|
|
913
|
-
const harnessStatus = result.harnessInitialized ?
|
|
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) =>
|
|
916
|
-
Target agents: ${result.configuredAgents.map((a) =>
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
1284
|
+
//# sourceMappingURL=chunk-HUG3MG3P.js.map
|