cortexhawk 3.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/.cortexhawk-team.yml +65 -0
- package/CHANGELOG.md +268 -0
- package/CLAUDE.md +96 -0
- package/LICENSE +21 -0
- package/PACKS.md +14 -0
- package/README.md +418 -0
- package/REGISTRY.md +23 -0
- package/agents/architect.md +46 -0
- package/agents/brainstormer.md +57 -0
- package/agents/code-simplifier.md +56 -0
- package/agents/codebase-mapper.md +63 -0
- package/agents/copywriter.md +48 -0
- package/agents/debugger.md +44 -0
- package/agents/designer.md +53 -0
- package/agents/devops.md +49 -0
- package/agents/docs-manager.md +50 -0
- package/agents/fullstack-developer.md +55 -0
- package/agents/git-manager.md +63 -0
- package/agents/implementer.md +30 -0
- package/agents/journal-writer.md +53 -0
- package/agents/planner.md +52 -0
- package/agents/project-manager.md +50 -0
- package/agents/researcher.md +46 -0
- package/agents/reviewer.md +63 -0
- package/agents/security-auditor.md +92 -0
- package/agents/teacher.md +71 -0
- package/agents/tester.md +41 -0
- package/commands/api-gen.md +17 -0
- package/commands/backlog.md +26 -0
- package/commands/bootstrap.md +32 -0
- package/commands/brainstorm.md +18 -0
- package/commands/build.md +16 -0
- package/commands/chain.md +46 -0
- package/commands/changelog.md +16 -0
- package/commands/check.md +40 -0
- package/commands/ci.md +32 -0
- package/commands/context.md +35 -0
- package/commands/debug.md +16 -0
- package/commands/deploy.md +16 -0
- package/commands/doc.md +15 -0
- package/commands/export.md +17 -0
- package/commands/journal.md +18 -0
- package/commands/learn.md +16 -0
- package/commands/map.md +16 -0
- package/commands/migrate.md +17 -0
- package/commands/monitor.md +16 -0
- package/commands/optimize.md +17 -0
- package/commands/plan.md +17 -0
- package/commands/pulse.md +46 -0
- package/commands/refactor.md +16 -0
- package/commands/research.md +18 -0
- package/commands/review.md +16 -0
- package/commands/scan.md +19 -0
- package/commands/ship.md +17 -0
- package/commands/simplify.md +16 -0
- package/commands/task.md +32 -0
- package/commands/tdd.md +17 -0
- package/commands/test.md +16 -0
- package/commands/upgrade.md +27 -0
- package/cortexhawk +450 -0
- package/hooks/agent-analytics.sh +67 -0
- package/hooks/branch-guard.sh +56 -0
- package/hooks/codex-dispatcher.sh +84 -0
- package/hooks/commit-guard.sh +71 -0
- package/hooks/compose.yml +47 -0
- package/hooks/dependency-check.sh +56 -0
- package/hooks/file-guard.sh +69 -0
- package/hooks/hooks.json +46 -0
- package/hooks/self-review.sh +71 -0
- package/hooks/session-start.sh +132 -0
- package/hooks/session-telemetry.sh +60 -0
- package/hooks/test-reminder.sh +75 -0
- package/install.sh +3805 -0
- package/mcp/README.md +37 -0
- package/mcp/context7.json +8 -0
- package/mcp/puppeteer.json +8 -0
- package/mcp/sequential-thinking.json +8 -0
- package/modes/default.md +5 -0
- package/modes/fast.md +5 -0
- package/modes/learn.md +9 -0
- package/modes/orchestration.md +5 -0
- package/modes/pair.md +10 -0
- package/modes/research.md +5 -0
- package/modes/review.md +5 -0
- package/package.json +32 -0
- package/profiles/api.json +27 -0
- package/profiles/data.json +23 -0
- package/profiles/fullstack.json +27 -0
- package/scripts/autodetect-profile.sh +68 -0
- package/scripts/benchmark.sh +106 -0
- package/scripts/chain-post-save.sh +23 -0
- package/scripts/generate-plans-index.sh +50 -0
- package/scripts/git-workflow-init.sh +115 -0
- package/scripts/install-codex.sh +128 -0
- package/scripts/interactive-init.sh +264 -0
- package/scripts/post-install-audit.sh +130 -0
- package/scripts/validate.sh +214 -0
- package/settings.json +90 -0
- package/setup.sh +67 -0
- package/skills/databases/schema-designer/SKILL.md +54 -0
- package/skills/databases/sql-optimizer/SKILL.md +37 -0
- package/skills/devops/ci-cd/SKILL.md +59 -0
- package/skills/devops/deployment/SKILL.md +49 -0
- package/skills/devops/docker/SKILL.md +57 -0
- package/skills/frameworks/api-design/SKILL.md +103 -0
- package/skills/frameworks/fastapi/SKILL.md +68 -0
- package/skills/frameworks/nextjs/SKILL.md +74 -0
- package/skills/frameworks/python/SKILL.md +89 -0
- package/skills/frameworks/react/SKILL.md +83 -0
- package/skills/frameworks/sveltekit/SKILL.md +69 -0
- package/skills/frameworks/tailwindcss/SKILL.md +75 -0
- package/skills/frameworks/typescript/SKILL.md +94 -0
- package/skills/meta/mcp-builder/SKILL.md +54 -0
- package/skills/meta/skill-creator/SKILL.md +43 -0
- package/skills/optimization/performance/SKILL.md +70 -0
- package/skills/quality/complexity-analyzer/SKILL.md +52 -0
- package/skills/quality/error-handling/SKILL.md +123 -0
- package/skills/quality/log-analyzer/SKILL.md +31 -0
- package/skills/quality/pattern-detector/SKILL.md +50 -0
- package/skills/security/auth-analyzer/SKILL.md +96 -0
- package/skills/security/compliance-checker/SKILL.md +92 -0
- package/skills/security/container-security/SKILL.md +128 -0
- package/skills/security/dependency-auditor/SKILL.md +100 -0
- package/skills/security/encryption/SKILL.md +94 -0
- package/skills/security/incident-response/SKILL.md +127 -0
- package/skills/security/secrets/SKILL.md +93 -0
- package/skills/security/security-headers/SKILL.md +83 -0
- package/skills/security/security-logging/SKILL.md +107 -0
- package/skills/security/vulnerability-scanner/SKILL.md +114 -0
- package/skills/testing/e2e-testing/SKILL.md +119 -0
- package/skills/testing/tdd/SKILL.md +40 -0
- package/skills/testing/test-generator/SKILL.md +39 -0
- package/skills/workflow/commit/SKILL.md +61 -0
- package/skills/workflow/confidence-check/SKILL.md +90 -0
- package/skills/workflow/pr-review-comments/SKILL.md +81 -0
- package/skills/workflow/pr-review-comments/scripts/fetch_comments.py +237 -0
|
@@ -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()
|