maestro-flow 0.3.43 → 0.3.45
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/.claude/commands/learn-decompose.md +10 -15
- package/.claude/commands/learn-follow.md +11 -16
- package/.claude/commands/learn-investigate.md +18 -22
- package/.claude/commands/learn-retro.md +23 -26
- package/.claude/commands/learn-second-opinion.md +11 -16
- package/.claude/commands/maestro-analyze.md +8 -0
- package/.claude/commands/maestro-brainstorm.md +8 -0
- package/.claude/commands/maestro-execute.md +11 -3
- package/.claude/commands/maestro-learn.md +4 -4
- package/.claude/commands/maestro-milestone-complete.md +6 -6
- package/.claude/commands/maestro-plan.md +8 -0
- package/.claude/commands/maestro-ralph-execute.md +1 -2
- package/.claude/commands/maestro-tools-execute.md +117 -0
- package/.claude/commands/maestro-tools-register.md +136 -0
- package/.claude/commands/maestro-ui-codify.md +67 -0
- package/.claude/commands/manage-harvest.md +1 -1
- package/.claude/commands/manage-learn.md +5 -7
- package/.claude/commands/manage-wiki.md +1 -1
- package/.claude/commands/quality-auto-test.md +1 -1
- package/.claude/commands/quality-debug.md +8 -0
- package/.claude/commands/quality-retrospective.md +4 -5
- package/.claude/commands/quality-review.md +8 -0
- package/.claude/commands/spec-add.md +18 -2
- package/.claude/commands/spec-load.md +25 -6
- package/.claude/commands/spec-setup.md +2 -2
- package/.claude/commands/wiki-connect.md +1 -1
- package/.claude/commands/wiki-digest.md +5 -6
- package/.claude/skills/codify-to-knowhow/SKILL.md +167 -0
- package/.claude/skills/codify-to-knowhow/phases/01-load-manifest.md +101 -0
- package/.claude/skills/codify-to-knowhow/phases/02-generate-knowhow.md +97 -0
- package/.claude/skills/codify-to-knowhow/phases/03-generate-specs.md +92 -0
- package/.claude/skills/codify-to-knowhow/phases/04-index-verify.md +119 -0
- package/.codex/skills/codify-to-knowhow/SKILL.md +427 -0
- package/.codex/skills/learn-decompose/SKILL.md +8 -8
- package/.codex/skills/learn-follow/SKILL.md +6 -6
- package/.codex/skills/learn-investigate/SKILL.md +4 -4
- package/.codex/skills/learn-retro/SKILL.md +7 -7
- package/.codex/skills/learn-second-opinion/SKILL.md +6 -6
- package/.codex/skills/maestro-analyze/SKILL.md +491 -491
- package/.codex/skills/maestro-collab/SKILL.md +1 -1
- package/.codex/skills/maestro-learn/SKILL.md +1 -1
- package/.codex/skills/maestro-milestone-complete/SKILL.md +122 -122
- package/.codex/skills/maestro-plan/SKILL.md +485 -485
- package/.codex/skills/maestro-tools-execute/SKILL.md +105 -0
- package/.codex/skills/maestro-tools-register/SKILL.md +149 -0
- package/.codex/skills/maestro-ui-codify/SKILL.md +398 -0
- package/.codex/skills/maestro-verify/SKILL.md +486 -486
- package/.codex/skills/manage-harvest/SKILL.md +3 -3
- package/.codex/skills/manage-issue-discover/SKILL.md +431 -431
- package/.codex/skills/manage-learn/SKILL.md +13 -13
- package/.codex/skills/manage-wiki/SKILL.md +1 -1
- package/.codex/skills/quality-auto-test/SKILL.md +553 -553
- package/.codex/skills/quality-refactor/SKILL.md +151 -151
- package/.codex/skills/quality-retrospective/SKILL.md +6 -6
- package/.codex/skills/spec-add/SKILL.md +104 -101
- package/.codex/skills/spec-load/SKILL.md +73 -77
- package/.codex/skills/team-quality-assurance/roles/executor/role.md +1 -1
- package/.codex/skills/wiki-connect/SKILL.md +5 -5
- package/.codex/skills/wiki-digest/SKILL.md +8 -8
- package/dashboard/dist-server/dashboard/src/server/routes/wiki.js +0 -1
- package/dashboard/dist-server/dashboard/src/server/routes/wiki.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/state/fs-watcher.js +2 -2
- package/dashboard/dist-server/dashboard/src/server/state/fs-watcher.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/supervisor/self-learning-service.d.ts +1 -1
- package/dashboard/dist-server/dashboard/src/server/supervisor/self-learning-service.js +1 -1
- package/dashboard/dist-server/dashboard/src/server/supervisor/self-learning-service.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/search.js +2 -2
- package/dashboard/dist-server/dashboard/src/server/wiki/search.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/spec-entry-parser.d.ts +6 -3
- package/dashboard/dist-server/dashboard/src/server/wiki/spec-entry-parser.js +29 -14
- package/dashboard/dist-server/dashboard/src/server/wiki/spec-entry-parser.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/stress.test.js +0 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/stress.test.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/virtual-wiki-adapters.d.ts +1 -2
- package/dashboard/dist-server/dashboard/src/server/wiki/virtual-wiki-adapters.js +2 -52
- package/dashboard/dist-server/dashboard/src/server/wiki/virtual-wiki-adapters.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.js +68 -33
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/wiki-types.d.ts +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/writer-stress.test.js +1 -11
- package/dashboard/dist-server/dashboard/src/server/wiki/writer-stress.test.js.map +1 -1
- package/dashboard/dist-server/dashboard/src/server/wiki/writer.d.ts +5 -4
- package/dashboard/dist-server/dashboard/src/server/wiki/writer.js +26 -12
- package/dashboard/dist-server/dashboard/src/server/wiki/writer.js.map +1 -1
- package/dashboard/dist-server/src/agents/cli-agent-runner.js +5 -5
- package/dashboard/dist-server/src/agents/cli-agent-runner.js.map +1 -1
- package/dashboard/dist-server/src/tools/spec-entry-parser.d.ts +4 -2
- package/dashboard/dist-server/src/tools/spec-entry-parser.js +19 -7
- package/dashboard/dist-server/src/tools/spec-entry-parser.js.map +1 -1
- package/dashboard/dist-server/src/tools/spec-loader.d.ts +4 -4
- package/dashboard/dist-server/src/tools/spec-loader.js +166 -23
- package/dashboard/dist-server/src/tools/spec-loader.js.map +1 -1
- package/dist/src/agents/cli-agent-runner.js +5 -5
- package/dist/src/agents/cli-agent-runner.js.map +1 -1
- package/dist/src/commands/knowhow.d.ts.map +1 -1
- package/dist/src/commands/knowhow.js +25 -6
- package/dist/src/commands/knowhow.js.map +1 -1
- package/dist/src/commands/spec.d.ts +1 -1
- package/dist/src/commands/spec.d.ts.map +1 -1
- package/dist/src/commands/spec.js +70 -3
- package/dist/src/commands/spec.js.map +1 -1
- package/dist/src/commands/wiki.d.ts.map +1 -1
- package/dist/src/commands/wiki.js +45 -6
- package/dist/src/commands/wiki.js.map +1 -1
- package/dist/src/hooks/keyword-spec-injector.js +1 -1
- package/dist/src/hooks/keyword-spec-injector.js.map +1 -1
- package/dist/src/hooks/plugins/spec-injection-plugin.js +2 -2
- package/dist/src/hooks/plugins/spec-injection-plugin.js.map +1 -1
- package/dist/src/hooks/spec-injector.d.ts +0 -6
- package/dist/src/hooks/spec-injector.d.ts.map +1 -1
- package/dist/src/hooks/spec-injector.js +36 -43
- package/dist/src/hooks/spec-injector.js.map +1 -1
- package/dist/src/hooks/wiki-role-loader.d.ts +18 -0
- package/dist/src/hooks/wiki-role-loader.d.ts.map +1 -0
- package/dist/src/hooks/wiki-role-loader.js +43 -0
- package/dist/src/hooks/wiki-role-loader.js.map +1 -0
- package/dist/src/tools/spec-entry-parser.d.ts +4 -2
- package/dist/src/tools/spec-entry-parser.d.ts.map +1 -1
- package/dist/src/tools/spec-entry-parser.js +19 -7
- package/dist/src/tools/spec-entry-parser.js.map +1 -1
- package/dist/src/tools/spec-init.js +54 -54
- package/dist/src/tools/spec-loader.d.ts +4 -4
- package/dist/src/tools/spec-loader.d.ts.map +1 -1
- package/dist/src/tools/spec-loader.js +166 -23
- package/dist/src/tools/spec-loader.js.map +1 -1
- package/dist/src/tools/spec-writer.d.ts +5 -0
- package/dist/src/tools/spec-writer.d.ts.map +1 -1
- package/dist/src/tools/spec-writer.js +27 -0
- package/dist/src/tools/spec-writer.js.map +1 -1
- package/dist/src/tools/store-knowhow.d.ts.map +1 -1
- package/dist/src/tools/store-knowhow.js +56 -27
- package/dist/src/tools/store-knowhow.js.map +1 -1
- package/package.json +1 -1
- package/workflows/execute.md +1 -1
- package/workflows/harvest.md +13 -13
- package/workflows/knowhow.md +72 -11
- package/workflows/learn.md +70 -58
- package/workflows/milestone-complete.md +1 -1
- package/workflows/retrospective.md +50 -56
- package/workflows/specs-load.md +15 -14
- package/workflows/tools-spec.md +65 -0
- package/workflows/ui-codify-extract.md +373 -0
- package/workflows/ui-codify-knowhow.md +258 -0
- package/workflows/ui-codify-package.md +161 -0
- package/workflows/ui-codify.md +225 -0
- package/workflows/verify.md +1 -1
- package/workflows/wiki-connect.md +7 -7
- package/workflows/wiki-digest.md +13 -13
- package/workflows/wiki-manage.md +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiki Category Loader — synchronous wiki knowledge loading by category.
|
|
3
|
+
*
|
|
4
|
+
* Reads the persisted wiki-index.json to quickly load category-tagged
|
|
5
|
+
* entries without requiring the full WikiIndexer or dashboard server.
|
|
6
|
+
* Used by spec-injector hook for lightweight context injection.
|
|
7
|
+
*/
|
|
8
|
+
export interface WikiCategoryResult {
|
|
9
|
+
content: string;
|
|
10
|
+
entryCount: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Load wiki entries matching a given category from the persisted index.
|
|
14
|
+
* Returns formatted title+summary content for injection, or null if
|
|
15
|
+
* no matching entries exist.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadWikiByCategory(projectPath: string, category: string): WikiCategoryResult | null;
|
|
18
|
+
//# sourceMappingURL=wiki-role-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-role-loader.d.ts","sourceRoot":"","sources":["../../../src/hooks/wiki-role-loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CA6BnG"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiki Category Loader — synchronous wiki knowledge loading by category.
|
|
3
|
+
*
|
|
4
|
+
* Reads the persisted wiki-index.json to quickly load category-tagged
|
|
5
|
+
* entries without requiring the full WikiIndexer or dashboard server.
|
|
6
|
+
* Used by spec-injector hook for lightweight context injection.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
/**
|
|
11
|
+
* Load wiki entries matching a given category from the persisted index.
|
|
12
|
+
* Returns formatted title+summary content for injection, or null if
|
|
13
|
+
* no matching entries exist.
|
|
14
|
+
*/
|
|
15
|
+
export function loadWikiByCategory(projectPath, category) {
|
|
16
|
+
const indexPath = join(projectPath, '.workflow', 'wiki-index.json');
|
|
17
|
+
let raw;
|
|
18
|
+
try {
|
|
19
|
+
raw = readFileSync(indexPath, 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
let index;
|
|
25
|
+
try {
|
|
26
|
+
index = JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (!index?.entries?.length)
|
|
32
|
+
return null;
|
|
33
|
+
const matched = index.entries
|
|
34
|
+
.filter(e => e.category === category)
|
|
35
|
+
.sort((a, b) => b.updated.localeCompare(a.updated))
|
|
36
|
+
.slice(0, 10);
|
|
37
|
+
if (matched.length === 0)
|
|
38
|
+
return null;
|
|
39
|
+
const content = `# Wiki Knowledge (category: ${category})\n\n` +
|
|
40
|
+
matched.map(e => `### [${e.type}] ${e.title}\n${e.summary}`).join('\n\n---\n\n');
|
|
41
|
+
return { content, entryCount: matched.length };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=wiki-role-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-role-loader.js","sourceRoot":"","sources":["../../../src/hooks/wiki-role-loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOjC;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB,EAAE,QAAgB;IACtE,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;IACpE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAuH,CAAC;IAC5H,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO;SAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC;SACpC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SAClD,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEhB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,OAAO,GAAG,+BAA+B,QAAQ,OAAO;QAC5D,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAEnF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;AACjD,CAAC"}
|
|
@@ -9,6 +9,7 @@ export interface SpecEntryParsed {
|
|
|
9
9
|
keywords: string[];
|
|
10
10
|
date: string;
|
|
11
11
|
source?: string;
|
|
12
|
+
ref?: string;
|
|
12
13
|
title: string;
|
|
13
14
|
content: string;
|
|
14
15
|
lineStart: number;
|
|
@@ -28,7 +29,7 @@ export interface ParseError {
|
|
|
28
29
|
line: number;
|
|
29
30
|
message: string;
|
|
30
31
|
}
|
|
31
|
-
export declare const VALID_CATEGORIES: readonly ["coding", "arch", "
|
|
32
|
+
export declare const VALID_CATEGORIES: readonly ["coding", "arch", "debug", "test", "review", "learning"];
|
|
32
33
|
export type ValidCategory = (typeof VALID_CATEGORIES)[number];
|
|
33
34
|
/**
|
|
34
35
|
* Parse spec file content into structured entries.
|
|
@@ -51,6 +52,7 @@ export declare function validateCategoryMatch(entry: SpecEntryParsed, fileCatego
|
|
|
51
52
|
export declare function formatSpecEntries(entries: SpecEntryParsed[], keyword?: string): string;
|
|
52
53
|
/**
|
|
53
54
|
* Format a single entry for writing to a spec file.
|
|
55
|
+
* Writes `category=` attribute as the primary classifier.
|
|
54
56
|
*/
|
|
55
|
-
export declare function formatNewEntry(category: string, keywords: string[], date: string, title: string, content: string, source?: string): string;
|
|
57
|
+
export declare function formatNewEntry(category: string, keywords: string[], date: string, title: string, content: string, source?: string, ref?: string): string;
|
|
56
58
|
//# sourceMappingURL=spec-entry-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-entry-parser.d.ts","sourceRoot":"","sources":["../../../src/tools/spec-entry-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"spec-entry-parser.d.ts","sourceRoot":"","sources":["../../../src/tools/spec-entry-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,eAAO,MAAM,gBAAgB,oEAAqE,CAAC;AAEnG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAyB9D;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAwD7D;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,EAAE,CAoBlE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKjG;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAQtF;AAqCD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,EACf,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,CAMR"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// ============================================================================
|
|
8
8
|
// Valid categories (shared with spec-loader)
|
|
9
9
|
// ============================================================================
|
|
10
|
-
export const VALID_CATEGORIES = ['coding', 'arch', '
|
|
10
|
+
export const VALID_CATEGORIES = ['coding', 'arch', 'debug', 'test', 'review', 'learning'];
|
|
11
11
|
// ============================================================================
|
|
12
12
|
// Core regex
|
|
13
13
|
// ============================================================================
|
|
@@ -50,11 +50,13 @@ export function parseSpecEntries(content) {
|
|
|
50
50
|
const titleMatch = body.match(HEADING_RE);
|
|
51
51
|
const title = titleMatch ? titleMatch[1].trim() : '';
|
|
52
52
|
// Validate and build entry
|
|
53
|
+
const ref = attrs.ref || undefined;
|
|
53
54
|
const entry = {
|
|
54
55
|
category: attrs.category ?? '',
|
|
55
56
|
keywords: attrs.keywords ? attrs.keywords.split(',').map(k => k.trim().toLowerCase()).filter(Boolean) : [],
|
|
56
57
|
date: attrs.date ?? '',
|
|
57
58
|
source: attrs.source,
|
|
59
|
+
ref,
|
|
58
60
|
title,
|
|
59
61
|
content: body.trim(),
|
|
60
62
|
lineStart,
|
|
@@ -137,23 +139,33 @@ function formatEntryClean(e) {
|
|
|
137
139
|
meta.push(e.date);
|
|
138
140
|
if (e.source)
|
|
139
141
|
meta.push(e.source);
|
|
140
|
-
if (meta.length === 0)
|
|
142
|
+
if (meta.length === 0 && !e.ref)
|
|
141
143
|
return e.content;
|
|
142
|
-
const metaLine = `> ${meta.join(' \u00b7 ')}
|
|
144
|
+
const metaLine = meta.length > 0 ? `> ${meta.join(' \u00b7 ')}` : '';
|
|
145
|
+
// Build ref detail line: knowhow/AST-oauth-flow.md → knowhow-oauth-flow
|
|
146
|
+
let refLine = '';
|
|
147
|
+
if (e.ref) {
|
|
148
|
+
const refStem = e.ref.replace(/^knowhow\//, '').replace(/\.md$/, '');
|
|
149
|
+
const refSlug = refStem.replace(/^(KNW|TIP|TPL|RCP|REF|DCS|AST|BLP|DOC)-/i, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
150
|
+
const refId = `knowhow-${refSlug}`;
|
|
151
|
+
refLine = `\n\u2192 Detail: maestro wiki load ${refId}`;
|
|
152
|
+
}
|
|
143
153
|
// Inject after first ### heading line
|
|
144
154
|
const idx = e.content.indexOf('\n');
|
|
145
155
|
if (idx !== -1 && e.content.trimStart().startsWith('###')) {
|
|
146
|
-
return e.content.slice(0, idx) + '\n' + metaLine + e.content.slice(idx);
|
|
156
|
+
return e.content.slice(0, idx) + (metaLine ? '\n' + metaLine : '') + refLine + e.content.slice(idx);
|
|
147
157
|
}
|
|
148
|
-
return metaLine + '\n\n' + e.content;
|
|
158
|
+
return (metaLine ? metaLine + '\n\n' : '') + e.content + refLine;
|
|
149
159
|
}
|
|
150
160
|
/**
|
|
151
161
|
* Format a single entry for writing to a spec file.
|
|
162
|
+
* Writes `category=` attribute as the primary classifier.
|
|
152
163
|
*/
|
|
153
|
-
export function formatNewEntry(category, keywords, date, title, content, source) {
|
|
164
|
+
export function formatNewEntry(category, keywords, date, title, content, source, ref) {
|
|
154
165
|
const kwStr = keywords.map(k => k.toLowerCase().trim()).filter(Boolean).join(',');
|
|
155
166
|
const sourceAttr = source ? ` source="${source}"` : '';
|
|
156
|
-
|
|
167
|
+
const refAttr = ref ? ` ref="${ref}"` : '';
|
|
168
|
+
return `<spec-entry category="${category}" keywords="${kwStr}" date="${date}"${sourceAttr}${refAttr}>\n\n### ${title}\n\n${content}\n\n</spec-entry>`;
|
|
157
169
|
}
|
|
158
170
|
// ============================================================================
|
|
159
171
|
// Internal helpers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-entry-parser.js","sourceRoot":"","sources":["../../../src/tools/spec-entry-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"spec-entry-parser.js","sourceRoot":"","sources":["../../../src/tools/spec-entry-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,18 +20,18 @@ const SEED_DOCS = [
|
|
|
20
20
|
category: 'coding',
|
|
21
21
|
keywords: ['style', 'naming', 'import', 'pattern', 'convention', 'formatting'],
|
|
22
22
|
},
|
|
23
|
-
body: `# Coding Conventions
|
|
24
|
-
|
|
25
|
-
## Formatting
|
|
26
|
-
|
|
27
|
-
## Naming
|
|
28
|
-
|
|
29
|
-
## Imports
|
|
30
|
-
|
|
31
|
-
## Patterns
|
|
32
|
-
|
|
33
|
-
## Entries
|
|
34
|
-
|
|
23
|
+
body: `# Coding Conventions
|
|
24
|
+
|
|
25
|
+
## Formatting
|
|
26
|
+
|
|
27
|
+
## Naming
|
|
28
|
+
|
|
29
|
+
## Imports
|
|
30
|
+
|
|
31
|
+
## Patterns
|
|
32
|
+
|
|
33
|
+
## Entries
|
|
34
|
+
|
|
35
35
|
`,
|
|
36
36
|
},
|
|
37
37
|
{
|
|
@@ -43,18 +43,18 @@ const SEED_DOCS = [
|
|
|
43
43
|
category: 'arch',
|
|
44
44
|
keywords: ['architecture', 'module', 'layer', 'boundary', 'dependency', 'structure'],
|
|
45
45
|
},
|
|
46
|
-
body: `# Architecture Constraints
|
|
47
|
-
|
|
48
|
-
## Module Structure
|
|
49
|
-
|
|
50
|
-
## Layer Boundaries
|
|
51
|
-
|
|
52
|
-
## Dependency Rules
|
|
53
|
-
|
|
54
|
-
## Technology Constraints
|
|
55
|
-
|
|
56
|
-
## Entries
|
|
57
|
-
|
|
46
|
+
body: `# Architecture Constraints
|
|
47
|
+
|
|
48
|
+
## Module Structure
|
|
49
|
+
|
|
50
|
+
## Layer Boundaries
|
|
51
|
+
|
|
52
|
+
## Dependency Rules
|
|
53
|
+
|
|
54
|
+
## Technology Constraints
|
|
55
|
+
|
|
56
|
+
## Entries
|
|
57
|
+
|
|
58
58
|
`,
|
|
59
59
|
},
|
|
60
60
|
{
|
|
@@ -66,12 +66,12 @@ const SEED_DOCS = [
|
|
|
66
66
|
category: 'learning',
|
|
67
67
|
keywords: ['bug', 'lesson', 'gotcha', 'learning'],
|
|
68
68
|
},
|
|
69
|
-
body: `# Learnings
|
|
70
|
-
|
|
71
|
-
Add entries with: \`/spec-add learning <description>\`
|
|
72
|
-
|
|
73
|
-
## Entries
|
|
74
|
-
|
|
69
|
+
body: `# Learnings
|
|
70
|
+
|
|
71
|
+
Add entries with: \`/spec-add learning <description>\`
|
|
72
|
+
|
|
73
|
+
## Entries
|
|
74
|
+
|
|
75
75
|
`,
|
|
76
76
|
},
|
|
77
77
|
{
|
|
@@ -83,10 +83,10 @@ Add entries with: \`/spec-add learning <description>\`
|
|
|
83
83
|
category: 'quality',
|
|
84
84
|
keywords: ['quality', 'lint', 'rule', 'enforcement'],
|
|
85
85
|
},
|
|
86
|
-
body: `# Quality Rules
|
|
87
|
-
|
|
88
|
-
## Entries
|
|
89
|
-
|
|
86
|
+
body: `# Quality Rules
|
|
87
|
+
|
|
88
|
+
## Entries
|
|
89
|
+
|
|
90
90
|
`,
|
|
91
91
|
},
|
|
92
92
|
{
|
|
@@ -98,10 +98,10 @@ Add entries with: \`/spec-add learning <description>\`
|
|
|
98
98
|
category: 'debug',
|
|
99
99
|
keywords: ['debug', 'issue', 'workaround', 'root-cause', 'gotcha'],
|
|
100
100
|
},
|
|
101
|
-
body: `# Debug Notes
|
|
102
|
-
|
|
103
|
-
## Entries
|
|
104
|
-
|
|
101
|
+
body: `# Debug Notes
|
|
102
|
+
|
|
103
|
+
## Entries
|
|
104
|
+
|
|
105
105
|
`,
|
|
106
106
|
},
|
|
107
107
|
{
|
|
@@ -113,18 +113,18 @@ Add entries with: \`/spec-add learning <description>\`
|
|
|
113
113
|
category: 'test',
|
|
114
114
|
keywords: ['test', 'coverage', 'mock', 'fixture', 'assertion', 'framework'],
|
|
115
115
|
},
|
|
116
|
-
body: `# Test Conventions
|
|
117
|
-
|
|
118
|
-
## Framework
|
|
119
|
-
|
|
120
|
-
## Directory Structure
|
|
121
|
-
|
|
122
|
-
## Naming Conventions
|
|
123
|
-
|
|
124
|
-
## Patterns
|
|
125
|
-
|
|
126
|
-
## Entries
|
|
127
|
-
|
|
116
|
+
body: `# Test Conventions
|
|
117
|
+
|
|
118
|
+
## Framework
|
|
119
|
+
|
|
120
|
+
## Directory Structure
|
|
121
|
+
|
|
122
|
+
## Naming Conventions
|
|
123
|
+
|
|
124
|
+
## Patterns
|
|
125
|
+
|
|
126
|
+
## Entries
|
|
127
|
+
|
|
128
128
|
`,
|
|
129
129
|
},
|
|
130
130
|
{
|
|
@@ -136,10 +136,10 @@ Add entries with: \`/spec-add learning <description>\`
|
|
|
136
136
|
category: 'review',
|
|
137
137
|
keywords: ['review', 'checklist', 'gate', 'approval', 'standard'],
|
|
138
138
|
},
|
|
139
|
-
body: `# Review Standards
|
|
140
|
-
|
|
141
|
-
## Entries
|
|
142
|
-
|
|
139
|
+
body: `# Review Standards
|
|
140
|
+
|
|
141
|
+
## Entries
|
|
142
|
+
|
|
143
143
|
`,
|
|
144
144
|
},
|
|
145
145
|
];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spec Loader
|
|
2
|
+
* Spec Loader
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Category-based loading with keyword cross-matching and knowhow tool discovery.
|
|
5
5
|
* Reads .workflow/specs/*.md, filters by category via static mapping,
|
|
6
|
-
* returns concatenated content.
|
|
6
|
+
* discovers knowhow tools with matching category, returns concatenated content.
|
|
7
7
|
*/
|
|
8
|
-
export type SpecCategory = 'coding' | 'arch' | '
|
|
8
|
+
export type SpecCategory = 'coding' | 'arch' | 'debug' | 'test' | 'review' | 'learning';
|
|
9
9
|
export type SpecScope = 'project' | 'global' | 'team' | 'personal';
|
|
10
10
|
export interface SpecLoadResult {
|
|
11
11
|
content: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-loader.d.ts","sourceRoot":"","sources":["../../../src/tools/spec-loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,
|
|
1
|
+
{"version":3,"file":"spec-loader.d.ts","sourceRoot":"","sources":["../../../src/tools/spec-loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAExF,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAEnE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAOrD,CAAC;AAGF,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAcvD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAW1F;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,CAqDrK"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spec Loader
|
|
2
|
+
* Spec Loader
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Category-based loading with keyword cross-matching and knowhow tool discovery.
|
|
5
5
|
* Reads .workflow/specs/*.md, filters by category via static mapping,
|
|
6
|
-
* returns concatenated content.
|
|
6
|
+
* discovers knowhow tools with matching category, returns concatenated content.
|
|
7
7
|
*/
|
|
8
8
|
import { readFileSync, existsSync, readdirSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
@@ -15,7 +15,6 @@ import { paths } from '../config/paths.js';
|
|
|
15
15
|
export const CATEGORY_MAP = {
|
|
16
16
|
'coding-conventions.md': 'coding',
|
|
17
17
|
'architecture-constraints.md': 'arch',
|
|
18
|
-
'quality-rules.md': 'quality',
|
|
19
18
|
'debug-notes.md': 'debug',
|
|
20
19
|
'test-conventions.md': 'test',
|
|
21
20
|
'review-standards.md': 'review',
|
|
@@ -87,6 +86,14 @@ export function loadSpecs(projectPath, category, uid, keyword, scope, options) {
|
|
|
87
86
|
allMatched.push(...matched);
|
|
88
87
|
totalCount += matched.length;
|
|
89
88
|
}
|
|
89
|
+
// Tool discovery: scan knowhow/ for documents matching category + tool: true
|
|
90
|
+
if (category) {
|
|
91
|
+
const toolSection = discoverKnowhowTools(join(projectPath, '.workflow'), category);
|
|
92
|
+
if (toolSection) {
|
|
93
|
+
allSections.push(toolSection.content);
|
|
94
|
+
totalCount += toolSection.count;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
90
97
|
return {
|
|
91
98
|
content: allSections.length > 0
|
|
92
99
|
? `# Project Specs (${totalCount} loaded)\n\n${allSections.join('\n\n---\n\n')}`
|
|
@@ -117,6 +124,10 @@ function buildLayers(projectPath, uid, scope, globalDir) {
|
|
|
117
124
|
/**
|
|
118
125
|
* Load spec files from a single directory. Returns empty arrays if the
|
|
119
126
|
* directory does not exist or is unreadable.
|
|
127
|
+
*
|
|
128
|
+
* When `category` is provided:
|
|
129
|
+
* - Primary category doc is loaded in full
|
|
130
|
+
* - Other files: only entries with matching keywords are included (cross-category)
|
|
120
131
|
*/
|
|
121
132
|
function loadFromDir(specsDir, category, keyword) {
|
|
122
133
|
if (!existsSync(specsDir))
|
|
@@ -144,7 +155,11 @@ function loadFromDir(specsDir, category, keyword) {
|
|
|
144
155
|
const body = stripFrontmatter(raw).trim();
|
|
145
156
|
if (!body)
|
|
146
157
|
continue;
|
|
147
|
-
|
|
158
|
+
// Primary category doc → full load; other files → keyword-filtered only
|
|
159
|
+
const fileCategory = CATEGORY_MAP[file];
|
|
160
|
+
const isPrimaryDoc = category && fileCategory === category;
|
|
161
|
+
const workflowRoot = join(specsDir, '..');
|
|
162
|
+
const formatted = formatFileContent(body, keyword, isPrimaryDoc ? undefined : category, workflowRoot);
|
|
148
163
|
if (formatted) {
|
|
149
164
|
sections.push(formatted);
|
|
150
165
|
matched.push(file);
|
|
@@ -156,48 +171,177 @@ function loadFromDir(specsDir, category, keyword) {
|
|
|
156
171
|
// Internal
|
|
157
172
|
// ============================================================================
|
|
158
173
|
function shouldInclude(filename, category) {
|
|
159
|
-
// No category filter → load all
|
|
160
174
|
if (!category)
|
|
161
|
-
return true;
|
|
175
|
+
return true; // No filter → load all
|
|
176
|
+
// Category filter: include primary doc + all other files (for keyword cross-matching)
|
|
162
177
|
const cat = CATEGORY_MAP[filename];
|
|
163
|
-
if (cat)
|
|
164
|
-
return
|
|
165
|
-
//
|
|
166
|
-
|
|
178
|
+
if (!cat)
|
|
179
|
+
return false; // Unknown file
|
|
180
|
+
// Primary category doc → always include (full load)
|
|
181
|
+
if (cat === category)
|
|
182
|
+
return true;
|
|
183
|
+
// Other category files → include for keyword-based cross-category matching
|
|
184
|
+
return true;
|
|
167
185
|
}
|
|
168
186
|
/**
|
|
169
187
|
* Parse file body, strip <spec-entry> tags, format clean output with metadata.
|
|
170
188
|
* When keyword is provided, only return matching entries.
|
|
189
|
+
* When crossCategory is provided, only return entries whose keywords overlap
|
|
190
|
+
* (cross-category matching for non-primary docs).
|
|
171
191
|
* Falls back to raw body for files with no structured entries.
|
|
172
192
|
*/
|
|
173
|
-
function formatFileContent(body, keyword) {
|
|
193
|
+
function formatFileContent(body, keyword, crossCategory, workflowRoot) {
|
|
174
194
|
const { entries, legacy } = parseSpecEntries(body);
|
|
175
195
|
// No structured entries → pass through raw body (or keyword-grep it)
|
|
176
196
|
if (entries.length === 0 && legacy.length === 0) {
|
|
197
|
+
// Cross-category mode: non-primary docs with no structured entries are skipped
|
|
198
|
+
if (crossCategory)
|
|
199
|
+
return null;
|
|
177
200
|
if (keyword) {
|
|
178
201
|
return body.toLowerCase().includes(keyword.toLowerCase()) ? body : null;
|
|
179
202
|
}
|
|
180
203
|
return body;
|
|
181
204
|
}
|
|
205
|
+
// In cross-category mode: only show entries that have keyword overlap
|
|
206
|
+
let filteredEntries = entries;
|
|
207
|
+
if (crossCategory && keyword) {
|
|
208
|
+
const kw = keyword.toLowerCase();
|
|
209
|
+
filteredEntries = entries.filter(e => e.keywords.includes(kw));
|
|
210
|
+
if (filteredEntries.length === 0)
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
else if (crossCategory) {
|
|
214
|
+
// Cross-category without keyword → skip (no way to match)
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
182
217
|
const parts = [];
|
|
218
|
+
// Separate ref entries (lightweight display) from regular entries
|
|
219
|
+
const refEntries = filteredEntries.filter(e => e.ref);
|
|
220
|
+
const regularEntries = filteredEntries.filter(e => !e.ref);
|
|
183
221
|
if (keyword) {
|
|
184
222
|
const kw = keyword.toLowerCase();
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
223
|
+
const matchedRegular = regularEntries.filter(e => e.keywords.includes(kw));
|
|
224
|
+
const matchedRef = refEntries.filter(e => e.keywords.includes(kw));
|
|
225
|
+
if (matchedRegular.length > 0)
|
|
226
|
+
parts.push(formatSpecEntries(matchedRegular));
|
|
227
|
+
if (matchedRef.length > 0)
|
|
228
|
+
parts.push(matchedRef.map(e => formatRefEntry(e, workflowRoot)).join('\n\n---\n\n'));
|
|
229
|
+
if (!crossCategory) {
|
|
230
|
+
for (const leg of legacy) {
|
|
231
|
+
if (leg.content.toLowerCase().includes(kw))
|
|
232
|
+
parts.push(leg.content);
|
|
233
|
+
}
|
|
191
234
|
}
|
|
192
235
|
}
|
|
193
236
|
else {
|
|
194
|
-
if (
|
|
195
|
-
parts.push(formatSpecEntries(
|
|
196
|
-
|
|
197
|
-
parts.push(
|
|
237
|
+
if (regularEntries.length > 0)
|
|
238
|
+
parts.push(formatSpecEntries(regularEntries));
|
|
239
|
+
if (refEntries.length > 0)
|
|
240
|
+
parts.push(refEntries.map(e => formatRefEntry(e, workflowRoot)).join('\n\n---\n\n'));
|
|
241
|
+
if (!crossCategory) {
|
|
242
|
+
for (const leg of legacy)
|
|
243
|
+
parts.push(leg.content);
|
|
244
|
+
}
|
|
198
245
|
}
|
|
199
246
|
return parts.length > 0 ? parts.join('\n\n---\n\n') : null;
|
|
200
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Format a ref entry as a lightweight summary with a load command hint.
|
|
250
|
+
*
|
|
251
|
+
* Summary resolution order:
|
|
252
|
+
* 1. YAML `summary` field from the referenced knowhow document
|
|
253
|
+
* 2. Spec-entry content body (first 200 chars after heading)
|
|
254
|
+
*/
|
|
255
|
+
function formatRefEntry(e, workflowRoot) {
|
|
256
|
+
const refStem = (e.ref ?? '').replace(/^knowhow\//, '').replace(/\.md$/, '');
|
|
257
|
+
const refSlug = refStem.replace(/^(KNW|TIP|TPL|RCP|REF|DCS|AST|BLP|DOC)-/i, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
258
|
+
const refId = `knowhow-${refSlug}`;
|
|
259
|
+
// Try to read YAML summary from the referenced knowhow document
|
|
260
|
+
let summary = resolveRefSummary(e.ref, workflowRoot);
|
|
261
|
+
// Fallback: extract summary from spec-entry content (strip heading)
|
|
262
|
+
if (!summary) {
|
|
263
|
+
summary = e.content;
|
|
264
|
+
const headingIdx = summary.indexOf('\n');
|
|
265
|
+
if (headingIdx !== -1 && summary.trimStart().startsWith('###')) {
|
|
266
|
+
summary = summary.slice(headingIdx).trim();
|
|
267
|
+
}
|
|
268
|
+
summary = summary.slice(0, 200).replace(/\s+/g, ' ').trim();
|
|
269
|
+
}
|
|
270
|
+
return `### ${e.title}\n\n${summary}\n\n\u2192 Detail: maestro wiki load ${refId}`;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Read a knowhow file's YAML frontmatter `summary` field.
|
|
274
|
+
* Returns null if the file doesn't exist or has no summary.
|
|
275
|
+
*/
|
|
276
|
+
function resolveRefSummary(ref, workflowRoot) {
|
|
277
|
+
if (!ref || !workflowRoot)
|
|
278
|
+
return null;
|
|
279
|
+
const absPath = join(workflowRoot, ref);
|
|
280
|
+
try {
|
|
281
|
+
const raw = readFileSync(absPath, 'utf-8');
|
|
282
|
+
const fmMatch = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
283
|
+
if (!fmMatch)
|
|
284
|
+
return null;
|
|
285
|
+
const summaryMatch = fmMatch[1].match(/^summary:\s*"?(.+?)"?\s*$/m);
|
|
286
|
+
return summaryMatch ? summaryMatch[1].trim() : null;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Scan knowhow/ for documents matching category + tool: true in YAML frontmatter.
|
|
294
|
+
* Returns a formatted section with tool summaries and load commands.
|
|
295
|
+
*/
|
|
296
|
+
function discoverKnowhowTools(workflowRoot, category) {
|
|
297
|
+
const knowhowDir = join(workflowRoot, 'knowhow');
|
|
298
|
+
if (!existsSync(knowhowDir))
|
|
299
|
+
return null;
|
|
300
|
+
let files;
|
|
301
|
+
try {
|
|
302
|
+
files = readdirSync(knowhowDir).filter(f => f.endsWith('.md'));
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
const tools = [];
|
|
308
|
+
for (const file of files) {
|
|
309
|
+
try {
|
|
310
|
+
const raw = readFileSync(join(knowhowDir, file), 'utf-8');
|
|
311
|
+
const fmMatch = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
312
|
+
if (!fmMatch)
|
|
313
|
+
continue;
|
|
314
|
+
const fm = fmMatch[1];
|
|
315
|
+
// Check tool: true
|
|
316
|
+
if (!/^tool:\s*true\s*$/m.test(fm))
|
|
317
|
+
continue;
|
|
318
|
+
// Check category match
|
|
319
|
+
const catMatch = fm.match(/^category:\s*(.+)$/m);
|
|
320
|
+
if (!catMatch || catMatch[1].trim() !== category)
|
|
321
|
+
continue;
|
|
322
|
+
const titleMatch = fm.match(/^title:\s*(.+)$/m);
|
|
323
|
+
const summaryMatch = fm.match(/^summary:\s*"?(.+?)"?\s*$/m);
|
|
324
|
+
const title = titleMatch ? titleMatch[1].trim() : file;
|
|
325
|
+
// Summary: YAML summary field, or first paragraph after frontmatter
|
|
326
|
+
let summary = summaryMatch ? summaryMatch[1].trim() : '';
|
|
327
|
+
if (!summary) {
|
|
328
|
+
const body = raw.slice(fmMatch[0].length + 1).trim();
|
|
329
|
+
summary = body.split('\n\n')[0].slice(0, 200).replace(/\s+/g, ' ');
|
|
330
|
+
}
|
|
331
|
+
const stem = file.replace(/\.md$/, '');
|
|
332
|
+
const slug = stem.replace(/^(KNW|TIP|TPL|RCP|REF|DCS|AST|BLP|DOC)-/i, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
333
|
+
tools.push({ title, summary, id: `knowhow-${slug}` });
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (tools.length === 0)
|
|
340
|
+
return null;
|
|
341
|
+
const content = `## Available Tools (${category})\n\n` +
|
|
342
|
+
tools.map(t => `### ${t.title} (tool)\n\n${t.summary}\n\n→ Load: maestro wiki load ${t.id}`).join('\n\n---\n\n');
|
|
343
|
+
return { content, count: tools.length };
|
|
344
|
+
}
|
|
201
345
|
function stripFrontmatter(raw) {
|
|
202
346
|
const trimmed = raw.trimStart();
|
|
203
347
|
if (!trimmed.startsWith('---'))
|
|
@@ -217,7 +361,6 @@ const AUTO_INIT_SEEDS = [
|
|
|
217
361
|
['coding-conventions.md', '# Coding Conventions\n\n## Entries\n\n'],
|
|
218
362
|
['architecture-constraints.md', '# Architecture Constraints\n\n## Entries\n\n'],
|
|
219
363
|
['learnings.md', '# Learnings\n\n## Entries\n\n'],
|
|
220
|
-
['quality-rules.md', '# Quality Rules\n\n## Entries\n\n'],
|
|
221
364
|
['debug-notes.md', '# Debug Notes\n\n## Entries\n\n'],
|
|
222
365
|
['test-conventions.md', '# Test Conventions\n\n## Entries\n\n'],
|
|
223
366
|
['review-standards.md', '# Review Standards\n\n## Entries\n\n'],
|