@craftsamo/github-tools 0.6.1 → 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 +3 -0
- package/dist/cli.js +194 -72
- package/dist/mcp.js +195 -73
- package/dist/opencode.js +194 -72
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/plugin.yaml +1 -1
- package/skills/github-projects/SKILL.md +16 -1
package/README.md
CHANGED
|
@@ -220,6 +220,9 @@ after `github_project_create` set the Roadmap view's "Date fields" to Start and
|
|
|
220
220
|
Target in the web UI. A column that mirrors an organization's issue field is
|
|
221
221
|
read like any other, but its value is the issue's own: the board tools refuse
|
|
222
222
|
to set or clear it, and `github_issue_edit` does both.
|
|
223
|
+
`github_project_field_ensure` with `issue_field: true` adds such a column, and
|
|
224
|
+
`github_project_create` with `dates: "issue_fields"` uses the issue fields Start
|
|
225
|
+
date and Target date as an organization board's dates.
|
|
223
226
|
|
|
224
227
|
## Reads and writes
|
|
225
228
|
|
package/dist/cli.js
CHANGED
|
@@ -20737,7 +20737,7 @@ function issueFieldValues(connection) {
|
|
|
20737
20737
|
}
|
|
20738
20738
|
var SCHEMA_ERROR_PATTERNS = [
|
|
20739
20739
|
/issueFieldValues.*(?:doesn['’]t exist on type|is not defined on type).*['"`]?Issue['"`]?/i,
|
|
20740
|
-
/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
20741
|
/(?:isIssueField|issueField).*(?:doesn['’]t exist on type|is not defined on type).*['"`]?ProjectV2/i,
|
|
20742
20742
|
/(?:No such type\s+['"`]?(?:ProjectV2ItemIssueFieldValue|ProjectV2IssueFieldValues)['"`]?|(?:ProjectV2ItemIssueFieldValue|ProjectV2IssueFieldValues).*(?:doesn['’]t exist|is not defined))/i
|
|
20743
20743
|
];
|
|
@@ -20756,7 +20756,9 @@ function isIssueFieldsMissingError(error62) {
|
|
|
20756
20756
|
var issueFieldInputSchema = issueFieldValuesSchema;
|
|
20757
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.`;
|
|
20758
20758
|
var MAX_ISSUE_FIELDS = 25;
|
|
20759
|
-
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}}}}}}`;
|
|
20760
20762
|
var VALUES_QUERY = `query($id:ID!){node(id:$id){... on Issue{${ISSUE_FIELD_VALUES_FRAGMENT}}}}`;
|
|
20761
20763
|
var issueFieldTypes = {
|
|
20762
20764
|
TEXT: "text",
|
|
@@ -20792,6 +20794,20 @@ async function readIssueFieldDefinitions(repo, ctx) {
|
|
|
20792
20794
|
}
|
|
20793
20795
|
return (data?.repository?.issueFields?.nodes ?? []).flatMap((raw) => definitionOf(raw) ?? []);
|
|
20794
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
|
+
}
|
|
20795
20811
|
function invalid4(message) {
|
|
20796
20812
|
return new ToolError("invalid_argument", message);
|
|
20797
20813
|
}
|
|
@@ -20799,7 +20815,7 @@ function fieldNames(definitions) {
|
|
|
20799
20815
|
return listOf(definitions.map((field) => field.name));
|
|
20800
20816
|
}
|
|
20801
20817
|
function fieldOf(repo, definitions, name) {
|
|
20802
|
-
const field = definitions
|
|
20818
|
+
const field = findIssueField(definitions, name);
|
|
20803
20819
|
if (!field) throw invalid4(`${repo.full} has no issue field "${name}". Fields: ${fieldNames(definitions)}.`);
|
|
20804
20820
|
return field;
|
|
20805
20821
|
}
|
|
@@ -21970,6 +21986,7 @@ var STATUS_FIELD = "Status";
|
|
|
21970
21986
|
var FIELD_REPLY = `projectV2Field{${FIELD_SELECTION}}`;
|
|
21971
21987
|
var createFieldMutation = (type, options) => `mutation($project:ID!,$name:String!){createProjectV2Field(input:{projectId:$project,dataType:${type.toUpperCase()},name:$name${options}}){${FIELD_REPLY}}}`;
|
|
21972
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}}}}`;
|
|
21973
21990
|
function invalid7(message) {
|
|
21974
21991
|
return new ToolError("invalid_argument", message);
|
|
21975
21992
|
}
|
|
@@ -21982,6 +21999,29 @@ function fieldRequest(name, type, options) {
|
|
|
21982
21999
|
}
|
|
21983
22000
|
return { name: trimmed, type, options: parsed };
|
|
21984
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
|
+
}
|
|
21985
22025
|
function optionsLiteral(options) {
|
|
21986
22026
|
const items = options.map((option) => {
|
|
21987
22027
|
if (!OPTION_COLORS.some((color) => color === option.color)) {
|
|
@@ -22039,6 +22079,23 @@ async function ensureField(board, fields, request2, policy, ctx) {
|
|
|
22039
22079
|
const field = fieldOf2(data?.updateProjectV2Field ?? null, existing.name);
|
|
22040
22080
|
return { field, created: false, added_options: merge2.added, updated_options: merge2.updated };
|
|
22041
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
|
+
}
|
|
22042
22099
|
|
|
22043
22100
|
// src/core/projects/support.ts
|
|
22044
22101
|
var BOARD_SELECTION = {
|
|
@@ -22062,60 +22119,65 @@ function boardSummary(board) {
|
|
|
22062
22119
|
// src/core/projects/template.ts
|
|
22063
22120
|
var STATUS_TODO = "Todo";
|
|
22064
22121
|
var STATUS_WAITING = "Waiting";
|
|
22065
|
-
var
|
|
22066
|
-
var
|
|
22067
|
-
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" };
|
|
22068
22125
|
var DEFAULT_VIEW_NAME = "View 1";
|
|
22069
|
-
|
|
22070
|
-
|
|
22071
|
-
|
|
22072
|
-
|
|
22073
|
-
|
|
22074
|
-
|
|
22075
|
-
|
|
22076
|
-
|
|
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 },
|
|
22077
22151
|
{
|
|
22078
|
-
name:
|
|
22079
|
-
|
|
22080
|
-
|
|
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
|
|
22081
22158
|
},
|
|
22082
|
-
{
|
|
22083
|
-
|
|
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
|
+
}
|
|
22084
22178
|
]
|
|
22085
|
-
}
|
|
22086
|
-
|
|
22087
|
-
{ name: TARGET_FIELD, type: "date", options: [] }
|
|
22088
|
-
];
|
|
22089
|
-
var STANDARD_VIEWS = [
|
|
22090
|
-
{ name: "Roadmap", layout: "roadmap", filter: "-status:Cancelled", sort_by: START_FIELD },
|
|
22091
|
-
{
|
|
22092
|
-
name: "Kanban",
|
|
22093
|
-
layout: "board",
|
|
22094
|
-
filter: NOT_DONE,
|
|
22095
|
-
column_by: STATUS_FIELD,
|
|
22096
|
-
fields: ["Title", "Assignees", "Repository", "Parent issue", TARGET_FIELD],
|
|
22097
|
-
sort_by: TARGET_FIELD
|
|
22098
|
-
},
|
|
22099
|
-
{
|
|
22100
|
-
name: "Backlog",
|
|
22101
|
-
layout: "table",
|
|
22102
|
-
filter: NOT_DONE,
|
|
22103
|
-
fields: [
|
|
22104
|
-
"Title",
|
|
22105
|
-
STATUS_FIELD,
|
|
22106
|
-
"Assignees",
|
|
22107
|
-
"Repository",
|
|
22108
|
-
"Milestone",
|
|
22109
|
-
"Parent issue",
|
|
22110
|
-
"Sub-issues progress",
|
|
22111
|
-
START_FIELD,
|
|
22112
|
-
TARGET_FIELD,
|
|
22113
|
-
"Linked pull requests"
|
|
22114
|
-
],
|
|
22115
|
-
group_by: STATUS_FIELD,
|
|
22116
|
-
sort_by: TARGET_FIELD
|
|
22117
|
-
}
|
|
22118
|
-
];
|
|
22179
|
+
};
|
|
22180
|
+
}
|
|
22119
22181
|
|
|
22120
22182
|
// src/core/projects/item-support.ts
|
|
22121
22183
|
var ITEM_BOARD_SELECTION = {
|
|
@@ -23350,6 +23412,9 @@ var input10 = external_exports.object({
|
|
|
23350
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)."
|
|
23351
23413
|
),
|
|
23352
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
|
+
),
|
|
23353
23418
|
link_repo: external_exports.string().optional().describe("owner/name of a repository to link the board to; needs write permission.")
|
|
23354
23419
|
});
|
|
23355
23420
|
var output10 = external_exports.object({
|
|
@@ -23381,23 +23446,41 @@ function openBoardTitled(boards, title) {
|
|
|
23381
23446
|
}
|
|
23382
23447
|
return matches[0];
|
|
23383
23448
|
}
|
|
23384
|
-
async function applyFields(board, fresh, report, ctx) {
|
|
23449
|
+
async function applyFields(board, fresh, mode, dates, definitions, report, ctx) {
|
|
23385
23450
|
const fields = await readBoardFields(board.id, ctx);
|
|
23386
|
-
|
|
23387
|
-
|
|
23388
|
-
|
|
23389
|
-
|
|
23390
|
-
|
|
23391
|
-
|
|
23392
|
-
|
|
23393
|
-
|
|
23394
|
-
|
|
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
|
+
}
|
|
23395
23478
|
}
|
|
23396
23479
|
}
|
|
23397
|
-
async function applyViews(board, report, notes, ctx) {
|
|
23480
|
+
async function applyViews(board, templateViews, roadmapNote2, report, notes, ctx) {
|
|
23398
23481
|
const before = await readBoardViewNodes(board.id, ctx);
|
|
23399
23482
|
const readers = viewFieldReaders(board, ctx);
|
|
23400
|
-
for (const request2 of
|
|
23483
|
+
for (const request2 of templateViews) {
|
|
23401
23484
|
if (before.some((view5) => sameTitle(view5.name, request2.name))) {
|
|
23402
23485
|
report.push({ view: request2.name, created: false });
|
|
23403
23486
|
continue;
|
|
@@ -23409,7 +23492,7 @@ async function applyViews(board, report, notes, ctx) {
|
|
|
23409
23492
|
throw error62;
|
|
23410
23493
|
}
|
|
23411
23494
|
report.push({ view: request2.name, created: true });
|
|
23412
|
-
if (request2.layout === "roadmap") notes.push(
|
|
23495
|
+
if (request2.layout === "roadmap") notes.push(roadmapNote2);
|
|
23413
23496
|
}
|
|
23414
23497
|
return before;
|
|
23415
23498
|
}
|
|
@@ -23419,6 +23502,7 @@ var projectCreate = defineTool({
|
|
|
23419
23502
|
description: [
|
|
23420
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.`,
|
|
23421
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.`,
|
|
23422
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.",
|
|
23423
23507
|
"Once the board exists, a failed step does not fail the call: it is listed in errors and the board is still returned.",
|
|
23424
23508
|
`notes lists what the API cannot do, such as setting the Roadmap view's date fields. ${PROJECT_SCOPE_NOTE}`
|
|
@@ -23444,6 +23528,23 @@ var projectCreate = defineTool({
|
|
|
23444
23528
|
linkRepo = await writableContext(repo, ctx);
|
|
23445
23529
|
}
|
|
23446
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);
|
|
23447
23548
|
const reused = openBoardTitled((await ownerBoards(owner.login, ctx, owner.type)).boards, title);
|
|
23448
23549
|
if (reused) assertBoardWritable(reused);
|
|
23449
23550
|
const source = reused || !templateRef ? void 0 : (await resolveBoard({ project: templateRef, owner: ownerArg }, ctx)).project;
|
|
@@ -23456,8 +23557,11 @@ var projectCreate = defineTool({
|
|
|
23456
23557
|
const notes = [];
|
|
23457
23558
|
let deletedDefaultView = false;
|
|
23458
23559
|
if (standard) {
|
|
23459
|
-
await steps.record("fields", () => applyFields(board, fresh, fields, ctx));
|
|
23460
|
-
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
|
+
);
|
|
23461
23565
|
const defaultView = fresh ? before?.find((view5) => view5.name === DEFAULT_VIEW_NAME) : void 0;
|
|
23462
23566
|
if (defaultView) {
|
|
23463
23567
|
deletedDefaultView = await steps.record("default_view", async () => {
|
|
@@ -23488,9 +23592,14 @@ var projectCreate = defineTool({
|
|
|
23488
23592
|
var input11 = external_exports.object({
|
|
23489
23593
|
...BOARD_SELECTION,
|
|
23490
23594
|
name: external_exports.string().describe("The field's name, matched without regard to case."),
|
|
23491
|
-
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
|
+
),
|
|
23492
23598
|
options: external_exports.array(external_exports.string()).optional().describe(
|
|
23493
|
-
`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."
|
|
23494
23603
|
)
|
|
23495
23604
|
});
|
|
23496
23605
|
var output11 = external_exports.object({
|
|
@@ -23499,12 +23608,25 @@ var output11 = external_exports.object({
|
|
|
23499
23608
|
created: external_exports.boolean().describe("Whether this call made the field."),
|
|
23500
23609
|
added_options: external_exports.array(external_exports.string()).describe("The names of the options this call added; [] when none.")
|
|
23501
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
|
+
}
|
|
23502
23623
|
var projectFieldEnsure = defineTool({
|
|
23503
23624
|
id: "github_project_field_ensure",
|
|
23504
23625
|
title: "Ensure a GitHub project field",
|
|
23505
23626
|
description: [
|
|
23506
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.",
|
|
23507
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.",
|
|
23508
23630
|
BOARD_LOOKUP_NOTE,
|
|
23509
23631
|
PROJECT_SCOPE_NOTE
|
|
23510
23632
|
].join(" "),
|
|
@@ -23514,11 +23636,11 @@ var projectFieldEnsure = defineTool({
|
|
|
23514
23636
|
input: input11,
|
|
23515
23637
|
output: output11,
|
|
23516
23638
|
async execute(args, ctx) {
|
|
23517
|
-
const
|
|
23639
|
+
const ensure = planEnsure(args);
|
|
23518
23640
|
const { project } = await resolveBoard(args, ctx);
|
|
23519
23641
|
assertBoardWritable(project);
|
|
23520
23642
|
const fields = await readBoardFields(project.id, ctx);
|
|
23521
|
-
const { field, created, added_options } = await
|
|
23643
|
+
const { field, created, added_options } = await ensure(project, fields, ctx);
|
|
23522
23644
|
return { project: boardSummary(project), field, created, added_options };
|
|
23523
23645
|
}
|
|
23524
23646
|
});
|