@arcships/morula-runtime 0.1.0-alpha.3 → 0.1.0-alpha.4
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/bin/morula-runtime.js +0 -0
- package/bin/morula.js +0 -0
- package/dist/index.js +1014 -176
- package/dist/morula.js +183 -36
- package/dist/plugin/plugin.json +1 -1
- package/dist/plugin/skills/morula-working-on-issues/SKILL.md +53 -0
- package/dist/plugin/skills/morula-working-on-issues/references/working-on-issues-source-map.md +8 -6
- package/package.json +1 -1
package/dist/morula.js
CHANGED
|
@@ -686,8 +686,8 @@ function page(ok, message) {
|
|
|
686
686
|
}
|
|
687
687
|
|
|
688
688
|
// ../morula-cli-core/dist/build-env.js
|
|
689
|
-
var BUILD_VERSION = "0.1.0-alpha.
|
|
690
|
-
var BUILD_ID = "
|
|
689
|
+
var BUILD_VERSION = "0.1.0-alpha.4";
|
|
690
|
+
var BUILD_ID = "20260916074556.nogit";
|
|
691
691
|
var BUILD_CONTEXTS = {
|
|
692
692
|
"local": "http://localhost:8080/api",
|
|
693
693
|
"test": "https://morula.basecastle.com/api",
|
|
@@ -1045,9 +1045,6 @@ var ReportApi = class {
|
|
|
1045
1045
|
listByAutopilot(autopilotId, query = {}) {
|
|
1046
1046
|
return this.client.get(`/agent-autopilots/${encodeURIComponent(requiredId2(autopilotId, "autopilot"))}/reports`, { ...query });
|
|
1047
1047
|
}
|
|
1048
|
-
archive(id) {
|
|
1049
|
-
return this.client.patch(`/agent-reports/${encodeURIComponent(requiredId2(id))}`, { status: "archived" });
|
|
1050
|
-
}
|
|
1051
1048
|
};
|
|
1052
1049
|
function requiredId2(id, label = "report") {
|
|
1053
1050
|
const normalized = id.trim();
|
|
@@ -1994,7 +1991,16 @@ async function repoCommand(context) {
|
|
|
1994
1991
|
if (action === "mr" && positionals[0] === "list") {
|
|
1995
1992
|
return mrList(context, flags);
|
|
1996
1993
|
}
|
|
1997
|
-
|
|
1994
|
+
if (action === "mr" && positionals[0] === "comment") {
|
|
1995
|
+
return mrComment(context, flags);
|
|
1996
|
+
}
|
|
1997
|
+
if (action === "mr" && positionals[0] === "get") {
|
|
1998
|
+
return mrGet(context, flags);
|
|
1999
|
+
}
|
|
2000
|
+
if (action === "mr" && positionals[0] === "diff") {
|
|
2001
|
+
return mrDiff(context, flags);
|
|
2002
|
+
}
|
|
2003
|
+
throw usage("usage: morula repo <list|checkout|prepare|mr create|mr list|mr comment|mr get|mr diff>");
|
|
1998
2004
|
}
|
|
1999
2005
|
function resolveMrBase(flagBase, env = process.env) {
|
|
2000
2006
|
return env.MORULA_BASE_BRANCH?.trim() || flagBase?.trim() || "main";
|
|
@@ -2137,23 +2143,136 @@ async function mrCreateViaPushOptions(input) {
|
|
|
2137
2143
|
throw new CliError("MR_CREATE_FAILED", EXIT.UPSTREAM, `git push \u6210\u529F\u4F46\u672A\u68C0\u6D4B\u5230 MR \u521B\u5EFA\u8F93\u51FA\uFF08\u5206\u652F\u53EF\u80FD\u5DF2\u5B58\u5728\u6216 push options \u672A\u751F\u6548\uFF09\u3002\u8F93\u51FA\u5C3E\u90E8: ${hint}`);
|
|
2138
2144
|
}
|
|
2139
2145
|
async function mrList(context, flags) {
|
|
2140
|
-
rejectUnknownFlags(flags, ["state", "limit"]);
|
|
2146
|
+
rejectUnknownFlags(flags, ["state", "limit", "author", "repo"]);
|
|
2141
2147
|
const taskId = context.parsed.positionals[1]?.trim();
|
|
2142
2148
|
if (!taskId)
|
|
2143
|
-
throw usage("usage: morula repo mr list <taskId> [--state opened|all|merged|closed] [--limit <n>]");
|
|
2149
|
+
throw usage("usage: morula repo mr list <taskId> [--state opened|all|merged|closed] [--limit <n>] [--author <username>] [--repo <owner/name>]");
|
|
2144
2150
|
const state = flagString(flags, "state") ?? "opened";
|
|
2145
2151
|
const limit = flagNumber(flags, "limit");
|
|
2152
|
+
const author = flagString(flags, "author");
|
|
2153
|
+
const repo = flagString(flags, "repo");
|
|
2146
2154
|
const result = await context.client.http.get(
|
|
2147
2155
|
`/agent-tasks/${taskId}/vcs/mr-list`,
|
|
2148
2156
|
{
|
|
2149
2157
|
state,
|
|
2150
|
-
...limit ? { limit } : {}
|
|
2158
|
+
...limit ? { limit } : {},
|
|
2159
|
+
...author ? { author } : {},
|
|
2160
|
+
...repo ? { repoFullName: repo } : {}
|
|
2151
2161
|
}
|
|
2152
2162
|
);
|
|
2153
2163
|
if (result?.ok === false)
|
|
2154
2164
|
throw new CliError(result.code ?? "MR_QUERY_FAILED", EXIT.OTHER, `MR \u5217\u8868\u67E5\u8BE2\u5931\u8D25: ${result.message ?? result.code ?? "\u672A\u77E5\u539F\u56E0"}`);
|
|
2155
2165
|
return result;
|
|
2156
2166
|
}
|
|
2167
|
+
async function mrComment(context, flags) {
|
|
2168
|
+
rejectUnknownFlags(flags, ["mr", "repo", "connection", "content", "content-file"]);
|
|
2169
|
+
const taskId = context.parsed.positionals[1]?.trim();
|
|
2170
|
+
if (!taskId)
|
|
2171
|
+
throw usage("usage: morula repo mr comment <taskId> --mr <iid> [--repo <owner/name>] [--connection <id>] (--content <text> | --content-file <path>)");
|
|
2172
|
+
const mrIid = flagNumber(flags, "mr");
|
|
2173
|
+
if (mrIid == null || !Number.isInteger(mrIid) || mrIid <= 0)
|
|
2174
|
+
throw usage("--mr \u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF08MR/PR \u7F16\u53F7\uFF09");
|
|
2175
|
+
const repo = flagString(flags, "repo");
|
|
2176
|
+
const connection = flagString(flags, "connection");
|
|
2177
|
+
const inline = flagString(flags, "content");
|
|
2178
|
+
const filePath = flagString(flags, "content-file");
|
|
2179
|
+
if (inline && filePath)
|
|
2180
|
+
throw usage("--content \u4E0E --content-file \u53EA\u80FD\u6307\u5B9A\u4E00\u4E2A");
|
|
2181
|
+
if (!inline && !filePath)
|
|
2182
|
+
throw usage("\u8BC4\u8BBA\u6B63\u6587\u5FC5\u586B\uFF1A--content <text> \u6216 --content-file <path>");
|
|
2183
|
+
let body = inline ?? "";
|
|
2184
|
+
if (filePath) {
|
|
2185
|
+
try {
|
|
2186
|
+
const { readFile: readFile3 } = await import("node:fs/promises");
|
|
2187
|
+
body = await readFile3(filePath, "utf8");
|
|
2188
|
+
} catch (error) {
|
|
2189
|
+
throw new CliError(
|
|
2190
|
+
"CONTENT_FILE_UNREADABLE",
|
|
2191
|
+
EXIT.OTHER,
|
|
2192
|
+
`\u8BFB\u53D6 --content-file \u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
if (!body?.trim())
|
|
2197
|
+
throw usage("\u8BC4\u8BBA\u6B63\u6587\u4E0D\u80FD\u4E3A\u7A7A");
|
|
2198
|
+
if (body.length > 2e4)
|
|
2199
|
+
throw usage(`\u8BC4\u8BBA\u6B63\u6587\u8D85\u8FC7 20000 \u5B57\u7B26\u4E0A\u9650\uFF1A\u957F\u5185\u5BB9\u8BF7\u7528 morula report create --content-file \u5199\u6210\u62A5\u544A\uFF0C\u8BC4\u8BBA\u91CC\u7ED9\u6458\u8981`);
|
|
2200
|
+
const result = await context.client.http.post(`/agent-tasks/${taskId}/vcs/mr-comment`, {
|
|
2201
|
+
mrIid,
|
|
2202
|
+
body,
|
|
2203
|
+
...repo ? { repoFullName: repo } : {},
|
|
2204
|
+
...connection ? { connectionId: connection } : {}
|
|
2205
|
+
});
|
|
2206
|
+
if (result?.ok === false) {
|
|
2207
|
+
throw new CliError(
|
|
2208
|
+
result.code ?? "MR_COMMENT_FAILED",
|
|
2209
|
+
exitFor(result.code),
|
|
2210
|
+
`MR \u8BC4\u8BBA\u5199\u5165\u5931\u8D25: ${result.message ?? result.code ?? "\u672A\u77E5\u539F\u56E0"}`
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
2213
|
+
return result;
|
|
2214
|
+
}
|
|
2215
|
+
function exitFor(code) {
|
|
2216
|
+
if (code === "TASK_NOT_FOUND" || code === "NO_CONNECTION")
|
|
2217
|
+
return EXIT.NOT_FOUND;
|
|
2218
|
+
if (code === "MR_NOT_ALLOWED" || code === "CONTENT_TOO_LARGE")
|
|
2219
|
+
return EXIT.USAGE;
|
|
2220
|
+
if (code === "MR_COMMENT_FAILED")
|
|
2221
|
+
return EXIT.UPSTREAM;
|
|
2222
|
+
return EXIT.OTHER;
|
|
2223
|
+
}
|
|
2224
|
+
async function mrGet(context, flags) {
|
|
2225
|
+
rejectUnknownFlags(flags, ["mr", "repo", "ci", "notes"]);
|
|
2226
|
+
const taskId = context.parsed.positionals[1]?.trim();
|
|
2227
|
+
if (!taskId)
|
|
2228
|
+
throw usage("usage: morula repo mr get <taskId> --mr <iid> [--repo <owner/name>] [--ci] [--notes]");
|
|
2229
|
+
const mrIid = flagNumber(flags, "mr");
|
|
2230
|
+
if (mrIid == null || !Number.isInteger(mrIid) || mrIid <= 0)
|
|
2231
|
+
throw usage("--mr \u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF08MR/PR \u7F16\u53F7\uFF09");
|
|
2232
|
+
const repo = flagString(flags, "repo");
|
|
2233
|
+
const ci = !!flags["ci"];
|
|
2234
|
+
const notes = !!flags["notes"];
|
|
2235
|
+
const result = await context.client.http.get(
|
|
2236
|
+
`/agent-tasks/${taskId}/vcs/mr-detail`,
|
|
2237
|
+
{
|
|
2238
|
+
mrIid,
|
|
2239
|
+
...repo ? { repoFullName: repo } : {},
|
|
2240
|
+
...ci ? { ci: 1 } : {},
|
|
2241
|
+
...notes ? { notes: 1 } : {}
|
|
2242
|
+
}
|
|
2243
|
+
);
|
|
2244
|
+
if (result?.ok === false)
|
|
2245
|
+
throw new CliError(result.code ?? "MR_QUERY_FAILED", EXIT.OTHER, `MR \u8BE6\u60C5\u67E5\u8BE2\u5931\u8D25: ${result.message ?? result.code ?? "\u672A\u77E5\u539F\u56E0"}`);
|
|
2246
|
+
return result;
|
|
2247
|
+
}
|
|
2248
|
+
async function mrDiff(context, flags) {
|
|
2249
|
+
rejectUnknownFlags(flags, ["mr", "repo", "max-files", "max-chars"]);
|
|
2250
|
+
const taskId = context.parsed.positionals[1]?.trim();
|
|
2251
|
+
if (!taskId)
|
|
2252
|
+
throw usage("usage: morula repo mr diff <taskId> --mr <iid> [--repo <owner/name>] [--max-files <n>] [--max-chars <n>]");
|
|
2253
|
+
const mrIid = flagNumber(flags, "mr");
|
|
2254
|
+
if (mrIid == null || !Number.isInteger(mrIid) || mrIid <= 0)
|
|
2255
|
+
throw usage("--mr \u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF08MR/PR \u7F16\u53F7\uFF09");
|
|
2256
|
+
const repo = flagString(flags, "repo");
|
|
2257
|
+
const maxFiles = flagNumber(flags, "max-files");
|
|
2258
|
+
const maxChars = flagNumber(flags, "max-chars");
|
|
2259
|
+
if (maxFiles != null && maxFiles <= 0)
|
|
2260
|
+
throw usage("--max-files \u5FC5\u987B\u662F\u6B63\u6574\u6570");
|
|
2261
|
+
if (maxChars != null && maxChars <= 0)
|
|
2262
|
+
throw usage("--max-chars \u5FC5\u987B\u662F\u6B63\u6574\u6570");
|
|
2263
|
+
const result = await context.client.http.get(
|
|
2264
|
+
`/agent-tasks/${taskId}/vcs/mr-diff`,
|
|
2265
|
+
{
|
|
2266
|
+
mrIid,
|
|
2267
|
+
...repo ? { repoFullName: repo } : {},
|
|
2268
|
+
...maxFiles ? { maxFiles } : {},
|
|
2269
|
+
...maxChars ? { maxChars } : {}
|
|
2270
|
+
}
|
|
2271
|
+
);
|
|
2272
|
+
if (result?.ok === false)
|
|
2273
|
+
throw new CliError(result.code ?? "MR_QUERY_FAILED", EXIT.OTHER, `MR diff \u67E5\u8BE2\u5931\u8D25: ${result.message ?? result.code ?? "\u672A\u77E5\u539F\u56E0"}`);
|
|
2274
|
+
return result;
|
|
2275
|
+
}
|
|
2157
2276
|
async function tryLocalCheckout(taskId, repoFullName, ref) {
|
|
2158
2277
|
try {
|
|
2159
2278
|
const { readFileSync: readFileSync2 } = await import("node:fs");
|
|
@@ -2209,7 +2328,6 @@ async function tryLocalPrepare(taskId, repoFullName) {
|
|
|
2209
2328
|
|
|
2210
2329
|
// ../morula-cli/src/commands/report.ts
|
|
2211
2330
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
2212
|
-
var REPORT_STATUSES = ["ready", "archived"];
|
|
2213
2331
|
async function reportCommand(context) {
|
|
2214
2332
|
if (!context.runtime.token)
|
|
2215
2333
|
throw new CliError("UNAUTHENTICATED", EXIT.UNAUTHENTICATED, "\u8BF7\u5148\u8FD0\u884C morula auth login");
|
|
@@ -2235,35 +2353,21 @@ async function reportCommand(context) {
|
|
|
2235
2353
|
return context.client.reports.get(id);
|
|
2236
2354
|
}
|
|
2237
2355
|
if (action === "list") {
|
|
2238
|
-
rejectUnknownFlags(flags, ["autopilot", "page", "page-size"
|
|
2356
|
+
rejectUnknownFlags(flags, ["autopilot", "page", "page-size"]);
|
|
2239
2357
|
const autopilotId = flagString(flags, "autopilot");
|
|
2240
2358
|
if (!autopilotId)
|
|
2241
|
-
throw usage("usage: morula report list --autopilot <id> [--page <n>] [--page-size <n>]
|
|
2242
|
-
const status2 = flagString(flags, "status");
|
|
2359
|
+
throw usage("usage: morula report list --autopilot <id> [--page <n>] [--page-size <n>]");
|
|
2243
2360
|
const query = {
|
|
2244
2361
|
page: flagNumber(flags, "page"),
|
|
2245
2362
|
pageSize: flagNumber(flags, "page-size")
|
|
2246
2363
|
};
|
|
2247
|
-
if (status2) {
|
|
2248
|
-
if (!isReportStatus(status2))
|
|
2249
|
-
throw usage("--status \u5FC5\u987B\u662F ready / archived");
|
|
2250
|
-
query.status = status2;
|
|
2251
|
-
}
|
|
2252
2364
|
return context.client.reports.listByAutopilot(autopilotId, query);
|
|
2253
2365
|
}
|
|
2254
|
-
|
|
2255
|
-
rejectUnknownFlags(flags, []);
|
|
2256
|
-
const id = positionals[0]?.trim();
|
|
2257
|
-
if (!id)
|
|
2258
|
-
throw usage("usage: morula report archive <id>");
|
|
2259
|
-
return context.client.reports.archive(id);
|
|
2260
|
-
}
|
|
2261
|
-
throw usage(`usage: morula report <create|get|list|archive>
|
|
2366
|
+
throw usage(`usage: morula report <create|get|list>
|
|
2262
2367
|
|
|
2263
2368
|
create \u4EA7\u51FA\u62A5\u544A\uFF08\u81EA\u52A8\u5316\u6267\u884C\u7684\u7ED3\u8BBA\u578B\u5185\u5BB9\uFF1B--autopilot \u53EF\u7701\u7565\uFF0C\u670D\u52A1\u7AEF\u6309\u5F53\u524D\u4EFB\u52A1\u53CD\u67E5\u6240\u5C5E\u81EA\u52A8\u5316\uFF09
|
|
2264
2369
|
get \u67E5\u770B\u62A5\u544A\u8BE6\u60C5\uFF08\u542B Markdown \u6B63\u6587\uFF09
|
|
2265
|
-
list \u5217\u51FA\u67D0\u81EA\u52A8\u5316\u7684\u62A5\u544A
|
|
2266
|
-
archive \u5F52\u6863\u62A5\u544A\uFF08\u5F52\u6863\u540E\u9ED8\u8BA4\u5217\u8868\u4E0D\u518D\u5C55\u793A\uFF09`);
|
|
2370
|
+
list \u5217\u51FA\u67D0\u81EA\u52A8\u5316\u7684\u62A5\u544A`);
|
|
2267
2371
|
}
|
|
2268
2372
|
async function resolveContent(flags) {
|
|
2269
2373
|
const inline = flagString(flags, "content");
|
|
@@ -2288,9 +2392,6 @@ async function resolveContent(flags) {
|
|
|
2288
2392
|
throw usage("\u62A5\u544A\u6B63\u6587\u4E0D\u80FD\u4E3A\u7A7A");
|
|
2289
2393
|
return content;
|
|
2290
2394
|
}
|
|
2291
|
-
function isReportStatus(value) {
|
|
2292
|
-
return REPORT_STATUSES.includes(value);
|
|
2293
|
-
}
|
|
2294
2395
|
|
|
2295
2396
|
// ../morula-cli/src/commands/review.ts
|
|
2296
2397
|
async function reviewCommand(context) {
|
|
@@ -2614,7 +2715,50 @@ async function taskCommand(context) {
|
|
|
2614
2715
|
throw new CliError(result.code ?? "ANALYSIS_VERDICT_FAILED", EXIT.OTHER, `\u5206\u6790\u7ED3\u8BBA\u4E0A\u62A5\u5931\u8D25: ${result.message ?? "\u672A\u77E5\u539F\u56E0"}`);
|
|
2615
2716
|
return { ok: true, verdict: result.verdict, alreadyReported: result.alreadyReported ?? false };
|
|
2616
2717
|
}
|
|
2617
|
-
|
|
2718
|
+
if (action === "inbox") {
|
|
2719
|
+
rejectUnknownFlags(flags, ["peek", "after-seq"]);
|
|
2720
|
+
const taskId = await resolveTaskId(context);
|
|
2721
|
+
if (!taskId) {
|
|
2722
|
+
throw usage(`morula task inbox \u9700\u8981\u4EFB\u52A1\u4E0A\u4E0B\u6587\uFF1A\u5F53\u524D\u76EE\u5F55\u627E\u4E0D\u5230 .morula/checkout.json \u4E14\u672A\u8BBE\u7F6E MORULA_TASK_ID\u3002
|
|
2723
|
+
|
|
2724
|
+
\u7528\u6CD5:
|
|
2725
|
+
morula task inbox # \u62C9\u53D6\u672C\u7EBF\u7A0B\u672A\u6D88\u8D39\u6D88\u606F\uFF08\u7EB3\u5165\u672C\u56DE\u5408\uFF0C\u670D\u52A1\u7AEF\u6807\u8BB0\u5DF2\u6D88\u8D39\uFF09
|
|
2726
|
+
morula task inbox --peek # \u53EA\u8BFB\uFF0C\u4E0D\u6807\u8BB0\u6D88\u8D39
|
|
2727
|
+
\u8BF4\u660E: \u4EC5\u5728\u9636\u6BB5\u8FD0\u884C\u8FC7\u7A0B\u4E2D\u3001\u4E14\u5230\u8FBE\u68C0\u67E5\u70B9\u65F6\u8C03\u7528\uFF08\u4E00\u6279\u6539\u52A8\u5B8C\u6210\u540E / git commit \u524D / git push \u524D / \u6536\u5230\u4E2D\u65AD\u4FE1\u53F7\u65F6\uFF09\uFF1B\u6CA1\u6709\u6D88\u606F\u5C31\u7ACB\u5373\u7EE7\u7EED\uFF0C\u4E0D\u8981\u53CD\u590D\u8F6E\u8BE2\u3002`);
|
|
2728
|
+
}
|
|
2729
|
+
const afterSeq = flagNumber(flags, "after-seq");
|
|
2730
|
+
const peek = flagBoolean(flags, "peek");
|
|
2731
|
+
const query = {};
|
|
2732
|
+
if (afterSeq !== void 0)
|
|
2733
|
+
query.afterSeq = afterSeq;
|
|
2734
|
+
if (peek)
|
|
2735
|
+
query.peek = 1;
|
|
2736
|
+
let result;
|
|
2737
|
+
try {
|
|
2738
|
+
result = await context.client.http.get(`/agent-tasks/${encodeURIComponent(taskId)}/thread/inbox`, query);
|
|
2739
|
+
} catch (error) {
|
|
2740
|
+
const code = error?.code ?? error?.response?.data?.code;
|
|
2741
|
+
if (code === "CHECKPOINT_UNSUPPORTED" || code === "CHECKPOINT_NO_ACTIVE_TURN") {
|
|
2742
|
+
throw new CliError(String(code), EXIT.OTHER, `${error?.message ?? "\u5F53\u524D\u4E0D\u53EF\u62C9\u53D6\u68C0\u67E5\u70B9\u6D88\u606F"}\uFF08\u6CA1\u6709\u8FDB\u884C\u4E2D\u7684\u56DE\u5408\u65F6\u65E0\u9700\u68C0\u67E5\u70B9\uFF1B\u7B49\u5F85\u672C\u8F6E\u7ED3\u675F\u540E\u7684\u7EED\u8DD1\u5373\u53EF\uFF09`);
|
|
2743
|
+
}
|
|
2744
|
+
throw error;
|
|
2745
|
+
}
|
|
2746
|
+
const messages = (result?.messages ?? []).map((m) => ({
|
|
2747
|
+
seq: m.seq,
|
|
2748
|
+
author: m.authorName ?? (m.authorType === "user" ? "\u4EBA" : m.authorType === "system" ? "\u5E73\u53F0" : "\u8FD0\u884C\u65F6"),
|
|
2749
|
+
state: m.deliveryState ?? "consumed",
|
|
2750
|
+
content: m.content
|
|
2751
|
+
}));
|
|
2752
|
+
return {
|
|
2753
|
+
taskId,
|
|
2754
|
+
turnNo: result?.turnNo ?? 1,
|
|
2755
|
+
consumed: result?.consumed === true,
|
|
2756
|
+
count: messages.length,
|
|
2757
|
+
messages,
|
|
2758
|
+
...messages.length === 0 ? { hint: "\u672C\u7EBF\u7A0B\u6CA1\u6709\u65B0\u6D88\u606F\uFF1A\u6309\u539F\u8BA1\u5212\u7EE7\u7EED\uFF0C\u4E0D\u8981\u5C55\u5F00\u8BA8\u8BBA\u3002" } : {}
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
throw usage("usage: morula task <list|create|get|update|delete|branch|state|analysis-verdict|inbox>");
|
|
2618
2762
|
}
|
|
2619
2763
|
function taskInput(flags, create) {
|
|
2620
2764
|
const description = flagString(flags, "description");
|
|
@@ -2683,7 +2827,8 @@ var HELP = {
|
|
|
2683
2827
|
"delete <id>": "hard delete task",
|
|
2684
2828
|
"branch <id> --name <name> [--repo <owner/name>]": "report the final branch name for an agent task (machine protocol; agent naming, runtimeId from env; --repo: secondary repo in a multi-repo task)",
|
|
2685
2829
|
"state <id> --state <code>": "self-transition task state (machine protocol; agent drives todo\u2192in_progress\u2192review, runtimeId from env)",
|
|
2686
|
-
"analysis-verdict <taskId> --verdict <ready|blocked> [--reason <text>]": "report the task-analysis verdict (machine protocol; ready \u2192 platform auto-starts development, blocked \u2192 task stays in analysis)"
|
|
2830
|
+
"analysis-verdict <taskId> --verdict <ready|blocked> [--reason <text>]": "report the task-analysis verdict (machine protocol; ready \u2192 platform auto-starts development, blocked \u2192 task stays in analysis)",
|
|
2831
|
+
"inbox [--peek]": "checkpoint: pull this thread's unconsumed messages into the current turn (machine protocol; --peek reads without marking them consumed)"
|
|
2687
2832
|
},
|
|
2688
2833
|
"space": {
|
|
2689
2834
|
"list [--page <n>] [--page-size <n>] [--keyword <text>]": "list visible spaces",
|
|
@@ -2708,15 +2853,17 @@ var HELP = {
|
|
|
2708
2853
|
"report": {
|
|
2709
2854
|
"create --title <text> [--content <markdown> | --content-file <path>] [--space <id>] [--autopilot <id>]": "create an autopilot report (\u6B63\u6587\u4E8C\u9009\u4E00\uFF1A--content \u77ED\u6B63\u6587 / --content-file \u957F Markdown\uFF1B--autopilot \u7701\u7565\u65F6\u6309\u5F53\u524D\u4EFB\u52A1\u53CD\u67E5\u6240\u5C5E\u81EA\u52A8\u5316)",
|
|
2710
2855
|
"get <id>": "get report details (\u542B Markdown \u6B63\u6587)",
|
|
2711
|
-
"list --autopilot <id> [--page <n>] [--page-size <n>]
|
|
2712
|
-
"archive <id>": "archive report (\u5F52\u6863\u540E\u9ED8\u8BA4\u5217\u8868\u4E0D\u518D\u5C55\u793A)"
|
|
2856
|
+
"list --autopilot <id> [--page <n>] [--page-size <n>]": "list reports of an autopilot"
|
|
2713
2857
|
},
|
|
2714
2858
|
"repo": {
|
|
2715
2859
|
"list --space <id>": "list task-space VCS connections (available repos; note: the system prompt already lists them for a task \u2014 you rarely need this)",
|
|
2716
2860
|
"checkout <taskId> <repoFullName> [--ref <branch|sha>]": "materialize a repo into the task workdir (local daemon endpoint preferred, works same-turn; falls back to server machine protocol which resolves scope + workContext)",
|
|
2717
2861
|
"prepare <taskId> <repoFullName>": "materialize a secondary repo into the task workdir (same level as the primary repo dir)",
|
|
2718
2862
|
"mr create <repoFullName> --branch <branch> [--base <base>] [--task-code <code>]": "create a GitLab MR for the task via the server channel (target project forced to the bound repo, PR linked immediately; push the branch first)",
|
|
2719
|
-
"mr list <taskId> [--state opened|closed|merged] [--limit <n>]": "list the task-space repos' merge requests (read-only; never approves/merges)"
|
|
2863
|
+
"mr list <taskId> [--state opened|closed|merged] [--limit <n>] [--author <username>] [--repo <owner/name>]": "list the task-space repos' merge requests (read-only; includes createdAt/updatedAt/author; never approves/merges)",
|
|
2864
|
+
"mr comment <taskId> --mr <iid> [--repo <owner/name>] (--content <text> | --content-file <path>)": "write a review comment on an MR/PR (write-only: never approves or merges). \u5199\u4E4B\u524D\u5FC5\u987B\u5148 `morula repo mr get <taskId> --mr <iid> --notes` \u8BFB\u5DF2\u6709\u8BA8\u8BBA\uFF1A\u82E5\u672C\u81EA\u52A8\u5316\u5DF2\u5C31\u540C\u4E00\u95EE\u9898\u7559\u8FC7\u7ED3\u8BBA\u4E14\u7ED3\u8BBA\u672A\u53D8\uFF0C\u4E0D\u8981\u518D\u8BC4\u8BBA\u3002\u5185\u5BB9\u4E3A\u7ED3\u8BBA + \u5173\u952E\u95EE\u9898\uFF08\u6587\u4EF6:\u884C + \u5EFA\u8BAE\uFF09\uFF0C\u672B\u5C3E\u52A0\u7F72\u540D\u884C\u300C> \u7531\u81EA\u52A8\u5316\u300C<\u540D\u79F0>\u300D\u4E8E <\u65E5\u671F> \u751F\u6210\u300D\uFF1B\u4E0D\u8981\u8D34\u5927\u6BB5\u65E5\u5FD7\u6216\u590D\u8FF0 diff\uFF1B\u957F\u5185\u5BB9\u5199\u6210\u62A5\u544A\u540E\u7528\u6458\u8981\u5F15\u7528\uFF08morula report create \u2192 \u8BC4\u8BBA\u91CC\u7ED9\u8981\u70B9\uFF09",
|
|
2865
|
+
"mr get <taskId> --mr <iid> [--repo <owner/name>] [--ci] [--notes]": "get a single MR detail (read-only; --ci attaches pipeline status where none = no pipeline, not an error; --notes attaches existing comments \u2014 read this before writing a comment to avoid duplicates)",
|
|
2866
|
+
"mr diff <taskId> --mr <iid> [--repo <owner/name>] [--max-files <n>] [--max-chars <n>]": "read an MR diff via the server (read-only; works without a local checkout; truncated flag is set when output was capped)"
|
|
2720
2867
|
},
|
|
2721
2868
|
"attachment": {
|
|
2722
2869
|
"upload <file> [--object <id>] [--comment <id>] [--ag-message] [--task <agentTaskId>] [--inline]": "upload a local file and attach it to a task (--object) / comment (--comment) / chat message (--ag-message); for pure chat tasks pass --task <agentTaskId> to attach to the chat session; omit target to auto-merge (object if bound, else chat message)",
|
package/dist/plugin/plugin.json
CHANGED
|
@@ -141,6 +141,7 @@ allowed-tools: Bash(morula *), Bash(git *), Bash(gh *)
|
|
|
141
141
|
morula repo checkout <taskId> <repoFullName> [--ref <branch|sha>] # 拉取/切换仓库(返回物化路径,见 0.5)
|
|
142
142
|
morula repo prepare <taskId> <repoFullName> # 多仓库任务:物化其他仓库到任务工作区根下(见 0.5)
|
|
143
143
|
morula comment list --object <objectId> # 读评论(任务=objectId)
|
|
144
|
+
morula task inbox [--peek] # 运行中检查点:取本线程未消费消息,纳入本回合(见 1.3)
|
|
144
145
|
# 注:最终总结评论由服务端统一发送(作者=智能体),不要调用 comment add 自行置评。
|
|
145
146
|
```
|
|
146
147
|
- **可用仓库清单**由系统提示直接给出(「可用仓库」/「多仓库任务」段),**不需要**自己查;只有在系统提示未给出、又确实需要枚举时才用
|
|
@@ -186,6 +187,44 @@ morula report create --title "<一句话标题>" --content "<Markdown 正文>"
|
|
|
186
187
|
- 报告落库后创建者与订阅者会收到轻通知(只含标题与跳转);
|
|
187
188
|
- 不产出也没关系:任务终态时平台会用你的最终输出兜底落一份报告(但兜底不如你主动组织的内容清晰,信息类产出**优先主动产出**)。
|
|
188
189
|
|
|
190
|
+
## 1.3 运行中反馈:检查点与打断(本回合内生效)
|
|
191
|
+
|
|
192
|
+
用户可以在**你正在运行的这一个回合内**发来意见——不必等本轮结束、也不必重开任务。你要在两处配合:
|
|
193
|
+
|
|
194
|
+
**① 主动查检查点(`morula task inbox`)——只在这四个时机查,不要更频繁**
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
morula task inbox # 取走本线程未消费的消息(服务端标记「本回合已消费」)
|
|
198
|
+
morula task inbox --peek # 只看不取(确认有没有新意见,不标记消费)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
四个时机(闭环枚举,**不要**每一步都查):
|
|
202
|
+
|
|
203
|
+
1. 一批文件改动**完成之后**(准备进入下一批之前);
|
|
204
|
+
2. `git commit` **之前**;
|
|
205
|
+
3. `git push` / 建 PR **之前**;
|
|
206
|
+
4. 收到「运行中被打断」提示时(见 ②)。
|
|
207
|
+
|
|
208
|
+
拿到消息后:
|
|
209
|
+
|
|
210
|
+
- **有消息** → 逐条纳入本回合,按用户的意见调整,并在最终总结里**回执式说明**(「已按要求改为 X」);
|
|
211
|
+
- **没有消息** → **立即继续手上的事,不要展开讨论、不要复述检查过程、不要在总结里提「检查过没有新意见」**;
|
|
212
|
+
- 用户的意见**不得推翻它没涉及的部分**:已完成的、与意见无关的改动保留;意见与原始需求冲突时**以意见为准**并在总结里说明差异;
|
|
213
|
+
- 不要反复轮询(一次检查就是一次工具调用,有开销)。
|
|
214
|
+
|
|
215
|
+
**② 被打断时(用户按了「立即打断」)你会在同一会话收到新的指令**
|
|
216
|
+
|
|
217
|
+
- 上一回合在**安全点**被中断:其中的工具进度已丢弃,**被中断的工具调用没有结果**(时间线标为已中断)——需要时重新执行,不要假定它已成功;
|
|
218
|
+
- 新消息就是用户的最新意见:先按它调整方向,再继续完成本任务;工作区文件与 git 状态仍在,不用重做已完成的部分。
|
|
219
|
+
|
|
220
|
+
**③ 用户意见与「要不要返工」由你自己判断,平台不代做**
|
|
221
|
+
|
|
222
|
+
- 平台**不会**因为用户说了句话就回退阶段、改任务状态或派返工任务——**那是你的判断**:评估后认为必须推倒重做,就按你自己的协议表达(评审阶段用 `morula review submit` 给出结论;分析阶段用 `morula task analysis-verdict`),状态流转由服务端按既有规则驱动;
|
|
223
|
+
- 你只跟**当前阶段**对话:消息就是发给当前阶段负责智能体的。**不要**因为收到意见就去操作别的阶段或别的对象的资源;
|
|
224
|
+
- **评审阶段你只做评审**(看 diff、核对目标、出结论),**不要改代码、不要建分支/提 PR**。
|
|
225
|
+
|
|
226
|
+
> 为什么是这四个时机:检查是**工具调用**,有 token 与延迟开销;「无消息立即继续」是为了不让检查点把主线带偏。
|
|
227
|
+
|
|
189
228
|
## 2. PR 链接与关单意图是两套不同的契约
|
|
190
229
|
|
|
191
230
|
- **链接通道**:PR 的 title / branch / body 中出现任务编号 → 服务端自动关联 PR 到任务。
|
|
@@ -275,6 +314,20 @@ morula report create --title "<一句话标题>" --content "<Markdown 正文>"
|
|
|
275
314
|
- **不要手动流转本任务状态**(不要调 `morula task state`):评审结论由服务端据此推进。
|
|
276
315
|
- **必须上报**:漏报会让任务停在「待评审」,开发无法进入下一轮。
|
|
277
316
|
|
|
317
|
+
## 4.3 在 MR/PR 上留评审意见(巡检/审查类任务,非评审任务时)
|
|
318
|
+
|
|
319
|
+
> **仅当本任务是巡检/审查类(如自动化定时 PR Review),且确实发现值得提出的问题时适用**;开发/分析/问答任务跳过本节。
|
|
320
|
+
|
|
321
|
+
- **正确姿势**:用 `morula repo mr comment`(服务端机器协议,只写评论:不 approve、不 merge、不 close),**一条 MR 最多一条评论**:
|
|
322
|
+
```bash
|
|
323
|
+
morula repo mr get <taskId> --mr <iid> --notes # 写之前先读已有讨论(平台不做去重,重复由你避免)
|
|
324
|
+
morula repo mr comment <taskId> --mr <iid> --content-file <path>
|
|
325
|
+
```
|
|
326
|
+
- 内容为**结论 + 关键问题**(文件:行 + 问题 + 建议),不要贴大段日志或复述 diff;长内容写成报告(`morula report create`)后用摘要引用;
|
|
327
|
+
- 末尾带署名行 `> 由自动化「<自动化名称>」于 <日期> 生成`——供人与 agent 下一轮识别这是本自动化的历史评论;
|
|
328
|
+
- 若本自动化已就同一问题留过评论、结论与这次一致 → **不要重复评论**;
|
|
329
|
+
- ⛔ **不要用 `morula review submit`**(那是评审任务的机器协议,会驱动业务任务状态回退)。
|
|
330
|
+
|
|
278
331
|
## 5. 评论交付纪律
|
|
279
332
|
|
|
280
333
|
- **何时发评论**:**最终总结评论由服务端统一以智能体身份发送**(任务完成时自动发布到任务对象,作者=当前智能体)。**你不需要、也不应调用 `morula comment add`**——只要在最终输出里给出用户关心的结论即可,服务端会作为该智能体的完成评论发布。chat 会话无 objectId 的任务(纯对话派发)不回评论——最终交付由 daemon 回填 assistant 消息。
|
package/dist/plugin/skills/morula-working-on-issues/references/working-on-issues-source-map.md
CHANGED
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
|
|
15
15
|
| 契约 | 实现 | 位置 |
|
|
16
16
|
|------|------|------|
|
|
17
|
-
| CLI 读评论 | `morula comment list --object <objectId>` | `
|
|
18
|
-
| CLI 写评论(**契约禁止 agent 自行调用**:完成评论由服务端统一发送,见本文件第 5 条) | `morula comment add`(命令存在,但本契约要求不调用) | `
|
|
19
|
-
| CLI 任务详情(**无参**取本任务;带参取业务对象) | `morula task get` → `GET /agent-tasks/:id`;`morula task get <objectId>` → `GET /objects/:id` | `
|
|
17
|
+
| CLI 读评论 | `morula comment list --object <objectId>` | `packages/morula-cli/src/commands/comment.ts` |
|
|
18
|
+
| CLI 写评论(**契约禁止 agent 自行调用**:完成评论由服务端统一发送,见本文件第 5 条) | `morula comment add`(命令存在,但本契约要求不调用) | `packages/morula-cli/src/commands/comment.ts` |
|
|
19
|
+
| CLI 任务详情(**无参**取本任务;带参取业务对象) | `morula task get` → `GET /agent-tasks/:id`;`morula task get <objectId>` → `GET /objects/:id` | `packages/morula-cli/src/commands/task.ts` |
|
|
20
20
|
| daemon 注入凭据(agent 无需自行登录) | runner 注入 `env` 给 Provider 子进程 | `packages/runtime/src/daemon/runner.ts` |
|
|
21
|
-
| 系统提示注入(阶段职责 / 目标 / 仓库清单 / 工具指引);任务详情由 `morula task get`(无参)获取 | `buildTaskPromptWithContract`(`runner.ts`,唯一进 dim 的 messages[0]);CLI `
|
|
21
|
+
| 系统提示注入(阶段职责 / 目标 / 仓库清单 / 工具指引);任务详情由 `morula task get`(无参)获取 | `buildTaskPromptWithContract`(`runner.ts`,唯一进 dim 的 messages[0]);CLI `packages/morula-cli/src/commands/task.ts`(resolveTaskId → `GET /agent-tasks/:id`) | `packages/runtime/src/daemon/runner.ts` |
|
|
22
|
+
| 运行中检查点(本回合内取用户意见) | CLI `morula task inbox [--peek]` → `GET /agent-tasks/:id/thread/inbox`(机器协议;默认取走即消费,落 `consumed` + 当回合号);契约时机见本 SKILL 1.3 | `packages/morula-cli/src/commands/task.ts`、`src/modules/agent-task/chat/object-agent-chat.controller.ts`、`object-agent-message.service.ts`(`getThreadInbox`) |
|
|
23
|
+
| 运行中打断(同一会话重开一轮) | daemon 在安全点(`inFlightTools === 0`)`session/cancel` → `session/resume` → 用新意见重开 prompt;回合号与中断点上报为 `turn_started` / `turn_interrupted` 事件 | `packages/runtime/src/daemon/acp/dim-acp-executor.ts`(回合循环)、`runner.ts`(`buildSteerPort`)、`src/modules/agent-task/chat/object-agent-message.service.ts`(`steerDeliveryStrategy`) |
|
|
22
24
|
|
|
23
25
|
## 3. 改代码默认产 PR(分支纪律,Agent 取名)
|
|
24
26
|
|
|
@@ -36,13 +38,13 @@
|
|
|
36
38
|
| 契约 | 实现 | 位置 |
|
|
37
39
|
|------|------|------|
|
|
38
40
|
| merge 自动关单(服务端驱动状态) | webhook merged → closeIntent → Object 完成 | `src/modules/vcs-integration/webhook/vcs-event.service.ts` |
|
|
39
|
-
| agent 不调 CLI 改状态 | 协作契约第 4 条。**注意:CLI 有 `morula task state <id> --state <code>`**(机器协议 `POST /agent-tasks/:id/state`,供平台/自流转使用)——是**契约要求 agent 不调用**它,不是「CLI 没有这个命令」 | `
|
|
41
|
+
| agent 不调 CLI 改状态 | 协作契约第 4 条。**注意:CLI 有 `morula task state <id> --state <code>`**(机器协议 `POST /agent-tasks/:id/state`,供平台/自流转使用)——是**契约要求 agent 不调用**它,不是「CLI 没有这个命令」 | `packages/morula-cli/src/commands/task.ts`(action `state`)、`src/modules/agent-task/agent-task.controller.ts`(端点) |
|
|
40
42
|
|
|
41
43
|
## 4.1 评审任务结论回传(morula review submit)
|
|
42
44
|
|
|
43
45
|
| 契约 | 实现 | 位置 |
|
|
44
46
|
|------|------|------|
|
|
45
|
-
| 评审结论机器协议(approved / changes_requested / commented,changes_requested 必须带 details) | `morula review submit <objectId> --verdict ... [--details] [--pr-number]` → `POST /agent-collaborations/review/submit`(服务端校验调用者 = 评审任务后落库并驱动回环) | `
|
|
47
|
+
| 评审结论机器协议(approved / changes_requested / commented,changes_requested 必须带 details) | `morula review submit <objectId> --verdict ... [--details] [--pr-number]` → `POST /agent-collaborations/review/submit`(服务端校验调用者 = 评审任务后落库并驱动回环) | `packages/morula-cli/src/commands/review.ts`、`src/modules/agent-task/review-loop.service.ts` |
|
|
46
48
|
| 评审对象(objectId)经 AgentTask 本体下发(`morula task get` 无参返回) | AgentTask 记录 `objectId` 字段(enqueue 时写入) | `src/modules/agent-task/review-loop.service.ts` |
|
|
47
49
|
|
|
48
50
|
## 5. 评论交付纪律(一条最终评论,结果上平台)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcships/morula-runtime",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.4",
|
|
5
5
|
"description": "Device-level CLI for the Morula local agent runtime (daemon/portal/models/auth) and the Morula business CLI (morula), distributed via npm",
|
|
6
6
|
"bin": {
|
|
7
7
|
"morula-runtime": "./bin/morula-runtime.js",
|