@jennie-shawn/starwork 0.1.0-alpha.24 → 0.1.0-alpha.25

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.
Files changed (35) hide show
  1. package/README.md +4 -2
  2. package/cli/src/cli.js +535 -4
  3. package/cli/test/init.test.js +197 -1
  4. package/docs/agent-install-guide.md +5 -3
  5. package/docs/alpha-test-guide.md +40 -2
  6. package/docs/cli-skill-registry.html +5 -5
  7. package/docs/m2.6-alpha-core-flows-optimization-spec.md +2 -2
  8. package/docs/multiagent-ai-install-playbook.md +7 -7
  9. package/docs/multiagent-user-guide.md +29 -0
  10. package/docs/starwork-multiagent-install-guide.md +18 -18
  11. package/package.json +2 -1
  12. package/skills/README.md +7 -1
  13. package/skills/starwork/references/install.md +1 -1
  14. package/skills/starworkMultiagent/SKILL.md +39 -0
  15. package/skills/starworkMultiagent-skill-plan.md +1 -1
  16. package/skills/starworkMultiagent-spec.md +25 -3
  17. package/skills-next/README.md +49 -0
  18. package/skills-next/starwork/SKILL.md +82 -0
  19. package/skills-next/starwork/references/install.md +64 -0
  20. package/skills-next/starwork/references/routing.md +44 -0
  21. package/skills-next/starworkDoctor/SKILL.md +489 -0
  22. package/skills-next/starworkDoctor/agents/openai.yaml +7 -0
  23. package/skills-next/starworkDoctor/references/agent-rules-template.md +64 -0
  24. package/skills-next/starworkDoctor/references/hub-upgrade.md +73 -0
  25. package/skills-next/starworkDoctor/references/response-guide.md +57 -0
  26. package/skills-next/starworkDoctor/references/rules-extraction-guide.md +92 -0
  27. package/skills-next/starworkDoctor-spec.md +803 -0
  28. package/skills-next/starworkInit/SKILL.md +620 -0
  29. package/skills-next/starworkInit/agents/openai.yaml +7 -0
  30. package/skills-next/starworkInit-spec.md +664 -0
  31. package/skills-next/starworkKnowledge/SKILL.md +196 -0
  32. package/skills-next/starworkKnowledge/agents/openai.yaml +3 -0
  33. package/skills-next/starworkMultiagent/SKILL.md +640 -0
  34. package/skills-next/starworkMultiagent/agents/openai.yaml +7 -0
  35. package/skills-next/starworkMultiagent-spec.md +308 -0
package/README.md CHANGED
@@ -128,12 +128,14 @@ StarWork 的基础能力是“让 AI 读懂并安全接手项目”。在此基
128
128
 
129
129
  如果你希望 AI 更懂 StarWork 的使用流程,可以安装 StarWork Skills。安装后,普通请求先交给 `starwork` 主入口;明确的知识库、多智能体、诊断或初始化场景,也可以直接点名对应专家能力。
130
130
 
131
- 当前默认面向 Codex:
131
+ 当前默认面向 Codex,安装 stable / latest 系统 Skills
132
132
 
133
133
  ```bash
134
- npx skills add jennie-shawn/StarWork -g -a codex -y
134
+ npx skills add https://github.com/jennie-shawn/StarWork/tree/main/product/skills --full-depth -g -a codex -y
135
135
  ```
136
136
 
137
+ Workflow next 内测用户使用独立目录 `product/skills-next/`;普通用户不要用 stable Skill 目录测试 workflow next。
138
+
137
139
  安装后,AI 会拥有一个 StarWork 主入口和几个专家 Skills,可以更自然地帮你:
138
140
 
139
141
  - 判断你现在应该走哪个 StarWork 能力。
package/cli/src/cli.js CHANGED
@@ -902,6 +902,10 @@ async function lanesCommand(argv) {
902
902
  await lanesStatus(argv.slice(1));
903
903
  return;
904
904
  }
