@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 +21 -0
- package/README.md +95 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +106 -0
- package/package.json +48 -0
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|