@djordje-stojanovic/sigmaskills 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -1
- package/README.md +1 -1
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/registry/skill-baselines.json +83 -0
- package/src/customization.js +55 -0
- package/src/status.js +17 -5
- package/src/transaction.js +7 -4
- package/src/update.js +30 -13
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to [Sigmaskills](https://github.com/Djordje-Stojanovic/Sigma
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.6.1] — 2026-10-06
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- A changed front-matter `description` in `SKILL.md` now counts as a Skill Customization. `status` reports it as `valid-customization`, not as corruption. `update --yes` keeps your description when it installs a new Release. If the new Release changes the description itself, `update` still asks for `--outside-edit` first. (#94)
|
|
12
|
+
|
|
7
13
|
## [0.6.0] — 2026-10-06
|
|
8
14
|
|
|
9
15
|
### Added
|
|
@@ -226,7 +232,8 @@ Ship high-rigor, installable agent skills (not apps): full-repo audits, performa
|
|
|
226
232
|
| 2026-08-11 | `5dab832` | Add CHANGELOG and mark v0.1.0 as the first Sigmaskills release |
|
|
227
233
|
| 2026-08-11 | `124a666` | Polish v0.1.0 docs for multi-host install and use |
|
|
228
234
|
|
|
229
|
-
[Unreleased]: https://github.com/Djordje-Stojanovic/Sigmaskills/compare/v0.6.
|
|
235
|
+
[Unreleased]: https://github.com/Djordje-Stojanovic/Sigmaskills/compare/v0.6.1...HEAD
|
|
236
|
+
[0.6.1]: https://github.com/Djordje-Stojanovic/Sigmaskills/compare/v0.6.0...v0.6.1
|
|
230
237
|
[0.6.0]: https://github.com/Djordje-Stojanovic/Sigmaskills/compare/v0.5.1...v0.6.0
|
|
231
238
|
[0.5.1]: https://github.com/Djordje-Stojanovic/Sigmaskills/compare/v0.5.0...v0.5.1
|
|
232
239
|
[0.5.0]: https://github.com/Djordje-Stojanovic/Sigmaskills/compare/v0.4.0...v0.5.0
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Portable [Agent Skills](https://agentskills.io/) that install once and run on **
|
|
|
6
6
|
|
|
7
7
|
| | |
|
|
8
8
|
|---|---|
|
|
9
|
-
| **Release** | [**v0.6.
|
|
9
|
+
| **Release** | [**v0.6.1**](https://github.com/Djordje-Stojanovic/Sigmaskills/releases/tag/v0.6.1) |
|
|
10
10
|
| **Changelog** | [CHANGELOG.md](CHANGELOG.md) |
|
|
11
11
|
| **License** | [MIT](LICENSE) |
|
|
12
12
|
| **Spec** | [agentskills.io](https://agentskills.io/) |
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -71,6 +71,16 @@
|
|
|
71
71
|
"references/brief-method.md": "1bd9200bec7d41106bdff4f0ab39b86597bb2524b8d37af0eb663e9ec854770e",
|
|
72
72
|
"references/prompt-contract.md": "198bc5ab7a4c27fa14f3c168fe2f79bf1851e1ab7ef89cd95e551df5e398485a"
|
|
73
73
|
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"release": "0.6.0",
|
|
77
|
+
"revision": "4ec9b13a2e3e0cf10f6f218ee5e733f290575f8870bf56feb29e7d69e6b884c0",
|
|
78
|
+
"files": {
|
|
79
|
+
"SKILL.md": "442f9ba7785a30510df995b85b89b95f429d3dd5fe051dc85850309d6361d9e5",
|
|
80
|
+
"agents/openai.yaml": "69c0219b0f8ab7974527ca9f6fb06b67b21683a42ac1de63ef6014cdef1dde85",
|
|
81
|
+
"references/brief-method.md": "1bd9200bec7d41106bdff4f0ab39b86597bb2524b8d37af0eb663e9ec854770e",
|
|
82
|
+
"references/prompt-contract.md": "198bc5ab7a4c27fa14f3c168fe2f79bf1851e1ab7ef89cd95e551df5e398485a"
|
|
83
|
+
}
|
|
74
84
|
}
|
|
75
85
|
],
|
|
76
86
|
"sigmaperformance": [
|
|
@@ -192,6 +202,17 @@
|
|
|
192
202
|
"references/method.md": "659848b0d8a7e2dbcbde3fa88de09d07f20b68bb91ea808e766d526b137adc03",
|
|
193
203
|
"references/report.md": "ebabc522ac35dcfd7538abe4657d4933bf795d5ea20242a968592e0c569e7b41"
|
|
194
204
|
}
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"release": "0.6.0",
|
|
208
|
+
"revision": "5ce9fb447bb7c3035c98cc3c8d2e46f960f93fb5ca45482a7c94233f1929e196",
|
|
209
|
+
"files": {
|
|
210
|
+
"SKILL.md": "d900e0d033861065542308c966a9e2e2ec5322a70ce2614aba6e8bddb7d9fb07",
|
|
211
|
+
"agents/openai.yaml": "fc9f585d84b751aa9978d3b275a9f8b0383a1d57cb49fec27e905e794fa14d5a",
|
|
212
|
+
"references/measure.md": "efd083364caedfce9c5e69a2267209d3317cfa218029b48455e49cdc2f9affe0",
|
|
213
|
+
"references/method.md": "659848b0d8a7e2dbcbde3fa88de09d07f20b68bb91ea808e766d526b137adc03",
|
|
214
|
+
"references/report.md": "ebabc522ac35dcfd7538abe4657d4933bf795d5ea20242a968592e0c569e7b41"
|
|
215
|
+
}
|
|
195
216
|
}
|
|
196
217
|
],
|
|
197
218
|
"sigmawrite": [
|
|
@@ -250,6 +271,14 @@
|
|
|
250
271
|
"SKILL.md": "8db1872ac240efceec949d6b5e13c651b38e1de7b4c82588375c7dcd8f123cb1",
|
|
251
272
|
"agents/openai.yaml": "19b53dc100195c2d8cfd03720028f577ce8e3ba95829551c6d31d96a23021144"
|
|
252
273
|
}
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"release": "0.6.0",
|
|
277
|
+
"revision": "ff7cf7f2ff2851b916a5ed4aa856b0b9421095e94e60e8fe88fa0614b363cc9c",
|
|
278
|
+
"files": {
|
|
279
|
+
"SKILL.md": "8db1872ac240efceec949d6b5e13c651b38e1de7b4c82588375c7dcd8f123cb1",
|
|
280
|
+
"agents/openai.yaml": "19b53dc100195c2d8cfd03720028f577ce8e3ba95829551c6d31d96a23021144"
|
|
281
|
+
}
|
|
253
282
|
}
|
|
254
283
|
],
|
|
255
284
|
"sigmarefactor": [
|
|
@@ -288,6 +317,15 @@
|
|
|
288
317
|
"agents/openai.yaml": "baaa351482edffe072ae708968dcc1c70842caebf38a8aa7fd667b8da7a01c70",
|
|
289
318
|
"references/scan.md": "049715a6948138109b64994e969a1ed5541cab8151b8c8d9070d5eae379d45c5"
|
|
290
319
|
}
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"release": "0.6.0",
|
|
323
|
+
"revision": "25b4efaa5d9d9309791dbbec0f1a32eab409137c4cd16892f646714019b04115",
|
|
324
|
+
"files": {
|
|
325
|
+
"SKILL.md": "a6bef0c53cf780c79da20204cbbd15db65fa98dac3afa0ef1c8475725cd7f4cb",
|
|
326
|
+
"agents/openai.yaml": "baaa351482edffe072ae708968dcc1c70842caebf38a8aa7fd667b8da7a01c70",
|
|
327
|
+
"references/scan.md": "049715a6948138109b64994e969a1ed5541cab8151b8c8d9070d5eae379d45c5"
|
|
328
|
+
}
|
|
291
329
|
}
|
|
292
330
|
],
|
|
293
331
|
"sigmaimprove": [
|
|
@@ -323,6 +361,17 @@
|
|
|
323
361
|
"references/issue.md": "59300698c9a1d32e1d868173b22dea5fcc7ea7b7ca97198faf688256e5638300",
|
|
324
362
|
"references/lenses.md": "c5f2982ed1f4330035895dabacef550a5c1b241e0e1a370547b8715f89f3f8b3"
|
|
325
363
|
}
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"release": "0.6.0",
|
|
367
|
+
"revision": "9efbc885f031786dd84637009a3b8c38806c50d587b84799692fe60828c32da0",
|
|
368
|
+
"files": {
|
|
369
|
+
"SKILL.md": "118dacdc212f6f359ad9be0564492451bb2dc93f1f03c8af0094994caeff3060",
|
|
370
|
+
"agents/openai.yaml": "ba80d59887cef951c006ad52ddd42fa1999d12b464dff17b6bb0b711f7eb505f",
|
|
371
|
+
"references/categories.md": "58ad43e0da22b5a67f0bec01abc13760ffa835dec9efc07343b07e8326cba7bd",
|
|
372
|
+
"references/issue.md": "59300698c9a1d32e1d868173b22dea5fcc7ea7b7ca97198faf688256e5638300",
|
|
373
|
+
"references/lenses.md": "c5f2982ed1f4330035895dabacef550a5c1b241e0e1a370547b8715f89f3f8b3"
|
|
374
|
+
}
|
|
326
375
|
}
|
|
327
376
|
],
|
|
328
377
|
"sigmaship": [
|
|
@@ -355,6 +404,40 @@
|
|
|
355
404
|
"references/land.md": "a1ae956d9017a9b67af68d6d71521d969b78e97cc1f9c3997d4bcedf9d69465f",
|
|
356
405
|
"references/review-round.md": "e9062112683c71c28dd3bee5329afd6b9bd555ac83a1643b5f7ae5946b09df3a"
|
|
357
406
|
}
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"release": "0.6.0",
|
|
410
|
+
"revision": "248e37ea81d74561de3600e936143dbd1b7ae5543492fb7f1f8770f61182e090",
|
|
411
|
+
"files": {
|
|
412
|
+
"SKILL.md": "572d6c64273cde938ecdb9e1bccb39bf6d72e21fc386704035a75b18930ee315",
|
|
413
|
+
"agents/openai.yaml": "29f73f7ef65b71dfd4efa27dfef26a961dae98eee2882c63084528b7abef3b96",
|
|
414
|
+
"references/land.md": "a1ae956d9017a9b67af68d6d71521d969b78e97cc1f9c3997d4bcedf9d69465f",
|
|
415
|
+
"references/review-round.md": "e9062112683c71c28dd3bee5329afd6b9bd555ac83a1643b5f7ae5946b09df3a"
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
],
|
|
419
|
+
"easytalk": [
|
|
420
|
+
{
|
|
421
|
+
"release": "0.6.0",
|
|
422
|
+
"revision": "7e945683bf985978a80f8bddc752d9358063281729d306f537d2f542b7223fcf",
|
|
423
|
+
"files": {
|
|
424
|
+
"SKILL.md": "88d7dc079348c00ae89f5395403d93be10b45fe319799ef62ccb303c4ef64d7c",
|
|
425
|
+
"agents/openai.yaml": "2b37c8e632553692e561b7d1de7aa2d00ab7612136e5f873934bd49395c08c9e",
|
|
426
|
+
"template.html": "e59ee52b94e26f2727f4b007af4c29bc20c0c048775b74caa7fbd53988b94483"
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
],
|
|
430
|
+
"sigmaresearch": [
|
|
431
|
+
{
|
|
432
|
+
"release": "0.6.0",
|
|
433
|
+
"revision": "7a69af76abe0c396745776dd75f94cef3e877642fe187427e4f32be92437cf7c",
|
|
434
|
+
"files": {
|
|
435
|
+
"SKILL.md": "76e09762fe34e212c9d6cd191b2a3773bad6e4446822a7e4a0d9375998ecffa0",
|
|
436
|
+
"agents/openai.yaml": "151f7a91edc15ded3969ade41ff3766c35bb73c4a0067bec4af77b42001265a3",
|
|
437
|
+
"references/public-sources.md": "97c328c261953dd19332abc3946d1c272a2572755daa61bbf93560d765667f0b",
|
|
438
|
+
"references/reddit.md": "0dfd0d2a78e40cf84103b3e0cfe29d8264c98d73b97b139e4b9f1738fdd52fcb",
|
|
439
|
+
"references/x.md": "63f24a0fb1f80e55d93ded34ba18d3721dea26f83fbaaa7041662278ec4577cc"
|
|
440
|
+
}
|
|
358
441
|
}
|
|
359
442
|
]
|
|
360
443
|
}
|
package/src/customization.js
CHANGED
|
@@ -318,6 +318,61 @@ export function injectRawCustomContent(baseMarkdownContent, rawCustomContent, sk
|
|
|
318
318
|
return `${baseMarkdownContent.slice(0, startIndex + CUSTOM_BLOCK_START.length)}${rawCustomContent}${baseMarkdownContent.slice(endIndex)}`;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
function descriptionSpan(markdown) {
|
|
322
|
+
const lines = markdown.split(/(?<=\n)/);
|
|
323
|
+
const bare = (line) => line.replace(/\r?\n$/, '');
|
|
324
|
+
if (bare(lines[0] || '') !== '---') return null;
|
|
325
|
+
const close = lines.findIndex((line, index) => index > 0 && bare(line) === '---');
|
|
326
|
+
const start = lines.findIndex((line, index) => index > 0 && index < close && /^description\s*:/.test(line));
|
|
327
|
+
if (close === -1 || start === -1) return null;
|
|
328
|
+
// Continuation lines are indented; blank lines inside a block scalar belong to it too.
|
|
329
|
+
let stop = start + 1;
|
|
330
|
+
for (let index = start + 1; index < close; index += 1) {
|
|
331
|
+
if (/^[ \t]+\S/.test(lines[index])) stop = index + 1;
|
|
332
|
+
else if (bare(lines[index]).trim() !== '') break;
|
|
333
|
+
}
|
|
334
|
+
return { lines, start, stop };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* The raw front-matter `description` entry: its key line and indented continuation lines.
|
|
339
|
+
*
|
|
340
|
+
* @param {string} markdown
|
|
341
|
+
* @returns {string|null}
|
|
342
|
+
*/
|
|
343
|
+
export function readDescriptionEntry(markdown) {
|
|
344
|
+
const span = typeof markdown === 'string' ? descriptionSpan(markdown) : null;
|
|
345
|
+
return span ? span.lines.slice(span.start, span.stop).join('') : null;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Put a raw `description` entry into the front matter, in the file's own line endings.
|
|
350
|
+
* Returns the markdown unchanged when it has no description entry or no entry is given.
|
|
351
|
+
*
|
|
352
|
+
* @param {string} markdown
|
|
353
|
+
* @param {string|null} entry
|
|
354
|
+
* @returns {string}
|
|
355
|
+
*/
|
|
356
|
+
export function replaceDescriptionEntry(markdown, entry) {
|
|
357
|
+
const span = typeof entry === 'string' ? descriptionSpan(markdown) : null;
|
|
358
|
+
if (!span) return markdown;
|
|
359
|
+
const eol = span.lines[0].endsWith('\r\n') ? '\r\n' : '\n';
|
|
360
|
+
const lines = [...span.lines];
|
|
361
|
+
lines.splice(span.start, span.stop - span.start, entry.replace(/\r?\n/g, eol));
|
|
362
|
+
return lines.join('');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* The live markdown with the official front-matter description in place of the local one.
|
|
367
|
+
*
|
|
368
|
+
* @param {string} liveMarkdown
|
|
369
|
+
* @param {string|null} officialMarkdown
|
|
370
|
+
* @returns {string}
|
|
371
|
+
*/
|
|
372
|
+
export function withOfficialDescription(liveMarkdown, officialMarkdown) {
|
|
373
|
+
return replaceDescriptionEntry(liveMarkdown, readDescriptionEntry(officialMarkdown));
|
|
374
|
+
}
|
|
375
|
+
|
|
321
376
|
export function escapeRegExp(string) {
|
|
322
377
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
323
378
|
}
|
package/src/status.js
CHANGED
|
@@ -3,7 +3,12 @@ import path from 'node:path';
|
|
|
3
3
|
import { walkLiveFiles } from './backup.js';
|
|
4
4
|
import { findPackageRoot } from './catalog.js';
|
|
5
5
|
import { isPathInside } from './paths.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
inspectCustomizationBlock,
|
|
8
|
+
withOfficialDescription,
|
|
9
|
+
CUSTOM_BLOCK_END,
|
|
10
|
+
CUSTOM_BLOCK_START,
|
|
11
|
+
} from './customization.js';
|
|
7
12
|
import {
|
|
8
13
|
UNIVERSAL_PROJECT_DESTINATION,
|
|
9
14
|
listGlobalDestinationGroups,
|
|
@@ -60,16 +65,21 @@ function officialMarkdownShell(markdown) {
|
|
|
60
65
|
return `${markdown.slice(0, start + CUSTOM_BLOCK_START.length)}${markdown.slice(end)}`;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
function inspectSkillMarkdown(liveMarkdown, skillId, liveHash, bundledMarkdown, bundledHash) {
|
|
68
|
+
function inspectSkillMarkdown(liveMarkdown, skillId, liveHash, bundledMarkdown, bundledHash, installedHash) {
|
|
64
69
|
const inspection = inspectCustomizationBlock(liveMarkdown, skillId);
|
|
65
|
-
|
|
70
|
+
// An untouched copy of the installed Release is not a local description, even when the new Release changed it.
|
|
71
|
+
const localEdit = Boolean(installedHash) && liveHash !== installedHash;
|
|
72
|
+
const live = inspection.status === 'malformed' || !localEdit
|
|
73
|
+
? liveMarkdown
|
|
74
|
+
: withOfficialDescription(liveMarkdown, bundledMarkdown);
|
|
75
|
+
if (inspection.status !== 'valid' && live === liveMarkdown) {
|
|
66
76
|
return {
|
|
67
77
|
status: inspection.status,
|
|
68
78
|
officialHash: liveHash,
|
|
69
79
|
hasCustomContent: false,
|
|
70
80
|
};
|
|
71
81
|
}
|
|
72
|
-
if (typeof bundledMarkdown === 'string' && officialMarkdownShell(
|
|
82
|
+
if (typeof bundledMarkdown === 'string' && officialMarkdownShell(live) === officialMarkdownShell(bundledMarkdown)) {
|
|
73
83
|
return {
|
|
74
84
|
status: 'valid',
|
|
75
85
|
officialHash: bundledHash || liveHash,
|
|
@@ -87,7 +97,7 @@ function isPackagedResource(file) {
|
|
|
87
97
|
return /^(references|scripts|assets|agents)\//.test(file);
|
|
88
98
|
}
|
|
89
99
|
|
|
90
|
-
function classifyLiveTree({ liveFiles, bundledFiles, skillId, skillMarkdown, bundledMarkdown }) {
|
|
100
|
+
function classifyLiveTree({ liveFiles, bundledFiles, skillId, skillMarkdown, bundledMarkdown, installedFiles }) {
|
|
91
101
|
const classifications = [];
|
|
92
102
|
const officialFiles = { ...liveFiles };
|
|
93
103
|
|
|
@@ -98,6 +108,7 @@ function classifyLiveTree({ liveFiles, bundledFiles, skillId, skillMarkdown, bun
|
|
|
98
108
|
liveFiles['SKILL.md'],
|
|
99
109
|
bundledMarkdown,
|
|
100
110
|
bundledFiles['SKILL.md'],
|
|
111
|
+
installedFiles?.['SKILL.md'],
|
|
101
112
|
);
|
|
102
113
|
officialFiles['SKILL.md'] = markdown.officialHash;
|
|
103
114
|
if (markdown.status === 'malformed') classifications.push('malformed-markers');
|
|
@@ -308,6 +319,7 @@ export function collectStatus(options = {}) {
|
|
|
308
319
|
skillId,
|
|
309
320
|
skillMarkdown,
|
|
310
321
|
bundledMarkdown,
|
|
322
|
+
installedFiles: copyEntry?.baseHashes || entry?.baseHashes,
|
|
311
323
|
});
|
|
312
324
|
destinations.push({
|
|
313
325
|
relativeDestination,
|
package/src/transaction.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { acquireFileLock } from './concurrency-lock.js';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { findPackageRoot, validateSkill } from './catalog.js';
|
|
5
|
-
import { injectRawCustomContent } from './customization.js';
|
|
5
|
+
import { injectRawCustomContent, replaceDescriptionEntry } from './customization.js';
|
|
6
6
|
import { commitSkillBackup, exportSkillTree, getBackupRoot, pruneOlderBackups } from './backup.js';
|
|
7
7
|
import { createInstallPlan } from './plan.js';
|
|
8
8
|
import { isForeignProjectLock, loadProjectLock, saveProjectLock, updateProjectLockSkill, PROJECT_LOCK_FILENAME } from './project-lock.js';
|
|
@@ -254,13 +254,16 @@ function stageSkill(tx) {
|
|
|
254
254
|
);
|
|
255
255
|
}
|
|
256
256
|
fileHashes = validatedStaged.files;
|
|
257
|
-
if (params.preservedCustomRaw !== undefined) {
|
|
257
|
+
if (params.preservedCustomRaw !== undefined || params.preservedDescription !== undefined) {
|
|
258
258
|
const stagedSkillMd = path.join(stagingDir, 'SKILL.md');
|
|
259
259
|
if (pathExists(stagedSkillMd)) {
|
|
260
|
-
|
|
260
|
+
let stagedMarkdown = fs.readFileSync(stagedSkillMd, 'utf8');
|
|
261
|
+
if (params.preservedCustomRaw !== undefined) {
|
|
262
|
+
stagedMarkdown = injectRawCustomContent(stagedMarkdown, params.preservedCustomRaw, skillId);
|
|
263
|
+
}
|
|
261
264
|
fs.writeFileSync(
|
|
262
265
|
stagedSkillMd,
|
|
263
|
-
|
|
266
|
+
replaceDescriptionEntry(stagedMarkdown, params.preservedDescription),
|
|
264
267
|
'utf8',
|
|
265
268
|
);
|
|
266
269
|
}
|
package/src/update.js
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
extractRawCustomContent,
|
|
9
9
|
injectRawCustomContent,
|
|
10
10
|
inspectCustomizationBlock,
|
|
11
|
+
readDescriptionEntry,
|
|
12
|
+
withOfficialDescription,
|
|
11
13
|
} from './customization.js';
|
|
12
14
|
import {
|
|
13
15
|
UNIVERSAL_PROJECT_DESTINATION,
|
|
@@ -190,20 +192,25 @@ function readSkillMarkdown(absDir) {
|
|
|
190
192
|
return fs.readFileSync(skillMd, 'utf8');
|
|
191
193
|
}
|
|
192
194
|
|
|
193
|
-
function officialLiveFiles(liveFiles, liveMarkdown, bundledMarkdown, skillId) {
|
|
195
|
+
function officialLiveFiles(liveFiles, liveMarkdown, bundledMarkdown, skillId, baseHashes) {
|
|
194
196
|
const next = { ...liveFiles };
|
|
195
197
|
if (typeof liveMarkdown !== 'string' || typeof bundledMarkdown !== 'string' || !next['SKILL.md']) {
|
|
196
|
-
return next;
|
|
198
|
+
return { files: next, localDescription: false };
|
|
197
199
|
}
|
|
198
200
|
const inspection = inspectCustomizationBlock(liveMarkdown, skillId);
|
|
199
|
-
if (inspection.status !== 'valid' && inspection.status !== 'empty') return next;
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
|
|
201
|
+
if (inspection.status !== 'valid' && inspection.status !== 'empty') return { files: next, localDescription: false };
|
|
202
|
+
const officialHash = (markdown) => hashBytes(Buffer.from(
|
|
203
|
+
injectRawCustomContent(markdown, extractRawCustomContent(bundledMarkdown, skillId), skillId),
|
|
204
|
+
'utf8',
|
|
205
|
+
));
|
|
206
|
+
next['SKILL.md'] = officialHash(liveMarkdown);
|
|
207
|
+
// A local description counts as customization only when it is the one difference from the installed Release.
|
|
208
|
+
const withDescription = withOfficialDescription(liveMarkdown, bundledMarkdown);
|
|
209
|
+
const localDescription = withDescription !== liveMarkdown
|
|
210
|
+
&& next['SKILL.md'] !== baseHashes['SKILL.md']
|
|
211
|
+
&& officialHash(withDescription) === baseHashes['SKILL.md'];
|
|
212
|
+
if (localDescription) next['SKILL.md'] = baseHashes['SKILL.md'];
|
|
213
|
+
return { files: next, localDescription };
|
|
207
214
|
}
|
|
208
215
|
|
|
209
216
|
function customizationKind(markdown, skillId) {
|
|
@@ -236,7 +243,13 @@ function compareSkill(options) {
|
|
|
236
243
|
const diagnosis = typeof liveMarkdown === 'string'
|
|
237
244
|
? diagnoseCustomizationMarkers(liveMarkdown, skillId)
|
|
238
245
|
: { status: 'absent', shape: 'missing-file', repairable: false };
|
|
239
|
-
const officialLive = officialLiveFiles(
|
|
246
|
+
const { files: officialLive, localDescription } = officialLiveFiles(
|
|
247
|
+
liveFiles,
|
|
248
|
+
liveMarkdown,
|
|
249
|
+
bundledMarkdown,
|
|
250
|
+
skillId,
|
|
251
|
+
baseHashes,
|
|
252
|
+
);
|
|
240
253
|
const liveChanged = !mapsEqual(officialLive, baseHashes)
|
|
241
254
|
|| symlinkEffects(inventory).length > 0
|
|
242
255
|
|| detectTypeChanges(baseHashes, inventory).length > 0;
|
|
@@ -264,13 +277,13 @@ function compareSkill(options) {
|
|
|
264
277
|
} else if (liveChanged) {
|
|
265
278
|
comparison = 'local-only';
|
|
266
279
|
changeKind = 'local-only';
|
|
267
|
-
} else if (upstreamChanged && customKind === 'populated') {
|
|
280
|
+
} else if (upstreamChanged && (customKind === 'populated' || localDescription)) {
|
|
268
281
|
comparison = 'upstream-and-customization';
|
|
269
282
|
changeKind = 'upstream-only';
|
|
270
283
|
} else if (upstreamChanged) {
|
|
271
284
|
comparison = 'upstream-only';
|
|
272
285
|
changeKind = 'upstream-only';
|
|
273
|
-
} else if (customKind === 'populated') {
|
|
286
|
+
} else if (customKind === 'populated' || localDescription) {
|
|
274
287
|
comparison = 'customization-only';
|
|
275
288
|
}
|
|
276
289
|
|
|
@@ -348,6 +361,8 @@ function compareSkill(options) {
|
|
|
348
361
|
rawCustom: (customKind === 'populated' || customKind === 'empty') && typeof liveMarkdown === 'string'
|
|
349
362
|
? extractRawCustomContent(liveMarkdown, skillId)
|
|
350
363
|
: undefined,
|
|
364
|
+
localDescription,
|
|
365
|
+
rawDescription: localDescription ? readDescriptionEntry(liveMarkdown) : undefined,
|
|
351
366
|
backup: { required: needsResolution },
|
|
352
367
|
exportPath,
|
|
353
368
|
exportCollision: pathExists(exportPath),
|
|
@@ -504,6 +519,7 @@ function installOptionsFor(skill, options, scope) {
|
|
|
504
519
|
adoptChanged: 'replace',
|
|
505
520
|
adoptMalformed: options.malformedMarkers === 'replace' ? 'replace' : undefined,
|
|
506
521
|
preservedCustomRaw: skill.rawCustom !== undefined ? skill.rawCustom : undefined,
|
|
522
|
+
preservedDescription: skill.rawDescription,
|
|
507
523
|
registry: options.registry || loadHostRegistry(findPackageRoot()),
|
|
508
524
|
afterBackup: options.afterBackup,
|
|
509
525
|
saveState: options.saveState,
|
|
@@ -776,6 +792,7 @@ export function formatUpdateHuman(plan) {
|
|
|
776
792
|
lines.push(`${title}:`);
|
|
777
793
|
for (const skill of skills) {
|
|
778
794
|
lines.push(` ${skill.title} (${skill.id}) [${skill.comparison}]`);
|
|
795
|
+
if (skill.localDescription) lines.push(' local description: kept');
|
|
779
796
|
if (skill.changelog) {
|
|
780
797
|
lines.push(' Whole-skill diff:');
|
|
781
798
|
for (const line of skill.changelog.split('\n')) lines.push(` ${line}`);
|