@nurix/etna 0.20.42 → 0.20.43
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/data/harness-index.json +1 -1
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +11 -2
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +11 -2
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +11 -2
- package/package.json +2 -2
package/data/harness-index.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "code",
|
|
3
3
|
"name": "code",
|
|
4
|
-
"version": "3.20.
|
|
4
|
+
"version": "3.20.13",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"origin": "https://cdn.nustack.io",
|
|
30
30
|
"track": "latest"
|
|
31
31
|
},
|
|
32
|
-
"digest": "
|
|
32
|
+
"digest": "55a739ce31d2"
|
|
33
33
|
}
|
|
@@ -455,7 +455,11 @@ function principleNumbers(lines) {
|
|
|
455
455
|
// The body's sentences long enough to state a rule, as content-word sets; headings, tables and the footer drop out.
|
|
456
456
|
function sentencesOf(lines) {
|
|
457
457
|
const out = [];
|
|
458
|
+
// The opening states the document's boundary and points at its neighbours; a sentence there is a pointer, not a rule.
|
|
459
|
+
let opened = false;
|
|
458
460
|
for (const { n, line } of lines) {
|
|
461
|
+
if (/^##\s+/.test(line)) opened = true;
|
|
462
|
+
if (!opened) continue;
|
|
459
463
|
if (DOCTRINE_CLOSING.test(line)) break;
|
|
460
464
|
if (/^\s*(#|\|)/.test(line) || !line.trim()) continue;
|
|
461
465
|
const plain = line.replace(/^\s*([-*]|\d+\.)\s+/, '').replace(/\*\*[^*]+\*\*:?/g, '').replace(/\[([^\]]+)\]\([^)]*\)/g, '$1').replace(/`/g, '');
|
|
@@ -520,12 +524,17 @@ function lintDoctrine(path, text, lines, disabled, findings) {
|
|
|
520
524
|
const words = bare.replace(/\*\*/g, '').replace(/^\s*([-*]|\d+\.)\s+/, '');
|
|
521
525
|
if (!disabled.has('modal-verb') && /\b(must|should|shall)\b/i.test(words)) findings.push({ path, n, severity: 'warn', id: 'modal-verb', message: 'a modal in a specification — state what happens ("the request is rejected"); being in this document makes it a requirement', excerpt: line.trim().slice(0, 90) });
|
|
522
526
|
if (!disabled.has('question') && /\?(\s|$)/.test(words)) findings.push({ path, n, severity: 'warn', id: 'question', message: 'a question in prose — state the rule declaratively', excerpt: line.trim().slice(0, 90) });
|
|
527
|
+
// A term the set governs (any sibling's objects:, defined in its Terms table) is a term of art, never a vague synonym.
|
|
528
|
+
const ownTerms = [...new Set([...siblings.flatMap((d) => d.objects), ...listValue((frontmatter(text) || new Map()).get('objects')).map((o) => o.toLowerCase())])];
|
|
529
|
+
const unowned = ownTerms.reduce((acc, term) => acc.replace(new RegExp(`\\b${term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}s?\\b`, 'gi'), 'X'), words);
|
|
523
530
|
if (!disabled.has('vague-term')) for (const [pattern, use] of VAGUE_TERMS) {
|
|
524
|
-
const m = pattern.exec(
|
|
531
|
+
const m = pattern.exec(unowned);
|
|
525
532
|
if (m) { findings.push({ path, n, severity: 'warn', id: 'vague-term', message: `"${m[0]}" — use the standard term: ${use} (references/style-guide.md § Vocabulary)`, excerpt: line.trim().slice(0, 90) }); break; }
|
|
526
533
|
}
|
|
527
534
|
if (!disabled.has('long-sentence')) {
|
|
528
|
-
|
|
535
|
+
// A bullet's bold label names the rule; the sentence it leads is counted without it.
|
|
536
|
+
const unlabelled = bare.replace(/^\s*([-*]|\d+\.)\s+\*\*[^*]+\*\*:?\s*/, '').replace(/\*\*/g, '');
|
|
537
|
+
for (const sentence of unlabelled.split(/(?<=[.;!])\s+(?=[A-Z(])/)) {
|
|
529
538
|
const list = /:\s/.test(sentence) && (sentence.split(/:\s/).slice(1).join(' ').match(/[,;]/g) || []).length >= 2;
|
|
530
539
|
const count = (sentence.match(/[\w'’-]+/g) || []).length;
|
|
531
540
|
if (count > 25 && !list) { findings.push({ path, n, severity: 'warn', id: 'long-sentence', message: `${count} words in one sentence — about twenty-five at most, one idea each; a colon list is exempt`, excerpt: sentence.trim().slice(0, 90) }); break; }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "delivery",
|
|
3
3
|
"name": "delivery",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.48",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
"origin": "https://cdn.nustack.io",
|
|
15
15
|
"track": "latest"
|
|
16
16
|
},
|
|
17
|
-
"digest": "
|
|
17
|
+
"digest": "9f42da40515c"
|
|
18
18
|
}
|
|
@@ -455,7 +455,11 @@ function principleNumbers(lines) {
|
|
|
455
455
|
// The body's sentences long enough to state a rule, as content-word sets; headings, tables and the footer drop out.
|
|
456
456
|
function sentencesOf(lines) {
|
|
457
457
|
const out = [];
|
|
458
|
+
// The opening states the document's boundary and points at its neighbours; a sentence there is a pointer, not a rule.
|
|
459
|
+
let opened = false;
|
|
458
460
|
for (const { n, line } of lines) {
|
|
461
|
+
if (/^##\s+/.test(line)) opened = true;
|
|
462
|
+
if (!opened) continue;
|
|
459
463
|
if (DOCTRINE_CLOSING.test(line)) break;
|
|
460
464
|
if (/^\s*(#|\|)/.test(line) || !line.trim()) continue;
|
|
461
465
|
const plain = line.replace(/^\s*([-*]|\d+\.)\s+/, '').replace(/\*\*[^*]+\*\*:?/g, '').replace(/\[([^\]]+)\]\([^)]*\)/g, '$1').replace(/`/g, '');
|
|
@@ -520,12 +524,17 @@ function lintDoctrine(path, text, lines, disabled, findings) {
|
|
|
520
524
|
const words = bare.replace(/\*\*/g, '').replace(/^\s*([-*]|\d+\.)\s+/, '');
|
|
521
525
|
if (!disabled.has('modal-verb') && /\b(must|should|shall)\b/i.test(words)) findings.push({ path, n, severity: 'warn', id: 'modal-verb', message: 'a modal in a specification — state what happens ("the request is rejected"); being in this document makes it a requirement', excerpt: line.trim().slice(0, 90) });
|
|
522
526
|
if (!disabled.has('question') && /\?(\s|$)/.test(words)) findings.push({ path, n, severity: 'warn', id: 'question', message: 'a question in prose — state the rule declaratively', excerpt: line.trim().slice(0, 90) });
|
|
527
|
+
// A term the set governs (any sibling's objects:, defined in its Terms table) is a term of art, never a vague synonym.
|
|
528
|
+
const ownTerms = [...new Set([...siblings.flatMap((d) => d.objects), ...listValue((frontmatter(text) || new Map()).get('objects')).map((o) => o.toLowerCase())])];
|
|
529
|
+
const unowned = ownTerms.reduce((acc, term) => acc.replace(new RegExp(`\\b${term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}s?\\b`, 'gi'), 'X'), words);
|
|
523
530
|
if (!disabled.has('vague-term')) for (const [pattern, use] of VAGUE_TERMS) {
|
|
524
|
-
const m = pattern.exec(
|
|
531
|
+
const m = pattern.exec(unowned);
|
|
525
532
|
if (m) { findings.push({ path, n, severity: 'warn', id: 'vague-term', message: `"${m[0]}" — use the standard term: ${use} (references/style-guide.md § Vocabulary)`, excerpt: line.trim().slice(0, 90) }); break; }
|
|
526
533
|
}
|
|
527
534
|
if (!disabled.has('long-sentence')) {
|
|
528
|
-
|
|
535
|
+
// A bullet's bold label names the rule; the sentence it leads is counted without it.
|
|
536
|
+
const unlabelled = bare.replace(/^\s*([-*]|\d+\.)\s+\*\*[^*]+\*\*:?\s*/, '').replace(/\*\*/g, '');
|
|
537
|
+
for (const sentence of unlabelled.split(/(?<=[.;!])\s+(?=[A-Z(])/)) {
|
|
529
538
|
const list = /:\s/.test(sentence) && (sentence.split(/:\s/).slice(1).join(' ').match(/[,;]/g) || []).length >= 2;
|
|
530
539
|
const count = (sentence.match(/[\w'’-]+/g) || []).length;
|
|
531
540
|
if (count > 25 && !list) { findings.push({ path, n, severity: 'warn', id: 'long-sentence', message: `${count} words in one sentence — about twenty-five at most, one idea each; a colon list is exempt`, excerpt: sentence.trim().slice(0, 90) }); break; }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "etna",
|
|
3
3
|
"name": "etna",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.38",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
"origin": "https://cdn.nustack.io",
|
|
15
15
|
"track": "latest"
|
|
16
16
|
},
|
|
17
|
-
"digest": "
|
|
17
|
+
"digest": "3e652499a2b3"
|
|
18
18
|
}
|
|
@@ -455,7 +455,11 @@ function principleNumbers(lines) {
|
|
|
455
455
|
// The body's sentences long enough to state a rule, as content-word sets; headings, tables and the footer drop out.
|
|
456
456
|
function sentencesOf(lines) {
|
|
457
457
|
const out = [];
|
|
458
|
+
// The opening states the document's boundary and points at its neighbours; a sentence there is a pointer, not a rule.
|
|
459
|
+
let opened = false;
|
|
458
460
|
for (const { n, line } of lines) {
|
|
461
|
+
if (/^##\s+/.test(line)) opened = true;
|
|
462
|
+
if (!opened) continue;
|
|
459
463
|
if (DOCTRINE_CLOSING.test(line)) break;
|
|
460
464
|
if (/^\s*(#|\|)/.test(line) || !line.trim()) continue;
|
|
461
465
|
const plain = line.replace(/^\s*([-*]|\d+\.)\s+/, '').replace(/\*\*[^*]+\*\*:?/g, '').replace(/\[([^\]]+)\]\([^)]*\)/g, '$1').replace(/`/g, '');
|
|
@@ -520,12 +524,17 @@ function lintDoctrine(path, text, lines, disabled, findings) {
|
|
|
520
524
|
const words = bare.replace(/\*\*/g, '').replace(/^\s*([-*]|\d+\.)\s+/, '');
|
|
521
525
|
if (!disabled.has('modal-verb') && /\b(must|should|shall)\b/i.test(words)) findings.push({ path, n, severity: 'warn', id: 'modal-verb', message: 'a modal in a specification — state what happens ("the request is rejected"); being in this document makes it a requirement', excerpt: line.trim().slice(0, 90) });
|
|
522
526
|
if (!disabled.has('question') && /\?(\s|$)/.test(words)) findings.push({ path, n, severity: 'warn', id: 'question', message: 'a question in prose — state the rule declaratively', excerpt: line.trim().slice(0, 90) });
|
|
527
|
+
// A term the set governs (any sibling's objects:, defined in its Terms table) is a term of art, never a vague synonym.
|
|
528
|
+
const ownTerms = [...new Set([...siblings.flatMap((d) => d.objects), ...listValue((frontmatter(text) || new Map()).get('objects')).map((o) => o.toLowerCase())])];
|
|
529
|
+
const unowned = ownTerms.reduce((acc, term) => acc.replace(new RegExp(`\\b${term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}s?\\b`, 'gi'), 'X'), words);
|
|
523
530
|
if (!disabled.has('vague-term')) for (const [pattern, use] of VAGUE_TERMS) {
|
|
524
|
-
const m = pattern.exec(
|
|
531
|
+
const m = pattern.exec(unowned);
|
|
525
532
|
if (m) { findings.push({ path, n, severity: 'warn', id: 'vague-term', message: `"${m[0]}" — use the standard term: ${use} (references/style-guide.md § Vocabulary)`, excerpt: line.trim().slice(0, 90) }); break; }
|
|
526
533
|
}
|
|
527
534
|
if (!disabled.has('long-sentence')) {
|
|
528
|
-
|
|
535
|
+
// A bullet's bold label names the rule; the sentence it leads is counted without it.
|
|
536
|
+
const unlabelled = bare.replace(/^\s*([-*]|\d+\.)\s+\*\*[^*]+\*\*:?\s*/, '').replace(/\*\*/g, '');
|
|
537
|
+
for (const sentence of unlabelled.split(/(?<=[.;!])\s+(?=[A-Z(])/)) {
|
|
529
538
|
const list = /:\s/.test(sentence) && (sentence.split(/:\s/).slice(1).join(' ').match(/[,;]/g) || []).length >= 2;
|
|
530
539
|
const count = (sentence.match(/[\w'’-]+/g) || []).length;
|
|
531
540
|
if (count > 25 && !list) { findings.push({ path, n, severity: 'warn', id: 'long-sentence', message: `${count} words in one sentence — about twenty-five at most, one idea each; a colon list is exempt`, excerpt: sentence.trim().slice(0, 90) }); break; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nurix/etna",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.43",
|
|
4
4
|
"description": "Bootstrap a project with a Nurix harness — writes the committed contract (CLAUDE.md, AGENTS.md, .claude/harness.json, settings.json, the mount hook) and mounts the harness itself, gitignored, from a bundled payload; sessions keep it current from the CDN.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"agent",
|
|
47
47
|
"plugin"
|
|
48
48
|
],
|
|
49
|
-
"etnaFingerprint": "
|
|
49
|
+
"etnaFingerprint": "774d2e20c947"
|
|
50
50
|
}
|