opencode-snippets 1.4.2 → 1.5.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.
Files changed (62) hide show
  1. package/README.md +21 -5
  2. package/dist/index.d.ts +11 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +157 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/arg-parser.d.ts +16 -0
  7. package/dist/src/arg-parser.d.ts.map +1 -0
  8. package/dist/src/arg-parser.js +94 -0
  9. package/dist/src/arg-parser.js.map +1 -0
  10. package/dist/src/commands.d.ts +30 -0
  11. package/dist/src/commands.d.ts.map +1 -0
  12. package/dist/src/commands.js +315 -0
  13. package/dist/src/commands.js.map +1 -0
  14. package/dist/src/config.d.ts +41 -0
  15. package/dist/src/config.d.ts.map +1 -0
  16. package/dist/src/config.js +150 -0
  17. package/dist/src/config.js.map +1 -0
  18. package/dist/src/constants.d.ts +35 -0
  19. package/dist/src/constants.d.ts.map +1 -0
  20. package/dist/src/constants.js +40 -0
  21. package/dist/src/constants.js.map +1 -0
  22. package/dist/src/expander.d.ts +36 -0
  23. package/dist/src/expander.d.ts.map +1 -0
  24. package/dist/src/expander.js +187 -0
  25. package/dist/src/expander.js.map +1 -0
  26. package/dist/src/loader.d.ts +46 -0
  27. package/dist/src/loader.d.ts.map +1 -0
  28. package/dist/src/loader.js +224 -0
  29. package/dist/src/loader.js.map +1 -0
  30. package/dist/src/logger.d.ts +15 -0
  31. package/dist/src/logger.d.ts.map +1 -0
  32. package/dist/src/logger.js +100 -0
  33. package/dist/src/logger.js.map +1 -0
  34. package/dist/src/notification.d.ts +11 -0
  35. package/dist/src/notification.d.ts.map +1 -0
  36. package/dist/src/notification.js +26 -0
  37. package/dist/src/notification.js.map +1 -0
  38. package/dist/src/shell.d.ts +18 -0
  39. package/dist/src/shell.d.ts.map +1 -0
  40. package/dist/src/shell.js +30 -0
  41. package/dist/src/shell.js.map +1 -0
  42. package/dist/src/types.d.ts +65 -0
  43. package/dist/src/types.d.ts.map +1 -0
  44. package/dist/src/types.js +2 -0
  45. package/dist/src/types.js.map +1 -0
  46. package/package.json +10 -6
  47. package/skill/snippets/SKILL.md +148 -0
  48. package/index.ts +0 -81
  49. package/src/arg-parser.test.ts +0 -177
  50. package/src/arg-parser.ts +0 -87
  51. package/src/commands.test.ts +0 -188
  52. package/src/commands.ts +0 -414
  53. package/src/constants.ts +0 -32
  54. package/src/expander.test.ts +0 -846
  55. package/src/expander.ts +0 -225
  56. package/src/loader.test.ts +0 -352
  57. package/src/loader.ts +0 -268
  58. package/src/logger.test.ts +0 -136
  59. package/src/logger.ts +0 -121
  60. package/src/notification.ts +0 -30
  61. package/src/shell.ts +0 -50
  62. package/src/types.ts +0 -71
package/README.md CHANGED
@@ -281,18 +281,34 @@ Be extremely concise. No explanations unless asked.
281
281
 
282
282
  ## Configuration
283
283
 
284
- ### Debug Logging
284
+ The plugin can be configured via `config.jsonc` files:
285
285
 
286
- Enable debug logs by setting an environment variable:
286
+ - **Global**: `~/.config/opencode/snippet/config.jsonc`
287
+ - **Project**: `.opencode/snippet/config.jsonc` (overrides global settings)
287
288
 
