@craftsamo/github-tools 0.6.0 → 0.7.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/.antigravity-plugin/plugin.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +6 -1
- package/dist/cli.js +315 -122
- package/dist/mcp.js +316 -123
- package/dist/opencode.js +315 -122
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/plugin.yaml +1 -1
- package/skills/github-projects/SKILL.md +26 -4
package/dist/opencode.js
CHANGED
|
@@ -20585,11 +20585,18 @@ function listOf(names) {
|
|
|
20585
20585
|
return names.length > 0 ? names.map((name) => `"${name}"`).join(", ") : "none";
|
|
20586
20586
|
}
|
|
20587
20587
|
function settableNames(fields) {
|
|
20588
|
-
return listOf(fields.filter((field) => isSettable(field.type)).map((field) => field.name));
|
|
20588
|
+
return listOf(fields.filter((field) => isSettable(field.type) && !field.issue_field).map((field) => field.name));
|
|
20589
20589
|
}
|
|
20590
|
-
function settableField(fields, name) {
|
|
20590
|
+
function settableField(fields, name, action = "set") {
|
|
20591
20591
|
const field = fields.find((candidate) => sameTitle(candidate.name, name));
|
|
20592
20592
|
if (!field) throw invalid3(`The board has no field "${name}". Fields that can be set: ${settableNames(fields)}.`);
|
|
20593
|
+
if (field.issue_field) {
|
|
20594
|
+
const verb = action === "clear" ? "cleared" : "set";
|
|
20595
|
+
const fix = action === "clear" ? "Clear it with clear_issue_fields in github_issue_edit." : "Set it with issue_fields in github_issue_edit or github_issue_create.";
|
|
20596
|
+
throw invalid3(
|
|
20597
|
+
`"${field.name}" mirrors the organization's issue field; its value is the issue's own and cannot be ${verb} on a board. ${fix} Fields that can be set: ${settableNames(fields)}.`
|
|
20598
|
+
);
|
|
20599
|
+
}
|
|
20593
20600
|
if (!isSettable(field.type)) {
|
|
20594
20601
|
const hint = ISSUE_FIELD_TYPES.includes(field.type) ? " github_issue_edit sets assignees, labels and milestone on the issue." : "";
|
|
20595
20602
|
throw invalid3(
|
|
@@ -20669,7 +20676,7 @@ function planValue(field, value, now) {
|
|
|
20669
20676
|
function planFieldValues(fields, values, now = /* @__PURE__ */ new Date()) {
|
|
20670
20677
|
const writes = [];
|
|
20671
20678
|
for (const [name, value] of Object.entries(values)) {
|
|
20672
|
-
const field = settableField(fields, name);
|
|
20679
|
+
const field = settableField(fields, name, "set");
|
|
20673
20680
|
if (writes.some((write) => write.field.id === field.id)) {
|
|
20674
20681
|
throw invalid3(`fields names "${field.name}" twice; field names are matched without regard to case.`);
|
|
20675
20682
|
}
|
|
@@ -20680,7 +20687,7 @@ function planFieldValues(fields, values, now = /* @__PURE__ */ new Date()) {
|
|
|
20680
20687
|
function planClears(fields, names) {
|
|
20681
20688
|
const cleared = [];
|
|
20682
20689
|
for (const name of names) {
|
|
20683
|
-
const field = settableField(fields, name);
|
|
20690
|
+
const field = settableField(fields, name, "clear");
|
|
20684
20691
|
if (!cleared.some((each) => each.id === field.id)) cleared.push(field);
|
|
20685
20692
|
}
|
|
20686
20693
|
return cleared;
|
|
@@ -20695,38 +20702,43 @@ function isHeld(write, current) {
|
|
|
20695
20702
|
|
|
20696
20703
|
// src/core/github/issue-fields.ts
|
|
20697
20704
|
var issueFieldValuesSchema = external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.array(external_exports.string())]));
|
|
20698
|
-
var
|
|
20705
|
+
var ISSUE_FIELD_VALUE_SELECTION = "__typename ... on IssueFieldTextValue{value} ... on IssueFieldNumberValue{value} ... on IssueFieldDateValue{value} ... on IssueFieldSingleSelectValue{name} ... on IssueFieldMultiSelectValue{options{name}}";
|
|
20706
|
+
var ISSUE_FIELD_VALUES_FRAGMENT = `issueFieldValues(first:25){nodes{... on IssueFieldValueCommon{field{... on IssueFieldCommon{name}}} ${ISSUE_FIELD_VALUE_SELECTION}}}`;
|
|
20707
|
+
function issueFieldValueOf(node2) {
|
|
20708
|
+
if (!node2) return void 0;
|
|
20709
|
+
switch (node2.__typename) {
|
|
20710
|
+
case "IssueFieldTextValue":
|
|
20711
|
+
return typeof node2.value === "string" ? node2.value : void 0;
|
|
20712
|
+
case "IssueFieldNumberValue":
|
|
20713
|
+
return typeof node2.value === "number" && !Number.isNaN(node2.value) ? node2.value : void 0;
|
|
20714
|
+
case "IssueFieldDateValue":
|
|
20715
|
+
return typeof node2.value === "string" ? node2.value : void 0;
|
|
20716
|
+
case "IssueFieldSingleSelectValue":
|
|
20717
|
+
return typeof node2.name === "string" ? node2.name : void 0;
|
|
20718
|
+
case "IssueFieldMultiSelectValue": {
|
|
20719
|
+
const options = (node2.options ?? []).map((opt) => opt?.name).filter((name) => typeof name === "string");
|
|
20720
|
+
return options.length > 0 ? options : void 0;
|
|
20721
|
+
}
|
|
20722
|
+
default:
|
|
20723
|
+
return void 0;
|
|
20724
|
+
}
|
|
20725
|
+
}
|
|
20699
20726
|
function issueFieldValues(connection) {
|
|
20700
20727
|
const values = {};
|
|
20701
20728
|
for (const node2 of connection?.nodes ?? []) {
|
|
20702
20729
|
if (!node2) continue;
|
|
20703
20730
|
const fieldName = node2.field?.name?.trim();
|
|
20704
20731
|
if (!fieldName) continue;
|
|
20705
|
-
|
|
20706
|
-
|
|
20707
|
-
if (typeof node2.value === "string") values[fieldName] = node2.value;
|
|
20708
|
-
break;
|
|
20709
|
-
case "IssueFieldNumberValue":
|
|
20710
|
-
if (typeof node2.value === "number" && !Number.isNaN(node2.value)) values[fieldName] = node2.value;
|
|
20711
|
-
break;
|
|
20712
|
-
case "IssueFieldDateValue":
|
|
20713
|
-
if (typeof node2.value === "string") values[fieldName] = node2.value;
|
|
20714
|
-
break;
|
|
20715
|
-
case "IssueFieldSingleSelectValue":
|
|
20716
|
-
if (typeof node2.name === "string") values[fieldName] = node2.name;
|
|
20717
|
-
break;
|
|
20718
|
-
case "IssueFieldMultiSelectValue": {
|
|
20719
|
-
const options = (node2.options ?? []).map((opt) => opt?.name).filter((name) => typeof name === "string");
|
|
20720
|
-
if (options.length > 0) values[fieldName] = options;
|
|
20721
|
-
break;
|
|
20722
|
-
}
|
|
20723
|
-
}
|
|
20732
|
+
const val = issueFieldValueOf(node2);
|
|
20733
|
+
if (val !== void 0) values[fieldName] = val;
|
|
20724
20734
|
}
|
|
20725
20735
|
return values;
|
|
20726
20736
|
}
|
|
20727
20737
|
var SCHEMA_ERROR_PATTERNS = [
|
|
20728
20738
|
/issueFieldValues.*(?:doesn['’]t exist on type|is not defined on type).*['"`]?Issue['"`]?/i,
|
|
20729
|
-
/issueFields.*(?:doesn['’]t exist on type|is not defined on type).*['"`]?Repository['"`]?/i
|
|
20739
|
+
/issueFields.*(?:doesn['’]t exist on type|is not defined on type).*['"`]?(?:Repository|Organization)['"`]?/i,
|
|
20740
|
+
/(?:isIssueField|issueField).*(?:doesn['’]t exist on type|is not defined on type).*['"`]?ProjectV2/i,
|
|
20741
|
+
/(?:No such type\s+['"`]?(?:ProjectV2ItemIssueFieldValue|ProjectV2IssueFieldValues)['"`]?|(?:ProjectV2ItemIssueFieldValue|ProjectV2IssueFieldValues).*(?:doesn['’]t exist|is not defined))/i
|
|
20730
20742
|
];
|
|
20731
20743
|
function matchesSchemaError(text) {
|
|
20732
20744
|
return SCHEMA_ERROR_PATTERNS.some((pattern) => pattern.test(text));
|
|
@@ -20743,7 +20755,9 @@ function isIssueFieldsMissingError(error62) {
|
|
|
20743
20755
|
var issueFieldInputSchema = issueFieldValuesSchema;
|
|
20744
20756
|
var ISSUE_FIELDS_ARGUMENT = `The organization's issue fields to set, by field name (without regard to case): {"Priority": "High", "Target date": "2026-12-01"}. Each takes a text, a number, a date as YYYY-MM-DD, an option name, or a list of option names for a multi-select field.`;
|
|
20745
20757
|
var MAX_ISSUE_FIELDS = 25;
|
|
20746
|
-
var
|
|
20758
|
+
var DEFINITION_SELECTION = "__typename ... on IssueFieldCommon{name dataType} ... on IssueFieldText{id} ... on IssueFieldNumber{id} ... on IssueFieldDate{id} ... on IssueFieldSingleSelect{id options{id name}} ... on IssueFieldMultiSelect{id options{id name}}";
|
|
20759
|
+
var DEFINITIONS_QUERY = `query($owner:String!,$name:String!){repository(owner:$owner,name:$name){issueFields(first:${MAX_ISSUE_FIELDS}){nodes{${DEFINITION_SELECTION}}}}}`;
|
|
20760
|
+
var ORGANIZATION_DEFINITIONS_QUERY = `query($login:String!){repositoryOwner(login:$login){__typename ... on Organization{issueFields(first:${MAX_ISSUE_FIELDS}){nodes{${DEFINITION_SELECTION}}}}}}`;
|
|
20747
20761
|
var VALUES_QUERY = `query($id:ID!){node(id:$id){... on Issue{${ISSUE_FIELD_VALUES_FRAGMENT}}}}`;
|
|
20748
20762
|
var issueFieldTypes = {
|
|
20749
20763
|
TEXT: "text",
|
|
@@ -20779,6 +20793,20 @@ async function readIssueFieldDefinitions(repo, ctx) {
|
|
|
20779
20793
|
}
|
|
20780
20794
|
return (data?.repository?.issueFields?.nodes ?? []).flatMap((raw) => definitionOf(raw) ?? []);
|
|
20781
20795
|
}
|
|
20796
|
+
async function readOrganizationIssueFields(login, ctx) {
|
|
20797
|
+
let data;
|
|
20798
|
+
try {
|
|
20799
|
+
data = await graphql(ORGANIZATION_DEFINITIONS_QUERY, { login }, ctx);
|
|
20800
|
+
} catch (error62) {
|
|
20801
|
+
if (isIssueFieldsMissingError(error62)) return null;
|
|
20802
|
+
throw error62;
|
|
20803
|
+
}
|
|
20804
|
+
if (data?.repositoryOwner?.__typename !== "Organization") return null;
|
|
20805
|
+
return (data.repositoryOwner.issueFields?.nodes ?? []).flatMap((raw) => definitionOf(raw) ?? []);
|
|
20806
|
+
}
|
|
20807
|
+
function findIssueField(definitions, name) {
|
|
20808
|
+
return definitions.find((candidate) => sameTitle(candidate.name, name));
|
|
20809
|
+
}
|
|
20782
20810
|
function invalid4(message) {
|
|
20783
20811
|
return new ToolError("invalid_argument", message);
|
|
20784
20812
|
}
|
|
@@ -20786,7 +20814,7 @@ function fieldNames(definitions) {
|
|
|
20786
20814
|
return listOf(definitions.map((field) => field.name));
|
|
20787
20815
|
}
|
|
20788
20816
|
function fieldOf(repo, definitions, name) {
|
|
20789
|
-
const field = definitions
|
|
20817
|
+
const field = findIssueField(definitions, name);
|
|
20790
20818
|
if (!field) throw invalid4(`${repo.full} has no issue field "${name}". Fields: ${fieldNames(definitions)}.`);
|
|
20791
20819
|
return field;
|
|
20792
20820
|
}
|
|
@@ -21651,12 +21679,18 @@ var issueComment = defineTool({
|
|
|
21651
21679
|
// src/core/github/board-fields.ts
|
|
21652
21680
|
var VIEW_LAYOUT_SUFFIX = /_LAYOUT$/;
|
|
21653
21681
|
var FIELD_SELECTION = "__typename ... on ProjectV2FieldCommon{id name dataType} ... on ProjectV2SingleSelectField{options{id name color description}} ... on ProjectV2IterationField{configuration{iterations{id title startDate duration}}}";
|
|
21654
|
-
var
|
|
21682
|
+
var ISSUE_FIELD_SELECTION = "isIssueField issueField{__typename ... on IssueFieldSingleSelect{options{id name color description}}}";
|
|
21683
|
+
var READ_FIELD_SELECTION = `${FIELD_SELECTION} ... on ProjectV2Field{${ISSUE_FIELD_SELECTION}} ... on ProjectV2SingleSelectField{${ISSUE_FIELD_SELECTION}}`;
|
|
21684
|
+
var FIELDS_QUERY2 = `query($id:ID!,$after:String){node(id:$id){... on ProjectV2{fields(first:${PAGE_SIZE},after:$after){${PAGE_INFO} nodes{${READ_FIELD_SELECTION}}}}}}`;
|
|
21685
|
+
var FALLBACK_FIELDS_QUERY = `query($id:ID!,$after:String){node(id:$id){... on ProjectV2{fields(first:${PAGE_SIZE},after:$after){${PAGE_INFO} nodes{${FIELD_SELECTION}}}}}}`;
|
|
21655
21686
|
var VIEWS_QUERY = `query($id:ID!,$after:String){node(id:$id){... on ProjectV2{views(first:${PAGE_SIZE},after:$after){${PAGE_INFO} nodes{id number name layout filter}}}}}`;
|
|
21656
21687
|
var boardFieldSchema = external_exports.object({
|
|
21657
21688
|
id: external_exports.string(),
|
|
21658
21689
|
name: external_exports.string(),
|
|
21659
21690
|
type: external_exports.string().describe("GitHub's data type in lower case: text, number, date, single_select, iteration, title, assignees, \u2026"),
|
|
21691
|
+
issue_field: external_exports.boolean().describe(
|
|
21692
|
+
"Whether the column mirrors one of the organization's issue fields: its value is the issue's own, set with the issue tools, not on the board."
|
|
21693
|
+
),
|
|
21660
21694
|
options: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), color: external_exports.string(), description: external_exports.string() })).optional().describe("single_select: the options, in order."),
|
|
21661
21695
|
iterations: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string(), start_date: external_exports.string(), duration: external_exports.number().int() })).optional().describe("iteration: the iterations; duration is in days.")
|
|
21662
21696
|
});
|
|
@@ -21667,9 +21701,16 @@ var boardViewSchema = external_exports.object({
|
|
|
21667
21701
|
filter: external_exports.string().nullable()
|
|
21668
21702
|
});
|
|
21669
21703
|
function toField(raw) {
|
|
21670
|
-
const
|
|
21671
|
-
|
|
21672
|
-
|
|
21704
|
+
const isIssueField = Boolean(raw.isIssueField);
|
|
21705
|
+
const field = {
|
|
21706
|
+
id: raw.id,
|
|
21707
|
+
name: raw.name ?? "",
|
|
21708
|
+
type: (raw.dataType ?? "").toLowerCase(),
|
|
21709
|
+
issue_field: isIssueField
|
|
21710
|
+
};
|
|
21711
|
+
const rawOptions = isIssueField ? raw.issueField?.options ?? (field.type === "single_select" ? [] : void 0) : raw.options;
|
|
21712
|
+
if (rawOptions) {
|
|
21713
|
+
field.options = rawOptions.map((option) => ({
|
|
21673
21714
|
id: option.id,
|
|
21674
21715
|
name: option.name,
|
|
21675
21716
|
color: option.color ?? "",
|
|
@@ -21687,11 +21728,21 @@ function toField(raw) {
|
|
|
21687
21728
|
return field;
|
|
21688
21729
|
}
|
|
21689
21730
|
async function readBoardFields(projectId, ctx) {
|
|
21690
|
-
const
|
|
21691
|
-
|
|
21692
|
-
|
|
21693
|
-
|
|
21694
|
-
|
|
21731
|
+
const fetchFields = async (query) => {
|
|
21732
|
+
const { items } = await paginate(query, { id: projectId }, (data) => data?.node?.fields, {
|
|
21733
|
+
cwd: ctx.cwd,
|
|
21734
|
+
signal: ctx.signal
|
|
21735
|
+
});
|
|
21736
|
+
return items.filter((raw) => Boolean(raw?.id)).map(toField);
|
|
21737
|
+
};
|
|
21738
|
+
try {
|
|
21739
|
+
return await fetchFields(FIELDS_QUERY2);
|
|
21740
|
+
} catch (error62) {
|
|
21741
|
+
if (isIssueFieldsMissingError(error62)) {
|
|
21742
|
+
return await fetchFields(FALLBACK_FIELDS_QUERY);
|
|
21743
|
+
}
|
|
21744
|
+
throw error62;
|
|
21745
|
+
}
|
|
21695
21746
|
}
|
|
21696
21747
|
async function readBoardViewNodes(projectId, ctx) {
|
|
21697
21748
|
const { items } = await paginate(VIEWS_QUERY, { id: projectId }, (data) => data?.node?.views, {
|
|
@@ -21921,6 +21972,7 @@ var STATUS_FIELD = "Status";
|
|
|
21921
21972
|
var FIELD_REPLY = `projectV2Field{${FIELD_SELECTION}}`;
|
|
21922
21973
|
var createFieldMutation = (type, options) => `mutation($project:ID!,$name:String!){createProjectV2Field(input:{projectId:$project,dataType:${type.toUpperCase()},name:$name${options}}){${FIELD_REPLY}}}`;
|
|
21923
21974
|
var updateOptionsMutation = (options) => `mutation($field:ID!){updateProjectV2Field(input:{fieldId:$field,singleSelectOptions:${options}}){${FIELD_REPLY}}}`;
|
|
21975
|
+
var createIssueFieldColumnMutation = `mutation($project:ID!,$field:ID!){createProjectV2IssueField(input:{projectId:$project,issueFieldId:$field}){projectV2Field{${READ_FIELD_SELECTION}}}}`;
|
|
21924
21976
|
function invalid7(message) {
|
|
21925
21977
|
return new ToolError("invalid_argument", message);
|
|
21926
21978
|
}
|
|
@@ -21933,6 +21985,29 @@ function fieldRequest(name, type, options) {
|
|
|
21933
21985
|
}
|
|
21934
21986
|
return { name: trimmed, type, options: parsed };
|
|
21935
21987
|
}
|
|
21988
|
+
function issueFieldColumnRequest(name, type) {
|
|
21989
|
+
const trimmed = name.trim();
|
|
21990
|
+
if (!trimmed) throw invalid7("name cannot be blank.");
|
|
21991
|
+
return { name: trimmed, type };
|
|
21992
|
+
}
|
|
21993
|
+
async function readOwnerIssueFields(owner, ctx) {
|
|
21994
|
+
return owner.owner_type === "organization" ? readOrganizationIssueFields(owner.owner, ctx) : null;
|
|
21995
|
+
}
|
|
21996
|
+
function requireIssueField(owner, definitions, request2) {
|
|
21997
|
+
if (owner.owner_type !== "organization") {
|
|
21998
|
+
throw invalid7(`Issue fields belong to an organization; ${owner.owner} is a user.`);
|
|
21999
|
+
}
|
|
22000
|
+
if (!definitions) throw invalid7("This GitHub has no issue fields.");
|
|
22001
|
+
const definition = findIssueField(definitions, request2.name);
|
|
22002
|
+
if (!definition) {
|
|
22003
|
+
const names = listOf(definitions.map((field) => field.name));
|
|
22004
|
+
throw invalid7(`${owner.owner} has no issue field "${request2.name}". Issue fields: ${names}.`);
|
|
22005
|
+
}
|
|
22006
|
+
if (request2.type && request2.type !== definition.type) {
|
|
22007
|
+
throw invalid7(`"${definition.name}" is ${definition.type}, not ${request2.type}.`);
|
|
22008
|
+
}
|
|
22009
|
+
return definition;
|
|
22010
|
+
}
|
|
21936
22011
|
function optionsLiteral(options) {
|
|
21937
22012
|
const items = options.map((option) => {
|
|
21938
22013
|
if (!OPTION_COLORS.some((color) => color === option.color)) {
|
|
@@ -21977,6 +22052,11 @@ async function ensureField(board, fields, request2, policy, ctx) {
|
|
|
21977
22052
|
if (request2.type !== "single_select" || request2.options.length === 0) return unchanged;
|
|
21978
22053
|
const merge2 = (policy === "replace" ? replaceOptions : appendOptions)(existing.options ?? [], request2.options);
|
|
21979
22054
|
if (!merge2.changed) return unchanged;
|
|
22055
|
+
if (existing.issue_field) {
|
|
22056
|
+
throw invalid7(
|
|
22057
|
+
`"${existing.name}" mirrors the organization's issue field; its options are the organization's and cannot be changed on a board.`
|
|
22058
|
+
);
|
|
22059
|
+
}
|
|
21980
22060
|
const data = await graphql(
|
|
21981
22061
|
updateOptionsMutation(optionsLiteral(merge2.options)),
|
|
21982
22062
|
{ field: existing.id },
|
|
@@ -21985,6 +22065,23 @@ async function ensureField(board, fields, request2, policy, ctx) {
|
|
|
21985
22065
|
const field = fieldOf2(data?.updateProjectV2Field ?? null, existing.name);
|
|
21986
22066
|
return { field, created: false, added_options: merge2.added, updated_options: merge2.updated };
|
|
21987
22067
|
}
|
|
22068
|
+
async function ensureIssueFieldColumn(board, fields, definitions, request2, ctx) {
|
|
22069
|
+
const definition = requireIssueField(board, definitions, request2);
|
|
22070
|
+
const existing = fields.find((field2) => sameTitle(field2.name, definition.name));
|
|
22071
|
+
if (existing?.issue_field) return { field: existing, created: false, added_options: [], updated_options: [] };
|
|
22072
|
+
if (existing) {
|
|
22073
|
+
throw invalid7(
|
|
22074
|
+
`The board has its own field "${existing.name}"; an issue field column cannot take the same name. Rename or delete the board's field in the web UI, or use the board's field.`
|
|
22075
|
+
);
|
|
22076
|
+
}
|
|
22077
|
+
const data = await graphql(
|
|
22078
|
+
createIssueFieldColumnMutation,
|
|
22079
|
+
{ project: board.id, field: definition.id },
|
|
22080
|
+
ctx
|
|
22081
|
+
);
|
|
22082
|
+
const field = fieldOf2(data?.createProjectV2IssueField ?? null, definition.name);
|
|
22083
|
+
return { field, created: true, added_options: [], updated_options: [] };
|
|
22084
|
+
}
|
|
21988
22085
|
|
|
21989
22086
|
// src/core/projects/support.ts
|
|
21990
22087
|
var BOARD_SELECTION = {
|
|
@@ -22008,60 +22105,65 @@ function boardSummary(board) {
|
|
|
22008
22105
|
// src/core/projects/template.ts
|
|
22009
22106
|
var STATUS_TODO = "Todo";
|
|
22010
22107
|
var STATUS_WAITING = "Waiting";
|
|
22011
|
-
var
|
|
22012
|
-
var
|
|
22013
|
-
var
|
|
22108
|
+
var DATE_MODES = ["board", "issue_fields"];
|
|
22109
|
+
var BOARD_DATE_FIELDS = { start: "Start", target: "Target" };
|
|
22110
|
+
var ISSUE_DATE_FIELDS = { start: "Start date", target: "Target date" };
|
|
22014
22111
|
var DEFAULT_VIEW_NAME = "View 1";
|
|
22015
|
-
|
|
22016
|
-
|
|
22017
|
-
|
|
22018
|
-
|
|
22019
|
-
|
|
22020
|
-
|
|
22021
|
-
|
|
22022
|
-
|
|
22112
|
+
function roadmapNote(dates) {
|
|
22113
|
+
return `Set the Roadmap view's date fields to ${dates.start} and ${dates.target} in the web UI; the API cannot set them.`;
|
|
22114
|
+
}
|
|
22115
|
+
var ROADMAP_NOTE = roadmapNote(BOARD_DATE_FIELDS);
|
|
22116
|
+
var STATUS_REQUEST = {
|
|
22117
|
+
name: STATUS_FIELD,
|
|
22118
|
+
type: "single_select",
|
|
22119
|
+
options: [
|
|
22120
|
+
{ name: STATUS_TODO, color: "BLUE", description: "Ready to start; nothing is in the way." },
|
|
22121
|
+
{ name: "In Progress", color: "YELLOW", description: "Being worked on now." },
|
|
22122
|
+
{
|
|
22123
|
+
name: STATUS_WAITING,
|
|
22124
|
+
color: "GRAY",
|
|
22125
|
+
description: "Cannot move until something else clears: an open blocking issue, a review or a reply."
|
|
22126
|
+
},
|
|
22127
|
+
{ name: "Done", color: "PURPLE", description: "Finished." },
|
|
22128
|
+
{ name: "Cancelled", color: "RED", description: "Will not be done; kept for the record." }
|
|
22129
|
+
]
|
|
22130
|
+
};
|
|
22131
|
+
function standardTemplate(dates) {
|
|
22132
|
+
const notDone = "-status:Done -status:Cancelled";
|
|
22133
|
+
return {
|
|
22134
|
+
roadmapNote: roadmapNote(dates),
|
|
22135
|
+
views: [
|
|
22136
|
+
{ name: "Roadmap", layout: "roadmap", filter: "-status:Cancelled", sort_by: dates.start },
|
|
22023
22137
|
{
|
|
22024
|
-
name:
|
|
22025
|
-
|
|
22026
|
-
|
|
22138
|
+
name: "Kanban",
|
|
22139
|
+
layout: "board",
|
|
22140
|
+
filter: notDone,
|
|
22141
|
+
column_by: STATUS_FIELD,
|
|
22142
|
+
fields: ["Title", "Assignees", "Repository", "Parent issue", dates.target],
|
|
22143
|
+
sort_by: dates.target
|
|
22027
22144
|
},
|
|
22028
|
-
{
|
|
22029
|
-
|
|
22145
|
+
{
|
|
22146
|
+
name: "Backlog",
|
|
22147
|
+
layout: "table",
|
|
22148
|
+
filter: notDone,
|
|
22149
|
+
fields: [
|
|
22150
|
+
"Title",
|
|
22151
|
+
STATUS_FIELD,
|
|
22152
|
+
"Assignees",
|
|
22153
|
+
"Repository",
|
|
22154
|
+
"Milestone",
|
|
22155
|
+
"Parent issue",
|
|
22156
|
+
"Sub-issues progress",
|
|
22157
|
+
dates.start,
|
|
22158
|
+
dates.target,
|
|
22159
|
+
"Linked pull requests"
|
|
22160
|
+
],
|
|
22161
|
+
group_by: STATUS_FIELD,
|
|
22162
|
+
sort_by: dates.target
|
|
22163
|
+
}
|
|
22030
22164
|
]
|
|
22031
|
-
}
|
|
22032
|
-
|
|
22033
|
-
{ name: TARGET_FIELD, type: "date", options: [] }
|
|
22034
|
-
];
|
|
22035
|
-
var STANDARD_VIEWS = [
|
|
22036
|
-
{ name: "Roadmap", layout: "roadmap", filter: "-status:Cancelled", sort_by: START_FIELD },
|
|
22037
|
-
{
|
|
22038
|
-
name: "Kanban",
|
|
22039
|
-
layout: "board",
|
|
22040
|
-
filter: NOT_DONE,
|
|
22041
|
-
column_by: STATUS_FIELD,
|
|
22042
|
-
fields: ["Title", "Assignees", "Repository", "Parent issue", TARGET_FIELD],
|
|
22043
|
-
sort_by: TARGET_FIELD
|
|
22044
|
-
},
|
|
22045
|
-
{
|
|
22046
|
-
name: "Backlog",
|
|
22047
|
-
layout: "table",
|
|
22048
|
-
filter: NOT_DONE,
|
|
22049
|
-
fields: [
|
|
22050
|
-
"Title",
|
|
22051
|
-
STATUS_FIELD,
|
|
22052
|
-
"Assignees",
|
|
22053
|
-
"Repository",
|
|
22054
|
-
"Milestone",
|
|
22055
|
-
"Parent issue",
|
|
22056
|
-
"Sub-issues progress",
|
|
22057
|
-
START_FIELD,
|
|
22058
|
-
TARGET_FIELD,
|
|
22059
|
-
"Linked pull requests"
|
|
22060
|
-
],
|
|
22061
|
-
group_by: STATUS_FIELD,
|
|
22062
|
-
sort_by: TARGET_FIELD
|
|
22063
|
-
}
|
|
22064
|
-
];
|
|
22165
|
+
};
|
|
22166
|
+
}
|
|
22065
22167
|
|
|
22066
22168
|
// src/core/projects/item-support.ts
|
|
22067
22169
|
var ITEM_BOARD_SELECTION = {
|
|
@@ -22074,10 +22176,10 @@ var itemArgument = external_exports.string().describe(
|
|
|
22074
22176
|
"The board item: its node ID (PVTI_\u2026), or an issue or pull request as 12, #12, owner/repo#12 or its URL, found on the board that project, owner and repo choose. A draft can only be named by its ID."
|
|
22075
22177
|
);
|
|
22076
22178
|
var ITEM_NOTE = "An item is named by its node ID (PVTI_\u2026), which also tells its board (a project given beside it must be that board), or by an issue or pull request (12, #12, owner/repo#12 or its URL) that is on the board the lookup finds. A draft can only be named by its ID.";
|
|
22077
|
-
var FIELDS_NOTE = "Field values are set by field name and option or iteration name, matched without regard to case: text as a string, number as a number or numeric string, date as YYYY-MM-DD, single select as an option name, iteration as an iteration title or @current (the iteration containing today, UTC). Only text, number, date, single select and iteration fields can be set; GitHub's own (Title, Assignees, Labels, Milestone, Repository, Linked pull requests, \u2026) are set on the issue, and github_issue_edit sets assignees, labels and milestone. Every field and value is checked before anything is written.";
|
|
22179
|
+
var FIELDS_NOTE = "Field values are set by field name and option or iteration name, matched without regard to case: text as a string, number as a number or numeric string, date as YYYY-MM-DD, single select as an option name, iteration as an iteration title or @current (the iteration containing today, UTC). Only text, number, date, single select and iteration fields can be set; a column that mirrors an issue field is set with the issue tools' issue_fields, not here. GitHub's own (Title, Assignees, Labels, Milestone, Repository, Linked pull requests, \u2026) are set on the issue, and github_issue_edit sets assignees, labels and milestone. Every field and value is checked before anything is written.";
|
|
22078
22180
|
var fieldValuesSchema = external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number()]));
|
|
22079
22181
|
var fieldValuesArgument = fieldValuesSchema.optional().describe(
|
|
22080
|
-
|
|
22182
|
+
`Field values to set, by field name: {"Status": "Todo", "Target": "2026-12-01"}. Values are text, a number, a YYYY-MM-DD date, an option name or an iteration title (@current is the one containing today). A column that mirrors an issue field is set with the issue tools' issue_fields, not here.`
|
|
22081
22183
|
);
|
|
22082
22184
|
var fieldsResultSchema = fieldValuesSchema.describe(
|
|
22083
22185
|
"The values that were set, by field name as the board spells it: an option or iteration by its name, a number as a number."
|
|
@@ -23296,6 +23398,9 @@ var input10 = external_exports.object({
|
|
|
23296
23398
|
"A board to copy, without its draft items, when the board has to be made: its number, owner/number or URL (a bare number or a title is looked up among owner's boards)."
|
|
23297
23399
|
),
|
|
23298
23400
|
standard: external_exports.boolean().optional().describe("Bring the board to the standard template: its fields and views (default true)."),
|
|
23401
|
+
dates: external_exports.enum(DATE_MODES).optional().describe(
|
|
23402
|
+
`"board" (default) makes the board's own date fields Start and Target; "issue_fields" uses the organization's issue fields Start date and Target date instead, so an issue carries the same dates on every board and they are set with the issue tools (a pull request or draft has none). Organization boards only.`
|
|
23403
|
+
),
|
|
23299
23404
|
link_repo: external_exports.string().optional().describe("owner/name of a repository to link the board to; needs write permission.")
|
|
23300
23405
|
});
|
|
23301
23406
|
var output10 = external_exports.object({
|
|
@@ -23327,23 +23432,41 @@ function openBoardTitled(boards, title) {
|
|
|
23327
23432
|
}
|
|
23328
23433
|
return matches[0];
|
|
23329
23434
|
}
|
|
23330
|
-
async function applyFields(board, fresh, report, ctx) {
|
|
23435
|
+
async function applyFields(board, fresh, mode, dates, definitions, report, ctx) {
|
|
23331
23436
|
const fields = await readBoardFields(board.id, ctx);
|
|
23332
|
-
|
|
23333
|
-
|
|
23334
|
-
|
|
23335
|
-
|
|
23336
|
-
|
|
23337
|
-
|
|
23338
|
-
|
|
23339
|
-
|
|
23340
|
-
|
|
23437
|
+
const status = await ensureField(board, fields, STATUS_REQUEST, fresh ? "replace" : "append", ctx);
|
|
23438
|
+
report.push({
|
|
23439
|
+
field: status.field.name,
|
|
23440
|
+
created: status.created,
|
|
23441
|
+
added_options: status.added_options,
|
|
23442
|
+
updated_options: status.updated_options
|
|
23443
|
+
});
|
|
23444
|
+
if (mode === "issue_fields") {
|
|
23445
|
+
for (const name of [dates.start, dates.target]) {
|
|
23446
|
+
const outcome = await ensureIssueFieldColumn(board, fields, definitions, { name, type: "date" }, ctx);
|
|
23447
|
+
report.push({
|
|
23448
|
+
field: outcome.field.name,
|
|
23449
|
+
created: outcome.created,
|
|
23450
|
+
added_options: outcome.added_options,
|
|
23451
|
+
updated_options: outcome.updated_options
|
|
23452
|
+
});
|
|
23453
|
+
}
|
|
23454
|
+
} else {
|
|
23455
|
+
for (const name of [dates.start, dates.target]) {
|
|
23456
|
+
const outcome = await ensureField(board, fields, { name, type: "date", options: [] }, "append", ctx);
|
|
23457
|
+
report.push({
|
|
23458
|
+
field: outcome.field.name,
|
|
23459
|
+
created: outcome.created,
|
|
23460
|
+
added_options: outcome.added_options,
|
|
23461
|
+
updated_options: outcome.updated_options
|
|
23462
|
+
});
|
|
23463
|
+
}
|
|
23341
23464
|
}
|
|
23342
23465
|
}
|
|
23343
|
-
async function applyViews(board, report, notes, ctx) {
|
|
23466
|
+
async function applyViews(board, templateViews, roadmapNote2, report, notes, ctx) {
|
|
23344
23467
|
const before = await readBoardViewNodes(board.id, ctx);
|
|
23345
23468
|
const readers = viewFieldReaders(board, ctx);
|
|
23346
|
-
for (const request2 of
|
|
23469
|
+
for (const request2 of templateViews) {
|
|
23347
23470
|
if (before.some((view5) => sameTitle(view5.name, request2.name))) {
|
|
23348
23471
|
report.push({ view: request2.name, created: false });
|
|
23349
23472
|
continue;
|
|
@@ -23355,7 +23478,7 @@ async function applyViews(board, report, notes, ctx) {
|
|
|
23355
23478
|
throw error62;
|
|
23356
23479
|
}
|
|
23357
23480
|
report.push({ view: request2.name, created: true });
|
|
23358
|
-
if (request2.layout === "roadmap") notes.push(
|
|
23481
|
+
if (request2.layout === "roadmap") notes.push(roadmapNote2);
|
|
23359
23482
|
}
|
|
23360
23483
|
return before;
|
|
23361
23484
|
}
|
|
@@ -23365,6 +23488,7 @@ var projectCreate = defineTool({
|
|
|
23365
23488
|
description: [
|
|
23366
23489
|
`Create a project board for a user or organization, or reuse the open board of that owner and exact title (case-insensitive; several are invalid_argument), and bring it to the standard template. The title defaults to ${DEFAULT_BOARD_TITLE}. A board that has to be made is empty, or a copy of template without its draft items.`,
|
|
23367
23490
|
"With standard (the default): the fields Status (Todo, In Progress, Waiting, Done, Cancelled, each with a color and a description), Start and Target (dates), and the views Roadmap, Kanban and Backlog. On a board this call made, Status gets exactly those options, keeping the ids of GitHub's own Todo, In Progress and Done, and GitHub's default view View 1 is deleted once the three views exist. On a reused or copied board only the missing fields, options and views are added, and nothing is deleted. A reused board must be one the viewer may update.",
|
|
23491
|
+
`dates: "issue_fields" uses the organization's issue fields Start date and Target date as the board's dates, refused before the board is made when the owner is a user or lacks them.`,
|
|
23368
23492
|
"link_repo links the board to a repository (needs write permission there) unless it is already linked. GitHub links only boards of the repository's own owner, so a repository of another owner is invalid_argument, before the board is made.",
|
|
23369
23493
|
"Once the board exists, a failed step does not fail the call: it is listed in errors and the board is still returned.",
|
|
23370
23494
|
`notes lists what the API cannot do, such as setting the Roadmap view's date fields. ${PROJECT_SCOPE_NOTE}`
|
|
@@ -23390,6 +23514,23 @@ var projectCreate = defineTool({
|
|
|
23390
23514
|
linkRepo = await writableContext(repo, ctx);
|
|
23391
23515
|
}
|
|
23392
23516
|
const owner = await readBoardOwner(login, ctx);
|
|
23517
|
+
const dateMode = args.dates ?? "board";
|
|
23518
|
+
let dateNames = BOARD_DATE_FIELDS;
|
|
23519
|
+
let definitions = null;
|
|
23520
|
+
if (dateMode === "issue_fields") {
|
|
23521
|
+
if (!standard) throw invalid5("dates applies only with standard.");
|
|
23522
|
+
definitions = await readOwnerIssueFields({ owner: owner.login, owner_type: owner.type }, ctx);
|
|
23523
|
+
const startDef = requireIssueField({ owner: owner.login, owner_type: owner.type }, definitions, {
|
|
23524
|
+
name: ISSUE_DATE_FIELDS.start,
|
|
23525
|
+
type: "date"
|
|
23526
|
+
});
|
|
23527
|
+
const targetDef = requireIssueField({ owner: owner.login, owner_type: owner.type }, definitions, {
|
|
23528
|
+
name: ISSUE_DATE_FIELDS.target,
|
|
23529
|
+
type: "date"
|
|
23530
|
+
});
|
|
23531
|
+
dateNames = { start: startDef.name, target: targetDef.name };
|
|
23532
|
+
}
|
|
23533
|
+
const template = standardTemplate(dateNames);
|
|
23393
23534
|
const reused = openBoardTitled((await ownerBoards(owner.login, ctx, owner.type)).boards, title);
|
|
23394
23535
|
if (reused) assertBoardWritable(reused);
|
|
23395
23536
|
const source = reused || !templateRef ? void 0 : (await resolveBoard({ project: templateRef, owner: ownerArg }, ctx)).project;
|
|
@@ -23402,8 +23543,11 @@ var projectCreate = defineTool({
|
|
|
23402
23543
|
const notes = [];
|
|
23403
23544
|
let deletedDefaultView = false;
|
|
23404
23545
|
if (standard) {
|
|
23405
|
-
await steps.record("fields", () => applyFields(board, fresh, fields, ctx));
|
|
23406
|
-
const before = await steps.record(
|
|
23546
|
+
await steps.record("fields", () => applyFields(board, fresh, dateMode, dateNames, definitions, fields, ctx));
|
|
23547
|
+
const before = await steps.record(
|
|
23548
|
+
"views",
|
|
23549
|
+
() => applyViews(board, template.views, template.roadmapNote, views, notes, ctx)
|
|
23550
|
+
);
|
|
23407
23551
|
const defaultView = fresh ? before?.find((view5) => view5.name === DEFAULT_VIEW_NAME) : void 0;
|
|
23408
23552
|
if (defaultView) {
|
|
23409
23553
|
deletedDefaultView = await steps.record("default_view", async () => {
|
|
@@ -23434,9 +23578,14 @@ var projectCreate = defineTool({
|
|
|
23434
23578
|
var input11 = external_exports.object({
|
|
23435
23579
|
...BOARD_SELECTION,
|
|
23436
23580
|
name: external_exports.string().describe("The field's name, matched without regard to case."),
|
|
23437
|
-
type: external_exports.enum(FIELD_TYPES).describe(
|
|
23581
|
+
type: external_exports.enum(FIELD_TYPES).optional().describe(
|
|
23582
|
+
"The field's type; a field of that name with another type is invalid_argument. Required unless issue_field is true, where it only checks the issue field's type."
|
|
23583
|
+
),
|
|
23438
23584
|
options: external_exports.array(external_exports.string()).optional().describe(
|
|
23439
|
-
`single_select only: Name, Name:COLOR or Name:COLOR:Description (the description is everything after the second colon). COLOR is one of ${OPTION_COLORS.join(", ")}, in any case; the default is GRAY.`
|
|
23585
|
+
`single_select only, not with issue_field: Name, Name:COLOR or Name:COLOR:Description (the description is everything after the second colon). COLOR is one of ${OPTION_COLORS.join(", ")}, in any case; the default is GRAY.`
|
|
23586
|
+
),
|
|
23587
|
+
issue_field: external_exports.boolean().optional().describe(
|
|
23588
|
+
"Add a column that mirrors the organization's issue field of that name instead of a field of the board's own; its options and values are the organization's and the issue's."
|
|
23440
23589
|
)
|
|
23441
23590
|
});
|
|
23442
23591
|
var output11 = external_exports.object({
|
|
@@ -23445,12 +23594,25 @@ var output11 = external_exports.object({
|
|
|
23445
23594
|
created: external_exports.boolean().describe("Whether this call made the field."),
|
|
23446
23595
|
added_options: external_exports.array(external_exports.string()).describe("The names of the options this call added; [] when none.")
|
|
23447
23596
|
});
|
|
23597
|
+
function planEnsure(args) {
|
|
23598
|
+
if (args.issue_field) {
|
|
23599
|
+
if (args.options && args.options.length > 0) {
|
|
23600
|
+
throw new ToolError("invalid_argument", "options are the organization's with issue_field; leave them out.");
|
|
23601
|
+
}
|
|
23602
|
+
const request3 = issueFieldColumnRequest(args.name, args.type);
|
|
23603
|
+
return async (board, fields, ctx) => ensureIssueFieldColumn(board, fields, await readOwnerIssueFields(board, ctx), request3, ctx);
|
|
23604
|
+
}
|
|
23605
|
+
if (!args.type) throw new ToolError("invalid_argument", "type is required unless issue_field is true.");
|
|
23606
|
+
const request2 = fieldRequest(args.name, args.type, args.options);
|
|
23607
|
+
return (board, fields, ctx) => ensureField(board, fields, request2, "append", ctx);
|
|
23608
|
+
}
|
|
23448
23609
|
var projectFieldEnsure = defineTool({
|
|
23449
23610
|
id: "github_project_field_ensure",
|
|
23450
23611
|
title: "Ensure a GitHub project field",
|
|
23451
23612
|
description: [
|
|
23452
23613
|
"Make sure a field exists on a project board the viewer may update, without ever making a second one: a missing field is created, and the missing options of an existing single select are appended after its own, which keep their id, name, color, description and order, so nothing already set moves.",
|
|
23453
23614
|
"A new single_select field needs options. Status can be extended but not created. A field of that name with another type, options on a field that is not single_select, a repeated option name and a color GitHub does not list are invalid_argument, before anything is written.",
|
|
23615
|
+
"With issue_field: true (organization boards only) the column mirrors the organization's issue field of that name, such as Priority, Start date or Target date; it is added when the board lacks it, and a field of the board's own with that name is invalid_argument.",
|
|
23454
23616
|
BOARD_LOOKUP_NOTE,
|
|
23455
23617
|
PROJECT_SCOPE_NOTE
|
|
23456
23618
|
].join(" "),
|
|
@@ -23460,11 +23622,11 @@ var projectFieldEnsure = defineTool({
|
|
|
23460
23622
|
input: input11,
|
|
23461
23623
|
output: output11,
|
|
23462
23624
|
async execute(args, ctx) {
|
|
23463
|
-
const
|
|
23625
|
+
const ensure = planEnsure(args);
|
|
23464
23626
|
const { project } = await resolveBoard(args, ctx);
|
|
23465
23627
|
assertBoardWritable(project);
|
|
23466
23628
|
const fields = await readBoardFields(project.id, ctx);
|
|
23467
|
-
const { field, created, added_options } = await
|
|
23629
|
+
const { field, created, added_options } = await ensure(project, fields, ctx);
|
|
23468
23630
|
return { project: boardSummary(project), field, created, added_options };
|
|
23469
23631
|
}
|
|
23470
23632
|
});
|
|
@@ -23544,7 +23706,10 @@ var FIELD_LABELS = 20;
|
|
|
23544
23706
|
var FIELD_USERS = 10;
|
|
23545
23707
|
var FIELD_PULL_REQUESTS = 10;
|
|
23546
23708
|
var FIELD_NAME = "field{... on ProjectV2FieldCommon{name}}";
|
|
23547
|
-
|
|
23709
|
+
function itemFields(includeIssueFields = true) {
|
|
23710
|
+
const issueFieldSelection = includeIssueFields ? `
|
|
23711
|
+
... on ProjectV2ItemIssueFieldValue{${FIELD_NAME} issueFieldValue{${ISSUE_FIELD_VALUE_SELECTION}}}` : "";
|
|
23712
|
+
return `id isArchived
|
|
23548
23713
|
fieldValues(first:${FIELD_VALUES}){nodes{
|
|
23549
23714
|
... on ProjectV2ItemFieldTextValue{text ${FIELD_NAME}}
|
|
23550
23715
|
... on ProjectV2ItemFieldNumberValue{number ${FIELD_NAME}}
|
|
@@ -23555,15 +23720,22 @@ fieldValues(first:${FIELD_VALUES}){nodes{
|
|
|
23555
23720
|
... on ProjectV2ItemFieldRepositoryValue{repository{nameWithOwner} ${FIELD_NAME}}
|
|
23556
23721
|
... on ProjectV2ItemFieldLabelValue{labels(first:${FIELD_LABELS}){nodes{name}} ${FIELD_NAME}}
|
|
23557
23722
|
... on ProjectV2ItemFieldUserValue{users(first:${FIELD_USERS}){nodes{login}} ${FIELD_NAME}}
|
|
23558
|
-
... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:${FIELD_PULL_REQUESTS}){nodes{number}} ${FIELD_NAME}}
|
|
23723
|
+
... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:${FIELD_PULL_REQUESTS}){nodes{number}} ${FIELD_NAME}}${issueFieldSelection}
|
|
23559
23724
|
}}
|
|
23560
23725
|
content{__typename
|
|
23561
23726
|
... on DraftIssue{id title body}
|
|
23562
23727
|
... on Issue{number title url issueState: state repository{nameWithOwner} issueType{name} labels(first:${CONTENT_LABELS}){nodes{name}} assignees(first:${CONTENT_ASSIGNEES}){nodes{login}} parent{number repository{nameWithOwner}} subIssuesSummary{total completed}}
|
|
23563
23728
|
... on PullRequest{number title url pullRequestState: state repository{nameWithOwner} labels(first:${CONTENT_LABELS}){nodes{name}} assignees(first:${CONTENT_ASSIGNEES}){nodes{login}}}
|
|
23564
23729
|
}`;
|
|
23565
|
-
|
|
23566
|
-
var
|
|
23730
|
+
}
|
|
23731
|
+
var ITEM_FIELDS = itemFields(true);
|
|
23732
|
+
var ITEM_FIELDS_WITHOUT_ISSUE_FIELDS = itemFields(false);
|
|
23733
|
+
var itemsQuery = (fields) => `query($id:ID!,$after:String){node(id:$id){... on ProjectV2{items(first:${PAGE_SIZE2},after:$after){pageInfo{hasNextPage endCursor} nodes{${fields}}}}}}`;
|
|
23734
|
+
var itemQuery = (fields) => `query($id:ID!){node(id:$id){... on ProjectV2Item{${fields}}}}`;
|
|
23735
|
+
var ITEMS_QUERY = itemsQuery(ITEM_FIELDS);
|
|
23736
|
+
var ITEMS_QUERY_WITHOUT_ISSUE_FIELDS = itemsQuery(ITEM_FIELDS_WITHOUT_ISSUE_FIELDS);
|
|
23737
|
+
var ITEM_QUERY = itemQuery(ITEM_FIELDS);
|
|
23738
|
+
var ITEM_QUERY_WITHOUT_ISSUE_FIELDS = itemQuery(ITEM_FIELDS_WITHOUT_ISSUE_FIELDS);
|
|
23567
23739
|
var iterationValueSchema = external_exports.object({
|
|
23568
23740
|
title: external_exports.string(),
|
|
23569
23741
|
start_date: external_exports.string(),
|
|
@@ -23594,7 +23766,7 @@ var boardItemSchema = external_exports.object({
|
|
|
23594
23766
|
item_id: external_exports.string(),
|
|
23595
23767
|
archived: external_exports.boolean(),
|
|
23596
23768
|
fields: external_exports.record(external_exports.string(), itemFieldValueSchema).describe(
|
|
23597
|
-
"Values by field name: text, number, date and single select as they are (the option's name), iteration as { title, start_date, duration }, milestone as its title, repository as owner/name, labels as names, users as logins, linked pull requests as numbers."
|
|
23769
|
+
"Values by field name: text, number, date and single select as they are (the option's name), a column mirroring an organization issue field as the issue's value (an option's name, or a list of names for a multi-select), iteration as { title, start_date, duration }, milestone as its title, repository as owner/name, labels as names, users as logins, linked pull requests as numbers."
|
|
23598
23770
|
),
|
|
23599
23771
|
content: itemContentSchema.nullable().describe("What the item stands for; null when GitHub did not return it.")
|
|
23600
23772
|
});
|
|
@@ -23602,6 +23774,7 @@ function nodesOf2(connection) {
|
|
|
23602
23774
|
return (connection?.nodes ?? []).filter((node2) => node2 != null);
|
|
23603
23775
|
}
|
|
23604
23776
|
function fieldValue(raw) {
|
|
23777
|
+
if ("issueFieldValue" in raw) return issueFieldValueOf(raw.issueFieldValue);
|
|
23605
23778
|
if ("text" in raw) return raw.text ?? null;
|
|
23606
23779
|
if ("number" in raw) return raw.number ?? null;
|
|
23607
23780
|
if ("date" in raw) return raw.date ?? null;
|
|
@@ -23665,16 +23838,34 @@ function toItem(raw) {
|
|
|
23665
23838
|
};
|
|
23666
23839
|
}
|
|
23667
23840
|
async function readBoardItems(projectId, ctx) {
|
|
23668
|
-
const
|
|
23669
|
-
|
|
23670
|
-
|
|
23671
|
-
|
|
23672
|
-
|
|
23673
|
-
|
|
23674
|
-
|
|
23841
|
+
const run = (query) => paginate(query, { id: projectId }, (data) => data?.node?.items, {
|
|
23842
|
+
cwd: ctx.cwd,
|
|
23843
|
+
signal: ctx.signal,
|
|
23844
|
+
limit: MAX_BOARD_ITEMS
|
|
23845
|
+
});
|
|
23846
|
+
let result;
|
|
23847
|
+
try {
|
|
23848
|
+
result = await run(ITEMS_QUERY);
|
|
23849
|
+
} catch (error62) {
|
|
23850
|
+
if (isIssueFieldsMissingError(error62)) {
|
|
23851
|
+
result = await run(ITEMS_QUERY_WITHOUT_ISSUE_FIELDS);
|
|
23852
|
+
} else {
|
|
23853
|
+
throw error62;
|
|
23854
|
+
}
|
|
23855
|
+
}
|
|
23856
|
+
return { items: result.items.filter((raw) => raw != null).map(toItem), truncated: result.truncated };
|
|
23675
23857
|
}
|
|
23676
23858
|
async function readBoardItem(itemId, ctx) {
|
|
23677
|
-
|
|
23859
|
+
let data;
|
|
23860
|
+
try {
|
|
23861
|
+
data = await graphql(ITEM_QUERY, { id: itemId }, ctx);
|
|
23862
|
+
} catch (error62) {
|
|
23863
|
+
if (isIssueFieldsMissingError(error62)) {
|
|
23864
|
+
data = await graphql(ITEM_QUERY_WITHOUT_ISSUE_FIELDS, { id: itemId }, ctx);
|
|
23865
|
+
} else {
|
|
23866
|
+
throw error62;
|
|
23867
|
+
}
|
|
23868
|
+
}
|
|
23678
23869
|
const raw = data?.node;
|
|
23679
23870
|
if (!raw?.id) return null;
|
|
23680
23871
|
return { item: toItem(raw), draftId: raw.content?.__typename === "DraftIssue" ? raw.content.id ?? null : null };
|
|
@@ -23899,7 +24090,9 @@ var input16 = external_exports.object({
|
|
|
23899
24090
|
item: itemArgument,
|
|
23900
24091
|
...ITEM_BOARD_SELECTION,
|
|
23901
24092
|
fields: fieldValuesArgument,
|
|
23902
|
-
clear_fields: external_exports.array(external_exports.string()).optional().describe(
|
|
24093
|
+
clear_fields: external_exports.array(external_exports.string()).optional().describe(
|
|
24094
|
+
"Names of the fields to empty. A column that mirrors an issue field is cleared with clear_issue_fields in github_issue_edit, not here."
|
|
24095
|
+
),
|
|
23903
24096
|
title: external_exports.string().optional().describe("Drafts only: the new title."),
|
|
23904
24097
|
body: external_exports.string().optional().describe("Drafts only: the new body, which replaces the whole old one; empty clears it."),
|
|
23905
24098
|
append_body: external_exports.string().optional().describe("Drafts only: text added after the current body, set off by a blank line.")
|
|
@@ -24207,7 +24400,7 @@ var projectRead = defineTool({
|
|
|
24207
24400
|
id: "github_project_read",
|
|
24208
24401
|
title: "Read GitHub project boards",
|
|
24209
24402
|
description: [
|
|
24210
|
-
'Read GitHub project boards (Projects v2). method "view": the board the lookup finds, its fields (with single-select options and iterations) and its views, with the route that found it and what every route considered.',
|
|
24403
|
+
'Read GitHub project boards (Projects v2). method "view": the board the lookup finds, its fields (with single-select options and iterations; a field mirroring an issue field is marked issue_field) and its views, with the route that found it and what every route considered.',
|
|
24211
24404
|
`method "list": an owner's boards (owner, a login or @me) or the boards linked to a repository (repo, default the working tree's), open ones unless include_closed; truncated says more matched than limit (default ${DEFAULT_LIST_LIMIT}).`,
|
|
24212
24405
|
`method "item_list": the board's items, filtered here by status (names, comma-separated, "-Name" leaves one out), content_type (issue, pr, draft), content_repo, search in the title and include_archived, with field values flattened by field name; truncated says the board holds more than ${MAX_BOARD_ITEMS} items or more matched than limit (default ${DEFAULT_ITEM_LIMIT}).`,
|
|
24213
24406
|
"A board is named by project: its number, owner/number or its URL, used as it is (closed too); a bare number takes owner, else the owner of repo or of the working tree's repository.",
|