@kw-yeh/vibeflow 4.8.0 → 4.10.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/README.md +2 -0
- package/dist/vibeflow.mjs +480 -67
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/{MCpYTSYna3Ll-2lXwbETR → BitLlFC-3hSS2mIvjdaGE}/_buildManifest.js +1 -1
- package/dist/web/_next/static/chunks/{02e71eac.0250f42685eae210.js → 02e71eac.a73ad9a7a96ae1e3.js} +1 -1
- package/dist/web/_next/static/chunks/5695.b97c93788c76800e.js +1 -0
- package/dist/web/_next/static/chunks/674-f8f6187ae7148f97.js +9 -0
- package/dist/web/_next/static/chunks/pages/{_app-496c474d7f14dd07.js → _app-ab090e0b95efc7ae.js} +1 -1
- package/dist/web/_next/static/chunks/pages/home-b06aaed2bc1f9020.js +12 -0
- package/dist/web/_next/static/chunks/{webpack-c8aa5df0425f6b7b.js → webpack-0eacf76ebd7d56d5.js} +1 -1
- package/dist/web/_next/static/css/69a4a772468eeb68.css +2 -0
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/5695.45dd981e66074a89.js +0 -1
- package/dist/web/_next/static/chunks/7451-e9f74fece50504ed.js +0 -9
- package/dist/web/_next/static/chunks/pages/home-ead2bccf81dc3d85.js +0 -12
- package/dist/web/_next/static/css/0e63f58e447f85ed.css +0 -2
- /package/dist/web/_next/static/{MCpYTSYna3Ll-2lXwbETR → BitLlFC-3hSS2mIvjdaGE}/_ssgManifest.js +0 -0
package/dist/vibeflow.mjs
CHANGED
|
@@ -621,22 +621,25 @@ function assembleCommand(agent, systemPrompt, prompt, model, effort, opts, workt
|
|
|
621
621
|
const combined = [systemPrompt, prompt].filter(Boolean).join("\n\n");
|
|
622
622
|
const codexEffortFlag = effort ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
|
|
623
623
|
const codexHome = opts?.library ? `CODEX_HOME=${shellQuote(toShellPath(opts.library.codexHome))} ` : "";
|
|
624
|
-
const
|
|
625
|
-
|
|
624
|
+
const args = [
|
|
625
|
+
codexAutoFlag(opts?.autoMode).trim(),
|
|
626
|
+
codexEffortFlag.trim(),
|
|
627
|
+
model ? `--model ${model}` : "",
|
|
628
|
+
combined ? shellQuote(combined) : ""
|
|
629
|
+
].filter(Boolean);
|
|
630
|
+
cmd = `${codexHome}${["codex", ...args].join(" ")}\r`;
|
|
626
631
|
}
|
|
627
632
|
if (cmd.length > 2e5) console.warn(`[VibeFlow] launch command is ${cmd.length} bytes \u2014 approaching ARG_MAX`);
|
|
628
633
|
return cmd;
|
|
629
634
|
}
|
|
630
|
-
function normalizeModel(agent, model) {
|
|
631
|
-
return LEGACY_MODEL_FALLBACKS[agent]?.[model] ?? model;
|
|
632
|
-
}
|
|
633
635
|
function taskAgent(task) {
|
|
634
636
|
const agent = task.agentCli;
|
|
635
637
|
return agent && agent in AGENT_NAMES ? agent : "claude";
|
|
636
638
|
}
|
|
637
639
|
function taskModel(task) {
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
+
const model = task.model?.trim();
|
|
641
|
+
if (!model || RETIRED_MODELS[taskAgent(task)]?.includes(model)) return void 0;
|
|
642
|
+
return model;
|
|
640
643
|
}
|
|
641
644
|
function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
|
|
642
645
|
const agent = taskAgent(task);
|
|
@@ -661,7 +664,7 @@ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
|
|
|
661
664
|
workspacePath
|
|
662
665
|
);
|
|
663
666
|
}
|
|
664
|
-
var ARTIFACTS_DIR_SUFFIX, SCRATCH_DIR_NAME, ARTIFACTS_FALLBACK_DIR, AUTO_PERMISSION_MODE, SUBAGENTS_DIR2, PROGRESS_EVENTS_DIR, TODO_TOOLS, AGENT_NAMES,
|
|
667
|
+
var ARTIFACTS_DIR_SUFFIX, SCRATCH_DIR_NAME, ARTIFACTS_FALLBACK_DIR, AUTO_PERMISSION_MODE, SUBAGENTS_DIR2, PROGRESS_EVENTS_DIR, TODO_TOOLS, AGENT_NAMES, RETIRED_MODELS;
|
|
665
668
|
var init_launch = __esm({
|
|
666
669
|
"packages/core/src/launch.ts"() {
|
|
667
670
|
"use strict";
|
|
@@ -676,15 +679,8 @@ var init_launch = __esm({
|
|
|
676
679
|
claude: "Claude Code",
|
|
677
680
|
codex: "Codex CLI"
|
|
678
681
|
};
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
codex: "gpt-5.5"
|
|
682
|
-
};
|
|
683
|
-
LEGACY_MODEL_FALLBACKS = {
|
|
684
|
-
codex: {
|
|
685
|
-
"gpt-5-codex": "gpt-5.5",
|
|
686
|
-
"gpt-5": "gpt-5.5"
|
|
687
|
-
}
|
|
682
|
+
RETIRED_MODELS = {
|
|
683
|
+
codex: ["gpt-5-codex", "gpt-5"]
|
|
688
684
|
};
|
|
689
685
|
}
|
|
690
686
|
});
|
|
@@ -790,6 +786,9 @@ var init_attachments = __esm({
|
|
|
790
786
|
});
|
|
791
787
|
|
|
792
788
|
// packages/core/src/effort.ts
|
|
789
|
+
function modelFor(models, modelId) {
|
|
790
|
+
return modelId ? models?.find((m) => m.id === modelId) : models?.find((m) => m.isDefault);
|
|
791
|
+
}
|
|
793
792
|
function isAgentEffort(value) {
|
|
794
793
|
return typeof value === "string" && AGENT_EFFORTS.includes(value);
|
|
795
794
|
}
|
|
@@ -1104,7 +1103,7 @@ import fs22 from "fs";
|
|
|
1104
1103
|
import os9 from "os";
|
|
1105
1104
|
import path26 from "path";
|
|
1106
1105
|
import React, { useEffect, useRef, useState } from "react";
|
|
1107
|
-
import { Box, Text, render, useApp, useInput } from "ink";
|
|
1106
|
+
import { Box, Text, render as render2, useApp, useInput } from "ink";
|
|
1108
1107
|
function newFormState(defaultAutoMode, agents) {
|
|
1109
1108
|
const agentCli = agents && agents.length > 0 && !agents.some((a) => a.id === "claude") ? agents[0].id : "claude";
|
|
1110
1109
|
return {
|
|
@@ -1174,8 +1173,7 @@ function modelOptions(ctx, agentCli, current) {
|
|
|
1174
1173
|
return options;
|
|
1175
1174
|
}
|
|
1176
1175
|
function effortOptions(ctx, agentCli, model) {
|
|
1177
|
-
|
|
1178
|
-
return selectableEfforts(ctx.models[agentCli]?.find((m) => m.id === id));
|
|
1176
|
+
return selectableEfforts(modelFor(ctx.models[agentCli], taskModel({ agentCli, model })));
|
|
1179
1177
|
}
|
|
1180
1178
|
function fitEffort(ctx, s) {
|
|
1181
1179
|
const effort = clampEffort(s.effort, effortOptions(ctx, s.agentCli, s.model)) ?? s.effort;
|
|
@@ -1417,7 +1415,8 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1417
1415
|
case "model": {
|
|
1418
1416
|
if (modelOptions(ctx, s.agentCli, s.model).length <= 1) return "\u7121\u6CD5\u53D6\u5F97 model list\uFF0C\u5C07\u4F7F\u7528\u9810\u8A2D model";
|
|
1419
1417
|
const listed = ctx.models[s.agentCli]?.find((m) => m.id === s.model);
|
|
1420
|
-
|
|
1418
|
+
const fallback = ctx.models[s.agentCli]?.find((m) => m.isDefault)?.label;
|
|
1419
|
+
return `\u2039 ${s.model ? listed?.label ?? s.model : fallback ? `\u4F7F\u7528\u9810\u8A2D model\uFF08${fallback}\uFF09` : "\u4F7F\u7528\u9810\u8A2D model"} \u203A`;
|
|
1421
1420
|
}
|
|
1422
1421
|
case "submit":
|
|
1423
1422
|
return busy ?? (s.mode === "new" ? "\uFF3B \u5EFA\u7ACB\u4EFB\u52D9 \uFF3D" : "\uFF3B \u5132\u5B58\u8B8A\u66F4 \uFF3D");
|
|
@@ -1478,7 +1477,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1478
1477
|
function showForm(api, ctx, initial, pristine, initialFocus = 0, initialMessage) {
|
|
1479
1478
|
return new Promise((resolve) => {
|
|
1480
1479
|
let result = { type: "cancel" };
|
|
1481
|
-
const instance =
|
|
1480
|
+
const instance = render2(
|
|
1482
1481
|
h(FormView, { api, ctx, initial, initialFocus, initialMessage, pristine, onResult: (r) => result = r }),
|
|
1483
1482
|
{ exitOnCtrlC: false }
|
|
1484
1483
|
);
|
|
@@ -1561,7 +1560,7 @@ __export(src_exports, {
|
|
|
1561
1560
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1562
1561
|
import path27 from "path";
|
|
1563
1562
|
import React2, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1564
|
-
import { Box as Box2, Text as Text2, render as
|
|
1563
|
+
import { Box as Box2, Text as Text2, render as render3, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
|
|
1565
1564
|
function findDetachKey(text) {
|
|
1566
1565
|
const plain = text.indexOf(DETACH_KEY);
|
|
1567
1566
|
const m = /\x1b\[\d+;\d+;29;1;\d+;\d+_/.exec(text);
|
|
@@ -1829,7 +1828,7 @@ function BoardView({ opts, initial, initialMessage, onAction }) {
|
|
|
1829
1828
|
function showBoard(opts, initial, initialMessage) {
|
|
1830
1829
|
return new Promise((resolve) => {
|
|
1831
1830
|
let result = { action: { type: "quit" }, sel: initial };
|
|
1832
|
-
const instance =
|
|
1831
|
+
const instance = render3(
|
|
1833
1832
|
h2(BoardView, {
|
|
1834
1833
|
opts,
|
|
1835
1834
|
initial,
|
|
@@ -2465,6 +2464,13 @@ function createBridge(t) {
|
|
|
2465
2464
|
logoutGithubAuth: () => t.invoke("settings:logoutGithubAuth"),
|
|
2466
2465
|
/** Open Issues/PRs of the board's GitHub repos that involve me; cached unless `force`. */
|
|
2467
2466
|
getGithubInbox: (opts) => t.invoke("github:inbox", opts ?? {}),
|
|
2467
|
+
getJiraAuthStatus: () => t.invoke("jira:authStatus"),
|
|
2468
|
+
startJiraAuthLogin: () => t.invoke("settings:startJiraAuthLogin"),
|
|
2469
|
+
cancelJiraAuthLogin: () => t.invoke("settings:cancelJiraAuthLogin"),
|
|
2470
|
+
inputJiraAuthLogin: (input) => t.invoke("settings:inputJiraAuthLogin", input),
|
|
2471
|
+
logoutJiraAuth: () => t.invoke("settings:logoutJiraAuth"),
|
|
2472
|
+
onJiraAuthEvent: (callback) => t.on("jira-auth:event", (event) => callback(event)),
|
|
2473
|
+
getJiraInbox: (opts) => t.invoke("jira:inbox", opts ?? {}),
|
|
2468
2474
|
/** The Issue/PR each card belongs to, keyed by task id. */
|
|
2469
2475
|
getGithubTaskLinks: (opts) => t.invoke("github:taskLinks", opts ?? {}),
|
|
2470
2476
|
onGithubAuthEvent: (callback) => {
|
|
@@ -2964,10 +2970,11 @@ function parseGithubRepo(remoteUrl) {
|
|
|
2964
2970
|
return match ? `${match[1]}/${match[2]}` : null;
|
|
2965
2971
|
}
|
|
2966
2972
|
var REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
2973
|
+
var isRepoName = (repo) => REPO_RE.test(repo) && repo.split("/").every((part) => part !== "." && part !== "..");
|
|
2967
2974
|
function isGithubRef(value) {
|
|
2968
2975
|
if (!value || typeof value !== "object") return false;
|
|
2969
2976
|
const v = value;
|
|
2970
|
-
return (v.kind === "issue" || v.kind === "pr") && typeof v.repo === "string" &&
|
|
2977
|
+
return (v.kind === "issue" || v.kind === "pr") && typeof v.repo === "string" && isRepoName(v.repo) && typeof v.number === "number" && Number.isInteger(v.number) && v.number > 0 && typeof v.url === "string" && v.url.startsWith(`https://github.com/${v.repo}/`);
|
|
2971
2978
|
}
|
|
2972
2979
|
var ITEM_FIELDS = "number,title,url,body,author,assignees,labels,milestone,createdAt";
|
|
2973
2980
|
var ISSUE_FIELDS = `${ITEM_FIELDS},issueType`;
|
|
@@ -2975,6 +2982,7 @@ var PR_FIELDS = `${ITEM_FIELDS},state,isDraft,reviewDecision,headRefName,baseRef
|
|
|
2975
2982
|
var PR_INDEX_FIELDS = "number,url,state,isDraft,headRefName,closingIssuesReferences";
|
|
2976
2983
|
var LIST_LIMIT = "50";
|
|
2977
2984
|
var PR_INDEX_LIMIT = "100";
|
|
2985
|
+
var SEARCH_LIMIT = "1000";
|
|
2978
2986
|
async function runJson(run3, args) {
|
|
2979
2987
|
const out = await run3(args);
|
|
2980
2988
|
const parsed = JSON.parse(out || "[]");
|
|
@@ -3058,18 +3066,47 @@ async function listPrs(run3, repo, filter) {
|
|
|
3058
3066
|
return rows.map((r) => toPr(r, repo));
|
|
3059
3067
|
}
|
|
3060
3068
|
async function fetchRepoItems(run3, repo) {
|
|
3061
|
-
const [assignedIssues, authoredIssues, assignedPrs, authoredPrs, reviewPrs] = await Promise.all([
|
|
3069
|
+
const [assignedIssues, authoredIssues, assignedPrs, authoredPrs, reviewPrs, reviewedPrs] = await Promise.all([
|
|
3062
3070
|
listIssues(run3, repo, ["--assignee", "@me"]),
|
|
3063
3071
|
listIssues(run3, repo, ["--author", "@me"]),
|
|
3064
3072
|
listPrs(run3, repo, ["--assignee", "@me"]),
|
|
3065
3073
|
listPrs(run3, repo, ["--author", "@me"]),
|
|
3066
|
-
listPrs(run3, repo, ["--search", "review-requested:@me"])
|
|
3074
|
+
listPrs(run3, repo, ["--search", "review-requested:@me"]),
|
|
3075
|
+
listPrs(run3, repo, ["--search", "reviewed-by:@me"])
|
|
3067
3076
|
]);
|
|
3068
3077
|
return {
|
|
3069
3078
|
issues: mergeItems([assignedIssues, authoredIssues]),
|
|
3070
|
-
prs: mergeItems([assignedPrs, authoredPrs, reviewPrs])
|
|
3079
|
+
prs: mergeItems([assignedPrs, authoredPrs, reviewPrs, reviewedPrs])
|
|
3071
3080
|
};
|
|
3072
3081
|
}
|
|
3082
|
+
async function findRelatedRepos(run3) {
|
|
3083
|
+
const searches = [
|
|
3084
|
+
["issues", ["--assignee", "@me"]],
|
|
3085
|
+
["issues", ["--author", "@me"]],
|
|
3086
|
+
["prs", ["--assignee", "@me"]],
|
|
3087
|
+
["prs", ["--author", "@me"]],
|
|
3088
|
+
["prs", ["--review-requested", "@me"]],
|
|
3089
|
+
["prs", ["--reviewed-by", "@me"]]
|
|
3090
|
+
];
|
|
3091
|
+
const results = await Promise.all(searches.map(([kind, filter]) => runJson(run3, [
|
|
3092
|
+
"search",
|
|
3093
|
+
kind,
|
|
3094
|
+
"--state",
|
|
3095
|
+
"open",
|
|
3096
|
+
"--limit",
|
|
3097
|
+
SEARCH_LIMIT,
|
|
3098
|
+
...filter,
|
|
3099
|
+
"--json",
|
|
3100
|
+
"repository"
|
|
3101
|
+
])));
|
|
3102
|
+
const found2 = /* @__PURE__ */ new Set();
|
|
3103
|
+
for (const rows of results) for (const row of rows) {
|
|
3104
|
+
const repository = row.repository;
|
|
3105
|
+
const name = repository?.nameWithOwner;
|
|
3106
|
+
if (typeof name === "string" && isRepoName(name)) found2.add(name);
|
|
3107
|
+
}
|
|
3108
|
+
return Array.from(found2).sort();
|
|
3109
|
+
}
|
|
3073
3110
|
async function fetchPrIndex(run3, repo) {
|
|
3074
3111
|
const rows = await runJson(run3, [
|
|
3075
3112
|
"pr",
|
|
@@ -3166,6 +3203,7 @@ function createGithubService(options) {
|
|
|
3166
3203
|
const items = /* @__PURE__ */ new Map();
|
|
3167
3204
|
const indexes = /* @__PURE__ */ new Map();
|
|
3168
3205
|
const repos = /* @__PURE__ */ new Map();
|
|
3206
|
+
const discovery = /* @__PURE__ */ new Map();
|
|
3169
3207
|
function cached2(map, key, force, load) {
|
|
3170
3208
|
const hit = map.get(key);
|
|
3171
3209
|
if (hit && !force && now() - hit.at < GITHUB_CACHE_TTL_MS) return hit;
|
|
@@ -3196,11 +3234,12 @@ function createGithubService(options) {
|
|
|
3196
3234
|
const byProject = await projectRepos(board, force);
|
|
3197
3235
|
const repoProject = /* @__PURE__ */ new Map();
|
|
3198
3236
|
for (const [project, repo] of byProject) if (!repoProject.has(repo)) repoProject.set(repo, project);
|
|
3199
|
-
const
|
|
3237
|
+
const discovered = cached2(discovery, "related", force, () => findRelatedRepos(run3));
|
|
3238
|
+
const entries = (await discovered.value).map((repo) => [repo, repoProject.get(repo) ?? null]);
|
|
3200
3239
|
const results = await Promise.all(
|
|
3201
3240
|
entries.map(async ([repo, projectPath]) => {
|
|
3202
3241
|
const entry = cached2(items, repo, force, () => fetchRepoItems(run3, repo));
|
|
3203
|
-
const head = { repo, projectPath, projectName: basename(projectPath) };
|
|
3242
|
+
const head = { repo, projectPath, projectName: projectPath ? basename(projectPath) : repo };
|
|
3204
3243
|
try {
|
|
3205
3244
|
return { at: entry.at, inbox: { ...head, ...await entry.value } };
|
|
3206
3245
|
} catch (err) {
|
|
@@ -3211,7 +3250,7 @@ function createGithubService(options) {
|
|
|
3211
3250
|
return {
|
|
3212
3251
|
status: "ok",
|
|
3213
3252
|
...auth.login ? { login: auth.login } : {},
|
|
3214
|
-
fetchedAt:
|
|
3253
|
+
fetchedAt: Math.min(discovered.at, ...results.map((r) => r.at)),
|
|
3215
3254
|
repos: results.map((r) => r.inbox).sort((a, b) => a.repo.localeCompare(b.repo))
|
|
3216
3255
|
};
|
|
3217
3256
|
},
|
|
@@ -3654,10 +3693,10 @@ async function ignoredEntryFitsCopyBudget(src) {
|
|
|
3654
3693
|
const stat = await fs8.lstat(current);
|
|
3655
3694
|
if (stat.isSymbolicLink()) continue;
|
|
3656
3695
|
if (stat.isDirectory()) {
|
|
3657
|
-
const
|
|
3658
|
-
files +=
|
|
3696
|
+
const children2 = await fs8.readdir(current, { withFileTypes: true });
|
|
3697
|
+
files += children2.length;
|
|
3659
3698
|
if (files > MAX_IGNORED_COPY_FILES) return false;
|
|
3660
|
-
for (const child of
|
|
3699
|
+
for (const child of children2) stack.push(path12.join(current, child.name));
|
|
3661
3700
|
continue;
|
|
3662
3701
|
}
|
|
3663
3702
|
files += 1;
|
|
@@ -4210,7 +4249,7 @@ var AGENT_CLIS = [
|
|
|
4210
4249
|
bin: "claude",
|
|
4211
4250
|
name: "Claude Code",
|
|
4212
4251
|
models: [
|
|
4213
|
-
{ id: "sonnet", label: "Sonnet\uFF08\u5E73\u8861\
|
|
4252
|
+
{ id: "sonnet", label: "Sonnet\uFF08\u5E73\u8861\uFF09" },
|
|
4214
4253
|
{ id: "haiku", label: "Haiku\uFF08\u8F15\u91CF\uFF09" },
|
|
4215
4254
|
{ id: "opus", label: "Opus\uFF08\u6700\u5F37\uFF09" }
|
|
4216
4255
|
]
|
|
@@ -4220,7 +4259,7 @@ var AGENT_CLIS = [
|
|
|
4220
4259
|
bin: "codex",
|
|
4221
4260
|
name: "Codex CLI",
|
|
4222
4261
|
models: [
|
|
4223
|
-
{ id: "gpt-5.5", label: "GPT-5.5
|
|
4262
|
+
{ id: "gpt-5.5", label: "GPT-5.5" },
|
|
4224
4263
|
{ id: "gpt-5.4", label: "GPT-5.4" },
|
|
4225
4264
|
{ id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
|
|
4226
4265
|
]
|
|
@@ -4305,7 +4344,8 @@ async function createTaskFromInput(input) {
|
|
|
4305
4344
|
model: input.model || void 0,
|
|
4306
4345
|
effort: input.effort ?? DEFAULT_TASK_EFFORT,
|
|
4307
4346
|
autoMode: input.autoMode,
|
|
4308
|
-
...input.github ? { github: input.github } : {}
|
|
4347
|
+
...input.github ? { github: input.github } : {},
|
|
4348
|
+
...input.jira ? { jira: input.jira } : {}
|
|
4309
4349
|
};
|
|
4310
4350
|
try {
|
|
4311
4351
|
const board = store.get("board");
|
|
@@ -4789,17 +4829,17 @@ function listLibrary(root, builtinDir) {
|
|
|
4789
4829
|
const full = path14.join(dir, name);
|
|
4790
4830
|
if (wantsDirectory && !fs10.existsSync(path14.join(full, "SKILL.md"))) continue;
|
|
4791
4831
|
const key = entryKey(kind, name);
|
|
4792
|
-
const
|
|
4832
|
+
const record2 = index.entries[key] ?? {};
|
|
4793
4833
|
entries.push({
|
|
4794
4834
|
key,
|
|
4795
4835
|
kind,
|
|
4796
4836
|
name,
|
|
4797
4837
|
path: full,
|
|
4798
|
-
description: wantsDirectory ? readSkillDescription(path14.join(full, "SKILL.md")) :
|
|
4799
|
-
sourcePath:
|
|
4800
|
-
importedAt:
|
|
4801
|
-
enabled:
|
|
4802
|
-
builtin: wantsDirectory &&
|
|
4838
|
+
description: wantsDirectory ? readSkillDescription(path14.join(full, "SKILL.md")) : record2.description,
|
|
4839
|
+
sourcePath: record2.sourcePath,
|
|
4840
|
+
importedAt: record2.importedAt,
|
|
4841
|
+
enabled: record2.enabled !== false,
|
|
4842
|
+
builtin: wantsDirectory && record2.builtinHash && builtinDir !== void 0 ? builtinStatus(full, name, record2.builtinHash, builtinDir) : void 0
|
|
4803
4843
|
});
|
|
4804
4844
|
}
|
|
4805
4845
|
}
|
|
@@ -4874,8 +4914,8 @@ function markBuiltinDeleted(root, key) {
|
|
|
4874
4914
|
function deleteEntry(root, kind, name) {
|
|
4875
4915
|
fs10.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
|
|
4876
4916
|
const key = entryKey(kind, name);
|
|
4877
|
-
const
|
|
4878
|
-
if (
|
|
4917
|
+
const record2 = readIndex(root).entries[key];
|
|
4918
|
+
if (record2?.builtinHash || record2?.builtinDeleted) {
|
|
4879
4919
|
markBuiltinDeleted(root, key);
|
|
4880
4920
|
return;
|
|
4881
4921
|
}
|
|
@@ -5070,10 +5110,10 @@ function syncBuiltinSkills(root, builtinDir) {
|
|
|
5070
5110
|
}
|
|
5071
5111
|
function syncOne(root, builtinDir, name, result) {
|
|
5072
5112
|
const key = entryKey("skill", name);
|
|
5073
|
-
const
|
|
5074
|
-
if (
|
|
5113
|
+
const record2 = readIndex(root).entries[key] ?? {};
|
|
5114
|
+
if (record2.builtinDeleted) return;
|
|
5075
5115
|
const target = entryPath(root, "skill", name);
|
|
5076
|
-
if (!
|
|
5116
|
+
if (!record2.builtinHash) {
|
|
5077
5117
|
if (fs11.existsSync(target)) return;
|
|
5078
5118
|
annotate(root, key, {
|
|
5079
5119
|
builtinHash: installBuiltin(root, builtinDir, name),
|
|
@@ -5087,8 +5127,8 @@ function syncOne(root, builtinDir, name, result) {
|
|
|
5087
5127
|
markBuiltinDeleted(root, key);
|
|
5088
5128
|
return;
|
|
5089
5129
|
}
|
|
5090
|
-
if (hashSkillDir(path15.join(builtinDir, name)) ===
|
|
5091
|
-
if (hashSkillDir(target) !==
|
|
5130
|
+
if (hashSkillDir(path15.join(builtinDir, name)) === record2.builtinHash) return;
|
|
5131
|
+
if (hashSkillDir(target) !== record2.builtinHash) return;
|
|
5092
5132
|
annotate(root, key, { builtinHash: installBuiltin(root, builtinDir, name) });
|
|
5093
5133
|
result.updated.push(name);
|
|
5094
5134
|
}
|
|
@@ -5103,10 +5143,10 @@ function restoreBuiltinSkill(root, builtinDir, name) {
|
|
|
5103
5143
|
ensureLibrary(root);
|
|
5104
5144
|
const builtinHash = installBuiltin(root, builtinDir, name);
|
|
5105
5145
|
const index = readIndex(root);
|
|
5106
|
-
const
|
|
5107
|
-
delete
|
|
5108
|
-
delete
|
|
5109
|
-
index.entries[key] =
|
|
5146
|
+
const record2 = { ...index.entries[key], builtinHash, enabled: true, importedAt: Date.now() };
|
|
5147
|
+
delete record2.builtinDeleted;
|
|
5148
|
+
delete record2.sourcePath;
|
|
5149
|
+
index.entries[key] = record2;
|
|
5110
5150
|
writeIndex(root, index);
|
|
5111
5151
|
const entry = listLibrary(root, builtinDir).find((e) => e.key === key);
|
|
5112
5152
|
if (!entry) throw new Error(`\u9084\u539F\u5931\u6557\uFF1A${name}`);
|
|
@@ -5203,12 +5243,12 @@ function spawnJsonLines(bin, args, opts) {
|
|
|
5203
5243
|
}
|
|
5204
5244
|
});
|
|
5205
5245
|
proc.on("exit", (code2, signal) => {
|
|
5206
|
-
finished();
|
|
5207
5246
|
clearTimeout(timer);
|
|
5208
5247
|
clearTimeout(graceTimer);
|
|
5209
5248
|
const reason = signal ? `was killed (${signal})` : `exited with code ${code2}`;
|
|
5210
5249
|
fail2(new Error(`${bin} ${reason}${stderr.trim() ? `: ${stderr.trim()}` : ""}`));
|
|
5211
5250
|
});
|
|
5251
|
+
proc.on("close", finished);
|
|
5212
5252
|
proc.stdin.on("error", () => {
|
|
5213
5253
|
});
|
|
5214
5254
|
const kill = () => {
|
|
@@ -5267,6 +5307,9 @@ function parseClaudeInitialize(message) {
|
|
|
5267
5307
|
if (outer?.subtype === "error") throw new Error(`claude refused the handshake: ${String(outer.error ?? "unknown error")}`);
|
|
5268
5308
|
const body = outer && isRecord(outer.response) ? outer.response : null;
|
|
5269
5309
|
if (!body || !Array.isArray(body.models)) throw new Error("claude handshake has no models array");
|
|
5310
|
+
const defaultEntry = body.models.find((e) => isRecord(e) && e.value === "default");
|
|
5311
|
+
const defaultResolved = isRecord(defaultEntry) && typeof defaultEntry.resolvedModel === "string" ? defaultEntry.resolvedModel : void 0;
|
|
5312
|
+
let defaultMarked = false;
|
|
5270
5313
|
const seen = /* @__PURE__ */ new Set();
|
|
5271
5314
|
const models = [];
|
|
5272
5315
|
for (const entry of body.models) {
|
|
@@ -5277,7 +5320,9 @@ function parseClaudeInitialize(message) {
|
|
|
5277
5320
|
const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
|
|
5278
5321
|
const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
|
|
5279
5322
|
const levels = entry.supportsEffort === true && Array.isArray(entry.supportedEffortLevels) ? entry.supportedEffortLevels.filter(isAgentEffort) : [];
|
|
5280
|
-
|
|
5323
|
+
const isDefault = !defaultMarked && defaultResolved !== void 0 && entry.resolvedModel === defaultResolved;
|
|
5324
|
+
if (isDefault) defaultMarked = true;
|
|
5325
|
+
models.push({ id, label, ...description ? { description } : {}, efforts: levels, ...isDefault ? { isDefault } : {} });
|
|
5281
5326
|
}
|
|
5282
5327
|
if (models.length === 0) throw new Error("claude handshake lists no selectable models");
|
|
5283
5328
|
return models;
|
|
@@ -5338,7 +5383,13 @@ function parseCodexModelPage(result) {
|
|
|
5338
5383
|
const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
|
|
5339
5384
|
const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
|
|
5340
5385
|
const efforts = Array.isArray(entry.supportedReasoningEfforts) ? entry.supportedReasoningEfforts.map((e) => isRecord2(e) ? e.reasoningEffort : void 0).filter(isAgentEffort) : void 0;
|
|
5341
|
-
models.push({
|
|
5386
|
+
models.push({
|
|
5387
|
+
id,
|
|
5388
|
+
label,
|
|
5389
|
+
...description ? { description } : {},
|
|
5390
|
+
...efforts ? { efforts } : {},
|
|
5391
|
+
...entry.isDefault === true ? { isDefault: true } : {}
|
|
5392
|
+
});
|
|
5342
5393
|
}
|
|
5343
5394
|
const nextCursor = typeof result.nextCursor === "string" && result.nextCursor ? result.nextCursor : null;
|
|
5344
5395
|
return { models, nextCursor };
|
|
@@ -5385,6 +5436,7 @@ async function fetchCodexModels(clientVersion, open = openCodex) {
|
|
|
5385
5436
|
}
|
|
5386
5437
|
|
|
5387
5438
|
// packages/core/src/agent-models.ts
|
|
5439
|
+
init_effort();
|
|
5388
5440
|
function builtinModels(id) {
|
|
5389
5441
|
return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
|
|
5390
5442
|
}
|
|
@@ -5470,7 +5522,7 @@ function createModelCatalog(opts) {
|
|
|
5470
5522
|
for (const agent of AGENT_CLIS) void refresh(agent.id);
|
|
5471
5523
|
},
|
|
5472
5524
|
findModel(id, modelId) {
|
|
5473
|
-
return settled.get(id)?.models
|
|
5525
|
+
return modelFor(settled.get(id)?.models, modelId);
|
|
5474
5526
|
}
|
|
5475
5527
|
};
|
|
5476
5528
|
}
|
|
@@ -5632,6 +5684,334 @@ function startGitHubCliLogin(onEvent) {
|
|
|
5632
5684
|
});
|
|
5633
5685
|
}
|
|
5634
5686
|
|
|
5687
|
+
// packages/core/src/jira-adf.ts
|
|
5688
|
+
function record(value) {
|
|
5689
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
5690
|
+
}
|
|
5691
|
+
function children(node) {
|
|
5692
|
+
return Array.isArray(node.content) ? node.content : [];
|
|
5693
|
+
}
|
|
5694
|
+
function render(node, depth = 0) {
|
|
5695
|
+
if (typeof node === "string") return node;
|
|
5696
|
+
const item = record(node);
|
|
5697
|
+
if (!item) return "";
|
|
5698
|
+
const type = item.type;
|
|
5699
|
+
const body = () => children(item).map((child) => render(child, depth)).join("");
|
|
5700
|
+
const attrs = record(item.attrs);
|
|
5701
|
+
if (type === "text") {
|
|
5702
|
+
let value = typeof item.text === "string" ? item.text : "";
|
|
5703
|
+
for (const mark of Array.isArray(item.marks) ? item.marks : []) {
|
|
5704
|
+
const m = record(mark);
|
|
5705
|
+
if (m?.type === "strong") value = `**${value}**`;
|
|
5706
|
+
if (m?.type === "em") value = `*${value}*`;
|
|
5707
|
+
if (m?.type === "strike") value = `~~${value}~~`;
|
|
5708
|
+
if (m?.type === "code") value = `\`${value}\``;
|
|
5709
|
+
if (m?.type === "link" && typeof record(m.attrs)?.href === "string") value = `[${value}](${record(m.attrs)?.href})`;
|
|
5710
|
+
}
|
|
5711
|
+
return value;
|
|
5712
|
+
}
|
|
5713
|
+
if (type === "hardBreak") return " \n";
|
|
5714
|
+
if (type === "rule") return "\n---\n\n";
|
|
5715
|
+
if (type === "mention") return `@${String(attrs?.text ?? attrs?.id ?? "")}`;
|
|
5716
|
+
if (type === "emoji") return String(attrs?.shortName ?? "");
|
|
5717
|
+
if (type === "inlineCard" || type === "blockCard") return `[${String(attrs?.url ?? "\u9023\u7D50")}](${String(attrs?.url ?? "")})`;
|
|
5718
|
+
if (type === "media" || type === "mediaSingle") return "[\u9644\u4EF6]";
|
|
5719
|
+
if (type === "paragraph") return `${body()}
|
|
5720
|
+
|
|
5721
|
+
`;
|
|
5722
|
+
if (type === "heading") return `${"#".repeat(Math.min(6, Math.max(1, Number(attrs?.level) || 1)))} ${body()}
|
|
5723
|
+
|
|
5724
|
+
`;
|
|
5725
|
+
if (type === "blockquote") return body().trim().split("\n").map((line) => `> ${line}`).join("\n") + "\n\n";
|
|
5726
|
+
if (type === "codeBlock") return `
|
|
5727
|
+
\`\`\`${String(attrs?.language ?? "")}
|
|
5728
|
+
${children(item).map((child) => render(child)).join("").trimEnd()}
|
|
5729
|
+
\`\`\`
|
|
5730
|
+
|
|
5731
|
+
`;
|
|
5732
|
+
if (type === "bulletList" || type === "orderedList") {
|
|
5733
|
+
return children(item).map((child, index) => {
|
|
5734
|
+
const content = render(child, depth + 1).trim().replace(/\n\n/g, "\n");
|
|
5735
|
+
const prefix = type === "orderedList" ? `${index + 1}. ` : "- ";
|
|
5736
|
+
return `${" ".repeat(depth)}${prefix}${content}
|
|
5737
|
+
`;
|
|
5738
|
+
}).join("") + "\n";
|
|
5739
|
+
}
|
|
5740
|
+
if (type === "listItem") return body();
|
|
5741
|
+
if (type === "table") {
|
|
5742
|
+
const rows = children(item).map((row) => {
|
|
5743
|
+
const cells = children(record(row) ?? {}).map((cell) => render(cell).trim().replace(/\|/g, "\\|").replace(/\n+/g, " "));
|
|
5744
|
+
return `| ${cells.join(" | ")} |`;
|
|
5745
|
+
});
|
|
5746
|
+
if (rows.length === 0) return "";
|
|
5747
|
+
const width = children(record(children(item)[0]) ?? {}).length;
|
|
5748
|
+
rows.splice(1, 0, `| ${Array(width).fill("---").join(" | ")} |`);
|
|
5749
|
+
return rows.join("\n") + "\n\n";
|
|
5750
|
+
}
|
|
5751
|
+
return body();
|
|
5752
|
+
}
|
|
5753
|
+
function adfToMarkdown(value) {
|
|
5754
|
+
return render(value).trim();
|
|
5755
|
+
}
|
|
5756
|
+
|
|
5757
|
+
// packages/core/src/jira-auth.ts
|
|
5758
|
+
init_env();
|
|
5759
|
+
import { execFile as execFile8 } from "child_process";
|
|
5760
|
+
import { promisify as promisify5 } from "util";
|
|
5761
|
+
import * as nodePty4 from "node-pty";
|
|
5762
|
+
var pexec5 = promisify5(execFile8);
|
|
5763
|
+
var activeLogin2 = null;
|
|
5764
|
+
var activeCancel = null;
|
|
5765
|
+
function stripAnsi2(value) {
|
|
5766
|
+
return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x07/g, "");
|
|
5767
|
+
}
|
|
5768
|
+
function hasSingleSitePrompt(output) {
|
|
5769
|
+
const clean = stripAnsi2(output);
|
|
5770
|
+
if (!/select the site to login/i.test(clean) || !/enter submit/i.test(clean)) return false;
|
|
5771
|
+
const sites = new Set([...clean.matchAll(/https:\/\/([a-z0-9.-]+\.atlassian\.net)\b/gi)].map((match) => match[1].toLowerCase()));
|
|
5772
|
+
return sites.size === 1;
|
|
5773
|
+
}
|
|
5774
|
+
function parseAuthStatus(output) {
|
|
5775
|
+
const clean = stripAnsi2(output);
|
|
5776
|
+
const site = clean.match(/(?:site|host)\s*:\s*(?:https?:\/\/)?([a-z0-9.-]+\.atlassian\.net)/i)?.[1];
|
|
5777
|
+
const email = clean.match(/(?:email|user)\s*:\s*([^\s]+@[^\s]+)/i)?.[1];
|
|
5778
|
+
return {
|
|
5779
|
+
installed: true,
|
|
5780
|
+
authenticated: Boolean(site) && !/not (?:logged|authenticated)|unauthenticated/i.test(clean),
|
|
5781
|
+
...site ? { site } : {},
|
|
5782
|
+
...email ? { email } : {}
|
|
5783
|
+
};
|
|
5784
|
+
}
|
|
5785
|
+
var defaultAcliRunner = async (args) => {
|
|
5786
|
+
const { stdout } = await pexec5("acli", args, {
|
|
5787
|
+
env: execEnv(),
|
|
5788
|
+
timeout: 6e4,
|
|
5789
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
5790
|
+
windowsHide: true
|
|
5791
|
+
});
|
|
5792
|
+
return stdout.toString();
|
|
5793
|
+
};
|
|
5794
|
+
async function getJiraAuthStatus(run3 = defaultAcliRunner) {
|
|
5795
|
+
try {
|
|
5796
|
+
return parseAuthStatus(await run3(["jira", "auth", "status"]));
|
|
5797
|
+
} catch (err) {
|
|
5798
|
+
const error = err;
|
|
5799
|
+
if (error.code === "ENOENT") return { installed: false, authenticated: false };
|
|
5800
|
+
return { installed: true, authenticated: false, error: stripAnsi2(String(error.stderr || error.message || err)) };
|
|
5801
|
+
}
|
|
5802
|
+
}
|
|
5803
|
+
function cancelJiraLogin() {
|
|
5804
|
+
activeCancel?.();
|
|
5805
|
+
activeCancel = null;
|
|
5806
|
+
if (!activeLogin2) return;
|
|
5807
|
+
const process2 = activeLogin2;
|
|
5808
|
+
activeLogin2 = null;
|
|
5809
|
+
try {
|
|
5810
|
+
process2.kill();
|
|
5811
|
+
} catch {
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
function inputJiraLogin(data) {
|
|
5815
|
+
activeLogin2?.write(data);
|
|
5816
|
+
}
|
|
5817
|
+
function startJiraLogin(onEvent, run3 = defaultAcliRunner) {
|
|
5818
|
+
cancelJiraLogin();
|
|
5819
|
+
onEvent({ type: "starting" });
|
|
5820
|
+
let process2;
|
|
5821
|
+
try {
|
|
5822
|
+
process2 = nodePty4.spawn("acli", ["jira", "auth", "login", "--web"], {
|
|
5823
|
+
name: "xterm-256color",
|
|
5824
|
+
cwd: processCwd(),
|
|
5825
|
+
env: { ...execEnv(), TERM: "xterm-256color" },
|
|
5826
|
+
cols: 100,
|
|
5827
|
+
rows: 24
|
|
5828
|
+
});
|
|
5829
|
+
} catch (err) {
|
|
5830
|
+
onEvent({ type: "error", error: String(err.message ?? err) });
|
|
5831
|
+
return;
|
|
5832
|
+
}
|
|
5833
|
+
activeLogin2 = process2;
|
|
5834
|
+
let output = "";
|
|
5835
|
+
let sentUrl = false;
|
|
5836
|
+
let autoSelectedSite = false;
|
|
5837
|
+
let cancelled = false;
|
|
5838
|
+
activeCancel = () => {
|
|
5839
|
+
cancelled = true;
|
|
5840
|
+
onEvent({ type: "cancelled" });
|
|
5841
|
+
};
|
|
5842
|
+
process2.onData((chunk) => {
|
|
5843
|
+
onEvent({ type: "output", data: chunk });
|
|
5844
|
+
output += chunk;
|
|
5845
|
+
if (output.length > 64e3) output = output.slice(-64e3);
|
|
5846
|
+
const clean = stripAnsi2(output);
|
|
5847
|
+
const url = clean.match(/https:\/\/[^\s<>"']+/)?.[0];
|
|
5848
|
+
if (url && !sentUrl) {
|
|
5849
|
+
sentUrl = true;
|
|
5850
|
+
onEvent({ type: "url", url });
|
|
5851
|
+
}
|
|
5852
|
+
if (!autoSelectedSite && hasSingleSitePrompt(clean)) {
|
|
5853
|
+
autoSelectedSite = true;
|
|
5854
|
+
process2.write("\r");
|
|
5855
|
+
}
|
|
5856
|
+
});
|
|
5857
|
+
process2.onExit(async ({ exitCode }) => {
|
|
5858
|
+
if (activeLogin2 !== process2) return;
|
|
5859
|
+
activeLogin2 = null;
|
|
5860
|
+
activeCancel = null;
|
|
5861
|
+
if (cancelled) return;
|
|
5862
|
+
if (exitCode === 0) {
|
|
5863
|
+
const status = await getJiraAuthStatus(run3);
|
|
5864
|
+
onEvent(status.authenticated ? { type: "success", status } : { type: "error", error: status.error ?? "\u767B\u5165\u5B8C\u6210\uFF0C\u4F46\u7121\u6CD5\u78BA\u8A8D Atlassian \u5E33\u865F\u3002" });
|
|
5865
|
+
} else {
|
|
5866
|
+
const clean = stripAnsi2(output);
|
|
5867
|
+
const error = clean.match(/(?:✗\s*)?Error:[^\r\n]*/g)?.at(-1) ?? clean.split(/[\r\n]+/).map((line) => line.trim()).filter(Boolean).at(-1);
|
|
5868
|
+
onEvent({ type: "error", error: error || `Atlassian \u767B\u5165\u5931\u6557\uFF08exit ${exitCode}\uFF09\u3002` });
|
|
5869
|
+
}
|
|
5870
|
+
});
|
|
5871
|
+
}
|
|
5872
|
+
function processCwd() {
|
|
5873
|
+
return process.cwd();
|
|
5874
|
+
}
|
|
5875
|
+
async function logoutJira(run3 = defaultAcliRunner) {
|
|
5876
|
+
await run3(["jira", "auth", "logout"]);
|
|
5877
|
+
return getJiraAuthStatus(run3);
|
|
5878
|
+
}
|
|
5879
|
+
|
|
5880
|
+
// packages/core/src/jira.ts
|
|
5881
|
+
var asRaw = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
5882
|
+
var nameOf = (v) => typeof v === "string" ? v : String(asRaw(v).name ?? "");
|
|
5883
|
+
var JIRA_INBOX_JQL = "assignee = currentUser() AND (statusCategory != Done OR sprint in openSprints()) ORDER BY updated DESC";
|
|
5884
|
+
var DEFAULT_POINT_FIELDS = ["customfield_10033", "customfield_10016"];
|
|
5885
|
+
var JIRA_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
5886
|
+
function buildSearchArgs() {
|
|
5887
|
+
return [
|
|
5888
|
+
"jira",
|
|
5889
|
+
"workitem",
|
|
5890
|
+
"search",
|
|
5891
|
+
"--jql",
|
|
5892
|
+
JIRA_INBOX_JQL,
|
|
5893
|
+
"--fields",
|
|
5894
|
+
"key,summary,status,issuetype,priority,description",
|
|
5895
|
+
"--json",
|
|
5896
|
+
"--paginate",
|
|
5897
|
+
"--limit",
|
|
5898
|
+
"200"
|
|
5899
|
+
];
|
|
5900
|
+
}
|
|
5901
|
+
function buildViewArgs(key, pointFields = DEFAULT_POINT_FIELDS) {
|
|
5902
|
+
return [
|
|
5903
|
+
"jira",
|
|
5904
|
+
"workitem",
|
|
5905
|
+
"view",
|
|
5906
|
+
key,
|
|
5907
|
+
"--fields",
|
|
5908
|
+
[.../* @__PURE__ */ new Set(["duedate", "parent", "created", "updated", "project", "customfield_10020", ...pointFields])].join(","),
|
|
5909
|
+
"--json"
|
|
5910
|
+
];
|
|
5911
|
+
}
|
|
5912
|
+
function isJiraRef(value) {
|
|
5913
|
+
const v = asRaw(value);
|
|
5914
|
+
return typeof v.key === "string" && /^[A-Z][A-Z0-9_]+-\d+$/.test(v.key) && typeof v.site === "string" && /^[a-z0-9-]+(?:\.[a-z0-9-]+)*\.atlassian\.net$/.test(v.site) && v.url === `https://${v.site}/browse/${v.key}`;
|
|
5915
|
+
}
|
|
5916
|
+
function toTicket(raw, site, pointFields = DEFAULT_POINT_FIELDS) {
|
|
5917
|
+
const row = asRaw(raw);
|
|
5918
|
+
const fields = asRaw(row.fields);
|
|
5919
|
+
const source = Object.keys(fields).length ? fields : row;
|
|
5920
|
+
const key = row.key;
|
|
5921
|
+
if (typeof key !== "string" || !/^[A-Z][A-Z0-9_]+-\d+$/.test(key)) return null;
|
|
5922
|
+
const status = asRaw(source.status);
|
|
5923
|
+
const category = asRaw(status.statusCategory);
|
|
5924
|
+
const categoryKey = String(category.key ?? category.name ?? "").toLowerCase();
|
|
5925
|
+
const points = pointFields.map((field) => source[field]).find((v) => typeof v === "number" && Number.isFinite(v));
|
|
5926
|
+
const sprintValue = source.customfield_10020;
|
|
5927
|
+
const sprintList = Array.isArray(sprintValue) ? sprintValue : sprintValue ? [sprintValue] : [];
|
|
5928
|
+
const activeSprint = sprintList.map(asRaw).find((s) => String(s.state ?? "").toLowerCase() === "active");
|
|
5929
|
+
const parent = asRaw(source.parent);
|
|
5930
|
+
return {
|
|
5931
|
+
key,
|
|
5932
|
+
url: `https://${site}/browse/${key}`,
|
|
5933
|
+
projectKey: String(asRaw(source.project).key ?? key.split("-")[0]),
|
|
5934
|
+
summary: String(source.summary ?? ""),
|
|
5935
|
+
status: { name: nameOf(source.status), category: categoryKey === "done" ? "done" : categoryKey === "indeterminate" || categoryKey === "in progress" ? "indeterminate" : "new" },
|
|
5936
|
+
issueType: nameOf(source.issuetype),
|
|
5937
|
+
priority: nameOf(source.priority) || null,
|
|
5938
|
+
storyPoints: typeof points === "number" ? points : null,
|
|
5939
|
+
dueDate: typeof source.duedate === "string" ? source.duedate : null,
|
|
5940
|
+
sprint: typeof activeSprint?.name === "string" ? activeSprint.name : null,
|
|
5941
|
+
parentKey: typeof parent.key === "string" ? parent.key : null,
|
|
5942
|
+
description: adfToMarkdown(source.description),
|
|
5943
|
+
createdAt: String(source.created ?? ""),
|
|
5944
|
+
updatedAt: String(source.updated ?? "")
|
|
5945
|
+
};
|
|
5946
|
+
}
|
|
5947
|
+
function searchRows(output) {
|
|
5948
|
+
const parsed = JSON.parse(output);
|
|
5949
|
+
if (Array.isArray(parsed)) return parsed;
|
|
5950
|
+
const body = asRaw(parsed);
|
|
5951
|
+
if (Array.isArray(body.issues)) return body.issues;
|
|
5952
|
+
if (Array.isArray(body.values)) return body.values;
|
|
5953
|
+
throw new Error("\u7121\u6CD5\u8FA8\u8B58 acli Jira search \u7684 JSON \u683C\u5F0F");
|
|
5954
|
+
}
|
|
5955
|
+
async function enrichRows(rows, run3, pointFields) {
|
|
5956
|
+
const enriched = Array.from(rows);
|
|
5957
|
+
let next = 0;
|
|
5958
|
+
let failed = 0;
|
|
5959
|
+
async function worker() {
|
|
5960
|
+
while (next < rows.length) {
|
|
5961
|
+
const index = next++;
|
|
5962
|
+
const row = asRaw(rows[index]);
|
|
5963
|
+
if (typeof row.key !== "string") continue;
|
|
5964
|
+
try {
|
|
5965
|
+
const detail = asRaw(JSON.parse(await run3(buildViewArgs(row.key, pointFields))));
|
|
5966
|
+
enriched[index] = { ...row, fields: { ...asRaw(row.fields), ...asRaw(detail.fields) } };
|
|
5967
|
+
} catch {
|
|
5968
|
+
failed++;
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
5971
|
+
}
|
|
5972
|
+
await Promise.all(Array.from({ length: Math.min(6, rows.length) }, () => worker()));
|
|
5973
|
+
return { rows: enriched, failed };
|
|
5974
|
+
}
|
|
5975
|
+
function createJiraService(options) {
|
|
5976
|
+
const run3 = options.run ?? defaultAcliRunner;
|
|
5977
|
+
const authStatus = options.authStatus ?? (() => getJiraAuthStatus(run3));
|
|
5978
|
+
const now = options.now ?? Date.now;
|
|
5979
|
+
let cache = null;
|
|
5980
|
+
return {
|
|
5981
|
+
clear() {
|
|
5982
|
+
cache = null;
|
|
5983
|
+
},
|
|
5984
|
+
async inbox(opts = {}) {
|
|
5985
|
+
const auth = await authStatus();
|
|
5986
|
+
if (!auth.installed) return { status: "acli-missing", fetchedAt: 0, tickets: [] };
|
|
5987
|
+
if (!auth.authenticated || !auth.site) return { status: "unauthenticated", fetchedAt: 0, tickets: [] };
|
|
5988
|
+
if (cache && cache.site === auth.site && !opts.force && now() - cache.at < JIRA_CACHE_TTL_MS) return cache.value;
|
|
5989
|
+
const at = now();
|
|
5990
|
+
const pointFields = options.pointFields?.() ?? DEFAULT_POINT_FIELDS;
|
|
5991
|
+
const value = run3(buildSearchArgs()).then(async (output) => {
|
|
5992
|
+
const detail = await enrichRows(searchRows(output), run3, pointFields);
|
|
5993
|
+
return {
|
|
5994
|
+
status: "ok",
|
|
5995
|
+
site: auth.site,
|
|
5996
|
+
email: auth.email,
|
|
5997
|
+
fetchedAt: at,
|
|
5998
|
+
tickets: detail.rows.map((row) => toTicket(row, auth.site, pointFields)).filter((ticket) => ticket !== null),
|
|
5999
|
+
...detail.failed ? { error: `${detail.failed} \u5F35 Jira ticket \u7684\u8A73\u7D30\u6B04\u4F4D\u8B80\u53D6\u5931\u6557\uFF1B\u6E05\u55AE\u4ECD\u53EF\u67E5\u770B\u3002` } : {}
|
|
6000
|
+
};
|
|
6001
|
+
}).catch((err) => ({
|
|
6002
|
+
status: "ok",
|
|
6003
|
+
site: auth.site,
|
|
6004
|
+
email: auth.email,
|
|
6005
|
+
fetchedAt: at,
|
|
6006
|
+
tickets: [],
|
|
6007
|
+
error: String(err.stderr || err.message || err)
|
|
6008
|
+
}));
|
|
6009
|
+
cache = { at, site: auth.site, value };
|
|
6010
|
+
return value;
|
|
6011
|
+
}
|
|
6012
|
+
};
|
|
6013
|
+
}
|
|
6014
|
+
|
|
5635
6015
|
// packages/core/src/board-cli.ts
|
|
5636
6016
|
import fs14 from "fs";
|
|
5637
6017
|
import path18 from "path";
|
|
@@ -5754,7 +6134,7 @@ async function readSpecsAt(cwd, ref, paths) {
|
|
|
5754
6134
|
// packages/core/src/chat-session.ts
|
|
5755
6135
|
init_env();
|
|
5756
6136
|
init_git_bash();
|
|
5757
|
-
import { spawn as
|
|
6137
|
+
import { spawn as spawn7 } from "child_process";
|
|
5758
6138
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5759
6139
|
|
|
5760
6140
|
// packages/core/src/chat-store.ts
|
|
@@ -5910,7 +6290,7 @@ ${attachmentLines.join("\n")}` : text;
|
|
|
5910
6290
|
`--append-system-prompt ${quote(systemPrompt)}`,
|
|
5911
6291
|
quote(fullText)
|
|
5912
6292
|
].filter(Boolean).join(" ") : buildRawAgentCommand(agentCli, model, fullText, systemPrompt, worktreePath, quote, workspacePath);
|
|
5913
|
-
const proc =
|
|
6293
|
+
const proc = spawn7(shell.command, shell.args(cmd), {
|
|
5914
6294
|
cwd: worktreePath,
|
|
5915
6295
|
env: execEnv()
|
|
5916
6296
|
});
|
|
@@ -6385,7 +6765,7 @@ function validBoard(value) {
|
|
|
6385
6765
|
}
|
|
6386
6766
|
return next;
|
|
6387
6767
|
}
|
|
6388
|
-
function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus, modelCatalog }) {
|
|
6768
|
+
function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus, acliRunner, jiraAuthStatus, modelCatalog }) {
|
|
6389
6769
|
const sink = bus.sink();
|
|
6390
6770
|
const models = modelCatalog ?? createModelCatalog({ version });
|
|
6391
6771
|
let libraryCodexHome = null;
|
|
@@ -6537,6 +6917,11 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6537
6917
|
authStatus: githubAuthStatus ?? getGitHubCliAuthStatus,
|
|
6538
6918
|
repoOf: async (projectPath) => parseGithubRepo((await getGitInfo(projectPath)).remoteUrl)
|
|
6539
6919
|
});
|
|
6920
|
+
const jira = createJiraService({
|
|
6921
|
+
run: acliRunner,
|
|
6922
|
+
authStatus: jiraAuthStatus ?? (() => getJiraAuthStatus(acliRunner)),
|
|
6923
|
+
pointFields: () => getSettings().jira?.storyPointsFields?.length ? getSettings().jira.storyPointsFields : DEFAULT_POINT_FIELDS
|
|
6924
|
+
});
|
|
6540
6925
|
const handlers = {
|
|
6541
6926
|
"vibeflow:getState": () => getState(),
|
|
6542
6927
|
// A card's progress right now (todo list, usage, activity), for a frontend
|
|
@@ -6556,7 +6941,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6556
6941
|
return getState();
|
|
6557
6942
|
},
|
|
6558
6943
|
"vibeflow:setSettings": (patch) => {
|
|
6559
|
-
|
|
6944
|
+
const value = obj(patch, "patch");
|
|
6945
|
+
if (value.jira !== void 0) {
|
|
6946
|
+
const fields = obj(value.jira, "jira")?.storyPointsFields;
|
|
6947
|
+
if (fields !== void 0 && (!Array.isArray(fields) || !fields.every((field) => typeof field === "string" && /^customfield_\d+$/.test(field)))) invalid("jira.storyPointsFields must contain customfield IDs");
|
|
6948
|
+
jira.clear();
|
|
6949
|
+
}
|
|
6950
|
+
setSettings(value);
|
|
6560
6951
|
return getState();
|
|
6561
6952
|
},
|
|
6562
6953
|
"settings:githubAuthStatus": () => getGitHubCliAuthStatus(),
|
|
@@ -6577,6 +6968,25 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6577
6968
|
const p = payload === void 0 ? {} : obj(payload, "payload");
|
|
6578
6969
|
return github.taskLinks(getState().board, { force: p.force === true });
|
|
6579
6970
|
},
|
|
6971
|
+
"jira:authStatus": () => (jiraAuthStatus ?? (() => getJiraAuthStatus(acliRunner)))(),
|
|
6972
|
+
"settings:startJiraAuthLogin": () => {
|
|
6973
|
+
startJiraLogin((event) => {
|
|
6974
|
+
if (event.type === "success") jira.clear();
|
|
6975
|
+
bus.emit("jira-auth:event", event);
|
|
6976
|
+
}, acliRunner);
|
|
6977
|
+
},
|
|
6978
|
+
"settings:cancelJiraAuthLogin": () => cancelJiraLogin(),
|
|
6979
|
+
"settings:inputJiraAuthLogin": (payload) => inputJiraLogin(str(payload, "input").slice(0, 1e3)),
|
|
6980
|
+
"settings:logoutJiraAuth": async () => {
|
|
6981
|
+
const status = await logoutJira(acliRunner);
|
|
6982
|
+
jira.clear();
|
|
6983
|
+
bus.emit("jira-auth:event", { type: "signed-out", status });
|
|
6984
|
+
return status;
|
|
6985
|
+
},
|
|
6986
|
+
"jira:inbox": (payload) => {
|
|
6987
|
+
const p = payload === void 0 ? {} : obj(payload, "payload");
|
|
6988
|
+
return jira.inbox({ force: p.force === true });
|
|
6989
|
+
},
|
|
6580
6990
|
"env:detectAgents": () => detectAgents(),
|
|
6581
6991
|
"agents:listModels": (payload) => {
|
|
6582
6992
|
const { agentId: rawId, refresh } = obj(payload, "payload");
|
|
@@ -6608,6 +7018,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6608
7018
|
if (p.effort !== void 0 && !AGENT_EFFORTS.includes(p.effort)) invalid("unknown effort");
|
|
6609
7019
|
if (p.agentCli !== void 0 && !AGENT_IDS2.includes(p.agentCli)) invalid("unknown agent");
|
|
6610
7020
|
if (p.github !== void 0 && !isGithubRef(p.github)) invalid("github must be an issue or pr ref on github.com");
|
|
7021
|
+
if (p.jira !== void 0 && !isJiraRef(p.jira)) invalid("jira must be a Jira ticket ref");
|
|
6611
7022
|
const { task } = await createTaskFromInput({
|
|
6612
7023
|
projectPath: p.projectPath,
|
|
6613
7024
|
title: p.title,
|
|
@@ -6620,7 +7031,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6620
7031
|
effort: p.effort,
|
|
6621
7032
|
autoMode: p.autoMode,
|
|
6622
7033
|
attachments: p.attachments,
|
|
6623
|
-
github: p.github
|
|
7034
|
+
github: p.github,
|
|
7035
|
+
jira: p.jira
|
|
6624
7036
|
});
|
|
6625
7037
|
return { state: getState(), task };
|
|
6626
7038
|
},
|
|
@@ -7037,6 +7449,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7037
7449
|
models.warm();
|
|
7038
7450
|
},
|
|
7039
7451
|
shutdown() {
|
|
7452
|
+
cancelJiraLogin();
|
|
7040
7453
|
sessions.shutdown();
|
|
7041
7454
|
cancelAllChatSends();
|
|
7042
7455
|
unwatchAllSubAgents();
|
|
@@ -7493,12 +7906,12 @@ function connectToHost(lock) {
|
|
|
7493
7906
|
// packages/cli/src/doctor.ts
|
|
7494
7907
|
import fs21 from "fs";
|
|
7495
7908
|
import path25 from "path";
|
|
7496
|
-
import { execFile as
|
|
7497
|
-
import { promisify as
|
|
7909
|
+
import { execFile as execFile9 } from "child_process";
|
|
7910
|
+
import { promisify as promisify6 } from "util";
|
|
7498
7911
|
init_env();
|
|
7499
7912
|
init_git_bash();
|
|
7500
7913
|
init_tmux_backend();
|
|
7501
|
-
var
|
|
7914
|
+
var pexec6 = promisify6(execFile9);
|
|
7502
7915
|
function parseVersion(text) {
|
|
7503
7916
|
const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
|
|
7504
7917
|
return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
|
|
@@ -7512,7 +7925,7 @@ function versionAtLeast(have, want) {
|
|
|
7512
7925
|
}
|
|
7513
7926
|
async function run2(cmd, args) {
|
|
7514
7927
|
try {
|
|
7515
|
-
const { stdout } = await
|
|
7928
|
+
const { stdout } = await pexec6(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
|
|
7516
7929
|
return String(stdout).trim();
|
|
7517
7930
|
} catch {
|
|
7518
7931
|
return null;
|