@sentry/junior-github 0.108.0 → 0.109.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/SETUP.md +2 -2
- package/dist/db/schema.d.ts +45 -0
- package/dist/index.js +102 -81
- package/dist/issue-outcomes/store.d.ts +6 -0
- package/migrations/0004_marvelous_toad_men.sql +5 -0
- package/migrations/meta/0004_snapshot.json +275 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +2 -2
- package/skills/attach-github-assets/SOURCES.md +2 -2
- package/skills/attach-github-assets/SPEC.md +2 -2
- package/skills/github-code/SKILL.md +3 -14
package/SETUP.md
CHANGED
|
@@ -133,8 +133,8 @@ Use `additionalUserScopes` only when a human-identity integration flow requires
|
|
|
133
133
|
## 3) Runtime behavior
|
|
134
134
|
|
|
135
135
|
- When either GitHub skill is active, authenticated `gh` and `git` commands cause the runtime to inject GitHub credentials automatically for the current turn.
|
|
136
|
-
- The plugin classifies GitHub traffic from the forwarded HTTP request. Reads use `installation-read`, while `GET /user` uses `user-read`. Allowlisted App-owned mutations
|
|
137
|
-
- `user-read` and explicitly human `user-write` operations require the actor, or an explicitly delegated user subject, to authorize the GitHub App through the private OAuth flow. Junior-owned issue, pull request,
|
|
136
|
+
- The plugin classifies GitHub traffic from the forwarded HTTP request. Reads use `installation-read`, while `GET /user` uses `user-read`. Allowlisted App-owned mutations and Git smart-HTTP pushes use repository-scoped `installation-write`. User-attachment uploads to `uploads.github.com/user-attachments/assets` use `user-write`. Unknown REST writes and GraphQL mutations are denied.
|
|
137
|
+
- `user-read` and explicitly human `user-write` operations require the actor, or an explicitly delegated user subject, to authorize the GitHub App through the private OAuth flow. Junior-owned issue, pull request, and branch operations do not fall back to user OAuth.
|
|
138
138
|
- Headless resource-event turns use the `resource-event` system actor and may receive the same repository-scoped installation grants. This lets Junior respond to subscribed pull request events by committing and pushing fixes without inheriting a subscriber's OAuth credential.
|
|
139
139
|
- Git commits use Junior as author and committer. Resolvable human run actors are credited once with `Co-Authored-By` trailers.
|
|
140
140
|
- Issued credentials are reused only within the current turn, credential leases are cached by plugin grant and repository lease scope, and upstream 403 permission denials clear the cached lease before the next retry.
|
package/dist/db/schema.d.ts
CHANGED
|
@@ -15,6 +15,13 @@ export declare const githubIssueStateSchema: z.ZodEnum<{
|
|
|
15
15
|
closed: "closed";
|
|
16
16
|
}>;
|
|
17
17
|
export type GitHubIssueState = z.output<typeof githubIssueStateSchema>;
|
|
18
|
+
export declare const githubIssueStateReasonSchema: z.ZodEnum<{
|
|
19
|
+
completed: "completed";
|
|
20
|
+
duplicate: "duplicate";
|
|
21
|
+
not_planned: "not_planned";
|
|
22
|
+
reopened: "reopened";
|
|
23
|
+
}>;
|
|
24
|
+
export type GitHubIssueStateReason = z.output<typeof githubIssueStateReasonSchema>;
|
|
18
25
|
/** Current provider projection for issues classified as Junior-owned. */
|
|
19
26
|
export declare const juniorGitHubIssues: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
20
27
|
name: "junior_github_issues";
|
|
@@ -107,6 +114,25 @@ export declare const juniorGitHubIssues: import("drizzle-orm/pg-core").PgTableWi
|
|
|
107
114
|
}, {}, {
|
|
108
115
|
$type: "open" | "closed";
|
|
109
116
|
}>;
|
|
117
|
+
stateReason: import("drizzle-orm/pg-core").PgColumn<{
|
|
118
|
+
name: "state_reason";
|
|
119
|
+
tableName: "junior_github_issues";
|
|
120
|
+
dataType: "string";
|
|
121
|
+
columnType: "PgText";
|
|
122
|
+
data: "completed" | "duplicate" | "not_planned" | "reopened";
|
|
123
|
+
driverParam: string;
|
|
124
|
+
notNull: false;
|
|
125
|
+
hasDefault: false;
|
|
126
|
+
isPrimaryKey: false;
|
|
127
|
+
isAutoincrement: false;
|
|
128
|
+
hasRuntimeDefault: false;
|
|
129
|
+
enumValues: [string, ...string[]];
|
|
130
|
+
baseColumn: never;
|
|
131
|
+
identity: undefined;
|
|
132
|
+
generated: undefined;
|
|
133
|
+
}, {}, {
|
|
134
|
+
$type: "completed" | "duplicate" | "not_planned" | "reopened";
|
|
135
|
+
}>;
|
|
110
136
|
openedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
111
137
|
name: "opened_at";
|
|
112
138
|
tableName: "junior_github_issues";
|
|
@@ -478,6 +504,25 @@ export declare const githubSqlSchema: {
|
|
|
478
504
|
}, {}, {
|
|
479
505
|
$type: "open" | "closed";
|
|
480
506
|
}>;
|
|
507
|
+
stateReason: import("drizzle-orm/pg-core").PgColumn<{
|
|
508
|
+
name: "state_reason";
|
|
509
|
+
tableName: "junior_github_issues";
|
|
510
|
+
dataType: "string";
|
|
511
|
+
columnType: "PgText";
|
|
512
|
+
data: "completed" | "duplicate" | "not_planned" | "reopened";
|
|
513
|
+
driverParam: string;
|
|
514
|
+
notNull: false;
|
|
515
|
+
hasDefault: false;
|
|
516
|
+
isPrimaryKey: false;
|
|
517
|
+
isAutoincrement: false;
|
|
518
|
+
hasRuntimeDefault: false;
|
|
519
|
+
enumValues: [string, ...string[]];
|
|
520
|
+
baseColumn: never;
|
|
521
|
+
identity: undefined;
|
|
522
|
+
generated: undefined;
|
|
523
|
+
}, {}, {
|
|
524
|
+
$type: "completed" | "duplicate" | "not_planned" | "reopened";
|
|
525
|
+
}>;
|
|
481
526
|
openedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
482
527
|
name: "opened_at";
|
|
483
528
|
tableName: "junior_github_issues";
|
package/dist/index.js
CHANGED
|
@@ -1113,6 +1113,12 @@ var githubPullRequestCommitCompositionSchema = z5.enum([
|
|
|
1113
1113
|
"mixed"
|
|
1114
1114
|
]);
|
|
1115
1115
|
var githubIssueStateSchema = z5.enum(["closed", "open"]);
|
|
1116
|
+
var githubIssueStateReasonSchema = z5.enum([
|
|
1117
|
+
"completed",
|
|
1118
|
+
"duplicate",
|
|
1119
|
+
"not_planned",
|
|
1120
|
+
"reopened"
|
|
1121
|
+
]);
|
|
1116
1122
|
var juniorGitHubIssues = pgTable(
|
|
1117
1123
|
"junior_github_issues",
|
|
1118
1124
|
{
|
|
@@ -1121,6 +1127,7 @@ var juniorGitHubIssues = pgTable(
|
|
|
1121
1127
|
repositoryFullName: text("repository_full_name").notNull(),
|
|
1122
1128
|
number: integer("number").notNull(),
|
|
1123
1129
|
state: text("state").$type().notNull(),
|
|
1130
|
+
stateReason: text("state_reason").$type(),
|
|
1124
1131
|
openedAt: timestamp("opened_at", { withTimezone: true }).notNull(),
|
|
1125
1132
|
closedAt: timestamp("closed_at", { withTimezone: true }),
|
|
1126
1133
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull()
|
|
@@ -1164,6 +1171,7 @@ var githubIssueOutcomeInputSchema = z6.object({
|
|
|
1164
1171
|
repositoryFullName: z6.string().min(1),
|
|
1165
1172
|
repositoryId: z6.string().min(1),
|
|
1166
1173
|
state: githubIssueStateSchema,
|
|
1174
|
+
stateReason: githubIssueStateReasonSchema.optional(),
|
|
1167
1175
|
updatedAt: z6.date()
|
|
1168
1176
|
}).strict();
|
|
1169
1177
|
function projectionValues(input) {
|
|
@@ -1174,6 +1182,7 @@ function projectionValues(input) {
|
|
|
1174
1182
|
repositoryFullName: input.repositoryFullName,
|
|
1175
1183
|
repositoryId: input.repositoryId,
|
|
1176
1184
|
state: input.state,
|
|
1185
|
+
stateReason: input.stateReason ?? null,
|
|
1177
1186
|
updatedAt: input.updatedAt
|
|
1178
1187
|
};
|
|
1179
1188
|
}
|
|
@@ -1305,6 +1314,7 @@ var canonicalIssueOutcomeSchema = z8.object({
|
|
|
1305
1314
|
created_at: z8.string(),
|
|
1306
1315
|
id: z8.number().int().positive(),
|
|
1307
1316
|
number: z8.number().int().positive(),
|
|
1317
|
+
state_reason: githubIssueStateReasonSchema.nullable().optional(),
|
|
1308
1318
|
updated_at: z8.string(),
|
|
1309
1319
|
user: z8.object({ login: z8.string().min(1) }).strict()
|
|
1310
1320
|
}).strict(),
|
|
@@ -1321,6 +1331,7 @@ var issueOutcomeSchema = z8.object({
|
|
|
1321
1331
|
created_at: z8.string(),
|
|
1322
1332
|
id: z8.number().int().positive(),
|
|
1323
1333
|
number: z8.number().int().positive(),
|
|
1334
|
+
state_reason: githubIssueStateReasonSchema.nullable().optional(),
|
|
1324
1335
|
updated_at: z8.string(),
|
|
1325
1336
|
user: z8.object({ login: z8.string().min(1) }).passthrough()
|
|
1326
1337
|
}).passthrough(),
|
|
@@ -1337,6 +1348,7 @@ var issueOutcomeSchema = z8.object({
|
|
|
1337
1348
|
created_at: provider.issue.created_at,
|
|
1338
1349
|
id: provider.issue.id,
|
|
1339
1350
|
number: provider.issue.number,
|
|
1351
|
+
state_reason: provider.issue.state_reason,
|
|
1340
1352
|
updated_at: provider.issue.updated_at,
|
|
1341
1353
|
user: { login: provider.issue.user.login }
|
|
1342
1354
|
},
|
|
@@ -1388,6 +1400,7 @@ function normalizeGitHubIssueOutcome(args) {
|
|
|
1388
1400
|
repositoryFullName: parsed.repository.full_name,
|
|
1389
1401
|
repositoryId: String(parsed.repository.id),
|
|
1390
1402
|
state: parsed.action === "closed" ? "closed" : "open",
|
|
1403
|
+
stateReason: parsed.action === "closed" ? issue.state_reason ?? void 0 : void 0,
|
|
1391
1404
|
updatedAt
|
|
1392
1405
|
};
|
|
1393
1406
|
}
|
|
@@ -1636,8 +1649,7 @@ var pullRequestStatsSchema = z10.object({
|
|
|
1636
1649
|
juniorOnly: z10.number().int().nonnegative(),
|
|
1637
1650
|
medianMergeTimeMs: z10.number().nonnegative().nullable(),
|
|
1638
1651
|
merged: z10.number().int().nonnegative(),
|
|
1639
|
-
mixed: z10.number().int().nonnegative()
|
|
1640
|
-
open: z10.number().int().nonnegative()
|
|
1652
|
+
mixed: z10.number().int().nonnegative()
|
|
1641
1653
|
}).strict().transform((row) => {
|
|
1642
1654
|
const terminal = row.merged + row.closed;
|
|
1643
1655
|
const classified = row.juniorOnly + row.mixed;
|
|
@@ -1655,7 +1667,6 @@ var pullRequestRepositoryStatsSchema = z10.object({
|
|
|
1655
1667
|
juniorOnly: z10.number().int().nonnegative(),
|
|
1656
1668
|
merged: z10.number().int().nonnegative(),
|
|
1657
1669
|
mixed: z10.number().int().nonnegative(),
|
|
1658
|
-
open: z10.number().int().nonnegative(),
|
|
1659
1670
|
repository: z10.string().min(1)
|
|
1660
1671
|
}).strict().transform((row) => {
|
|
1661
1672
|
const terminal = row.merged + row.closed;
|
|
@@ -1665,19 +1676,23 @@ var pullRequestRepositoryStatsSchema = z10.object({
|
|
|
1665
1676
|
};
|
|
1666
1677
|
});
|
|
1667
1678
|
var issueStatsSchema = z10.object({
|
|
1668
|
-
|
|
1679
|
+
closedCompleted: z10.number().int().nonnegative(),
|
|
1680
|
+
closedDuplicate: z10.number().int().nonnegative(),
|
|
1681
|
+
closedNotPlanned: z10.number().int().nonnegative(),
|
|
1682
|
+
closedUnknown: z10.number().int().nonnegative(),
|
|
1669
1683
|
created: z10.number().int().nonnegative(),
|
|
1670
1684
|
days: z10.number().int().positive(),
|
|
1671
|
-
medianCloseTimeMs: z10.number().nonnegative().nullable()
|
|
1672
|
-
open: z10.number().int().nonnegative()
|
|
1685
|
+
medianCloseTimeMs: z10.number().nonnegative().nullable()
|
|
1673
1686
|
}).strict().transform((row) => ({
|
|
1674
1687
|
...row,
|
|
1675
1688
|
medianCloseTimeMs: row.medianCloseTimeMs ?? void 0
|
|
1676
1689
|
}));
|
|
1677
1690
|
var issueRepositoryStatsSchema = z10.object({
|
|
1678
|
-
|
|
1691
|
+
closedCompleted: z10.number().int().nonnegative(),
|
|
1692
|
+
closedDuplicate: z10.number().int().nonnegative(),
|
|
1693
|
+
closedNotPlanned: z10.number().int().nonnegative(),
|
|
1694
|
+
closedUnknown: z10.number().int().nonnegative(),
|
|
1679
1695
|
created: z10.number().int().nonnegative(),
|
|
1680
|
-
open: z10.number().int().nonnegative(),
|
|
1681
1696
|
repository: z10.string().min(1)
|
|
1682
1697
|
}).strict();
|
|
1683
1698
|
function queryRows(result) {
|
|
@@ -1707,8 +1722,7 @@ async function aggregatePullRequestWindows(args) {
|
|
|
1707
1722
|
${table.mergedAt},
|
|
1708
1723
|
${table.closedAt}
|
|
1709
1724
|
FROM ${table}
|
|
1710
|
-
WHERE ${table.
|
|
1711
|
-
OR ${table.openedAt} >= ${oldestStart}
|
|
1725
|
+
WHERE ${table.openedAt} >= ${oldestStart}
|
|
1712
1726
|
OR ${table.mergedAt} >= ${oldestStart}
|
|
1713
1727
|
OR ${table.closedAt} >= ${oldestStart}
|
|
1714
1728
|
)
|
|
@@ -1727,8 +1741,6 @@ async function aggregatePullRequestWindows(args) {
|
|
|
1727
1741
|
WHERE recent_pull_requests.state = 'closed_unmerged'
|
|
1728
1742
|
AND recent_pull_requests.closed_at >= windows.start_at
|
|
1729
1743
|
)::integer AS "closed",
|
|
1730
|
-
count(recent_pull_requests.pull_request_id)
|
|
1731
|
-
FILTER (WHERE recent_pull_requests.state = 'open')::integer AS "open",
|
|
1732
1744
|
count(recent_pull_requests.pull_request_id)
|
|
1733
1745
|
FILTER (
|
|
1734
1746
|
WHERE recent_pull_requests.state = 'merged'
|
|
@@ -1781,7 +1793,6 @@ async function aggregatePullRequestRepositories(args) {
|
|
|
1781
1793
|
WHERE ${table.state} = 'closed_unmerged'
|
|
1782
1794
|
AND ${table.closedAt} >= ${start}
|
|
1783
1795
|
)::integer AS "closed",
|
|
1784
|
-
count(*) FILTER (WHERE ${table.state} = 'open')::integer AS "open",
|
|
1785
1796
|
count(*) FILTER (
|
|
1786
1797
|
WHERE ${table.state} = 'merged'
|
|
1787
1798
|
AND ${table.mergedAt} >= ${start}
|
|
@@ -1798,8 +1809,7 @@ async function aggregatePullRequestRepositories(args) {
|
|
|
1798
1809
|
AND ${table.commitComposition} IS NULL
|
|
1799
1810
|
)::integer AS "compositionUnknown"
|
|
1800
1811
|
FROM ${table}
|
|
1801
|
-
WHERE ${table.
|
|
1802
|
-
OR ${table.openedAt} >= ${start}
|
|
1812
|
+
WHERE ${table.openedAt} >= ${start}
|
|
1803
1813
|
OR ${table.mergedAt} >= ${start}
|
|
1804
1814
|
OR ${table.closedAt} >= ${start}
|
|
1805
1815
|
GROUP BY ${table.repositoryFullName}
|
|
@@ -1824,11 +1834,11 @@ async function aggregateIssueWindows(args) {
|
|
|
1824
1834
|
SELECT
|
|
1825
1835
|
${table.issueId},
|
|
1826
1836
|
${table.state},
|
|
1837
|
+
${table.stateReason},
|
|
1827
1838
|
${table.openedAt},
|
|
1828
1839
|
${table.closedAt}
|
|
1829
1840
|
FROM ${table}
|
|
1830
|
-
WHERE ${table.
|
|
1831
|
-
OR ${table.openedAt} >= ${oldestStart}
|
|
1841
|
+
WHERE ${table.openedAt} >= ${oldestStart}
|
|
1832
1842
|
OR ${table.closedAt} >= ${oldestStart}
|
|
1833
1843
|
)
|
|
1834
1844
|
SELECT
|
|
@@ -1839,10 +1849,27 @@ async function aggregateIssueWindows(args) {
|
|
|
1839
1849
|
count(recent_issues.issue_id)
|
|
1840
1850
|
FILTER (
|
|
1841
1851
|
WHERE recent_issues.state = 'closed'
|
|
1852
|
+
AND recent_issues.state_reason = 'completed'
|
|
1842
1853
|
AND recent_issues.closed_at >= windows.start_at
|
|
1843
|
-
)::integer AS "
|
|
1854
|
+
)::integer AS "closedCompleted",
|
|
1855
|
+
count(recent_issues.issue_id)
|
|
1856
|
+
FILTER (
|
|
1857
|
+
WHERE recent_issues.state = 'closed'
|
|
1858
|
+
AND recent_issues.state_reason = 'duplicate'
|
|
1859
|
+
AND recent_issues.closed_at >= windows.start_at
|
|
1860
|
+
)::integer AS "closedDuplicate",
|
|
1861
|
+
count(recent_issues.issue_id)
|
|
1862
|
+
FILTER (
|
|
1863
|
+
WHERE recent_issues.state = 'closed'
|
|
1864
|
+
AND recent_issues.state_reason = 'not_planned'
|
|
1865
|
+
AND recent_issues.closed_at >= windows.start_at
|
|
1866
|
+
)::integer AS "closedNotPlanned",
|
|
1844
1867
|
count(recent_issues.issue_id)
|
|
1845
|
-
FILTER (
|
|
1868
|
+
FILTER (
|
|
1869
|
+
WHERE recent_issues.state = 'closed'
|
|
1870
|
+
AND recent_issues.state_reason IS NULL
|
|
1871
|
+
AND recent_issues.closed_at >= windows.start_at
|
|
1872
|
+
)::integer AS "closedUnknown",
|
|
1846
1873
|
(
|
|
1847
1874
|
percentile_cont(0.5) WITHIN GROUP (
|
|
1848
1875
|
ORDER BY extract(
|
|
@@ -1869,15 +1896,30 @@ async function aggregateIssueRepositories(args) {
|
|
|
1869
1896
|
count(*) FILTER (WHERE ${table.openedAt} >= ${start})::integer
|
|
1870
1897
|
AS "created",
|
|
1871
1898
|
count(*) FILTER (
|
|
1872
|
-
WHERE ${table.state} = 'closed'
|
|
1873
|
-
|
|
1874
|
-
|
|
1899
|
+
WHERE ${table.state} = 'closed'
|
|
1900
|
+
AND ${table.stateReason} = 'completed'
|
|
1901
|
+
AND ${table.closedAt} >= ${start}
|
|
1902
|
+
)::integer AS "closedCompleted",
|
|
1903
|
+
count(*) FILTER (
|
|
1904
|
+
WHERE ${table.state} = 'closed'
|
|
1905
|
+
AND ${table.stateReason} = 'duplicate'
|
|
1906
|
+
AND ${table.closedAt} >= ${start}
|
|
1907
|
+
)::integer AS "closedDuplicate",
|
|
1908
|
+
count(*) FILTER (
|
|
1909
|
+
WHERE ${table.state} = 'closed'
|
|
1910
|
+
AND ${table.stateReason} = 'not_planned'
|
|
1911
|
+
AND ${table.closedAt} >= ${start}
|
|
1912
|
+
)::integer AS "closedNotPlanned",
|
|
1913
|
+
count(*) FILTER (
|
|
1914
|
+
WHERE ${table.state} = 'closed'
|
|
1915
|
+
AND ${table.stateReason} IS NULL
|
|
1916
|
+
AND ${table.closedAt} >= ${start}
|
|
1917
|
+
)::integer AS "closedUnknown"
|
|
1875
1918
|
FROM ${table}
|
|
1876
|
-
WHERE ${table.
|
|
1877
|
-
OR ${table.openedAt} >= ${start}
|
|
1919
|
+
WHERE ${table.openedAt} >= ${start}
|
|
1878
1920
|
OR ${table.closedAt} >= ${start}
|
|
1879
1921
|
GROUP BY ${table.repositoryFullName}
|
|
1880
|
-
ORDER BY "created" DESC, "
|
|
1922
|
+
ORDER BY "created" DESC, "closedCompleted" DESC, "repository" ASC
|
|
1881
1923
|
LIMIT 25
|
|
1882
1924
|
`);
|
|
1883
1925
|
return z10.array(issueRepositoryStatsSchema).parse(queryRows(result));
|
|
@@ -1918,7 +1960,10 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
1918
1960
|
label: "Junior-only merge rate \xB7 30d",
|
|
1919
1961
|
value: formatPercent(thirtyDays.juniorOnlyRate)
|
|
1920
1962
|
},
|
|
1921
|
-
{
|
|
1963
|
+
{
|
|
1964
|
+
label: "PRs closed unmerged \xB7 30d",
|
|
1965
|
+
value: String(thirtyDays.closed)
|
|
1966
|
+
},
|
|
1922
1967
|
{
|
|
1923
1968
|
label: "PR merge rate \xB7 30d",
|
|
1924
1969
|
value: formatPercent(thirtyDays.mergeRate)
|
|
@@ -1928,7 +1973,19 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
1928
1973
|
value: formatDuration(thirtyDays.medianMergeTimeMs)
|
|
1929
1974
|
},
|
|
1930
1975
|
{ label: "issues created \xB7 30d", value: String(issueThirtyDays.created) },
|
|
1931
|
-
{
|
|
1976
|
+
{
|
|
1977
|
+
label: "issues completed \xB7 30d",
|
|
1978
|
+
tone: issueThirtyDays.closedCompleted > 0 ? "good" : "neutral",
|
|
1979
|
+
value: String(issueThirtyDays.closedCompleted)
|
|
1980
|
+
},
|
|
1981
|
+
{
|
|
1982
|
+
label: "issues closed as duplicate \xB7 30d",
|
|
1983
|
+
value: String(issueThirtyDays.closedDuplicate)
|
|
1984
|
+
},
|
|
1985
|
+
{
|
|
1986
|
+
label: "issues closed as not planned \xB7 30d",
|
|
1987
|
+
value: String(issueThirtyDays.closedNotPlanned)
|
|
1988
|
+
}
|
|
1932
1989
|
],
|
|
1933
1990
|
recordSets: [
|
|
1934
1991
|
{
|
|
@@ -1963,7 +2020,6 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
1963
2020
|
{ key: "created", label: "Created" },
|
|
1964
2021
|
{ key: "merged", label: "Merged" },
|
|
1965
2022
|
{ key: "closed", label: "Closed unmerged" },
|
|
1966
|
-
{ key: "open", label: "Open now" },
|
|
1967
2023
|
{ key: "commitComposition", label: "Merged commit composition" },
|
|
1968
2024
|
{ key: "mergeRate", label: "Merge rate" }
|
|
1969
2025
|
],
|
|
@@ -1974,7 +2030,6 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
1974
2030
|
created: String(stats.created),
|
|
1975
2031
|
merged: String(stats.merged),
|
|
1976
2032
|
closed: String(stats.closed),
|
|
1977
|
-
open: String(stats.open),
|
|
1978
2033
|
commitComposition: formatCommitComposition(stats),
|
|
1979
2034
|
mergeRate: formatPercent(stats.mergeRate)
|
|
1980
2035
|
}
|
|
@@ -1985,8 +2040,10 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
1985
2040
|
fields: [
|
|
1986
2041
|
{ key: "window", label: "Window" },
|
|
1987
2042
|
{ key: "created", label: "Created" },
|
|
1988
|
-
{ key: "
|
|
1989
|
-
{ key: "
|
|
2043
|
+
{ key: "completed", label: "Completed" },
|
|
2044
|
+
{ key: "duplicate", label: "Duplicate" },
|
|
2045
|
+
{ key: "notPlanned", label: "Not planned" },
|
|
2046
|
+
{ key: "unknown", label: "Unknown reason" },
|
|
1990
2047
|
{ key: "closeTime", label: "Median close time" }
|
|
1991
2048
|
],
|
|
1992
2049
|
records: issueWindows.map((stats) => ({
|
|
@@ -1994,8 +2051,10 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
1994
2051
|
values: {
|
|
1995
2052
|
window: `${stats.days} days`,
|
|
1996
2053
|
created: String(stats.created),
|
|
1997
|
-
|
|
1998
|
-
|
|
2054
|
+
completed: String(stats.closedCompleted),
|
|
2055
|
+
duplicate: String(stats.closedDuplicate),
|
|
2056
|
+
notPlanned: String(stats.closedNotPlanned),
|
|
2057
|
+
unknown: String(stats.closedUnknown),
|
|
1999
2058
|
closeTime: formatDuration(stats.medianCloseTimeMs)
|
|
2000
2059
|
}
|
|
2001
2060
|
}))
|
|
@@ -2006,16 +2065,20 @@ async function buildGitHubOutcomeReport(args) {
|
|
|
2006
2065
|
fields: [
|
|
2007
2066
|
{ key: "repository", label: "Repository" },
|
|
2008
2067
|
{ key: "created", label: "Created" },
|
|
2009
|
-
{ key: "
|
|
2010
|
-
{ key: "
|
|
2068
|
+
{ key: "completed", label: "Completed" },
|
|
2069
|
+
{ key: "duplicate", label: "Duplicate" },
|
|
2070
|
+
{ key: "notPlanned", label: "Not planned" },
|
|
2071
|
+
{ key: "unknown", label: "Unknown reason" }
|
|
2011
2072
|
],
|
|
2012
2073
|
records: issueRepositories.map(({ repository, ...stats }) => ({
|
|
2013
2074
|
id: repository,
|
|
2014
2075
|
values: {
|
|
2015
2076
|
repository,
|
|
2016
2077
|
created: String(stats.created),
|
|
2017
|
-
|
|
2018
|
-
|
|
2078
|
+
completed: String(stats.closedCompleted),
|
|
2079
|
+
duplicate: String(stats.closedDuplicate),
|
|
2080
|
+
notPlanned: String(stats.closedNotPlanned),
|
|
2081
|
+
unknown: String(stats.closedUnknown)
|
|
2019
2082
|
}
|
|
2020
2083
|
}))
|
|
2021
2084
|
}
|
|
@@ -2641,28 +2704,6 @@ function githubRepositoryFromLeaseScope(leaseScope) {
|
|
|
2641
2704
|
}
|
|
2642
2705
|
return { owner: match[1], name: match[2] };
|
|
2643
2706
|
}
|
|
2644
|
-
function githubRepositoryIdFromUploadUrl(upstreamUrl) {
|
|
2645
|
-
const repositoryId = Number(upstreamUrl.searchParams.get("repository_id"));
|
|
2646
|
-
if (!Number.isSafeInteger(repositoryId) || repositoryId <= 0) {
|
|
2647
|
-
throw new EgressPolicyDenied(
|
|
2648
|
-
"GitHub asset upload request is missing a valid repository_id."
|
|
2649
|
-
);
|
|
2650
|
-
}
|
|
2651
|
-
return repositoryId;
|
|
2652
|
-
}
|
|
2653
|
-
function githubRepositoryIdLeaseScope(repositoryId) {
|
|
2654
|
-
return `repository-id:${repositoryId}`;
|
|
2655
|
-
}
|
|
2656
|
-
function githubRepositoryIdFromLeaseScope(leaseScope) {
|
|
2657
|
-
const match = /^repository-id:(\d+)$/.exec(leaseScope ?? "");
|
|
2658
|
-
const repositoryId = Number(match?.[1]);
|
|
2659
|
-
if (!Number.isSafeInteger(repositoryId) || repositoryId <= 0) {
|
|
2660
|
-
throw new GitHubPluginSetupError(
|
|
2661
|
-
"GitHub asset upload grant is missing a repository id lease scope."
|
|
2662
|
-
);
|
|
2663
|
-
}
|
|
2664
|
-
return repositoryId;
|
|
2665
|
-
}
|
|
2666
2707
|
async function resolveUserAccount(tokens) {
|
|
2667
2708
|
const account = await githubRequest("https://api.github.com", "/user", {
|
|
2668
2709
|
token: tokens.accessToken
|
|
@@ -2843,8 +2884,7 @@ async function issueInstallationToken(options) {
|
|
|
2843
2884
|
const permissions = "permissions" in options ? options.permissions : typeof options.loadPermissions === "function" ? await options.loadPermissions({ appJwt, installationId }) : void 0;
|
|
2844
2885
|
const body = {
|
|
2845
2886
|
...permissions ? { permissions } : {},
|
|
2846
|
-
..."repositories" in options ? { repositories: options.repositories } : {}
|
|
2847
|
-
..."repositoryIds" in options ? { repository_ids: options.repositoryIds } : {}
|
|
2887
|
+
..."repositories" in options ? { repositories: options.repositories } : {}
|
|
2848
2888
|
};
|
|
2849
2889
|
const accessTokenResponse = await githubRequest(
|
|
2850
2890
|
"https://api.github.com",
|
|
@@ -2864,7 +2904,6 @@ async function issueInstallationToken(options) {
|
|
|
2864
2904
|
async function issueInstallationCredential(options) {
|
|
2865
2905
|
const token = await issueInstallationToken(options);
|
|
2866
2906
|
return createCredentialLease({
|
|
2867
|
-
...options.domains ? { domains: options.domains } : {},
|
|
2868
2907
|
token: token.token,
|
|
2869
2908
|
expiresAtMs: token.expiresAtMs
|
|
2870
2909
|
});
|
|
@@ -3183,13 +3222,7 @@ async function githubGrantForEgress(ctx) {
|
|
|
3183
3222
|
upstreamUrl
|
|
3184
3223
|
});
|
|
3185
3224
|
if (isGitHubAssetUploadRequest(method, upstreamUrl)) {
|
|
3186
|
-
|
|
3187
|
-
return grantForAccess(
|
|
3188
|
-
"write",
|
|
3189
|
-
"github.asset-upload",
|
|
3190
|
-
"installation-write",
|
|
3191
|
-
githubRepositoryIdLeaseScope(repositoryId)
|
|
3192
|
-
);
|
|
3225
|
+
return grantForAccess("write", "github.asset-upload", "user-write");
|
|
3193
3226
|
}
|
|
3194
3227
|
const smartHttpAccess = githubSmartHttpAccess(upstreamUrl);
|
|
3195
3228
|
if (smartHttpAccess) {
|
|
@@ -3423,18 +3456,6 @@ function githubPlugin(options = {}) {
|
|
|
3423
3456
|
});
|
|
3424
3457
|
}
|
|
3425
3458
|
if (ctx.grant.name === "installation-write") {
|
|
3426
|
-
if (ctx.grant.reason === "github.asset-upload") {
|
|
3427
|
-
const repositoryId = githubRepositoryIdFromLeaseScope(
|
|
3428
|
-
ctx.grant.leaseScope
|
|
3429
|
-
);
|
|
3430
|
-
return await issueInstallationCredential({
|
|
3431
|
-
appIdEnv,
|
|
3432
|
-
domains: GITHUB_ASSET_UPLOAD_CREDENTIAL_DOMAINS,
|
|
3433
|
-
privateKeyEnv,
|
|
3434
|
-
installationIdEnv,
|
|
3435
|
-
repositoryIds: [repositoryId]
|
|
3436
|
-
});
|
|
3437
|
-
}
|
|
3438
3459
|
const repository = githubRepositoryFromLeaseScope(
|
|
3439
3460
|
ctx.grant.leaseScope
|
|
3440
3461
|
);
|
|
@@ -12,6 +12,12 @@ declare const githubIssueOutcomeInputSchema: z.ZodObject<{
|
|
|
12
12
|
open: "open";
|
|
13
13
|
closed: "closed";
|
|
14
14
|
}>;
|
|
15
|
+
stateReason: z.ZodOptional<z.ZodEnum<{
|
|
16
|
+
completed: "completed";
|
|
17
|
+
duplicate: "duplicate";
|
|
18
|
+
not_planned: "not_planned";
|
|
19
|
+
reopened: "reopened";
|
|
20
|
+
}>>;
|
|
15
21
|
updatedAt: z.ZodDate;
|
|
16
22
|
}, z.core.$strict>;
|
|
17
23
|
export type GitHubIssueOutcomeInput = z.output<typeof githubIssueOutcomeInputSchema>;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "6e0acdc7-db48-4ebf-846e-0589fc32afb5",
|
|
3
|
+
"prevId": "fb7f4132-d92f-4127-be54-23f93c79034b",
|
|
4
|
+
"version": "7",
|
|
5
|
+
"dialect": "postgresql",
|
|
6
|
+
"tables": {
|
|
7
|
+
"public.junior_github_issues": {
|
|
8
|
+
"name": "junior_github_issues",
|
|
9
|
+
"schema": "",
|
|
10
|
+
"columns": {
|
|
11
|
+
"issue_id": {
|
|
12
|
+
"name": "issue_id",
|
|
13
|
+
"type": "text",
|
|
14
|
+
"primaryKey": true,
|
|
15
|
+
"notNull": true
|
|
16
|
+
},
|
|
17
|
+
"repository_id": {
|
|
18
|
+
"name": "repository_id",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"primaryKey": false,
|
|
21
|
+
"notNull": true
|
|
22
|
+
},
|
|
23
|
+
"repository_full_name": {
|
|
24
|
+
"name": "repository_full_name",
|
|
25
|
+
"type": "text",
|
|
26
|
+
"primaryKey": false,
|
|
27
|
+
"notNull": true
|
|
28
|
+
},
|
|
29
|
+
"number": {
|
|
30
|
+
"name": "number",
|
|
31
|
+
"type": "integer",
|
|
32
|
+
"primaryKey": false,
|
|
33
|
+
"notNull": true
|
|
34
|
+
},
|
|
35
|
+
"state": {
|
|
36
|
+
"name": "state",
|
|
37
|
+
"type": "text",
|
|
38
|
+
"primaryKey": false,
|
|
39
|
+
"notNull": true
|
|
40
|
+
},
|
|
41
|
+
"state_reason": {
|
|
42
|
+
"name": "state_reason",
|
|
43
|
+
"type": "text",
|
|
44
|
+
"primaryKey": false,
|
|
45
|
+
"notNull": false
|
|
46
|
+
},
|
|
47
|
+
"opened_at": {
|
|
48
|
+
"name": "opened_at",
|
|
49
|
+
"type": "timestamp with time zone",
|
|
50
|
+
"primaryKey": false,
|
|
51
|
+
"notNull": true
|
|
52
|
+
},
|
|
53
|
+
"closed_at": {
|
|
54
|
+
"name": "closed_at",
|
|
55
|
+
"type": "timestamp with time zone",
|
|
56
|
+
"primaryKey": false,
|
|
57
|
+
"notNull": false
|
|
58
|
+
},
|
|
59
|
+
"updated_at": {
|
|
60
|
+
"name": "updated_at",
|
|
61
|
+
"type": "timestamp with time zone",
|
|
62
|
+
"primaryKey": false,
|
|
63
|
+
"notNull": true
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"indexes": {
|
|
67
|
+
"junior_github_issues_opened_at_idx": {
|
|
68
|
+
"name": "junior_github_issues_opened_at_idx",
|
|
69
|
+
"columns": [
|
|
70
|
+
{
|
|
71
|
+
"expression": "opened_at",
|
|
72
|
+
"isExpression": false,
|
|
73
|
+
"asc": true,
|
|
74
|
+
"nulls": "last"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"isUnique": false,
|
|
78
|
+
"concurrently": false,
|
|
79
|
+
"method": "btree",
|
|
80
|
+
"with": {}
|
|
81
|
+
},
|
|
82
|
+
"junior_github_issues_closed_at_idx": {
|
|
83
|
+
"name": "junior_github_issues_closed_at_idx",
|
|
84
|
+
"columns": [
|
|
85
|
+
{
|
|
86
|
+
"expression": "closed_at",
|
|
87
|
+
"isExpression": false,
|
|
88
|
+
"asc": true,
|
|
89
|
+
"nulls": "last"
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"isUnique": false,
|
|
93
|
+
"concurrently": false,
|
|
94
|
+
"method": "btree",
|
|
95
|
+
"with": {}
|
|
96
|
+
},
|
|
97
|
+
"junior_github_issues_open_idx": {
|
|
98
|
+
"name": "junior_github_issues_open_idx",
|
|
99
|
+
"columns": [
|
|
100
|
+
{
|
|
101
|
+
"expression": "issue_id",
|
|
102
|
+
"isExpression": false,
|
|
103
|
+
"asc": true,
|
|
104
|
+
"nulls": "last"
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"isUnique": false,
|
|
108
|
+
"where": "\"junior_github_issues\".\"state\" = 'open'",
|
|
109
|
+
"concurrently": false,
|
|
110
|
+
"method": "btree",
|
|
111
|
+
"with": {}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"foreignKeys": {},
|
|
115
|
+
"compositePrimaryKeys": {},
|
|
116
|
+
"uniqueConstraints": {},
|
|
117
|
+
"policies": {},
|
|
118
|
+
"checkConstraints": {},
|
|
119
|
+
"isRLSEnabled": false
|
|
120
|
+
},
|
|
121
|
+
"public.junior_github_pull_requests": {
|
|
122
|
+
"name": "junior_github_pull_requests",
|
|
123
|
+
"schema": "",
|
|
124
|
+
"columns": {
|
|
125
|
+
"pull_request_id": {
|
|
126
|
+
"name": "pull_request_id",
|
|
127
|
+
"type": "text",
|
|
128
|
+
"primaryKey": true,
|
|
129
|
+
"notNull": true
|
|
130
|
+
},
|
|
131
|
+
"repository_id": {
|
|
132
|
+
"name": "repository_id",
|
|
133
|
+
"type": "text",
|
|
134
|
+
"primaryKey": false,
|
|
135
|
+
"notNull": true
|
|
136
|
+
},
|
|
137
|
+
"repository_full_name": {
|
|
138
|
+
"name": "repository_full_name",
|
|
139
|
+
"type": "text",
|
|
140
|
+
"primaryKey": false,
|
|
141
|
+
"notNull": true
|
|
142
|
+
},
|
|
143
|
+
"number": {
|
|
144
|
+
"name": "number",
|
|
145
|
+
"type": "integer",
|
|
146
|
+
"primaryKey": false,
|
|
147
|
+
"notNull": true
|
|
148
|
+
},
|
|
149
|
+
"state": {
|
|
150
|
+
"name": "state",
|
|
151
|
+
"type": "text",
|
|
152
|
+
"primaryKey": false,
|
|
153
|
+
"notNull": true
|
|
154
|
+
},
|
|
155
|
+
"commit_composition": {
|
|
156
|
+
"name": "commit_composition",
|
|
157
|
+
"type": "text",
|
|
158
|
+
"primaryKey": false,
|
|
159
|
+
"notNull": false
|
|
160
|
+
},
|
|
161
|
+
"conversation_ids": {
|
|
162
|
+
"name": "conversation_ids",
|
|
163
|
+
"type": "text[]",
|
|
164
|
+
"primaryKey": false,
|
|
165
|
+
"notNull": true,
|
|
166
|
+
"default": "ARRAY[]::text[]"
|
|
167
|
+
},
|
|
168
|
+
"opened_at": {
|
|
169
|
+
"name": "opened_at",
|
|
170
|
+
"type": "timestamp with time zone",
|
|
171
|
+
"primaryKey": false,
|
|
172
|
+
"notNull": true
|
|
173
|
+
},
|
|
174
|
+
"merged_at": {
|
|
175
|
+
"name": "merged_at",
|
|
176
|
+
"type": "timestamp with time zone",
|
|
177
|
+
"primaryKey": false,
|
|
178
|
+
"notNull": false
|
|
179
|
+
},
|
|
180
|
+
"closed_at": {
|
|
181
|
+
"name": "closed_at",
|
|
182
|
+
"type": "timestamp with time zone",
|
|
183
|
+
"primaryKey": false,
|
|
184
|
+
"notNull": false
|
|
185
|
+
},
|
|
186
|
+
"updated_at": {
|
|
187
|
+
"name": "updated_at",
|
|
188
|
+
"type": "timestamp with time zone",
|
|
189
|
+
"primaryKey": false,
|
|
190
|
+
"notNull": true
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"indexes": {
|
|
194
|
+
"junior_github_pull_requests_opened_at_idx": {
|
|
195
|
+
"name": "junior_github_pull_requests_opened_at_idx",
|
|
196
|
+
"columns": [
|
|
197
|
+
{
|
|
198
|
+
"expression": "opened_at",
|
|
199
|
+
"isExpression": false,
|
|
200
|
+
"asc": true,
|
|
201
|
+
"nulls": "last"
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
"isUnique": false,
|
|
205
|
+
"concurrently": false,
|
|
206
|
+
"method": "btree",
|
|
207
|
+
"with": {}
|
|
208
|
+
},
|
|
209
|
+
"junior_github_pull_requests_merged_at_idx": {
|
|
210
|
+
"name": "junior_github_pull_requests_merged_at_idx",
|
|
211
|
+
"columns": [
|
|
212
|
+
{
|
|
213
|
+
"expression": "merged_at",
|
|
214
|
+
"isExpression": false,
|
|
215
|
+
"asc": true,
|
|
216
|
+
"nulls": "last"
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
"isUnique": false,
|
|
220
|
+
"concurrently": false,
|
|
221
|
+
"method": "btree",
|
|
222
|
+
"with": {}
|
|
223
|
+
},
|
|
224
|
+
"junior_github_pull_requests_closed_at_idx": {
|
|
225
|
+
"name": "junior_github_pull_requests_closed_at_idx",
|
|
226
|
+
"columns": [
|
|
227
|
+
{
|
|
228
|
+
"expression": "closed_at",
|
|
229
|
+
"isExpression": false,
|
|
230
|
+
"asc": true,
|
|
231
|
+
"nulls": "last"
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
"isUnique": false,
|
|
235
|
+
"concurrently": false,
|
|
236
|
+
"method": "btree",
|
|
237
|
+
"with": {}
|
|
238
|
+
},
|
|
239
|
+
"junior_github_pull_requests_open_idx": {
|
|
240
|
+
"name": "junior_github_pull_requests_open_idx",
|
|
241
|
+
"columns": [
|
|
242
|
+
{
|
|
243
|
+
"expression": "pull_request_id",
|
|
244
|
+
"isExpression": false,
|
|
245
|
+
"asc": true,
|
|
246
|
+
"nulls": "last"
|
|
247
|
+
}
|
|
248
|
+
],
|
|
249
|
+
"isUnique": false,
|
|
250
|
+
"where": "\"junior_github_pull_requests\".\"state\" = 'open'",
|
|
251
|
+
"concurrently": false,
|
|
252
|
+
"method": "btree",
|
|
253
|
+
"with": {}
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
"foreignKeys": {},
|
|
257
|
+
"compositePrimaryKeys": {},
|
|
258
|
+
"uniqueConstraints": {},
|
|
259
|
+
"policies": {},
|
|
260
|
+
"checkConstraints": {},
|
|
261
|
+
"isRLSEnabled": false
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
"enums": {},
|
|
265
|
+
"schemas": {},
|
|
266
|
+
"sequences": {},
|
|
267
|
+
"roles": {},
|
|
268
|
+
"policies": {},
|
|
269
|
+
"views": {},
|
|
270
|
+
"_meta": {
|
|
271
|
+
"columns": {},
|
|
272
|
+
"schemas": {},
|
|
273
|
+
"tables": {}
|
|
274
|
+
}
|
|
275
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.109.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@sinclair/typebox": "^0.34.49",
|
|
32
32
|
"drizzle-orm": "^0.45.2",
|
|
33
33
|
"zod": "^4.4.3",
|
|
34
|
-
"@sentry/junior-plugin-api": "0.
|
|
34
|
+
"@sentry/junior-plugin-api": "0.109.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.9.1",
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
- **Adopted:** preserve the upstream one-file-per-upload behavior, supported formats, markdown output, and upload endpoint.
|
|
15
15
|
- **Adopted:** preserve the upstream MIT license in the skill directory as explicitly required.
|
|
16
|
-
- **Replaced:** direct `gh auth token` access with Junior's host-managed
|
|
16
|
+
- **Replaced:** direct `gh auth token` access with Junior's host-managed requesting-user OAuth credential injection so credentials never enter script output or files.
|
|
17
17
|
- **Replaced:** numeric repository-id override with explicit `owner/repo`; the script resolves the id through authenticated `gh api`.
|
|
18
18
|
- **Narrowed:** automatic activation requires a concrete local path and GitHub destination.
|
|
19
|
-
- **
|
|
19
|
+
- **Verified:** a live upload with the repository-scoped installation credential returned HTTP 500; the endpoint's upstream implementation uses a user token.
|
|
20
20
|
|
|
21
21
|
## Coverage
|
|
22
22
|
|
|
@@ -23,7 +23,7 @@ Out of scope:
|
|
|
23
23
|
- Run `scripts/upload.sh` once per local file.
|
|
24
24
|
- Return image markdown or a bare video URL.
|
|
25
25
|
- Surface per-file failures exactly enough for the user to act.
|
|
26
|
-
- Use the GitHub plugin's
|
|
26
|
+
- Use the GitHub plugin's requesting-user OAuth credential injection; do not retrieve tokens in the script.
|
|
27
27
|
|
|
28
28
|
## Reference Architecture
|
|
29
29
|
|
|
@@ -40,7 +40,7 @@ Out of scope:
|
|
|
40
40
|
## Known Limitations
|
|
41
41
|
|
|
42
42
|
- GitHub's user-attachment endpoint is not part of the documented public REST API.
|
|
43
|
-
- A real upload requires
|
|
43
|
+
- A real upload requires the requesting user to authorize the GitHub App.
|
|
44
44
|
|
|
45
45
|
## Maintenance Notes
|
|
46
46
|
|
|
@@ -21,7 +21,8 @@ Use `git` and `gh` for repository work. Use `github_createPullRequest`, not `gh
|
|
|
21
21
|
- Read applicable `AGENTS.md` files before editing. Narrower repo/task instructions win.
|
|
22
22
|
- Preserve unrelated work. Never force-push, delete refs, or perform destructive merges.
|
|
23
23
|
- Base conclusions on repository evidence. Do not claim a check ran unless it did.
|
|
24
|
-
-
|
|
24
|
+
- For Junior-owned pull requests, push the branch before creating the PR. The runtime supplies repository-scoped GitHub App credentials for both; try the operations before requesting remediation and never ask for a user token.
|
|
25
|
+
- A tool-routing denial requires the named tool; only an upstream denial justifies permission remediation.
|
|
25
26
|
- Stop for ambiguous targets, missing access, destructive operations, or unresolved upstream permission failures.
|
|
26
27
|
|
|
27
28
|
## Workflow
|
|
@@ -36,9 +37,7 @@ For edits, choose the smallest credible validation path before changing files. C
|
|
|
36
37
|
|
|
37
38
|
### 2. Investigate
|
|
38
39
|
|
|
39
|
-
Establish where the behavior lives, current versus requested behavior, root cause or gap, and the smallest proof of correctness. Read linked issues, PRs, specs, and failing output when provided. If the request is investigation-only, report evidence without editing.
|
|
40
|
-
|
|
41
|
-
For non-trivial architecture, API, security, concurrency, migration, or broad cross-file work, use the available advisor after gathering evidence and before editing. Resolve material concerns in the plan.
|
|
40
|
+
Establish where the behavior lives, current versus requested behavior, root cause or gap, and the smallest proof of correctness. Read linked issues, PRs, specs, and failing output when provided. For pull requests, inspect conversation comments, inline review comments, reviews, the diff, and checks. If the request is investigation-only, report evidence without editing.
|
|
42
41
|
|
|
43
42
|
### 3. Edit
|
|
44
43
|
|
|
@@ -56,8 +55,6 @@ Do not install or repair the GitHub plugin runtime itself; that is manifest-owne
|
|
|
56
55
|
|
|
57
56
|
Run targeted changed-file/package checks before broad suites. Separate regressions from baseline failures. For instruction-only changes, run available structural checks and perform a content-consistency review.
|
|
58
57
|
|
|
59
|
-
For non-trivial work, review the final diff and initial results with the available advisor before packaging. Address material correctness, regression, and testing concerns, then rerun affected checks.
|
|
60
|
-
|
|
61
58
|
### 5. Package every completed edit
|
|
62
59
|
|
|
63
60
|
Unless the user explicitly says not to create a PR, every completed repository edit must end in a pushed branch and PR. Default to draft; honor an explicit user or repo instruction to open it ready for review. Do not stop at local changes or a commit.
|
|
@@ -77,11 +74,3 @@ If PR creation is blocked, report the exact failed command/tool call and leave t
|
|
|
77
74
|
When PR creation returns a subscribable resource hint, subscribe to suggested review/CI events. Report only actionable feedback addressed, build failures fixed, fully green/ready state, or merge.
|
|
78
75
|
|
|
79
76
|
Return: repo, branch, PR URL/number, checks and results, pre-existing failures, and anything not run with the reason.
|
|
80
|
-
|
|
81
|
-
## Operation notes
|
|
82
|
-
|
|
83
|
-
- **Clone/history:** clone shallowly by default; deepen before any operation that relies on omitted ancestry.
|
|
84
|
-
- **PR inspection:** read conversation comments, inline review comments, reviews, diff, and checks.
|
|
85
|
-
- **PR mutation:** push before create; use only supported endpoints in the API reference.
|
|
86
|
-
- **Workflow dispatch:** `gh workflow run` is supported only for workflows declaring `workflow_dispatch`.
|
|
87
|
-
- **Permissions:** a tool-routing denial requires the named tool; only an upstream denial justifies permission remediation.
|