905
+ if (subcommand === "upgrade") {
906
+ await lanesUpgrade(argv.slice(1));
907
+ return;
908
+ }
905
909
  if (subcommand === "read") {
906
910
  await lanesRead(argv.slice(1));
907
911
  return;
@@ -946,6 +950,7 @@ async function lanesInit(argv) {
946
950
  }
947
951
  const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
948
952
  assertAgentDocsReadyForMultiagent(workspaceRoot);
953
+ assertMultiagentWritable(workspaceRoot);
949
954
  const lanes = parseLaneList(options.lanes || "").map((id) => ({
950
955
  lane: id,
951
956
  purpose: "待补充",
@@ -978,6 +983,7 @@ async function lanesAdd(argv) {
978
983
  }
979
984
  const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
980
985
  assertAgentDocsReadyForMultiagent(workspaceRoot);
986
+ assertMultiagentWritable(workspaceRoot);
981
987
  const registry = readLanesRegistry(workspaceRoot);
982
988
  const collaboration = getCollaborationPaths(readWorkspaceState(workspaceRoot));
983
989
  if (registry.lanes.some((lane) => lane.lane === laneId)) {
@@ -1012,6 +1018,7 @@ async function lanesBind(argv) {
1012
1018
  const laneId = normalizeLaneId(options._?.[0], "lane");
1013
1019
  const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1014
1020
  assertAgentDocsReadyForMultiagent(workspaceRoot);
1021
+ assertMultiagentWritable(workspaceRoot);
1015
1022
  const registry = readLanesRegistry(workspaceRoot);
1016
1023
  const lane = findLaneOrThrow(registry.lanes, laneId);
1017
1024
  const session = resolveLaneSession(options);
@@ -1098,6 +1105,7 @@ async function lanesRelease(argv) {
1098
1105
  }
1099
1106
  const laneId = normalizeLaneId(options._?.[0], "lane");
1100
1107
  const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1108
+ assertMultiagentWritable(workspaceRoot);
1101
1109
  const registry = readLanesRegistry(workspaceRoot);
1102
1110
  const lane = findLaneOrThrow(registry.lanes, laneId);
1103
1111
  const nextLanes = registry.lanes.map((item) => item.lane === laneId ? { ...item, current_session: "unbound" } : item);
@@ -1118,8 +1126,9 @@ async function lanesStatus(argv) {
1118
1126
  return;
1119
1127
  }
1120
1128
  const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1121
- const registry = readLanesRegistry(workspaceRoot);
1122
- const shared = readSharedContext(workspaceRoot);
1129
+ const compatibilityReport = inspectMultiagentCompatibility(workspaceRoot);
1130
+ const registry = { lanes: compatibilityReport.lanes };
1131
+ const shared = compatibilityReport.shared;
1123
1132
  if (options.host) {
1124
1133
  const report = await collectLanesHostStatus(workspaceRoot, registry, { load: Boolean(options.load), transcript: options.transcript });
1125
1134
  if (options.json) {
@@ -1135,7 +1144,10 @@ async function lanesStatus(argv) {
1135
1144
  workspace_root: workspaceRoot,
1136
1145
  lanes: registry.lanes,
1137
1146
  shared_outputs: shared.outputs,
1138
- cross_lane_requests: shared.requests
1147
+ cross_lane_requests: shared.requests,
1148
+ multiagent: {
1149
+ compatibility: compatibilityReport.compatibility
1150
+ }
1139
1151
  }, null, 2));
1140
1152
  return;
1141
1153
  }
@@ -1162,6 +1174,35 @@ async function lanesStatus(argv) {
1162
1174
  console.log("待处理协作请求:");
1163
1175
  openRequests.forEach((request) => console.log(`- ${request.from} -> ${request.to}: ${request.request} (${request.status})`));
1164
1176
  }
1177
+ printMultiagentCompatibilitySummary(compatibilityReport);
1178
+ }
1179
+
1180
+ async function lanesUpgrade(argv) {
1181
+ const options = parseArgs(argv);
1182
+ if (options.help) {
1183
+ printLanesUpgradeHelp();
1184
+ return;
1185
+ }
1186
+ const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1187
+ const plan = buildMultiagentMigrationPlan(workspaceRoot);
1188
+ if (options.json) {
1189
+ console.log(JSON.stringify(renderMultiagentMigrationPlanJson(plan), null, 2));
1190
+ } else {
1191
+ printMultiagentMigrationPlan(plan, options.dryRun || !options.yes);
1192
+ }
1193
+ if (options.dryRun || (!options.yes && !options.dryRun)) {
1194
+ return;
1195
+ }
1196
+ if (!plan.safeToApply) {
1197
+ throw new Error("检测到冲突或无法安全迁移的旧结构,不能执行 --yes。请先人工处理冲突。");
1198
+ }
1199
+ await confirmOrThrow(options, "是否执行 MultiAgent 结构迁移?");
1200
+ applyMultiagentMigrationPlan(plan);
1201
+ if (!options.json) {
1202
+ console.log("");
1203
+ console.log("MultiAgent 结构迁移已完成。");
1204
+ console.log(`迁移报告:${plan.reportPath}`);
1205
+ }
1165
1206
  }
1166
1207
 
1167
1208
  async function collectLanesHostStatus(workspaceRoot, registry, options = {}) {
@@ -1283,6 +1324,7 @@ async function lanesInstruct(argv) {
1283
1324
  const fromLane = normalizeLaneId(options.from || "user", "from lane");
1284
1325
  if (!options.message) throw new Error("multiagent instruct 需要 --message <text>。");
1285
1326
  const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1327
+ assertMultiagentWritable(workspaceRoot);
1286
1328
  const state = readWorkspaceState(workspaceRoot);
1287
1329
  const collaboration = getCollaborationPaths(state);
1288
1330
  const registry = readLanesRegistry(workspaceRoot);
@@ -1626,6 +1668,7 @@ async function lanesRequestRecord(argv) {
1626
1668
  const hostDelivery = normalizeHostDeliveryStatus(options.hostDelivery || options.status || "");
1627
1669
  const deliveryTool = normalizeMarkdownCell(options.deliveryTool || "manual");
1628
1670
  const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1671
+ assertMultiagentWritable(workspaceRoot);
1629
1672
  const state = readWorkspaceState(workspaceRoot);
1630
1673
  const collaboration = getCollaborationPaths(state);
1631
1674
  const registry = readLanesRegistry(workspaceRoot);
@@ -1707,6 +1750,7 @@ async function lanesShare(argv) {
1707
1750
  throw new Error("multiagent share 需要 --audience <lane-list>。");
1708
1751
  }
1709
1752
  const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1753
+ assertMultiagentWritable(workspaceRoot);
1710
1754
  const state = readWorkspaceState(workspaceRoot);
1711
1755
  const collaboration = getCollaborationPaths(state);
1712
1756
  const registry = readLanesRegistry(workspaceRoot);
@@ -1891,6 +1935,7 @@ function collectDoctorResult(targetDir, options = {}) {
1891
1935
  checkAgentRuleReferences(result, workspaceRoot);
1892
1936
  checkAgentDocsDraftState(result, workspaceRoot);
1893
1937
  checkHostAdapters(result, workspaceRoot, state, options);
1938
+ checkMultiagentCompatibility(result, workspaceRoot);
1894
1939
  result.ok = result.summary.fail === 0;
1895
1940
  result.strict_ok = result.ok;
1896
1941
  result.exitCode = result.ok ? 0 : 1;
@@ -1911,6 +1956,7 @@ function createDoctorResult(targetDir) {
1911
1956
  workspace: null,
1912
1957
  skills: null,
1913
1958
  upgrade: null,
1959
+ multiagent: null,
1914
1960
  knowledge: null,
1915
1961
  inventory: null,
1916
1962
  signals: null,
@@ -4895,6 +4941,32 @@ function addLegacyChecks(result, legacy) {
4895
4941
  }
4896
4942
  }
4897
4943
 
4944
+ function checkMultiagentCompatibility(result, workspaceRoot) {
4945
+ const report = inspectMultiagentCompatibility(workspaceRoot);
4946
+ result.multiagent = {
4947
+ status: report.compatibility.status === "current" ? "pass" : "warn",
4948
+ lanes_count: report.lanes.length,
4949
+ compatibility: report.compatibility
4950
+ };
4951
+ if (report.compatibility.status === "current") {
4952
+ if (report.lanes.length) {
4953
+ addCheck(result, "multiagent.compatibility.current", "pass", "MultiAgent 协作记录为当前结构。", ".starwork/agent-lanes/state.json");
4954
+ } else {
4955
+ addCheck(result, "multiagent.compatibility.not_enabled", "info", "当前工作台尚未启用 MultiAgent;这不是结构问题。");
4956
+ }
4957
+ return;
4958
+ }
4959
+ if (report.compatibility.status === "blocked_conflict") {
4960
+ addCheck(result, "multiagent.compatibility.conflict", "warn", `检测到旧版 MultiAgent 协作记录存在冲突:${report.conflicts.join(";")}`, ".starwork/agent-lanes/state.json");
4961
+ return;
4962
+ }
4963
+ if (report.compatibility.status === "unknown_partial") {
4964
+ addCheck(result, "multiagent.compatibility.partial", "warn", "检测到部分旧版 MultiAgent 协作记录,但机器状态可能损坏;可以读取可见 AI 岗位,写入前需要人工确认。", ".starwork/agent-lanes/state.json");
4965
+ return;
4966
+ }
4967
+ addCheck(result, "multiagent.compatibility.migration_required", "warn", `检测到旧版 MultiAgent 协作记录,目前可以读取已有 AI 岗位;写入新岗位、绑定会话或记录交接前,请先预览迁移:${report.compatibility.migration.dry_run_command}`, ".starwork/agent-lanes/state.json");
4968
+ }
4969
+
4898
4970
  function addCheck(result, id, level, message, checkPath) {
4899
4971
  result.summary[level] += 1;
4900
4972
  result.checks.push({
@@ -7440,6 +7512,440 @@ function readSharedContext(workspaceRoot) {
7440
7512
  };
7441
7513
  }
7442
7514
 
7515
+ function inspectMultiagentCompatibility(workspaceRoot) {
7516
+ const workspaceState = readWorkspaceState(workspaceRoot);
7517
+ const collaboration = getCollaborationPaths(workspaceState);
7518
+ const stateInfo = readAgentLanesStateRaw(workspaceRoot);
7519
+ const currentRegistry = readLanesRegistryTolerant(workspaceRoot, collaboration.registry, "current_language_markdown");
7520
+ const currentShared = readSharedContextTolerant(workspaceRoot, collaboration.shared, "current_language_markdown");
7521
+ const alternate = getAlternateCollaborationPaths(collaboration.root);
7522
+ const alternateRegistry = alternate ? readLanesRegistryTolerant(workspaceRoot, alternate.registry, "alternate_language_markdown") : emptyRegistryRead(null, "alternate_language_markdown");
7523
+ const alternateShared = alternate ? readSharedContextTolerant(workspaceRoot, alternate.shared, "alternate_language_markdown") : emptySharedRead(null, "alternate_language_markdown");
7524
+ const legacyClasses = [];
7525
+ const conflicts = [];
7526
+ const warnings = [];
7527
+ const readSources = [];
7528
+ if (stateInfo.exists) readSources.push(".starwork/agent-lanes/state.json");
7529
+ if (currentRegistry.exists) readSources.push(collaboration.registry);
7530
+ if (currentShared.exists) readSources.push(collaboration.shared);
7531
+
7532
+ if (stateInfo.exists && stateInfo.parseError) {
7533
+ legacyClasses.push("malformed_state");
7534
+ } else if (stateInfo.exists && stateInfo.version === 1) {
7535
+ legacyClasses.push("state_v1_current");
7536
+ } else if (stateInfo.exists && stateInfo.version == null) {
7537
+ legacyClasses.push("state_unversioned");
7538
+ }
7539
+ if (!stateInfo.exists && currentRegistry.lanes.length) legacyClasses.push("markdown_only");
7540
+ if (stateInfo.exists && !stateInfo.parseError && stateInfo.lanes.length && !currentRegistry.exists) legacyClasses.push("state_only");
7541
+ if ((alternateRegistry.lanes.length || alternateShared.requests.length) && (currentRegistry.lanes.length || currentShared.requests.length || stateInfo.lanes.length)) {
7542
+ legacyClasses.push("mixed_language_paths");
7543
+ conflicts.push(`当前语言协作路径和另一套语言协作路径都存在 MultiAgent 记录:${collaboration.root} / ${alternate.root}`);
7544
+ }
7545
+ const stateByLane = new Map(stateInfo.lanes.map((lane) => [lane.lane, lane]));
7546
+ for (const lane of currentRegistry.lanes) {
7547
+ const stateLane = stateByLane.get(lane.lane);
7548
+ if (!stateLane) continue;
7549
+ if (stateLane.current_session && lane.current_session && stateLane.current_session !== lane.current_session) {
7550
+ legacyClasses.push("conflicting_lane");
7551
+ conflicts.push(`lane ${lane.lane} 的 state.json session ${stateLane.current_session} 与 agent-lanes.md ${lane.current_session} 不一致`);
7552
+ }
7553
+ }
7554
+ if (currentRegistry.exists && currentRegistry.parseWarning) warnings.push(currentRegistry.parseWarning);
7555
+ if (currentShared.exists && currentShared.parseWarning) warnings.push(currentShared.parseWarning);
7556
+ const lanes = mergeCompatibleLanes(currentRegistry.lanes, stateInfo.lanes);
7557
+ const shared = currentShared.exists ? currentShared.shared : { outputs: [], requests: [], agreements: [] };
7558
+ const status = resolveMultiagentCompatibilityStatus({ stateInfo, currentRegistry, lanes, conflicts, legacyClasses });
7559
+ const requiredForWrite = status === "migration_required_for_write" || status === "blocked_conflict" || status === "unknown_partial";
7560
+ return {
7561
+ workspaceRoot,
7562
+ collaboration,
7563
+ stateInfo,
7564
+ registry: currentRegistry,
7565
+ shared,
7566
+ alternateRegistry,
7567
+ alternateShared,
7568
+ lanes,
7569
+ conflicts,
7570
+ warnings,
7571
+ compatibility: {
7572
+ status,
7573
+ structure_version: stateInfo.version,
7574
+ target_structure_version: 1,
7575
+ legacy_classes: [...new Set(legacyClasses.filter((item) => item !== "state_v1_current" || status === "current"))],
7576
+ read_sources: readSources,
7577
+ required_for_write: requiredForWrite,
7578
+ migration: {
7579
+ required_for_write: requiredForWrite,
7580
+ available: ["migration_required_for_write", "current"].includes(status) && conflicts.length === 0,
7581
+ dry_run_command: `starwork multiagent upgrade --target ${workspaceRoot} --dry-run`
7582
+ },
7583
+ conflicts,
7584
+ warnings
7585
+ }
7586
+ };
7587
+ }
7588
+
7589
+ function readAgentLanesStateRaw(workspaceRoot) {
7590
+ const statePath = agentLanesStatePath(workspaceRoot);
7591
+ if (!fs.existsSync(statePath)) {
7592
+ return { exists: false, path: statePath, version: null, data: defaultAgentLanesState(), lanes: [], requests: [], parseError: null };
7593
+ }
7594
+ try {
7595
+ const data = JSON.parse(fs.readFileSync(statePath, "utf8"));
7596
+ const lanesObject = data.lanes && typeof data.lanes === "object" ? data.lanes : {};
7597
+ const lanes = Object.entries(lanesObject).map(([laneId, record]) => normalizeLaneRecord({
7598
+ lane: laneId,
7599
+ purpose: normalizeMarkdownCell(record?.purpose || "待补充"),
7600
+ current_session: normalizeMarkdownCell(record?.current_session || "unbound"),
7601
+ write_scope: normalizeMarkdownCell(record?.write_scope || "待补充"),
7602
+ worklog: record?.worklog || defaultLaneWorklogPath(laneId),
7603
+ workspace: record?.workspace || defaultLaneWorkspacePath(laneId)
7604
+ }));
7605
+ return {
7606
+ exists: true,
7607
+ path: statePath,
7608
+ version: Number.isInteger(data.version) ? data.version : null,
7609
+ data,
7610
+ lanes,
7611
+ requests: Array.isArray(data.requests) ? data.requests : [],
7612
+ parseError: null
7613
+ };
7614
+ } catch (error) {
7615
+ return { exists: true, path: statePath, version: null, data: null, lanes: [], requests: [], parseError: error.message };
7616
+ }
7617
+ }
7618
+
7619
+ function readLanesRegistryTolerant(workspaceRoot, relativePath, source) {
7620
+ const registryPath = path.join(workspaceRoot, relativePath);
7621
+ if (!fs.existsSync(registryPath)) return emptyRegistryRead(relativePath, source);
7622
+ const content = fs.readFileSync(registryPath, "utf8");
7623
+ const lanes = parseMarkdownTableSection(content, "## Lanes", ["lane", "purpose", "current_session", "write_scope", "worklog", "workspace"])
7624
+ .filter((row) => row.lane)
7625
+ .map(normalizeLaneRecord);
7626
+ const hasLanesHeading = /^## Lanes\s*$/m.test(content);
7627
+ return {
7628
+ exists: true,
7629
+ relativePath,
7630
+ source,
7631
+ lanes,
7632
+ parseWarning: hasLanesHeading && !lanes.length ? `${relativePath} 包含 Lanes 段落但没有可解析 lane 表格。` : null
7633
+ };
7634
+ }
7635
+
7636
+ function emptyRegistryRead(relativePath, source) {
7637
+ return { exists: false, relativePath, source, lanes: [], parseWarning: null };
7638
+ }
7639
+
7640
+ function readSharedContextTolerant(workspaceRoot, relativePath, source) {
7641
+ const sharedPath = path.join(workspaceRoot, relativePath);
7642
+ if (!fs.existsSync(sharedPath)) return emptySharedRead(relativePath, source);
7643
+ const content = fs.readFileSync(sharedPath, "utf8");
7644
+ const requests = parseMarkdownTableSection(content, "## Cross-Lane Requests", ["id", "from", "to", "request", "status", "host_delivery", "link", "updated"]);
7645
+ const legacyRequests = requests.length ? [] : parseMarkdownTableSection(content, "## Cross-Lane Requests", ["from", "to", "request", "status", "link"]).map((row) => ({
7646
+ id: "",
7647
+ from: row.from,
7648
+ to: row.to,
7649
+ request: row.request,
7650
+ status: row.status,
7651
+ host_delivery: "",
7652
+ link: row.link,
7653
+ updated: ""
7654
+ }));
7655
+ const shared = {
7656
+ outputs: parseMarkdownTableSection(content, "## Shared Outputs", ["from", "title", "path", "audience", "status", "updated"]),
7657
+ requests: requests.length ? requests : legacyRequests,
7658
+ agreements: parseMarkdownTableSection(content, "## Shared Agreements", ["agreement", "owner", "status", "link"])
7659
+ };
7660
+ const hasRequestsHeading = /^## Cross-Lane Requests\s*$/m.test(content);
7661
+ return {
7662
+ exists: true,
7663
+ relativePath,
7664
+ source,
7665
+ shared,
7666
+ requests: shared.requests,
7667
+ parseWarning: hasRequestsHeading && !shared.requests.length ? `${relativePath} 包含 Cross-Lane Requests 段落但没有可解析 request 表格。` : null
7668
+ };
7669
+ }
7670
+
7671
+ function emptySharedRead(relativePath, source) {
7672
+ return { exists: false, relativePath, source, shared: { outputs: [], requests: [], agreements: [] }, requests: [], parseWarning: null };
7673
+ }
7674
+
7675
+ function getAlternateCollaborationPaths(currentRoot) {
7676
+ const alternateRoot = currentRoot === path.join("_系统", "协作")
7677
+ ? path.join("_system", "collaboration")
7678
+ : path.join("_系统", "协作");
7679
+ return {
7680
+ root: alternateRoot,
7681
+ registry: path.join(alternateRoot, "agent-lanes.md"),
7682
+ shared: path.join(alternateRoot, "shared.md")
7683
+ };
7684
+ }
7685
+
7686
+ function mergeCompatibleLanes(markdownLanes, stateLanes) {
7687
+ const byLane = new Map();
7688
+ for (const lane of stateLanes) byLane.set(lane.lane, normalizeLaneRecord(lane));
7689
+ for (const lane of markdownLanes) {
7690
+ const stateLane = byLane.get(lane.lane) || {};
7691
+ byLane.set(lane.lane, normalizeLaneRecord({
7692
+ ...stateLane,
7693
+ ...lane,
7694
+ current_session: lane.current_session && lane.current_session !== "unbound"
7695
+ ? lane.current_session
7696
+ : (stateLane.current_session || lane.current_session || "unbound")
7697
+ }));
7698
+ }
7699
+ return [...byLane.values()].sort((a, b) => a.lane.localeCompare(b.lane));
7700
+ }
7701
+
7702
+ function resolveMultiagentCompatibilityStatus({ stateInfo, currentRegistry, lanes, conflicts, legacyClasses }) {
7703
+ if (conflicts.length || legacyClasses.includes("mixed_language_paths") || legacyClasses.includes("conflicting_lane")) return "blocked_conflict";
7704
+ if (stateInfo.exists && stateInfo.parseError) return lanes.length || currentRegistry.lanes.length ? "unknown_partial" : "blocked_conflict";
7705
+ if (!stateInfo.exists && !currentRegistry.exists && lanes.length === 0) return "current";
7706
+ if (stateInfo.version === 1 && currentRegistry.exists) return "current";
7707
+ if (legacyClasses.includes("state_unversioned") || legacyClasses.includes("markdown_only") || legacyClasses.includes("state_only")) {
7708
+ return "migration_required_for_write";
7709
+ }
7710
+ return "current";
7711
+ }
7712
+
7713
+ function assertMultiagentWritable(workspaceRoot) {
7714
+ const compatibility = inspectMultiagentCompatibility(workspaceRoot).compatibility;
7715
+ if (!compatibility.required_for_write) return;
7716
+ throw new Error(`检测到旧版或冲突的 MultiAgent 协作记录(${compatibility.status})。写入前请先运行:${compatibility.migration.dry_run_command}`);
7717
+ }
7718
+
7719
+ function buildMultiagentMigrationPlan(workspaceRoot) {
7720
+ const report = inspectMultiagentCompatibility(workspaceRoot);
7721
+ const timestamp = timestampForFile();
7722
+ const actions = [];
7723
+ const willCreate = [];
7724
+ const willUpdate = [];
7725
+ const willPreserve = [];
7726
+ const willNotTouch = [
7727
+ "不删除旧文件",
7728
+ "不覆盖非空 worklog、shared outputs 或 request 记录",
7729
+ "不创建、通知、改名、置顶或归档任何 AI 会话",
7730
+ "不修改 AGENTS.md / CLAUDE.md / README.md"
7731
+ ];
7732
+ const backupSources = [];
7733
+ const stateRelativePath = path.join(".starwork", "agent-lanes", "state.json");
7734
+ const stateExists = report.stateInfo.exists && !report.stateInfo.parseError;
7735
+ const safeToApply = report.conflicts.length === 0 && !report.stateInfo.parseError;
7736
+ let nextState = stateExists ? normalizeAgentLanesStateData(report.stateInfo.data) : defaultAgentLanesState();
7737
+ if (!report.stateInfo.exists || report.compatibility.legacy_classes.includes("markdown_only")) {
7738
+ nextState = {
7739
+ version: 1,
7740
+ lanes: Object.fromEntries(report.lanes.map((lane) => [lane.lane, laneToStateRecord(lane)])),
7741
+ requests: report.shared.requests.map(sharedRequestToStateRequest)
7742
+ };
7743
+ actions.push(directoryAction(workspaceRoot, path.join(".starwork", "agent-lanes")));
7744
+ actions.push(upsertFileAction(workspaceRoot, stateRelativePath, renderAgentLanesState(nextState)));
7745
+ willCreate.push({ path: stateRelativePath, reason: "从可读 Markdown 协作记录补齐机器状态文件" });
7746
+ } else if (report.stateInfo.version !== 1) {
7747
+ nextState = {
7748
+ ...nextState,
7749
+ version: 1,
7750
+ lanes: {
7751
+ ...(nextState.lanes || {}),
7752
+ ...Object.fromEntries(report.lanes.map((lane) => [lane.lane, {
7753
+ ...(nextState.lanes?.[lane.lane] || {}),
7754
+ ...laneToStateRecord(lane)
7755
+ }]))
7756
+ }
7757
+ };
7758
+ actions.push(overwriteFileAction(workspaceRoot, stateRelativePath, renderAgentLanesState(nextState)));
7759
+ backupSources.push(stateRelativePath);
7760
+ willUpdate.push({ path: stateRelativePath, reason: "补充 version: 1,并保留已有 lanes / requests" });
7761
+ }
7762
+ if (!report.registry.exists && report.lanes.length) {
7763
+ actions.push(fileAction(workspaceRoot, report.collaboration.registry, renderAgentLanesRegistry(report.lanes)));
7764
+ willCreate.push({ path: report.collaboration.registry, reason: "从 state.json 补齐人类可读 lane 注册表" });
7765
+ } else if (report.registry.exists) {
7766
+ backupSources.push(report.collaboration.registry);
7767
+ willPreserve.push(report.collaboration.registry);
7768
+ }
7769
+ if (!fs.existsSync(path.join(workspaceRoot, report.collaboration.shared))) {
7770
+ actions.push(fileAction(workspaceRoot, report.collaboration.shared, renderSharedContext(report.shared)));
7771
+ willCreate.push({ path: report.collaboration.shared, reason: "补齐共享输出和交接记录索引" });
7772
+ } else {
7773
+ backupSources.push(report.collaboration.shared);
7774
+ willPreserve.push(report.collaboration.shared);
7775
+ }
7776
+ for (const lane of report.lanes) {
7777
+ const worklog = path.join(report.collaboration.root, lane.worklog);
7778
+ const workspaceReadme = path.join(report.collaboration.root, lane.workspace, "README.md");
7779
+ if (!fs.existsSync(path.join(workspaceRoot, worklog))) {
7780
+ actions.push(fileAction(workspaceRoot, worklog, renderLaneWorklog(lane.lane)));
7781
+ willCreate.push({ path: worklog, reason: `补齐 ${lane.lane} lane worklog` });
7782
+ } else {
7783
+ willPreserve.push(worklog);
7784
+ }
7785
+ if (!fs.existsSync(path.join(workspaceRoot, workspaceReadme))) {
7786
+ actions.push(fileAction(workspaceRoot, workspaceReadme, renderLaneWorkspaceReadme(lane.lane, report.collaboration)));
7787
+ willCreate.push({ path: workspaceReadme, reason: `补齐 ${lane.lane} lane workspace 说明` });
7788
+ } else {
7789
+ willPreserve.push(workspaceReadme);
7790
+ }
7791
+ }
7792
+ const uniqueBackupSources = [...new Set(backupSources)].filter((relativePath) => fs.existsSync(path.join(workspaceRoot, relativePath)));
7793
+ return {
7794
+ workspaceRoot,
7795
+ status: report.compatibility.status === "current" ? "current" : "migration_available",
7796
+ targetStructureVersion: 1,
7797
+ safeToApply,
7798
+ compatibility: report.compatibility,
7799
+ actions: dedupeActions(actions),
7800
+ willCreate,
7801
+ willUpdate,
7802
+ willCopy: uniqueBackupSources.map((relativePath) => ({ from: relativePath, to: path.join(".starwork", "backups", "multiagent", timestamp, relativePath) })),
7803
+ willPreserve: [...new Set(willPreserve)],
7804
+ willNotTouch,
7805
+ conflicts: report.conflicts,
7806
+ warnings: report.warnings,
7807
+ backupPath: path.join(".starwork", "backups", "multiagent", timestamp),
7808
+ backupSources: uniqueBackupSources,
7809
+ reportPath: path.join(".starwork", "agent-lanes", `migration-report-${timestamp}.json`),
7810
+ timestamp
7811
+ };
7812
+ }
7813
+
7814
+ function normalizeAgentLanesStateData(data = {}) {
7815
+ return {
7816
+ version: 1,
7817
+ lanes: data.lanes && typeof data.lanes === "object" ? data.lanes : {},
7818
+ requests: Array.isArray(data.requests) ? data.requests : []
7819
+ };
7820
+ }
7821
+
7822
+ function laneToStateRecord(lane) {
7823
+ const parsed = parseAdapterSession(lane.current_session || "unbound");
7824
+ return {
7825
+ current_session: lane.current_session || "unbound",
7826
+ host: parsed.host,
7827
+ session_id: parsed.id,
7828
+ thread_id: parsed.host === "codex" ? parsed.id : null,
7829
+ purpose: lane.purpose || "待补充",
7830
+ write_scope: lane.write_scope || "待补充",
7831
+ worklog: lane.worklog || defaultLaneWorklogPath(lane.lane),
7832
+ workspace: lane.workspace || defaultLaneWorkspacePath(lane.lane)
7833
+ };
7834
+ }
7835
+
7836
+ function sharedRequestToStateRequest(row) {
7837
+ return {
7838
+ id: row.id || buildLaneRequestId(row.to || "lane"),
7839
+ from: row.from || "user",
7840
+ to: row.to || "unknown",
7841
+ message_type: "instruction",
7842
+ recorded_in: row.link || "",
7843
+ host_delivery: {
7844
+ status: row.host_delivery || row.status || "recorded_only",
7845
+ delivery_tool: "legacy_shared_md",
7846
+ mode: "migrated"
7847
+ }
7848
+ };
7849
+ }
7850
+
7851
+ function renderMultiagentMigrationPlanJson(plan) {
7852
+ return {
7853
+ status: plan.status,
7854
+ target_structure_version: plan.targetStructureVersion,
7855
+ safe_to_apply: plan.safeToApply,
7856
+ compatibility: plan.compatibility,
7857
+ will_create: plan.willCreate,
7858
+ will_update: plan.willUpdate,
7859
+ will_copy: plan.willCopy,
7860
+ will_preserve: plan.willPreserve,
7861
+ will_not_touch: plan.willNotTouch,
7862
+ conflicts: plan.conflicts,
7863
+ warnings: plan.warnings,
7864
+ backup: {
7865
+ will_create_backup: plan.willCopy.length > 0,
7866
+ path: plan.backupPath
7867
+ },
7868
+ report_path: plan.reportPath
7869
+ };
7870
+ }
7871
+
7872
+ function printMultiagentMigrationPlan(plan, dryRun = true) {
7873
+ console.log("");
7874
+ console.log(dryRun ? "这是一次预览,不会写入文件。" : "MultiAgent 结构迁移计划:");
7875
+ console.log("");
7876
+ console.log("已识别:");
7877
+ for (const source of plan.compatibility.read_sources || []) console.log(`- ${source}`);
7878
+ if (!plan.compatibility.read_sources?.length) console.log("- 未发现已有 MultiAgent 协作记录。");
7879
+ printMigrationGroup("将会创建:", plan.willCreate.map((item) => `${item.path}:${item.reason}`));
7880
+ printMigrationGroup("将会更新:", plan.willUpdate.map((item) => `${item.path}:${item.reason}`));
7881
+ printMigrationGroup("将会备份:", plan.willCopy.map((item) => `${item.from} -> ${item.to}`));
7882
+ printMigrationGroup("将会保留:", plan.willPreserve);
7883
+ printMigrationGroup("不会做:", plan.willNotTouch);
7884
+ if (plan.conflicts.length) printMigrationGroup("需要人工处理的冲突:", plan.conflicts);
7885
+ if (plan.warnings.length) printMigrationGroup("提醒:", plan.warnings);
7886
+ }
7887
+
7888
+ function printMigrationGroup(title, items) {
7889
+ console.log("");
7890
+ console.log(title);
7891
+ if (!items.length) {
7892
+ console.log("- 无");
7893
+ return;
7894
+ }
7895
+ for (const item of items) console.log(`- ${item}`);
7896
+ }
7897
+
7898
+ function applyMultiagentMigrationPlan(plan) {
7899
+ const backupRoot = path.join(plan.workspaceRoot, plan.backupPath);
7900
+ if (plan.willCopy.length) {
7901
+ fs.mkdirSync(backupRoot, { recursive: true });
7902
+ for (const relativePath of plan.backupSources) {
7903
+ const source = path.join(plan.workspaceRoot, relativePath);
7904
+ if (!fs.existsSync(source)) continue;
7905
+ const target = path.join(backupRoot, relativePath);
7906
+ fs.mkdirSync(path.dirname(target), { recursive: true });
7907
+ fs.copyFileSync(source, target);
7908
+ }
7909
+ }
7910
+ applyPlan({ targetDir: plan.workspaceRoot, actions: plan.actions });
7911
+ const report = {
7912
+ source_version: plan.compatibility.structure_version,
7913
+ target_version: plan.targetStructureVersion,
7914
+ legacy_classes: plan.compatibility.legacy_classes,
7915
+ applied_actions: plan.actions.map((action) => ({
7916
+ type: action.type,
7917
+ mode: action.mode,
7918
+ path: action.relativePath
7919
+ })),
7920
+ backup_path: plan.willCopy.length ? plan.backupPath : null,
7921
+ skipped_actions: plan.willPreserve,
7922
+ conflicts_resolved: [],
7923
+ conflicts_unresolved: plan.conflicts,
7924
+ command_timestamp: new Date().toISOString()
7925
+ };
7926
+ fs.mkdirSync(path.dirname(path.join(plan.workspaceRoot, plan.reportPath)), { recursive: true });
7927
+ fs.writeFileSync(path.join(plan.workspaceRoot, plan.reportPath), `${JSON.stringify(report, null, 2)}\n`, "utf8");
7928
+ }
7929
+
7930
+ function timestampForFile(date = new Date()) {
7931
+ return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
7932
+ }
7933
+
7934
+ function printMultiagentCompatibilitySummary(report) {
7935
+ if (report.compatibility.status === "current") return;
7936
+ console.log("");
7937
+ console.log("升级提示:");
7938
+ if (report.compatibility.status === "blocked_conflict") {
7939
+ console.log("- 检测到旧版 MultiAgent 协作记录存在冲突,暂不能自动迁移。");
7940
+ } else if (report.compatibility.status === "unknown_partial") {
7941
+ console.log("- 检测到部分旧版 MultiAgent 协作记录,但机器状态可能损坏。");
7942
+ } else {
7943
+ console.log("- 已检测到旧版 MultiAgent 协作记录,目前可以读取已有 AI 岗位。");
7944
+ console.log("- 写入新岗位、绑定会话或记录交接前,建议先预览迁移。预览不会写入文件。");
7945
+ }
7946
+ console.log(`- 预览命令:${report.compatibility.migration.dry_run_command}`);
7947
+ }
7948
+
7443
7949
  function parseMarkdownTableSection(content, heading, fields) {
7444
7950
  const lines = content.split(/\r?\n/);
7445
7951
  const headingIndex = lines.findIndex((line) => line.trim() === heading);
@@ -10009,7 +10515,7 @@ function printLanesHelp() {
10009
10515
  console.log(`StarWork Multiagent
10010
10516
 
10011
10517
  Usage:
10012
- starwork multiagent <init|add|bind|release|status|read|instruct|handoff|continue|launch|message|request|share> [options]
10518
+ starwork multiagent <init|add|bind|release|status|upgrade|read|instruct|handoff|continue|launch|message|request|share> [options]
10013
10519
 
10014
10520
  Agent Lanes 用于同一项目内多个 Agent 会话按项目自定义职责位协作。
10015
10521
 
@@ -10019,6 +10525,7 @@ Subcommands:
10019
10525
  bind 将当前会话绑定到 lane。
10020
10526
  release 释放 lane 的当前会话绑定。
10021
10527
  status 查看 lane 分工和共享请求,可加 --host 观察宿主会话。
10528
+ upgrade 预览并确认迁移旧版 MultiAgent 协作结构。
10022
10529
  read 读取某个 lane 绑定宿主的可用近况;Claude Code 只输出 transcript 摘要。
10023
10530
  instruct 向另一个 lane 发送格式化跨会话指令。
10024
10531
  handoff 生成并记录人工交付消息,不后台发送。
@@ -10033,6 +10540,7 @@ Subcommands:
10033
10540
  starwork multiagent add review --purpose "审校和风险检查" --write "reviews/**,product/docs/**" --target ./my-workspace --yes
10034
10541
  starwork multiagent bind research --session codex:manual-research-1 --session-name "Research Agent" --target ./my-workspace --yes
10035
10542
  starwork multiagent status --host --target ./my-workspace --json
10543
+ starwork multiagent upgrade --target ./my-workspace --dry-run
10036
10544
  starwork multiagent message instruct development --from product-planning --message "请根据 SPEC 开始实现。" --target ./my-workspace --json
10037
10545
  starwork multiagent request record --from product-planning --to development --message "请根据 SPEC 开始实现。" --host-delivery delivered_via_codex_thread_tool --delivery-tool send_message_to_thread --target ./my-workspace --yes
10038
10546
  `);
@@ -10120,6 +10628,29 @@ Options:
10120
10628
  `);
10121
10629
  }
10122
10630
 
10631
+ function printLanesUpgradeHelp() {
10632
+ console.log(`StarWork Multiagent Upgrade
10633
+
10634
+ Usage:
10635
+ starwork multiagent upgrade [options]
10636
+
10637
+ Options:
10638
+ --target <path>
10639
+ --dry-run
10640
+ --json
10641
+ --yes, -y
10642
+
10643
+ 说明:
10644
+ 这是 MultiAgent 协作结构迁移,不会创建或通知 AI 会话。
10645
+ 默认先使用 --dry-run 预览;确认后 --yes 会备份旧文件、补齐安全结构并写 migration report。
10646
+
10647
+ 示例:
10648
+ starwork multiagent upgrade --target ./my-workspace --dry-run
10649
+ starwork multiagent upgrade --target ./my-workspace --json --dry-run
10650
+ starwork multiagent upgrade --target ./my-workspace --yes
10651
+ `);
10652
+ }
10653
+
10123
10654
  function printLanesReadHelp() {
10124
10655
  console.log(`StarWork Multiagent Read
10125
10656