288
- ```bash
289
- DEBUG_SNIPPETS=true opencode
289
+ A default config file is created automatically on first run.
290
+
291
+ ### Full Configuration Example
292
+
293
+ ```jsonc
294
+ {
295
+ "$schema": "https://raw.githubusercontent.com/JosXa/opencode-snippets/main/schema/config.schema.json",
296
+ "logging": {
297
+ "debug": false // Enable debug logging (logs: ~/.config/opencode/logs/snippets/daily/)
298
+ },
299
+ "installSkill": true // Auto-install SKILL.md to ~/.config/opencode/skill/snippets/
300
+ }
290
301
  ```
291
302
 
292
- Logs are written to `~/.config/opencode/logs/snippets/daily/`.
303
+ All boolean settings accept: `true`, `false`, `"enabled"`, `"disabled"`
304
+
305
+ ### Debug Logging
306
+
307
+ Logs are written to `~/.config/opencode/logs/snippets/daily/` when enabled.
293
308
 
294
309
  ## Behavior Notes
295
310
 
311
+ - Snippets expand everywhere: regular chat, question responses, skills, and slash commands
296
312
  - Snippets are loaded once at plugin startup
297
313
  - Hashtag matching is **case-insensitive** (`#Hello` = `#hello`)
298
314
  - Unknown hashtags are left unchanged
