opencodekit 0.20.8 → 0.21.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/dist/index.js +1 -1
- package/dist/template/.opencode/AGENTS.md +12 -0
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +83 -609
- package/dist/template/.opencode/opencodex-fast.jsonc +1 -1
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugin/copilot-auth.ts +27 -12
- package/dist/template/.opencode/plugin/prompt-leverage.ts +193 -0
- package/dist/template/.opencode/plugin/prompt-leverage.ts.bak +228 -0
- package/dist/template/.opencode/plugin/sdk/copilot/copilot-provider.ts +14 -2
- package/dist/template/.opencode/plugin/sdk/copilot/index.ts +2 -2
- package/dist/template/.opencode/plugin/sdk/copilot/responses/convert-to-openai-responses-input.ts +335 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-config.ts +18 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-error.ts +22 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-api-types.ts +214 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-language-model.ts +1770 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-prepare-tools.ts +173 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-settings.ts +1 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/code-interpreter.ts +87 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/file-search.ts +127 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/image-generation.ts +114 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/local-shell.ts +64 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search-preview.ts +103 -0
- package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search.ts +102 -0
- package/dist/template/.opencode/skill/gh-address-comments/SKILL.md +29 -0
- package/dist/template/.opencode/skill/gh-address-comments/scripts/fetch_comments.py +237 -0
- package/dist/template/.opencode/skill/gh-fix-ci/SKILL.md +38 -0
- package/dist/template/.opencode/skill/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
- package/dist/template/.opencode/skill/prompt-leverage/SKILL.md +90 -0
- package/dist/template/.opencode/skill/prompt-leverage/references/framework.md +91 -0
- package/dist/template/.opencode/skill/prompt-leverage/scripts/augment_prompt.py +157 -0
- package/dist/template/.opencode/skill/screenshot/SKILL.md +48 -0
- package/dist/template/.opencode/skill/screenshot/scripts/ensure_macos_permissions.sh +54 -0
- package/dist/template/.opencode/skill/screenshot/scripts/macos_display_info.swift +22 -0
- package/dist/template/.opencode/skill/screenshot/scripts/macos_permissions.swift +40 -0
- package/dist/template/.opencode/skill/screenshot/scripts/macos_window_info.swift +126 -0
- package/dist/template/.opencode/skill/screenshot/scripts/take_screenshot.ps1 +163 -0
- package/dist/template/.opencode/skill/screenshot/scripts/take_screenshot.py +585 -0
- package/dist/template/.opencode/skill/security-threat-model/SKILL.md +36 -0
- package/dist/template/.opencode/skill/security-threat-model/references/prompt-template.md +255 -0
- package/dist/template/.opencode/skill/security-threat-model/references/security-controls-and-assets.md +32 -0
- package/dist/template/.opencode/skill/skill-installer/SKILL.md +58 -0
- package/dist/template/.opencode/skill/skill-installer/scripts/github_utils.py +21 -0
- package/dist/template/.opencode/skill/skill-installer/scripts/install-skill-from-github.py +313 -0
- package/dist/template/.opencode/skill/skill-installer/scripts/list-skills.py +106 -0
- package/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createProviderToolFactory } from "@ai-sdk/provider-utils"
|
|
2
|
+
import { z } from "zod/v4"
|
|
3
|
+
|
|
4
|
+
export const webSearchArgsSchema = z.object({
|
|
5
|
+
filters: z
|
|
6
|
+
.object({
|
|
7
|
+
allowedDomains: z.array(z.string()).optional(),
|
|
8
|
+
})
|
|
9
|
+
.optional(),
|
|
10
|
+
|
|
11
|
+
searchContextSize: z.enum(["low", "medium", "high"]).optional(),
|
|
12
|
+
|
|
13
|
+
userLocation: z
|
|
14
|
+
.object({
|
|
15
|
+
type: z.literal("approximate"),
|
|
16
|
+
country: z.string().optional(),
|
|
17
|
+
city: z.string().optional(),
|
|
18
|
+
region: z.string().optional(),
|
|
19
|
+
timezone: z.string().optional(),
|
|
20
|
+
})
|
|
21
|
+
.optional(),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export const webSearchToolFactory = createProviderToolFactory<
|
|
25
|
+
{
|
|
26
|
+
// Web search doesn't take input parameters - it's controlled by the prompt
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
/**
|
|
30
|
+
* Filters for the search.
|
|
31
|
+
*/
|
|
32
|
+
filters?: {
|
|
33
|
+
/**
|
|
34
|
+
* Allowed domains for the search.
|
|
35
|
+
* If not provided, all domains are allowed.
|
|
36
|
+
* Subdomains of the provided domains are allowed as well.
|
|
37
|
+
*/
|
|
38
|
+
allowedDomains?: string[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Search context size to use for the web search.
|
|
43
|
+
* - high: Most comprehensive context, highest cost, slower response
|
|
44
|
+
* - medium: Balanced context, cost, and latency (default)
|
|
45
|
+
* - low: Least context, lowest cost, fastest response
|
|
46
|
+
*/
|
|
47
|
+
searchContextSize?: "low" | "medium" | "high"
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* User location information to provide geographically relevant search results.
|
|
51
|
+
*/
|
|
52
|
+
userLocation?: {
|
|
53
|
+
/**
|
|
54
|
+
* Type of location (always 'approximate')
|
|
55
|
+
*/
|
|
56
|
+
type: "approximate"
|
|
57
|
+
/**
|
|
58
|
+
* Two-letter ISO country code (e.g., 'US', 'GB')
|
|
59
|
+
*/
|
|
60
|
+
country?: string
|
|
61
|
+
/**
|
|
62
|
+
* City name (free text, e.g., 'Minneapolis')
|
|
63
|
+
*/
|
|
64
|
+
city?: string
|
|
65
|
+
/**
|
|
66
|
+
* Region name (free text, e.g., 'Minnesota')
|
|
67
|
+
*/
|
|
68
|
+
region?: string
|
|
69
|
+
/**
|
|
70
|
+
* IANA timezone (e.g., 'America/Chicago')
|
|
71
|
+
*/
|
|
72
|
+
timezone?: string
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
>({
|
|
76
|
+
id: "openai.web_search",
|
|
77
|
+
inputSchema: z.object({
|
|
78
|
+
action: z
|
|
79
|
+
.discriminatedUnion("type", [
|
|
80
|
+
z.object({
|
|
81
|
+
type: z.literal("search"),
|
|
82
|
+
query: z.string().nullish(),
|
|
83
|
+
}),
|
|
84
|
+
z.object({
|
|
85
|
+
type: z.literal("open_page"),
|
|
86
|
+
url: z.string(),
|
|
87
|
+
}),
|
|
88
|
+
z.object({
|
|
89
|
+
type: z.literal("find"),
|
|
90
|
+
url: z.string(),
|
|
91
|
+
pattern: z.string(),
|
|
92
|
+
}),
|
|
93
|
+
])
|
|
94
|
+
.nullish(),
|
|
95
|
+
}),
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
export const webSearch = (
|
|
99
|
+
args: Parameters<typeof webSearchToolFactory>[0] = {}, // default
|
|
100
|
+
) => {
|
|
101
|
+
return webSearchToolFactory(args)
|
|
102
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gh-address-comments
|
|
3
|
+
description: Use when the user asks to review and address GitHub PR comments/threads for the current branch using gh CLI.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags: [workflow, git, agent-coordination]
|
|
6
|
+
dependencies: []
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# gh-address-comments
|
|
10
|
+
|
|
11
|
+
Fetch PR comments and review threads, then apply selected fixes.
|
|
12
|
+
|
|
13
|
+
## When to Use
|
|
14
|
+
|
|
15
|
+
- User asks to resolve PR review comments
|
|
16
|
+
- User wants a numbered list of comment threads to triage
|
|
17
|
+
|
|
18
|
+
## When NOT to Use
|
|
19
|
+
|
|
20
|
+
- No open PR associated with current branch
|
|
21
|
+
- `gh` is unavailable/auth is missing and user declines auth
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
1. Check auth: `gh auth status`
|
|
26
|
+
2. Fetch comments: `python3 .opencode/skill/gh-address-comments/scripts/fetch_comments.py`
|
|
27
|
+
3. Present numbered comment threads with short fix summaries
|
|
28
|
+
4. Ask which comments to address
|
|
29
|
+
5. Implement only selected fixes and report results
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Fetch all PR conversation comments + reviews + review threads (inline threads)
|
|
4
|
+
for the PR associated with the current git branch, by shelling out to:
|
|
5
|
+
|
|
6
|
+
gh api graphql
|
|
7
|
+
|
|
8
|
+
Requires:
|
|
9
|
+
- `gh auth login` already set up
|
|
10
|
+
- current branch has an associated (open) PR
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
python fetch_comments.py > pr_comments.json
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
QUERY = """\
|
|
24
|
+
query(
|
|
25
|
+
$owner: String!,
|
|
26
|
+
$repo: String!,
|
|
27
|
+
$number: Int!,
|
|
28
|
+
$commentsCursor: String,
|
|
29
|
+
$reviewsCursor: String,
|
|
30
|
+
$threadsCursor: String
|
|
31
|
+
) {
|
|
32
|
+
repository(owner: $owner, name: $repo) {
|
|
33
|
+
pullRequest(number: $number) {
|
|
34
|
+
number
|
|
35
|
+
url
|
|
36
|
+
title
|
|
37
|
+
state
|
|
38
|
+
|
|
39
|
+
# Top-level "Conversation" comments (issue comments on the PR)
|
|
40
|
+
comments(first: 100, after: $commentsCursor) {
|
|
41
|
+
pageInfo { hasNextPage endCursor }
|
|
42
|
+
nodes {
|
|
43
|
+
id
|
|
44
|
+
body
|
|
45
|
+
createdAt
|
|
46
|
+
updatedAt
|
|
47
|
+
author { login }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Review submissions (Approve / Request changes / Comment), with body if present
|
|
52
|
+
reviews(first: 100, after: $reviewsCursor) {
|
|
53
|
+
pageInfo { hasNextPage endCursor }
|
|
54
|
+
nodes {
|
|
55
|
+
id
|
|
56
|
+
state
|
|
57
|
+
body
|
|
58
|
+
submittedAt
|
|
59
|
+
author { login }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Inline review threads (grouped), includes resolved state
|
|
64
|
+
reviewThreads(first: 100, after: $threadsCursor) {
|
|
65
|
+
pageInfo { hasNextPage endCursor }
|
|
66
|
+
nodes {
|
|
67
|
+
id
|
|
68
|
+
isResolved
|
|
69
|
+
isOutdated
|
|
70
|
+
path
|
|
71
|
+
line
|
|
72
|
+
diffSide
|
|
73
|
+
startLine
|
|
74
|
+
startDiffSide
|
|
75
|
+
originalLine
|
|
76
|
+
originalStartLine
|
|
77
|
+
resolvedBy { login }
|
|
78
|
+
comments(first: 100) {
|
|
79
|
+
nodes {
|
|
80
|
+
id
|
|
81
|
+
body
|
|
82
|
+
createdAt
|
|
83
|
+
updatedAt
|
|
84
|
+
author { login }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _run(cmd: list[str], stdin: str | None = None) -> str:
|
|
96
|
+
p = subprocess.run(cmd, input=stdin, capture_output=True, text=True)
|
|
97
|
+
if p.returncode != 0:
|
|
98
|
+
raise RuntimeError(f"Command failed: {' '.join(cmd)}\n{p.stderr}")
|
|
99
|
+
return p.stdout
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _run_json(cmd: list[str], stdin: str | None = None) -> dict[str, Any]:
|
|
103
|
+
out = _run(cmd, stdin=stdin)
|
|
104
|
+
try:
|
|
105
|
+
return json.loads(out)
|
|
106
|
+
except json.JSONDecodeError as e:
|
|
107
|
+
raise RuntimeError(f"Failed to parse JSON from command output: {e}\nRaw:\n{out}") from e
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _ensure_gh_authenticated() -> None:
|
|
111
|
+
try:
|
|
112
|
+
_run(["gh", "auth", "status"])
|
|
113
|
+
except RuntimeError:
|
|
114
|
+
print("run `gh auth login` to authenticate the GitHub CLI", file=sys.stderr)
|
|
115
|
+
raise RuntimeError("gh auth status failed; run `gh auth login` to authenticate the GitHub CLI") from None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def gh_pr_view_json(fields: str) -> dict[str, Any]:
|
|
119
|
+
# fields is a comma-separated list like: "number,headRepositoryOwner,headRepository"
|
|
120
|
+
return _run_json(["gh", "pr", "view", "--json", fields])
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_current_pr_ref() -> tuple[str, str, int]:
|
|
124
|
+
"""
|
|
125
|
+
Resolve the PR for the current branch (whatever gh considers associated).
|
|
126
|
+
Works for cross-repo PRs too, by reading head repository owner/name.
|
|
127
|
+
"""
|
|
128
|
+
pr = gh_pr_view_json("number,headRepositoryOwner,headRepository")
|
|
129
|
+
owner = pr["headRepositoryOwner"]["login"]
|
|
130
|
+
repo = pr["headRepository"]["name"]
|
|
131
|
+
number = int(pr["number"])
|
|
132
|
+
return owner, repo, number
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def gh_api_graphql(
|
|
136
|
+
owner: str,
|
|
137
|
+
repo: str,
|
|
138
|
+
number: int,
|
|
139
|
+
comments_cursor: str | None = None,
|
|
140
|
+
reviews_cursor: str | None = None,
|
|
141
|
+
threads_cursor: str | None = None,
|
|
142
|
+
) -> dict[str, Any]:
|
|
143
|
+
"""
|
|
144
|
+
Call `gh api graphql` using -F variables, avoiding JSON blobs with nulls.
|
|
145
|
+
Query is passed via stdin using query=@- to avoid shell newline/quoting issues.
|
|
146
|
+
"""
|
|
147
|
+
cmd = [
|
|
148
|
+
"gh",
|
|
149
|
+
"api",
|
|
150
|
+
"graphql",
|
|
151
|
+
"-F",
|
|
152
|
+
"query=@-",
|
|
153
|
+
"-F",
|
|
154
|
+
f"owner={owner}",
|
|
155
|
+
"-F",
|
|
156
|
+
f"repo={repo}",
|
|
157
|
+
"-F",
|
|
158
|
+
f"number={number}",
|
|
159
|
+
]
|
|
160
|
+
if comments_cursor:
|
|
161
|
+
cmd += ["-F", f"commentsCursor={comments_cursor}"]
|
|
162
|
+
if reviews_cursor:
|
|
163
|
+
cmd += ["-F", f"reviewsCursor={reviews_cursor}"]
|
|
164
|
+
if threads_cursor:
|
|
165
|
+
cmd += ["-F", f"threadsCursor={threads_cursor}"]
|
|
166
|
+
|
|
167
|
+
return _run_json(cmd, stdin=QUERY)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def fetch_all(owner: str, repo: str, number: int) -> dict[str, Any]:
|
|
171
|
+
conversation_comments: list[dict[str, Any]] = []
|
|
172
|
+
reviews: list[dict[str, Any]] = []
|
|
173
|
+
review_threads: list[dict[str, Any]] = []
|
|
174
|
+
|
|
175
|
+
comments_cursor: str | None = None
|
|
176
|
+
reviews_cursor: str | None = None
|
|
177
|
+
threads_cursor: str | None = None
|
|
178
|
+
|
|
179
|
+
pr_meta: dict[str, Any] | None = None
|
|
180
|
+
|
|
181
|
+
while True:
|
|
182
|
+
payload = gh_api_graphql(
|
|
183
|
+
owner=owner,
|
|
184
|
+
repo=repo,
|
|
185
|
+
number=number,
|
|
186
|
+
comments_cursor=comments_cursor,
|
|
187
|
+
reviews_cursor=reviews_cursor,
|
|
188
|
+
threads_cursor=threads_cursor,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if "errors" in payload and payload["errors"]:
|
|
192
|
+
raise RuntimeError(f"GitHub GraphQL errors:\n{json.dumps(payload['errors'], indent=2)}")
|
|
193
|
+
|
|
194
|
+
pr = payload["data"]["repository"]["pullRequest"]
|
|
195
|
+
if pr_meta is None:
|
|
196
|
+
pr_meta = {
|
|
197
|
+
"number": pr["number"],
|
|
198
|
+
"url": pr["url"],
|
|
199
|
+
"title": pr["title"],
|
|
200
|
+
"state": pr["state"],
|
|
201
|
+
"owner": owner,
|
|
202
|
+
"repo": repo,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
c = pr["comments"]
|
|
206
|
+
r = pr["reviews"]
|
|
207
|
+
t = pr["reviewThreads"]
|
|
208
|
+
|
|
209
|
+
conversation_comments.extend(c.get("nodes") or [])
|
|
210
|
+
reviews.extend(r.get("nodes") or [])
|
|
211
|
+
review_threads.extend(t.get("nodes") or [])
|
|
212
|
+
|
|
213
|
+
comments_cursor = c["pageInfo"]["endCursor"] if c["pageInfo"]["hasNextPage"] else None
|
|
214
|
+
reviews_cursor = r["pageInfo"]["endCursor"] if r["pageInfo"]["hasNextPage"] else None
|
|
215
|
+
threads_cursor = t["pageInfo"]["endCursor"] if t["pageInfo"]["hasNextPage"] else None
|
|
216
|
+
|
|
217
|
+
if not (comments_cursor or reviews_cursor or threads_cursor):
|
|
218
|
+
break
|
|
219
|
+
|
|
220
|
+
assert pr_meta is not None
|
|
221
|
+
return {
|
|
222
|
+
"pull_request": pr_meta,
|
|
223
|
+
"conversation_comments": conversation_comments,
|
|
224
|
+
"reviews": reviews,
|
|
225
|
+
"review_threads": review_threads,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def main() -> None:
|
|
230
|
+
_ensure_gh_authenticated()
|
|
231
|
+
owner, repo, number = get_current_pr_ref()
|
|
232
|
+
result = fetch_all(owner, repo, number)
|
|
233
|
+
print(json.dumps(result, indent=2))
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
if __name__ == "__main__":
|
|
237
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gh-fix-ci
|
|
3
|
+
description: Use when a user asks to diagnose or fix failing GitHub PR checks in GitHub Actions using gh CLI and logs.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags: [debugging, devops, git]
|
|
6
|
+
dependencies: []
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# gh-fix-ci
|
|
10
|
+
|
|
11
|
+
Diagnose failing PR checks, extract actionable logs, and propose focused fixes.
|
|
12
|
+
|
|
13
|
+
## When to Use
|
|
14
|
+
|
|
15
|
+
- User asks to fix failing GitHub PR checks
|
|
16
|
+
- CI failures are in GitHub Actions
|
|
17
|
+
|
|
18
|
+
## When NOT to Use
|
|
19
|
+
|
|
20
|
+
- Checks are from external providers (Buildkite/Circle/etc.) without actionable logs in GitHub
|
|
21
|
+
- No `gh` auth and user does not want to authenticate
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
1. Verify auth: `gh auth status`
|
|
26
|
+
2. Resolve PR (`gh pr view --json number,url`) or use provided PR number/URL
|
|
27
|
+
3. Run check inspector script
|
|
28
|
+
4. Summarize failing checks and log snippets
|
|
29
|
+
5. Propose fix plan and implement
|
|
30
|
+
6. Re-run relevant checks
|
|
31
|
+
|
|
32
|
+
## Script
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
python3 .opencode/skill/gh-fix-ci/scripts/inspect_pr_checks.py --repo . --pr 123
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Add `--json` for machine-friendly output.
|