@cristochang/spec-integration 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 OpenCode
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @cristochang/spec-integration
2
+
3
+ > Passive workflow reminders when working with OpenCode specs
4
+
5
+ ## Compatibility Warning
6
+
7
+ **This plugin is version-locked to opencode.**
8
+
9
+ It relies on internal Spec capabilities provided at runtime by opencode core.
10
+ Do not expect compatibility across major opencode versions.
11
+
12
+ Current compatibility: opencode >=0.9.0 <0.10.0
13
+
14
+ ## What it does
15
+
16
+ - Shows you which spec is currently active for your session
17
+ - Warns you when making file edits while a spec is still in "pending" status
18
+ - Reminds you of the spec's scope and success criteria when making changes
19
+
20
+ ## What it doesn't do
21
+
22
+ - **Does not block** any operations (passive mode only)
23
+ - **Does not modify** tool arguments or behavior
24
+ - **Does not persist** any data to disk
25
+ - **Does not create** files or directories
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ npm install @cristochang/spec-integration
31
+ ```
32
+
33
+ Add to your `.opencode/config.json`:
34
+
35
+ ```json
36
+ {
37
+ "plugin": [
38
+ "@cristochang/spec-integration@latest"
39
+ ]
40
+ }
41
+ ```
42
+
43
+ ## Configuration
44
+
45
+ Add an `experimental.specIntegration` section to your `.opencode/config.json`:
46
+
47
+ ```json
48
+ {
49
+ "experimental": {
50
+ "specIntegration": {
51
+ "enabled": true,
52
+ "mode": "passive",
53
+ "warningCooldown": 60000
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### Options
60
+
61
+ | Option | Type | Default | Description |
62
+ |--------|------|---------|-------------|
63
+ | `enabled` | boolean | `true` | Enable or disable the plugin |
64
+ | `mode` | `"passive"` | `"passive"` | Warning mode (only "passive" is supported) |
65
+ | `warningCooldown` | number | `60000` | Milliseconds between warnings for the same session |
66
+
67
+ ## How it works
68
+
69
+ The plugin monitors the following tools and shows reminders when an active spec exists:
70
+
71
+ - `edit` - Editing existing files
72
+ - `write` - Creating new files
73
+ - `multiedit` - Batch edits
74
+ - `patch` - Patch operations
75
+ - `bash` - Shell commands
76
+
77
+ ### Failure Strategy
78
+
79
+ If the Spec capability is not available (e.g., running on an incompatible opencode version), the plugin **silently disables itself** and will not interfere with your workflow.
80
+
81
+ ## When to use this plugin
82
+
83
+ **Use it when:**
84
+ - You want to maintain awareness of your active spec while coding
85
+ - You're working in a team that uses specs for task tracking
86
+ - You want gentle reminders about spec boundaries without enforced constraints
87
+
88
+ **Don't use it when:**
89
+ - You prefer complete autonomy without any workflow suggestions
90
+ - You're doing exploratory coding that doesn't align with spec-based workflows
91
+ - You find the reminders distracting (just set `enabled: false`)
92
+
93
+ ## License
94
+
95
+ MIT
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @opencode-ai/spec-integration
3
+ *
4
+ * Spec workflow integration plugin for OpenCode
5
+ *
6
+ * This plugin provides passive reminders when working with code changes
7
+ * while a spec is active in your session.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ import type { Hooks, PluginInput } from "@opencode-ai/plugin";
12
+ /**
13
+ * Spec Integration Plugin for OpenCode
14
+ *
15
+ * A passive workflow integration that provides context-aware reminders
16
+ * when working with code changes under active specs.
17
+ *
18
+ * This plugin uses the Spec capability provided by opencode core at runtime.
19
+ * If the Spec capability is not available, the plugin silently disables itself.
20
+ */
21
+ export declare function SpecIntegrationPlugin(input: PluginInput): Promise<Hooks>;
22
+ export default SpecIntegrationPlugin;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAY,MAAM,qBAAqB,CAAA;AAyEvE;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CA6D9E;AAED,eAAe,qBAAqB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,106 @@
1
+ // src/index.ts
2
+ var MONITORED_TOOLS = [
3
+ "edit",
4
+ "write",
5
+ "multiedit",
6
+ "patch",
7
+ "bash"
8
+ ];
9
+ var callWarnings = /* @__PURE__ */ new Map();
10
+ var warningCooldowns = /* @__PURE__ */ new Map();
11
+ var DEFAULT_CONFIG = {
12
+ enabled: true,
13
+ mode: "passive",
14
+ warningCooldown: 6e4
15
+ };
16
+ var pluginConfig = { ...DEFAULT_CONFIG };
17
+ function shouldShowWarning(sessionID) {
18
+ const lastWarning = warningCooldowns.get(sessionID) ?? 0;
19
+ const now = Date.now();
20
+ if (now - lastWarning < pluginConfig.warningCooldown) {
21
+ return false;
22
+ }
23
+ return true;
24
+ }
25
+ function markNeedsWarning(callID, specID, message) {
26
+ callWarnings.set(callID, { specID, message });
27
+ }
28
+ function getWarning(callID) {
29
+ const warning = callWarnings.get(callID);
30
+ callWarnings.delete(callID);
31
+ return warning;
32
+ }
33
+ function generateWarningMessage(spec, tool) {
34
+ const objective = spec.objective;
35
+ const inclusions = spec.scope.inclusions.join(", ");
36
+ switch (spec.status) {
37
+ case "pending":
38
+ return `\u26A0\uFE0F Working on ${tool} while spec is in "pending" status. Spec: "${objective}". Consider approving the spec with spec_approve before making changes. Use spec_get to view full details.`;
39
+ case "approved":
40
+ return `\u2139\uFE0F Working on spec: "${objective}". In-scope: ${inclusions}. Use spec_get to review full scope and success criteria.`;
41
+ case "implemented":
42
+ return `\u2139\uFE0F Working on ${tool} for a spec already marked as "implemented". Spec: "${objective}". Consider if new changes are still within the original scope.`;
43
+ case "cancelled":
44
+ return `\u26A0\uFE0F Working on ${tool} while spec is "cancelled". Spec: "${objective}". Consider creating a new spec for this work.`;
45
+ default:
46
+ return `\u2139\uFE0F Active spec: "${objective}"`;
47
+ }
48
+ }
49
+ async function SpecIntegrationPlugin(input) {
50
+ const specCap = input.capabilities?.spec;
51
+ if (!specCap) {
52
+ pluginConfig.enabled = false;
53
+ }
54
+ return {
55
+ config: async (config) => {
56
+ const integrationConfig = config.experimental?.specIntegration;
57
+ if (integrationConfig) {
58
+ pluginConfig = {
59
+ enabled: integrationConfig.enabled ?? DEFAULT_CONFIG.enabled,
60
+ mode: integrationConfig.mode ?? DEFAULT_CONFIG.mode,
61
+ warningCooldown: integrationConfig.warningCooldown ?? DEFAULT_CONFIG.warningCooldown
62
+ };
63
+ } else {
64
+ pluginConfig = { ...DEFAULT_CONFIG };
65
+ }
66
+ },
67
+ "tool.execute.before": async ({ tool, sessionID, callID }, _output) => {
68
+ if (!pluginConfig.enabled || !specCap) {
69
+ return;
70
+ }
71
+ const spec = await specCap.getActive(sessionID);
72
+ if (!spec) {
73
+ return;
74
+ }
75
+ if (tool.startsWith("spec_")) {
76
+ return;
77
+ }
78
+ if (MONITORED_TOOLS.includes(tool)) {
79
+ if (shouldShowWarning(sessionID)) {
80
+ const message = generateWarningMessage(spec, tool);
81
+ markNeedsWarning(callID, spec.id, message);
82
+ warningCooldowns.set(sessionID, Date.now());
83
+ }
84
+ }
85
+ },
86
+ "tool.execute.after": async ({ tool, sessionID, callID }, output) => {
87
+ if (!pluginConfig.enabled) {
88
+ return;
89
+ }
90
+ const warning = getWarning(callID);
91
+ if (warning) {
92
+ if (output.metadata) {
93
+ output.metadata._specWarning = {
94
+ specID: warning.specID,
95
+ message: warning.message
96
+ };
97
+ }
98
+ }
99
+ }
100
+ };
101
+ }
102
+ var index_default = SpecIntegrationPlugin;
103
+ export {
104
+ SpecIntegrationPlugin,
105
+ index_default as default
106
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@cristochang/spec-integration",
3
+ "version": "0.1.0",
4
+ "description": "Spec workflow integration plugin for OpenCode - passive reminders when working with specs",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build": "npm run build:js && npm run build:dts",
21
+ "build:js": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@opencode-ai/plugin",
22
+ "build:dts": "tsc --emitDeclarationOnly",
23
+ "prepublishOnly": "npm run build"
24
+ },
25
+ "keywords": [
26
+ "opencode",
27
+ "opencode-plugin",
28
+ "spec",
29
+ "workflow",
30
+ "integration",
31
+ "passive",
32
+ "reminder"
33
+ ],
34
+ "author": "OpenCode",
35
+ "license": "MIT",
36
+ "peerDependencies": {
37
+ "@opencode-ai/plugin": ">=0.9.0 <0.10.0"
38
+ },
39
+ "devDependencies": {
40
+ "@opencode-ai/plugin": "workspace:*",
41
+ "@types/node": "22.13.9",
42
+ "esbuild": "0.27.2",
43
+ "typescript": "^5.0.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
48
+ }