@lucasviola/specwiki 1.0.2 → 1.1.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <br><br>
6
6
 
7
7
  [![CI](https://github.com/lucasviola/specwiki/actions/workflows/ci.yml/badge.svg)](https://github.com/lucasviola/specwiki/actions/workflows/ci.yml)
8
- [![version](https://img.shields.io/badge/version-0.1.0-blue)](package.json)
8
+ [![version](https://img.shields.io/badge/version-1.1.1-blue)](package.json)
9
9
  [![node](https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=white)](package.json)
10
10
  [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
11
11
 
@@ -222,6 +222,8 @@ npm run typecheck
222
222
  npm test
223
223
  ```
224
224
 
225
+ See [CHANGELOG.md](CHANGELOG.md) for release history. When shipping user-facing changes, add a bullet under `[Unreleased]` there.
226
+
225
227
  ## License
226
228
 
227
229
  MIT
@@ -34,6 +34,19 @@
34
34
  --specwiki-syntax-deletion: #cf222e;
35
35
  --specwiki-syntax-deletion-background: #ffebe9;
36
36
  --specwiki-header-block-size: calc(var(--size-tool) + 1px);
37
+ --font-size-h1: 2rem;
38
+ --font-size-h2: 1.625rem;
39
+ --font-size-h3: 1.375rem;
40
+ --font-size-h4: 1.125rem;
41
+ --font-size-h5: 1rem;
42
+ --font-size-h6: 0.875rem;
43
+ --font-size-caption: 0.6875rem;
44
+ --font-size-ui-sm: 0.75rem;
45
+ --font-size-ui: 0.8125rem;
46
+ --font-size-body: 0.875rem;
47
+ --font-family-monospace-brand:
48
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
49
+ monospace;
37
50
  }
38
51
 
39
52
  :root[data-theme="light"] {
@@ -118,8 +131,8 @@
118
131
 
119
132
  body.specwiki {
120
133
  margin: 0;
121
- font-family: var(--font-family-base);
122
- font-size: 0.875rem;
134
+ font-family: var(--font-family-system-sans);
135
+ font-size: var(--font-size-body);
123
136
  line-height: var(--line-height-base);
124
137
  color: var(--color-base);
125
138
  background-color: var(--background-color-base);
@@ -200,8 +213,8 @@ body.specwiki {
200
213
  padding: 0.4rem 0.625rem;
201
214
  color: var(--color-base--subtle);
202
215
  background-color: var(--background-color-interactive-subtle);
203
- font-size: 0.75rem;
204
- font-weight: var(--font-weight-bold);
216
+ font-size: var(--font-size-ui-sm);
217
+ font-weight: var(--font-weight-semi-bold);
205
218
  text-transform: uppercase;
206
219
  letter-spacing: 0.04em;
207
220
  }
@@ -252,7 +265,7 @@ body.specwiki {
252
265
  border-radius: var(--border-radius-base, 2px);
253
266
  color: var(--color-base);
254
267
  background-color: var(--specwiki-search-badge-background);
255
- font-size: 0.6875rem;
268
+ font-size: var(--font-size-caption);
256
269
  font-weight: var(--font-weight-normal);
257
270
  text-overflow: ellipsis;
258
271
  white-space: nowrap;
@@ -264,7 +277,7 @@ body.specwiki {
264
277
  min-width: 0;
265
278
  overflow: hidden;
266
279
  color: var(--color-base--subtle);
267
- font-size: 0.8125rem;
280
+ font-size: var(--font-size-ui);
268
281
  font-weight: normal;
269
282
  text-overflow: ellipsis;
270
283
  white-space: nowrap;
@@ -296,7 +309,7 @@ body.specwiki {
296
309
  text-decoration: none;
297
310
  font-weight: var(--font-weight-bold);
298
311
  font-size: 1rem;
299
- font-family: var(--font-family-monospace);
312
+ font-family: var(--font-family-monospace-brand);
300
313
  letter-spacing: -0.02em;
301
314
  }
302
315
 
@@ -446,7 +459,8 @@ body.specwiki {
446
459
  display: block;
447
460
  color: var(--color-primary);
448
461
  text-decoration: none;
449
- font-size: 0.8125rem;
462
+ font-size: var(--font-size-ui);
463
+ font-weight: var(--font-weight-semi-bold);
450
464
  margin-bottom: 0.25rem;
451
465
  }
452
466
 
@@ -466,7 +480,7 @@ body.specwiki {
466
480
  border-radius: var(--border-radius-base, 2px);
467
481
  color: var(--color-base--subtle);
468
482
  background-color: var(--background-color-interactive-subtle);
469
- font-size: 0.6875rem;
483
+ font-size: var(--font-size-caption);
470
484
  font-weight: var(--font-weight-normal);
471
485
  }
472
486
 
@@ -483,7 +497,7 @@ body.specwiki {
483
497
  .category-nav-pages a {
484
498
  color: var(--color-base);
485
499
  text-decoration: none;
486
- font-size: 0.75rem;
500
+ font-size: var(--font-size-ui-sm);
487
501
  }
488
502
 
489
503
  .category-nav-pages a:hover {
@@ -501,16 +515,51 @@ body.specwiki {
501
515
  margin-top: 0.25rem;
502
516
  }
503
517
 
518
+ .category-nav-subgroup-summary {
519
+ display: flex;
520
+ align-items: center;
521
+ gap: 0.25rem;
522
+ cursor: pointer;
523
+ list-style: none;
524
+ }
525
+
526
+ .category-nav-subgroup-group > summary::-webkit-details-marker {
527
+ display: none;
528
+ }
529
+
530
+ .category-nav-subgroup-group > summary::marker {
531
+ content: "";
532
+ }
533
+
534
+ .category-nav-subgroup-group > summary::before {
535
+ content: "›";
536
+ color: var(--color-base--subtle);
537
+ font-size: 0.875rem;
538
+ line-height: 1;
539
+ transform: rotate(0deg);
540
+ transition: transform 160ms ease-out;
541
+ }
542
+
543
+ .category-nav-subgroup-group[open] > summary::before {
544
+ transform: rotate(90deg);
545
+ }
546
+
504
547
  .category-nav-subgroup-label {
505
548
  display: block;
506
- font-size: 0.6875rem;
507
- font-weight: var(--font-weight-bold);
549
+ font-size: var(--font-size-caption);
550
+ font-weight: var(--font-weight-semi-bold);
508
551
  color: var(--color-base--subtle);
509
552
  margin: 0 0 0.1875rem;
510
553
  text-transform: uppercase;
511
554
  letter-spacing: 0.04em;
512
555
  }
513
556
 
557
+ .category-nav-subgroup-summary .category-nav-subgroup-label {
558
+ flex: 1;
559
+ min-width: 0;
560
+ margin-bottom: 0;
561
+ }
562
+
514
563
  .category-nav-subgroup-pages {
515
564
  list-style: none;
516
565
  margin: 0;
@@ -520,6 +569,7 @@ body.specwiki {
520
569
 
521
570
  .category-nav-subgroup-pages > li > a {
522
571
  color: var(--color-base);
572
+ font-size: var(--font-size-ui-sm);
523
573
  }
524
574
 
525
575
  .category-nav-subgroup-nested {
@@ -527,8 +577,8 @@ body.specwiki {
527
577
  }
528
578
 
529
579
  .category-nav-subgroup-nested .category-nav-subgroup-label {
530
- font-weight: var(--font-weight-bold);
531
- font-size: 0.6875rem;
580
+ font-weight: var(--font-weight-semi-bold);
581
+ font-size: var(--font-size-caption);
532
582
  text-transform: none;
533
583
  letter-spacing: normal;
534
584
  color: var(--color-base--subtle);
@@ -545,7 +595,7 @@ body.specwiki {
545
595
  }
546
596
 
547
597
  .breadcrumb {
548
- font-size: 0.8125rem;
598
+ font-size: var(--font-size-ui);
549
599
  color: var(--color-base--subtle);
550
600
  margin-bottom: var(--padding-horizontal-base);
551
601
  }
@@ -566,7 +616,7 @@ body.specwiki {
566
616
  border: 1px solid var(--border-color-base);
567
617
  background-color: var(--background-color-interactive-subtle);
568
618
  padding: var(--padding-vertical-base) var(--padding-horizontal-base);
569
- font-size: 0.8125rem;
619
+ font-size: var(--font-size-ui);
570
620
  }
571
621
 
572
622
  .infobox-title {
@@ -596,7 +646,7 @@ body.specwiki {
596
646
  }
597
647
 
598
648
  .infobox-table code {
599
- font-size: 0.75rem;
649
+ font-size: var(--font-size-ui-sm);
600
650
  word-break: break-all;
601
651
  }
602
652
 
@@ -613,7 +663,7 @@ body.specwiki {
613
663
 
614
664
  .toc-heading {
615
665
  margin: 0 0 var(--padding-vertical-base);
616
- font-size: 0.8125rem;
666
+ font-size: var(--font-size-ui);
617
667
  font-weight: var(--font-weight-bold);
618
668
  }
619
669
 
@@ -630,7 +680,7 @@ body.specwiki {
630
680
  .toc-list a {
631
681
  color: var(--color-primary);
632
682
  text-decoration: none;
633
- font-size: 0.75rem;
683
+ font-size: var(--font-size-ui-sm);
634
684
  }
635
685
 
636
686
  .toc-list a:hover {
@@ -653,10 +703,58 @@ body.specwiki {
653
703
  padding-left: 3rem;
654
704
  }
655
705
 
656
- .specwiki-portal h1,
657
- .mw-parser-output h1,
658
- .mw-parser-output h2,
659
- .mw-parser-output h3 {
706
+ .specwiki-article-body .mw-parser-output h1,
707
+ .specwiki-article-body .mw-parser-output h2 {
708
+ font-family: var(--font-family-heading-main);
709
+ font-weight: 400;
710
+ line-height: var(--line-height-heading);
711
+ }
712
+
713
+ .specwiki-article-body .mw-parser-output h3,
714
+ .specwiki-article-body .mw-parser-output h4,
715
+ .specwiki-article-body .mw-parser-output h5,
716
+ .specwiki-article-body .mw-parser-output h6 {
717
+ font-family: var(--font-family-system-sans);
718
+ font-weight: 600;
719
+ line-height: var(--line-height-heading);
720
+ }
721
+
722
+ .specwiki-article-body .mw-parser-output h1 {
723
+ font-size: var(--font-size-h1);
724
+ margin: 0 0 0.75rem;
725
+ }
726
+
727
+ .specwiki-article-body .mw-parser-output h2 {
728
+ font-size: var(--font-size-h2);
729
+ margin: 1.5rem 0 0.5rem;
730
+ padding-bottom: 0.15rem;
731
+ border-bottom: 1px solid var(--border-color-divider);
732
+ }
733
+
734
+ .specwiki-article-body .mw-parser-output h3 {
735
+ font-size: var(--font-size-h3);
736
+ margin: 1.25rem 0 0.35rem;
737
+ }
738
+
739
+ .specwiki-article-body .mw-parser-output h4 {
740
+ font-size: var(--font-size-h4);
741
+ margin: 1rem 0 0.35rem;
742
+ }
743
+
744
+ .specwiki-article-body .mw-parser-output h5 {
745
+ font-size: var(--font-size-h5);
746
+ margin: 0.85rem 0 0.25rem;
747
+ }
748
+
749
+ .specwiki-article-body .mw-parser-output h6 {
750
+ font-size: var(--font-size-h6);
751
+ margin: 0.75rem 0 0.25rem;
752
+ }
753
+
754
+ .specwiki-portal h1 {
755
+ font-family: var(--font-family-heading-main);
756
+ font-size: var(--font-size-h1);
757
+ font-weight: 400;
660
758
  line-height: var(--line-height-heading);
661
759
  }
662
760
 
@@ -763,7 +861,7 @@ body.specwiki {
763
861
  padding: 0.15em 0.4em;
764
862
  border-radius: var(--border-radius-base);
765
863
  font-size: 0.9em;
766
- font-family: var(--font-family-monospace);
864
+ font-family: var(--font-family-monospace-brand);
767
865
  }
768
866
 
769
867
  .mw-parser-output pre {
@@ -771,6 +869,7 @@ body.specwiki {
771
869
  padding: var(--padding-horizontal-base);
772
870
  border-radius: var(--border-radius-base);
773
871
  overflow-x: auto;
872
+ font-family: var(--font-family-monospace-brand);
774
873
  }
775
874
 
776
875
  .mw-parser-output pre code {
@@ -946,7 +1045,8 @@ body.specwiki {
946
1045
 
947
1046
  @media (prefers-reduced-motion: reduce) {
948
1047
  .specwiki-nav-drawer,
949
- .category-nav-group > summary::before {
1048
+ .category-nav-group > summary::before,
1049
+ .category-nav-subgroup-group > summary::before {
950
1050
  transition: none;
951
1051
  }
952
1052
  }
@@ -0,0 +1,32 @@
1
+ export interface AgentSkillCatalogEntry {
2
+ skillId: string;
3
+ displayName?: string;
4
+ phase?: string;
5
+ module?: string;
6
+ description?: string;
7
+ isAgent: boolean;
8
+ agentName?: string;
9
+ agentTitle?: string;
10
+ agentIcon?: string;
11
+ /** True when at least one non-_meta CSV row contributed to this entry. */
12
+ inCsv: boolean;
13
+ }
14
+ export interface NavGroupingContext {
15
+ readonly loaded: boolean;
16
+ readonly skillsById: ReadonlyMap<string, AgentSkillCatalogEntry>;
17
+ }
18
+ export declare function loadNavGroupingContext(projectRoot: string): Promise<NavGroupingContext>;
19
+ /** Exported for unit tests — keeps catalog reads under projectRoot. */
20
+ export declare function catalogPath(root: string, ...parts: string[]): string;
21
+ /** Minimal RFC4180 line parser (quoted fields, escaped quotes). */
22
+ export declare function parseCsvLine(line: string): string[];
23
+ interface ParsedSkillToml {
24
+ isAgent: boolean;
25
+ agentName?: string;
26
+ agentTitle?: string;
27
+ agentIcon?: string;
28
+ }
29
+ /** Section detect + scalar extract — not a full TOML AST. */
30
+ export declare function parseSkillCustomizeToml(text: string): ParsedSkillToml;
31
+ export {};
32
+ //# sourceMappingURL=nav-grouping-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav-grouping-catalog.d.ts","sourceRoot":"","sources":["../../../src/output/html/nav-grouping-catalog.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CAClE;AAqBD,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC,CA2C7B;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAOpE;AA0ED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAqCnD;AAuJD,UAAU,eAAe;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,6DAA6D;AAC7D,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAYrE"}
@@ -0,0 +1,294 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { log } from "../../core/Logger.js";
4
+ import { assertRealpathConfinedUnder, PathEscapeError, } from "../../core/paths.js";
5
+ const SDLC_PHASES = new Set([
6
+ "1-analysis",
7
+ "2-planning",
8
+ "3-solutioning",
9
+ "4-implementation",
10
+ ]);
11
+ function emptyContext() {
12
+ return { loaded: false, skillsById: new Map() };
13
+ }
14
+ export async function loadNavGroupingContext(projectRoot) {
15
+ try {
16
+ const resolvedRoot = path.resolve(projectRoot);
17
+ const csvPath = catalogPath(resolvedRoot, "_bmad", "_config", "bmad-help.csv");
18
+ const csvText = await readConfinedFile(resolvedRoot, csvPath, "bmad-help.csv");
19
+ if (csvText === null) {
20
+ return emptyContext();
21
+ }
22
+ const skillsById = new Map();
23
+ for (const row of parseBmadHelpCsv(csvText)) {
24
+ mergeCsvRow(skillsById, row);
25
+ }
26
+ await enrichFromSkillTomls(resolvedRoot, skillsById);
27
+ let agentCount = 0;
28
+ for (const entry of skillsById.values()) {
29
+ if (entry.isAgent) {
30
+ agentCount += 1;
31
+ }
32
+ }
33
+ log.info("output.write", {
34
+ relativePath: "_bmad/_config/bmad-help.csv",
35
+ skillsLoaded: skillsById.size,
36
+ agentsFound: agentCount,
37
+ });
38
+ return { loaded: true, skillsById };
39
+ }
40
+ catch {
41
+ return emptyContext();
42
+ }
43
+ }
44
+ /** Exported for unit tests — keeps catalog reads under projectRoot. */
45
+ export function catalogPath(root, ...parts) {
46
+ const resolved = path.resolve(root, ...parts);
47
+ const relative = path.relative(root, resolved);
48
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
49
+ throw new Error("catalog path escapes project root");
50
+ }
51
+ return resolved;
52
+ }
53
+ async function readConfinedFile(projectRoot, targetPath, label) {
54
+ try {
55
+ await assertRealpathConfinedUnder(projectRoot, targetPath, label);
56
+ }
57
+ catch (err) {
58
+ if (err instanceof PathEscapeError) {
59
+ return null;
60
+ }
61
+ throw err;
62
+ }
63
+ try {
64
+ return await fs.readFile(targetPath, "utf-8");
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ function descriptionLooksDeprecated(description) {
71
+ return (description ?? "").toLowerCase().includes("deprecated");
72
+ }
73
+ function parseBmadHelpCsv(text) {
74
+ const lines = text.split(/\r?\n/);
75
+ const headerFields = parseCsvLine(lines[0] || "");
76
+ const index = {
77
+ module: headerFields.indexOf("module"),
78
+ skill: headerFields.indexOf("skill"),
79
+ displayName: headerFields.indexOf("display-name"),
80
+ description: headerFields.indexOf("description"),
81
+ phase: headerFields.indexOf("phase"),
82
+ };
83
+ if (index.skill < 0) {
84
+ return [];
85
+ }
86
+ const rows = [];
87
+ for (let i = 1; i < lines.length; i++) {
88
+ const line = lines[i];
89
+ if (!line.trim()) {
90
+ continue;
91
+ }
92
+ const fields = parseCsvLine(line);
93
+ const skill = (fields[index.skill] || "").trim();
94
+ if (!skill || skill === "_meta") {
95
+ continue;
96
+ }
97
+ rows.push({
98
+ module: fieldAt(fields, index.module),
99
+ skill,
100
+ displayName: fieldAt(fields, index.displayName),
101
+ description: fieldAt(fields, index.description),
102
+ phase: fieldAt(fields, index.phase),
103
+ });
104
+ }
105
+ return rows;
106
+ }
107
+ function fieldAt(fields, index) {
108
+ if (index < 0 || index >= fields.length) {
109
+ return "";
110
+ }
111
+ return fields[index].trim();
112
+ }
113
+ /** Minimal RFC4180 line parser (quoted fields, escaped quotes). */
114
+ export function parseCsvLine(line) {
115
+ const fields = [];
116
+ let current = "";
117
+ let inQuotes = false;
118
+ for (let i = 0; i < line.length; i++) {
119
+ const ch = line[i];
120
+ if (inQuotes) {
121
+ if (ch === '"') {
122
+ if (line[i + 1] === '"') {
123
+ current += '"';
124
+ i += 1;
125
+ }
126
+ else {
127
+ inQuotes = false;
128
+ }
129
+ }
130
+ else {
131
+ current += ch;
132
+ }
133
+ continue;
134
+ }
135
+ if (ch === '"') {
136
+ inQuotes = true;
137
+ continue;
138
+ }
139
+ if (ch === ",") {
140
+ fields.push(current);
141
+ current = "";
142
+ continue;
143
+ }
144
+ current += ch;
145
+ }
146
+ fields.push(current);
147
+ return fields;
148
+ }
149
+ function csvRowScore(row) {
150
+ let score = 0;
151
+ if (SDLC_PHASES.has(row.phase)) {
152
+ score += 100;
153
+ }
154
+ else if (row.phase === "anytime") {
155
+ score += 10;
156
+ }
157
+ if (row.module.includes("BMad Method") ||
158
+ (row.module && row.module !== "Core")) {
159
+ score += 5;
160
+ }
161
+ else if (row.module === "Core") {
162
+ score += 1;
163
+ }
164
+ return score;
165
+ }
166
+ function mergeCsvRow(skillsById, row) {
167
+ const existing = skillsById.get(row.skill);
168
+ if (!existing) {
169
+ skillsById.set(row.skill, {
170
+ skillId: row.skill,
171
+ displayName: row.displayName || undefined,
172
+ phase: row.phase || undefined,
173
+ module: row.module || undefined,
174
+ description: row.description || undefined,
175
+ isAgent: false,
176
+ inCsv: true,
177
+ });
178
+ return;
179
+ }
180
+ const existingAsRow = {
181
+ module: existing.module || "",
182
+ skill: existing.skillId,
183
+ displayName: existing.displayName || "",
184
+ description: existing.description || "",
185
+ phase: existing.phase || "",
186
+ };
187
+ const priorDeprecatedDescription = descriptionLooksDeprecated(existing.description)
188
+ ? existing.description
189
+ : undefined;
190
+ if (csvRowScore(row) > csvRowScore(existingAsRow)) {
191
+ if (row.displayName) {
192
+ existing.displayName = row.displayName;
193
+ }
194
+ if (row.phase) {
195
+ existing.phase = row.phase;
196
+ }
197
+ if (row.module) {
198
+ existing.module = row.module;
199
+ }
200
+ if (row.description) {
201
+ existing.description = row.description;
202
+ }
203
+ }
204
+ // Any CSV row marking DEPRECATED wins membership, even on score ties / losses.
205
+ if (descriptionLooksDeprecated(row.description)) {
206
+ existing.description = row.description;
207
+ }
208
+ else if (priorDeprecatedDescription) {
209
+ existing.description = priorDeprecatedDescription;
210
+ }
211
+ existing.inCsv = true;
212
+ }
213
+ async function enrichFromSkillTomls(projectRoot, skillsById) {
214
+ const skillsDir = catalogPath(projectRoot, ".agents", "skills");
215
+ try {
216
+ await assertRealpathConfinedUnder(projectRoot, skillsDir, "agent skills directory");
217
+ }
218
+ catch (err) {
219
+ if (err instanceof PathEscapeError) {
220
+ return;
221
+ }
222
+ throw err;
223
+ }
224
+ let entries;
225
+ try {
226
+ entries = await fs.readdir(skillsDir);
227
+ }
228
+ catch {
229
+ return;
230
+ }
231
+ for (const skillId of entries) {
232
+ let tomlPath;
233
+ try {
234
+ tomlPath = catalogPath(projectRoot, ".agents", "skills", skillId, "customize.toml");
235
+ }
236
+ catch {
237
+ continue;
238
+ }
239
+ const tomlText = await readConfinedFile(projectRoot, tomlPath, `customize.toml (${skillId})`);
240
+ if (tomlText === null) {
241
+ continue;
242
+ }
243
+ const parsed = parseSkillCustomizeToml(tomlText);
244
+ const existing = skillsById.get(skillId);
245
+ if (existing) {
246
+ if (parsed.isAgent) {
247
+ existing.isAgent = true;
248
+ existing.agentName = parsed.agentName;
249
+ existing.agentTitle = parsed.agentTitle;
250
+ existing.agentIcon = parsed.agentIcon;
251
+ }
252
+ continue;
253
+ }
254
+ if (parsed.isAgent) {
255
+ skillsById.set(skillId, {
256
+ skillId,
257
+ isAgent: true,
258
+ agentName: parsed.agentName,
259
+ agentTitle: parsed.agentTitle,
260
+ agentIcon: parsed.agentIcon,
261
+ inCsv: false,
262
+ });
263
+ }
264
+ }
265
+ }
266
+ /** Section detect + scalar extract — not a full TOML AST. */
267
+ export function parseSkillCustomizeToml(text) {
268
+ if (!/^\s*\[agent\]\s*$/m.test(text)) {
269
+ return { isAgent: false };
270
+ }
271
+ const agentBody = extractTomlSection(text, "agent");
272
+ return {
273
+ isAgent: true,
274
+ agentName: extractTomlString(agentBody, "name"),
275
+ agentTitle: extractTomlString(agentBody, "title"),
276
+ agentIcon: extractTomlString(agentBody, "icon"),
277
+ };
278
+ }
279
+ function extractTomlSection(text, section) {
280
+ const header = new RegExp(String.raw `^\s*\[${section}\]\s*(?:\r?\n|$)`, "m").exec(text);
281
+ if (!header) {
282
+ return "";
283
+ }
284
+ const rest = text.slice(header.index + header[0].length);
285
+ const nextHeader = rest.search(/^\s*\[[^\]]+\]\s*$/m);
286
+ return nextHeader === -1 ? rest : rest.slice(0, nextHeader);
287
+ }
288
+ function extractTomlString(sectionBody, key) {
289
+ const pattern = new RegExp(String.raw `^\s*${key}\s*=\s*(?:"([^"]*)"|'([^']*)')\s*$`, "m");
290
+ const match = sectionBody.match(pattern);
291
+ const value = match?.[1] ?? match?.[2];
292
+ return value ? value : undefined;
293
+ }
294
+ //# sourceMappingURL=nav-grouping-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav-grouping-catalog.js","sourceRoot":"","sources":["../../../src/output/html/nav-grouping-catalog.ts"],"names":[],"mappings":"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"}