@3lineas/grooveskills 1.5.0 → 1.7.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 +8 -0
- package/dist/{chunk-PLMYUKGW.js → chunk-E63GTCOE.js} +475 -146
- package/dist/chunk-E63GTCOE.js.map +1 -0
- package/dist/cli.js +23 -3
- package/dist/cli.js.map +1 -1
- package/dist/main.js +1 -1
- package/package.json +3 -2
- 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/docs-standards/SKILL.md +113 -0
- package/skills/docs-standards/references/adr-template-and-lifecycle.md +111 -0
- package/skills/docs-standards/references/code-comments-and-docstrings.md +147 -0
- package/skills/docs-standards/references/relative-paths-and-diagrams.md +123 -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/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-PLMYUKGW.js.map +0 -1
|
@@ -94,6 +94,13 @@ var SKILL_CATALOG = [
|
|
|
94
94
|
dependencies: [],
|
|
95
95
|
directoryName: "ux-writing"
|
|
96
96
|
},
|
|
97
|
+
{
|
|
98
|
+
id: "docs-standards",
|
|
99
|
+
name: "Documentation & ADR Standards",
|
|
100
|
+
description: "English documentation standard, relative POSIX links, resilient Mermaid diagrams, ADR lifecycle, and intent-focused docstrings",
|
|
101
|
+
dependencies: [],
|
|
102
|
+
directoryName: "docs-standards"
|
|
103
|
+
},
|
|
97
104
|
{
|
|
98
105
|
id: "typescript-clean",
|
|
99
106
|
name: "TypeScript Clean Code (TS 5.5+)",
|
|
@@ -136,6 +143,55 @@ var SKILL_CATALOG = [
|
|
|
136
143
|
dependencies: [],
|
|
137
144
|
directoryName: "clean-architecture-ddd"
|
|
138
145
|
},
|
|
146
|
+
{
|
|
147
|
+
id: "external-service-adapters",
|
|
148
|
+
name: "External Service Adapters & Wrappers",
|
|
149
|
+
description: "Standardized adapter and wrapper architecture for external services (payment gateways, emails, SMS, cloud storage)",
|
|
150
|
+
dependencies: [],
|
|
151
|
+
directoryName: "external-service-adapters"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "storage-file-adapters",
|
|
155
|
+
name: "Storage & File Adapters (S3, R2, Supabase)",
|
|
156
|
+
description: "Standardized wrapper & adapter architecture for object storage, uploads, presigned URLs, and streaming without repositories",
|
|
157
|
+
dependencies: [],
|
|
158
|
+
directoryName: "storage-file-adapters"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "auth-rbac-policies",
|
|
162
|
+
name: "Auth Adapters & RBAC/ABAC Policies",
|
|
163
|
+
description: "Authentication adapter architecture and pure domain authorization policies (RBAC/ABAC) with AuthenticatedActor contract",
|
|
164
|
+
dependencies: [],
|
|
165
|
+
directoryName: "auth-rbac-policies"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: "controller-orchestration-queues",
|
|
169
|
+
name: "Controller Orchestration & Queue Adapters",
|
|
170
|
+
description: "Strict orchestration rules for Controllers, atomic Actions, DB Transactions, and background Queue Adapters (Cloudflare, BullMQ, SQS)",
|
|
171
|
+
dependencies: [],
|
|
172
|
+
directoryName: "controller-orchestration-queues"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: "webhook-idempotency",
|
|
176
|
+
name: "Webhook Verifiers & Idempotency Guards",
|
|
177
|
+
description: "Incoming webhook HMAC verification adapters and atomic idempotency guards guaranteeing at-most-once processing",
|
|
178
|
+
dependencies: [],
|
|
179
|
+
directoryName: "webhook-idempotency"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: "caching-rate-limiting",
|
|
183
|
+
name: "Cache-Aside & Rate Limiting Adapters",
|
|
184
|
+
description: "Cache-aside remember wrapper and interchangeable sliding-window rate limiter adapters (Redis, Cloudflare KV, In-Memory)",
|
|
185
|
+
dependencies: [],
|
|
186
|
+
directoryName: "caching-rate-limiting"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: "env-feature-flags",
|
|
190
|
+
name: "Fail-Fast Env & Feature Flags Adapters",
|
|
191
|
+
description: "Fail-fast environment schema validation with Valibot and decoupled feature flags adapters (PostHog, LaunchDarkly, Unleash)",
|
|
192
|
+
dependencies: [],
|
|
193
|
+
directoryName: "env-feature-flags"
|
|
194
|
+
},
|
|
139
195
|
{
|
|
140
196
|
id: "testing-vitest",
|
|
141
197
|
name: "Vitest Testing Standards",
|
|
@@ -194,8 +250,16 @@ function detectProjectContext(targetPath) {
|
|
|
194
250
|
"odd-ai",
|
|
195
251
|
"best-practices",
|
|
196
252
|
"ux-writing",
|
|
253
|
+
"docs-standards",
|
|
197
254
|
"error-handling-logging",
|
|
198
|
-
"clean-architecture-ddd"
|
|
255
|
+
"clean-architecture-ddd",
|
|
256
|
+
"external-service-adapters",
|
|
257
|
+
"storage-file-adapters",
|
|
258
|
+
"auth-rbac-policies",
|
|
259
|
+
"controller-orchestration-queues",
|
|
260
|
+
"webhook-idempotency",
|
|
261
|
+
"caching-rate-limiting",
|
|
262
|
+
"env-feature-flags"
|
|
199
263
|
];
|
|
200
264
|
for (const skill of SKILL_CATALOG) {
|
|
201
265
|
if (skill.dependencies.length === 0) {
|
|
@@ -221,7 +285,7 @@ function detectProjectContext(targetPath) {
|
|
|
221
285
|
|
|
222
286
|
// src/domains/ui/ui-orchestrator.ts
|
|
223
287
|
import * as p5 from "@clack/prompts";
|
|
224
|
-
import
|
|
288
|
+
import pc3 from "picocolors";
|
|
225
289
|
|
|
226
290
|
// src/domains/ui/banners.ts
|
|
227
291
|
import pc from "picocolors";
|
|
@@ -326,7 +390,7 @@ function verifySkillExists(targetPath, skillDirectoryName, agents) {
|
|
|
326
390
|
import { join as join11 } from "path";
|
|
327
391
|
|
|
328
392
|
// src/domains/adapters/skill-reference-builder.ts
|
|
329
|
-
import { existsSync as
|
|
393
|
+
import { existsSync as existsSync4, lstatSync as lstatSync2, mkdirSync, readFileSync as readFileSync3, rmSync, unlinkSync } from "fs";
|
|
330
394
|
import { join as join5 } from "path";
|
|
331
395
|
|
|
332
396
|
// src/domains/project-store/project-paths.ts
|
|
@@ -347,9 +411,46 @@ function getProjectConfigPath(targetPath) {
|
|
|
347
411
|
return join4(getProjectStoreDir(targetPath), "config.json");
|
|
348
412
|
}
|
|
349
413
|
|
|
414
|
+
// src/domains/fs/safe-file-writer.ts
|
|
415
|
+
import { chmodSync, existsSync as existsSync3, writeFileSync } from "fs";
|
|
416
|
+
|
|
417
|
+
// src/errors/file-permission-error.ts
|
|
418
|
+
var FilePermissionError = class extends Error {
|
|
419
|
+
code = "EACCES";
|
|
420
|
+
filePath;
|
|
421
|
+
suggestedFixCommand;
|
|
422
|
+
constructor(filePath) {
|
|
423
|
+
super(`No tienes permisos de escritura para modificar '${filePath}'.`);
|
|
424
|
+
this.name = "FilePermissionError";
|
|
425
|
+
this.filePath = filePath;
|
|
426
|
+
this.suggestedFixCommand = `chmod u+w "${filePath}"`;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/domains/fs/safe-file-writer.ts
|
|
431
|
+
function safeWriteFileSync(filePath, content) {
|
|
432
|
+
try {
|
|
433
|
+
writeFileSync(filePath, content, "utf-8");
|
|
434
|
+
} catch (error) {
|
|
435
|
+
const err = error;
|
|
436
|
+
if (err.code === "EACCES" || err.code === "EPERM") {
|
|
437
|
+
if (existsSync3(filePath)) {
|
|
438
|
+
try {
|
|
439
|
+
chmodSync(filePath, 420);
|
|
440
|
+
writeFileSync(filePath, content, "utf-8");
|
|
441
|
+
return;
|
|
442
|
+
} catch {
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
throw new FilePermissionError(filePath);
|
|
446
|
+
}
|
|
447
|
+
throw error;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
350
451
|
// src/domains/adapters/skill-reference-builder.ts
|
|
351
452
|
function removeExistingPathSafely(path) {
|
|
352
|
-
const fileExists =
|
|
453
|
+
const fileExists = existsSync4(path);
|
|
353
454
|
if (!fileExists) {
|
|
354
455
|
try {
|
|
355
456
|
const lstat = lstatSync2(path);
|
|
@@ -375,7 +476,7 @@ function removeExistingPathSafely(path) {
|
|
|
375
476
|
}
|
|
376
477
|
}
|
|
377
478
|
function extractSkillDescription(canonicalSkillMdPath, fallback) {
|
|
378
|
-
if (!
|
|
479
|
+
if (!existsSync4(canonicalSkillMdPath)) {
|
|
379
480
|
return fallback;
|
|
380
481
|
}
|
|
381
482
|
try {
|
|
@@ -389,8 +490,14 @@ function extractSkillDescription(canonicalSkillMdPath, fallback) {
|
|
|
389
490
|
}
|
|
390
491
|
return fallback;
|
|
391
492
|
}
|
|
392
|
-
function writeAgentSkillReference(targetPath, agentRelativeSkillsDir, skill) {
|
|
493
|
+
function writeAgentSkillReference(targetPath, agentRelativeSkillsDir, skill, transaction) {
|
|
393
494
|
const agentSkillFolder = join5(targetPath, agentRelativeSkillsDir, skill.directoryName);
|
|
495
|
+
const targetFile = join5(agentSkillFolder, "SKILL.md");
|
|
496
|
+
if (transaction) {
|
|
497
|
+
transaction.recordCreatedDir(join5(targetPath, agentRelativeSkillsDir));
|
|
498
|
+
transaction.recordCreatedDir(agentSkillFolder);
|
|
499
|
+
transaction.recordBeforeWrite(targetFile);
|
|
500
|
+
}
|
|
394
501
|
removeExistingPathSafely(agentSkillFolder);
|
|
395
502
|
mkdirSync(agentSkillFolder, { recursive: true });
|
|
396
503
|
const canonicalSkillMd = join5(getProjectSkillsDir(targetPath), skill.directoryName, "SKILL.md");
|
|
@@ -409,16 +516,19 @@ Canonical skill specification stored in \`.grooveskills/\`.
|
|
|
409
516
|
|
|
410
517
|
> **Instruction**: Read \`.grooveskills/skills/${skill.directoryName}/SKILL.md\` before executing tasks matching this skill.
|
|
411
518
|
`;
|
|
412
|
-
|
|
413
|
-
writeFileSync(targetFile, referenceContent, "utf-8");
|
|
519
|
+
safeWriteFileSync(targetFile, referenceContent);
|
|
414
520
|
return targetFile;
|
|
415
521
|
}
|
|
416
|
-
function writeCursorRuleReference(targetPath, cursorRelativeRulesDir, skill) {
|
|
522
|
+
function writeCursorRuleReference(targetPath, cursorRelativeRulesDir, skill, transaction) {
|
|
417
523
|
const rulesDir = join5(targetPath, cursorRelativeRulesDir);
|
|
418
|
-
|
|
524
|
+
const targetFile = join5(rulesDir, `${skill.id}.mdc`);
|
|
525
|
+
if (transaction) {
|
|
526
|
+
transaction.recordCreatedDir(rulesDir);
|
|
527
|
+
transaction.recordBeforeWrite(targetFile);
|
|
528
|
+
}
|
|
529
|
+
if (!existsSync4(rulesDir)) {
|
|
419
530
|
mkdirSync(rulesDir, { recursive: true });
|
|
420
531
|
}
|
|
421
|
-
const targetFile = join5(rulesDir, `${skill.id}.mdc`);
|
|
422
532
|
removeExistingPathSafely(targetFile);
|
|
423
533
|
const canonicalSkillMd = join5(getProjectSkillsDir(targetPath), skill.directoryName, "SKILL.md");
|
|
424
534
|
const description = extractSkillDescription(canonicalSkillMd, skill.description);
|
|
@@ -437,7 +547,7 @@ Canonical rule specification stored in \`.grooveskills/\`.
|
|
|
437
547
|
|
|
438
548
|
> **Instruction**: Read \`.grooveskills/skills/${skill.directoryName}/SKILL.md\` before executing tasks matching this rule.
|
|
439
549
|
`;
|
|
440
|
-
|
|
550
|
+
safeWriteFileSync(targetFile, referenceContent);
|
|
441
551
|
return targetFile;
|
|
442
552
|
}
|
|
443
553
|
|
|
@@ -446,8 +556,8 @@ var AntigravityAdapter = class {
|
|
|
446
556
|
id = "antigravity";
|
|
447
557
|
name = "Antigravity";
|
|
448
558
|
targetRelativeDir = ".agents/skills";
|
|
449
|
-
async installSkill(targetPath, skill, _skillSourceDir) {
|
|
450
|
-
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill);
|
|
559
|
+
async installSkill(targetPath, skill, _skillSourceDir, transaction) {
|
|
560
|
+
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill, transaction);
|
|
451
561
|
return [file];
|
|
452
562
|
}
|
|
453
563
|
};
|
|
@@ -457,8 +567,8 @@ var ClaudeAdapter = class {
|
|
|
457
567
|
id = "claude";
|
|
458
568
|
name = "Claude Code";
|
|
459
569
|
targetRelativeDir = ".claude/skills";
|
|
460
|
-
async installSkill(targetPath, skill, _skillSourceDir) {
|
|
461
|
-
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill);
|
|
570
|
+
async installSkill(targetPath, skill, _skillSourceDir, transaction) {
|
|
571
|
+
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill, transaction);
|
|
462
572
|
return [file];
|
|
463
573
|
}
|
|
464
574
|
};
|
|
@@ -468,8 +578,8 @@ var CursorAdapter = class {
|
|
|
468
578
|
id = "cursor";
|
|
469
579
|
name = "Cursor";
|
|
470
580
|
targetRelativeDir = ".cursor/rules";
|
|
471
|
-
async installSkill(targetPath, skill, _skillSourceDir) {
|
|
472
|
-
const file = writeCursorRuleReference(targetPath, this.targetRelativeDir, skill);
|
|
581
|
+
async installSkill(targetPath, skill, _skillSourceDir, transaction) {
|
|
582
|
+
const file = writeCursorRuleReference(targetPath, this.targetRelativeDir, skill, transaction);
|
|
473
583
|
return [file];
|
|
474
584
|
}
|
|
475
585
|
};
|
|
@@ -479,35 +589,35 @@ var GeminiAdapter = class {
|
|
|
479
589
|
id = "gemini";
|
|
480
590
|
name = "Gemini Code Assist";
|
|
481
591
|
targetRelativeDir = ".gemini/skills";
|
|
482
|
-
async installSkill(targetPath, skill, _skillSourceDir) {
|
|
483
|
-
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill);
|
|
592
|
+
async installSkill(targetPath, skill, _skillSourceDir, transaction) {
|
|
593
|
+
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill, transaction);
|
|
484
594
|
return [file];
|
|
485
595
|
}
|
|
486
596
|
};
|
|
487
597
|
|
|
488
598
|
// src/domains/project-store/sync-project-store.ts
|
|
489
|
-
import { cpSync, existsSync as
|
|
599
|
+
import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync2, rmSync as rmSync2 } from "fs";
|
|
490
600
|
import { join as join7 } from "path";
|
|
491
601
|
|
|
492
602
|
// src/domains/installer/resolve-skills-source.ts
|
|
493
603
|
import { fileURLToPath } from "url";
|
|
494
604
|
import { dirname, resolve, join as join6 } from "path";
|
|
495
|
-
import { existsSync as
|
|
605
|
+
import { existsSync as existsSync5 } from "fs";
|
|
496
606
|
function resolveSkillsSourceRoot() {
|
|
497
607
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
498
608
|
const currentDir = dirname(currentFilePath);
|
|
499
609
|
const candidateFromDist = resolve(currentDir, "../skills");
|
|
500
|
-
if (
|
|
610
|
+
if (existsSync5(candidateFromDist)) {
|
|
501
611
|
return candidateFromDist;
|
|
502
612
|
}
|
|
503
613
|
const candidateFromSrc = resolve(currentDir, "../../../skills");
|
|
504
|
-
if (
|
|
614
|
+
if (existsSync5(candidateFromSrc)) {
|
|
505
615
|
return candidateFromSrc;
|
|
506
616
|
}
|
|
507
617
|
let searchDir = currentDir;
|
|
508
618
|
for (let i = 0; i < 5; i++) {
|
|
509
619
|
const candidate = join6(searchDir, "skills");
|
|
510
|
-
if (
|
|
620
|
+
if (existsSync5(candidate)) {
|
|
511
621
|
return candidate;
|
|
512
622
|
}
|
|
513
623
|
const parent = dirname(searchDir);
|
|
@@ -522,14 +632,17 @@ function resolveSkillsSourceRoot() {
|
|
|
522
632
|
}
|
|
523
633
|
|
|
524
634
|
// src/domains/project-store/sync-project-store.ts
|
|
525
|
-
function syncProjectStore(targetPath, selectedSkillIds, _selectedAgents) {
|
|
635
|
+
function syncProjectStore(targetPath, selectedSkillIds, _selectedAgents, transaction) {
|
|
526
636
|
const bundledSkillsDir = resolveSkillsSourceRoot();
|
|
527
637
|
const projectSkillsDir = getProjectSkillsDir(targetPath);
|
|
528
|
-
if (
|
|
638
|
+
if (transaction) {
|
|
639
|
+
transaction.recordCreatedDir(projectSkillsDir);
|
|
640
|
+
}
|
|
641
|
+
if (!existsSync6(projectSkillsDir)) {
|
|
529
642
|
mkdirSync2(projectSkillsDir, { recursive: true });
|
|
530
643
|
}
|
|
531
644
|
const legacyCursorRules = join7(targetPath, ".grooveskills", "cursor-rules");
|
|
532
|
-
if (
|
|
645
|
+
if (existsSync6(legacyCursorRules)) {
|
|
533
646
|
try {
|
|
534
647
|
rmSync2(legacyCursorRules, { recursive: true, force: true });
|
|
535
648
|
} catch {
|
|
@@ -542,14 +655,17 @@ function syncProjectStore(targetPath, selectedSkillIds, _selectedAgents) {
|
|
|
542
655
|
}
|
|
543
656
|
const sourceSkillDir = join7(bundledSkillsDir, skill.directoryName);
|
|
544
657
|
const destSkillDir = join7(projectSkillsDir, skill.directoryName);
|
|
545
|
-
if (
|
|
658
|
+
if (transaction) {
|
|
659
|
+
transaction.recordCreatedDir(destSkillDir);
|
|
660
|
+
}
|
|
661
|
+
if (existsSync6(sourceSkillDir)) {
|
|
546
662
|
cpSync(sourceSkillDir, destSkillDir, { recursive: true });
|
|
547
663
|
}
|
|
548
664
|
}
|
|
549
665
|
}
|
|
550
666
|
|
|
551
667
|
// src/domains/harness/install-harness.ts
|
|
552
|
-
import { existsSync as
|
|
668
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3 } from "fs";
|
|
553
669
|
import { join as join10 } from "path";
|
|
554
670
|
|
|
555
671
|
// src/domains/harness/harness-languages.ts
|
|
@@ -558,31 +674,41 @@ var SUPPORTED_LANGUAGES = {
|
|
|
558
674
|
code: "es",
|
|
559
675
|
name: "Spanish",
|
|
560
676
|
nativeName: "Espa\xF1ol",
|
|
561
|
-
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."
|
|
677
|
+
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.",
|
|
678
|
+
onboardingPromptTitle: "Prompt de inicializaci\xF3n para tu Agente IA (Copia y pega en el chat):",
|
|
679
|
+
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."
|
|
562
680
|
},
|
|
563
681
|
en: {
|
|
564
682
|
code: "en",
|
|
565
683
|
name: "English",
|
|
566
684
|
nativeName: "English",
|
|
567
|
-
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."
|
|
685
|
+
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.",
|
|
686
|
+
onboardingPromptTitle: "Initialization prompt for your AI Agent (Copy and paste into your chat):",
|
|
687
|
+
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."
|
|
568
688
|
},
|
|
569
689
|
pt: {
|
|
570
690
|
code: "pt",
|
|
571
691
|
name: "Portuguese",
|
|
572
692
|
nativeName: "Portugu\xEAs",
|
|
573
|
-
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."
|
|
693
|
+
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.",
|
|
694
|
+
onboardingPromptTitle: "Prompt de inicializa\xE7\xE3o para o seu Agente de IA (Copie e cole no chat):",
|
|
695
|
+
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."
|
|
574
696
|
},
|
|
575
697
|
it: {
|
|
576
698
|
code: "it",
|
|
577
699
|
name: "Italian",
|
|
578
700
|
nativeName: "Italiano",
|
|
579
|
-
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."
|
|
701
|
+
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.",
|
|
702
|
+
onboardingPromptTitle: "Prompt di inizializzazione per il tuo Agente AI (Copia e incolla nella chat):",
|
|
703
|
+
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."
|
|
580
704
|
},
|
|
581
705
|
fr: {
|
|
582
706
|
code: "fr",
|
|
583
707
|
name: "French",
|
|
584
708
|
nativeName: "Fran\xE7ais",
|
|
585
|
-
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."
|
|
709
|
+
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.",
|
|
710
|
+
onboardingPromptTitle: "Prompt d'initialisation pour votre Agent IA (Copiez et collez dans le chat) :",
|
|
711
|
+
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."
|
|
586
712
|
}
|
|
587
713
|
};
|
|
588
714
|
|
|
@@ -664,20 +790,23 @@ createdAt: ${now}
|
|
|
664
790
|
# 00 \u2014 Initial Project Onboarding & Architecture Exploration: ${projectName}
|
|
665
791
|
|
|
666
792
|
## Outcome Hypothesis
|
|
667
|
-
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
|
|
793
|
+
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.
|
|
668
794
|
|
|
669
795
|
## Scope
|
|
670
|
-
- In scope: Inspect project manifests, entry points, directory conventions, and script execution.
|
|
671
|
-
- Out of scope: Refactoring business logic or modifying functional code.
|
|
796
|
+
- In scope: Inspect project manifests, entry points, directory conventions, ask interactive alignment questions, and verify script execution.
|
|
797
|
+
- Out of scope: Refactoring business logic or modifying functional code before alignment.
|
|
672
798
|
|
|
673
799
|
## Checklist
|
|
674
800
|
- [ ] Verify package manager and essential scripts (\`build\`, \`test\`, \`typecheck\`).
|
|
675
801
|
- [ ] Map high-level module architecture and data flow in \`.grooveskills/memory/context.md\`.
|
|
802
|
+
- [ ] Ask the user step-by-step interactive questions to clarify goals, domain scope, and adapt project conventions.
|
|
803
|
+
- [ ] Document agreed technical decisions in \`.grooveskills/memory/decisions.md\`.
|
|
676
804
|
- [ ] Verify test runner or static checks pass cleanly.
|
|
677
805
|
- [ ] Switch status to \`completed\` once project context is fully verified.
|
|
678
806
|
|
|
679
807
|
## Evidence
|
|
680
808
|
- Observed command runs and outputs during the initial onboarding pass.
|
|
809
|
+
- User answers to alignment questions captured in memory.
|
|
681
810
|
|
|
682
811
|
## Next
|
|
683
812
|
- Once onboarding is marked completed, proceed with domain tasks following the 3-lane ODD protocol.
|
|
@@ -736,11 +865,11 @@ This project uses the **GrooveSkills Harness** located in \`.grooveskills/\`.
|
|
|
736
865
|
}
|
|
737
866
|
|
|
738
867
|
// src/domains/harness/scan-project-onboarding.ts
|
|
739
|
-
import { existsSync as
|
|
868
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
740
869
|
import { basename, join as join8 } from "path";
|
|
741
870
|
function detectProjectName(targetPath) {
|
|
742
871
|
const packageJsonPath = join8(targetPath, "package.json");
|
|
743
|
-
if (
|
|
872
|
+
if (existsSync7(packageJsonPath)) {
|
|
744
873
|
try {
|
|
745
874
|
const raw = readFileSync4(packageJsonPath, "utf-8");
|
|
746
875
|
const parsed = JSON.parse(raw);
|
|
@@ -751,7 +880,7 @@ function detectProjectName(targetPath) {
|
|
|
751
880
|
}
|
|
752
881
|
}
|
|
753
882
|
const cargoPath = join8(targetPath, "Cargo.toml");
|
|
754
|
-
if (
|
|
883
|
+
if (existsSync7(cargoPath)) {
|
|
755
884
|
try {
|
|
756
885
|
const cargoRaw = readFileSync4(cargoPath, "utf-8");
|
|
757
886
|
const nameMatch = cargoRaw.match(/name\s*=\s*"([^"]+)"/);
|
|
@@ -762,7 +891,7 @@ function detectProjectName(targetPath) {
|
|
|
762
891
|
}
|
|
763
892
|
}
|
|
764
893
|
const pyprojectPath = join8(targetPath, "pyproject.toml");
|
|
765
|
-
if (
|
|
894
|
+
if (existsSync7(pyprojectPath)) {
|
|
766
895
|
try {
|
|
767
896
|
const pyRaw = readFileSync4(pyprojectPath, "utf-8");
|
|
768
897
|
const pyMatch = pyRaw.match(/name\s*=\s*"([^"]+)"/);
|
|
@@ -775,32 +904,32 @@ function detectProjectName(targetPath) {
|
|
|
775
904
|
return basename(targetPath) || "project";
|
|
776
905
|
}
|
|
777
906
|
function detectPackageManager(targetPath) {
|
|
778
|
-
if (
|
|
907
|
+
if (existsSync7(join8(targetPath, "pnpm-lock.yaml"))) {
|
|
779
908
|
return "pnpm";
|
|
780
909
|
}
|
|
781
|
-
if (
|
|
910
|
+
if (existsSync7(join8(targetPath, "bun.lockb")) || existsSync7(join8(targetPath, "bun.lock"))) {
|
|
782
911
|
return "bun";
|
|
783
912
|
}
|
|
784
|
-
if (
|
|
913
|
+
if (existsSync7(join8(targetPath, "yarn.lock"))) {
|
|
785
914
|
return "yarn";
|
|
786
915
|
}
|
|
787
|
-
if (
|
|
916
|
+
if (existsSync7(join8(targetPath, "package-lock.json"))) {
|
|
788
917
|
return "npm";
|
|
789
918
|
}
|
|
790
|
-
if (
|
|
919
|
+
if (existsSync7(join8(targetPath, "Cargo.lock")) || existsSync7(join8(targetPath, "Cargo.toml"))) {
|
|
791
920
|
return "cargo";
|
|
792
921
|
}
|
|
793
|
-
if (
|
|
922
|
+
if (existsSync7(join8(targetPath, "uv.lock"))) {
|
|
794
923
|
return "uv";
|
|
795
924
|
}
|
|
796
|
-
if (
|
|
925
|
+
if (existsSync7(join8(targetPath, "poetry.lock"))) {
|
|
797
926
|
return "poetry";
|
|
798
927
|
}
|
|
799
928
|
return "npm";
|
|
800
929
|
}
|
|
801
930
|
function detectCommands(targetPath, pkgManager) {
|
|
802
931
|
const packageJsonPath = join8(targetPath, "package.json");
|
|
803
|
-
if (!
|
|
932
|
+
if (!existsSync7(packageJsonPath)) {
|
|
804
933
|
return {};
|
|
805
934
|
}
|
|
806
935
|
try {
|
|
@@ -835,7 +964,7 @@ function detectCommands(targetPath, pkgManager) {
|
|
|
835
964
|
}
|
|
836
965
|
function detectPathAliases(targetPath) {
|
|
837
966
|
const tsconfigPath = join8(targetPath, "tsconfig.json");
|
|
838
|
-
if (!
|
|
967
|
+
if (!existsSync7(tsconfigPath)) {
|
|
839
968
|
return {};
|
|
840
969
|
}
|
|
841
970
|
try {
|
|
@@ -855,7 +984,7 @@ function detectPathAliases(targetPath) {
|
|
|
855
984
|
}
|
|
856
985
|
function detectSourceDirectories(targetPath) {
|
|
857
986
|
const srcPath = join8(targetPath, "src");
|
|
858
|
-
const scanTarget =
|
|
987
|
+
const scanTarget = existsSync7(srcPath) ? srcPath : targetPath;
|
|
859
988
|
try {
|
|
860
989
|
const entries = readdirSync2(scanTarget, { withFileTypes: true });
|
|
861
990
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !name.startsWith(".") && name !== "node_modules" && name !== "dist");
|
|
@@ -879,36 +1008,39 @@ function scanProjectOnboarding(targetPath) {
|
|
|
879
1008
|
}
|
|
880
1009
|
|
|
881
1010
|
// src/domains/harness/upsert-agent-instructions.ts
|
|
882
|
-
import { existsSync as
|
|
1011
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
883
1012
|
import { join as join9 } from "path";
|
|
884
1013
|
var START_MARKER = "<!-- grooveskills:harness:start -->";
|
|
885
1014
|
var END_MARKER = "<!-- grooveskills:harness:end -->";
|
|
886
|
-
function applyHarnessBlock(filePath, language = "es") {
|
|
1015
|
+
function applyHarnessBlock(filePath, language = "es", transaction) {
|
|
1016
|
+
if (transaction) {
|
|
1017
|
+
transaction.recordBeforeWrite(filePath);
|
|
1018
|
+
}
|
|
887
1019
|
const block = buildAgentHarnessBlock(language);
|
|
888
|
-
if (!
|
|
889
|
-
|
|
890
|
-
|
|
1020
|
+
if (!existsSync8(filePath)) {
|
|
1021
|
+
safeWriteFileSync(filePath, `${block}
|
|
1022
|
+
`);
|
|
891
1023
|
return;
|
|
892
1024
|
}
|
|
893
1025
|
const existingContent = readFileSync5(filePath, "utf-8");
|
|
894
1026
|
if (existingContent.includes(START_MARKER) && existingContent.includes(END_MARKER)) {
|
|
895
1027
|
const regex = new RegExp(`${START_MARKER}[\\s\\S]*?${END_MARKER}`, "g");
|
|
896
1028
|
const updated = existingContent.replace(regex, block);
|
|
897
|
-
|
|
1029
|
+
safeWriteFileSync(filePath, updated);
|
|
898
1030
|
return;
|
|
899
1031
|
}
|
|
900
1032
|
const separator = existingContent.endsWith("\n") ? "\n" : "\n\n";
|
|
901
|
-
|
|
902
|
-
|
|
1033
|
+
safeWriteFileSync(filePath, `${existingContent}${separator}${block}
|
|
1034
|
+
`);
|
|
903
1035
|
}
|
|
904
|
-
function upsertAgentInstructions(targetPath, selectedAgents, language = "es") {
|
|
1036
|
+
function upsertAgentInstructions(targetPath, selectedAgents, language = "es", transaction) {
|
|
905
1037
|
const updatedFiles = [];
|
|
906
1038
|
const agentsMdPath = join9(targetPath, "AGENTS.md");
|
|
907
|
-
applyHarnessBlock(agentsMdPath, language);
|
|
1039
|
+
applyHarnessBlock(agentsMdPath, language, transaction);
|
|
908
1040
|
updatedFiles.push(agentsMdPath);
|
|
909
1041
|
if (selectedAgents.includes("claude")) {
|
|
910
1042
|
const claudeMdPath = join9(targetPath, "CLAUDE.md");
|
|
911
|
-
applyHarnessBlock(claudeMdPath, language);
|
|
1043
|
+
applyHarnessBlock(claudeMdPath, language, transaction);
|
|
912
1044
|
updatedFiles.push(claudeMdPath);
|
|
913
1045
|
}
|
|
914
1046
|
return updatedFiles;
|
|
@@ -920,57 +1052,77 @@ function installHarness(options) {
|
|
|
920
1052
|
const tasksDir = getProjectTasksDir(options.targetPath);
|
|
921
1053
|
const language = options.language ?? "es";
|
|
922
1054
|
const forceOnboarding = Boolean(options.forceOnboarding);
|
|
923
|
-
|
|
1055
|
+
const tx = options.transaction;
|
|
1056
|
+
if (tx) {
|
|
1057
|
+
tx.recordCreatedDir(memoryDir);
|
|
1058
|
+
tx.recordCreatedDir(tasksDir);
|
|
1059
|
+
}
|
|
1060
|
+
if (!existsSync9(tasksDir)) {
|
|
924
1061
|
mkdirSync3(tasksDir, { recursive: true });
|
|
925
1062
|
}
|
|
926
1063
|
const createdFiles = [];
|
|
927
1064
|
const contextPath = join10(memoryDir, "context.md");
|
|
928
|
-
const shouldWriteContext = !
|
|
1065
|
+
const shouldWriteContext = !existsSync9(contextPath) || forceOnboarding;
|
|
929
1066
|
if (shouldWriteContext) {
|
|
1067
|
+
if (tx) {
|
|
1068
|
+
tx.recordBeforeWrite(contextPath);
|
|
1069
|
+
}
|
|
930
1070
|
const profile = scanProjectOnboarding(options.targetPath);
|
|
931
1071
|
const contextContent = buildContextTemplate(
|
|
932
1072
|
options.detectedDependencies,
|
|
933
1073
|
language,
|
|
934
1074
|
profile
|
|
935
1075
|
);
|
|
936
|
-
|
|
1076
|
+
safeWriteFileSync(contextPath, contextContent);
|
|
937
1077
|
createdFiles.push(contextPath);
|
|
938
1078
|
const onboardingTaskPath = join10(tasksDir, "00-onboarding.md");
|
|
939
|
-
|
|
1079
|
+
if (tx) {
|
|
1080
|
+
tx.recordBeforeWrite(onboardingTaskPath);
|
|
1081
|
+
}
|
|
1082
|
+
safeWriteFileSync(
|
|
940
1083
|
onboardingTaskPath,
|
|
941
|
-
buildOnboardingTaskTemplate(profile.projectName)
|
|
942
|
-
"utf-8"
|
|
1084
|
+
buildOnboardingTaskTemplate(profile.projectName)
|
|
943
1085
|
);
|
|
944
1086
|
createdFiles.push(onboardingTaskPath);
|
|
945
1087
|
}
|
|
946
1088
|
const decisionsPath = join10(memoryDir, "decisions.md");
|
|
947
|
-
if (!
|
|
948
|
-
|
|
1089
|
+
if (!existsSync9(decisionsPath)) {
|
|
1090
|
+
if (tx) {
|
|
1091
|
+
tx.recordBeforeWrite(decisionsPath);
|
|
1092
|
+
}
|
|
1093
|
+
safeWriteFileSync(decisionsPath, buildDecisionsTemplate(language));
|
|
949
1094
|
createdFiles.push(decisionsPath);
|
|
950
1095
|
}
|
|
951
1096
|
const templateTaskPath = join10(tasksDir, "_template.md");
|
|
952
|
-
if (!
|
|
953
|
-
|
|
1097
|
+
if (!existsSync9(templateTaskPath)) {
|
|
1098
|
+
if (tx) {
|
|
1099
|
+
tx.recordBeforeWrite(templateTaskPath);
|
|
1100
|
+
}
|
|
1101
|
+
safeWriteFileSync(templateTaskPath, buildTaskTemplate());
|
|
954
1102
|
createdFiles.push(templateTaskPath);
|
|
955
1103
|
}
|
|
956
1104
|
const instructions = upsertAgentInstructions(
|
|
957
1105
|
options.targetPath,
|
|
958
1106
|
options.selectedAgents,
|
|
959
|
-
language
|
|
1107
|
+
language,
|
|
1108
|
+
tx
|
|
960
1109
|
);
|
|
961
1110
|
createdFiles.push(...instructions);
|
|
962
|
-
return
|
|
1111
|
+
return {
|
|
1112
|
+
createdFiles,
|
|
1113
|
+
onboardingInitialized: shouldWriteContext
|
|
1114
|
+
};
|
|
963
1115
|
}
|
|
964
1116
|
|
|
965
1117
|
// src/domains/harness/write-project-config.ts
|
|
966
|
-
import { existsSync as
|
|
1118
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
967
1119
|
function readExistingConfig(configPath) {
|
|
968
1120
|
const fallback = {
|
|
969
1121
|
skills: [],
|
|
970
1122
|
agents: [],
|
|
971
1123
|
harness: false
|
|
972
1124
|
};
|
|
973
|
-
if (!
|
|
1125
|
+
if (!existsSync10(configPath)) {
|
|
974
1126
|
return fallback;
|
|
975
1127
|
}
|
|
976
1128
|
try {
|
|
@@ -986,8 +1138,11 @@ function readExistingConfig(configPath) {
|
|
|
986
1138
|
return fallback;
|
|
987
1139
|
}
|
|
988
1140
|
}
|
|
989
|
-
function writeProjectConfig(targetPath, newSkills, newAgents, harnessEnabled, language) {
|
|
1141
|
+
function writeProjectConfig(targetPath, newSkills, newAgents, harnessEnabled, language, transaction) {
|
|
990
1142
|
const configPath = getProjectConfigPath(targetPath);
|
|
1143
|
+
if (transaction) {
|
|
1144
|
+
transaction.recordBeforeWrite(configPath);
|
|
1145
|
+
}
|
|
991
1146
|
const existing = readExistingConfig(configPath);
|
|
992
1147
|
const mergedSkills = Array.from(/* @__PURE__ */ new Set([...existing.skills, ...newSkills]));
|
|
993
1148
|
const mergedAgents = Array.from(/* @__PURE__ */ new Set([...existing.agents, ...newAgents]));
|
|
@@ -1001,11 +1156,84 @@ function writeProjectConfig(targetPath, newSkills, newAgents, harnessEnabled, la
|
|
|
1001
1156
|
skills: mergedSkills,
|
|
1002
1157
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1003
1158
|
};
|
|
1004
|
-
|
|
1005
|
-
|
|
1159
|
+
safeWriteFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
1160
|
+
`);
|
|
1006
1161
|
return configPath;
|
|
1007
1162
|
}
|
|
1008
1163
|
|
|
1164
|
+
// src/domains/fs/install-transaction.ts
|
|
1165
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync7, rmSync as rmSync3 } from "fs";
|
|
1166
|
+
var InstallTransaction = class {
|
|
1167
|
+
originalFiles = /* @__PURE__ */ new Map();
|
|
1168
|
+
createdDirs = [];
|
|
1169
|
+
recordBeforeWrite(filePath) {
|
|
1170
|
+
if (this.originalFiles.has(filePath)) {
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
if (existsSync11(filePath)) {
|
|
1174
|
+
try {
|
|
1175
|
+
this.originalFiles.set(filePath, readFileSync7(filePath, "utf-8"));
|
|
1176
|
+
} catch {
|
|
1177
|
+
this.originalFiles.set(filePath, null);
|
|
1178
|
+
}
|
|
1179
|
+
} else {
|
|
1180
|
+
this.originalFiles.set(filePath, null);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
recordCreatedDir(dirPath) {
|
|
1184
|
+
if (!existsSync11(dirPath) && !this.createdDirs.includes(dirPath)) {
|
|
1185
|
+
this.createdDirs.push(dirPath);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
rollback() {
|
|
1189
|
+
const restoredFiles = [];
|
|
1190
|
+
const removedFiles = [];
|
|
1191
|
+
const removedDirs = [];
|
|
1192
|
+
for (const [filePath, content] of this.originalFiles.entries()) {
|
|
1193
|
+
try {
|
|
1194
|
+
if (content === null) {
|
|
1195
|
+
if (existsSync11(filePath)) {
|
|
1196
|
+
rmSync3(filePath, { force: true });
|
|
1197
|
+
removedFiles.push(filePath);
|
|
1198
|
+
}
|
|
1199
|
+
} else {
|
|
1200
|
+
safeWriteFileSync(filePath, content);
|
|
1201
|
+
restoredFiles.push(filePath);
|
|
1202
|
+
}
|
|
1203
|
+
} catch {
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
const sortedDirs = [...this.createdDirs].sort((a, b) => b.length - a.length);
|
|
1207
|
+
for (const dir of sortedDirs) {
|
|
1208
|
+
try {
|
|
1209
|
+
if (existsSync11(dir) && readdirSync3(dir).length === 0) {
|
|
1210
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
1211
|
+
removedDirs.push(dir);
|
|
1212
|
+
}
|
|
1213
|
+
} catch {
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return { restoredFiles, removedFiles, removedDirs };
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
// src/errors/install-transaction-error.ts
|
|
1221
|
+
var InstallTransactionError = class extends Error {
|
|
1222
|
+
stage;
|
|
1223
|
+
causeError;
|
|
1224
|
+
rollbackReport;
|
|
1225
|
+
suggestedFix;
|
|
1226
|
+
constructor(options) {
|
|
1227
|
+
const rawMessage = options.cause instanceof Error ? options.cause.message : String(options.cause);
|
|
1228
|
+
super(`Error durante la etapa '${options.stage}': ${rawMessage}`);
|
|
1229
|
+
this.name = "InstallTransactionError";
|
|
1230
|
+
this.stage = options.stage;
|
|
1231
|
+
this.causeError = options.cause;
|
|
1232
|
+
this.rollbackReport = options.rollbackReport;
|
|
1233
|
+
this.suggestedFix = options.suggestedFix;
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
|
|
1009
1237
|
// src/domains/installer/install-orchestrator.ts
|
|
1010
1238
|
var ADAPTER_REGISTRY = {
|
|
1011
1239
|
antigravity: new AntigravityAdapter(),
|
|
@@ -1013,7 +1241,20 @@ var ADAPTER_REGISTRY = {
|
|
|
1013
1241
|
cursor: new CursorAdapter(),
|
|
1014
1242
|
gemini: new GeminiAdapter()
|
|
1015
1243
|
};
|
|
1244
|
+
function formatSuggestedFix(cause) {
|
|
1245
|
+
if (cause instanceof FilePermissionError) {
|
|
1246
|
+
return cause.suggestedFixCommand;
|
|
1247
|
+
}
|
|
1248
|
+
const err = cause;
|
|
1249
|
+
if (err?.code === "EACCES" || err?.code === "EPERM") {
|
|
1250
|
+
if (err.path) {
|
|
1251
|
+
return `chmod u+w "${err.path}"`;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return void 0;
|
|
1255
|
+
}
|
|
1016
1256
|
async function installSkills(options) {
|
|
1257
|
+
const transaction = new InstallTransaction();
|
|
1017
1258
|
const skillsToInstall = [...options.selectedSkillIds];
|
|
1018
1259
|
if (!skillsToInstall.includes("groove-harness")) {
|
|
1019
1260
|
skillsToInstall.push("groove-harness");
|
|
@@ -1021,47 +1262,90 @@ async function installSkills(options) {
|
|
|
1021
1262
|
if (!skillsToInstall.includes("odd-ai")) {
|
|
1022
1263
|
skillsToInstall.push("odd-ai");
|
|
1023
1264
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
for (const agentId of options.selectedAgents) {
|
|
1034
|
-
const adapter = ADAPTER_REGISTRY[agentId];
|
|
1035
|
-
if (!adapter) {
|
|
1265
|
+
let currentStage = "Sincronizaci\xF3n del almac\xE9n de skills (.grooveskills/skills)";
|
|
1266
|
+
try {
|
|
1267
|
+
syncProjectStore(options.targetPath, skillsToInstall, options.selectedAgents, transaction);
|
|
1268
|
+
const projectSkillsDir = getProjectSkillsDir(options.targetPath);
|
|
1269
|
+
const createdFiles = [];
|
|
1270
|
+
currentStage = "Configuraci\xF3n de referencias de agentes (.agents, .claude, etc.)";
|
|
1271
|
+
for (const skillId of skillsToInstall) {
|
|
1272
|
+
const skill = SKILL_CATALOG.find((item) => item.id === skillId);
|
|
1273
|
+
if (!skill) {
|
|
1036
1274
|
continue;
|
|
1037
1275
|
}
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1276
|
+
const skillSourceDir = join11(projectSkillsDir, skill.directoryName);
|
|
1277
|
+
for (const agentId of options.selectedAgents) {
|
|
1278
|
+
const adapter = ADAPTER_REGISTRY[agentId];
|
|
1279
|
+
if (!adapter) {
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
const files = await adapter.installSkill(
|
|
1283
|
+
options.targetPath,
|
|
1284
|
+
skill,
|
|
1285
|
+
skillSourceDir,
|
|
1286
|
+
transaction
|
|
1287
|
+
);
|
|
1288
|
+
createdFiles.push(...files);
|
|
1289
|
+
}
|
|
1040
1290
|
}
|
|
1291
|
+
currentStage = "Instalaci\xF3n del Groove Harness (contexto, decisiones, tareas e instrucciones)";
|
|
1292
|
+
const harnessResult = installHarness({
|
|
1293
|
+
targetPath: options.targetPath,
|
|
1294
|
+
selectedAgents: options.selectedAgents,
|
|
1295
|
+
selectedSkillIds: skillsToInstall,
|
|
1296
|
+
detectedDependencies: options.detectedDependencies || [],
|
|
1297
|
+
language: options.language,
|
|
1298
|
+
forceOnboarding: options.forceOnboarding,
|
|
1299
|
+
transaction
|
|
1300
|
+
});
|
|
1301
|
+
createdFiles.push(...harnessResult.createdFiles);
|
|
1302
|
+
currentStage = "Persistencia de la configuraci\xF3n del proyecto (.grooveskills/config.json)";
|
|
1303
|
+
const configPath = writeProjectConfig(
|
|
1304
|
+
options.targetPath,
|
|
1305
|
+
skillsToInstall,
|
|
1306
|
+
options.selectedAgents,
|
|
1307
|
+
true,
|
|
1308
|
+
options.language,
|
|
1309
|
+
transaction
|
|
1310
|
+
);
|
|
1311
|
+
createdFiles.push(configPath);
|
|
1312
|
+
return {
|
|
1313
|
+
installedSkills: skillsToInstall,
|
|
1314
|
+
configuredAgents: options.selectedAgents,
|
|
1315
|
+
createdFiles,
|
|
1316
|
+
harnessInitialized: harnessResult.onboardingInitialized,
|
|
1317
|
+
language: options.language
|
|
1318
|
+
};
|
|
1319
|
+
} catch (cause) {
|
|
1320
|
+
const rollbackReport = transaction.rollback();
|
|
1321
|
+
const suggestedFix = formatSuggestedFix(cause);
|
|
1322
|
+
throw new InstallTransactionError({
|
|
1323
|
+
stage: currentStage,
|
|
1324
|
+
cause,
|
|
1325
|
+
rollbackReport,
|
|
1326
|
+
suggestedFix
|
|
1327
|
+
});
|
|
1041
1328
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
);
|
|
1058
|
-
createdFiles.push(configPath);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// src/domains/ui/render-onboarding-prompt.ts
|
|
1332
|
+
import pc2 from "picocolors";
|
|
1333
|
+
function formatOnboardingPrompt(language = "es") {
|
|
1334
|
+
const langConfig = SUPPORTED_LANGUAGES[language] ?? SUPPORTED_LANGUAGES.es;
|
|
1335
|
+
const title = langConfig.onboardingPromptTitle;
|
|
1336
|
+
const prompt = langConfig.onboardingPrompt;
|
|
1337
|
+
const separator = pc2.cyan("\u2500".repeat(60));
|
|
1338
|
+
const formattedOutput = [
|
|
1339
|
+
separator,
|
|
1340
|
+
pc2.bold(pc2.yellow(title)),
|
|
1341
|
+
"",
|
|
1342
|
+
pc2.green(prompt),
|
|
1343
|
+
separator
|
|
1344
|
+
].join("\n");
|
|
1059
1345
|
return {
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
harnessInitialized: true,
|
|
1064
|
-
language: options.language
|
|
1346
|
+
title,
|
|
1347
|
+
prompt,
|
|
1348
|
+
formattedOutput
|
|
1065
1349
|
};
|
|
1066
1350
|
}
|
|
1067
1351
|
|
|
@@ -1072,7 +1356,7 @@ async function resolveLanguageSelection(harnessExists, configuredLanguage, force
|
|
|
1072
1356
|
}
|
|
1073
1357
|
const langChoice = await promptLanguage(configuredLanguage ?? "es");
|
|
1074
1358
|
if (p5.isCancel(langChoice)) {
|
|
1075
|
-
p5.cancel("
|
|
1359
|
+
p5.cancel("Instalaci\xF3n cancelada. \xA1Que tengas un excelente d\xEDa!");
|
|
1076
1360
|
process.exit(0);
|
|
1077
1361
|
}
|
|
1078
1362
|
return langChoice;
|
|
@@ -1087,31 +1371,53 @@ async function resolveOverwriteConfirmation(targetPath, selectedSkills, selected
|
|
|
1087
1371
|
}
|
|
1088
1372
|
const confirm2 = await promptConfirmOverwrite();
|
|
1089
1373
|
if (p5.isCancel(confirm2) || !confirm2) {
|
|
1090
|
-
p5.cancel("
|
|
1374
|
+
p5.cancel("Instalaci\xF3n detenida para proteger las skills existentes.");
|
|
1091
1375
|
process.exit(0);
|
|
1092
1376
|
}
|
|
1093
1377
|
return Boolean(confirm2);
|
|
1094
1378
|
}
|
|
1379
|
+
function renderInstallFailure(error) {
|
|
1380
|
+
const { stage, causeError, rollbackReport, suggestedFix } = error;
|
|
1381
|
+
const rawMsg = causeError instanceof Error ? causeError.message : String(causeError);
|
|
1382
|
+
const lines = [
|
|
1383
|
+
`${pc3.bold("Fase afectada:")} ${pc3.yellow(stage)}`,
|
|
1384
|
+
`${pc3.bold("Causa:")} ${pc3.red(rawMsg)}`,
|
|
1385
|
+
"",
|
|
1386
|
+
pc3.bold("Reporte de Rollback autom\xE1tico:"),
|
|
1387
|
+
` \u2022 Archivos restaurados a su estado previo: ${pc3.cyan(String(rollbackReport.restoredFiles.length))}`,
|
|
1388
|
+
` \u2022 Archivos temporales eliminados: ${pc3.cyan(String(rollbackReport.removedFiles.length))}`,
|
|
1389
|
+
` \u2022 Directorios vac\xEDos eliminados: ${pc3.cyan(String(rollbackReport.removedDirs.length))}`
|
|
1390
|
+
];
|
|
1391
|
+
if (suggestedFix) {
|
|
1392
|
+
lines.push(
|
|
1393
|
+
"",
|
|
1394
|
+
pc3.bold("Soluci\xF3n recomendada:"),
|
|
1395
|
+
` Ejecuta en tu terminal: ${pc3.green(suggestedFix)}`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
p5.note(lines.join("\n"), "Detalles de la interrupci\xF3n y reversi\xF3n");
|
|
1399
|
+
p5.outro(pc3.yellow("Tu proyecto no sufri\xF3 alteraciones y se preserv\xF3 intacto. Corrige la causa y vuelve a intentarlo cuando gustes."));
|
|
1400
|
+
}
|
|
1095
1401
|
async function runInteractiveUi(context, forceOnboarding = false) {
|
|
1096
1402
|
console.log(renderBanner());
|
|
1097
|
-
p5.intro(
|
|
1403
|
+
p5.intro(pc3.magenta("\xA1Bienvenido a GrooveSkills! Configuremos tus agentes de IA con est\xE1ndares limpios de arquitectura."));
|
|
1098
1404
|
if (context.detectedDependencies.length > 0) {
|
|
1099
|
-
const harnessInfo = context.harnessExists && !forceOnboarding ?
|
|
1405
|
+
const harnessInfo = context.harnessExists && !forceOnboarding ? pc3.green(`Activo (.grooveskills/memory, idioma: ${context.configuredLanguage ?? "es"})`) : pc3.cyan("Ser\xE1 inicializado");
|
|
1100
1406
|
p5.note(
|
|
1101
|
-
`
|
|
1102
|
-
|
|
1407
|
+
`Dependencias detectadas: ${context.detectedDependencies.map((d) => pc3.bold(d)).join(", ")}
|
|
1408
|
+
Carpetas de IA activas: ${context.detectedAgents.map((a) => pc3.green(a)).join(", ") || "Ninguna (se crear\xE1n)"}
|
|
1103
1409
|
GrooveSkills Harness: ${harnessInfo}`,
|
|
1104
|
-
"
|
|
1410
|
+
"Entorno del Proyecto"
|
|
1105
1411
|
);
|
|
1106
1412
|
}
|
|
1107
1413
|
const selectedSkills = await promptSkills(context.suggestedSkillIds);
|
|
1108
1414
|
if (p5.isCancel(selectedSkills)) {
|
|
1109
|
-
p5.cancel("
|
|
1415
|
+
p5.cancel("Instalaci\xF3n cancelada. \xA1Que tengas un excelente d\xEDa!");
|
|
1110
1416
|
process.exit(0);
|
|
1111
1417
|
}
|
|
1112
1418
|
const selectedAgents = await promptAgents(context.detectedAgents);
|
|
1113
1419
|
if (p5.isCancel(selectedAgents)) {
|
|
1114
|
-
p5.cancel("
|
|
1420
|
+
p5.cancel("Instalaci\xF3n cancelada. \xA1Que tengas un excelente d\xEDa!");
|
|
1115
1421
|
process.exit(0);
|
|
1116
1422
|
}
|
|
1117
1423
|
const selectedLanguage = await resolveLanguageSelection(
|
|
@@ -1125,26 +1431,42 @@ GrooveSkills Harness: ${harnessInfo}`,
|
|
|
1125
1431
|
selectedAgents
|
|
1126
1432
|
);
|
|
1127
1433
|
const s = p5.spinner();
|
|
1128
|
-
s.start("
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1434
|
+
s.start("Instalando y adaptando skills para tus agentes de IA configurados...");
|
|
1435
|
+
try {
|
|
1436
|
+
const result = await installSkills({
|
|
1437
|
+
targetPath: context.targetPath,
|
|
1438
|
+
selectedSkillIds: selectedSkills,
|
|
1439
|
+
selectedAgents,
|
|
1440
|
+
forceOverwrite,
|
|
1441
|
+
enableHarness: true,
|
|
1442
|
+
detectedDependencies: context.detectedDependencies,
|
|
1443
|
+
language: selectedLanguage,
|
|
1444
|
+
forceOnboarding
|
|
1445
|
+
});
|
|
1446
|
+
s.stop(pc3.green("\xA1Todas las skills y el harness fueron configurados exitosamente!"));
|
|
1447
|
+
const harnessStatus = result.harnessInitialized ? pc3.green(`Inicializado en .grooveskills/ (Idioma: ${selectedLanguage})`) : pc3.cyan(`Preservado en .grooveskills/ (Idioma: ${selectedLanguage})`);
|
|
1448
|
+
p5.note(
|
|
1449
|
+
`Skills configuradas: ${result.installedSkills.map((sk) => pc3.bold(sk)).join(", ")}
|
|
1450
|
+
Agentes objetivo: ${result.configuredAgents.map((a) => pc3.cyan(a)).join(", ")}
|
|
1144
1451
|
GrooveSkills Harness: ${harnessStatus}`,
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1452
|
+
"Resumen de Instalaci\xF3n"
|
|
1453
|
+
);
|
|
1454
|
+
if (result.harnessInitialized) {
|
|
1455
|
+
const promptRender = formatOnboardingPrompt(selectedLanguage);
|
|
1456
|
+
p5.note(
|
|
1457
|
+
pc3.green(promptRender.prompt),
|
|
1458
|
+
promptRender.title
|
|
1459
|
+
);
|
|
1460
|
+
}
|
|
1461
|
+
p5.outro(pc3.green("\xA1Tus agentes de IA ahora est\xE1n potenciados con los h\xE1bitos arquitect\xF3nicos de tu equipo! \u{1F680}"));
|
|
1462
|
+
} catch (error) {
|
|
1463
|
+
s.stop(pc3.yellow("La instalaci\xF3n no pudo completarse."));
|
|
1464
|
+
if (error instanceof InstallTransactionError) {
|
|
1465
|
+
renderInstallFailure(error);
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
throw error;
|
|
1469
|
+
}
|
|
1148
1470
|
}
|
|
1149
1471
|
|
|
1150
1472
|
// src/main.ts
|
|
@@ -1157,7 +1479,7 @@ async function runGrooveSkills(options = {}) {
|
|
|
1157
1479
|
const isDirectInit = Boolean(options.forceOnboarding) && hasAgents;
|
|
1158
1480
|
if (hasSkills && hasAgents || isDirectHarness || isDirectInit) {
|
|
1159
1481
|
const language = options.language ?? context.configuredLanguage ?? "es";
|
|
1160
|
-
await installSkills({
|
|
1482
|
+
const result = await installSkills({
|
|
1161
1483
|
targetPath,
|
|
1162
1484
|
selectedSkillIds: options.skills || [],
|
|
1163
1485
|
selectedAgents: options.agents || [],
|
|
@@ -1167,12 +1489,19 @@ async function runGrooveSkills(options = {}) {
|
|
|
1167
1489
|
language,
|
|
1168
1490
|
forceOnboarding: options.forceOnboarding
|
|
1169
1491
|
});
|
|
1492
|
+
if (result.harnessInitialized) {
|
|
1493
|
+
const promptRender = formatOnboardingPrompt(language);
|
|
1494
|
+
console.log(`
|
|
1495
|
+
${promptRender.formattedOutput}
|
|
1496
|
+
`);
|
|
1497
|
+
}
|
|
1170
1498
|
return;
|
|
1171
1499
|
}
|
|
1172
1500
|
await runInteractiveUi(context, Boolean(options.forceOnboarding));
|
|
1173
1501
|
}
|
|
1174
1502
|
|
|
1175
1503
|
export {
|
|
1504
|
+
InstallTransactionError,
|
|
1176
1505
|
runGrooveSkills
|
|
1177
1506
|
};
|
|
1178
|
-
//# sourceMappingURL=chunk-
|
|
1507
|
+
//# sourceMappingURL=chunk-E63GTCOE.js.map
|