@@ -0,0 +1,11 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ /**
3
+ * Snippets Plugin for OpenCode
4
+ *
5
+ * Expands hashtag-based shortcuts in user messages into predefined text snippets.
6
+ * Also provides /snippet command for managing snippets.
7
+ *
8
+ * @see https://github.com/JosXa/opencode-snippets for full documentation
9
+ */
10
+ export declare const SnippetsPlugin: Plugin;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA8ClD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,MAoH5B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,157 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { createCommandExecuteHandler } from "./src/commands.js";
5
+ import { loadConfig } from "./src/config.js";
6
+ import { assembleMessage, expandHashtags } from "./src/expander.js";
7
+ import { loadSnippets } from "./src/loader.js";
8
+ import { logger } from "./src/logger.js";
9
+ import { executeShellCommands } from "./src/shell.js";
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+ const PLUGIN_ROOT = join(__dirname, "..");
13
+ const SKILL_DIR = join(PLUGIN_ROOT, "skill");
14
+ // Install skill to global config directory
15
+ async function installSkillToGlobal() {
16
+ const home = process.env.HOME || process.env.USERPROFILE || "";
17
+ const globalSkillDir = join(home, ".config", "opencode", "skill", "snippets");
18
+ const globalSkillPath = join(globalSkillDir, "SKILL.md");
19
+ const sourceSkillPath = join(SKILL_DIR, "snippets", "SKILL.md");
20
+ try {
21
+ const sourceFile = Bun.file(sourceSkillPath);
22
+ if (!(await sourceFile.exists())) {
23
+ logger.debug("Source skill not found", { path: sourceSkillPath });
24
+ return;
25
+ }
26
+ // Check if already installed with same content
27
+ const globalFile = Bun.file(globalSkillPath);
28
+ if (await globalFile.exists()) {
29
+ const existing = await globalFile.text();
30
+ const source = await sourceFile.text();
31
+ if (existing === source) {
32
+ logger.debug("Skill already installed", { path: globalSkillPath });
33
+ return;
34
+ }
35
+ }
36
+ mkdirSync(globalSkillDir, { recursive: true });
37
+ await Bun.write(globalSkillPath, sourceFile);
38
+ logger.debug("Installed snippets skill", { path: globalSkillPath });
39
+ }
40
+ catch (err) {
41
+ logger.debug("Failed to install skill", { error: String(err) });
42
+ }
43
+ }
44
+ /**
45
+ * Snippets Plugin for OpenCode
46
+ *
47
+ * Expands hashtag-based shortcuts in user messages into predefined text snippets.
48
+ * Also provides /snippet command for managing snippets.
49
+ *
50
+ * @see https://github.com/JosXa/opencode-snippets for full documentation
51
+ */
52
+ export const SnippetsPlugin = async (ctx) => {
53
+ // Load configuration (global + project-local override)
54
+ const config = loadConfig(ctx.directory);
55
+ // Apply config settings
56
+ logger.debugEnabled = config.logging.debug;
57
+ // Install skill to global config so OpenCode discovers it (if enabled)
58
+ if (config.installSkill) {
59
+ await installSkillToGlobal();
60
+ }
61
+ // Load all snippets at startup (global + project directory)
62
+ const startupStart = performance.now();
63
+ const snippets = await loadSnippets(ctx.directory);
64
+ const startupTime = performance.now() - startupStart;
65
+ logger.debug("Plugin startup complete", {
66
+ startupTimeMs: startupTime.toFixed(2),
67
+ snippetCount: snippets.size,
68
+ installSkill: config.installSkill,
69
+ debugLogging: config.logging.debug,
70
+ });
71
+ // Create command handler
72
+ const commandHandler = createCommandExecuteHandler(ctx.client, snippets, ctx.directory);
73
+ /**
74
+ * Processes text parts for snippet expansion and shell command execution
75
+ */
76
+ const processTextParts = async (parts) => {
77
+ const messageStart = performance.now();
78
+ let expandTimeTotal = 0;
79
+ let shellTimeTotal = 0;
80
+ let processedParts = 0;
81
+ for (const part of parts) {
82
+ if (part.type === "text" && part.text) {
83
+ // 1. Expand hashtags recursively with loop detection
84
+ const expandStart = performance.now();
85
+ const expansionResult = expandHashtags(part.text, snippets);
86
+ part.text = assembleMessage(expansionResult);
87
+ const expandTime = performance.now() - expandStart;
88
+ expandTimeTotal += expandTime;
89
+ // 2. Execute shell commands: !`command`
90
+ const shellStart = performance.now();
91
+ part.text = await executeShellCommands(part.text, ctx);
92
+ const shellTime = performance.now() - shellStart;
93
+ shellTimeTotal += shellTime;
94
+ processedParts += 1;
95
+ }
96
+ }
97
+ const totalTime = performance.now() - messageStart;
98
+ if (processedParts > 0) {
99
+ logger.debug("Text parts processing complete", {
100
+ totalTimeMs: totalTime.toFixed(2),
101
+ snippetExpandTimeMs: expandTimeTotal.toFixed(2),
102
+ shellTimeMs: shellTimeTotal.toFixed(2),
103
+ processedParts,
104
+ });
105
+ }
106
+ };
107
+ return {
108
+ // Register /snippet command
109
+ config: async (opencodeConfig) => {
110
+ opencodeConfig.command ??= {};
111
+ opencodeConfig.command.snippet = {
112
+ template: "",
113
+ description: "Manage text snippets (add, delete, list, help)",
114
+ };
115
+ },
116
+ // Handle /snippet command execution
117
+ "command.execute.before": commandHandler,
118
+ "chat.message": async (_input, output) => {
119
+ // Only process user messages, never assistant messages
120
+ if (output.message.role !== "user")
121
+ return;
122
+ // Skip processing if any part is marked as ignored (e.g., command output)
123
+ if (output.parts.some((part) => "ignored" in part && part.ignored))
124
+ return;
125
+ await processTextParts(output.parts);
126
+ },
127
+ // Process all messages including question tool responses
128
+ "experimental.chat.messages.transform": async (_input, output) => {
129
+ for (const message of output.messages) {
130
+ // Only process user messages
131
+ if (message.info.role === "user") {
132
+ // Skip processing if any part is marked as ignored (e.g., command output)
133
+ if (message.parts.some((part) => "ignored" in part && part.ignored))
134
+ continue;
135
+ await processTextParts(message.parts);
136
+ }
137
+ }
138
+ },
139
+ // Process skill tool output to expand snippets in skill content
140
+ "tool.execute.after": async (input, output) => {
141
+ // Only process the skill tool
142
+ if (input.tool !== "skill")
143
+ return;
144
+ // The skill tool returns markdown content in its output
145
+ // Expand hashtags in the skill content
146
+ if (typeof output.output === "string" && output.output.trim()) {
147
+ const expansionResult = expandHashtags(output.output, snippets);
148
+ output.output = assembleMessage(expansionResult);
149
+ logger.debug("Skill content expanded", {
150
+ tool: input.tool,
151
+ callID: input.callID,
152
+ });
153
+ }
154
+ },
155
+ };
156
+ };
157
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shell-like argument parser that handles quoted strings correctly.
3
+ *
4
+ * Supports:
5
+ * - Space-separated arguments
6
+ * - Double-quoted strings (preserves spaces, allows single quotes inside)
7
+ * - Single-quoted strings (preserves spaces, allows double quotes inside)
8
+ * - --key=value syntax with quoted values
9
+ * - Multiline content inside quotes
10
+ * - Backslash escapes for quotes inside quoted strings
11
+ *
12
+ * @param input - The raw argument string to parse
13
+ * @returns Array of parsed arguments with quotes stripped
14
+ */
15
+ export declare function parseCommandArgs(input: string): string[];
16
+ //# sourceMappingURL=arg-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arg-parser.d.ts","sourceRoot":"","sources":["../../src/arg-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAwExD"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Shell-like argument parser that handles quoted strings correctly.
3
+ *
4
+ * Supports:
5
+ * - Space-separated arguments
6
+ * - Double-quoted strings (preserves spaces, allows single quotes inside)
7
+ * - Single-quoted strings (preserves spaces, allows double quotes inside)
8
+ * - --key=value syntax with quoted values
9
+ * - Multiline content inside quotes
10
+ * - Backslash escapes for quotes inside quoted strings
11
+ *
12
+ * @param input - The raw argument string to parse
13
+ * @returns Array of parsed arguments with quotes stripped
14
+ */
15
+ export function parseCommandArgs(input) {
16
+ const args = [];
17
+ let current = "";
18
+ let state = "normal";
19
+ let hasQuotedContent = false; // Track if we've entered a quoted section
20
+ let i = 0;
21
+ while (i < input.length) {
22
+ const char = input[i];
23
+ if (state === "normal") {
24
+ if (char === " " || char === "\t") {
25
+ // Whitespace in normal mode: finish current token
26
+ if (current.length > 0 || hasQuotedContent) {
27
+ args.push(current);
28
+ current = "";
29
+ hasQuotedContent = false;
30
+ }
31
+ }
32
+ else if (char === '"') {
33
+ // Enter double-quote mode
34
+ state = "double";
35
+ hasQuotedContent = true;
36
+ }
37
+ else if (char === "'") {
38
+ // Enter single-quote mode
39
+ state = "single";
40
+ hasQuotedContent = true;
41
+ }
42
+ else {
43
+ current += char;
44
+ }
45
+ }
46
+ else if (state === "double") {
47
+ if (char === "\\") {
48
+ // Check for escape sequences
49
+ const next = input[i + 1];
50
+ if (next === '"' || next === "\\") {
51
+ current += next;
52
+ i++; // Skip the escaped character
53
+ }
54
+ else {
55
+ current += char;
56
+ }
57
+ }
58
+ else if (char === '"') {
59
+ // Exit double-quote mode
60
+ state = "normal";
61
+ }
62
+ else {
63
+ current += char;
64
+ }
65
+ }
66
+ else if (state === "single") {
67
+ if (char === "\\") {
68
+ // Check for escape sequences
69
+ const next = input[i + 1];
70
+ if (next === "'" || next === "\\") {
71
+ current += next;
72
+ i++; // Skip the escaped character
73
+ }
74
+ else {
75
+ current += char;
76
+ }
77
+ }
78
+ else if (char === "'") {
79
+ // Exit single-quote mode
80
+ state = "normal";
81
+ }
82
+ else {
83
+ current += char;
84
+ }
85
+ }
86
+ i++;
87
+ }
88
+ // Handle any remaining content (including unclosed quotes or empty quoted strings)
89
+ if (current.length > 0 || hasQuotedContent) {
90
+ args.push(current);
91
+ }
92
+ return args;
93
+ }
94
+ //# sourceMappingURL=arg-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arg-parser.js","sourceRoot":"","sources":["../../src/arg-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { OpencodeClient, SnippetRegistry } from "./types.js";
2
+ /**
3
+ * Parsed options from the add command arguments
4
+ */
5
+ export interface AddOptions {
6
+ aliases: string[];
7
+ description: string | undefined;
8
+ isProject: boolean;
9
+ }
10
+ /**
11
+ * Parses option arguments for the add command.
12
+ *
13
+ * Supports all variations per PR #13 requirements:
14
+ * - --alias=a,b, --alias a,b, --aliases=a,b, --aliases a,b
15
+ * - --desc=x, --desc x, --description=x, --description x
16
+ * - --project flag
17
+ *
18
+ * @param args - Array of parsed arguments (after name and content extraction)
19
+ * @returns Parsed options object
20
+ */
21
+ export declare function parseAddOptions(args: string[]): AddOptions;
22
+ /**
23
+ * Creates the command execute handler for the snippets command
24
+ */
25
+ export declare function createCommandExecuteHandler(client: OpencodeClient, snippets: SnippetRegistry, projectDir?: string): (input: {
26
+ command: string;
27
+ sessionID: string;
28
+ arguments: string;
29
+ }) => Promise<void>;
30
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAclE;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA8D1D;AAYD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,eAAe,EACzB,UAAU,CAAC,EAAE,MAAM,IAEL,OAAO;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,mBAsD/E"}