@ojhaujjwal/opencode-plugin-ci-gate 0.0.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ # @ojhaujjwal/opencode-plugin-ci-gate
2
+
3
+ An opencode plugin that automatically runs `npm run ci` on session idle and
4
+ injects failure output back into the assistant's context for self-healing.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ npm install @ojhaujjwal/opencode-plugin-ci-gate
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ Add to your `.opencode/plugin/index.ts`:
15
+
16
+ ```ts
17
+ export { default } from "@ojhaujjwal/opencode-plugin-ci-gate"
18
+ ```
19
+
20
+ The plugin activates automatically on `session.idle` events.
21
+ # opencode-plugin-ci-gate
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ declare const plugin: Plugin;
3
+ export default plugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAUlD,QAAA,MAAM,MAAM,EAAE,MAqDb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,66 @@
1
+ import { isShellStartedEvent } from "./types.js";
2
+ import { notify } from "./toast.js";
3
+ import { isCiCommand, treeHash, runCi, firstTextPart, truncate } from "./runtime.js";
4
+ import { CiGateState } from "./state.js";
5
+ const CI_TIMEOUT_MS = 5 * 60_000;
6
+ const MAX_AUTOFIX_TURNS = 5;
7
+ const SENTINEL = "[CI-GATE-AUTO]";
8
+ const plugin = async ({ client, directory }) => {
9
+ const state = new CiGateState();
10
+ const note = (m, v = "info") => notify(client, m, v);
11
+ return {
12
+ "chat.message": async (input, output) => {
13
+ if (!input.sessionID)
14
+ return;
15
+ const text = firstTextPart(output.parts);
16
+ if (text.startsWith(SENTINEL))
17
+ return;
18
+ state.onChatStart(input.sessionID, treeHash(directory));
19
+ },
20
+ event: async ({ event }) => {
21
+ const ev = event;
22
+ if (isShellStartedEvent(ev)) {
23
+ if (isCiCommand(ev.properties.command ?? ""))
24
+ state.markCiRan(ev.properties.sessionID);
25
+ return;
26
+ }
27
+ if (event.type !== "session.idle")
28
+ return;
29
+ const sid = event.properties.sessionID;
30
+ if (!sid || state.isInflight(sid))
31
+ return;
32
+ const current = treeHash(directory);
33
+ const changed = state.rememberIdle(sid, current);
34
+ if (state.consumeCiRan(sid) && changed) {
35
+ note("working tree changed but assistant already ran `npm run ci`; skipping.", "info");
36
+ return;
37
+ }
38
+ if (!changed)
39
+ return;
40
+ if (state.autofixCount(sid) >= MAX_AUTOFIX_TURNS) {
41
+ note(`autofix cap reached; not injecting. Run \`npm run ci\` manually.`, "warning");
42
+ return;
43
+ }
44
+ state.startInflight(sid);
45
+ try {
46
+ note("running `npm run ci` ...", "info");
47
+ const { exitCode, stdout } = await runCi(directory, CI_TIMEOUT_MS);
48
+ state.advanceHash(sid, treeHash(directory));
49
+ if (exitCode === 0) {
50
+ note("`npm run ci` PASSED (auto).", "success");
51
+ state.resetAutofix(sid);
52
+ return;
53
+ }
54
+ const n = state.bumpAutofix(sid);
55
+ note(`\`npm run ci\` FAILED (exit ${exitCode}); injecting turn ${n}/${MAX_AUTOFIX_TURNS}.`, "error");
56
+ const body = `${SENTINEL}\n... <ci-output>\n${truncate(stdout, 12_000)}\n</ci-output>\n`;
57
+ await client.session.prompt({ path: { id: sid }, body: { parts: [{ type: "text", text: body }] } });
58
+ }
59
+ finally {
60
+ state.endInflight(sid);
61
+ }
62
+ }
63
+ };
64
+ };
65
+ export default plugin;
66
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Part } from "@opencode-ai/sdk";
2
+ export declare function isCiCommand(command: string): boolean;
3
+ export declare function treeHash(directory: string): string;
4
+ export declare function runCi(directory: string, timeoutMs: number): Promise<{
5
+ exitCode: number;
6
+ stdout: string;
7
+ }>;
8
+ export declare function firstTextPart(parts: Part[]): string;
9
+ export declare function truncate(text: string, max: number): string;
10
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI7C,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAWlD;AAED,wBAAgB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAuBzG;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CAGnD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG1D"}
@@ -0,0 +1,52 @@
1
+ import { execSync, spawn } from "node:child_process";
2
+ const CI_RE = /^(?:npm\s+(?:run\s+)?ci(?:t)?|npmx?\s+ci|pnpm\s+ci)\b/;
3
+ export function isCiCommand(command) {
4
+ return CI_RE.test(command.trim());
5
+ }
6
+ export function treeHash(directory) {
7
+ try {
8
+ return execSync("git stash create --include-untracked", {
9
+ cwd: directory,
10
+ encoding: "utf-8",
11
+ timeout: 5_000,
12
+ stdio: ["pipe", "pipe", "pipe"]
13
+ }).trim();
14
+ }
15
+ catch {
16
+ return "";
17
+ }
18
+ }
19
+ export function runCi(directory, timeoutMs) {
20
+ return new Promise((resolve, reject) => {
21
+ const child = spawn("npm", ["run", "ci"], {
22
+ cwd: directory,
23
+ timeout: timeoutMs,
24
+ stdio: ["pipe", "pipe", "pipe"],
25
+ shell: false
26
+ });
27
+ let stdout = "";
28
+ let stderr = "";
29
+ child.stdout?.on("data", (chunk) => {
30
+ stdout += chunk.toString();
31
+ });
32
+ child.stderr?.on("data", (chunk) => {
33
+ stderr += chunk.toString();
34
+ });
35
+ child.on("error", (err) => {
36
+ reject(err);
37
+ });
38
+ child.on("close", (code) => {
39
+ resolve({ exitCode: code ?? 1, stdout: stdout + stderr });
40
+ });
41
+ });
42
+ }
43
+ export function firstTextPart(parts) {
44
+ const part = parts.find((p) => p.type === "text");
45
+ return part?.text ?? "";
46
+ }
47
+ export function truncate(text, max) {
48
+ if (text.length <= max)
49
+ return text;
50
+ return "..." + text.slice(text.length - max + 3);
51
+ }
52
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,KAAK,GAAG,uDAAuD,CAAC;AAEtE,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,SAAiB;IACxC,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,sCAAsC,EAAE;YACtD,GAAG,EAAE,SAAS;YACd,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,SAAiB,EAAE,SAAiB;IACxD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE;YACxC,GAAG,EAAE,SAAS;YACd,OAAO,EAAE,SAAS;YAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QACH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACzC,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAgC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAChF,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,GAAW;IAChD,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,18 @@
1
+ export declare class CiGateState {
2
+ private _lastSeenHash;
3
+ private _ciRanThisTurn;
4
+ private _autofixCount;
5
+ private _inflight;
6
+ onChatStart(sessionID: string, hash: string): void;
7
+ rememberIdle(sessionID: string, hash: string): boolean;
8
+ advanceHash(sessionID: string, hash: string): void;
9
+ markCiRan(sessionID: string): void;
10
+ consumeCiRan(sessionID: string): boolean;
11
+ autofixCount(sessionID: string): number;
12
+ bumpAutofix(sessionID: string): number;
13
+ resetAutofix(sessionID: string): void;
14
+ isInflight(sessionID: string): boolean;
15
+ startInflight(sessionID: string): void;
16
+ endInflight(sessionID: string): void;
17
+ }
18
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAW;IACtB,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,SAAS,CAAqB;IAEtC,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMlD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAOtD,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIlD,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIlC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAMxC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIvC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAMtC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAItC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAGrC"}
package/dist/state.js ADDED
@@ -0,0 +1,50 @@
1
+ export class CiGateState {
2
+ _lastSeenHash = new Map();
3
+ _ciRanThisTurn = new Set();
4
+ _autofixCount = new Map();
5
+ _inflight = new Set();
6
+ onChatStart(sessionID, hash) {
7
+ this._lastSeenHash.set(sessionID, hash);
8
+ this._ciRanThisTurn.delete(sessionID);
9
+ this._autofixCount.delete(sessionID);
10
+ }
11
+ rememberIdle(sessionID, hash) {
12
+ const prev = this._lastSeenHash.get(sessionID);
13
+ const changed = prev !== hash;
14
+ this._lastSeenHash.set(sessionID, hash);
15
+ return changed;
16
+ }
17
+ advanceHash(sessionID, hash) {
18
+ this._lastSeenHash.set(sessionID, hash);
19
+ }
20
+ markCiRan(sessionID) {
21
+ this._ciRanThisTurn.add(sessionID);
22
+ }
23
+ consumeCiRan(sessionID) {
24
+ if (!this._ciRanThisTurn.has(sessionID))
25
+ return false;
26
+ this._ciRanThisTurn.delete(sessionID);
27
+ return true;
28
+ }
29
+ autofixCount(sessionID) {
30
+ return this._autofixCount.get(sessionID) ?? 0;
31
+ }
32
+ bumpAutofix(sessionID) {
33
+ const n = (this._autofixCount.get(sessionID) ?? 0) + 1;
34
+ this._autofixCount.set(sessionID, n);
35
+ return n;
36
+ }
37
+ resetAutofix(sessionID) {
38
+ this._autofixCount.delete(sessionID);
39
+ }
40
+ isInflight(sessionID) {
41
+ return this._inflight.has(sessionID);
42
+ }
43
+ startInflight(sessionID) {
44
+ this._inflight.add(sessionID);
45
+ }
46
+ endInflight(sessionID) {
47
+ this._inflight.delete(sessionID);
48
+ }
49
+ }
50
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAW;IACd,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAEtC,WAAW,CAAC,SAAiB,EAAE,IAAY;QACzC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,YAAY,CAAC,SAAiB,EAAE,IAAY;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;QAC9B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACxC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,IAAY;QACzC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,SAAS,CAAC,SAAiB;QACzB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,YAAY,CAAC,SAAiB;QAC5B,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QACtD,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY,CAAC,SAAiB;QAC5B,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IAED,WAAW,CAAC,SAAiB;QAC3B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACvD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACrC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,YAAY,CAAC,SAAiB;QAC5B,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,aAAa,CAAC,SAAiB;QAC7B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;IAED,WAAW,CAAC,SAAiB;QAC3B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ export declare function notify(client: PluginInput["client"], message: string, variant: "info" | "success" | "warning" | "error", title?: string): void;
3
+ //# sourceMappingURL=toast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../src/toast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,wBAAgB,MAAM,CACpB,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,EACjD,KAAK,SAAY,GAChB,IAAI,CAIN"}
package/dist/toast.js ADDED
@@ -0,0 +1,6 @@
1
+ export function notify(client, message, variant, title = "ci-gate") {
2
+ void client.tui.showToast({
3
+ body: { title, message, variant, duration: variant === "error" ? 10_000 : 4_000 }
4
+ });
5
+ }
6
+ //# sourceMappingURL=toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.js","sourceRoot":"","sources":["../src/toast.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,MAAM,CACpB,MAA6B,EAC7B,OAAe,EACf,OAAiD,EACjD,KAAK,GAAG,SAAS;IAEjB,KAAK,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;QACxB,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE;KAClF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,13 @@
1
+ export type ShellStartedEvent = {
2
+ id: string;
3
+ type: "session.next.shell.started";
4
+ properties: {
5
+ timestamp: number;
6
+ sessionID: string;
7
+ messageID: string;
8
+ callID: string;
9
+ command: string;
10
+ };
11
+ };
12
+ export declare function isShellStartedEvent(input: unknown): input is ShellStartedEvent;
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,4BAA4B,CAAC;IACnC,UAAU,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAI9E"}
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ export function isShellStartedEvent(input) {
2
+ if (input === null || typeof input !== "object")
3
+ return false;
4
+ if (!("type" in input))
5
+ return false;
6
+ return input.type === "session.next.shell.started";
7
+ }
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,KAAK,CAAC,IAAI,KAAK,4BAA4B,CAAC;AACrD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@ojhaujjwal/opencode-plugin-ci-gate",
3
+ "version": "0.0.1",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "typecheck": "tsc --noEmit -p tsconfig.test.json",
16
+ "lint": "NODE_OPTIONS='--import tsx/esm' oxlint --disable-nested-config",
17
+ "lint:fix": "NODE_OPTIONS='--import tsx/esm' oxlint --disable-nested-config --fix",
18
+ "test": "vitest run test/",
19
+ "format": "oxfmt --check",
20
+ "format-fix": "oxfmt --write",
21
+ "ci": "npm run typecheck && npm run lint && npm run test && npm run format",
22
+ "build": "tsc",
23
+ "prepublishOnly": "npm run ci && npm run build"
24
+ },
25
+ "dependencies": {},
26
+ "devDependencies": {
27
+ "@opencode-ai/plugin": "1.17.9",
28
+ "@opencode-ai/sdk": "1.17.9",
29
+ "@types/node": "22.19.20",
30
+ "oxfmt": "0.45.0",
31
+ "oxlint": "1.68.0",
32
+ "tsx": "4.22.4",
33
+ "typescript": "5.9.3",
34
+ "vitest": "3.2.6"
35
+ },
36
+ "peerDependencies": {
37
+ "@opencode-ai/plugin": ">=1.17.9"
38
+ },
39
+ "engines": {
40
+ "node": ">=22"
41
+ }
42
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "."
5
+ },
6
+ "include": ["src", "test"]
7
+ }