@3lineas/grooveskills 1.3.1 → 1.5.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 +33 -32
- package/dist/{chunk-M7ZPBGQE.js → chunk-PLMYUKGW.js} +445 -193
- package/dist/chunk-PLMYUKGW.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/groove-harness/SKILL.md +1 -0
- package/dist/chunk-M7ZPBGQE.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/domains/detector/project-detector.ts
|
|
2
|
-
import { existsSync as existsSync2 } from "fs";
|
|
2
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
3
3
|
import { join as join2 } from "path";
|
|
4
4
|
|
|
5
5
|
// src/domains/detector/detect-agents.ts
|
|
@@ -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",
|
|
@@ -173,6 +174,19 @@ var SKILL_CATALOG = [
|
|
|
173
174
|
];
|
|
174
175
|
|
|
175
176
|
// src/domains/detector/project-detector.ts
|
|
177
|
+
function readConfiguredLanguage(targetPath) {
|
|
178
|
+
const configPath = join2(targetPath, ".grooveskills", "config.json");
|
|
179
|
+
if (!existsSync2(configPath)) {
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const raw = readFileSync2(configPath, "utf-8");
|
|
184
|
+
const parsed = JSON.parse(raw);
|
|
185
|
+
return typeof parsed.language === "string" ? parsed.language : void 0;
|
|
186
|
+
} catch {
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
176
190
|
function detectProjectContext(targetPath) {
|
|
177
191
|
const detectedAgents = detectAgents(targetPath);
|
|
178
192
|
const { dependencies, exists } = detectDependencies(targetPath);
|
|
@@ -193,18 +207,20 @@ function detectProjectContext(targetPath) {
|
|
|
193
207
|
}
|
|
194
208
|
}
|
|
195
209
|
const harnessExists = existsSync2(join2(targetPath, ".grooveskills", "memory", "context.md"));
|
|
210
|
+
const configuredLanguage = readConfiguredLanguage(targetPath);
|
|
196
211
|
return {
|
|
197
212
|
targetPath,
|
|
198
213
|
detectedDependencies: dependencies,
|
|
199
214
|
suggestedSkillIds,
|
|
200
215
|
detectedAgents,
|
|
201
216
|
packageJsonExists: exists,
|
|
202
|
-
harnessExists
|
|
217
|
+
harnessExists,
|
|
218
|
+
configuredLanguage
|
|
203
219
|
};
|
|
204
220
|
}
|
|
205
221
|
|
|
206
222
|
// src/domains/ui/ui-orchestrator.ts
|
|
207
|
-
import * as
|
|
223
|
+
import * as p5 from "@clack/prompts";
|
|
208
224
|
import pc2 from "picocolors";
|
|
209
225
|
|
|
210
226
|
// src/domains/ui/banners.ts
|
|
@@ -220,7 +236,8 @@ function renderBanner() {
|
|
|
220
236
|
// src/domains/ui/prompt-skills.ts
|
|
221
237
|
import * as p from "@clack/prompts";
|
|
222
238
|
async function promptSkills(suggestedSkillIds) {
|
|
223
|
-
const
|
|
239
|
+
const selectableSkills = SKILL_CATALOG.filter((skill) => !skill.isCoreHarness);
|
|
240
|
+
const options = selectableSkills.map((skill) => {
|
|
224
241
|
const isSuggested = suggestedSkillIds.includes(skill.id);
|
|
225
242
|
return {
|
|
226
243
|
value: skill.id,
|
|
@@ -256,19 +273,8 @@ async function promptAgents(detectedAgents) {
|
|
|
256
273
|
return selected;
|
|
257
274
|
}
|
|
258
275
|
|
|
259
|
-
// src/domains/ui/prompt-harness.ts
|
|
260
|
-
import * as p3 from "@clack/prompts";
|
|
261
|
-
async function promptHarness(harnessExists) {
|
|
262
|
-
const message = harnessExists ? "GrooveSkills Harness is already initialized. Would you like to update agent rules and memory templates?" : "Would you like to initialize the GrooveSkills Harness in .grooveskills/ (project memory, ADR, and 3-lane task execution)?";
|
|
263
|
-
const confirm3 = await p3.confirm({
|
|
264
|
-
message,
|
|
265
|
-
initialValue: true
|
|
266
|
-
});
|
|
267
|
-
return confirm3;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
276
|
// src/domains/ui/prompt-language.ts
|
|
271
|
-
import * as
|
|
277
|
+
import * as p3 from "@clack/prompts";
|
|
272
278
|
async function promptLanguage(defaultLanguage = "es") {
|
|
273
279
|
const options = [
|
|
274
280
|
{ value: "es", label: "Espa\xF1ol (Spanish)", hint: "Recomendado para proyectos en espa\xF1ol" },
|
|
@@ -277,7 +283,7 @@ async function promptLanguage(defaultLanguage = "es") {
|
|
|
277
283
|
{ value: "it", label: "Italiano (Italian)", hint: "Comunicazione in lingua italiana" },
|
|
278
284
|
{ value: "fr", label: "Fran\xE7ais (French)", hint: "Communication en langue fran\xE7aise" }
|
|
279
285
|
];
|
|
280
|
-
const selected = await
|
|
286
|
+
const selected = await p3.select({
|
|
281
287
|
message: "Which language should AI agents use for user-facing copy and communication?",
|
|
282
288
|
options,
|
|
283
289
|
initialValue: defaultLanguage
|
|
@@ -286,9 +292,9 @@ async function promptLanguage(defaultLanguage = "es") {
|
|
|
286
292
|
}
|
|
287
293
|
|
|
288
294
|
// src/domains/ui/prompt-confirm-overwrite.ts
|
|
289
|
-
import * as
|
|
295
|
+
import * as p4 from "@clack/prompts";
|
|
290
296
|
async function promptConfirmOverwrite() {
|
|
291
|
-
return await
|
|
297
|
+
return await p4.confirm({
|
|
292
298
|
message: "Some skills already exist in this project. Would you like to overwrite them with the latest version?",
|
|
293
299
|
initialValue: true
|
|
294
300
|
});
|
|
@@ -317,10 +323,10 @@ function verifySkillExists(targetPath, skillDirectoryName, agents) {
|
|
|
317
323
|
}
|
|
318
324
|
|
|
319
325
|
// src/domains/installer/install-orchestrator.ts
|
|
320
|
-
import { join as
|
|
326
|
+
import { join as join11 } from "path";
|
|
321
327
|
|
|
322
|
-
// src/domains/adapters/
|
|
323
|
-
import { mkdirSync,
|
|
328
|
+
// src/domains/adapters/skill-reference-builder.ts
|
|
329
|
+
import { existsSync as existsSync3, lstatSync as lstatSync2, mkdirSync, readFileSync as readFileSync3, rmSync, unlinkSync, writeFileSync } from "fs";
|
|
324
330
|
import { join as join5 } from "path";
|
|
325
331
|
|
|
326
332
|
// src/domains/project-store/project-paths.ts
|
|
@@ -331,9 +337,6 @@ function getProjectStoreDir(targetPath) {
|
|
|
331
337
|
function getProjectSkillsDir(targetPath) {
|
|
332
338
|
return join4(getProjectStoreDir(targetPath), "skills");
|
|
333
339
|
}
|
|
334
|
-
function getProjectCursorRulesDir(targetPath) {
|
|
335
|
-
return join4(getProjectStoreDir(targetPath), "cursor-rules");
|
|
336
|
-
}
|
|
337
340
|
function getProjectMemoryDir(targetPath) {
|
|
338
341
|
return join4(getProjectStoreDir(targetPath), "memory");
|
|
339
342
|
}
|
|
@@ -344,13 +347,26 @@ function getProjectConfigPath(targetPath) {
|
|
|
344
347
|
return join4(getProjectStoreDir(targetPath), "config.json");
|
|
345
348
|
}
|
|
346
349
|
|
|
347
|
-
// src/domains/adapters/
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
350
|
+
// src/domains/adapters/skill-reference-builder.ts
|
|
351
|
+
function removeExistingPathSafely(path) {
|
|
352
|
+
const fileExists = existsSync3(path);
|
|
353
|
+
if (!fileExists) {
|
|
354
|
+
try {
|
|
355
|
+
const lstat = lstatSync2(path);
|
|
356
|
+
if (lstat.isSymbolicLink()) {
|
|
357
|
+
unlinkSync(path);
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
351
363
|
try {
|
|
352
364
|
const lstat = lstatSync2(path);
|
|
353
|
-
if (lstat.
|
|
365
|
+
if (lstat.isSymbolicLink()) {
|
|
366
|
+
unlinkSync(path);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (lstat.isDirectory()) {
|
|
354
370
|
rmSync(path, { recursive: true, force: true });
|
|
355
371
|
return;
|
|
356
372
|
}
|
|
@@ -358,11 +374,71 @@ function removeExistingLinkPath(path) {
|
|
|
358
374
|
} catch {
|
|
359
375
|
}
|
|
360
376
|
}
|
|
361
|
-
function
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
377
|
+
function extractSkillDescription(canonicalSkillMdPath, fallback) {
|
|
378
|
+
if (!existsSync3(canonicalSkillMdPath)) {
|
|
379
|
+
return fallback;
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
const content = readFileSync3(canonicalSkillMdPath, "utf-8");
|
|
383
|
+
const match = content.match(/^description:\s*(.+)$/m);
|
|
384
|
+
const hasMatchedDesc = Boolean(match && match[1]);
|
|
385
|
+
if (hasMatchedDesc && match?.[1]) {
|
|
386
|
+
return match[1].trim();
|
|
387
|
+
}
|
|
388
|
+
} catch {
|
|
389
|
+
}
|
|
390
|
+
return fallback;
|
|
391
|
+
}
|
|
392
|
+
function writeAgentSkillReference(targetPath, agentRelativeSkillsDir, skill) {
|
|
393
|
+
const agentSkillFolder = join5(targetPath, agentRelativeSkillsDir, skill.directoryName);
|
|
394
|
+
removeExistingPathSafely(agentSkillFolder);
|
|
395
|
+
mkdirSync(agentSkillFolder, { recursive: true });
|
|
396
|
+
const canonicalSkillMd = join5(getProjectSkillsDir(targetPath), skill.directoryName, "SKILL.md");
|
|
397
|
+
const description = extractSkillDescription(canonicalSkillMd, skill.description);
|
|
398
|
+
const referenceContent = `---
|
|
399
|
+
name: ${skill.id}
|
|
400
|
+
description: ${description}
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
# ${skill.name} \u2014 Skill Reference
|
|
404
|
+
|
|
405
|
+
Canonical skill specification stored in \`.grooveskills/\`.
|
|
406
|
+
|
|
407
|
+
- **Specification**: [\`.grooveskills/skills/${skill.directoryName}/SKILL.md\`](../../../.grooveskills/skills/${skill.directoryName}/SKILL.md)
|
|
408
|
+
- **References & Templates**: \`.grooveskills/skills/${skill.directoryName}/\`
|
|
409
|
+
|
|
410
|
+
> **Instruction**: Read \`.grooveskills/skills/${skill.directoryName}/SKILL.md\` before executing tasks matching this skill.
|
|
411
|
+
`;
|
|
412
|
+
const targetFile = join5(agentSkillFolder, "SKILL.md");
|
|
413
|
+
writeFileSync(targetFile, referenceContent, "utf-8");
|
|
414
|
+
return targetFile;
|
|
415
|
+
}
|
|
416
|
+
function writeCursorRuleReference(targetPath, cursorRelativeRulesDir, skill) {
|
|
417
|
+
const rulesDir = join5(targetPath, cursorRelativeRulesDir);
|
|
418
|
+
if (!existsSync3(rulesDir)) {
|
|
419
|
+
mkdirSync(rulesDir, { recursive: true });
|
|
420
|
+
}
|
|
421
|
+
const targetFile = join5(rulesDir, `${skill.id}.mdc`);
|
|
422
|
+
removeExistingPathSafely(targetFile);
|
|
423
|
+
const canonicalSkillMd = join5(getProjectSkillsDir(targetPath), skill.directoryName, "SKILL.md");
|
|
424
|
+
const description = extractSkillDescription(canonicalSkillMd, skill.description);
|
|
425
|
+
const referenceContent = `---
|
|
426
|
+
description: ${description}
|
|
427
|
+
globs: ["**/*"]
|
|
428
|
+
alwaysApply: false
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
# ${skill.name} \u2014 Rule Reference
|
|
432
|
+
|
|
433
|
+
Canonical rule specification stored in \`.grooveskills/\`.
|
|
434
|
+
|
|
435
|
+
- **Specification**: \`.grooveskills/skills/${skill.directoryName}/SKILL.md\`
|
|
436
|
+
- **References & Templates**: \`.grooveskills/skills/${skill.directoryName}/\`
|
|
437
|
+
|
|
438
|
+
> **Instruction**: Read \`.grooveskills/skills/${skill.directoryName}/SKILL.md\` before executing tasks matching this rule.
|
|
439
|
+
`;
|
|
440
|
+
writeFileSync(targetFile, referenceContent, "utf-8");
|
|
441
|
+
return targetFile;
|
|
366
442
|
}
|
|
367
443
|
|
|
368
444
|
// src/domains/adapters/antigravity.adapter.ts
|
|
@@ -371,101 +447,70 @@ var AntigravityAdapter = class {
|
|
|
371
447
|
name = "Antigravity";
|
|
372
448
|
targetRelativeDir = ".agents/skills";
|
|
373
449
|
async installSkill(targetPath, skill, _skillSourceDir) {
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
mkdirSync(parentDir, { recursive: true });
|
|
377
|
-
}
|
|
378
|
-
const destination = join5(parentDir, skill.directoryName);
|
|
379
|
-
const projectSkillDir = join5(getProjectSkillsDir(targetPath), skill.directoryName);
|
|
380
|
-
createOrReplaceSymlink(projectSkillDir, destination, "dir");
|
|
381
|
-
return [destination];
|
|
450
|
+
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill);
|
|
451
|
+
return [file];
|
|
382
452
|
}
|
|
383
453
|
};
|
|
384
454
|
|
|
385
455
|
// src/domains/adapters/claude.adapter.ts
|
|
386
|
-
import { mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
387
|
-
import { join as join6 } from "path";
|
|
388
456
|
var ClaudeAdapter = class {
|
|
389
457
|
id = "claude";
|
|
390
458
|
name = "Claude Code";
|
|
391
459
|
targetRelativeDir = ".claude/skills";
|
|
392
460
|
async installSkill(targetPath, skill, _skillSourceDir) {
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
mkdirSync2(parentDir, { recursive: true });
|
|
396
|
-
}
|
|
397
|
-
const destination = join6(parentDir, skill.directoryName);
|
|
398
|
-
const projectSkillDir = join6(getProjectSkillsDir(targetPath), skill.directoryName);
|
|
399
|
-
createOrReplaceSymlink(projectSkillDir, destination, "dir");
|
|
400
|
-
return [destination];
|
|
461
|
+
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill);
|
|
462
|
+
return [file];
|
|
401
463
|
}
|
|
402
464
|
};
|
|
403
465
|
|
|
404
466
|
// src/domains/adapters/cursor.adapter.ts
|
|
405
|
-
import { mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
|
|
406
|
-
import { join as join7 } from "path";
|
|
407
467
|
var CursorAdapter = class {
|
|
408
468
|
id = "cursor";
|
|
409
469
|
name = "Cursor";
|
|
410
470
|
targetRelativeDir = ".cursor/rules";
|
|
411
471
|
async installSkill(targetPath, skill, _skillSourceDir) {
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
mkdirSync3(rulesDir, { recursive: true });
|
|
415
|
-
}
|
|
416
|
-
const filename = `${skill.id}.mdc`;
|
|
417
|
-
const destination = join7(rulesDir, filename);
|
|
418
|
-
const projectRuleFile = join7(getProjectCursorRulesDir(targetPath), filename);
|
|
419
|
-
createOrReplaceSymlink(projectRuleFile, destination, "file");
|
|
420
|
-
return [destination];
|
|
472
|
+
const file = writeCursorRuleReference(targetPath, this.targetRelativeDir, skill);
|
|
473
|
+
return [file];
|
|
421
474
|
}
|
|
422
475
|
};
|
|
423
476
|
|
|
424
477
|
// src/domains/adapters/gemini.adapter.ts
|
|
425
|
-
import { mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
|
|
426
|
-
import { join as join8 } from "path";
|
|
427
478
|
var GeminiAdapter = class {
|
|
428
479
|
id = "gemini";
|
|
429
480
|
name = "Gemini Code Assist";
|
|
430
481
|
targetRelativeDir = ".gemini/skills";
|
|
431
482
|
async installSkill(targetPath, skill, _skillSourceDir) {
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
mkdirSync4(parentDir, { recursive: true });
|
|
435
|
-
}
|
|
436
|
-
const destination = join8(parentDir, skill.directoryName);
|
|
437
|
-
const projectSkillDir = join8(getProjectSkillsDir(targetPath), skill.directoryName);
|
|
438
|
-
createOrReplaceSymlink(projectSkillDir, destination, "dir");
|
|
439
|
-
return [destination];
|
|
483
|
+
const file = writeAgentSkillReference(targetPath, this.targetRelativeDir, skill);
|
|
484
|
+
return [file];
|
|
440
485
|
}
|
|
441
486
|
};
|
|
442
487
|
|
|
443
488
|
// src/domains/project-store/sync-project-store.ts
|
|
444
|
-
import { cpSync,
|
|
445
|
-
import { join as
|
|
489
|
+
import { cpSync, existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync as rmSync2 } from "fs";
|
|
490
|
+
import { join as join7 } from "path";
|
|
446
491
|
|
|
447
492
|
// src/domains/installer/resolve-skills-source.ts
|
|
448
493
|
import { fileURLToPath } from "url";
|
|
449
|
-
import { dirname
|
|
450
|
-
import { existsSync as
|
|
494
|
+
import { dirname, resolve, join as join6 } from "path";
|
|
495
|
+
import { existsSync as existsSync4 } from "fs";
|
|
451
496
|
function resolveSkillsSourceRoot() {
|
|
452
497
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
453
|
-
const currentDir =
|
|
498
|
+
const currentDir = dirname(currentFilePath);
|
|
454
499
|
const candidateFromDist = resolve(currentDir, "../skills");
|
|
455
|
-
if (
|
|
500
|
+
if (existsSync4(candidateFromDist)) {
|
|
456
501
|
return candidateFromDist;
|
|
457
502
|
}
|
|
458
503
|
const candidateFromSrc = resolve(currentDir, "../../../skills");
|
|
459
|
-
if (
|
|
504
|
+
if (existsSync4(candidateFromSrc)) {
|
|
460
505
|
return candidateFromSrc;
|
|
461
506
|
}
|
|
462
507
|
let searchDir = currentDir;
|
|
463
508
|
for (let i = 0; i < 5; i++) {
|
|
464
|
-
const candidate =
|
|
465
|
-
if (
|
|
509
|
+
const candidate = join6(searchDir, "skills");
|
|
510
|
+
if (existsSync4(candidate)) {
|
|
466
511
|
return candidate;
|
|
467
512
|
}
|
|
468
|
-
const parent =
|
|
513
|
+
const parent = dirname(searchDir);
|
|
469
514
|
if (parent === searchDir) {
|
|
470
515
|
break;
|
|
471
516
|
}
|
|
@@ -477,51 +522,35 @@ function resolveSkillsSourceRoot() {
|
|
|
477
522
|
}
|
|
478
523
|
|
|
479
524
|
// src/domains/project-store/sync-project-store.ts
|
|
480
|
-
function syncProjectStore(targetPath, selectedSkillIds,
|
|
525
|
+
function syncProjectStore(targetPath, selectedSkillIds, _selectedAgents) {
|
|
481
526
|
const bundledSkillsDir = resolveSkillsSourceRoot();
|
|
482
527
|
const projectSkillsDir = getProjectSkillsDir(targetPath);
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
mkdirSync5(projectSkillsDir, { recursive: true });
|
|
528
|
+
if (!existsSync5(projectSkillsDir)) {
|
|
529
|
+
mkdirSync2(projectSkillsDir, { recursive: true });
|
|
486
530
|
}
|
|
487
|
-
const
|
|
488
|
-
if (
|
|
489
|
-
|
|
531
|
+
const legacyCursorRules = join7(targetPath, ".grooveskills", "cursor-rules");
|
|
532
|
+
if (existsSync5(legacyCursorRules)) {
|
|
533
|
+
try {
|
|
534
|
+
rmSync2(legacyCursorRules, { recursive: true, force: true });
|
|
535
|
+
} catch {
|
|
536
|
+
}
|
|
490
537
|
}
|
|
491
538
|
for (const skillId of selectedSkillIds) {
|
|
492
539
|
const skill = SKILL_CATALOG.find((item) => item.id === skillId);
|
|
493
540
|
if (!skill) {
|
|
494
541
|
continue;
|
|
495
542
|
}
|
|
496
|
-
const sourceSkillDir =
|
|
497
|
-
const destSkillDir =
|
|
498
|
-
if (
|
|
543
|
+
const sourceSkillDir = join7(bundledSkillsDir, skill.directoryName);
|
|
544
|
+
const destSkillDir = join7(projectSkillsDir, skill.directoryName);
|
|
545
|
+
if (existsSync5(sourceSkillDir)) {
|
|
499
546
|
cpSync(sourceSkillDir, destSkillDir, { recursive: true });
|
|
500
547
|
}
|
|
501
|
-
if (!needsCursorRules) {
|
|
502
|
-
continue;
|
|
503
|
-
}
|
|
504
|
-
const skillMdPath = join10(destSkillDir, "SKILL.md");
|
|
505
|
-
if (!existsSync8(skillMdPath)) {
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
const content = readFileSync2(skillMdPath, "utf-8");
|
|
509
|
-
const mdcHeader = `---
|
|
510
|
-
description: ${skill.description}
|
|
511
|
-
globs: ["**/*"]
|
|
512
|
-
alwaysApply: false
|
|
513
|
-
---
|
|
514
|
-
|
|
515
|
-
`;
|
|
516
|
-
const cleanedBody = content.replace(/^---[\s\S]*?---\s*/, "");
|
|
517
|
-
const targetMdc = join10(projectCursorRulesDir, `${skill.id}.mdc`);
|
|
518
|
-
writeFileSync(targetMdc, `${mdcHeader}${cleanedBody}`, "utf-8");
|
|
519
548
|
}
|
|
520
549
|
}
|
|
521
550
|
|
|
522
551
|
// src/domains/harness/install-harness.ts
|
|
523
|
-
import { existsSync as
|
|
524
|
-
import { join as
|
|
552
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
553
|
+
import { join as join10 } from "path";
|
|
525
554
|
|
|
526
555
|
// src/domains/harness/harness-languages.ts
|
|
527
556
|
var SUPPORTED_LANGUAGES = {
|
|
@@ -558,29 +587,59 @@ var SUPPORTED_LANGUAGES = {
|
|
|
558
587
|
};
|
|
559
588
|
|
|
560
589
|
// src/domains/harness/harness-templates.ts
|
|
561
|
-
function buildContextTemplate(detectedDependencies, language = "es") {
|
|
590
|
+
function buildContextTemplate(detectedDependencies, language = "es", profile) {
|
|
562
591
|
const depsFormatted = detectedDependencies.length > 0 ? detectedDependencies.map((d) => `- \`${d}\``).join("\n") : "- (No external dependencies detected)";
|
|
563
592
|
const langConfig = SUPPORTED_LANGUAGES[language];
|
|
564
|
-
|
|
593
|
+
const projectName = profile?.projectName ?? "Project";
|
|
594
|
+
const pkgManager = profile?.packageManager ?? "npm";
|
|
595
|
+
const commandLines = [];
|
|
596
|
+
if (profile?.commands) {
|
|
597
|
+
for (const [key, cmd] of Object.entries(profile.commands)) {
|
|
598
|
+
commandLines.push(`- **${key}**: \`${cmd}\``);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const commandsSection = commandLines.length > 0 ? commandLines.join("\n") : `- Build: Refer to \`package.json\` scripts.
|
|
602
|
+
- Typecheck: Refer to TypeScript configuration.
|
|
603
|
+
- Tests: Refer to test runner configuration.`;
|
|
604
|
+
const aliasLines = [];
|
|
605
|
+
if (profile?.pathAliases) {
|
|
606
|
+
for (const [alias, target] of Object.entries(profile.pathAliases)) {
|
|
607
|
+
aliasLines.push(`- \`${alias}\` -> \`${target}\``);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const aliasesSection = aliasLines.length > 0 ? `
|
|
611
|
+
### Path Aliases
|
|
612
|
+
${aliasLines.join("\n")}
|
|
613
|
+
` : "";
|
|
614
|
+
const dirLines = [];
|
|
615
|
+
if (profile?.sourceDirectories && profile.sourceDirectories.length > 0) {
|
|
616
|
+
for (const dir of profile.sourceDirectories) {
|
|
617
|
+
dirLines.push(`- \`${dir}/\``);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const dirsSection = dirLines.length > 0 ? `
|
|
621
|
+
### Source Modules
|
|
622
|
+
${dirLines.join("\n")}
|
|
623
|
+
` : "";
|
|
624
|
+
return `# Project Context & Architecture: ${projectName}
|
|
565
625
|
|
|
566
626
|
This file provides AI agents with instant architectural orientation for this project.
|
|
567
627
|
|
|
568
628
|
## 1. Stack & Dependencies
|
|
629
|
+
- **Package Manager**: \`${pkgManager}\`
|
|
569
630
|
${depsFormatted}
|
|
570
631
|
|
|
571
632
|
## 2. Directory Structure Conventions
|
|
572
633
|
- Follow single-responsibility per file (<200 lines).
|
|
573
634
|
- Maintain direct imports with path aliases; avoid barrel files.
|
|
574
635
|
- Keep domain boundaries explicit and clean.
|
|
575
|
-
|
|
636
|
+
${aliasesSection}${dirsSection}
|
|
576
637
|
## 3. Communication & User Copy Language
|
|
577
638
|
- **Configured Language**: ${langConfig.nativeName} (\`${language}\`).
|
|
578
639
|
- **Policy**: ${langConfig.instruction}
|
|
579
640
|
|
|
580
641
|
## 4. Essential Commands
|
|
581
|
-
|
|
582
|
-
- Typecheck: Refer to TypeScript configuration.
|
|
583
|
-
- Tests: Refer to test runner configuration.
|
|
642
|
+
${commandsSection}
|
|
584
643
|
`;
|
|
585
644
|
}
|
|
586
645
|
function buildDecisionsTemplate(language = "es") {
|
|
@@ -595,6 +654,35 @@ An append-only log of significant technical and architectural choices.
|
|
|
595
654
|
| ${now} | Adopt GrooveSkills Harness | Initialized \`.grooveskills/\` control plane with ODD | Clean, encapsulated agent memory, 3-lane ODD flow, and ${langConfig.nativeName} copy standard |
|
|
596
655
|
`;
|
|
597
656
|
}
|
|
657
|
+
function buildOnboardingTaskTemplate(projectName = "Project") {
|
|
658
|
+
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
659
|
+
return `---
|
|
660
|
+
status: pending
|
|
661
|
+
createdAt: ${now}
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
# 00 \u2014 Initial Project Onboarding & Architecture Exploration: ${projectName}
|
|
665
|
+
|
|
666
|
+
## 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\` and validating the essential build/typecheck commands.
|
|
668
|
+
|
|
669
|
+
## 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.
|
|
672
|
+
|
|
673
|
+
## Checklist
|
|
674
|
+
- [ ] Verify package manager and essential scripts (\`build\`, \`test\`, \`typecheck\`).
|
|
675
|
+
- [ ] Map high-level module architecture and data flow in \`.grooveskills/memory/context.md\`.
|
|
676
|
+
- [ ] Verify test runner or static checks pass cleanly.
|
|
677
|
+
- [ ] Switch status to \`completed\` once project context is fully verified.
|
|
678
|
+
|
|
679
|
+
## Evidence
|
|
680
|
+
- Observed command runs and outputs during the initial onboarding pass.
|
|
681
|
+
|
|
682
|
+
## Next
|
|
683
|
+
- Once onboarding is marked completed, proceed with domain tasks following the 3-lane ODD protocol.
|
|
684
|
+
`;
|
|
685
|
+
}
|
|
598
686
|
function buildTaskTemplate() {
|
|
599
687
|
return `---
|
|
600
688
|
status: in-progress
|
|
@@ -630,6 +718,7 @@ function buildAgentHarnessBlock(language = "es") {
|
|
|
630
718
|
This project uses the **GrooveSkills Harness** located in \`.grooveskills/\`.
|
|
631
719
|
|
|
632
720
|
## 1. Three-Lane ODD Protocol
|
|
721
|
+
- **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\`.
|
|
633
722
|
- **Authorize & Explore**: Follow the 7-step ODD protocol (Authorize, Explore, Resolve, Classify, Track & Hypothesize, Execute, Verify).
|
|
634
723
|
- **Lane 1 (Answer)**: Explanations, audits, or questions. Read-only; write zero code.
|
|
635
724
|
- **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\`.
|
|
@@ -646,19 +735,162 @@ This project uses the **GrooveSkills Harness** located in \`.grooveskills/\`.
|
|
|
646
735
|
<!-- grooveskills:harness:end -->`;
|
|
647
736
|
}
|
|
648
737
|
|
|
738
|
+
// src/domains/harness/scan-project-onboarding.ts
|
|
739
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
|
|
740
|
+
import { basename, join as join8 } from "path";
|
|
741
|
+
function detectProjectName(targetPath) {
|
|
742
|
+
const packageJsonPath = join8(targetPath, "package.json");
|
|
743
|
+
if (existsSync6(packageJsonPath)) {
|
|
744
|
+
try {
|
|
745
|
+
const raw = readFileSync4(packageJsonPath, "utf-8");
|
|
746
|
+
const parsed = JSON.parse(raw);
|
|
747
|
+
if (typeof parsed.name === "string" && parsed.name.trim().length > 0) {
|
|
748
|
+
return parsed.name.trim();
|
|
749
|
+
}
|
|
750
|
+
} catch {
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const cargoPath = join8(targetPath, "Cargo.toml");
|
|
754
|
+
if (existsSync6(cargoPath)) {
|
|
755
|
+
try {
|
|
756
|
+
const cargoRaw = readFileSync4(cargoPath, "utf-8");
|
|
757
|
+
const nameMatch = cargoRaw.match(/name\s*=\s*"([^"]+)"/);
|
|
758
|
+
if (nameMatch?.[1]) {
|
|
759
|
+
return nameMatch[1];
|
|
760
|
+
}
|
|
761
|
+
} catch {
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const pyprojectPath = join8(targetPath, "pyproject.toml");
|
|
765
|
+
if (existsSync6(pyprojectPath)) {
|
|
766
|
+
try {
|
|
767
|
+
const pyRaw = readFileSync4(pyprojectPath, "utf-8");
|
|
768
|
+
const pyMatch = pyRaw.match(/name\s*=\s*"([^"]+)"/);
|
|
769
|
+
if (pyMatch?.[1]) {
|
|
770
|
+
return pyMatch[1];
|
|
771
|
+
}
|
|
772
|
+
} catch {
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
return basename(targetPath) || "project";
|
|
776
|
+
}
|
|
777
|
+
function detectPackageManager(targetPath) {
|
|
778
|
+
if (existsSync6(join8(targetPath, "pnpm-lock.yaml"))) {
|
|
779
|
+
return "pnpm";
|
|
780
|
+
}
|
|
781
|
+
if (existsSync6(join8(targetPath, "bun.lockb")) || existsSync6(join8(targetPath, "bun.lock"))) {
|
|
782
|
+
return "bun";
|
|
783
|
+
}
|
|
784
|
+
if (existsSync6(join8(targetPath, "yarn.lock"))) {
|
|
785
|
+
return "yarn";
|
|
786
|
+
}
|
|
787
|
+
if (existsSync6(join8(targetPath, "package-lock.json"))) {
|
|
788
|
+
return "npm";
|
|
789
|
+
}
|
|
790
|
+
if (existsSync6(join8(targetPath, "Cargo.lock")) || existsSync6(join8(targetPath, "Cargo.toml"))) {
|
|
791
|
+
return "cargo";
|
|
792
|
+
}
|
|
793
|
+
if (existsSync6(join8(targetPath, "uv.lock"))) {
|
|
794
|
+
return "uv";
|
|
795
|
+
}
|
|
796
|
+
if (existsSync6(join8(targetPath, "poetry.lock"))) {
|
|
797
|
+
return "poetry";
|
|
798
|
+
}
|
|
799
|
+
return "npm";
|
|
800
|
+
}
|
|
801
|
+
function detectCommands(targetPath, pkgManager) {
|
|
802
|
+
const packageJsonPath = join8(targetPath, "package.json");
|
|
803
|
+
if (!existsSync6(packageJsonPath)) {
|
|
804
|
+
return {};
|
|
805
|
+
}
|
|
806
|
+
try {
|
|
807
|
+
const raw = readFileSync4(packageJsonPath, "utf-8");
|
|
808
|
+
const parsed = JSON.parse(raw);
|
|
809
|
+
const scripts = parsed.scripts ?? {};
|
|
810
|
+
const commands = {};
|
|
811
|
+
const runner = pkgManager === "npm" ? "npm run" : pkgManager;
|
|
812
|
+
if (scripts.build) {
|
|
813
|
+
commands.build = `${runner} build`;
|
|
814
|
+
}
|
|
815
|
+
if (scripts["check-types"]) {
|
|
816
|
+
commands.typecheck = `${runner} check-types`;
|
|
817
|
+
} else if (scripts.typecheck) {
|
|
818
|
+
commands.typecheck = `${runner} typecheck`;
|
|
819
|
+
} else if (scripts.tsc) {
|
|
820
|
+
commands.typecheck = `${runner} tsc`;
|
|
821
|
+
}
|
|
822
|
+
if (scripts.test) {
|
|
823
|
+
commands.test = `${runner} test`;
|
|
824
|
+
}
|
|
825
|
+
if (scripts.lint) {
|
|
826
|
+
commands.lint = `${runner} lint`;
|
|
827
|
+
}
|
|
828
|
+
if (scripts.dev) {
|
|
829
|
+
commands.dev = `${runner} dev`;
|
|
830
|
+
}
|
|
831
|
+
return commands;
|
|
832
|
+
} catch {
|
|
833
|
+
return {};
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
function detectPathAliases(targetPath) {
|
|
837
|
+
const tsconfigPath = join8(targetPath, "tsconfig.json");
|
|
838
|
+
if (!existsSync6(tsconfigPath)) {
|
|
839
|
+
return {};
|
|
840
|
+
}
|
|
841
|
+
try {
|
|
842
|
+
const raw = readFileSync4(tsconfigPath, "utf-8");
|
|
843
|
+
const parsed = JSON.parse(raw);
|
|
844
|
+
const paths = parsed.compilerOptions?.paths ?? {};
|
|
845
|
+
const aliases = {};
|
|
846
|
+
for (const [key, targets] of Object.entries(paths)) {
|
|
847
|
+
if (Array.isArray(targets) && typeof targets[0] === "string") {
|
|
848
|
+
aliases[key] = targets[0];
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return aliases;
|
|
852
|
+
} catch {
|
|
853
|
+
return {};
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function detectSourceDirectories(targetPath) {
|
|
857
|
+
const srcPath = join8(targetPath, "src");
|
|
858
|
+
const scanTarget = existsSync6(srcPath) ? srcPath : targetPath;
|
|
859
|
+
try {
|
|
860
|
+
const entries = readdirSync2(scanTarget, { withFileTypes: true });
|
|
861
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => !name.startsWith(".") && name !== "node_modules" && name !== "dist");
|
|
862
|
+
} catch {
|
|
863
|
+
return [];
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
function scanProjectOnboarding(targetPath) {
|
|
867
|
+
const projectName = detectProjectName(targetPath);
|
|
868
|
+
const packageManager = detectPackageManager(targetPath);
|
|
869
|
+
const commands = detectCommands(targetPath, packageManager);
|
|
870
|
+
const pathAliases = detectPathAliases(targetPath);
|
|
871
|
+
const sourceDirectories = detectSourceDirectories(targetPath);
|
|
872
|
+
return {
|
|
873
|
+
projectName,
|
|
874
|
+
packageManager,
|
|
875
|
+
commands,
|
|
876
|
+
pathAliases,
|
|
877
|
+
sourceDirectories
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
|
|
649
881
|
// src/domains/harness/upsert-agent-instructions.ts
|
|
650
|
-
import { existsSync as
|
|
651
|
-
import { join as
|
|
882
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
883
|
+
import { join as join9 } from "path";
|
|
652
884
|
var START_MARKER = "<!-- grooveskills:harness:start -->";
|
|
653
885
|
var END_MARKER = "<!-- grooveskills:harness:end -->";
|
|
654
886
|
function applyHarnessBlock(filePath, language = "es") {
|
|
655
887
|
const block = buildAgentHarnessBlock(language);
|
|
656
|
-
if (!
|
|
888
|
+
if (!existsSync7(filePath)) {
|
|
657
889
|
writeFileSync2(filePath, `${block}
|
|
658
890
|
`, "utf-8");
|
|
659
891
|
return;
|
|
660
892
|
}
|
|
661
|
-
const existingContent =
|
|
893
|
+
const existingContent = readFileSync5(filePath, "utf-8");
|
|
662
894
|
if (existingContent.includes(START_MARKER) && existingContent.includes(END_MARKER)) {
|
|
663
895
|
const regex = new RegExp(`${START_MARKER}[\\s\\S]*?${END_MARKER}`, "g");
|
|
664
896
|
const updated = existingContent.replace(regex, block);
|
|
@@ -671,11 +903,11 @@ function applyHarnessBlock(filePath, language = "es") {
|
|
|
671
903
|
}
|
|
672
904
|
function upsertAgentInstructions(targetPath, selectedAgents, language = "es") {
|
|
673
905
|
const updatedFiles = [];
|
|
674
|
-
const agentsMdPath =
|
|
906
|
+
const agentsMdPath = join9(targetPath, "AGENTS.md");
|
|
675
907
|
applyHarnessBlock(agentsMdPath, language);
|
|
676
908
|
updatedFiles.push(agentsMdPath);
|
|
677
909
|
if (selectedAgents.includes("claude")) {
|
|
678
|
-
const claudeMdPath =
|
|
910
|
+
const claudeMdPath = join9(targetPath, "CLAUDE.md");
|
|
679
911
|
applyHarnessBlock(claudeMdPath, language);
|
|
680
912
|
updatedFiles.push(claudeMdPath);
|
|
681
913
|
}
|
|
@@ -687,43 +919,62 @@ function installHarness(options) {
|
|
|
687
919
|
const memoryDir = getProjectMemoryDir(options.targetPath);
|
|
688
920
|
const tasksDir = getProjectTasksDir(options.targetPath);
|
|
689
921
|
const language = options.language ?? "es";
|
|
690
|
-
|
|
691
|
-
|
|
922
|
+
const forceOnboarding = Boolean(options.forceOnboarding);
|
|
923
|
+
if (!existsSync8(tasksDir)) {
|
|
924
|
+
mkdirSync3(tasksDir, { recursive: true });
|
|
692
925
|
}
|
|
693
926
|
const createdFiles = [];
|
|
694
|
-
const contextPath =
|
|
695
|
-
|
|
696
|
-
|
|
927
|
+
const contextPath = join10(memoryDir, "context.md");
|
|
928
|
+
const shouldWriteContext = !existsSync8(contextPath) || forceOnboarding;
|
|
929
|
+
if (shouldWriteContext) {
|
|
930
|
+
const profile = scanProjectOnboarding(options.targetPath);
|
|
931
|
+
const contextContent = buildContextTemplate(
|
|
932
|
+
options.detectedDependencies,
|
|
933
|
+
language,
|
|
934
|
+
profile
|
|
935
|
+
);
|
|
936
|
+
writeFileSync3(contextPath, contextContent, "utf-8");
|
|
697
937
|
createdFiles.push(contextPath);
|
|
938
|
+
const onboardingTaskPath = join10(tasksDir, "00-onboarding.md");
|
|
939
|
+
writeFileSync3(
|
|
940
|
+
onboardingTaskPath,
|
|
941
|
+
buildOnboardingTaskTemplate(profile.projectName),
|
|
942
|
+
"utf-8"
|
|
943
|
+
);
|
|
944
|
+
createdFiles.push(onboardingTaskPath);
|
|
698
945
|
}
|
|
699
|
-
const decisionsPath =
|
|
700
|
-
if (!
|
|
946
|
+
const decisionsPath = join10(memoryDir, "decisions.md");
|
|
947
|
+
if (!existsSync8(decisionsPath)) {
|
|
701
948
|
writeFileSync3(decisionsPath, buildDecisionsTemplate(language), "utf-8");
|
|
702
949
|
createdFiles.push(decisionsPath);
|
|
703
950
|
}
|
|
704
|
-
const templateTaskPath =
|
|
705
|
-
if (!
|
|
951
|
+
const templateTaskPath = join10(tasksDir, "_template.md");
|
|
952
|
+
if (!existsSync8(templateTaskPath)) {
|
|
706
953
|
writeFileSync3(templateTaskPath, buildTaskTemplate(), "utf-8");
|
|
707
954
|
createdFiles.push(templateTaskPath);
|
|
708
955
|
}
|
|
709
|
-
const instructions = upsertAgentInstructions(
|
|
956
|
+
const instructions = upsertAgentInstructions(
|
|
957
|
+
options.targetPath,
|
|
958
|
+
options.selectedAgents,
|
|
959
|
+
language
|
|
960
|
+
);
|
|
710
961
|
createdFiles.push(...instructions);
|
|
711
962
|
return createdFiles;
|
|
712
963
|
}
|
|
713
964
|
|
|
714
965
|
// src/domains/harness/write-project-config.ts
|
|
715
|
-
import { existsSync as
|
|
966
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
716
967
|
function readExistingConfig(configPath) {
|
|
717
968
|
const fallback = {
|
|
718
969
|
skills: [],
|
|
719
970
|
agents: [],
|
|
720
971
|
harness: false
|
|
721
972
|
};
|
|
722
|
-
if (!
|
|
973
|
+
if (!existsSync9(configPath)) {
|
|
723
974
|
return fallback;
|
|
724
975
|
}
|
|
725
976
|
try {
|
|
726
|
-
const raw =
|
|
977
|
+
const raw = readFileSync6(configPath, "utf-8");
|
|
727
978
|
const parsed = JSON.parse(raw);
|
|
728
979
|
return {
|
|
729
980
|
skills: Array.isArray(parsed.skills) ? parsed.skills : [],
|
|
@@ -764,10 +1015,10 @@ var ADAPTER_REGISTRY = {
|
|
|
764
1015
|
};
|
|
765
1016
|
async function installSkills(options) {
|
|
766
1017
|
const skillsToInstall = [...options.selectedSkillIds];
|
|
767
|
-
if (
|
|
1018
|
+
if (!skillsToInstall.includes("groove-harness")) {
|
|
768
1019
|
skillsToInstall.push("groove-harness");
|
|
769
1020
|
}
|
|
770
|
-
if (
|
|
1021
|
+
if (!skillsToInstall.includes("odd-ai")) {
|
|
771
1022
|
skillsToInstall.push("odd-ai");
|
|
772
1023
|
}
|
|
773
1024
|
syncProjectStore(options.targetPath, skillsToInstall, options.selectedAgents);
|
|
@@ -778,7 +1029,7 @@ async function installSkills(options) {
|
|
|
778
1029
|
if (!skill) {
|
|
779
1030
|
continue;
|
|
780
1031
|
}
|
|
781
|
-
const skillSourceDir =
|
|
1032
|
+
const skillSourceDir = join11(projectSkillsDir, skill.directoryName);
|
|
782
1033
|
for (const agentId of options.selectedAgents) {
|
|
783
1034
|
const adapter = ADAPTER_REGISTRY[agentId];
|
|
784
1035
|
if (!adapter) {
|
|
@@ -788,23 +1039,20 @@ async function installSkills(options) {
|
|
|
788
1039
|
createdFiles.push(...files);
|
|
789
1040
|
}
|
|
790
1041
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
createdFiles.push(...harnessFiles);
|
|
801
|
-
harnessInitialized = true;
|
|
802
|
-
}
|
|
1042
|
+
const harnessFiles = installHarness({
|
|
1043
|
+
targetPath: options.targetPath,
|
|
1044
|
+
selectedAgents: options.selectedAgents,
|
|
1045
|
+
selectedSkillIds: skillsToInstall,
|
|
1046
|
+
detectedDependencies: options.detectedDependencies || [],
|
|
1047
|
+
language: options.language,
|
|
1048
|
+
forceOnboarding: options.forceOnboarding
|
|
1049
|
+
});
|
|
1050
|
+
createdFiles.push(...harnessFiles);
|
|
803
1051
|
const configPath = writeProjectConfig(
|
|
804
1052
|
options.targetPath,
|
|
805
1053
|
skillsToInstall,
|
|
806
1054
|
options.selectedAgents,
|
|
807
|
-
|
|
1055
|
+
true,
|
|
808
1056
|
options.language
|
|
809
1057
|
);
|
|
810
1058
|
createdFiles.push(configPath);
|
|
@@ -812,19 +1060,19 @@ async function installSkills(options) {
|
|
|
812
1060
|
installedSkills: skillsToInstall,
|
|
813
1061
|
configuredAgents: options.selectedAgents,
|
|
814
1062
|
createdFiles,
|
|
815
|
-
harnessInitialized,
|
|
1063
|
+
harnessInitialized: true,
|
|
816
1064
|
language: options.language
|
|
817
1065
|
};
|
|
818
1066
|
}
|
|
819
1067
|
|
|
820
1068
|
// src/domains/ui/ui-orchestrator.ts
|
|
821
|
-
async function resolveLanguageSelection(
|
|
822
|
-
if (!
|
|
823
|
-
return "es";
|
|
1069
|
+
async function resolveLanguageSelection(harnessExists, configuredLanguage, forceOnboarding) {
|
|
1070
|
+
if (harnessExists && !forceOnboarding) {
|
|
1071
|
+
return configuredLanguage ?? "es";
|
|
824
1072
|
}
|
|
825
|
-
const langChoice = await promptLanguage("es");
|
|
826
|
-
if (
|
|
827
|
-
|
|
1073
|
+
const langChoice = await promptLanguage(configuredLanguage ?? "es");
|
|
1074
|
+
if (p5.isCancel(langChoice)) {
|
|
1075
|
+
p5.cancel("Installation cancelled. Have a wonderful day!");
|
|
828
1076
|
process.exit(0);
|
|
829
1077
|
}
|
|
830
1078
|
return langChoice;
|
|
@@ -837,65 +1085,66 @@ async function resolveOverwriteConfirmation(targetPath, selectedSkills, selected
|
|
|
837
1085
|
if (!anyExists) {
|
|
838
1086
|
return true;
|
|
839
1087
|
}
|
|
840
|
-
const
|
|
841
|
-
if (
|
|
842
|
-
|
|
1088
|
+
const confirm2 = await promptConfirmOverwrite();
|
|
1089
|
+
if (p5.isCancel(confirm2) || !confirm2) {
|
|
1090
|
+
p5.cancel("Installation stopped to protect existing skills.");
|
|
843
1091
|
process.exit(0);
|
|
844
1092
|
}
|
|
845
|
-
return Boolean(
|
|
1093
|
+
return Boolean(confirm2);
|
|
846
1094
|
}
|
|
847
|
-
async function runInteractiveUi(context) {
|
|
1095
|
+
async function runInteractiveUi(context, forceOnboarding = false) {
|
|
848
1096
|
console.log(renderBanner());
|
|
849
|
-
|
|
1097
|
+
p5.intro(pc2.magenta("Welcome to GrooveSkills! Let's tailor your AI agents with clean architectural standards."));
|
|
850
1098
|
if (context.detectedDependencies.length > 0) {
|
|
851
|
-
|
|
1099
|
+
const harnessInfo = context.harnessExists && !forceOnboarding ? pc2.green(`Active (.grooveskills/memory, lang: ${context.configuredLanguage ?? "es"})`) : pc2.cyan("Will be initialized");
|
|
1100
|
+
p5.note(
|
|
852
1101
|
`Detected dependencies: ${context.detectedDependencies.map((d) => pc2.bold(d)).join(", ")}
|
|
853
|
-
Active AI folders: ${context.detectedAgents.map((a) => pc2.green(a)).join(", ") || "None (will be created)"}
|
|
1102
|
+
Active AI folders: ${context.detectedAgents.map((a) => pc2.green(a)).join(", ") || "None (will be created)"}
|
|
1103
|
+
GrooveSkills Harness: ${harnessInfo}`,
|
|
854
1104
|
"Project Environment"
|
|
855
1105
|
);
|
|
856
1106
|
}
|
|
857
1107
|
const selectedSkills = await promptSkills(context.suggestedSkillIds);
|
|
858
|
-
if (
|
|
859
|
-
|
|
1108
|
+
if (p5.isCancel(selectedSkills)) {
|
|
1109
|
+
p5.cancel("Installation cancelled. Have a wonderful day!");
|
|
860
1110
|
process.exit(0);
|
|
861
1111
|
}
|
|
862
1112
|
const selectedAgents = await promptAgents(context.detectedAgents);
|
|
863
|
-
if (
|
|
864
|
-
|
|
865
|
-
process.exit(0);
|
|
866
|
-
}
|
|
867
|
-
const harnessChoice = await promptHarness(context.harnessExists);
|
|
868
|
-
if (p6.isCancel(harnessChoice)) {
|
|
869
|
-
p6.cancel("Installation cancelled. Have a wonderful day!");
|
|
1113
|
+
if (p5.isCancel(selectedAgents)) {
|
|
1114
|
+
p5.cancel("Installation cancelled. Have a wonderful day!");
|
|
870
1115
|
process.exit(0);
|
|
871
1116
|
}
|
|
872
|
-
const
|
|
873
|
-
|
|
1117
|
+
const selectedLanguage = await resolveLanguageSelection(
|
|
1118
|
+
context.harnessExists,
|
|
1119
|
+
context.configuredLanguage,
|
|
1120
|
+
forceOnboarding
|
|
1121
|
+
);
|
|
874
1122
|
const forceOverwrite = await resolveOverwriteConfirmation(
|
|
875
1123
|
context.targetPath,
|
|
876
1124
|
selectedSkills,
|
|
877
1125
|
selectedAgents
|
|
878
1126
|
);
|
|
879
|
-
const s =
|
|
1127
|
+
const s = p5.spinner();
|
|
880
1128
|
s.start("Installing and adapting skills across your configured AI agents...");
|
|
881
1129
|
const result = await installSkills({
|
|
882
1130
|
targetPath: context.targetPath,
|
|
883
1131
|
selectedSkillIds: selectedSkills,
|
|
884
1132
|
selectedAgents,
|
|
885
1133
|
forceOverwrite,
|
|
886
|
-
enableHarness:
|
|
1134
|
+
enableHarness: true,
|
|
887
1135
|
detectedDependencies: context.detectedDependencies,
|
|
888
|
-
language: selectedLanguage
|
|
1136
|
+
language: selectedLanguage,
|
|
1137
|
+
forceOnboarding
|
|
889
1138
|
});
|
|
890
1139
|
s.stop(pc2.green("All skills and harness have been successfully configured!"));
|
|
891
|
-
const harnessStatus = result.harnessInitialized ? pc2.green(`Initialized in .grooveskills/ (Language: ${selectedLanguage})`) : pc2.
|
|
892
|
-
|
|
1140
|
+
const harnessStatus = result.harnessInitialized ? pc2.green(`Initialized in .grooveskills/ (Language: ${selectedLanguage})`) : pc2.cyan(`Preserved in .grooveskills/ (Language: ${selectedLanguage})`);
|
|
1141
|
+
p5.note(
|
|
893
1142
|
`Configured skills: ${result.installedSkills.map((s2) => pc2.bold(s2)).join(", ")}
|
|
894
1143
|
Target agents: ${result.configuredAgents.map((a) => pc2.cyan(a)).join(", ")}
|
|
895
1144
|
GrooveSkills Harness: ${harnessStatus}`,
|
|
896
1145
|
"Installation Summary"
|
|
897
1146
|
);
|
|
898
|
-
|
|
1147
|
+
p5.outro(pc2.green("Your AI programming pair is now empowered with your team's architectural habits! \u{1F680}"));
|
|
899
1148
|
}
|
|
900
1149
|
|
|
901
1150
|
// src/main.ts
|
|
@@ -905,22 +1154,25 @@ async function runGrooveSkills(options = {}) {
|
|
|
905
1154
|
const hasSkills = options.skills && options.skills.length > 0;
|
|
906
1155
|
const hasAgents = options.agents && options.agents.length > 0;
|
|
907
1156
|
const isDirectHarness = Boolean(options.harness) && hasAgents;
|
|
908
|
-
|
|
1157
|
+
const isDirectInit = Boolean(options.forceOnboarding) && hasAgents;
|
|
1158
|
+
if (hasSkills && hasAgents || isDirectHarness || isDirectInit) {
|
|
1159
|
+
const language = options.language ?? context.configuredLanguage ?? "es";
|
|
909
1160
|
await installSkills({
|
|
910
1161
|
targetPath,
|
|
911
1162
|
selectedSkillIds: options.skills || [],
|
|
912
1163
|
selectedAgents: options.agents || [],
|
|
913
1164
|
forceOverwrite: Boolean(options.yes),
|
|
914
|
-
enableHarness:
|
|
1165
|
+
enableHarness: true,
|
|
915
1166
|
detectedDependencies: context.detectedDependencies,
|
|
916
|
-
language
|
|
1167
|
+
language,
|
|
1168
|
+
forceOnboarding: options.forceOnboarding
|
|
917
1169
|
});
|
|
918
1170
|
return;
|
|
919
1171
|
}
|
|
920
|
-
await runInteractiveUi(context);
|
|
1172
|
+
await runInteractiveUi(context, Boolean(options.forceOnboarding));
|
|
921
1173
|
}
|
|
922
1174
|
|
|
923
1175
|
export {
|
|
924
1176
|
runGrooveSkills
|
|
925
1177
|
};
|
|
926
|
-
//# sourceMappingURL=chunk-
|
|
1178
|
+
//# sourceMappingURL=chunk-PLMYUKGW.js.map
|