opencode-snippets 1.6.0 → 1.7.0
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 +68 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -55
- package/dist/index.js.map +1 -1
- package/dist/src/config.d.ts +11 -2
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +20 -10
- package/dist/src/config.js.map +1 -1
- package/dist/src/constants.d.ts +7 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +7 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/expander.d.ts +13 -4
- package/dist/src/expander.d.ts.map +1 -1
- package/dist/src/expander.js +29 -11
- package/dist/src/expander.js.map +1 -1
- package/dist/src/hook-types.d.ts +39 -0
- package/dist/src/hook-types.d.ts.map +1 -0
- package/dist/src/hook-types.js +5 -0
- package/dist/src/hook-types.js.map +1 -0
- package/dist/src/injection-manager.d.ts +20 -0
- package/dist/src/injection-manager.d.ts.map +1 -0
- package/dist/src/injection-manager.js +36 -0
- package/dist/src/injection-manager.js.map +1 -0
- package/dist/src/skill-loader.d.ts +35 -0
- package/dist/src/skill-loader.d.ts.map +1 -0
- package/dist/src/skill-loader.js +118 -0
- package/dist/src/skill-loader.js.map +1 -0
- package/dist/src/skill-renderer.d.ts +14 -0
- package/dist/src/skill-renderer.d.ts.map +1 -0
- package/dist/src/skill-renderer.js +42 -0
- package/dist/src/skill-renderer.js.map +1 -0
- package/dist/src/types.d.ts +4 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/skill/snippets/SKILL.md +69 -5
package/dist/src/expander.js
CHANGED
|
@@ -5,7 +5,7 @@ import { logger } from "./logger.js";
|
|
|
5
5
|
*/
|
|
6
6
|
const MAX_EXPANSION_COUNT = 15;
|
|
7
7
|
/**
|
|
8
|
-
* Parses snippet content to extract inline text and prepend/append blocks
|
|
8
|
+
* Parses snippet content to extract inline text and prepend/append/inject blocks
|
|
9
9
|
*
|
|
10
10
|
* Uses a lenient stack-based parser:
|
|
11
11
|
* - Unclosed tags → treat rest of content as block
|
|
@@ -13,14 +13,18 @@ const MAX_EXPANSION_COUNT = 15;
|
|
|
13
13
|
* - Multiple blocks → collected in document order
|
|
14
14
|
*
|
|
15
15
|
* @param content - The raw snippet content to parse
|
|
16
|
-
* @
|
|
16
|
+
* @param options - Parsing options
|
|
17
|
+
* @returns Parsed content with inline, prepend, append, and inject parts, or null on error
|
|
17
18
|
*/
|
|
18
|
-
export function parseSnippetBlocks(content) {
|
|
19
|
+
export function parseSnippetBlocks(content, options = {}) {
|
|
20
|
+
const { extractInject = true } = options;
|
|
19
21
|
const prepend = [];
|
|
20
22
|
const append = [];
|
|
23
|
+
const inject = [];
|
|
21
24
|
let inline = "";
|
|
22
|
-
//
|
|
23
|
-
const
|
|
25
|
+
// Build regex pattern based on what tags we're processing
|
|
26
|
+
const tagTypes = extractInject ? "prepend|append|inject" : "prepend|append";
|
|
27
|
+
const tagPattern = new RegExp(`<(/?)(?<tagName>${tagTypes})>`, "gi");
|
|
24
28
|
let lastIndex = 0;
|
|
25
29
|
let currentBlock = null;
|
|
26
30
|
let match = tagPattern.exec(content);
|
|
@@ -46,9 +50,12 @@ export function parseSnippetBlocks(content) {
|
|
|
46
50
|
if (currentBlock.type === "prepend") {
|
|
47
51
|
prepend.push(blockContent);
|
|
48
52
|
}
|
|
49
|
-
else {
|
|
53
|
+
else if (currentBlock.type === "append") {
|
|
50
54
|
append.push(blockContent);
|
|
51
55
|
}
|
|
56
|
+
else {
|
|
57
|
+
inject.push(blockContent);
|
|
58
|
+
}
|
|
52
59
|
}
|
|
53
60
|
lastIndex = tagEnd;
|
|
54
61
|
currentBlock = null;
|
|
@@ -74,9 +81,12 @@ export function parseSnippetBlocks(content) {
|
|
|
74
81
|
if (currentBlock.type === "prepend") {
|
|
75
82
|
prepend.push(blockContent);
|
|
76
83
|
}
|
|
77
|
-
else {
|
|
84
|
+
else if (currentBlock.type === "append") {
|
|
78
85
|
append.push(blockContent);
|
|
79
86
|
}
|
|
87
|
+
else {
|
|
88
|
+
inject.push(blockContent);
|
|
89
|
+
}
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
92
|
else {
|
|
@@ -87,6 +97,7 @@ export function parseSnippetBlocks(content) {
|
|
|
87
97
|
inline: inline.trim(),
|
|
88
98
|
prepend,
|
|
89
99
|
append,
|
|
100
|
+
inject,
|
|
90
101
|
};
|
|
91
102
|
}
|
|
92
103
|
/**
|
|
@@ -98,11 +109,13 @@ export function parseSnippetBlocks(content) {
|
|
|
98
109
|
* @param text - The text containing hashtags to expand
|
|
99
110
|
* @param registry - The snippet registry to look up hashtags
|
|
100
111
|
* @param expansionCounts - Map tracking how many times each snippet has been expanded
|
|
112
|
+
* @param options - Expansion options
|
|
101
113
|
* @returns ExpansionResult with text and collected blocks
|
|
102
114
|
*/
|
|
103
|
-
export function expandHashtags(text, registry, expansionCounts = new Map()) {
|
|
115
|
+
export function expandHashtags(text, registry, expansionCounts = new Map(), options = {}) {
|
|
104
116
|
const collectedPrepend = [];
|
|
105
117
|
const collectedAppend = [];
|
|
118
|
+
const collectedInject = [];
|
|
106
119
|
let expanded = text;
|
|
107
120
|
let hasChanges = true;
|
|
108
121
|
// Keep expanding until no more hashtags are found
|
|
@@ -114,6 +127,7 @@ export function expandHashtags(text, registry, expansionCounts = new Map()) {
|
|
|
114
127
|
// We need to collect blocks during replacement, so we track them here
|
|
115
128
|
const roundPrepend = [];
|
|
116
129
|
const roundAppend = [];
|
|
130
|
+
const roundInject = [];
|
|
117
131
|
expanded = expanded.replace(PATTERNS.HASHTAG, (match, name) => {
|
|
118
132
|
const key = name.toLowerCase();
|
|
119
133
|
const snippet = registry.get(key);
|
|
@@ -131,25 +145,28 @@ export function expandHashtags(text, registry, expansionCounts = new Map()) {
|
|
|
131
145
|
}
|
|
132
146
|
expansionCounts.set(key, count);
|
|
133
147
|
// Parse the snippet content for blocks
|
|
134
|
-
const parsed = parseSnippetBlocks(snippet.content);
|
|
148
|
+
const parsed = parseSnippetBlocks(snippet.content, options);
|
|
135
149
|
if (parsed === null) {
|
|
136
150
|
// Parse error - leave hashtag unchanged
|
|
137
151
|
logger.warn(`Failed to parse snippet '${key}', leaving hashtag unchanged`);
|
|
138
152
|
return match;
|
|
139
153
|
}
|
|
140
|
-
// Collect prepend/append blocks
|
|
154
|
+
// Collect prepend/append/inject blocks
|
|
141
155
|
roundPrepend.push(...parsed.prepend);
|
|
142
156
|
roundAppend.push(...parsed.append);
|
|
157
|
+
roundInject.push(...parsed.inject);
|
|
143
158
|
// Recursively expand any hashtags in the inline content
|
|
144
|
-
const nestedResult = expandHashtags(parsed.inline, registry, expansionCounts);
|
|
159
|
+
const nestedResult = expandHashtags(parsed.inline, registry, expansionCounts, options);
|
|
145
160
|
// Collect blocks from nested expansion
|
|
146
161
|
roundPrepend.push(...nestedResult.prepend);
|
|
147
162
|
roundAppend.push(...nestedResult.append);
|
|
163
|
+
roundInject.push(...nestedResult.inject);
|
|
148
164
|
return nestedResult.text;
|
|
149
165
|
});
|
|
150
166
|
// Add this round's blocks to collected blocks
|
|
151
167
|
collectedPrepend.push(...roundPrepend);
|
|
152
168
|
collectedAppend.push(...roundAppend);
|
|
169
|
+
collectedInject.push(...roundInject);
|
|
153
170
|
// Only continue if the text actually changed AND no loop was detected
|
|
154
171
|
hasChanges = expanded !== previous && !loopDetected;
|
|
155
172
|
}
|
|
@@ -157,6 +174,7 @@ export function expandHashtags(text, registry, expansionCounts = new Map()) {
|
|
|
157
174
|
text: expanded,
|
|
158
175
|
prepend: collectedPrepend,
|
|
159
176
|
append: collectedAppend,
|
|
177
|
+
inject: collectedInject,
|
|
160
178
|
};
|
|
161
179
|
}
|
|
162
180
|
/**
|
package/dist/src/expander.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expander.js","sourceRoot":"","sources":["../../src/expander.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC;;GAEG;AACH,MAAM,mBAAmB,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"expander.js","sourceRoot":"","sources":["../../src/expander.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin hook types - minimal definitions for type safety
|
|
3
|
+
*/
|
|
4
|
+
export interface MessagePart {
|
|
5
|
+
type: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
ignored?: boolean;
|
|
8
|
+
snippetsProcessed?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ChatMessageInput {
|
|
11
|
+
sessionID: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ChatMessageOutput {
|
|
14
|
+
message: {
|
|
15
|
+
role: string;
|
|
16
|
+
};
|
|
17
|
+
parts: MessagePart[];
|
|
18
|
+
}
|
|
19
|
+
export interface TransformMessageInfo {
|
|
20
|
+
role: string;
|
|
21
|
+
sessionID?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface TransformMessage {
|
|
24
|
+
info: TransformMessageInfo;
|
|
25
|
+
parts: MessagePart[];
|
|
26
|
+
}
|
|
27
|
+
export interface TransformInput {
|
|
28
|
+
sessionID?: string;
|
|
29
|
+
session?: {
|
|
30
|
+
id?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface TransformOutput {
|
|
34
|
+
messages: TransformMessage[];
|
|
35
|
+
}
|
|
36
|
+
export interface SessionIdleEvent {
|
|
37
|
+
sessionID: string;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=hook-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-types.d.ts","sourceRoot":"","sources":["../../src/hook-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QACR,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-types.js","sourceRoot":"","sources":["../../src/hook-types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages injection lifecycle per session.
|
|
3
|
+
* Injections persist for the entire agentic loop until session idle.
|
|
4
|
+
*/
|
|
5
|
+
export declare class InjectionManager {
|
|
6
|
+
private activeInjections;
|
|
7
|
+
/**
|
|
8
|
+
* Adds injections to a session without duplicates.
|
|
9
|
+
*/
|
|
10
|
+
addInjections(sessionID: string, newInjections: string[]): void;
|
|
11
|
+
/**
|
|
12
|
+
* Gets active injections for a session without removing them.
|
|
13
|
+
*/
|
|
14
|
+
getInjections(sessionID: string): string[] | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Clears all injections for a session.
|
|
17
|
+
*/
|
|
18
|
+
clearSession(sessionID: string): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=injection-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injection-manager.d.ts","sourceRoot":"","sources":["../../src/injection-manager.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,gBAAgB,CAA+B;IAEvD;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI;IAW/D;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IAItD;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAMtC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { logger } from "./logger.js";
|
|
2
|
+
/**
|
|
3
|
+
* Manages injection lifecycle per session.
|
|
4
|
+
* Injections persist for the entire agentic loop until session idle.
|
|
5
|
+
*/
|
|
6
|
+
export class InjectionManager {
|
|
7
|
+
activeInjections = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Adds injections to a session without duplicates.
|
|
10
|
+
*/
|
|
11
|
+
addInjections(sessionID, newInjections) {
|
|
12
|
+
if (newInjections.length === 0)
|
|
13
|
+
return;
|
|
14
|
+
const existing = this.activeInjections.get(sessionID) || [];
|
|
15
|
+
const uniqueInjections = newInjections.filter((inj) => !existing.includes(inj));
|
|
16
|
+
if (uniqueInjections.length > 0) {
|
|
17
|
+
this.activeInjections.set(sessionID, [...existing, ...uniqueInjections]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Gets active injections for a session without removing them.
|
|
22
|
+
*/
|
|
23
|
+
getInjections(sessionID) {
|
|
24
|
+
return this.activeInjections.get(sessionID);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Clears all injections for a session.
|
|
28
|
+
*/
|
|
29
|
+
clearSession(sessionID) {
|
|
30
|
+
if (this.activeInjections.has(sessionID)) {
|
|
31
|
+
this.activeInjections.delete(sessionID);
|
|
32
|
+
logger.debug("Cleared active injections on session idle", { sessionID });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=injection-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injection-manager.js","sourceRoot":"","sources":["../../src/injection-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACnB,gBAAgB,GAAG,IAAI,GAAG,EAAoB,CAAC;IAEvD;;OAEG;IACH,aAAa,CAAC,SAAiB,EAAE,aAAuB;QACtD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEvC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAC5D,MAAM,gBAAgB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QAEhF,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,QAAQ,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,SAAiB;QAC7B,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,SAAiB;QAC5B,IAAI,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACxC,MAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loaded skill info
|
|
3
|
+
*/
|
|
4
|
+
export interface SkillInfo {
|
|
5
|
+
/** The skill name */
|
|
6
|
+
name: string;
|
|
7
|
+
/** The skill content body (markdown, excluding frontmatter) */
|
|
8
|
+
content: string;
|
|
9
|
+
/** Optional description from frontmatter */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** Where the skill was loaded from */
|
|
12
|
+
source: "global" | "project";
|
|
13
|
+
/** Full path to the skill file */
|
|
14
|
+
filePath: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Skill registry that maps skill names to their info
|
|
18
|
+
*/
|
|
19
|
+
export type SkillRegistry = Map<string, SkillInfo>;
|
|
20
|
+
/**
|
|
21
|
+
* Loads all skills from global and project directories
|
|
22
|
+
*
|
|
23
|
+
* @param projectDir - Optional project directory path
|
|
24
|
+
* @returns A map of skill names (lowercase) to their SkillInfo
|
|
25
|
+
*/
|
|
26
|
+
export declare function loadSkills(projectDir?: string): Promise<SkillRegistry>;
|
|
27
|
+
/**
|
|
28
|
+
* Gets a skill by name from the registry
|
|
29
|
+
*
|
|
30
|
+
* @param registry - The skill registry
|
|
31
|
+
* @param name - The skill name (case-insensitive)
|
|
32
|
+
* @returns The skill info, or undefined if not found
|
|
33
|
+
*/
|
|
34
|
+
export declare function getSkill(registry: SkillRegistry, name: string): SkillInfo | undefined;
|
|
35
|
+
//# sourceMappingURL=skill-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-loader.d.ts","sourceRoot":"","sources":["../../src/skill-loader.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AA2BnD;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiB5E;AA2ED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAErF"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import matter from "gray-matter";
|
|
5
|
+
import { logger } from "./logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* OpenCode skill directory patterns (in order of priority)
|
|
8
|
+
*
|
|
9
|
+
* Global paths:
|
|
10
|
+
* - ~/.config/opencode/skill/<name>/SKILL.md
|
|
11
|
+
* - ~/.config/opencode/skills/<name>/SKILL.md
|
|
12
|
+
*
|
|
13
|
+
* Project paths (higher priority):
|
|
14
|
+
* - .opencode/skill/<name>/SKILL.md
|
|
15
|
+
* - .opencode/skills/<name>/SKILL.md
|
|
16
|
+
* - .claude/skills/<name>/SKILL.md (Claude Code compatibility)
|
|
17
|
+
*/
|
|
18
|
+
const GLOBAL_SKILL_DIRS = [
|
|
19
|
+
join(homedir(), ".config", "opencode", "skill"),
|
|
20
|
+
join(homedir(), ".config", "opencode", "skills"),
|
|
21
|
+
];
|
|
22
|
+
function getProjectSkillDirs(projectDir) {
|
|
23
|
+
return [
|
|
24
|
+
join(projectDir, ".opencode", "skill"),
|
|
25
|
+
join(projectDir, ".opencode", "skills"),
|
|
26
|
+
join(projectDir, ".claude", "skills"),
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Loads all skills from global and project directories
|
|
31
|
+
*
|
|
32
|
+
* @param projectDir - Optional project directory path
|
|
33
|
+
* @returns A map of skill names (lowercase) to their SkillInfo
|
|
34
|
+
*/
|
|
35
|
+
export async function loadSkills(projectDir) {
|
|
36
|
+
const skills = new Map();
|
|
37
|
+
// Load from global directories first
|
|
38
|
+
for (const dir of GLOBAL_SKILL_DIRS) {
|
|
39
|
+
await loadFromDirectory(dir, skills, "global");
|
|
40
|
+
}
|
|
41
|
+
// Load from project directories (overrides global)
|
|
42
|
+
if (projectDir) {
|
|
43
|
+
for (const dir of getProjectSkillDirs(projectDir)) {
|
|
44
|
+
await loadFromDirectory(dir, skills, "project");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
logger.debug("Skills loaded", { count: skills.size });
|
|
48
|
+
return skills;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Loads skills from a specific directory
|
|
52
|
+
*/
|
|
53
|
+
async function loadFromDirectory(dir, registry, source) {
|
|
54
|
+
try {
|
|
55
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
if (!entry.isDirectory())
|
|
58
|
+
continue;
|
|
59
|
+
const skill = await loadSkill(dir, entry.name, source);
|
|
60
|
+
if (skill) {
|
|
61
|
+
registry.set(skill.name.toLowerCase(), skill);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
logger.debug(`Loaded skills from ${source} directory`, { path: dir });
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Directory doesn't exist or can't be read - that's fine
|
|
68
|
+
logger.debug(`${source} skill directory not found`, { path: dir });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Loads a single skill from its directory
|
|
73
|
+
*
|
|
74
|
+
* @param baseDir - Base skill directory
|
|
75
|
+
* @param skillName - Name of the skill (directory name)
|
|
76
|
+
* @param source - Whether this is a global or project skill
|
|
77
|
+
* @returns The parsed skill info, or null if not found/invalid
|
|
78
|
+
*/
|
|
79
|
+
async function loadSkill(baseDir, skillName, source) {
|
|
80
|
+
const filePath = join(baseDir, skillName, "SKILL.md");
|
|
81
|
+
try {
|
|
82
|
+
const file = Bun.file(filePath);
|
|
83
|
+
if (!(await file.exists())) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const fileContent = await file.text();
|
|
87
|
+
const parsed = matter(fileContent);
|
|
88
|
+
const content = parsed.content.trim();
|
|
89
|
+
const frontmatter = parsed.data;
|
|
90
|
+
// Use frontmatter name if available, otherwise use directory name
|
|
91
|
+
const name = frontmatter.name || skillName;
|
|
92
|
+
return {
|
|
93
|
+
name,
|
|
94
|
+
content,
|
|
95
|
+
description: frontmatter.description,
|
|
96
|
+
source,
|
|
97
|
+
filePath,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
logger.warn("Failed to load skill", {
|
|
102
|
+
skillName,
|
|
103
|
+
error: error instanceof Error ? error.message : String(error),
|
|
104
|
+
});
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Gets a skill by name from the registry
|
|
110
|
+
*
|
|
111
|
+
* @param registry - The skill registry
|
|
112
|
+
* @param name - The skill name (case-insensitive)
|
|
113
|
+
* @returns The skill info, or undefined if not found
|
|
114
|
+
*/
|
|
115
|
+
export function getSkill(registry, name) {
|
|
116
|
+
return registry.get(name.toLowerCase());
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=skill-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-loader.js","sourceRoot":"","sources":["../../src/skill-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SkillRegistry } from "./skill-loader.js";
|
|
2
|
+
/**
|
|
3
|
+
* Expands skill tags in text, replacing them with the skill's content body
|
|
4
|
+
*
|
|
5
|
+
* Supports two formats:
|
|
6
|
+
* 1. Self-closing: <skill name="skill-name" />
|
|
7
|
+
* 2. Block format: <skill>skill-name</skill>
|
|
8
|
+
*
|
|
9
|
+
* @param text - The text containing skill tags to expand
|
|
10
|
+
* @param registry - The skill registry to look up skills
|
|
11
|
+
* @returns The text with skill tags replaced by their content
|
|
12
|
+
*/
|
|
13
|
+
export declare function expandSkillTags(text: string, registry: SkillRegistry): string;
|
|
14
|
+
//# sourceMappingURL=skill-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-renderer.d.ts","sourceRoot":"","sources":["../../src/skill-renderer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,MAAM,CAkC7E"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { PATTERNS } from "./constants.js";
|
|
2
|
+
import { logger } from "./logger.js";
|
|
3
|
+
/**
|
|
4
|
+
* Expands skill tags in text, replacing them with the skill's content body
|
|
5
|
+
*
|
|
6
|
+
* Supports two formats:
|
|
7
|
+
* 1. Self-closing: <skill name="skill-name" />
|
|
8
|
+
* 2. Block format: <skill>skill-name</skill>
|
|
9
|
+
*
|
|
10
|
+
* @param text - The text containing skill tags to expand
|
|
11
|
+
* @param registry - The skill registry to look up skills
|
|
12
|
+
* @returns The text with skill tags replaced by their content
|
|
13
|
+
*/
|
|
14
|
+
export function expandSkillTags(text, registry) {
|
|
15
|
+
let expanded = text;
|
|
16
|
+
// Expand self-closing tags: <skill name="skill-name" />
|
|
17
|
+
PATTERNS.SKILL_TAG_SELF_CLOSING.lastIndex = 0;
|
|
18
|
+
expanded = expanded.replace(PATTERNS.SKILL_TAG_SELF_CLOSING, (match, name) => {
|
|
19
|
+
const key = name.trim().toLowerCase();
|
|
20
|
+
const skill = registry.get(key);
|
|
21
|
+
if (!skill) {
|
|
22
|
+
logger.warn(`Skill not found: '${name}', leaving tag unchanged`);
|
|
23
|
+
return match;
|
|
24
|
+
}
|
|
25
|
+
logger.debug(`Expanded skill tag: ${name}`, { source: skill.source });
|
|
26
|
+
return skill.content;
|
|
27
|
+
});
|
|
28
|
+
// Expand block tags: <skill>skill-name</skill>
|
|
29
|
+
PATTERNS.SKILL_TAG_BLOCK.lastIndex = 0;
|
|
30
|
+
expanded = expanded.replace(PATTERNS.SKILL_TAG_BLOCK, (match, name) => {
|
|
31
|
+
const key = name.trim().toLowerCase();
|
|
32
|
+
const skill = registry.get(key);
|
|
33
|
+
if (!skill) {
|
|
34
|
+
logger.warn(`Skill not found: '${name}', leaving tag unchanged`);
|
|
35
|
+
return match;
|
|
36
|
+
}
|
|
37
|
+
logger.debug(`Expanded skill tag: ${name}`, { source: skill.source });
|
|
38
|
+
return skill.content;
|
|
39
|
+
});
|
|
40
|
+
return expanded;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=skill-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-renderer.js","sourceRoot":"","sources":["../../src/skill-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAAuB;IACnE,IAAI,QAAQ,GAAG,IAAI,CAAC;IAEpB,wDAAwD;IACxD,QAAQ,CAAC,sBAAsB,CAAC,SAAS,GAAG,CAAC,CAAC;IAC9C,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEhC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,0BAA0B,CAAC,CAAC;YACjE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,uBAAuB,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACtE,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,+CAA+C;IAC/C,QAAQ,CAAC,eAAe,CAAC,SAAS,GAAG,CAAC,CAAC;IACvC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEhC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,qBAAqB,IAAI,0BAA0B,CAAC,CAAC;YACjE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,uBAAuB,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACtE,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -50,6 +50,8 @@ export interface ParsedSnippetContent {
|
|
|
50
50
|
prepend: string[];
|
|
51
51
|
/** <append> block contents in document order */
|
|
52
52
|
append: string[];
|
|
53
|
+
/** <inject> block contents in document order */
|
|
54
|
+
inject: string[];
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
57
|
* Result of expanding hashtags, including collected prepend/append blocks
|
|
@@ -61,5 +63,7 @@ export interface ExpansionResult {
|
|
|
61
63
|
prepend: string[];
|
|
62
64
|
/** Collected append blocks from all expanded snippets */
|
|
63
65
|
append: string[];
|
|
66
|
+
/** Collected inject blocks from all expanded snippets */
|
|
67
|
+
inject: string[];
|
|
64
68
|
}
|
|
65
69
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gDAAgD;IAChD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yDAAyD;IACzD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gDAAgD;IAChD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,gDAAgD;IAChD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yDAAyD;IACzD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,yDAAyD;IACzD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB"}
|
package/package.json
CHANGED
package/skill/snippets/SKILL.md
CHANGED
|
@@ -17,6 +17,12 @@ Reusable text blocks expanded via `#hashtag` in messages.
|
|
|
17
17
|
- **Global**: `~/.config/opencode/snippet/config.jsonc`
|
|
18
18
|
- **Project**: `.opencode/snippet/config.jsonc` (merges with global, project takes priority)
|
|
19
19
|
|
|
20
|
+
IMPORTANT: When modifying snippet configuration:
|
|
21
|
+
1. Check BOTH locations for existing config files
|
|
22
|
+
2. If only one exists, modify that one
|
|
23
|
+
3. If both exist, ask the user which one to modify
|
|
24
|
+
4. If neither exists, create the global config
|
|
25
|
+
|
|
20
26
|
### Logs
|
|
21
27
|
- **Debug logs**: `~/.config/opencode/logs/snippets/daily/YYYY-MM-DD.log`
|
|
22
28
|
|
|
@@ -39,11 +45,19 @@ Full config example with all options:
|
|
|
39
45
|
"debug": false
|
|
40
46
|
},
|
|
41
47
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
// Experimental features (may change or be removed)
|
|
49
|
+
"experimental": {
|
|
50
|
+
// Enable <inject>...</inject> blocks for persistent context messages
|
|
51
|
+
// Default: false
|
|
52
|
+
"injectBlocks": false,
|
|
53
|
+
// Enable skill rendering with <skill>name</skill> syntax
|
|
54
|
+
// Default: false
|
|
55
|
+
"skillRendering": false
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
// Hide shell command in output, showing only the result
|
|
59
|
+
// Default: false
|
|
60
|
+
"hideCommandInOutput": false
|
|
47
61
|
}
|
|
48
62
|
```
|
|
49
63
|
|
|
@@ -88,6 +102,56 @@ Output: Prepended section at top + `Create bug in Jira MCP about leak`.
|
|
|
88
102
|
|
|
89
103
|
Use `<append>` for reference material at end. Content inside blocks should use `##` headings.
|
|
90
104
|
|
|
105
|
+
### Inject Blocks (Experimental)
|
|
106
|
+
|
|
107
|
+
Add persistent context that the LLM sees throughout the entire agentic loop, without cluttering the visible message.
|
|
108
|
+
|
|
109
|
+
```md
|
|
110
|
+
---
|
|
111
|
+
aliases: safe
|
|
112
|
+
---
|
|
113
|
+
Think step by step.
|
|
114
|
+
<inject>
|
|
115
|
+
IMPORTANT: Double-check all code for security vulnerabilities.
|
|
116
|
+
Always suggest tests for any implementation.
|
|
117
|
+
</inject>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Input: `Review this code #safe`
|
|
121
|
+
Output: User sees "Review this code Think step by step." but the LLM also receives the inject content as separate context that persists for the entire conversation turn.
|
|
122
|
+
|
|
123
|
+
Use for rules, constraints, or context that should influence all responses without appearing inline.
|
|
124
|
+
|
|
125
|
+
Enable in config:
|
|
126
|
+
```jsonc
|
|
127
|
+
{
|
|
128
|
+
"experimental": {
|
|
129
|
+
"injectBlocks": true
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Skill Rendering (Experimental)
|
|
135
|
+
|
|
136
|
+
Inline OpenCode skills directly into messages using XML tags:
|
|
137
|
+
|
|
138
|
+
```md
|
|
139
|
+
Create a Jira ticket. <skill>jira</skill>
|
|
140
|
+
<!-- or -->
|
|
141
|
+
<skill name="jira" />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Enable in config:
|
|
145
|
+
```jsonc
|
|
146
|
+
{
|
|
147
|
+
"experimental": {
|
|
148
|
+
"skillRendering": true
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Skills are loaded from OpenCode's standard directories (`~/.config/opencode/skill/` and `.opencode/skill/`).
|
|
154
|
+
|
|
91
155
|
## Commands
|
|
92
156
|
|
|
93
157
|
- `/snippet add <name> [content]` - create global snippet
|