@jennie-shawn/starwork 0.1.0-alpha.20 → 0.1.0-alpha.21

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/cli/src/cli.js CHANGED
@@ -143,6 +143,13 @@ const AGENT_DOCS_PLAN_SCHEMA = "starwork.agent_docs_plan.v0.1";
143
143
  const AGENT_DOCS_DRAFT_DIR = path.join(".starwork", "drafts");
144
144
  const LEGACY_AGENT_DOC_SIDECARS = ["AGENTS.starwork.md", "AGENTS.starwork-new.md", "README.starwork-new.md", "CLAUDE.starwork.md"];
145
145
  const MANUAL_HANDOFF_STATUS = "manual_handoff_required";
146
+ const HOST_DELIVERY_STATUSES = new Set([
147
+ "delivered",
148
+ "delivered_via_codex_thread_tool",
149
+ "recorded_only",
150
+ MANUAL_HANDOFF_STATUS,
151
+ "failed"
152
+ ]);
146
153
 
147
154
  const KIT_BUNDLED_SKILLS = {
148
155
  hub: [
@@ -320,6 +327,11 @@ async function init(argv) {
320
327
  })
321
328
  : null;
322
329
 
330
+ if (options.json && options.dryRun) {
331
+ console.log(JSON.stringify(renderInitPlanJson(plan, true), null, 2));
332
+ return;
333
+ }
334
+
323
335
  printPlan(plan, options.dryRun);
324
336
  if (adapterHosts.length && options.dryRun) {
325
337
  console.log("");
@@ -351,7 +363,10 @@ async function init(argv) {
351
363
  applyPlan(adapterPlan);
352
364
  }
353
365
  console.log("");
354
- console.log("StarWork 工作台已创建。");
366
+ console.log("StarWork 工作台已经创建好了。");
367
+ console.log("");
368
+ console.log("这次写入的是项目协作文件,不是业务代码。");
369
+ console.log("健康检查可以确认 AI 后续能找到项目说明、当前任务和协作规则。");
355
370
  console.log("");
356
371
  console.log("下一步建议:");
357
372
  console.log(`1. 运行 starwork doctor --target ${plan.targetDir}`);
@@ -361,14 +376,15 @@ async function init(argv) {
361
376
  console.log("4. 创建项目后,运行 starwork audit 巡检项目中心里的项目登记。");
362
377
  } else {
363
378
  if (hasAgentDocsDrafts(targetDir)) {
364
- console.log("2. AI 入口文档需要 Skill 整合后再生效;请用 starworkInit 读取 .starwork/drafts/agent-docs-plan.json 和 proposed 草稿。");
379
+ console.log("2. 还有一步没有自动完成:你的项目已经有 AI 规则文件,所以 StarWork 只生成了待整合草稿,没有直接覆盖原文件。");
380
+ console.log("3. AI 入口文档需要 Skill 整合后再生效;请用 starworkInit 读取 .starwork/drafts/agent-docs-plan.json 和 proposed 草稿,确认后再合并最终入口。");
365
381
  } else {
366
382
  console.log("2. 打开 AGENTS.md,确认 AI 入口规则。");
367
383
  }
368
384
  if (adapterHosts.length) {
369
- console.log(`3. 已生成 ${adapterHosts.join(", ")} 适配入口;运行 starwork doctor --target ${plan.targetDir} --host ${adapterHosts.length === 1 ? adapterHosts[0] : "all"} 再检查一次。`);
385
+ console.log(`4. 已生成 ${adapterHosts.join(", ")} 适配入口;运行 starwork doctor --target ${plan.targetDir} --host ${adapterHosts.length === 1 ? adapterHosts[0] : "all"} 再检查一次。`);
370
386
  } else {
371
- console.log("3. 如需生成特定 AI 工具适配文件,运行 starwork adapt。");
387
+ console.log("3. 下一步你可以用 Codex / Claude Code / Cursor 打开这个目录,让 AI 先读项目规则。");
372
388
  }
373
389
  }
374
390
  }
@@ -750,8 +766,14 @@ function parseArgs(argv) {
750
766
  options.sessionName = readValue(argv, ++i, arg);
751
767
  } else if (arg === "--from") {
752
768
  options.from = readValue(argv, ++i, arg);
769
+ } else if (arg === "--to") {
770
+ options.to = readValue(argv, ++i, arg);
753
771
  } else if (arg === "--message") {
754
772
  options.message = readValue(argv, ++i, arg);
773
+ } else if (arg === "--delivery-tool") {
774
+ options.deliveryTool = readValue(argv, ++i, arg);
775
+ } else if (arg === "--host-delivery") {
776
+ options.hostDelivery = readValue(argv, ++i, arg);
755
777
  } else if (arg === "--turns") {
756
778
  options.turns = readValue(argv, ++i, arg);
757
779
  } else if (arg === "--timeout") {
@@ -899,6 +921,14 @@ async function lanesCommand(argv) {
899
921
  await lanesLaunch(argv.slice(1));
900
922
  return;
901
923
  }
924
+ if (subcommand === "message") {
925
+ await lanesMessage(argv.slice(1));
926
+ return;
927
+ }
928
+ if (subcommand === "request") {
929
+ await lanesRequest(argv.slice(1));
930
+ return;
931
+ }
902
932
  if (subcommand === "share") {
903
933
  await lanesShare(argv.slice(1));
904
934
  return;
@@ -1029,19 +1059,19 @@ async function lanesBind(argv) {
1029
1059
  if (!options.json) {
1030
1060
  printGenericPlan(options.dryRun ? "绑定 Lane 预览(dry run):" : "绑定 Lane 计划:", plan.actions);
1031
1061
  if (sessionName) {
1032
- console.log(`将尝试同步宿主会话名:${sessionName}(best effort)`);
1062
+ console.log(`宿主会话名需由 starworkMultiagent 在 Codex App 中调用 set_thread_title 后,再由本命令记录:${sessionName}`);
1033
1063
  console.log("");
1034
1064
  }
1035
1065
  if (options.pin) {
1036
- console.log("将尝试置顶 Codex thread(best effort;当前 Codex 版本可能不支持)。");
1066
+ console.log("宿主置顶需由 starworkMultiagent 在 Codex App 中调用 set_thread_pinned;本命令只记录 StarWork binding。");
1037
1067
  console.log("");
1038
1068
  }
1039
1069
  }
1040
1070
  if (options.dryRun) return;
1041
1071
  await confirmOrThrow(options, `是否将当前会话绑定到 Lane ${laneId}?`);
1042
1072
  applyPlan(plan);
1043
- const sessionNameSync = await renameHostSessionBestEffort({ session, sessionName });
1044
- const pinSync = pinHostThreadBestEffort({ session, requested: Boolean(options.pin) });
1073
+ const sessionNameSync = createSessionNameRecordOnlyResult({ sessionName });
1074
+ const pinSync = createHostPinRecordOnlyResult({ requested: Boolean(options.pin) });
1045
1075
  if (options.json) {
1046
1076
  console.log(JSON.stringify(renderLanesBindResult({
1047
1077
  workspaceRoot,
@@ -1176,7 +1206,7 @@ function printLanesHostStatus(report) {
1176
1206
  console.log(` ${item.host.adapter || "host"} observation: ${item.host.status}${item.host.name ? `;name=${item.host.name}` : ""}${item.host.cwd ? `;cwd=${item.host.cwd}` : ""}${Number.isInteger(item.host.turn_count) ? `;turns=${item.host.turn_count}` : ""}`);
1177
1207
  if (item.host.continue_command) console.log(` Continue: ${item.host.continue_command}`);
1178
1208
  if (item.host.status === "notLoaded") {
1179
- console.log(" 说明:notLoaded 表示 thread 可能存在于历史中,但当前 app-server 尚未加载;可显式使用 --load。");
1209
+ console.log(" 说明:notLoaded 表示 thread 可能存在于历史中,但当前宿主观察接口尚未加载;可显式使用 --load。");
1180
1210
  }
1181
1211
  if (item.host.warning) console.log(` Warning: ${item.host.warning}`);
1182
1212
  }
@@ -1463,73 +1493,202 @@ async function lanesLaunch(argv) {
1463
1493
  launchResults.forEach((result) => console.log(`- ${result.lane}${result.session_name ? ` -> ${result.session_name}` : ""}`));
1464
1494
  return;
1465
1495
  }
1466
- await confirmOrThrow(options, `是否 launch ${lanes.length} 个 Codex lane thread?`);
1467
- let nextRegistryLanes = registry.lanes;
1468
- let lanesState = readAgentLanesState(workspaceRoot);
1496
+ await confirmOrThrow(options, `是否生成 ${lanes.length} 个 Codex lane launch message?`);
1469
1497
  for (const lane of lanes) {
1470
1498
  const sessionName = buildLaneLaunchSessionName({ lane, workspaceRoot, explicitName: options.sessionName });
1471
1499
  const launchMessage = renderMultiagentLaunchMessage({ lane, fromLane: options.from || "user", workspaceRoot, collaboration });
1472
- const launch = await launchCodexLane({ message: launchMessage, workspaceRoot, timeout: parsePositiveInt(options.timeout, 90000) });
1473
- const launchedThreadId = launch.thread_id || launch.created_thread_id || "";
1474
- const session = launchedThreadId ? `codex:${launchedThreadId}` : "unbound";
1475
- const sessionNameSync = launchedThreadId
1476
- ? await renameHostSessionBestEffort({ session, sessionName })
1477
- : createSessionNameSyncResult({
1478
- requested: Boolean(sessionName),
1479
- supported: false,
1480
- status: sessionName ? "skipped" : "not_requested",
1481
- name: sessionName,
1482
- warning: sessionName ? "No host thread was created to rename." : null
1483
- });
1484
- const bindingStatus = launch.thread_id && launch.status === "completed" ? "bound" : "unbound";
1485
- if (launch.thread_id && launch.status === "completed") {
1486
- const pinSync = pinHostThreadBestEffort({ session, requested: Boolean(options.pin) });
1487
- nextRegistryLanes = nextRegistryLanes.map((item) => item.lane === lane.lane ? { ...item, current_session: session } : item);
1488
- lanesState = updateAgentLaneHostState(lanesState, lane.lane, {
1489
- host: "codex",
1490
- current_session: session,
1491
- thread_id: launch.thread_id,
1492
- session_name: sessionName,
1493
- pinned: pinSync.status === "ok",
1494
- pin_status: pinSync.status,
1495
- created_by: "starwork multiagent launch",
1496
- created_at: new Date().toISOString(),
1497
- last_host_status: {
1498
- type: launch.status,
1499
- observed_at: new Date().toISOString()
1500
- }
1501
- });
1502
- launch.session_name_sync = sessionNameSync;
1503
- launch.pin_sync = pinSync;
1504
- }
1505
1500
  launchResults.push({
1506
1501
  lane: lane.lane,
1507
- ...launch,
1508
- session,
1509
- session_id: launchedThreadId || undefined,
1502
+ adapter: "codex",
1503
+ status: MANUAL_HANDOFF_STATUS,
1510
1504
  session_name: sessionName,
1511
- launch_status: launch.status,
1512
- rename_status: sessionNameSync.status,
1513
- rename_warning: sessionNameSync.warning || undefined,
1514
- binding_status: bindingStatus,
1515
- session_name_sync: sessionNameSync
1505
+ launch_status: MANUAL_HANDOFF_STATUS,
1506
+ rename_status: sessionName ? "requires_starworkMultiagent_tool" : "not_requested",
1507
+ binding_status: "unbound",
1508
+ message: launchMessage,
1509
+ instructions: "Codex App 标准路径必须由 starworkMultiagent 直接调用 create_thread;CLI 只生成 Launch Message,不创建或绑定 Codex thread。",
1510
+ warning: "CLI no longer launches Codex threads directly."
1516
1511
  });
1517
1512
  }
1518
- actions.push(...buildLanesRegistryPlan(workspaceRoot, nextRegistryLanes).actions);
1519
- actions.push(stateFileAction(workspaceRoot, lanesState));
1520
- applyPlan({ targetDir: workspaceRoot, actions: dedupeActions(actions) });
1521
1513
  if (options.json) {
1522
1514
  console.log(JSON.stringify({ schema: "starwork.agent_lanes.launch.v0.3", launches: launchResults }, null, 2));
1523
1515
  return;
1524
1516
  }
1525
1517
  console.log("");
1526
1518
  launchResults.forEach((result) => {
1527
- console.log(`Lane ${result.lane}: ${result.status}${result.thread_id ? ` (${result.thread_id})` : ""}${result.session_name ? ` -> ${result.session_name}` : ""}${result.warning ? ` - ${result.warning}` : ""}`);
1528
- if (result.rename_warning) console.log(` Warning: host session rename skipped: ${result.rename_warning}`);
1529
- if (result.binding_status !== "bound") console.log(" Warning: lane was not bound because launch did not complete.");
1519
+ console.log(`Lane ${result.lane}: ${result.status}${result.session_name ? ` -> ${result.session_name}` : ""}${result.warning ? ` - ${result.warning}` : ""}`);
1520
+ console.log(" 需要在 starworkMultiagent Skill 中调用 create_thread,成功返回 threadId 后再运行 multiagent bind 记录绑定。");
1521
+ console.log("");
1522
+ console.log(result.message.trimEnd());
1530
1523
  });
1531
1524
  }
1532
1525
 
1526
+ async function lanesMessage(argv) {
1527
+ const subcommand = argv[0];
1528
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
1529
+ printLanesMessageHelp();
1530
+ return;
1531
+ }
1532
+ if (subcommand === "launch") {
1533
+ await lanesMessageLaunch(argv.slice(1));
1534
+ return;
1535
+ }
1536
+ if (subcommand === "instruct") {
1537
+ await lanesMessageInstruct(argv.slice(1));
1538
+ return;
1539
+ }
1540
+ throw new Error(`未知 multiagent message 子命令:${subcommand}`);
1541
+ }
1542
+
1543
+ async function lanesMessageLaunch(argv) {
1544
+ const options = parseArgs(argv);
1545
+ if (options.help) {
1546
+ printLanesMessageLaunchHelp();
1547
+ return;
1548
+ }
1549
+ const laneId = normalizeLaneId(options._?.[0], "lane");
1550
+ const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1551
+ const state = readWorkspaceState(workspaceRoot);
1552
+ const collaboration = getCollaborationPaths(state);
1553
+ const registry = readLanesRegistry(workspaceRoot);
1554
+ const lane = findLaneOrThrow(registry.lanes, laneId);
1555
+ const sessionName = buildLaneLaunchSessionName({ lane, workspaceRoot, explicitName: options.sessionName });
1556
+ const message = renderMultiagentLaunchMessage({ lane, fromLane: options.from || "user", workspaceRoot, collaboration });
1557
+ const payload = {
1558
+ schema: "starwork.agent_lanes.message.v0.1",
1559
+ type: "launch",
1560
+ lane: laneId,
1561
+ session_name: sessionName,
1562
+ message
1563
+ };
1564
+ if (options.json) {
1565
+ console.log(JSON.stringify(payload, null, 2));
1566
+ return;
1567
+ }
1568
+ console.log(message.trimEnd());
1569
+ }
1570
+
1571
+ async function lanesMessageInstruct(argv) {
1572
+ const options = parseArgs(argv);
1573
+ if (options.help) {
1574
+ printLanesMessageInstructHelp();
1575
+ return;
1576
+ }
1577
+ const toLane = normalizeLaneId(options._?.[0], "to-lane");
1578
+ const fromLane = normalizeLaneId(options.from || "user", "from lane");
1579
+ if (!options.message) throw new Error("multiagent message instruct 需要 --message <text>。");
1580
+ const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1581
+ const { requestId, message } = buildMultiagentInstructionPayload({
1582
+ workspaceRoot,
1583
+ toLane,
1584
+ fromLane,
1585
+ text: options.message,
1586
+ requestId: options.id
1587
+ });
1588
+ const payload = {
1589
+ schema: "starwork.agent_lanes.message.v0.1",
1590
+ type: "instruction",
1591
+ request_id: requestId,
1592
+ from_lane: fromLane,
1593
+ to_lane: toLane,
1594
+ message
1595
+ };
1596
+ if (options.json) {
1597
+ console.log(JSON.stringify(payload, null, 2));
1598
+ return;
1599
+ }
1600
+ console.log(message.trimEnd());
1601
+ }
1602
+
1603
+ async function lanesRequest(argv) {
1604
+ const subcommand = argv[0];
1605
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
1606
+ printLanesRequestHelp();
1607
+ return;
1608
+ }
1609
+ if (subcommand === "record") {
1610
+ await lanesRequestRecord(argv.slice(1));
1611
+ return;
1612
+ }
1613
+ throw new Error(`未知 multiagent request 子命令:${subcommand}`);
1614
+ }
1615
+
1616
+ async function lanesRequestRecord(argv) {
1617
+ const options = parseArgs(argv);
1618
+ if (options.help) {
1619
+ printLanesRequestRecordHelp();
1620
+ return;
1621
+ }
1622
+ const fromLane = normalizeLaneId(options.from || "user", "from lane");
1623
+ const toLane = normalizeLaneId(options.to || options._?.[0], "to-lane");
1624
+ if (!options.message) throw new Error("multiagent request record 需要 --message <text>。");
1625
+ const hostDelivery = normalizeHostDeliveryStatus(options.hostDelivery || options.status || "");
1626
+ const deliveryTool = normalizeMarkdownCell(options.deliveryTool || "manual");
1627
+ const workspaceRoot = requireWorkspaceRoot(path.resolve(options.target || process.cwd()));
1628
+ const state = readWorkspaceState(workspaceRoot);
1629
+ const collaboration = getCollaborationPaths(state);
1630
+ const registry = readLanesRegistry(workspaceRoot);
1631
+ if (fromLane !== "user") findLaneOrThrow(registry.lanes, fromLane);
1632
+ findLaneOrThrow(registry.lanes, toLane);
1633
+ const requestId = options.id ? normalizeMarkdownCell(options.id) : buildLaneRequestId(toLane);
1634
+ const shared = readSharedContext(workspaceRoot);
1635
+ const requestRow = buildSharedRequestRow({
1636
+ id: requestId,
1637
+ from: fromLane,
1638
+ to: toLane,
1639
+ request: options.message,
1640
+ status: hostDelivery,
1641
+ hostDelivery,
1642
+ link: collaboration.shared
1643
+ });
1644
+ const lanesState = readAgentLanesState(workspaceRoot);
1645
+ const nextState = {
1646
+ ...lanesState,
1647
+ requests: [...(lanesState.requests || []), {
1648
+ id: requestId,
1649
+ from: fromLane,
1650
+ to: toLane,
1651
+ message_type: "instruction",
1652
+ recorded_in: collaboration.shared,
1653
+ host_delivery: {
1654
+ status: hostDelivery,
1655
+ delivery_tool: deliveryTool,
1656
+ mode: "record_only"
1657
+ }
1658
+ }]
1659
+ };
1660
+ const actions = [
1661
+ ...buildSharedContextPlan(workspaceRoot, {
1662
+ outputs: shared.outputs,
1663
+ requests: [...shared.requests, requestRow],
1664
+ agreements: shared.agreements
1665
+ }).actions,
1666
+ stateFileAction(workspaceRoot, nextState)
1667
+ ];
1668
+ if (!options.json) printGenericPlan(options.dryRun ? "记录跨 lane 请求预览(dry run):" : "记录跨 lane 请求计划:", actions);
1669
+ if (options.dryRun) {
1670
+ if (options.json) console.log(JSON.stringify({ schema: "starwork.agent_lanes.request_record.v0.1", dry_run: true, request: requestRow }, null, 2));
1671
+ return;
1672
+ }
1673
+ await confirmOrThrow(options, `是否记录 ${fromLane} -> ${toLane} 的跨 lane 请求?`);
1674
+ applyPlan({ targetDir: workspaceRoot, actions });
1675
+ const result = {
1676
+ schema: "starwork.agent_lanes.request_record.v0.1",
1677
+ request: requestRow,
1678
+ host_delivery: {
1679
+ status: hostDelivery,
1680
+ delivery_tool: deliveryTool,
1681
+ mode: "record_only"
1682
+ }
1683
+ };
1684
+ if (options.json) {
1685
+ console.log(JSON.stringify(result, null, 2));
1686
+ return;
1687
+ }
1688
+ console.log("");
1689
+ console.log(`已记录跨 lane 请求:${requestId}`);
1690
+ }
1691
+
1533
1692
  async function lanesShare(argv) {
1534
1693
  const options = parseArgs(argv);
1535
1694
  if (options.help) {
@@ -3818,6 +3977,25 @@ function getHubStatePathsForLanguage(language = "en") {
3818
3977
  };
3819
3978
  }
3820
3979
 
3980
+ function resolveWorkspaceRolePath(state, role, fallbackPath) {
3981
+ const mappings = state?.upgrade?.core_role_mapping;
3982
+ if (Array.isArray(mappings)) {
3983
+ const mapping = mappings.find((item) => item?.role === role && item.path);
3984
+ if (mapping) {
3985
+ return {
3986
+ role,
3987
+ path: normalizeSafeRelativePath(mapping.path, `upgrade core_role_mapping.${role}`),
3988
+ source: "upgrade.core_role_mapping"
3989
+ };
3990
+ }
3991
+ }
3992
+ return {
3993
+ role,
3994
+ path: normalizeRelativePath(fallbackPath),
3995
+ source: "default"
3996
+ };
3997
+ }
3998
+
3821
3999
  function checkPackInstallations(result, workspaceRoot, state) {
3822
4000
  if (!Array.isArray(state.packs)) return;
3823
4001
  for (const installedPack of state.packs) {
@@ -3918,6 +4096,7 @@ function checkUpgradeRoleMappings(result, workspaceRoot, state) {
3918
4096
 
3919
4097
  function checkSkillInstallations(result, workspaceRoot, state) {
3920
4098
  const manifestPath = path.join(workspaceRoot, ".starwork", "skills.json");
4099
+ let manifestSkills = [];
3921
4100
  const skills = {
3922
4101
  project_manifest: {
3923
4102
  exists: fs.existsSync(manifestPath),
@@ -3925,6 +4104,7 @@ function checkSkillInstallations(result, workspaceRoot, state) {
3925
4104
  count: 0
3926
4105
  },
3927
4106
  registry: null,
4107
+ required: [],
3928
4108
  mounts: []
3929
4109
  };
3930
4110
  result.skills = skills;
@@ -3946,7 +4126,7 @@ function checkSkillInstallations(result, workspaceRoot, state) {
3946
4126
  } else {
3947
4127
  addCheck(result, "skills.project_manifest.schema", "fail", "项目 Skill 清单 schema 不正确。", ".starwork/skills.json");
3948
4128
  }
3949
- const manifestSkills = Array.isArray(manifest.skills) ? manifest.skills : [];
4129
+ manifestSkills = Array.isArray(manifest.skills) ? manifest.skills : [];
3950
4130
  skills.project_manifest.count = manifestSkills.length;
3951
4131
  for (const skill of manifestSkills) {
3952
4132
  if (!skill?.id) {
@@ -3975,40 +4155,135 @@ function checkSkillInstallations(result, workspaceRoot, state) {
3975
4155
 
3976
4156
  if (state.workspace_type === "hub") {
3977
4157
  const hubPaths = getHubPaths(state);
3978
- const registryRelativePath = path.join(hubPaths.formalSkills, "registry.json");
4158
+ const skillsRoot = resolveWorkspaceRolePath(state, "skills", hubPaths.formalSkills);
4159
+ const registryRelativePath = path.join(skillsRoot.path, "registry.json");
3979
4160
  const registryPath = path.join(workspaceRoot, registryRelativePath);
3980
4161
  skills.registry = {
3981
4162
  exists: fs.existsSync(registryPath),
3982
4163
  path: registryRelativePath,
4164
+ path_source: skillsRoot.source,
4165
+ role: skillsRoot.role,
3983
4166
  count: 0
3984
4167
  };
3985
4168
  if (!skills.registry.exists) {
3986
4169
  addCheck(result, "skills.registry.exists", "warn", "项目中心缺少托管 Skill 注册表。", registryRelativePath);
3987
- return;
3988
- }
3989
- addCheck(result, "skills.registry.exists", "pass", "Project Center skill registry exists", registryRelativePath);
3990
- let registry;
3991
- try {
3992
- registry = JSON.parse(fs.readFileSync(registryPath, "utf8"));
3993
- } catch (error) {
3994
- addCheck(result, "skills.registry.parse", "fail", `无法解析项目中心 Skill registry:${error.message}`, registryRelativePath);
3995
- return;
3996
- }
3997
- if (registry.schema === "starwork.skill_registry.v0.1") {
3998
- addCheck(result, "skills.registry.schema", "pass", "Project Center skill registry schema is valid", registryRelativePath);
3999
4170
  } else {
4000
- addCheck(result, "skills.registry.schema", "fail", "项目中心 Skill registry schema 不正确。", registryRelativePath);
4001
- }
4002
- const registrySkills = Array.isArray(registry.skills) ? registry.skills : [];
4003
- skills.registry.count = registrySkills.length;
4004
- for (const skill of registrySkills) {
4005
- if (!skill?.id) {
4006
- addCheck(result, "skills.registry.id.exists", "fail", "项目中心 Skill registry 中存在缺少 id 的条目。", registryRelativePath);
4007
- continue;
4171
+ addCheck(result, "skills.registry.exists", "pass", "Project Center skill registry exists", registryRelativePath);
4172
+ let registry;
4173
+ try {
4174
+ registry = JSON.parse(fs.readFileSync(registryPath, "utf8"));
4175
+ } catch (error) {
4176
+ addCheck(result, "skills.registry.parse", "fail", `无法解析项目中心 Skill registry:${error.message}`, registryRelativePath);
4177
+ registry = null;
4178
+ }
4179
+ if (registry) {
4180
+ if (registry.schema === "starwork.skill_registry.v0.1") {
4181
+ addCheck(result, "skills.registry.schema", "pass", "Project Center skill registry schema is valid", registryRelativePath);
4182
+ } else {
4183
+ addCheck(result, "skills.registry.schema", "fail", "项目中心 Skill registry schema 不正确。", registryRelativePath);
4184
+ }
4185
+ const registrySkills = Array.isArray(registry.skills) ? registry.skills : [];
4186
+ skills.registry.count = registrySkills.length;
4187
+ for (const skill of registrySkills) {
4188
+ if (!skill?.id) {
4189
+ addCheck(result, "skills.registry.id.exists", "fail", "项目中心 Skill registry 中存在缺少 id 的条目。", registryRelativePath);
4190
+ continue;
4191
+ }
4192
+ checkPathExists(result, workspaceRoot, path.join(skillsRoot.path, skill.id), "skills.registry.source.exists", `Project Center skill source exists: ${skill.id}`, `项目中心托管 Skill 缺少目录:${path.join(skillsRoot.path, skill.id)}`);
4193
+ }
4008
4194
  }
4009
- checkPathExists(result, workspaceRoot, path.join(hubPaths.formalSkills, skill.id), "skills.registry.source.exists", `Project Center skill source exists: ${skill.id}`, `项目中心托管 Skill 缺少目录:${path.join(hubPaths.formalSkills, skill.id)}`);
4010
4195
  }
4011
4196
  }
4197
+
4198
+ checkRequiredSkillInstallations(result, workspaceRoot, state, manifestSkills);
4199
+ }
4200
+
4201
+ function collectRequiredSkills(state) {
4202
+ if (state.workspace_type === "hub" && state.kit === "hub") {
4203
+ return (KIT_BUNDLED_SKILLS.hub || []).map((skill) => ({
4204
+ ...skill,
4205
+ required_by: "kit:hub"
4206
+ }));
4207
+ }
4208
+ return [];
4209
+ }
4210
+
4211
+ function checkRequiredSkillInstallations(result, workspaceRoot, state, manifestSkills) {
4212
+ const requiredSkills = collectRequiredSkills(state);
4213
+ if (!requiredSkills.length) return;
4214
+
4215
+ const hubPaths = getHubPaths(state);
4216
+ const skillsRoot = resolveWorkspaceRolePath(state, "skills", hubPaths.formalSkills);
4217
+ const manifestIds = new Set(manifestSkills.map((skill) => skill?.id).filter(Boolean));
4218
+ const reports = [];
4219
+
4220
+ for (const required of requiredSkills) {
4221
+ const sourcePath = path.join(skillsRoot.path, required.id);
4222
+ const skillFilePath = path.join(sourcePath, "SKILL.md");
4223
+ const sourceExists = fs.existsSync(path.join(workspaceRoot, skillFilePath));
4224
+ const frontmatter = sourceExists
4225
+ ? parseSkillFrontmatter(fs.readFileSync(path.join(workspaceRoot, skillFilePath), "utf8"))
4226
+ : {};
4227
+ const frontmatterOk = sourceExists && Boolean(frontmatter.name) && Boolean(frontmatter.description);
4228
+ const mounts = (required.install || [])
4229
+ .filter((install) => install.agent !== "hub")
4230
+ .map((install) => {
4231
+ const mountPath = normalizeRelativePath(install.path);
4232
+ return {
4233
+ agent: install.agent,
4234
+ path: mountPath,
4235
+ status: fs.existsSync(path.join(workspaceRoot, mountPath)) ? "ok" : "missing"
4236
+ };
4237
+ });
4238
+ const manifestStatus = manifestIds.has(required.id) ? "ok" : "missing";
4239
+ const missingMount = mounts.some((mount) => mount.status !== "ok");
4240
+ let status = "ok";
4241
+ if (!sourceExists) {
4242
+ status = "missing_source";
4243
+ } else if (!frontmatterOk) {
4244
+ status = "invalid_frontmatter";
4245
+ } else if (manifestStatus !== "ok") {
4246
+ status = "missing_manifest";
4247
+ } else if (missingMount) {
4248
+ status = "missing_mount";
4249
+ }
4250
+
4251
+ const report = {
4252
+ id: required.id,
4253
+ required_by: required.required_by,
4254
+ status,
4255
+ source: {
4256
+ path: sourcePath,
4257
+ status: sourceExists ? "ok" : "missing",
4258
+ path_source: skillsRoot.source
4259
+ },
4260
+ manifest: {
4261
+ path: ".starwork/skills.json",
4262
+ status: manifestStatus
4263
+ },
4264
+ mounts,
4265
+ frontmatter: {
4266
+ path: skillFilePath,
4267
+ status: frontmatterOk ? "ok" : (sourceExists ? "invalid" : "missing")
4268
+ },
4269
+ repair_hint: "这是 Hub Kit 自带 Skill。请重新运行受控的 StarWork Hub Kit 安装/同步流程,或按文档补齐项目中心内的 Kit Skill;不要把它安装成全局系统 Skill。"
4270
+ };
4271
+ reports.push(report);
4272
+
4273
+ if (status === "ok") {
4274
+ addCheck(result, `skills.required.${slugifyCheckId(required.id)}`, "pass", `Hub required Skill is installed: ${required.id}`, sourcePath);
4275
+ } else {
4276
+ const missingMounts = mounts.filter((mount) => mount.status !== "ok").map((mount) => mount.path);
4277
+ const details = [
4278
+ `应在项目中心内存在:${sourcePath}`,
4279
+ `应登记在:.starwork/skills.json`,
4280
+ ...missingMounts.map((mountPath) => `应挂载给宿主:${mountPath}`)
4281
+ ].join(";");
4282
+ addCheck(result, `skills.required.${slugifyCheckId(required.id)}`, "warn", `缺少 Hub 自带 Skill:${required.id}。${details}。建议:重新运行受控的 StarWork Hub Kit 安装/同步流程,或按文档补齐项目中心内的 Kit Skill。不要把它安装成全局系统 Skill。`, sourcePath);
4283
+ }
4284
+ }
4285
+
4286
+ result.skills.required = reports;
4012
4287
  }
4013
4288
 
4014
4289
  function checkPathExists(result, workspaceRoot, relativePath, id, passMessage, failMessage) {
@@ -4733,7 +5008,9 @@ function friendlyCheckLevel(level) {
4733
5008
  }
4734
5009
 
4735
5010
  function friendlyDoctorMessage(message) {
4736
- return String(message || "")
5011
+ const text = String(message || "");
5012
+ if (text.includes("缺少 Hub 自带 Skill")) return text;
5013
+ return text
4737
5014
  .replace(/这是一个可升级的历史模板工作区,但缺少 \.starwork\/workspace\.json。/g, "这个目录像旧版工作区,但还缺少 StarWork 工作台身份证(.starwork/workspace.json)。")
4738
5015
  .replace(/检测到历史模板升级候选,置信度:(?:high|medium|low)。/g, "这个目录像旧版工作区,可以进一步判断如何整理。")
4739
5016
  .replace(/检测到主库 \/ Hub 候选,置信度:(?:high|medium|low)。/g, "这个目录像项目中心,可以进一步判断如何接入 StarWork。")
@@ -4819,10 +5096,17 @@ async function choosePack(workspaceType, workspaceConfig, options) {
4819
5096
  function printInitIntro(options, targetDir) {
4820
5097
  if (options.yes || !process.stdin.isTTY) return;
4821
5098
  console.log("");
4822
- console.log("StarWork 初始化向导");
5099
+ console.log("可以,我先简单说清楚 StarWork 在做什么。");
5100
+ console.log("");
5101
+ console.log("StarWork 是给 AI 协作准备的项目工作台。它会把项目说明、当前任务、协作规则、交接记录和健康检查入口放到固定位置,让 AI 每次进入项目时不用从零猜上下文。");
5102
+ console.log("");
5103
+ console.log("这次我会带你做三件事:");
5104
+ console.log("1. 确认这个工作台服务哪个项目;");
5105
+ console.log("2. 预览 StarWork 准备补哪些协作文件;");
5106
+ console.log("3. 你确认后再正式写入,并做一次检查。");
4823
5107
  console.log("");
4824
5108
  console.log(`目标目录:${targetDir}`);
4825
- console.log("我会先确认工作台类型、语言和 Pack,然后给出写入预览。");
5109
+ console.log("我会先预览,不会直接改你的业务代码,也不会直接覆盖已有 AI 规则文件。");
4826
5110
  }
4827
5111
 
4828
5112
  async function chooseLanguage(options) {
@@ -7345,6 +7629,31 @@ ${message}
7345
7629
  `;
7346
7630
  }
7347
7631
 
7632
+ function buildMultiagentInstructionPayload({ workspaceRoot, toLane, fromLane, text, requestId = "" }) {
7633
+ const state = readWorkspaceState(workspaceRoot);
7634
+ const collaboration = getCollaborationPaths(state);
7635
+ const registry = readLanesRegistry(workspaceRoot);
7636
+ const targetLane = findLaneOrThrow(registry.lanes, toLane);
7637
+ if (fromLane !== "user") findLaneOrThrow(registry.lanes, fromLane);
7638
+ const resolvedRequestId = normalizeMarkdownCell(requestId || buildLaneRequestId(toLane));
7639
+ return {
7640
+ requestId: resolvedRequestId,
7641
+ fromLane,
7642
+ toLane,
7643
+ targetLane,
7644
+ collaboration,
7645
+ message: renderMultiagentInstructionMessage({
7646
+ requestId: resolvedRequestId,
7647
+ fromLane,
7648
+ toLane,
7649
+ message: text,
7650
+ collaboration,
7651
+ targetLane,
7652
+ workspaceRoot
7653
+ })
7654
+ };
7655
+ }
7656
+
7348
7657
  function parseLaneList(value) {
7349
7658
  return String(value || "")
7350
7659
  .split(",")
@@ -7355,11 +7664,53 @@ function parseLaneList(value) {
7355
7664
 
7356
7665
  function buildLaneLaunchSessionName({ lane, workspaceRoot, explicitName }) {
7357
7666
  const requestedName = normalizeMarkdownCell(explicitName || "");
7358
- if (requestedName) return requestedName;
7359
- const roleName = normalizeMarkdownCell(lane?.purpose || lane?.lane || "");
7667
+ if (requestedName) return sanitizeLaneSessionName(requestedName);
7668
+ const roleName = deriveLaneRoleName(lane);
7360
7669
  return normalizeMarkdownCell(`${roleName || "Agent"} Agent`);
7361
7670
  }
7362
7671
 
7672
+ function deriveLaneRoleName(lane) {
7673
+ const fallback = humanizeLaneId(lane?.lane || "agent");
7674
+ const purpose = normalizeMarkdownCell(lane?.purpose || "");
7675
+ if (/^(根据|只负责|用于)\s*/u.test(purpose)) {
7676
+ return sanitizeLaneRoleName(fallback);
7677
+ }
7678
+ let roleName = purpose;
7679
+ roleName = roleName.split(/[::。;;,,、\n\r]/)[0].trim();
7680
+ roleName = roleName.replace(/^(只负责|主要负责|负责|用于|协助|根据)\s*/u, "").trim();
7681
+ if (!roleName || /^根据\s*/u.test(roleName) || /^(SPEC|spec|需求|任务)\b/.test(roleName)) {
7682
+ roleName = fallback;
7683
+ }
7684
+ roleName = roleName.replace(/^(生成|实现|维护|处理)\s+/u, "").trim() || fallback;
7685
+ return sanitizeLaneRoleName(roleName);
7686
+ }
7687
+
7688
+ function humanizeLaneId(laneId) {
7689
+ const normalized = normalizeMarkdownCell(laneId || "agent");
7690
+ const parts = normalized.split(/[-_]+/).filter(Boolean);
7691
+ if (!parts.length) return "Agent";
7692
+ return parts.map((part) => {
7693
+ if (/^[a-z]+$/i.test(part)) return `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`;
7694
+ return part;
7695
+ }).join(" ");
7696
+ }
7697
+
7698
+ function sanitizeLaneRoleName(value) {
7699
+ const sanitized = normalizeMarkdownCell(value)
7700
+ .replace(/[::。;;,,、\n\r].*$/u, "")
7701
+ .replace(/\s+/g, " ")
7702
+ .trim();
7703
+ return sanitized.slice(0, 24).trim() || "Agent";
7704
+ }
7705
+
7706
+ function sanitizeLaneSessionName(value) {
7707
+ const sanitized = normalizeMarkdownCell(value)
7708
+ .replace(/[\n\r]/g, " ")
7709
+ .replace(/\s+/g, " ")
7710
+ .trim();
7711
+ return sanitized.slice(0, 40).trim();
7712
+ }
7713
+
7363
7714
  function resolveLaneLaunchHost({ options, lanes, workspaceRoot }) {
7364
7715
  const explicit = typeof options.host === "string" ? options.host : (options.agent || options.adapter || "");
7365
7716
  if (explicit) return normalizeAdapterHost(explicit);
@@ -7483,7 +7834,10 @@ async function observeHostSession(session, options = {}) {
7483
7834
  };
7484
7835
  }
7485
7836
  if (parsed.host === "codex") {
7486
- return observeCodexThread({ threadId: parsed.id, includeTurns: Boolean(options.includeTurns), load: Boolean(options.load), turnLimit: options.turnLimit || 0 });
7837
+ return observeManualHostSession(parsed, {
7838
+ status: "use_starworkMultiagent_tool",
7839
+ warning: "Codex lane reading must be performed by starworkMultiagent with read_thread in the Codex App; CLI only reports the StarWork binding."
7840
+ });
7487
7841
  }
7488
7842
  if (parsed.host === "claude-code") {
7489
7843
  return observeClaudeCodeSession(parsed, options);
@@ -8013,7 +8367,9 @@ function probeHostStandardSendCapability(host) {
8013
8367
  host,
8014
8368
  available: false,
8015
8369
  mode: null,
8016
- warning: `${host} standard background delivery capability is not available in this CLI runtime; low-level turn APIs are not used for multiagent instruct.`
8370
+ warning: host === "codex"
8371
+ ? "Codex delivery must be performed by starworkMultiagent with send_message_to_thread in the Codex App; CLI only records state and manual fallback."
8372
+ : `${host} standard background delivery capability is not available in this CLI runtime; low-level turn APIs are not used for multiagent instruct.`
8017
8373
  };
8018
8374
  }
8019
8375
 
@@ -8122,6 +8478,17 @@ function createSessionNameSyncResult({ requested, supported, status, name = "",
8122
8478
  };
8123
8479
  }
8124
8480
 
8481
+ function createSessionNameRecordOnlyResult({ sessionName }) {
8482
+ const requested = Boolean(sessionName);
8483
+ return createSessionNameSyncResult({
8484
+ requested,
8485
+ supported: false,
8486
+ status: requested ? "requires_starworkMultiagent_tool" : "not_requested",
8487
+ name: sessionName,
8488
+ warning: requested ? "Codex host title changes must be performed by starworkMultiagent with set_thread_title; CLI bind only records StarWork state." : null
8489
+ });
8490
+ }
8491
+
8125
8492
  function printSessionNameSyncResult(result) {
8126
8493
  if (!result.requested) return;
8127
8494
  if (result.status === "ok") {
@@ -8142,6 +8509,15 @@ function createHostPinResult({ requested, supported, status, warning = null }) {
8142
8509
  };
8143
8510
  }
8144
8511
 
8512
+ function createHostPinRecordOnlyResult({ requested }) {
8513
+ return createHostPinResult({
8514
+ requested: Boolean(requested),
8515
+ supported: false,
8516
+ status: requested ? "requires_starworkMultiagent_tool" : "not_requested",
8517
+ warning: requested ? "Codex host pinning must be performed by starworkMultiagent with set_thread_pinned; CLI bind only records StarWork state." : null
8518
+ });
8519
+ }
8520
+
8145
8521
  function pinHostThreadBestEffort({ session, requested }) {
8146
8522
  if (!requested) {
8147
8523
  return createHostPinResult({ requested: false, supported: false, status: "not_requested" });
@@ -8765,6 +9141,14 @@ function buildSharedRequestRow({ id, from, to, request, status, hostDelivery, li
8765
9141
  };
8766
9142
  }
8767
9143
 
9144
+ function normalizeHostDeliveryStatus(value) {
9145
+ const normalized = normalizeMarkdownCell(value || "");
9146
+ if (!HOST_DELIVERY_STATUSES.has(normalized)) {
9147
+ throw new Error(`--host-delivery 必须是 ${[...HOST_DELIVERY_STATUSES].join(" / ")}。`);
9148
+ }
9149
+ return normalized;
9150
+ }
9151
+
8768
9152
  function parsePositiveInt(value, fallback) {
8769
9153
  if (value == null || value === "") return fallback;
8770
9154
  const parsed = Number.parseInt(value, 10);
@@ -9022,12 +9406,16 @@ function printGenericPlan(title, actions) {
9022
9406
  }
9023
9407
 
9024
9408
  function printPlan(plan, dryRun) {
9025
- const creates = plan.actions.filter((action) => action.mode === "create");
9026
- const emptyUpdates = plan.actions.filter((action) => action.mode === "overwrite-empty");
9409
+ const creates = plan.actions.filter(actionCreatesFromUserView);
9410
+ const updates = plan.actions.filter(actionUpdatesFromUserView);
9027
9411
  const createNew = plan.actions.filter((action) => action.mode === "create-new");
9412
+ const isExistingProject = isExistingProjectTarget(plan);
9028
9413
 
9029
9414
  console.log("");
9030
9415
  console.log(dryRun ? "创建工作台预览:" : "创建工作台计划:");
9416
+ if (dryRun) {
9417
+ console.log("这是预览,不会写入文件。");
9418
+ }
9031
9419
  console.log("");
9032
9420
  console.log(`工作台名称:${plan.workspaceName}`);
9033
9421
  console.log(`工作台类型:${plan.workspaceLabel}`);
@@ -9045,23 +9433,107 @@ function printPlan(plan, dryRun) {
9045
9433
  console.log(`会带上的 AI 使用说明:${plan.skills.map((skill) => skill.id).join("、")}`);
9046
9434
  }
9047
9435
  console.log("");
9436
+ if (isExistingProject) {
9437
+ console.log("检测到这是已有项目。");
9438
+ console.log("StarWork 会保留现有文件,先生成待整合草稿,不直接覆盖已有 AI 规则文件。");
9439
+ console.log("");
9440
+ }
9048
9441
 
9442
+ console.log("会创建:");
9049
9443
  if (creates.length) {
9050
- console.log("会创建这些文件或文件夹:");
9051
- creates.slice(0, 40).forEach((action) => console.log(`- ${action.relativePath}`));
9052
- if (creates.length > 40) console.log(`- ... 另有 ${creates.length - 40} 项`);
9053
- console.log("");
9444
+ creates.forEach((action) => console.log(`- ${action.relativePath}`));
9445
+ } else {
9446
+ console.log("- 无");
9054
9447
  }
9055
- if (emptyUpdates.length) {
9056
- console.log("会补充这些空文件:");
9057
- emptyUpdates.forEach((action) => console.log(`- ${action.relativePath}`));
9058
- console.log("");
9448
+ console.log("");
9449
+
9450
+ console.log("会更新:");
9451
+ if (updates.length) {
9452
+ updates.forEach((action) => console.log(`- ${action.relativePath}`));
9453
+ } else {
9454
+ console.log("- 无");
9059
9455
  }
9456
+ console.log("");
9457
+
9458
+ console.log("不会改动:");
9459
+ console.log("- 你的业务代码");
9460
+ console.log("- 已有非空 AI 规则文件");
9060
9461
  if (createNew.length) {
9061
- console.log("发现已有同名文件,不会覆盖,会另存为:");
9462
+ console.log("- 已有同名文件会保留,StarWork 会另存旁路文件");
9062
9463
  createNew.forEach((action) => console.log(`- ${path.relative(plan.targetDir, action.originalTarget)} -> ${action.relativePath}`));
9063
- console.log("");
9064
9464
  }
9465
+ console.log("");
9466
+
9467
+ console.log("需要你确认:");
9468
+ console.log("- 目标路径是否正确");
9469
+ console.log("- 是否接受这些 StarWork 协作文件");
9470
+ console.log("");
9471
+ }
9472
+
9473
+ function renderInitPlanJson(plan, dryRun) {
9474
+ return {
9475
+ schema: "starwork.init.plan_result.v0.1",
9476
+ target: plan.targetDir,
9477
+ dry_run: Boolean(dryRun),
9478
+ ok: true,
9479
+ workspace_type: plan.workspaceType,
9480
+ kit: plan.kit,
9481
+ language: plan.language,
9482
+ pack: plan.pack?.id || null,
9483
+ actions: plan.actions.map((action) => ({
9484
+ type: action.type,
9485
+ mode: action.mode,
9486
+ path: action.relativePath,
9487
+ status: action.mode === "exists" ? "exists" : "planned"
9488
+ })),
9489
+ user_summary: buildInitUserSummary(plan, dryRun)
9490
+ };
9491
+ }
9492
+
9493
+ function buildInitUserSummary(plan, dryRun) {
9494
+ const willCreate = plan.actions
9495
+ .filter(actionCreatesFromUserView)
9496
+ .map((action) => action.relativePath);
9497
+ const willUpdate = plan.actions
9498
+ .filter(actionUpdatesFromUserView)
9499
+ .map((action) => action.relativePath);
9500
+ return {
9501
+ product_purpose: "把项目整理成 AI 协作工作台",
9502
+ mode: dryRun ? "preview_no_write" : "write_after_confirmation",
9503
+ target_kind: isExistingProjectTarget(plan) ? "existing_project" : "new_workspace",
9504
+ will_create: willCreate,
9505
+ will_update: willUpdate,
9506
+ will_not_touch: [
9507
+ "你的业务代码",
9508
+ "已有非空 AI 规则文件"
9509
+ ],
9510
+ needs_confirmation: [
9511
+ "目标路径是否正确",
9512
+ "是否接受这些 StarWork 协作文件"
9513
+ ]
9514
+ };
9515
+ }
9516
+
9517
+ function actionCreatesFromUserView(action) {
9518
+ if (!action || action.mode === "exists" || action.mode === "skip") return false;
9519
+ if (action.type === "directory") return action.mode === "create";
9520
+ if (action.type === "symlink") return action.mode === "create" && !fs.existsSync(action.target);
9521
+ if (action.type !== "file") return false;
9522
+ if (action.mode === "create" || action.mode === "create-new") return true;
9523
+ if (action.mode === "overwrite" || action.mode === "overwrite-empty") return !fs.existsSync(action.target);
9524
+ return false;
9525
+ }
9526
+
9527
+ function actionUpdatesFromUserView(action) {
9528
+ if (!action || action.type !== "file") return false;
9529
+ if (action.mode !== "overwrite" && action.mode !== "overwrite-empty") return false;
9530
+ return fs.existsSync(action.target);
9531
+ }
9532
+
9533
+ function isExistingProjectTarget(plan) {
9534
+ if (!plan.targetExists || !fs.existsSync(plan.targetDir)) return false;
9535
+ const entries = fs.readdirSync(plan.targetDir).filter((entry) => entry !== ".DS_Store");
9536
+ return entries.length > 0;
9065
9537
  }
9066
9538
 
9067
9539
  function printSpawnPlan(plan, dryRun) {
@@ -9297,12 +9769,13 @@ function printInitHelp() {
9297
9769
  Usage:
9298
9770
  starwork init [options]
9299
9771
 
9300
- 创建一个 StarWork 工作台。v0.1 中,项目工作台默认加入通用工作能力;
9301
- 项目中心会自动加入项目中心管理能力。
9772
+ starwork init 会把一个目录整理成 StarWork 工作台,让 AI 能找到项目说明、当前任务、协作规则和交接记录。
9773
+
9774
+ 项目工作台默认加入通用工作能力;项目中心会自动加入项目中心管理能力。
9302
9775
 
9303
9776
  Options:
9304
9777
  --type <project|hub>
9305
- project 创建项目工作台;hub 创建项目中心
9778
+ project 创建项目工作台;hub 创建项目中心。
9306
9779
  --pack <general|content-creator|hub-management|path>
9307
9780
  --language <zh|en>
9308
9781
  --name <name>
@@ -9313,11 +9786,13 @@ Options:
9313
9786
  --adapter <codex|claude-code|cursor|trae|all>
9314
9787
  初始化完成后继续生成对应 AI 工具适配入口。
9315
9788
  --agent-docs <draft|skip|write>
9316
- AI 入口文档策略。已有非空入口默认写入 .starwork/drafts 草稿,等待 starworkInit 整合。
9789
+ 已有 AI 规则文件时,先生成待整合草稿,不覆盖原文件。
9317
9790
  --target <path>
9318
9791
  --dry-run
9792
+ 预览将要写入的文件,不做真实改动。
9319
9793
  --no-skills
9320
9794
  --yes, -y
9795
+ 确认执行,会真实写入 StarWork 工作台文件。
9321
9796
 
9322
9797
  示例:
9323
9798
  starwork init --type project --pack general --language zh --target ./my-workspace --yes
@@ -9533,7 +10008,7 @@ function printLanesHelp() {
9533
10008
  console.log(`StarWork Multiagent
9534
10009
 
9535
10010
  Usage:
9536
- starwork multiagent <init|add|bind|release|status|read|instruct|handoff|continue|launch|share> [options]
10011
+ starwork multiagent <init|add|bind|release|status|read|instruct|handoff|continue|launch|message|request|share> [options]
9537
10012
 
9538
10013
  Agent Lanes 用于同一项目内多个 Agent 会话按项目自定义职责位协作。
9539
10014
 
@@ -9547,7 +10022,9 @@ Subcommands:
9547
10022
  instruct 向另一个 lane 发送格式化跨会话指令。
9548
10023
  handoff 生成并记录人工交付消息,不后台发送。
9549
10024
  continue 输出继续某个 lane 宿主会话的人工命令或步骤。
9550
- launch 为已有 lane 创建并绑定独立宿主会话。
10025
+ launch 旧入口:为 Codex 生成 Launch Message;实际创建由 starworkMultiagent 调用 create_thread。
10026
+ message 生成标准 launch / instruction 消息模板,不写状态。
10027
+ request 记录已由 Skill 或人工完成的跨 lane 请求投递状态。
9551
10028
  share 登记一个跨 lane 可读输出。
9552
10029
 
9553
10030
  示例:
@@ -9555,7 +10032,8 @@ Subcommands:
9555
10032
  starwork multiagent add review --purpose "审校和风险检查" --write "reviews/**,product/docs/**" --target ./my-workspace --yes
9556
10033
  starwork multiagent bind research --session codex:manual-research-1 --session-name "Research Agent" --target ./my-workspace --yes
9557
10034
  starwork multiagent status --host --target ./my-workspace --json
9558
- starwork multiagent instruct development --from product-planning --message "请根据 SPEC 开始实现。" --target ./my-workspace --yes
10035
+ starwork multiagent message instruct development --from product-planning --message "请根据 SPEC 开始实现。" --target ./my-workspace --json
10036
+ 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
9559
10037
  `);
9560
10038
  }
9561
10039
 
@@ -9608,9 +10086,8 @@ Options:
9608
10086
  --yes, -y
9609
10087
 
9610
10088
  说明:
9611
- --session-name 会在绑定成功后 best-effort 同步宿主工具的会话名称。
9612
- --pin 会请求宿主置顶;不支持时只输出 warning,不回滚绑定。
9613
- instruct 的自动投递由 CLI 运行时判断;没有标准后台投递能力时返回 manual_handoff_required。
10089
+ bind 只记录 StarWork lane binding,不调用 Codex host 工具。
10090
+ --session-name / --pin 需要 starworkMultiagent 先在 Codex App 中调用 set_thread_title / set_thread_pinned,本命令只记录结果。
9614
10091
  `);
9615
10092
  }
9616
10093
 
@@ -9725,6 +10202,80 @@ Options:
9725
10202
  --json
9726
10203
  --dry-run
9727
10204
  --yes, -y
10205
+
10206
+ 说明:
10207
+ Codex 标准路径下,CLI 不创建 thread、不改名、不置顶。请让 starworkMultiagent 调用 create_thread / set_thread_title / set_thread_pinned,成功后再用 bind 记录 StarWork 状态。
10208
+ `);
10209
+ }
10210
+
10211
+ function printLanesMessageHelp() {
10212
+ console.log(`StarWork Multiagent Message
10213
+
10214
+ Usage:
10215
+ starwork multiagent message <launch|instruct> [options]
10216
+
10217
+ 说明:
10218
+ 只生成 STARWORK:MULTIAGENT_MESSAGE 模板,不调用宿主、不写 shared/state。
10219
+ `);
10220
+ }
10221
+
10222
+ function printLanesMessageLaunchHelp() {
10223
+ console.log(`StarWork Multiagent Message Launch
10224
+
10225
+ Usage:
10226
+ starwork multiagent message launch <lane-id> [options]
10227
+
10228
+ Options:
10229
+ --target <path>
10230
+ --from <lane-id>
10231
+ --session-name <name>
10232
+ --json
10233
+ `);
10234
+ }
10235
+
10236
+ function printLanesMessageInstructHelp() {
10237
+ console.log(`StarWork Multiagent Message Instruct
10238
+
10239
+ Usage:
10240
+ starwork multiagent message instruct <to-lane> --from <from-lane> --message <text> [options]
10241
+
10242
+ Options:
10243
+ --target <path>
10244
+ --from <lane-id>
10245
+ --message <text>
10246
+ --id <request-id>
10247
+ --json
10248
+ `);
10249
+ }
10250
+
10251
+ function printLanesRequestHelp() {
10252
+ console.log(`StarWork Multiagent Request
10253
+
10254
+ Usage:
10255
+ starwork multiagent request record [options]
10256
+
10257
+ 说明:
10258
+ 只记录已发生的跨 lane 请求投递状态,不调用宿主。
10259
+ `);
10260
+ }
10261
+
10262
+ function printLanesRequestRecordHelp() {
10263
+ console.log(`StarWork Multiagent Request Record
10264
+
10265
+ Usage:
10266
+ starwork multiagent request record --from <lane> --to <lane> --message <text> --host-delivery <status> --delivery-tool <tool> [options]
10267
+
10268
+ Options:
10269
+ --target <path>
10270
+ --from <lane-id>
10271
+ --to <lane-id>
10272
+ --message <text>
10273
+ --host-delivery <delivered_via_codex_thread_tool|recorded_only|manual_handoff_required|failed>
10274
+ --delivery-tool <tool-name>
10275
+ --id <request-id>
10276
+ --json
10277
+ --dry-run
10278
+ --yes, -y
9728
10279
  `);
9729
10280
  }
9730
10281