@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.
- package/README.md +115 -49
- package/dist/{chunk-5Q6O3BRA.js → chunk-M7ZPBGQE.js} +279 -90
- package/dist/chunk-M7ZPBGQE.js.map +1 -0
- package/dist/cli.js +4 -3
- package/dist/cli.js.map +1 -1
- package/dist/main.d.ts +3 -0
- package/dist/main.js +1 -1
- package/package.json +1 -1
- package/skills/clean-architecture-ddd/SKILL.md +103 -0
- package/skills/error-handling-logging/SKILL.md +105 -0
- package/skills/error-handling-logging/templates/app-error.template.md +44 -0
- package/skills/error-handling-logging/templates/logger.template.md +41 -0
- package/skills/groove-harness/SKILL.md +14 -14
- package/skills/odd-ai/SKILL.md +64 -0
- package/skills/odd-ai/references/odd-protocol-examples.md +57 -0
- package/skills/python-clean/SKILL.md +113 -0
- package/skills/python-clean/references/python-tooling-and-types.md +78 -0
- package/skills/rust-clean/SKILL.md +119 -0
- package/skills/rust-clean/references/rust-error-and-clippy.md +92 -0
- package/skills/testing-vitest/SKILL.md +109 -0
- package/skills/typescript-clean/SKILL.md +111 -0
- package/skills/typescript-clean/references/js-ts-antipatterns.md +91 -0
- package/skills/ux-writing/SKILL.md +103 -0
- package/skills/ux-writing/references/copy-patterns-es-en.md +54 -0
- package/skills/valibot-validation/SKILL.md +102 -0
- package/dist/chunk-5Q6O3BRA.js.map +0 -1
|
@@ -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 (
|
|
34
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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 = [
|
|
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
|
|
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-
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
##
|
|
462
|
-
Describe
|
|
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
|
|
486
|
-
- **
|
|
487
|
-
- **
|
|
488
|
-
- **
|
|
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
|
-
##
|
|
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
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
584
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
666
|
-
|
|
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 (
|
|
671
|
-
|
|
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 (
|
|
676
|
-
|
|
868
|
+
if (p6.isCancel(harnessChoice)) {
|
|
869
|
+
p6.cancel("Installation cancelled. Have a wonderful day!");
|
|
677
870
|
process.exit(0);
|
|
678
871
|
}
|
|
679
|
-
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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:
|
|
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(
|
|
704
|
-
|
|
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
|
-
|
|
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-
|
|
926
|
+
//# sourceMappingURL=chunk-M7ZPBGQE.js.map
|