@productbrain/cli 0.1.0-beta.6 → 0.1.0-beta.9
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 +84 -1
- package/dist/__tests__/capture.test.d.ts +2 -0
- package/dist/__tests__/capture.test.d.ts.map +1 -0
- package/dist/__tests__/capture.test.js +86 -0
- package/dist/__tests__/capture.test.js.map +1 -0
- package/dist/__tests__/constellation.test.d.ts +2 -0
- package/dist/__tests__/constellation.test.d.ts.map +1 -0
- package/dist/__tests__/constellation.test.js +260 -0
- package/dist/__tests__/constellation.test.js.map +1 -0
- package/dist/__tests__/fields.test.d.ts +2 -0
- package/dist/__tests__/fields.test.d.ts.map +1 -0
- package/dist/__tests__/fields.test.js +238 -0
- package/dist/__tests__/fields.test.js.map +1 -0
- package/dist/__tests__/handshake.test.d.ts +2 -0
- package/dist/__tests__/handshake.test.d.ts.map +1 -0
- package/dist/__tests__/handshake.test.js +187 -0
- package/dist/__tests__/handshake.test.js.map +1 -0
- package/dist/__tests__/relate.test.d.ts +2 -0
- package/dist/__tests__/relate.test.d.ts.map +1 -0
- package/dist/__tests__/relate.test.js +79 -0
- package/dist/__tests__/relate.test.js.map +1 -0
- package/dist/__tests__/repo-detect.test.d.ts +2 -0
- package/dist/__tests__/repo-detect.test.d.ts.map +1 -0
- package/dist/__tests__/repo-detect.test.js +119 -0
- package/dist/__tests__/repo-detect.test.js.map +1 -0
- package/dist/__tests__/runner.test.d.ts +2 -0
- package/dist/__tests__/runner.test.d.ts.map +1 -0
- package/dist/__tests__/runner.test.js +215 -0
- package/dist/__tests__/runner.test.js.map +1 -0
- package/dist/__tests__/session-touch.test.d.ts +2 -0
- package/dist/__tests__/session-touch.test.d.ts.map +1 -0
- package/dist/__tests__/session-touch.test.js +134 -0
- package/dist/__tests__/session-touch.test.js.map +1 -0
- package/dist/__tests__/session.test.d.ts +2 -0
- package/dist/__tests__/session.test.d.ts.map +1 -0
- package/dist/__tests__/session.test.js +52 -0
- package/dist/__tests__/session.test.js.map +1 -0
- package/dist/__tests__/strip.test.d.ts +2 -0
- package/dist/__tests__/strip.test.d.ts.map +1 -0
- package/dist/__tests__/strip.test.js +136 -0
- package/dist/__tests__/strip.test.js.map +1 -0
- package/dist/__tests__/update.test.d.ts +2 -0
- package/dist/__tests__/update.test.d.ts.map +1 -0
- package/dist/__tests__/update.test.js +148 -0
- package/dist/__tests__/update.test.js.map +1 -0
- package/dist/commands/capture.d.ts +21 -0
- package/dist/commands/capture.d.ts.map +1 -0
- package/dist/commands/capture.js +100 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/constellation.d.ts +11 -0
- package/dist/commands/constellation.d.ts.map +1 -0
- package/dist/commands/constellation.js +28 -0
- package/dist/commands/constellation.js.map +1 -0
- package/dist/commands/context.d.ts +2 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +15 -9
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/fields.d.ts +9 -0
- package/dist/commands/fields.d.ts.map +1 -0
- package/dist/commands/fields.js +26 -0
- package/dist/commands/fields.js.map +1 -0
- package/dist/commands/get.d.ts +8 -1
- package/dist/commands/get.d.ts.map +1 -1
- package/dist/commands/get.js +51 -6
- package/dist/commands/get.js.map +1 -1
- package/dist/commands/handshake.d.ts +18 -0
- package/dist/commands/handshake.d.ts.map +1 -0
- package/dist/commands/handshake.js +378 -0
- package/dist/commands/handshake.js.map +1 -0
- package/dist/commands/ingest.d.ts +1 -0
- package/dist/commands/ingest.d.ts.map +1 -1
- package/dist/commands/ingest.js +25 -8
- package/dist/commands/ingest.js.map +1 -1
- package/dist/commands/orient.d.ts +1 -0
- package/dist/commands/orient.d.ts.map +1 -1
- package/dist/commands/orient.js +13 -8
- package/dist/commands/orient.js.map +1 -1
- package/dist/commands/relate.d.ts +15 -0
- package/dist/commands/relate.d.ts.map +1 -0
- package/dist/commands/relate.js +72 -0
- package/dist/commands/relate.js.map +1 -0
- package/dist/commands/search.d.ts +1 -0
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +5 -3
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +134 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/update.d.ts +16 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +89 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/formatters/capture.d.ts +30 -0
- package/dist/formatters/capture.d.ts.map +1 -0
- package/dist/formatters/capture.js +58 -0
- package/dist/formatters/capture.js.map +1 -0
- package/dist/formatters/constellation.d.ts +34 -0
- package/dist/formatters/constellation.d.ts.map +1 -0
- package/dist/formatters/constellation.js +38 -0
- package/dist/formatters/constellation.js.map +1 -0
- package/dist/formatters/fields.d.ts +32 -0
- package/dist/formatters/fields.d.ts.map +1 -0
- package/dist/formatters/fields.js +49 -0
- package/dist/formatters/fields.js.map +1 -0
- package/dist/formatters/handshake.d.ts +17 -0
- package/dist/formatters/handshake.d.ts.map +1 -0
- package/dist/formatters/handshake.js +51 -0
- package/dist/formatters/handshake.js.map +1 -0
- package/dist/formatters/relate.d.ts +11 -0
- package/dist/formatters/relate.d.ts.map +1 -0
- package/dist/formatters/relate.js +10 -0
- package/dist/formatters/relate.js.map +1 -0
- package/dist/formatters/session.d.ts +11 -0
- package/dist/formatters/session.d.ts.map +1 -0
- package/dist/formatters/session.js +51 -0
- package/dist/formatters/session.js.map +1 -0
- package/dist/formatters/update.d.ts +17 -0
- package/dist/formatters/update.d.ts.map +1 -0
- package/dist/formatters/update.js +43 -0
- package/dist/formatters/update.js.map +1 -0
- package/dist/generators/adapters.d.ts +10 -0
- package/dist/generators/adapters.d.ts.map +1 -0
- package/dist/generators/adapters.js +102 -0
- package/dist/generators/adapters.js.map +1 -0
- package/dist/generators/briefing-md.d.ts +8 -0
- package/dist/generators/briefing-md.d.ts.map +1 -0
- package/dist/generators/briefing-md.js +51 -0
- package/dist/generators/briefing-md.js.map +1 -0
- package/dist/generators/context-md.d.ts +8 -0
- package/dist/generators/context-md.d.ts.map +1 -0
- package/dist/generators/context-md.js +123 -0
- package/dist/generators/context-md.js.map +1 -0
- package/dist/generators/portable-knowledge.d.ts +72 -0
- package/dist/generators/portable-knowledge.d.ts.map +1 -0
- package/dist/generators/portable-knowledge.js +246 -0
- package/dist/generators/portable-knowledge.js.map +1 -0
- package/dist/generators/portable-knowledge.test.d.ts +2 -0
- package/dist/generators/portable-knowledge.test.d.ts.map +1 -0
- package/dist/generators/portable-knowledge.test.js +399 -0
- package/dist/generators/portable-knowledge.test.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +231 -6
- package/dist/index.js.map +1 -1
- package/dist/lib/client.d.ts +19 -0
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/client.js +102 -8
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/repo-detect.d.ts +14 -0
- package/dist/lib/repo-detect.d.ts.map +1 -0
- package/dist/lib/repo-detect.js +58 -0
- package/dist/lib/repo-detect.js.map +1 -0
- package/dist/lib/runner.d.ts +31 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/runner.js +65 -0
- package/dist/lib/runner.js.map +1 -0
- package/dist/lib/session.d.ts +17 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +43 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/strip.d.ts +11 -0
- package/dist/lib/strip.d.ts.map +1 -0
- package/dist/lib/strip.js +26 -0
- package/dist/lib/strip.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates .productbrain/context.md — workspace briefing snapshot.
|
|
3
|
+
* Pure function: orient view in, markdown string out.
|
|
4
|
+
*/
|
|
5
|
+
function entryLine(e, maxPreview = 100) {
|
|
6
|
+
const id = e.entryId ?? e.name;
|
|
7
|
+
if (e.preview) {
|
|
8
|
+
const p = e.preview.length > maxPreview ? e.preview.slice(0, maxPreview) + '...' : e.preview;
|
|
9
|
+
return `- **${id}** — ${p}`;
|
|
10
|
+
}
|
|
11
|
+
return `- **${id}** — ${e.name}`;
|
|
12
|
+
}
|
|
13
|
+
export function generateContextMd(view, repo, timestamp) {
|
|
14
|
+
const lines = [];
|
|
15
|
+
lines.push('<!-- auto-generated by pb handshake — ' + timestamp + ' -->');
|
|
16
|
+
lines.push('<!-- Re-run `pb handshake` to refresh. Do not edit manually. -->');
|
|
17
|
+
lines.push('');
|
|
18
|
+
lines.push('# Product Brain — Workspace Context');
|
|
19
|
+
lines.push('');
|
|
20
|
+
lines.push(`_Generated: ${timestamp}_`);
|
|
21
|
+
if (repo.name)
|
|
22
|
+
lines.push(`_Repo: ${repo.name}${repo.repoSlug ? ` (${repo.repoSlug})` : ''}_`);
|
|
23
|
+
lines.push('');
|
|
24
|
+
// Stage
|
|
25
|
+
lines.push('## Stage');
|
|
26
|
+
lines.push(`**${view.stage}**`);
|
|
27
|
+
lines.push('');
|
|
28
|
+
// Strategic context
|
|
29
|
+
const sc = view.strategicContext;
|
|
30
|
+
lines.push('## Strategic Context');
|
|
31
|
+
if (sc.vision)
|
|
32
|
+
lines.push(`- **Vision**: ${sc.vision}`);
|
|
33
|
+
if (sc.purpose)
|
|
34
|
+
lines.push(`- **Purpose**: ${sc.purpose}`);
|
|
35
|
+
if (sc.currentBet)
|
|
36
|
+
lines.push(`- **Current bet**: ${sc.currentBet}`);
|
|
37
|
+
lines.push(`- **Active bets**: ${sc.activeBetCount}`);
|
|
38
|
+
lines.push(`- **Active tensions**: ${sc.activeTensionCount}`);
|
|
39
|
+
if (sc.productAreas.length > 0)
|
|
40
|
+
lines.push(`- **Product areas**: ${sc.productAreas.join(', ')}`);
|
|
41
|
+
lines.push('');
|
|
42
|
+
// Active bets
|
|
43
|
+
if (view.activeBets.length > 0) {
|
|
44
|
+
lines.push('## Active Bets');
|
|
45
|
+
for (const bet of view.activeBets.slice(0, 10)) {
|
|
46
|
+
lines.push(`- **${bet.entryId ?? bet.name}** — ${bet.name}`);
|
|
47
|
+
if (bet.linkedTensions && bet.linkedTensions.length > 0) {
|
|
48
|
+
for (const t of bet.linkedTensions.slice(0, 3)) {
|
|
49
|
+
lines.push(` - ${t.name}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
lines.push('');
|
|
54
|
+
}
|
|
55
|
+
// Governance
|
|
56
|
+
const gov = view.governance;
|
|
57
|
+
if (gov) {
|
|
58
|
+
const hasGov = gov.principles.length > 0 || gov.standards.length > 0 || gov.businessRules.length > 0;
|
|
59
|
+
if (hasGov) {
|
|
60
|
+
lines.push('## Governance');
|
|
61
|
+
if (gov.principles.length > 0) {
|
|
62
|
+
lines.push('### Principles');
|
|
63
|
+
for (const p of gov.principles.slice(0, 5))
|
|
64
|
+
lines.push(entryLine(p));
|
|
65
|
+
}
|
|
66
|
+
if (gov.standards.length > 0) {
|
|
67
|
+
lines.push('### Standards');
|
|
68
|
+
for (const s of gov.standards.slice(0, 5))
|
|
69
|
+
lines.push(entryLine(s));
|
|
70
|
+
}
|
|
71
|
+
if (gov.businessRules.length > 0) {
|
|
72
|
+
lines.push('### Business Rules');
|
|
73
|
+
for (const br of gov.businessRules.slice(0, 5))
|
|
74
|
+
lines.push(entryLine(br));
|
|
75
|
+
}
|
|
76
|
+
lines.push('');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Architecture notes
|
|
80
|
+
if (view.architectureNotes && view.architectureNotes.length > 0) {
|
|
81
|
+
lines.push('## Architecture');
|
|
82
|
+
for (const a of view.architectureNotes.slice(0, 5))
|
|
83
|
+
lines.push(entryLine(a));
|
|
84
|
+
lines.push('');
|
|
85
|
+
}
|
|
86
|
+
// Open tensions
|
|
87
|
+
if (view.tensions && view.tensions.length > 0) {
|
|
88
|
+
lines.push('## Open Tensions');
|
|
89
|
+
for (const t of view.tensions.slice(0, 10)) {
|
|
90
|
+
lines.push(`- **${t.entryId ?? t.name}** — ${t.name}`);
|
|
91
|
+
}
|
|
92
|
+
lines.push('');
|
|
93
|
+
}
|
|
94
|
+
// Planned work
|
|
95
|
+
const pw = view.plannedWork;
|
|
96
|
+
if (pw) {
|
|
97
|
+
const hasPlanned = pw.uncommittedDrafts.length > 0 || pw.inProgressEntries.length > 0 || pw.openTensions.length > 0;
|
|
98
|
+
if (hasPlanned) {
|
|
99
|
+
lines.push('## Planned Work');
|
|
100
|
+
if (pw.uncommittedDrafts.length > 0) {
|
|
101
|
+
lines.push('### Drafts');
|
|
102
|
+
for (const d of pw.uncommittedDrafts.slice(0, 5))
|
|
103
|
+
lines.push(`- ${d.name} [${d.collection}]`);
|
|
104
|
+
}
|
|
105
|
+
if (pw.inProgressEntries.length > 0) {
|
|
106
|
+
lines.push('### In Progress');
|
|
107
|
+
for (const e of pw.inProgressEntries.slice(0, 5))
|
|
108
|
+
lines.push(`- ${e.name} [${e.collection}]`);
|
|
109
|
+
}
|
|
110
|
+
if (pw.openTensions.length > 0) {
|
|
111
|
+
lines.push('### Open Tensions');
|
|
112
|
+
for (const t of pw.openTensions.slice(0, 5))
|
|
113
|
+
lines.push(`- ${t.name}`);
|
|
114
|
+
}
|
|
115
|
+
lines.push('');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
lines.push('---');
|
|
119
|
+
lines.push('_This is a snapshot. Run `pb orient` for live data. Run `pb get <ID>` for full entry details._');
|
|
120
|
+
lines.push('');
|
|
121
|
+
return lines.join('\n');
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=context-md.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-md.js","sourceRoot":"","sources":["../../src/generators/context-md.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable knowledge — reads canonical skills/rules from .productbrain/
|
|
3
|
+
* and generates tool-specific copies for Cursor, Claude Code, etc.
|
|
4
|
+
*/
|
|
5
|
+
export interface CanonicalSkill {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
triggers: string[];
|
|
9
|
+
body: string;
|
|
10
|
+
sourcePath: string;
|
|
11
|
+
targets?: string[];
|
|
12
|
+
level?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CanonicalRule {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
scope?: string;
|
|
18
|
+
autoApply: boolean;
|
|
19
|
+
body: string;
|
|
20
|
+
sourcePath: string;
|
|
21
|
+
targets?: string[];
|
|
22
|
+
level?: string;
|
|
23
|
+
}
|
|
24
|
+
export type TargetName = 'cursor' | 'claude' | 'copilot';
|
|
25
|
+
/** Returns true if the entry should be emitted to the given target. */
|
|
26
|
+
export declare function shouldEmitToTarget(entry: CanonicalRule | CanonicalSkill, target: TargetName): boolean;
|
|
27
|
+
export type HandshakeLevel = 'beginner' | 'intermediate' | 'expert';
|
|
28
|
+
/**
|
|
29
|
+
* Filter items by graduated level.
|
|
30
|
+
* - If requestedLevel is undefined/null: return ALL items (backward compat).
|
|
31
|
+
* - Items with no `level` field: always included (backward compat).
|
|
32
|
+
* - Otherwise: include items whose level is within the hierarchy for requestedLevel.
|
|
33
|
+
*/
|
|
34
|
+
export declare function filterByLevel<T extends {
|
|
35
|
+
level?: string;
|
|
36
|
+
}>(items: T[], requestedLevel?: string): T[];
|
|
37
|
+
export declare function readCanonicalSkills(productbrainDir: string): CanonicalSkill[];
|
|
38
|
+
export declare function readCanonicalRules(productbrainDir: string): CanonicalRule[];
|
|
39
|
+
/**
|
|
40
|
+
* Strip transport-conditional sections from a skill body.
|
|
41
|
+
*
|
|
42
|
+
* Sections are delimited by HTML comments:
|
|
43
|
+
* <!-- transport:TARGET_NAME -->
|
|
44
|
+
* ...content...
|
|
45
|
+
* <!-- /transport -->
|
|
46
|
+
*
|
|
47
|
+
* - Blocks matching the current target: keep the content, remove the delimiter comments.
|
|
48
|
+
* - Blocks for OTHER targets: remove entirely (delimiters + content).
|
|
49
|
+
* - Content outside any transport block: keep as-is (universal content).
|
|
50
|
+
*/
|
|
51
|
+
export declare function stripTransportSections(body: string, target: TargetName): string;
|
|
52
|
+
/**
|
|
53
|
+
* Generate a Cursor SKILL.md from a canonical skill.
|
|
54
|
+
* Cursor expects: name, description (with triggers embedded).
|
|
55
|
+
*/
|
|
56
|
+
export declare function generateCursorSkill(skill: CanonicalSkill): string;
|
|
57
|
+
/**
|
|
58
|
+
* Generate a Cursor .mdc rule from a canonical rule.
|
|
59
|
+
* Cursor expects: description, globs, alwaysApply.
|
|
60
|
+
*/
|
|
61
|
+
export declare function generateCursorRule(rule: CanonicalRule): string;
|
|
62
|
+
/**
|
|
63
|
+
* Generate a Claude Code .md rule from a canonical rule.
|
|
64
|
+
* Claude Code expects: description, optional paths (array).
|
|
65
|
+
*/
|
|
66
|
+
export declare function generateClaudeRule(rule: CanonicalRule): string;
|
|
67
|
+
/**
|
|
68
|
+
* Generate a Claude Code skill-router.md — imperative skill activation rule.
|
|
69
|
+
* Loaded automatically by Claude Code via .claude/rules/ with no paths (always active).
|
|
70
|
+
*/
|
|
71
|
+
export declare function generateClaudeSkillRouter(skills: CanonicalSkill[]): string;
|
|
72
|
+
//# sourceMappingURL=portable-knowledge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portable-knowledge.d.ts","sourceRoot":"","sources":["../../src/generators/portable-knowledge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAGrG;AAID,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,cAAc,GAAG,QAAQ,CAAC;AAUpE;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CASpG;AAkED,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,GAAG,cAAc,EAAE,CAsB7E;AAED,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,aAAa,EAAE,CAuB3E;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAa/E;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAyBjE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAc9D;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAqB9D;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CA4B1E"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable knowledge — reads canonical skills/rules from .productbrain/
|
|
3
|
+
* and generates tool-specific copies for Cursor, Claude Code, etc.
|
|
4
|
+
*/
|
|
5
|
+
import { readdirSync, readFileSync, existsSync } from 'fs';
|
|
6
|
+
import { join, basename } from 'path';
|
|
7
|
+
import { MARKER } from './adapters.js';
|
|
8
|
+
/** Returns true if the entry should be emitted to the given target. */
|
|
9
|
+
export function shouldEmitToTarget(entry, target) {
|
|
10
|
+
if (!entry.targets)
|
|
11
|
+
return true;
|
|
12
|
+
return entry.targets.includes(target);
|
|
13
|
+
}
|
|
14
|
+
const LEVEL_HIERARCHY = {
|
|
15
|
+
beginner: ['core'],
|
|
16
|
+
intermediate: ['core', 'intermediate'],
|
|
17
|
+
expert: ['core', 'intermediate', 'expert'],
|
|
18
|
+
};
|
|
19
|
+
const VALID_LEVELS = new Set(Object.keys(LEVEL_HIERARCHY));
|
|
20
|
+
/**
|
|
21
|
+
* Filter items by graduated level.
|
|
22
|
+
* - If requestedLevel is undefined/null: return ALL items (backward compat).
|
|
23
|
+
* - Items with no `level` field: always included (backward compat).
|
|
24
|
+
* - Otherwise: include items whose level is within the hierarchy for requestedLevel.
|
|
25
|
+
*/
|
|
26
|
+
export function filterByLevel(items, requestedLevel) {
|
|
27
|
+
if (!requestedLevel)
|
|
28
|
+
return items;
|
|
29
|
+
if (!VALID_LEVELS.has(requestedLevel)) {
|
|
30
|
+
throw new Error(`Unknown level "${requestedLevel}". Valid levels: ${[...VALID_LEVELS].join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
const allowedLevels = LEVEL_HIERARCHY[requestedLevel];
|
|
33
|
+
return items.filter((item) => !item.level || allowedLevels.includes(item.level));
|
|
34
|
+
}
|
|
35
|
+
// ── Frontmatter parser ─────────────────────────────────────────────────
|
|
36
|
+
function parseFrontmatter(raw) {
|
|
37
|
+
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
38
|
+
if (!match)
|
|
39
|
+
return { fields: new Map(), arrayFields: new Map(), body: raw };
|
|
40
|
+
const yaml = match[1];
|
|
41
|
+
const body = match[2];
|
|
42
|
+
const fields = new Map();
|
|
43
|
+
const arrayFields = new Map();
|
|
44
|
+
const lines = yaml.split('\n');
|
|
45
|
+
let currentKey = null;
|
|
46
|
+
let collectingMultiline = false;
|
|
47
|
+
let collectingArray = false;
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
// Array item: " - value"
|
|
50
|
+
if (collectingArray && currentKey && /^\s+-\s/.test(line)) {
|
|
51
|
+
const items = arrayFields.get(currentKey) ?? [];
|
|
52
|
+
items.push(line.replace(/^\s+-\s+/, '').trim().replace(/^["']|["']$/g, ''));
|
|
53
|
+
arrayFields.set(currentKey, items);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// Multiline continuation: indented text after >- or >
|
|
57
|
+
if (collectingMultiline && currentKey && /^\s+\S/.test(line)) {
|
|
58
|
+
const existing = fields.get(currentKey) ?? '';
|
|
59
|
+
fields.set(currentKey, (existing ? existing + ' ' : '') + line.trim());
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// New key — reset collection state
|
|
63
|
+
collectingMultiline = false;
|
|
64
|
+
collectingArray = false;
|
|
65
|
+
const kv = line.match(/^([\w-]+):\s*(.*)/);
|
|
66
|
+
if (!kv)
|
|
67
|
+
continue;
|
|
68
|
+
currentKey = kv[1];
|
|
69
|
+
const val = kv[2].trim();
|
|
70
|
+
if (val === '' || val === '>-' || val === '>') {
|
|
71
|
+
// Could be array or multiline — peek ahead via next iterations
|
|
72
|
+
// If next line starts with " -", it's an array; if indented text, it's multiline
|
|
73
|
+
collectingMultiline = val === '>-' || val === '>';
|
|
74
|
+
collectingArray = val === '';
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
fields.set(currentKey, val.replace(/^["']|["']$/g, ''));
|
|
78
|
+
}
|
|
79
|
+
return { fields, arrayFields, body };
|
|
80
|
+
}
|
|
81
|
+
// ── Readers ────────────────────────────────────────────────────────────
|
|
82
|
+
export function readCanonicalSkills(productbrainDir) {
|
|
83
|
+
const skillsDir = join(productbrainDir, 'skills');
|
|
84
|
+
if (!existsSync(skillsDir))
|
|
85
|
+
return [];
|
|
86
|
+
const files = readdirSync(skillsDir).filter((f) => f.endsWith('.md'));
|
|
87
|
+
const skills = [];
|
|
88
|
+
for (const file of files) {
|
|
89
|
+
const filePath = join(skillsDir, file);
|
|
90
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
91
|
+
const { fields, arrayFields, body } = parseFrontmatter(raw);
|
|
92
|
+
const name = fields.get('name') ?? basename(file, '.md');
|
|
93
|
+
const description = fields.get('description') ?? '';
|
|
94
|
+
const triggers = arrayFields.get('triggers') ?? [];
|
|
95
|
+
const targets = arrayFields.get('targets');
|
|
96
|
+
const level = fields.get('level');
|
|
97
|
+
skills.push({ name, description, triggers, body, sourcePath: filePath, targets: targets?.length ? targets : undefined, level: level || undefined });
|
|
98
|
+
}
|
|
99
|
+
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
100
|
+
}
|
|
101
|
+
export function readCanonicalRules(productbrainDir) {
|
|
102
|
+
const rulesDir = join(productbrainDir, 'rules');
|
|
103
|
+
if (!existsSync(rulesDir))
|
|
104
|
+
return [];
|
|
105
|
+
const files = readdirSync(rulesDir).filter((f) => f.endsWith('.md'));
|
|
106
|
+
const rules = [];
|
|
107
|
+
for (const file of files) {
|
|
108
|
+
const filePath = join(rulesDir, file);
|
|
109
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
110
|
+
const { fields, arrayFields, body } = parseFrontmatter(raw);
|
|
111
|
+
const name = fields.get('name') ?? basename(file, '.md');
|
|
112
|
+
const description = fields.get('description') ?? '';
|
|
113
|
+
const scope = fields.get('scope');
|
|
114
|
+
const autoApply = fields.get('autoApply') === 'true';
|
|
115
|
+
const targets = arrayFields.get('targets');
|
|
116
|
+
const level = fields.get('level');
|
|
117
|
+
rules.push({ name, description, scope, autoApply, body, sourcePath: filePath, targets: targets?.length ? targets : undefined, level: level || undefined });
|
|
118
|
+
}
|
|
119
|
+
return rules.sort((a, b) => a.name.localeCompare(b.name));
|
|
120
|
+
}
|
|
121
|
+
// ── Transport section stripping ───────────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* Strip transport-conditional sections from a skill body.
|
|
124
|
+
*
|
|
125
|
+
* Sections are delimited by HTML comments:
|
|
126
|
+
* <!-- transport:TARGET_NAME -->
|
|
127
|
+
* ...content...
|
|
128
|
+
* <!-- /transport -->
|
|
129
|
+
*
|
|
130
|
+
* - Blocks matching the current target: keep the content, remove the delimiter comments.
|
|
131
|
+
* - Blocks for OTHER targets: remove entirely (delimiters + content).
|
|
132
|
+
* - Content outside any transport block: keep as-is (universal content).
|
|
133
|
+
*/
|
|
134
|
+
export function stripTransportSections(body, target) {
|
|
135
|
+
// Match transport blocks: <!-- transport:NAME -->...<!-- /transport -->
|
|
136
|
+
// Using a non-greedy match between the opening and the NEXT closing tag.
|
|
137
|
+
const transportBlockRe = /<!-- transport:(\w+) -->\n?([\s\S]*?)<!-- \/transport -->\n?/g;
|
|
138
|
+
return body.replace(transportBlockRe, (_match, blockTarget, content) => {
|
|
139
|
+
if (blockTarget === target) {
|
|
140
|
+
// Keep the content, remove the delimiter comments
|
|
141
|
+
return content;
|
|
142
|
+
}
|
|
143
|
+
// Remove the entire block for other targets
|
|
144
|
+
return '';
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
// ── Cursor generators ──────────────────────────────────────────────────
|
|
148
|
+
/**
|
|
149
|
+
* Generate a Cursor SKILL.md from a canonical skill.
|
|
150
|
+
* Cursor expects: name, description (with triggers embedded).
|
|
151
|
+
*/
|
|
152
|
+
export function generateCursorSkill(skill) {
|
|
153
|
+
// Cursor puts trigger phrases in the description field
|
|
154
|
+
const triggerLine = skill.triggers.length > 0
|
|
155
|
+
? ` Use when the user says ${skill.triggers.map((t) => `"${t}"`).join(', ')}.`
|
|
156
|
+
: '';
|
|
157
|
+
const description = skill.description + triggerLine;
|
|
158
|
+
// Strip transport sections BEFORE path replacement
|
|
159
|
+
const stripped = stripTransportSections(skill.body, 'cursor');
|
|
160
|
+
// Replace canonical paths with Cursor paths in body
|
|
161
|
+
const body = stripped
|
|
162
|
+
.replace(/\.productbrain\/rules\/(\S+)\.md/g, '.cursor/rules/$1.mdc')
|
|
163
|
+
.replace(/\.productbrain\/skills\/(\S+)\.md/g, '.cursor/skills/$1/SKILL.md');
|
|
164
|
+
return `---
|
|
165
|
+
name: ${skill.name}
|
|
166
|
+
description: >-
|
|
167
|
+
${description.replace(/\n/g, '\n ')}
|
|
168
|
+
---
|
|
169
|
+
<!-- ${MARKER} — source: .productbrain/skills/${skill.name}.md -->
|
|
170
|
+
|
|
171
|
+
${body}`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Generate a Cursor .mdc rule from a canonical rule.
|
|
175
|
+
* Cursor expects: description, globs, alwaysApply.
|
|
176
|
+
*/
|
|
177
|
+
export function generateCursorRule(rule) {
|
|
178
|
+
// Replace canonical paths with Cursor paths in body
|
|
179
|
+
const body = rule.body
|
|
180
|
+
.replace(/\.productbrain\/rules\/(\S+)\.md/g, '.cursor/rules/$1.mdc')
|
|
181
|
+
.replace(/\.productbrain\/skills\/(\S+)\.md/g, '.cursor/skills/$1/SKILL.md');
|
|
182
|
+
return `---
|
|
183
|
+
description: ${rule.description}
|
|
184
|
+
globs: ${rule.scope ?? ''}
|
|
185
|
+
alwaysApply: ${rule.autoApply}
|
|
186
|
+
---
|
|
187
|
+
<!-- ${MARKER} — source: .productbrain/rules/${rule.name}.md -->
|
|
188
|
+
|
|
189
|
+
${body}`;
|
|
190
|
+
}
|
|
191
|
+
// ── Claude Code generators ────────────────────────────────────────────
|
|
192
|
+
/**
|
|
193
|
+
* Generate a Claude Code .md rule from a canonical rule.
|
|
194
|
+
* Claude Code expects: description, optional paths (array).
|
|
195
|
+
*/
|
|
196
|
+
export function generateClaudeRule(rule) {
|
|
197
|
+
// Replace canonical paths with Claude Code paths in body
|
|
198
|
+
const body = rule.body
|
|
199
|
+
.replace(/\.productbrain\/rules\/(\S+)\.md/g, '.claude/rules/$1.md')
|
|
200
|
+
.replace(/\.productbrain\/skills\/(\S+)\.md/g, '.productbrain/skills/$1.md');
|
|
201
|
+
// Build frontmatter
|
|
202
|
+
const fmLines = ['---'];
|
|
203
|
+
fmLines.push(`description: "${rule.description.replace(/"/g, '\\"')}"`);
|
|
204
|
+
if (rule.scope) {
|
|
205
|
+
fmLines.push('paths:');
|
|
206
|
+
fmLines.push(` - "${rule.scope}"`);
|
|
207
|
+
}
|
|
208
|
+
fmLines.push('---');
|
|
209
|
+
return `${fmLines.join('\n')}
|
|
210
|
+
<!-- ${MARKER} — source: .productbrain/rules/${rule.name}.md -->
|
|
211
|
+
|
|
212
|
+
${body}`;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Generate a Claude Code skill-router.md — imperative skill activation rule.
|
|
216
|
+
* Loaded automatically by Claude Code via .claude/rules/ with no paths (always active).
|
|
217
|
+
*/
|
|
218
|
+
export function generateClaudeSkillRouter(skills) {
|
|
219
|
+
if (skills.length === 0)
|
|
220
|
+
return '';
|
|
221
|
+
const triggerBlocks = skills.map((skill) => {
|
|
222
|
+
const triggerList = skill.triggers.map((t) => `"${t}"`).join(' / ');
|
|
223
|
+
return `- User says ${triggerList}
|
|
224
|
+
→ READ \`.productbrain/skills/${skill.name}.md\` — follow its full protocol`;
|
|
225
|
+
});
|
|
226
|
+
return `---
|
|
227
|
+
description: "Skill activation router — MUST read skill file before responding when triggers match"
|
|
228
|
+
---
|
|
229
|
+
<!-- ${MARKER} — source: generated from .productbrain/skills/*.md -->
|
|
230
|
+
|
|
231
|
+
# Skill Router — Mandatory Activation
|
|
232
|
+
|
|
233
|
+
BEFORE responding to ANY user message, scan for these triggers.
|
|
234
|
+
If ANY trigger matches: READ the skill file FIRST, then follow its full protocol.
|
|
235
|
+
Do NOT respond with your own approach. The skill IS the approach.
|
|
236
|
+
|
|
237
|
+
## Triggers
|
|
238
|
+
|
|
239
|
+
${triggerBlocks.join('\n\n')}
|
|
240
|
+
|
|
241
|
+
## Why This Matters
|
|
242
|
+
|
|
243
|
+
Skipping the skill file produces a shallow response that misses governance, blast radius, and Chain context. This is the #1 quality failure in this workspace. The skill files encode the full protocol — including Chain checks, domain identification, and review gates — that a direct response will miss.
|
|
244
|
+
`;
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=portable-knowledge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portable-knowledge.js","sourceRoot":"","sources":["../../src/generators/portable-knowledge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portable-knowledge.test.d.ts","sourceRoot":"","sources":["../../src/generators/portable-knowledge.test.ts"],"names":[],"mappings":""}
|