greprag 5.82.0 → 5.83.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/dist/claude-lifecycle-cutover.js +5 -16
- package/dist/commands/codex.js +4 -0
- package/dist/commands/corpus/client.js +16 -0
- package/dist/commands/corpus/index.js +5 -2
- package/dist/commands/corpus/section.js +3 -3
- package/dist/commands/corpus/walk.js +3 -3
- package/dist/commands/delivery.js +64 -0
- package/dist/commands/docptr.js +10 -24
- package/dist/commands/init.js +4 -5
- package/dist/commands/load-primer-reminder.js +2 -2
- package/dist/commands/load.js +45 -12
- package/dist/commands/merge-guard.js +94 -9
- package/dist/commands/parity.js +0 -2
- package/dist/commands/pipe-wrap.js +4 -2
- package/dist/commands/skill-mirror-reminder.js +24 -6
- package/dist/commands/skill.js +24 -8
- package/dist/commands/skillgain.js +5 -1
- package/dist/commands/state-trigger.js +67 -31
- package/dist/delivery-config.js +69 -0
- package/dist/deploy-locks.js +18 -3
- package/dist/doc-mirror-client.js +39 -11
- package/dist/hook.js +35 -39
- package/dist/index.js +62 -27
- package/dist/native-adapter-refresh.js +114 -0
- package/dist/native-skill-adoption.js +20 -53
- package/dist/native-skill-mirror.js +130 -52
- package/dist/native-skill-pending.js +5 -2
- package/dist/opencode-plugin.bundle.js +1079 -159
- package/dist/opencode-plugin.js +6 -0
- package/dist/skill-activation-manifest.js +98 -22
- package/dist/skill-landing.js +6 -0
- package/dist/skill-mirror-files.js +59 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +27 -13
- package/skill/greprag/docs/corpus.md +2 -2
- package/skill/greprag/docs/setup.md +1 -1
- package/skill/mechanic/SKILL.md +14 -39
- package/skill/mechanic/docs/skill-fix-conventions.md +2 -2
- package/skill/templates/chip-spawn.md +36 -20
|
@@ -39,15 +39,25 @@ exports.NATIVE_SKILL_ADAPTER_PLATFORMS = void 0;
|
|
|
39
39
|
exports.nativeSkillMirrorEnabled = nativeSkillMirrorEnabled;
|
|
40
40
|
exports.nativeSkillRoot = nativeSkillRoot;
|
|
41
41
|
exports.nativeProjectSkillRoot = nativeProjectSkillRoot;
|
|
42
|
+
exports.writerOwnedProjectSkillRoot = writerOwnedProjectSkillRoot;
|
|
42
43
|
exports.assertNoNativeSkillCollision = assertNoNativeSkillCollision;
|
|
44
|
+
exports.readState = readState;
|
|
45
|
+
exports.writeState = writeState;
|
|
46
|
+
exports.targetPath = targetPath;
|
|
47
|
+
exports.filePath = filePath;
|
|
48
|
+
exports.readManagedFiles = readManagedFiles;
|
|
49
|
+
exports.nativeAdapterFormat = nativeAdapterFormat;
|
|
43
50
|
exports.buildNativeSkillAdapter = buildNativeSkillAdapter;
|
|
51
|
+
exports.removeTrackedFiles = removeTrackedFiles;
|
|
44
52
|
exports.applyNativeSkillMirror = applyNativeSkillMirror;
|
|
53
|
+
exports.fetchNativeMirroredSkill = fetchNativeMirroredSkill;
|
|
45
54
|
exports.syncNativeSkillMirror = syncNativeSkillMirror;
|
|
46
55
|
exports.nativeSkillMirrorSummary = nativeSkillMirrorSummary;
|
|
47
56
|
const fs = __importStar(require("node:fs"));
|
|
48
57
|
const os = __importStar(require("node:os"));
|
|
49
58
|
const path = __importStar(require("node:path"));
|
|
50
59
|
const skill_mirror_files_1 = require("./skill-mirror-files");
|
|
60
|
+
const skill_activation_manifest_1 = require("./skill-activation-manifest");
|
|
51
61
|
const SKILL_NAME_RE = /^[A-Za-z0-9._-]{1,64}$/;
|
|
52
62
|
const NATIVE_SKILL_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
53
63
|
const PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -78,6 +88,18 @@ function nativeProjectSkillRoot(platform, projectDir) {
|
|
|
78
88
|
return path.join(projectDir, '.agents', 'skills');
|
|
79
89
|
return path.join(projectDir, '.opencode', 'skills');
|
|
80
90
|
}
|
|
91
|
+
/** The project root a GrepRAG writer (sync, prune, adoption, pending capture)
|
|
92
|
+
* may own, or null. At the home directory the project paths are user-level
|
|
93
|
+
* roots owned by others: Codex reads `~/.agents/skills` as the user's skills,
|
|
94
|
+
* and `npx skills` installs there and links each package into
|
|
95
|
+
* `~/.claude/skills`. A sync run from home once adopted those packages, and
|
|
96
|
+
* its Codex launchers reached Claude through the links.
|
|
97
|
+
* adr: adr/skill-bundle-shape.md */
|
|
98
|
+
function writerOwnedProjectSkillRoot(platform, projectDir, homeDir = os.homedir()) {
|
|
99
|
+
if (path.resolve(projectDir).toLowerCase() === path.resolve(homeDir).toLowerCase())
|
|
100
|
+
return null;
|
|
101
|
+
return nativeProjectSkillRoot(platform, projectDir);
|
|
102
|
+
}
|
|
81
103
|
function skillScope(skill) {
|
|
82
104
|
return skill.scope === 'project' ? 'project' : 'global';
|
|
83
105
|
}
|
|
@@ -122,7 +144,7 @@ function rootForSkill(platform, skill, homeDir, projectDir) {
|
|
|
122
144
|
return nativeSkillRoot(platform, homeDir);
|
|
123
145
|
if (!projectDir)
|
|
124
146
|
return null;
|
|
125
|
-
return
|
|
147
|
+
return writerOwnedProjectSkillRoot(platform, projectDir, homeDir);
|
|
126
148
|
}
|
|
127
149
|
function emptyState() {
|
|
128
150
|
return { version: 1, skills: {} };
|
|
@@ -364,11 +386,9 @@ function renderAdapterBody(platform, skill) {
|
|
|
364
386
|
const identityArg = canonicalLookupArgs(skill);
|
|
365
387
|
const editArgs = canonicalEditArgs(skill);
|
|
366
388
|
const creationLocations = nativeCreationLocations(platform);
|
|
367
|
-
const syncCommand = platform
|
|
368
|
-
? 'greprag skill mirror sync claude-code --cutover-lifecycle'
|
|
369
|
-
: `greprag skill mirror sync ${platform}`;
|
|
389
|
+
const syncCommand = (0, skill_activation_manifest_1.nativeAdapterInstallCommand)(platform, [skillName]);
|
|
370
390
|
const creationLine = platform === 'claude-code'
|
|
371
|
-
? '- **Create another skill:**
|
|
391
|
+
? '- **Create another skill:** use the normal native creation path and leave it unmanaged until a separate Claude cutover is approved.'
|
|
372
392
|
: `- **Create another skill:** create a normal native skill at ${creationLocations}; run \`${syncCommand} --dry-run\` and confirm it says \`adopt\`; then run \`${syncCommand}\`. GrepRAG adopts it as canon and replaces the native package with an adapter.`;
|
|
373
393
|
const companions = skill.files.map(file => file.path).filter(relative => relative !== 'SKILL.md').sort();
|
|
374
394
|
const harnessArg = `--harness ${platform}`;
|
|
@@ -428,10 +448,7 @@ Read and follow that canonical payload before responding or taking action.
|
|
|
428
448
|
${companionLines}
|
|
429
449
|
`;
|
|
430
450
|
}
|
|
431
|
-
function
|
|
432
|
-
if (!nativeSkillMirrorEnabled(platform)
|
|
433
|
-
&& !(platform === 'claude-code' && options.allowClaudeCode))
|
|
434
|
-
return null;
|
|
451
|
+
function renderNativeSkillAdapter(platform, skill, stamps) {
|
|
435
452
|
if (!NATIVE_SKILL_NAME_RE.test(skill.skillName))
|
|
436
453
|
return null;
|
|
437
454
|
if (!skill.files.some(file => file.path === 'SKILL.md')
|
|
@@ -450,8 +467,9 @@ function buildNativeSkillAdapter(platform, skill, options = {}) {
|
|
|
450
467
|
...(typeof skill.revision === 'number' ? [`greprag_revision: ${skill.revision}`] : []),
|
|
451
468
|
...(scope === 'project' && skill.projectId ? [`greprag_project_id: ${skill.projectId}`] : []),
|
|
452
469
|
...(skill.replaces?.length ? [`replaces: ${skill.replaces.join(', ')}`] : []),
|
|
470
|
+
...stamps,
|
|
453
471
|
].join('\n');
|
|
454
|
-
|
|
472
|
+
return `---
|
|
455
473
|
name: ${skill.skillName}
|
|
456
474
|
description: |-
|
|
457
475
|
${yamlLiteral(description)}
|
|
@@ -459,8 +477,57 @@ ${extraFrontmatter}
|
|
|
459
477
|
---
|
|
460
478
|
|
|
461
479
|
${renderAdapterBody(platform, skill)}`;
|
|
462
|
-
return [{ path: 'SKILL.md', content }];
|
|
463
480
|
}
|
|
481
|
+
// Any change to the adapter template changes how these probes render, so their
|
|
482
|
+
// fingerprint names the renderer that wrote an adapter. Without it a renderer fix
|
|
483
|
+
// never reaches adapters whose canon has not moved: on 2026-10-04 every Claude
|
|
484
|
+
// adapter kept naming a sync that could not refresh it until someone re-synced
|
|
485
|
+
// all 40 by hand.
|
|
486
|
+
const PROBE_PROJECT_ID = '00000000-0000-4000-8000-000000000000';
|
|
487
|
+
const ADAPTER_FORMAT_PROBES = [
|
|
488
|
+
{
|
|
489
|
+
skillName: 'format-probe',
|
|
490
|
+
skillId: 'global:format-probe',
|
|
491
|
+
contentHash: '0'.repeat(64),
|
|
492
|
+
revision: 1,
|
|
493
|
+
triggerDescription: 'Format probe.',
|
|
494
|
+
files: [{ path: 'SKILL.md', content: '# Format probe\n' }, { path: 'docs/probe.md', content: '' }],
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
skillName: 'format-probe',
|
|
498
|
+
skillId: `project:${PROBE_PROJECT_ID}:format-probe`,
|
|
499
|
+
scope: 'project',
|
|
500
|
+
projectId: PROBE_PROJECT_ID,
|
|
501
|
+
replaces: ['global:format-probe'],
|
|
502
|
+
triggerDescription: 'Format probe.',
|
|
503
|
+
files: [{ path: 'SKILL.md', content: '<!-- greprag-source-bindings -->\n' }],
|
|
504
|
+
},
|
|
505
|
+
];
|
|
506
|
+
const adapterFormats = new Map();
|
|
507
|
+
/** Fingerprint of this CLI's adapter renderer for one harness. Every adapter
|
|
508
|
+
* carries it, so an adapter written by an older renderer reads as stale. */
|
|
509
|
+
function nativeAdapterFormat(platform) {
|
|
510
|
+
let format = adapterFormats.get(platform);
|
|
511
|
+
if (!format) {
|
|
512
|
+
format = (0, skill_mirror_files_1.computeSkillFileHash)(ADAPTER_FORMAT_PROBES.map((probe, index) => ({
|
|
513
|
+
path: String(index),
|
|
514
|
+
content: renderNativeSkillAdapter(platform, probe, []) || '',
|
|
515
|
+
}))).slice(0, 12);
|
|
516
|
+
adapterFormats.set(platform, format);
|
|
517
|
+
}
|
|
518
|
+
return format;
|
|
519
|
+
}
|
|
520
|
+
function buildNativeSkillAdapter(platform, skill, options = {}) {
|
|
521
|
+
if (!nativeSkillMirrorEnabled(platform)
|
|
522
|
+
&& !(platform === 'claude-code' && options.allowClaudeCode))
|
|
523
|
+
return null;
|
|
524
|
+
const content = renderNativeSkillAdapter(platform, skill, [
|
|
525
|
+
`greprag_adapter_format: ${nativeAdapterFormat(platform)}`,
|
|
526
|
+
]);
|
|
527
|
+
return content === null ? null : [{ path: 'SKILL.md', content }];
|
|
528
|
+
}
|
|
529
|
+
/** Remove the named files from a skill package, then any directory they leave
|
|
530
|
+
* empty. Every other file in the package stays. */
|
|
464
531
|
function removeTrackedFiles(target, relativeFiles) {
|
|
465
532
|
const directories = new Set();
|
|
466
533
|
for (const relative of relativeFiles) {
|
|
@@ -539,6 +606,16 @@ function applyNativeSkillMirror(params) {
|
|
|
539
606
|
}
|
|
540
607
|
const target = targetPath(root, skill.skillName);
|
|
541
608
|
const managed = rootEntry.state.skills[skill.skillName];
|
|
609
|
+
// A folder owned elsewhere (a link, another installer's package) is not
|
|
610
|
+
// ours; a managed name that became one is held for the operator, an
|
|
611
|
+
// unmanaged one is skipped like any package.
|
|
612
|
+
if ((0, skill_mirror_files_1.isSkillFolderOwnedElsewhere)(target)) {
|
|
613
|
+
if (managed)
|
|
614
|
+
result.blocked++;
|
|
615
|
+
else
|
|
616
|
+
result.skipped++;
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
542
619
|
if (fs.existsSync(target) && !managed) {
|
|
543
620
|
result.skipped++;
|
|
544
621
|
continue;
|
|
@@ -580,8 +657,9 @@ function applyNativeSkillMirror(params) {
|
|
|
580
657
|
const globalRoot = nativeSkillRoot(params.platform, homeDir);
|
|
581
658
|
if (params.pruneGlobalRoot !== false)
|
|
582
659
|
rootState(globalRoot);
|
|
583
|
-
|
|
584
|
-
|
|
660
|
+
const projectRoot = projectDir ? writerOwnedProjectSkillRoot(params.platform, projectDir, homeDir) : null;
|
|
661
|
+
if (projectRoot)
|
|
662
|
+
rootState(projectRoot);
|
|
585
663
|
for (const [root, entry] of roots) {
|
|
586
664
|
if (params.pruneGlobalRoot === false && root === globalRoot)
|
|
587
665
|
continue;
|
|
@@ -589,7 +667,7 @@ function applyNativeSkillMirror(params) {
|
|
|
589
667
|
if (entry.present.has(skillName))
|
|
590
668
|
continue;
|
|
591
669
|
const target = targetPath(root, skillName);
|
|
592
|
-
const currentFiles = readManagedFiles(target, managed);
|
|
670
|
+
const currentFiles = (0, skill_mirror_files_1.isSkillFolderOwnedElsewhere)(target) ? null : readManagedFiles(target, managed);
|
|
593
671
|
if (!currentFiles || (0, skill_mirror_files_1.computeSkillFileHash)(currentFiles) !== managed.hash) {
|
|
594
672
|
result.blocked++;
|
|
595
673
|
continue;
|
|
@@ -607,6 +685,43 @@ function applyNativeSkillMirror(params) {
|
|
|
607
685
|
}
|
|
608
686
|
return result;
|
|
609
687
|
}
|
|
688
|
+
/** Fetch the canonical package behind one roster row. Null when the mirror
|
|
689
|
+
* cannot serve it. */
|
|
690
|
+
async function fetchNativeMirroredSkill(apiUrl, apiKey, row) {
|
|
691
|
+
const skillQs = new URLSearchParams();
|
|
692
|
+
if (row.skillId)
|
|
693
|
+
skillQs.set('skillId', row.skillId);
|
|
694
|
+
else if (row.scope) {
|
|
695
|
+
skillQs.set('scope', row.scope);
|
|
696
|
+
if (row.projectId)
|
|
697
|
+
skillQs.set('projectId', row.projectId);
|
|
698
|
+
}
|
|
699
|
+
const response = await fetch(`${apiUrl}/v1/skillmirror/${encodeURIComponent(row.skillName)}${skillQs.size ? `?${skillQs.toString()}` : ''}`, { headers: { Authorization: `Bearer ${apiKey}` } });
|
|
700
|
+
if (!response.ok)
|
|
701
|
+
return null;
|
|
702
|
+
const body = await response.json();
|
|
703
|
+
return Array.isArray(body.files) ? {
|
|
704
|
+
skillName: row.skillName,
|
|
705
|
+
skillId: typeof row.skillId === 'string' ? row.skillId : undefined,
|
|
706
|
+
scope: row.scope === 'project' ? 'project' : 'global',
|
|
707
|
+
projectId: typeof row.projectId === 'string' ? row.projectId : undefined,
|
|
708
|
+
projectName: typeof row.projectName === 'string' ? row.projectName : undefined,
|
|
709
|
+
replaces: Array.isArray(row.replaces) ? row.replaces : [],
|
|
710
|
+
contentHash: typeof body.contentHash === 'string'
|
|
711
|
+
? body.contentHash
|
|
712
|
+
: typeof row.contentHash === 'string'
|
|
713
|
+
? row.contentHash
|
|
714
|
+
: undefined,
|
|
715
|
+
revision: typeof body.revision === 'number'
|
|
716
|
+
? body.revision
|
|
717
|
+
: typeof row.revision === 'number'
|
|
718
|
+
? row.revision
|
|
719
|
+
: undefined,
|
|
720
|
+
files: body.files,
|
|
721
|
+
description: typeof row.description === 'string' ? row.description : undefined,
|
|
722
|
+
triggerDescription: typeof row.triggerDescription === 'string' ? row.triggerDescription : undefined,
|
|
723
|
+
} : null;
|
|
724
|
+
}
|
|
610
725
|
async function syncNativeSkillMirror(params) {
|
|
611
726
|
if (!nativeSkillMirrorEnabled(params.platform)) {
|
|
612
727
|
return { installed: 0, updated: 0, current: 0, removed: 0, skipped: 0, blocked: 0 };
|
|
@@ -625,44 +740,7 @@ async function syncNativeSkillMirror(params) {
|
|
|
625
740
|
const data = await list.json();
|
|
626
741
|
const rows = (data.skills || [])
|
|
627
742
|
.filter((row) => typeof row.skillName === 'string' && SKILL_NAME_RE.test(row.skillName));
|
|
628
|
-
const fetched = await Promise.all(rows.map(
|
|
629
|
-
const skillName = row.skillName;
|
|
630
|
-
const skillQs = new URLSearchParams();
|
|
631
|
-
if (row.skillId)
|
|
632
|
-
skillQs.set('skillId', row.skillId);
|
|
633
|
-
else if (row.scope) {
|
|
634
|
-
skillQs.set('scope', row.scope);
|
|
635
|
-
if (row.projectId)
|
|
636
|
-
skillQs.set('projectId', row.projectId);
|
|
637
|
-
}
|
|
638
|
-
const response = await fetch(`${params.apiUrl}/v1/skillmirror/${encodeURIComponent(skillName)}${skillQs.size ? `?${skillQs.toString()}` : ''}`, {
|
|
639
|
-
headers: { Authorization: `Bearer ${params.apiKey}` },
|
|
640
|
-
});
|
|
641
|
-
if (!response.ok)
|
|
642
|
-
return null;
|
|
643
|
-
const body = await response.json();
|
|
644
|
-
return Array.isArray(body.files) ? {
|
|
645
|
-
skillName,
|
|
646
|
-
skillId: typeof row.skillId === 'string' ? row.skillId : undefined,
|
|
647
|
-
scope: row.scope === 'project' ? 'project' : 'global',
|
|
648
|
-
projectId: typeof row.projectId === 'string' ? row.projectId : undefined,
|
|
649
|
-
projectName: typeof row.projectName === 'string' ? row.projectName : undefined,
|
|
650
|
-
replaces: Array.isArray(row.replaces) ? row.replaces : [],
|
|
651
|
-
contentHash: typeof body.contentHash === 'string'
|
|
652
|
-
? body.contentHash
|
|
653
|
-
: typeof row.contentHash === 'string'
|
|
654
|
-
? row.contentHash
|
|
655
|
-
: undefined,
|
|
656
|
-
revision: typeof body.revision === 'number'
|
|
657
|
-
? body.revision
|
|
658
|
-
: typeof row.revision === 'number'
|
|
659
|
-
? row.revision
|
|
660
|
-
: undefined,
|
|
661
|
-
files: body.files,
|
|
662
|
-
description: typeof row.description === 'string' ? row.description : undefined,
|
|
663
|
-
triggerDescription: typeof row.triggerDescription === 'string' ? row.triggerDescription : undefined,
|
|
664
|
-
} : null;
|
|
665
|
-
}));
|
|
743
|
+
const fetched = await Promise.all(rows.map(row => fetchNativeMirroredSkill(params.apiUrl, params.apiKey, row)));
|
|
666
744
|
if (fetched.some(skill => skill === null)) {
|
|
667
745
|
throw new Error('skill mirror snapshot incomplete; existing native adapters preserved');
|
|
668
746
|
}
|
|
@@ -99,10 +99,13 @@ function ownedNativeRoots(params) {
|
|
|
99
99
|
root: path.resolve((0, native_skill_mirror_1.nativeSkillRoot)(platform, params.homeDir)),
|
|
100
100
|
scope: 'global',
|
|
101
101
|
});
|
|
102
|
-
|
|
102
|
+
const projectRoot = params.projectId
|
|
103
|
+
? (0, native_skill_mirror_1.writerOwnedProjectSkillRoot)(platform, params.cwd, params.homeDir)
|
|
104
|
+
: null;
|
|
105
|
+
if (projectRoot) {
|
|
103
106
|
roots.push({
|
|
104
107
|
platform,
|
|
105
|
-
root: path.resolve(
|
|
108
|
+
root: path.resolve(projectRoot),
|
|
106
109
|
scope: 'project',
|
|
107
110
|
projectId: params.projectId,
|
|
108
111
|
projectName: params.projectName,
|