@brainervirus/workit-core 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/package.json +10 -14
- package/scripts/doctor-check.ts +20 -0
- package/scripts/install-cursor-plugin.sh +51 -28
- package/scripts/install-opencode-plugin.sh +25 -22
- package/scripts/rewrite-workspace-deps.ts +15 -9
- package/scripts/sync-runtime.sh +71 -19
- package/scripts/vendor-assets.ts +37 -0
- package/skills/wk-implement/SKILL.md +2 -2
- package/skills/wk-pr/SKILL.md +1 -1
- package/src/core/boundary.ts +27 -0
- package/src/core/branch-policy.ts +63 -0
- package/src/core/branch.ts +166 -57
- package/src/core/changelog.ts +17 -14
- package/src/core/config-guard.ts +9 -2
- package/src/core/config.ts +227 -32
- package/src/core/detector.ts +22 -11
- package/src/core/docs-layout.ts +251 -0
- package/src/core/docs-migration.ts +639 -0
- package/src/core/docs-repo.ts +58 -21
- package/src/core/docs-validate.ts +181 -43
- package/src/core/doctor.ts +801 -0
- package/src/core/flow-state.ts +1579 -137
- package/src/core/git.ts +22 -5
- package/src/core/gitignore.ts +11 -2
- package/src/core/handoff-context.ts +18 -5
- package/src/{tools/handoff.ts → core/handoff-tools.ts} +16 -63
- package/src/core/hygiene.ts +53 -17
- package/src/core/init.ts +128 -31
- package/src/core/logger.ts +321 -0
- package/src/core/package-root.ts +28 -0
- package/src/core/parse-sections.ts +2 -2
- package/src/core/plan-tasks.ts +13 -3
- package/src/core/ports/init-toolkit-status.ts +1 -1
- package/src/core/ports/vcs-verify-token.ts +1 -1
- package/src/core/ports/youtrack-api.ts +4 -2
- package/src/core/ports/youtrack-config.ts +1 -3
- package/src/core/ports/youtrack-verify-token.ts +1 -1
- package/src/core/pr-create.ts +156 -29
- package/src/core/present.ts +11 -2
- package/src/core/registration.ts +215 -0
- package/src/core/reminder.ts +1 -2
- package/src/core/repo-context.ts +447 -0
- package/src/core/repo-tool.ts +4 -1
- package/src/core/repo-tools.ts +23 -0
- package/src/core/rules.ts +10 -7
- package/src/core/safe-write.ts +22 -0
- package/src/core/scripts.ts +3 -39
- package/src/core/sdd.ts +56 -31
- package/src/core/setup-state.ts +54 -0
- package/src/core/setup.ts +1216 -0
- package/src/core/skill-manifests.ts +95 -0
- package/src/core/support-matrix.ts +12 -0
- package/src/core/sync-runtime.ts +348 -0
- package/src/core/templates.ts +16 -6
- package/src/core/vcs-config.ts +191 -62
- package/src/core/verify-parse.ts +4 -2
- package/src/core/verify-project.ts +181 -0
- package/src/core/workspaces.ts +136 -17
- package/src/core/youtrack-tools.ts +228 -0
- package/src/core/youtrack.ts +320 -87
- package/src/core.ts +18 -3
- package/templates/execution-contract.md +9 -7
- package/templates/superpowers-doc-contract.md +5 -4
- package/scripts/_shared/common.sh +0 -158
- package/scripts/changelog-context.sh +0 -42
- package/scripts/docs-refresh-context.sh +0 -40
- package/scripts/init/apply.sh +0 -5
- package/scripts/init/status.sh +0 -5
- package/scripts/init/toolkit-status.sh +0 -5
- package/scripts/pr-create.sh +0 -5
- package/scripts/pr-ready-context.sh +0 -88
- package/scripts/present/ascii-wireframe.sh +0 -5
- package/scripts/present/flow-diagram.sh +0 -5
- package/scripts/release-notes-context.sh +0 -40
- package/scripts/vcs/config.sh +0 -5
- package/scripts/vcs/merged-style.sh +0 -5
- package/scripts/vcs/token-create-urls.sh +0 -5
- package/scripts/vcs/verify-token.sh +0 -5
- package/scripts/verify-project.sh +0 -140
- package/scripts/youtrack/api.sh +0 -5
- package/scripts/youtrack/config.sh +0 -5
- package/scripts/youtrack/greeting.sh +0 -5
- package/scripts/youtrack/parse-duration.sh +0 -5
- package/scripts/youtrack/token-create-url.sh +0 -5
- package/scripts/youtrack/verify-token.sh +0 -5
- package/scripts/youtrack/work-date-ms.sh +0 -5
- package/src/tools/docs-repo.ts +0 -42
- package/src/tools/flow.ts +0 -88
- package/src/tools/index.ts +0 -22
- package/src/tools/present.ts +0 -45
- package/src/tools/repo.ts +0 -357
- package/src/tools/rules.ts +0 -30
- package/src/tools/sdd.ts +0 -189
- package/src/tools/templates.ts +0 -27
- package/src/tools/youtrack.ts +0 -360
package/src/tools/youtrack.ts
DELETED
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
import { readFileSync, statSync } from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { tool, type ToolContext } from "@opencode-ai/plugin";
|
|
5
|
-
import { fail, ok, resolveInside, type Result } from "../core";
|
|
6
|
-
import { configGuardError, describeConfigGaps } from "../core/config-guard";
|
|
7
|
-
import { configDir } from "../core/config";
|
|
8
|
-
import {
|
|
9
|
-
buildDraft as legacyBuildDraft,
|
|
10
|
-
context as legacyContext,
|
|
11
|
-
logTime as legacyLogTime,
|
|
12
|
-
parseDuration as legacyParseDuration,
|
|
13
|
-
parseIssueRef,
|
|
14
|
-
postUpdate as legacyPostUpdate,
|
|
15
|
-
verifyYouTrackToken,
|
|
16
|
-
} from "../core/youtrack";
|
|
17
|
-
|
|
18
|
-
const ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
19
|
-
const output = (value: unknown) => JSON.stringify(value, null, 2);
|
|
20
|
-
const message = (error: unknown) => error instanceof Error ? error.message : String(error);
|
|
21
|
-
|
|
22
|
-
// Both override names point at the config dir itself, same precedence as
|
|
23
|
-
// src/core/config.ts and scripts/init/status.sh: WORKFLOW_TOOLKIT_CONFIG → WORKFLOW_TOOLKIT_CONFIG_DIR → XDG.
|
|
24
|
-
// Default env (no args) routes through configDir() so the legacy migration runs.
|
|
25
|
-
export const configPath = (env: NodeJS.ProcessEnv = process.env, home = os.homedir()) =>
|
|
26
|
-
path.join(
|
|
27
|
-
env === process.env
|
|
28
|
-
? configDir()
|
|
29
|
-
: env.WORKFLOW_TOOLKIT_CONFIG
|
|
30
|
-
?? env.WORKFLOW_TOOLKIT_CONFIG_DIR
|
|
31
|
-
?? path.join(env.XDG_CONFIG_HOME || path.join(home, ".config"), "workit"),
|
|
32
|
-
"youtrack.json",
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
export function readCredentials(env: NodeJS.ProcessEnv = process.env, home = os.homedir()) {
|
|
36
|
-
const resolvedConfig = configPath(env, home);
|
|
37
|
-
const config = JSON.parse(readFileSync(resolvedConfig, "utf8")) as { tokenFile?: string };
|
|
38
|
-
const tokenFile = config.tokenFile ?? "youtrack.token";
|
|
39
|
-
const tokenPath = path.resolve(path.dirname(resolvedConfig), tokenFile.replace(/^~(?=\/)/, home));
|
|
40
|
-
if (process.platform !== "win32" && (statSync(tokenPath).mode & 0o777) !== 0o600) throw new Error("youtrack.token mode must be 0600");
|
|
41
|
-
const token = readFileSync(tokenPath, "utf8").trim();
|
|
42
|
-
if (!token) throw new Error("youtrack.token is empty");
|
|
43
|
-
return { configPath: resolvedConfig, token };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const redact = (text: string, token: string) =>
|
|
47
|
-
token ? text.split(token).join("[REDACTED]") : text;
|
|
48
|
-
|
|
49
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
50
|
-
type LegacyValue = Record<string, unknown> | void;
|
|
51
|
-
export type NotApplied = { ok: false; error: string; outcome: "not_applied" };
|
|
52
|
-
|
|
53
|
-
export type YouTrackOperations = {
|
|
54
|
-
verifyToken(): MaybePromise<LegacyValue>;
|
|
55
|
-
context(input: Record<string, unknown>): MaybePromise<LegacyValue>;
|
|
56
|
-
parseDuration(text: string, workspaceRoot: string): MaybePromise<LegacyValue>;
|
|
57
|
-
postComment(issueId: string, markdown: string, workspaceRoot?: string): MaybePromise<LegacyValue>;
|
|
58
|
-
logTime(input: Record<string, unknown>): MaybePromise<LegacyValue>;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const unwrap = (value: LegacyValue) => {
|
|
62
|
-
if (!value) return {};
|
|
63
|
-
if (value.error) throw new Error(String(value.error));
|
|
64
|
-
if (value.ok === false) throw new Error(String(value.error ?? "YouTrack operation failed"));
|
|
65
|
-
return (value.data as Record<string, unknown> | undefined) ?? value;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const defaultOperations: YouTrackOperations = {
|
|
69
|
-
verifyToken: () => unwrap(verifyYouTrackToken()),
|
|
70
|
-
context: (input) => legacyContext(input as never),
|
|
71
|
-
parseDuration: (text, workspaceRoot) => legacyParseDuration(text, workspaceRoot),
|
|
72
|
-
postComment: (issueId, markdown, workspaceRoot) => legacyPostUpdate({
|
|
73
|
-
confirmed: true, issueId, markdown, workspace_root: workspaceRoot,
|
|
74
|
-
} as never),
|
|
75
|
-
logTime: (input) => legacyLogTime(input as never),
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
type PostInput = {
|
|
79
|
-
confirmed: boolean;
|
|
80
|
-
issueId: string;
|
|
81
|
-
markdown: string;
|
|
82
|
-
minutes?: number;
|
|
83
|
-
workspace_root?: string;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
type PostData = {
|
|
87
|
-
issueId: string;
|
|
88
|
-
postedComment: boolean;
|
|
89
|
-
loggedMinutes: number;
|
|
90
|
-
outcome?: "unknown" | "not_applied";
|
|
91
|
-
instructions?: string;
|
|
92
|
-
retry?: "workflow_youtrack_post" | "workflow_youtrack_log_time";
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const notApplied = (value: LegacyValue): value is NotApplied =>
|
|
96
|
-
value?.ok === false && value.outcome === "not_applied";
|
|
97
|
-
|
|
98
|
-
const withWriteFlag = async <T>(fn: () => Promise<T>): Promise<T> => {
|
|
99
|
-
const previous = process.env.WORKFLOW_YT_WRITE;
|
|
100
|
-
process.env.WORKFLOW_YT_WRITE = "1";
|
|
101
|
-
try {
|
|
102
|
-
return await fn();
|
|
103
|
-
} finally {
|
|
104
|
-
if (previous === undefined) delete process.env.WORKFLOW_YT_WRITE;
|
|
105
|
-
else process.env.WORKFLOW_YT_WRITE = previous;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export async function postUpdate(
|
|
110
|
-
input: PostInput,
|
|
111
|
-
operations: Pick<YouTrackOperations, "postComment" | "logTime"> = defaultOperations,
|
|
112
|
-
): Promise<Result<PostData>> {
|
|
113
|
-
if (input.confirmed !== true) return fail("confirmed: true required");
|
|
114
|
-
if (!ISSUE_RE.test(input.issueId)) return fail("invalid issueId");
|
|
115
|
-
if (!input.markdown?.trim()) return fail("markdown required");
|
|
116
|
-
if (input.minutes != null && input.minutes <= 0) return fail("minutes must be positive");
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
const comment = await operations.postComment(input.issueId, input.markdown, input.workspace_root);
|
|
120
|
-
if (notApplied(comment)) return fail(comment.error, {
|
|
121
|
-
issueId: input.issueId, postedComment: false, loggedMinutes: 0,
|
|
122
|
-
outcome: "not_applied", retry: "workflow_youtrack_post",
|
|
123
|
-
});
|
|
124
|
-
unwrap(comment);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
return fail(message(error), {
|
|
127
|
-
issueId: input.issueId, postedComment: false, loggedMinutes: 0, outcome: "unknown",
|
|
128
|
-
instructions: "Check YouTrack comments manually; do not retry while the outcome is unknown.",
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (input.minutes != null) {
|
|
133
|
-
try {
|
|
134
|
-
const time = await operations.logTime({
|
|
135
|
-
issueId: input.issueId,
|
|
136
|
-
minutes: input.minutes,
|
|
137
|
-
text: "workit update",
|
|
138
|
-
workspace_root: input.workspace_root,
|
|
139
|
-
});
|
|
140
|
-
if (notApplied(time)) return fail(time.error, {
|
|
141
|
-
issueId: input.issueId, postedComment: true, loggedMinutes: 0,
|
|
142
|
-
outcome: "not_applied", retry: "workflow_youtrack_log_time",
|
|
143
|
-
});
|
|
144
|
-
unwrap(time);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
return fail(message(error), {
|
|
147
|
-
issueId: input.issueId, postedComment: true, loggedMinutes: 0, outcome: "unknown",
|
|
148
|
-
instructions: "Check YouTrack time entries manually; do not retry while the outcome is unknown.",
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return ok({
|
|
154
|
-
issueId: input.issueId,
|
|
155
|
-
postedComment: true,
|
|
156
|
-
loggedMinutes: input.minutes ?? 0,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export async function logTimeUpdate(
|
|
161
|
-
input: Record<string, unknown>,
|
|
162
|
-
operation: Pick<YouTrackOperations, "logTime"> = defaultOperations,
|
|
163
|
-
): Promise<Result<Record<string, unknown>>> {
|
|
164
|
-
try {
|
|
165
|
-
const value = await operation.logTime(input);
|
|
166
|
-
if (notApplied(value)) return fail(value.error, {
|
|
167
|
-
issueId: input.issueId, loggedMinutes: 0, outcome: "not_applied",
|
|
168
|
-
retry: "workflow_youtrack_log_time",
|
|
169
|
-
});
|
|
170
|
-
return ok(unwrap(value));
|
|
171
|
-
} catch (error) {
|
|
172
|
-
return fail(message(error), {
|
|
173
|
-
issueId: input.issueId, loggedMinutes: 0, outcome: "unknown",
|
|
174
|
-
instructions: "Check YouTrack time entries manually; do not retry while the outcome is unknown.",
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export function normalizeContext(value: LegacyValue, mode?: string): LegacyValue {
|
|
180
|
-
if (!value || mode !== "meetings") return value;
|
|
181
|
-
const config = value.config as Record<string, unknown> | undefined;
|
|
182
|
-
const issue = String(config?.meetingIssue || "IRPT-12");
|
|
183
|
-
const { meetingIssues: _meetingIssues, ...singleMeetingConfig } = config ?? {};
|
|
184
|
-
const options = Array.isArray(value.meetingOptions)
|
|
185
|
-
? value.meetingOptions as Array<Record<string, unknown>>
|
|
186
|
-
: [];
|
|
187
|
-
const selected = options.find((option) => option.issue === issue) ?? {
|
|
188
|
-
key: "general", issue, label: issue, workItemText: "Reuniones",
|
|
189
|
-
};
|
|
190
|
-
return {
|
|
191
|
-
...value,
|
|
192
|
-
config: singleMeetingConfig,
|
|
193
|
-
meetingOptions: [selected],
|
|
194
|
-
requiresMeetingChoice: false,
|
|
195
|
-
issueId: issue,
|
|
196
|
-
workItemText: selected.workItemText ?? "Reuniones",
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
const standardResult = (value: LegacyValue, token = "") => {
|
|
201
|
-
try {
|
|
202
|
-
const data = unwrap(value);
|
|
203
|
-
const { ok: _legacyOk, ...normalized } = data;
|
|
204
|
-
return ok(normalized);
|
|
205
|
-
} catch (error) {
|
|
206
|
-
return fail(redact(message(error), token));
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const invoke = async (operation: () => MaybePromise<LegacyValue>, token = "") => {
|
|
211
|
-
try {
|
|
212
|
-
return output(standardResult(await operation(), token));
|
|
213
|
-
} catch (error) {
|
|
214
|
-
return output(fail(redact(message(error), token)));
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
const credentials = () => readCredentials();
|
|
219
|
-
const configGap = () => {
|
|
220
|
-
const { missing } = describeConfigGaps(["youtrack_json", "youtrack_token"]);
|
|
221
|
-
return missing.length > 0 ? output(fail(configGuardError(missing))) : null;
|
|
222
|
-
};
|
|
223
|
-
const requireConfirmed = (confirmed: boolean) => confirmed === true
|
|
224
|
-
? null
|
|
225
|
-
: output(fail("confirmed: true required"));
|
|
226
|
-
|
|
227
|
-
const rejectedTimeInput = (issueId: string, minutes: number) => {
|
|
228
|
-
const error = !ISSUE_RE.test(issueId)
|
|
229
|
-
? "invalid issueId"
|
|
230
|
-
: !Number.isFinite(minutes) || minutes <= 0
|
|
231
|
-
? "minutes must be positive"
|
|
232
|
-
: null;
|
|
233
|
-
return error ? output(fail(error, {
|
|
234
|
-
issueId, loggedMinutes: 0, outcome: "not_applied",
|
|
235
|
-
retry: "workflow_youtrack_log_time",
|
|
236
|
-
instructions: "Correct the invalid input, then retry workflow_youtrack_log_time once.",
|
|
237
|
-
})) : null;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
export function createYouTrackTools(operations: YouTrackOperations = defaultOperations) {
|
|
241
|
-
return {
|
|
242
|
-
workflow_youtrack_verify_token: tool({
|
|
243
|
-
description: "Verify the configured YouTrack token with a read-only request",
|
|
244
|
-
args: {},
|
|
245
|
-
execute: async () => {
|
|
246
|
-
let token = "";
|
|
247
|
-
try { token = credentials().token; } catch (error) {
|
|
248
|
-
const gap = configGap();
|
|
249
|
-
if (gap) return gap;
|
|
250
|
-
return output(fail(message(error)));
|
|
251
|
-
}
|
|
252
|
-
return invoke(() => operations.verifyToken(), token);
|
|
253
|
-
},
|
|
254
|
-
}),
|
|
255
|
-
workflow_youtrack_parse_issue: tool({
|
|
256
|
-
description: "Parse an existing YouTrack issue URL or id",
|
|
257
|
-
args: { issue_ref: tool.schema.string() },
|
|
258
|
-
execute: async ({ issue_ref }) => invoke(() => parseIssueRef(issue_ref)),
|
|
259
|
-
}),
|
|
260
|
-
workflow_youtrack_context: tool({
|
|
261
|
-
description: "Load YouTrack context for the configured meeting issue or an existing task issue",
|
|
262
|
-
args: {
|
|
263
|
-
mode: tool.schema.enum(["meetings", "task"]).optional(),
|
|
264
|
-
issue_id: tool.schema.string().optional(),
|
|
265
|
-
issue_url: tool.schema.string().optional(),
|
|
266
|
-
issue_ref: tool.schema.string().optional(),
|
|
267
|
-
spec_path: tool.schema.string().optional(),
|
|
268
|
-
plan_path: tool.schema.string().optional(),
|
|
269
|
-
},
|
|
270
|
-
execute: async (input, context) => {
|
|
271
|
-
try {
|
|
272
|
-
for (const candidate of [input.spec_path, input.plan_path].filter(Boolean) as string[]) {
|
|
273
|
-
if (path.isAbsolute(candidate)) throw new Error("path must be repository-relative");
|
|
274
|
-
resolveInside(context.directory, candidate);
|
|
275
|
-
}
|
|
276
|
-
} catch (error) {
|
|
277
|
-
const detail = message(error);
|
|
278
|
-
return output(fail(detail.includes("repository-relative") ? detail : `path must be repository-relative: ${detail}`));
|
|
279
|
-
}
|
|
280
|
-
let token = "";
|
|
281
|
-
try { token = credentials().token; } catch (error) {
|
|
282
|
-
const gap = configGap();
|
|
283
|
-
if (gap) return gap;
|
|
284
|
-
return output(fail(message(error)));
|
|
285
|
-
}
|
|
286
|
-
return invoke(async () => normalizeContext(
|
|
287
|
-
await operations.context({ ...input, workspace_root: context.directory }), input.mode,
|
|
288
|
-
), token);
|
|
289
|
-
},
|
|
290
|
-
}),
|
|
291
|
-
workflow_youtrack_parse_duration: tool({
|
|
292
|
-
description: "Parse duration text into integer minutes",
|
|
293
|
-
args: { text: tool.schema.string() },
|
|
294
|
-
execute: async ({ text }, context) => invoke(() => operations.parseDuration(text, context.directory)),
|
|
295
|
-
}),
|
|
296
|
-
workflow_youtrack_draft: tool({
|
|
297
|
-
description: "Build an es-CL update comment without posting it",
|
|
298
|
-
args: {
|
|
299
|
-
issueId: tool.schema.string(),
|
|
300
|
-
userNotes: tool.schema.string(),
|
|
301
|
-
greeting: tool.schema.string().optional(),
|
|
302
|
-
projectName: tool.schema.string().optional(),
|
|
303
|
-
includeProjectOpener: tool.schema.boolean().optional(),
|
|
304
|
-
includeFacts: tool.schema.boolean().optional(),
|
|
305
|
-
facts: tool.schema.object({
|
|
306
|
-
progress_excerpt: tool.schema.array(tool.schema.string()).optional(),
|
|
307
|
-
git_commits: tool.schema.array(tool.schema.string()).optional(),
|
|
308
|
-
}).optional(),
|
|
309
|
-
},
|
|
310
|
-
execute: async (input) => invoke(() => legacyBuildDraft(input as never)),
|
|
311
|
-
}),
|
|
312
|
-
workflow_youtrack_log_time: tool({
|
|
313
|
-
description: "Log confirmed time on an existing YouTrack issue without posting a comment",
|
|
314
|
-
args: {
|
|
315
|
-
confirmed: tool.schema.boolean(),
|
|
316
|
-
issueId: tool.schema.string(),
|
|
317
|
-
minutes: tool.schema.number(),
|
|
318
|
-
text: tool.schema.string().optional(),
|
|
319
|
-
dateMs: tool.schema.number().optional(),
|
|
320
|
-
},
|
|
321
|
-
execute: async ({ confirmed, ...input }, context: ToolContext) => {
|
|
322
|
-
const rejected = requireConfirmed(confirmed);
|
|
323
|
-
if (rejected) return rejected;
|
|
324
|
-
const invalid = rejectedTimeInput(input.issueId, input.minutes);
|
|
325
|
-
if (invalid) return invalid;
|
|
326
|
-
let token = "";
|
|
327
|
-
try { token = credentials().token; } catch (error) {
|
|
328
|
-
const gap = configGap();
|
|
329
|
-
if (gap) return gap;
|
|
330
|
-
return output(fail(message(error)));
|
|
331
|
-
}
|
|
332
|
-
const result = await withWriteFlag(() =>
|
|
333
|
-
logTimeUpdate({ ...input, workspace_root: context.directory }, operations));
|
|
334
|
-
return output(result.ok ? result : { ...result, error: redact(result.error, token) });
|
|
335
|
-
},
|
|
336
|
-
}),
|
|
337
|
-
workflow_youtrack_post: tool({
|
|
338
|
-
description: "Post a confirmed es-CL comment, then optionally log time",
|
|
339
|
-
args: {
|
|
340
|
-
confirmed: tool.schema.boolean(),
|
|
341
|
-
issueId: tool.schema.string(),
|
|
342
|
-
markdown: tool.schema.string(),
|
|
343
|
-
minutes: tool.schema.number().optional(),
|
|
344
|
-
},
|
|
345
|
-
execute: async (input, context: ToolContext) => {
|
|
346
|
-
const rejected = requireConfirmed(input.confirmed);
|
|
347
|
-
if (rejected) return rejected;
|
|
348
|
-
let token = "";
|
|
349
|
-
try { token = credentials().token; } catch (error) {
|
|
350
|
-
const gap = configGap();
|
|
351
|
-
if (gap) return gap;
|
|
352
|
-
return output(fail(message(error)));
|
|
353
|
-
}
|
|
354
|
-
const result = await withWriteFlag(() =>
|
|
355
|
-
postUpdate({ ...input, workspace_root: context.directory }, operations));
|
|
356
|
-
return output(result.ok ? result : { ...result, error: redact(result.error, token) });
|
|
357
|
-
},
|
|
358
|
-
}),
|
|
359
|
-
};
|
|
360
|
-
}
|