@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/cli.js
CHANGED
|
@@ -20586,11 +20586,18 @@ function listOf(names) {
|
|
|
20586
20586
|
return names.length > 0 ? names.map((name) => `"${name}"`).join(", ") : "none";
|
|
20587
20587
|
}
|
|
20588
20588
|
function settableNames(fields) {
|
|
20589
|
-
return listOf(fields.filter((field) => isSettable(field.type)).map((field) => field.name));
|
|
20589
|
+
return listOf(fields.filter((field) => isSettable(field.type) && !field.issue_field).map((field) => field.name));
|
|
20590
20590
|
}
|
|
20591
|
-
function settableField(fields, name) {
|
|
20591
|
+
function settableField(fields, name, action = "set") {
|
|
20592
20592
|
const field = fields.find((candidate) => sameTitle(candidate.name, name));
|
|
20593
20593
|
if (!field) throw invalid3(`The board has no field "${name}". Fields that can be set: ${settableNames(fields)}.`);
|
|
20594
|
+
if (field.issue_field) {
|
|
20595
|
+
const verb = action === "clear" ? "cleared" : "set";
|
|
20596
|
+
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.";
|
|
20597
|
+
throw invalid3(
|
|
20598
|
+
`"${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)}.`
|
|
20599
|
+
);
|
|
20600
|
+
}
|
|
20594
20601
|
if (!isSettable(field.type)) {
|
|
20595
20602
|
const hint = ISSUE_FIELD_TYPES.includes(field.type) ? " github_issue_edit sets assignees, labels and milestone on the issue." : "";
|
|
20596
20603
|
throw invalid3(
|
|
@@ -20670,7 +20677,7 @@ function planValue(field, value, now) {
|
|
|
20670
20677
|
function planFieldValues(fields, values, now = /* @__PURE__ */ new Date()) {
|
|
20671
20678
|
const writes = [];
|
|
20672
20679
|
for (const [name, value] of Object.entries(values)) {
|
|
20673
|
-
const field = settableField(fields, name);
|
|
20680
|
+
const field = settableField(fields, name, "set");
|
|
20674
20681
|
if (writes.some((write) => write.field.id === field.id)) {
|
|
20675
20682
|
throw invalid3(`fields names "${field.name}" twice; field names are matched without regard to case.`);
|
|
20676
20683
|
}
|
|
@@ -20681,7 +20688,7 @@ function planFieldValues(fields, values, now = /* @__PURE__ */ new Date()) {
|
|
|
20681
20688
|
function planClears(fields, names) {
|
|
20682
20689
|
const cleared = [];
|
|
20683
20690
|
for (const name of names) {
|
|
20684
|
-
const field = settableField(fields, name);
|
|
20691
|
+
const field = settableField(fields, name, "clear");
|
|
20685
20692
|
if (!cleared.some((each) => each.id === field.id)) cleared.push(field);
|
|
20686
20693
|
}
|
|
20687
20694
|
return cleared;
|
|
@@ -20696,38 +20703,43 @@ function isHeld(write, current) {
|
|
|
20696
20703
|
|
|
20697
20704
|
// src/core/github/issue-fields.ts
|
|
20698
20705
|
var issueFieldValuesSchema = external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.array(external_exports.string())]));
|
|
20699
|
-
var
|
|
20706
|
+
var ISSUE_FIELD_VALUE_SELECTION = "__typename ... on IssueFieldTextValue{value} ... on IssueFieldNumberValue{value} ... on IssueFieldDateValue{value} ... on IssueFieldSingleSelectValue{name} ... on IssueFieldMultiSelectValue{options{name}}";
|
|
20707
|
+
var ISSUE_FIELD_VALUES_FRAGMENT = `issueFieldValues(first:25){nodes{... on IssueFieldValueCommon{field{... on IssueFieldCommon{name}}} ${ISSUE_FIELD_VALUE_SELECTION}}}`;
|
|
20708
|
+
function issueFieldValueOf(node2) {
|
|
20709
|
+
if (!node2) return void 0;
|
|
20710
|
+
switch (node2.__typename) {
|
|
20711
|
+
case "IssueFieldTextValue":
|
|
20712
|
+
return typeof node2.value === "string" ? node2.value : void 0;
|
|
20713
|
+
case "IssueFieldNumberValue":
|
|
20714
|
+
return typeof node2.value === "number" && !Number.isNaN(node2.value) ? node2.value : void 0;
|
|
20715
|
+
case "IssueFieldDateValue":
|
|
20716
|
+
return typeof node2.value === "string" ? node2.value : void 0;
|
|
20717
|
+
case "IssueFieldSingleSelectValue":
|
|
20718
|
+
return typeof node2.name === "string" ? node2.name : void 0;
|
|
20719
|
+
case "IssueFieldMultiSelectValue": {
|
|
20720
|
+
const options = (node2.options ?? []).map((opt) => opt?.name).filter((name) => typeof name === "string");
|
|
20721
|
+
return options.length > 0 ? options : void 0;
|
|
20722
|
+
}
|
|
20723
|
+
default:
|
|
20724
|
+
return void 0;
|
|
20725
|
+
}
|
|
20726
|
+
}
|
|
20700
20727
|
function issueFieldValues(connection) {
|
|
20701
20728
|
const values = {};
|
|
20702
20729
|
for (const node2 of connection?.nodes ?? []) {
|
|
20703
20730
|
if (!node2) continue;
|
|
20704
20731
|
const fieldName = node2.field?.name?.trim();
|
|
20705
20732
|
if (!fieldName) continue;
|
|
20706
|
-
|
|
20707
|
-
|
|
20708
|
-
if (typeof node2.value === "string") values[fieldName] = node2.value;
|
|
20709
|
-
break;
|
|
20710
|
-
case "IssueFieldNumberValue":
|
|
20711
|
-
if (typeof node2.value === "number" && !Number.isNaN(node2.value)) values[fieldName] = node2.value;
|
|
20712
|
-
break;
|
|
20713
|
-
case "IssueFieldDateValue":
|
|
20714
|
-
if (typeof node2.value === "string") values[fieldName] = node2.value;
|
|
20715
|
-
break;
|
|
20716
|
-
case "IssueFieldSingleSelectValue":
|
|
20717
|
-
if (typeof node2.name === "string") values[fieldName] = node2.name;
|
|
20718
|
-
break;
|
|
20719
|
-
case "IssueFieldMultiSelectValue": {
|
|
20720
|
-
const options = (node2.options ?? []).map((opt) => opt?.name).filter((name) => typeof name === "string");
|
|
20721
|
-
if (options.length > 0) values[fieldName] = options;
|
|
20722
|
-
break;
|
|
20723
|
-
}
|
|
20724
|
-
}
|
|
20733
|
+
const val = issueFieldValueOf(node2);
|
|
20734
|
+
if (val !== void 0) values[fieldName] = val;
|
|
20725
20735
|
}
|
|
20726
20736
|
return values;
|
|
20727
20737
|
}
|
|
20728
20738
|
var SCHEMA_ERROR_PATTERNS = [
|
|
20729
20739
|
/issueFieldValues.*(?:doesn['’]t exist on type|is not defined on type).*['"`]?Issue['"`]?/i,
|
|
20730
|
-
/issueFields.*(?:doesn['’]t exist on type|is not defined on type).*['"`]?Repository['"`]?/i
|
|
20740
|
+
/issueFields.*(?:doesn['’]t exist on type|is not defined on type).*['"`]?(?:Repository|Organization)['"`]?/i,
|
|
20741
|
+
/(?:isIssueField|issueField).*(?:doesn['’]t exist on type|is not defined on type).*['"`]?ProjectV2/i,
|
|
20742
|
+
/(?:No such type\s+['"`]?(?:ProjectV2ItemIssueFieldValue|ProjectV2IssueFieldValues)['"`]?|(?:ProjectV2ItemIssueFieldValue|ProjectV2IssueFieldValues).*(?:doesn['’]t exist|is not defined))/i
|
|
20731
20743
|
];
|
|
20732
20744
|
function matchesSchemaError(text) {
|
|
20733
20745
|
return SCHEMA_ERROR_PATTERNS.some((pattern) => pattern.test(text));
|
|
@@ -20744,7 +20756,9 @@ function isIssueFieldsMissingError(error62) {
|
|
|
20744
20756
|
var issueFieldInputSchema = issueFieldValuesSchema;
|
|
20745
20757
|
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.`;
|
|
20746
20758
|
var MAX_ISSUE_FIELDS = 25;
|
|
20747
|
-
var
|
|
20759
|
+
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}}";
|
|
20760
|
+
var DEFINITIONS_QUERY = `query($owner:String!,$name:String!){repository(owner:$owner,name:$name){issueFields(first:${MAX_ISSUE_FIELDS}){nodes{${DEFINITION_SELECTION}}}}}`;
|
|
20761
|
+
var ORGANIZATION_DEFINITIONS_QUERY = `query($login:String!){repositoryOwner(login:$login){__typename ... on Organization{issueFields(first:${MAX_ISSUE_FIELDS}){nodes{${DEFINITION_SELECTION}}}}}}`;
|
|
20748
20762
|
var VALUES_QUERY = `query($id:ID!){node(id:$id){... on Issue{${ISSUE_FIELD_VALUES_FRAGMENT}}}}`;
|
|
20749
20763
|
var issueFieldTypes = {
|
|
20750
20764
|
TEXT: "text",
|
|
@@ -20780,6 +20794,20 @@ async function readIssueFieldDefinitions(repo, ctx) {
|
|
|
20780
20794
|
}
|
|
20781
20795
|
return (data?.repository?.issueFields?.nodes ?? []).flatMap((raw) => definitionOf(raw) ?? []);
|
|
20782
20796
|
}
|
|
20797
|
+
async function readOrganizationIssueFields(login, ctx) {
|
|
20798
|
+
let data;
|
|
20799
|
+
try {
|
|
20800
|
+
data = await graphql(ORGANIZATION_DEFINITIONS_QUERY, { login }, ctx);
|
|
20801
|
+
} catch (error62) {
|
|
20802
|
+
if (isIssueFieldsMissingError(error62)) return null;
|
|
20803
|
+
throw error62;
|
|
20804
|
+
}
|
|
20805
|
+
if (data?.repositoryOwner?.__typename !== "Organization") return null;
|
|
20806
|
+
return (data.repositoryOwner.issueFields?.nodes ?? []).flatMap((raw) => definitionOf(raw) ?? []);
|
|
20807
|
+
}
|
|
20808
|
+
function findIssueField(definitions, name) {
|
|
20809
|
+
return definitions.find((candidate) => sameTitle(candidate.name, name));
|
|
20810
|
+
}
|
|
20783
20811
|
function invalid4(message) {
|
|
20784
20812
|
return new ToolError("invalid_argument", message);
|
|
20785
20813
|
}
|
|
@@ -20787,7 +20815,7 @@ function fieldNames(definitions) {
|
|
|
20787
20815
|
return listOf(definitions.map((field) => field.name));
|
|
20788
20816
|
}
|
|
20789
20817
|
function fieldOf(repo, definitions, name) {
|
|
20790
|
-
const field = definitions
|
|
20818
|
+
const field = findIssueField(definitions, name);
|
|
20791
20819
|
if (!field) throw invalid4(`${repo.full} has no issue field "${name}". Fields: ${fieldNames(definitions)}.`);
|
|
20792
20820
|
return field;
|
|
20793
20821
|
}
|
|
@@ -21665,12 +21693,18 @@ var issueComment = defineTool({
|
|
|
21665
21693
|
// src/core/github/board-fields.ts
|
|
21666
21694
|
var VIEW_LAYOUT_SUFFIX = /_LAYOUT$/;
|
|
21667
21695
|
var FIELD_SELECTION = "__typename ... on ProjectV2FieldCommon{id name dataType} ... on ProjectV2SingleSelectField{options{id name color description}} ... on ProjectV2IterationField{configuration{iterations{id title startDate duration}}}";
|
|
21668
|
-
var
|
|
21696
|
+
var ISSUE_FIELD_SELECTION = "isIssueField issueField{__typename ... on IssueFieldSingleSelect{options{id name color description}}}";
|
|
21697
|
+
var READ_FIELD_SELECTION = `${FIELD_SELECTION} ... on ProjectV2Field{${ISSUE_FIELD_SELECTION}} ... on ProjectV2SingleSelectField{${ISSUE_FIELD_SELECTION}}`;
|
|
21698
|
+
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}}}}}}`;
|
|
21699
|
+
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}}}}}}`;
|
|
21669
21700
|
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}}}}}`;
|
|
21670
21701
|
var boardFieldSchema = external_exports.object({
|
|
21671
21702
|
id: external_exports.string(),
|
|
21672
21703
|
name: external_exports.string(),
|
|
21673
21704
|
type: external_exports.string().describe("GitHub's data type in lower case: text, number, date, single_select, iteration, title, assignees, \u2026"),
|
|
21705
|
+
issue_field: external_exports.boolean().describe(
|
|
21706
|
+
"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."
|
|
21707
|
+
),
|
|
21674
21708
|
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."),
|
|
21675
21709
|
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.")
|
|
21676
21710
|
});
|
|
@@ -21681,9 +21715,16 @@ var boardViewSchema = external_exports.object({
|
|
|
21681
21715
|
filter: external_exports.string().nullable()
|
|
21682
21716
|
});
|
|
21683
21717
|
function toField(raw) {
|
|
21684
|
-
const
|
|
21685
|
-
|
|
21686
|
-
|
|
21718
|
+
const isIssueField = Boolean(raw.isIssueField);
|
|
21719
|
+
const field = {
|
|
21720
|
+
id: raw.id,
|
|
21721
|
+
name: raw.name ?? "",
|
|
21722
|
+
type: (raw.dataType ?? "").toLowerCase(),
|
|
21723
|
+
issue_field: isIssueField
|
|
21724
|
+
};
|
|
21725
|
+
const rawOptions = isIssueField ? raw.issueField?.options ?? (field.type === "single_select" ? [] : void 0) : raw.options;
|
|
21726
|
+
if (rawOptions) {
|
|
21727
|
+
field.options = rawOptions.map((option) => ({
|
|
21687
21728
|
id: option.id,
|
|
21688
21729
|
name: option.name,
|
|
21689
21730
|
color: option.color ?? "",
|
|
@@ -21701,11 +21742,21 @@ function toField(raw) {
|
|
|
21701
21742
|
return field;
|
|
21702
21743
|
}
|
|
21703
21744
|
async function readBoardFields(projectId, ctx) {
|
|
21704
|
-
const
|
|
21705
|
-
|
|
21706
|
-
|
|
21707
|
-
|
|
21708
|
-
|
|
21745
|
+
const fetchFields = async (query) => {
|
|
21746
|
+
const { items } = await paginate(query, { id: projectId }, (data) => data?.node?.fields, {
|
|
21747
|
+
cwd: ctx.cwd,
|
|
21748
|
+
signal: ctx.signal
|
|
21749
|
+
});
|
|
21750
|
+
return items.filter((raw) => Boolean(raw?.id)).map(toField);
|
|
21751
|
+
};
|
|
21752
|
+
try {
|
|
21753
|
+
return await fetchFields(FIELDS_QUERY2);
|
|
21754
|
+
} catch (error62) {
|
|
21755
|
+
if (isIssueFieldsMissingError(error62)) {
|
|
21756
|
+
return await fetchFields(FALLBACK_FIELDS_QUERY);
|
|
21757
|
+
}
|
|
21758
|
+
throw error62;
|
|
21759
|
+
}
|
|
21709
21760
|
}
|
|
21710
21761
|
async function readBoardViewNodes(projectId, ctx) {
|
|
21711
21762
|
const { items } = await paginate(VIEWS_QUERY, { id: projectId }, (data) => data?.node?.views, {
|
|
@@ -21935,6 +21986,7 @@ var STATUS_FIELD = "Status";
|
|
|
21935
21986
|
var FIELD_REPLY = `projectV2Field{${FIELD_SELECTION}}`;
|
|
21936
21987
|
var createFieldMutation = (type, options) => `mutation($project:ID!,$name:String!){createProjectV2Field(input:{projectId:$project,dataType:${type.toUpperCase()},name:$name${options}}){${FIELD_REPLY}}}`;
|
|
21937
21988
|
var updateOptionsMutation = (options) => `mutation($field:ID!){updateProjectV2Field(input:{fieldId:$field,singleSelectOptions:${options}}){${FIELD_REPLY}}}`;
|
|
21989
|
+
var createIssueFieldColumnMutation = `mutation($project:ID!,$field:ID!){createProjectV2IssueField(input:{projectId:$project,issueFieldId:$field}){projectV2Field{${READ_FIELD_SELECTION}}}}`;
|
|
21938
21990
|
function invalid7(message) {
|
|
21939
21991
|
return new ToolError("invalid_argument", message);
|
|
21940
21992
|
}
|
|
@@ -21947,6 +21999,29 @@ function fieldRequest(name, type, options) {
|
|
|
21947
21999
|
}
|
|
21948
22000
|
return { name: trimmed, type, options: parsed };
|
|
21949
22001
|
}
|
|
22002
|
+
function issueFieldColumnRequest(name, type) {
|
|
22003
|
+
const trimmed = name.trim();
|
|
22004
|
+
if (!trimmed) throw invalid7("name cannot be blank.");
|
|
22005
|
+
return { name: trimmed, type };
|
|
22006
|
+
}
|
|
22007
|
+
async function readOwnerIssueFields(owner, ctx) {
|
|
22008
|
+
return owner.owner_type === "organization" ? readOrganizationIssueFields(owner.owner, ctx) : null;
|
|
22009
|
+
}
|
|
22010
|
+
function requireIssueField(owner, definitions, request2) {
|
|
22011
|
+
if (owner.owner_type !== "organization") {
|
|
22012
|
+
throw invalid7(`Issue fields belong to an organization; ${owner.owner} is a user.`);
|
|
22013
|
+
}
|
|
22014
|
+
if (!definitions) throw invalid7("This GitHub has no issue fields.");
|
|
22015
|
+
const definition = findIssueField(definitions, request2.name);
|
|
22016
|
+
if (!definition) {
|
|
22017
|
+
const names = listOf(definitions.map((field) => field.name));
|
|
22018
|
+
throw invalid7(`${owner.owner} has no issue field "${request2.name}". Issue fields: ${names}.`);
|
|
22019
|
+
}
|
|
22020
|
+
if (request2.type && request2.type !== definition.type) {
|
|
22021
|
+
throw invalid7(`"${definition.name}" is ${definition.type}, not ${request2.type}.`);
|
|
22022
|
+
}
|
|
22023
|
+
return definition;
|
|
22024
|
+
}
|
|
21950
22025
|
function optionsLiteral(options) {
|
|
21951
22026
|
const items = options.map((option) => {
|
|
21952
22027
|
if (!OPTION_COLORS.some((color) => color === option.color)) {
|
|
@@ -21991,6 +22066,11 @@ async function ensureField(board, fields, request2, policy, ctx) {
|
|
|
21991
22066
|
if (request2.type !== "single_select" || request2.options.length === 0) return unchanged;
|
|
21992
22067
|
const merge2 = (policy === "replace" ? replaceOptions : appendOptions)(existing.options ?? [], request2.options);
|
|
21993
22068
|
if (!merge2.changed) return unchanged;
|
|
22069
|
+
if (existing.issue_field) {
|
|
22070
|
+
throw invalid7(
|
|
22071
|
+
`"${existing.name}" mirrors the organization's issue field; its options are the organization's and cannot be changed on a board.`
|
|
22072
|
+
);
|
|
22073
|
+
}
|
|
21994
22074
|
const data = await graphql(
|
|
21995
22075
|
updateOptionsMutation(optionsLiteral(merge2.options)),
|
|
21996
22076
|
{ field: existing.id },
|
|
@@ -21999,6 +22079,23 @@ async function ensureField(board, fields, request2, policy, ctx) {
|
|
|
21999
22079
|
const field = fieldOf2(data?.updateProjectV2Field ?? null, existing.name);
|
|
22000
22080
|
return { field, created: false, added_options: merge2.added, updated_options: merge2.updated };
|
|
22001
22081
|
}
|
|
22082
|
+
async function ensureIssueFieldColumn(board, fields, definitions, request2, ctx) {
|
|
22083
|
+
const definition = requireIssueField(board, definitions, request2);
|
|
22084
|
+
const existing = fields.find((field2) => sameTitle(field2.name, definition.name));
|
|
22085
|
+
if (existing?.issue_field) return { field: existing, created: false, added_options: [], updated_options: [] };
|
|
22086
|
+
if (existing) {
|
|
22087
|
+
throw invalid7(
|
|
22088
|
+
`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.`
|
|
22089
|
+
);
|
|
22090
|
+
}
|
|
22091
|
+
const data = await graphql(
|
|
22092
|
+
createIssueFieldColumnMutation,
|
|
22093
|
+
{ project: board.id, field: definition.id },
|
|
22094
|
+
ctx
|
|
22095
|
+
);
|
|
22096
|
+
const field = fieldOf2(data?.createProjectV2IssueField ?? null, definition.name);
|
|
22097
|
+
return { field, created: true, added_options: [], updated_options: [] };
|
|
22098
|
+
}
|
|
22002
22099
|
|
|
22003
22100
|
// src/core/projects/support.ts
|
|
22004
22101
|
var BOARD_SELECTION = {
|
|
@@ -22022,60 +22119,65 @@ function boardSummary(board) {
|
|
|
22022
22119
|
// src/core/projects/template.ts
|
|
22023
22120
|
var STATUS_TODO = "Todo";
|
|
22024
22121
|
var STATUS_WAITING = "Waiting";
|
|
22025
|
-
var
|
|
22026
|
-
var
|
|
22027
|
-
var
|
|
22122
|
+
var DATE_MODES = ["board", "issue_fields"];
|
|
22123
|
+
var BOARD_DATE_FIELDS = { start: "Start", target: "Target" };
|
|
22124
|
+
var ISSUE_DATE_FIELDS = { start: "Start date", target: "Target date" };
|
|
22028
22125
|
var DEFAULT_VIEW_NAME = "View 1";
|
|
22029
|
-
|
|
22030
|
-
|
|
22031
|
-
|
|
22032
|
-
|
|
22033
|
-
|
|
22034
|
-
|
|
22035
|
-
|
|
22036
|
-
|
|
22126
|
+
function roadmapNote(dates) {
|
|
22127
|
+
return `Set the Roadmap view's date fields to ${dates.start} and ${dates.target} in the web UI; the API cannot set them.`;
|
|
22128
|
+
}
|
|
22129
|
+
var ROADMAP_NOTE = roadmapNote(BOARD_DATE_FIELDS);
|
|
22130
|
+
var STATUS_REQUEST = {
|
|
22131
|
+
name: STATUS_FIELD,
|
|
22132
|
+
type: "single_select",
|
|
22133
|
+
options: [
|
|
22134
|
+
{ name: STATUS_TODO, color: "BLUE", description: "Ready to start; nothing is in the way." },
|
|
22135
|
+
{ name: "In Progress", color: "YELLOW", description: "Being worked on now." },
|
|
22136
|
+
{
|
|
22137
|
+
name: STATUS_WAITING,
|
|
22138
|
+
color: "GRAY",
|
|
22139
|
+
description: "Cannot move until something else clears: an open blocking issue, a review or a reply."
|
|
22140
|
+
},
|
|
22141
|
+
{ name: "Done", color: "PURPLE", description: "Finished." },
|
|
22142
|
+
{ name: "Cancelled", color: "RED", description: "Will not be done; kept for the record." }
|
|
22143
|
+
]
|
|
22144
|
+
};
|
|
22145
|
+
function standardTemplate(dates) {
|
|
22146
|
+
const notDone = "-status:Done -status:Cancelled";
|
|
22147
|
+
return {
|
|
22148
|
+
roadmapNote: roadmapNote(dates),
|
|
22149
|
+
views: [
|
|
22150
|
+
{ name: "Roadmap", layout: "roadmap", filter: "-status:Cancelled", sort_by: dates.start },
|
|
22037
22151
|
{
|
|
22038
|
-
name:
|
|
22039
|
-
|
|
22040
|
-
|
|
22152
|
+
name: "Kanban",
|
|
22153
|
+
layout: "board",
|
|
22154
|
+
filter: notDone,
|
|
22155
|
+
column_by: STATUS_FIELD,
|
|
22156
|
+
fields: ["Title", "Assignees", "Repository", "Parent issue", dates.target],
|
|
22157
|
+
sort_by: dates.target
|
|
22041
22158
|
},
|
|
22042
|
-
{
|
|
22043
|
-
|
|
22159
|
+
{
|
|
22160
|
+
name: "Backlog",
|
|
22161
|
+
layout: "table",
|
|
22162
|
+
filter: notDone,
|
|
22163
|
+
fields: [
|
|
22164
|
+
"Title",
|
|
22165
|
+
STATUS_FIELD,
|
|
22166
|
+
"Assignees",
|
|
22167
|
+
"Repository",
|
|
22168
|
+
"Milestone",
|
|
22169
|
+
"Parent issue",
|
|
22170
|
+
"Sub-issues progress",
|
|
22171
|
+
dates.start,
|
|
22172
|
+
dates.target,
|
|
22173
|
+
"Linked pull requests"
|
|
22174
|
+
],
|
|
22175
|
+
group_by: STATUS_FIELD,
|
|
22176
|
+
sort_by: dates.target
|
|
22177
|
+
}
|
|
22044
22178
|
]
|
|
22045
|
-
}
|
|
22046
|
-
|
|
22047
|
-
{ name: TARGET_FIELD, type: "date", options: [] }
|
|
22048
|
-
];
|
|
22049
|
-
var STANDARD_VIEWS = [
|
|
22050
|
-
{ name: "Roadmap", layout: "roadmap", filter: "-status:Cancelled", sort_by: START_FIELD },
|
|
22051
|
-
{
|
|
22052
|
-
name: "Kanban",
|
|
22053
|
-
layout: "board",
|
|
22054
|
-
filter: NOT_DONE,
|
|
22055
|
-
column_by: STATUS_FIELD,
|
|
22056
|
-
fields: ["Title", "Assignees", "Repository", "Parent issue", TARGET_FIELD],
|
|
22057
|
-
sort_by: TARGET_FIELD
|
|
22058
|
-
},
|
|
22059
|
-
{
|
|
22060
|
-
name: "Backlog",
|
|
22061
|
-
layout: "table",
|
|
22062
|
-
filter: NOT_DONE,
|
|
22063
|
-
fields: [
|
|
22064
|
-
"Title",
|
|
22065
|
-
STATUS_FIELD,
|
|
22066
|
-
"Assignees",
|
|
22067
|
-
"Repository",
|
|
22068
|
-
"Milestone",
|
|
22069
|
-
"Parent issue",
|
|
22070
|
-
"Sub-issues progress",
|
|
22071
|
-
START_FIELD,
|
|
22072
|
-
TARGET_FIELD,
|
|
22073
|
-
"Linked pull requests"
|
|
22074
|
-
],
|
|
22075
|
-
group_by: STATUS_FIELD,
|
|
22076
|
-
sort_by: TARGET_FIELD
|
|
22077
|
-
}
|
|
22078
|
-
];
|
|
22179
|
+
};
|
|
22180
|
+
}
|
|
22079
22181
|
|
|
22080
22182
|
// src/core/projects/item-support.ts
|
|
22081
22183
|
var ITEM_BOARD_SELECTION = {
|
|
@@ -22088,10 +22190,10 @@ var itemArgument = external_exports.string().describe(
|
|
|
22088
22190
|
"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."
|
|
22089
22191
|
);
|
|
22090
22192
|
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.";
|
|
22091
|
-
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.";
|
|
22193
|
+
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.";
|
|
22092
22194
|
var fieldValuesSchema = external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number()]));
|
|
22093
22195
|
var fieldValuesArgument = fieldValuesSchema.optional().describe(
|
|
22094
|
-
|
|
22196
|
+
`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.`
|
|
22095
22197
|
);
|
|
22096
22198
|
var fieldsResultSchema = fieldValuesSchema.describe(
|
|
22097
22199
|
"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."
|
|
@@ -23310,6 +23412,9 @@ var input10 = external_exports.object({
|
|
|
23310
23412
|
"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)."
|
|
23311
23413
|
),
|
|
23312
23414
|
standard: external_exports.boolean().optional().describe("Bring the board to the standard template: its fields and views (default true)."),
|
|
23415
|
+
dates: external_exports.enum(DATE_MODES).optional().describe(
|
|
23416
|
+
`"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.`
|
|
23417
|
+
),
|
|
23313
23418
|
link_repo: external_exports.string().optional().describe("owner/name of a repository to link the board to; needs write permission.")
|
|
23314
23419
|
});
|
|
23315
23420
|
var output10 = external_exports.object({
|
|
@@ -23341,23 +23446,41 @@ function openBoardTitled(boards, title) {
|
|
|
23341
23446
|
}
|
|
23342
23447
|
return matches[0];
|
|
23343
23448
|
}
|
|
23344
|
-
async function applyFields(board, fresh, report, ctx) {
|
|
23449
|
+
async function applyFields(board, fresh, mode, dates, definitions, report, ctx) {
|
|
23345
23450
|
const fields = await readBoardFields(board.id, ctx);
|
|
23346
|
-
|
|
23347
|
-
|
|
23348
|
-
|
|
23349
|
-
|
|
23350
|
-
|
|
23351
|
-
|
|
23352
|
-
|
|
23353
|
-
|
|
23354
|
-
|
|
23451
|
+
const status = await ensureField(board, fields, STATUS_REQUEST, fresh ? "replace" : "append", ctx);
|
|
23452
|
+
report.push({
|
|
23453
|
+
field: status.field.name,
|
|
23454
|
+
created: status.created,
|
|
23455
|
+
added_options: status.added_options,
|
|
23456
|
+
updated_options: status.updated_options
|
|
23457
|
+
});
|
|
23458
|
+
if (mode === "issue_fields") {
|
|
23459
|
+
for (const name of [dates.start, dates.target]) {
|
|
23460
|
+
const outcome = await ensureIssueFieldColumn(board, fields, definitions, { name, type: "date" }, ctx);
|
|
23461
|
+
report.push({
|
|
23462
|
+
field: outcome.field.name,
|
|
23463
|
+
created: outcome.created,
|
|
23464
|
+
added_options: outcome.added_options,
|
|
23465
|
+
updated_options: outcome.updated_options
|
|
23466
|
+
});
|
|
23467
|
+
}
|
|
23468
|
+
} else {
|
|
23469
|
+
for (const name of [dates.start, dates.target]) {
|
|
23470
|
+
const outcome = await ensureField(board, fields, { name, type: "date", options: [] }, "append", ctx);
|
|
23471
|
+
report.push({
|
|
23472
|
+
field: outcome.field.name,
|
|
23473
|
+
created: outcome.created,
|
|
23474
|
+
added_options: outcome.added_options,
|
|
23475
|
+
updated_options: outcome.updated_options
|
|
23476
|
+
});
|
|
23477
|
+
}
|
|
23355
23478
|
}
|
|
23356
23479
|
}
|
|
23357
|
-
async function applyViews(board, report, notes, ctx) {
|
|
23480
|
+
async function applyViews(board, templateViews, roadmapNote2, report, notes, ctx) {
|
|
23358
23481
|
const before = await readBoardViewNodes(board.id, ctx);
|
|
23359
23482
|
const readers = viewFieldReaders(board, ctx);
|
|
23360
|
-
for (const request2 of
|
|
23483
|
+
for (const request2 of templateViews) {
|
|
23361
23484
|
if (before.some((view5) => sameTitle(view5.name, request2.name))) {
|
|
23362
23485
|
report.push({ view: request2.name, created: false });
|
|
23363
23486
|
continue;
|
|
@@ -23369,7 +23492,7 @@ async function applyViews(board, report, notes, ctx) {
|
|
|
23369
23492
|
throw error62;
|
|
23370
23493
|
}
|
|
23371
23494
|
report.push({ view: request2.name, created: true });
|
|
23372
|
-
if (request2.layout === "roadmap") notes.push(
|
|
23495
|
+
if (request2.layout === "roadmap") notes.push(roadmapNote2);
|
|
23373
23496
|
}
|
|
23374
23497
|
return before;
|
|
23375
23498
|
}
|
|
@@ -23379,6 +23502,7 @@ var projectCreate = defineTool({
|
|
|
23379
23502
|
description: [
|
|
23380
23503
|
`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.`,
|
|
23381
23504
|
"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.",
|
|
23505
|
+
`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.`,
|
|
23382
23506
|
"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.",
|
|
23383
23507
|
"Once the board exists, a failed step does not fail the call: it is listed in errors and the board is still returned.",
|
|
23384
23508
|
`notes lists what the API cannot do, such as setting the Roadmap view's date fields. ${PROJECT_SCOPE_NOTE}`
|
|
@@ -23404,6 +23528,23 @@ var projectCreate = defineTool({
|
|
|
23404
23528
|
linkRepo = await writableContext(repo, ctx);
|
|
23405
23529
|
}
|
|
23406
23530
|
const owner = await readBoardOwner(login, ctx);
|
|
23531
|
+
const dateMode = args.dates ?? "board";
|
|
23532
|
+
let dateNames = BOARD_DATE_FIELDS;
|
|
23533
|
+
let definitions = null;
|
|
23534
|
+
if (dateMode === "issue_fields") {
|
|
23535
|
+
if (!standard) throw invalid5("dates applies only with standard.");
|
|
23536
|
+
definitions = await readOwnerIssueFields({ owner: owner.login, owner_type: owner.type }, ctx);
|
|
23537
|
+
const startDef = requireIssueField({ owner: owner.login, owner_type: owner.type }, definitions, {
|
|
23538
|
+
name: ISSUE_DATE_FIELDS.start,
|
|
23539
|
+
type: "date"
|
|
23540
|
+
});
|
|
23541
|
+
const targetDef = requireIssueField({ owner: owner.login, owner_type: owner.type }, definitions, {
|
|
23542
|
+
name: ISSUE_DATE_FIELDS.target,
|
|
23543
|
+
type: "date"
|
|
23544
|
+
});
|
|
23545
|
+
dateNames = { start: startDef.name, target: targetDef.name };
|
|
23546
|
+
}
|
|
23547
|
+
const template = standardTemplate(dateNames);
|
|
23407
23548
|
const reused = openBoardTitled((await ownerBoards(owner.login, ctx, owner.type)).boards, title);
|
|
23408
23549
|
if (reused) assertBoardWritable(reused);
|
|
23409
23550
|
const source = reused || !templateRef ? void 0 : (await resolveBoard({ project: templateRef, owner: ownerArg }, ctx)).project;
|
|
@@ -23416,8 +23557,11 @@ var projectCreate = defineTool({
|
|
|
23416
23557
|
const notes = [];
|
|
23417
23558
|
let deletedDefaultView = false;
|
|
23418
23559
|
if (standard) {
|
|
23419
|
-
await steps.record("fields", () => applyFields(board, fresh, fields, ctx));
|
|
23420
|
-
const before = await steps.record(
|
|
23560
|
+
await steps.record("fields", () => applyFields(board, fresh, dateMode, dateNames, definitions, fields, ctx));
|
|
23561
|
+
const before = await steps.record(
|
|
23562
|
+
"views",
|
|
23563
|
+
() => applyViews(board, template.views, template.roadmapNote, views, notes, ctx)
|
|
23564
|
+
);
|
|
23421
23565
|
const defaultView = fresh ? before?.find((view5) => view5.name === DEFAULT_VIEW_NAME) : void 0;
|
|
23422
23566
|
if (defaultView) {
|
|
23423
23567
|
deletedDefaultView = await steps.record("default_view", async () => {
|
|
@@ -23448,9 +23592,14 @@ var projectCreate = defineTool({
|
|
|
23448
23592
|
var input11 = external_exports.object({
|
|
23449
23593
|
...BOARD_SELECTION,
|
|
23450
23594
|
name: external_exports.string().describe("The field's name, matched without regard to case."),
|
|
23451
|
-
type: external_exports.enum(FIELD_TYPES).describe(
|
|
23595
|
+
type: external_exports.enum(FIELD_TYPES).optional().describe(
|
|
23596
|
+
"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."
|
|
23597
|
+
),
|
|
23452
23598
|
options: external_exports.array(external_exports.string()).optional().describe(
|
|
23453
|
-
`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.`
|
|
23599
|
+
`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.`
|
|
23600
|
+
),
|
|
23601
|
+
issue_field: external_exports.boolean().optional().describe(
|
|
23602
|
+
"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."
|
|
23454
23603
|
)
|
|
23455
23604
|
});
|
|
23456
23605
|
var output11 = external_exports.object({
|
|
@@ -23459,12 +23608,25 @@ var output11 = external_exports.object({
|
|
|
23459
23608
|
created: external_exports.boolean().describe("Whether this call made the field."),
|
|
23460
23609
|
added_options: external_exports.array(external_exports.string()).describe("The names of the options this call added; [] when none.")
|
|
23461
23610
|
});
|
|
23611
|
+
function planEnsure(args) {
|
|
23612
|
+
if (args.issue_field) {
|
|
23613
|
+
if (args.options && args.options.length > 0) {
|
|
23614
|
+
throw new ToolError("invalid_argument", "options are the organization's with issue_field; leave them out.");
|
|
23615
|
+
}
|
|
23616
|
+
const request3 = issueFieldColumnRequest(args.name, args.type);
|
|
23617
|
+
return async (board, fields, ctx) => ensureIssueFieldColumn(board, fields, await readOwnerIssueFields(board, ctx), request3, ctx);
|
|
23618
|
+
}
|
|
23619
|
+
if (!args.type) throw new ToolError("invalid_argument", "type is required unless issue_field is true.");
|
|
23620
|
+
const request2 = fieldRequest(args.name, args.type, args.options);
|
|
23621
|
+
return (board, fields, ctx) => ensureField(board, fields, request2, "append", ctx);
|
|
23622
|
+
}
|
|
23462
23623
|
var projectFieldEnsure = defineTool({
|
|
23463
23624
|
id: "github_project_field_ensure",
|
|
23464
23625
|
title: "Ensure a GitHub project field",
|
|
23465
23626
|
description: [
|
|
23466
23627
|
"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.",
|
|
23467
23628
|
"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.",
|
|
23629
|
+
"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.",
|
|
23468
23630
|
BOARD_LOOKUP_NOTE,
|
|
23469
23631
|
PROJECT_SCOPE_NOTE
|
|
23470
23632
|
].join(" "),
|
|
@@ -23474,11 +23636,11 @@ var projectFieldEnsure = defineTool({
|
|
|
23474
23636
|
input: input11,
|
|
23475
23637
|
output: output11,
|
|
23476
23638
|
async execute(args, ctx) {
|
|
23477
|
-
const
|
|
23639
|
+
const ensure = planEnsure(args);
|
|
23478
23640
|
const { project } = await resolveBoard(args, ctx);
|
|
23479
23641
|
assertBoardWritable(project);
|
|
23480
23642
|
const fields = await readBoardFields(project.id, ctx);
|
|
23481
|
-
const { field, created, added_options } = await
|
|
23643
|
+
const { field, created, added_options } = await ensure(project, fields, ctx);
|
|
23482
23644
|
return { project: boardSummary(project), field, created, added_options };
|
|
23483
23645
|
}
|
|
23484
23646
|
});
|
|
@@ -23558,7 +23720,10 @@ var FIELD_LABELS = 20;
|
|
|
23558
23720
|
var FIELD_USERS = 10;
|
|
23559
23721
|
var FIELD_PULL_REQUESTS = 10;
|
|
23560
23722
|
var FIELD_NAME = "field{... on ProjectV2FieldCommon{name}}";
|
|
23561
|
-
|
|
23723
|
+
function itemFields(includeIssueFields = true) {
|
|
23724
|
+
const issueFieldSelection = includeIssueFields ? `
|
|
23725
|
+
... on ProjectV2ItemIssueFieldValue{${FIELD_NAME} issueFieldValue{${ISSUE_FIELD_VALUE_SELECTION}}}` : "";
|
|
23726
|
+
return `id isArchived
|
|
23562
23727
|
fieldValues(first:${FIELD_VALUES}){nodes{
|
|
23563
23728
|
... on ProjectV2ItemFieldTextValue{text ${FIELD_NAME}}
|
|
23564
23729
|
... on ProjectV2ItemFieldNumberValue{number ${FIELD_NAME}}
|
|
@@ -23569,15 +23734,22 @@ fieldValues(first:${FIELD_VALUES}){nodes{
|
|
|
23569
23734
|
... on ProjectV2ItemFieldRepositoryValue{repository{nameWithOwner} ${FIELD_NAME}}
|
|
23570
23735
|
... on ProjectV2ItemFieldLabelValue{labels(first:${FIELD_LABELS}){nodes{name}} ${FIELD_NAME}}
|
|
23571
23736
|
... on ProjectV2ItemFieldUserValue{users(first:${FIELD_USERS}){nodes{login}} ${FIELD_NAME}}
|
|
23572
|
-
... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:${FIELD_PULL_REQUESTS}){nodes{number}} ${FIELD_NAME}}
|
|
23737
|
+
... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:${FIELD_PULL_REQUESTS}){nodes{number}} ${FIELD_NAME}}${issueFieldSelection}
|
|
23573
23738
|
}}
|
|
23574
23739
|
content{__typename
|
|
23575
23740
|
... on DraftIssue{id title body}
|
|
23576
23741
|
... 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}}
|
|
23577
23742
|
... on PullRequest{number title url pullRequestState: state repository{nameWithOwner} labels(first:${CONTENT_LABELS}){nodes{name}} assignees(first:${CONTENT_ASSIGNEES}){nodes{login}}}
|
|
23578
23743
|
}`;
|
|
23579
|
-
|
|
23580
|
-
var
|
|
23744
|
+
}
|
|
23745
|
+
var ITEM_FIELDS = itemFields(true);
|
|
23746
|
+
var ITEM_FIELDS_WITHOUT_ISSUE_FIELDS = itemFields(false);
|
|
23747
|
+
var itemsQuery = (fields) => `query($id:ID!,$after:String){node(id:$id){... on ProjectV2{items(first:${PAGE_SIZE2},after:$after){pageInfo{hasNextPage endCursor} nodes{${fields}}}}}}`;
|
|
23748
|
+
var itemQuery = (fields) => `query($id:ID!){node(id:$id){... on ProjectV2Item{${fields}}}}`;
|
|
23749
|
+
var ITEMS_QUERY = itemsQuery(ITEM_FIELDS);
|
|
23750
|
+
var ITEMS_QUERY_WITHOUT_ISSUE_FIELDS = itemsQuery(ITEM_FIELDS_WITHOUT_ISSUE_FIELDS);
|
|
23751
|
+
var ITEM_QUERY = itemQuery(ITEM_FIELDS);
|
|
23752
|
+
var ITEM_QUERY_WITHOUT_ISSUE_FIELDS = itemQuery(ITEM_FIELDS_WITHOUT_ISSUE_FIELDS);
|
|
23581
23753
|
var iterationValueSchema = external_exports.object({
|
|
23582
23754
|
title: external_exports.string(),
|
|
23583
23755
|
start_date: external_exports.string(),
|
|
@@ -23608,7 +23780,7 @@ var boardItemSchema = external_exports.object({
|
|
|
23608
23780
|
item_id: external_exports.string(),
|
|
23609
23781
|
archived: external_exports.boolean(),
|
|
23610
23782
|
fields: external_exports.record(external_exports.string(), itemFieldValueSchema).describe(
|
|
23611
|
-
"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."
|
|
23783
|
+
"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."
|
|
23612
23784
|
),
|
|
23613
23785
|
content: itemContentSchema.nullable().describe("What the item stands for; null when GitHub did not return it.")
|
|
23614
23786
|
});
|
|
@@ -23616,6 +23788,7 @@ function nodesOf2(connection) {
|
|
|
23616
23788
|
return (connection?.nodes ?? []).filter((node2) => node2 != null);
|
|
23617
23789
|
}
|
|
23618
23790
|
function fieldValue(raw) {
|
|
23791
|
+
if ("issueFieldValue" in raw) return issueFieldValueOf(raw.issueFieldValue);
|
|
23619
23792
|
if ("text" in raw) return raw.text ?? null;
|
|
23620
23793
|
if ("number" in raw) return raw.number ?? null;
|
|
23621
23794
|
if ("date" in raw) return raw.date ?? null;
|
|
@@ -23679,16 +23852,34 @@ function toItem(raw) {
|
|
|
23679
23852
|
};
|
|
23680
23853
|
}
|
|
23681
23854
|
async function readBoardItems(projectId, ctx) {
|
|
23682
|
-
const
|
|
23683
|
-
|
|
23684
|
-
|
|
23685
|
-
|
|
23686
|
-
|
|
23687
|
-
|
|
23688
|
-
|
|
23855
|
+
const run = (query) => paginate(query, { id: projectId }, (data) => data?.node?.items, {
|
|
23856
|
+
cwd: ctx.cwd,
|
|
23857
|
+
signal: ctx.signal,
|
|
23858
|
+
limit: MAX_BOARD_ITEMS
|
|
23859
|
+
});
|
|
23860
|
+
let result;
|
|
23861
|
+
try {
|
|
23862
|
+
result = await run(ITEMS_QUERY);
|
|
23863
|
+
} catch (error62) {
|
|
23864
|
+
if (isIssueFieldsMissingError(error62)) {
|
|
23865
|
+
result = await run(ITEMS_QUERY_WITHOUT_ISSUE_FIELDS);
|
|
23866
|
+
} else {
|
|
23867
|
+
throw error62;
|
|
23868
|
+
}
|
|
23869
|
+
}
|
|
23870
|
+
return { items: result.items.filter((raw) => raw != null).map(toItem), truncated: result.truncated };
|
|
23689
23871
|
}
|
|
23690
23872
|
async function readBoardItem(itemId, ctx) {
|
|
23691
|
-
|
|
23873
|
+
let data;
|
|
23874
|
+
try {
|
|
23875
|
+
data = await graphql(ITEM_QUERY, { id: itemId }, ctx);
|
|
23876
|
+
} catch (error62) {
|
|
23877
|
+
if (isIssueFieldsMissingError(error62)) {
|
|
23878
|
+
data = await graphql(ITEM_QUERY_WITHOUT_ISSUE_FIELDS, { id: itemId }, ctx);
|
|
23879
|
+
} else {
|
|
23880
|
+
throw error62;
|
|
23881
|
+
}
|
|
23882
|
+
}
|
|
23692
23883
|
const raw = data?.node;
|
|
23693
23884
|
if (!raw?.id) return null;
|
|
23694
23885
|
return { item: toItem(raw), draftId: raw.content?.__typename === "DraftIssue" ? raw.content.id ?? null : null };
|
|
@@ -23913,7 +24104,9 @@ var input16 = external_exports.object({
|
|
|
23913
24104
|
item: itemArgument,
|
|
23914
24105
|
...ITEM_BOARD_SELECTION,
|
|
23915
24106
|
fields: fieldValuesArgument,
|
|
23916
|
-
clear_fields: external_exports.array(external_exports.string()).optional().describe(
|
|
24107
|
+
clear_fields: external_exports.array(external_exports.string()).optional().describe(
|
|
24108
|
+
"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."
|
|
24109
|
+
),
|
|
23917
24110
|
title: external_exports.string().optional().describe("Drafts only: the new title."),
|
|
23918
24111
|
body: external_exports.string().optional().describe("Drafts only: the new body, which replaces the whole old one; empty clears it."),
|
|
23919
24112
|
append_body: external_exports.string().optional().describe("Drafts only: text added after the current body, set off by a blank line.")
|
|
@@ -24221,7 +24414,7 @@ var projectRead = defineTool({
|
|
|
24221
24414
|
id: "github_project_read",
|
|
24222
24415
|
title: "Read GitHub project boards",
|
|
24223
24416
|
description: [
|
|
24224
|
-
'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.',
|
|
24417
|
+
'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.',
|
|
24225
24418
|
`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}).`,
|
|
24226
24419
|
`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}).`,
|
|
24227
24420
|
"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.",
|