@my-life-buddies/cli 0.1.2

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 (158) hide show
  1. package/README.md +314 -0
  2. package/dist/bin/buddy.js +443 -0
  3. package/dist/bin/buddy.js.map +7 -0
  4. package/dist/bin/core.js +5638 -0
  5. package/dist/bin/core.js.map +7 -0
  6. package/dist/bin/preview.js +1429 -0
  7. package/dist/bin/preview.js.map +7 -0
  8. package/dist/web/app.css +508 -0
  9. package/dist/web/app.js +724 -0
  10. package/dist/web/assets/developer-platform-icon.png.base64 +1 -0
  11. package/dist/web/brand.css +100 -0
  12. package/dist/web/index.html +236 -0
  13. package/dist/web/tokens.css +15 -0
  14. package/package.json +45 -0
  15. package/resources/README.md +52 -0
  16. package/resources/agent-template/package-lock.json +1726 -0
  17. package/resources/agent-template/package.json +16 -0
  18. package/resources/brand/README.md +11 -0
  19. package/resources/brand/login-result.css +25 -0
  20. package/resources/brand/logo.png +0 -0
  21. package/resources/brand/tokens.css +100 -0
  22. package/resources/buddy-creator/INSTALL.md +25 -0
  23. package/resources/buddy-creator/PATCHES.md +33 -0
  24. package/resources/buddy-creator/SKILL.md +66 -0
  25. package/resources/buddy-creator/THIRD_PARTY_NOTICES.md +154 -0
  26. package/resources/buddy-creator/agents/openai.yaml +4 -0
  27. package/resources/buddy-creator/assets/licenses/bail.txt +26 -0
  28. package/resources/buddy-creator/assets/licenses/ccount.txt +26 -0
  29. package/resources/buddy-creator/assets/licenses/character-entities-html4.txt +26 -0
  30. package/resources/buddy-creator/assets/licenses/character-entities-legacy.txt +26 -0
  31. package/resources/buddy-creator/assets/licenses/character-entities.txt +26 -0
  32. package/resources/buddy-creator/assets/licenses/character-reference-invalid.txt +26 -0
  33. package/resources/buddy-creator/assets/licenses/comma-separated-tokens.txt +26 -0
  34. package/resources/buddy-creator/assets/licenses/debug.txt +24 -0
  35. package/resources/buddy-creator/assets/licenses/decode-named-character-reference.txt +26 -0
  36. package/resources/buddy-creator/assets/licenses/dequal.txt +25 -0
  37. package/resources/buddy-creator/assets/licenses/devlop.txt +26 -0
  38. package/resources/buddy-creator/assets/licenses/escape-string-regexp.txt +13 -0
  39. package/resources/buddy-creator/assets/licenses/estree-util-is-identifier-name.txt +26 -0
  40. package/resources/buddy-creator/assets/licenses/extend.txt +27 -0
  41. package/resources/buddy-creator/assets/licenses/hast-util-to-jsx-runtime.txt +26 -0
  42. package/resources/buddy-creator/assets/licenses/hast-util-whitespace.txt +26 -0
  43. package/resources/buddy-creator/assets/licenses/html-url-attributes.txt +25 -0
  44. package/resources/buddy-creator/assets/licenses/inline-style-parser.txt +13 -0
  45. package/resources/buddy-creator/assets/licenses/is-alphabetical.txt +26 -0
  46. package/resources/buddy-creator/assets/licenses/is-alphanumerical.txt +26 -0
  47. package/resources/buddy-creator/assets/licenses/is-decimal.txt +26 -0
  48. package/resources/buddy-creator/assets/licenses/is-hexadecimal.txt +26 -0
  49. package/resources/buddy-creator/assets/licenses/is-plain-obj.txt +13 -0
  50. package/resources/buddy-creator/assets/licenses/longest-streak.txt +26 -0
  51. package/resources/buddy-creator/assets/licenses/markdown-table.txt +26 -0
  52. package/resources/buddy-creator/assets/licenses/mdast-util-find-and-replace.txt +26 -0
  53. package/resources/buddy-creator/assets/licenses/mdast-util-from-markdown.txt +26 -0
  54. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-autolink-literal.txt +26 -0
  55. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-footnote.txt +26 -0
  56. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-strikethrough.txt +26 -0
  57. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-table.txt +26 -0
  58. package/resources/buddy-creator/assets/licenses/mdast-util-gfm-task-list-item.txt +26 -0
  59. package/resources/buddy-creator/assets/licenses/mdast-util-gfm.txt +26 -0
  60. package/resources/buddy-creator/assets/licenses/mdast-util-mdx-expression.txt +26 -0
  61. package/resources/buddy-creator/assets/licenses/mdast-util-mdx-jsx.txt +26 -0
  62. package/resources/buddy-creator/assets/licenses/mdast-util-mdxjs-esm.txt +26 -0
  63. package/resources/buddy-creator/assets/licenses/mdast-util-phrasing.txt +27 -0
  64. package/resources/buddy-creator/assets/licenses/mdast-util-to-hast.txt +26 -0
  65. package/resources/buddy-creator/assets/licenses/mdast-util-to-markdown.txt +26 -0
  66. package/resources/buddy-creator/assets/licenses/mdast-util-to-string.txt +26 -0
  67. package/resources/buddy-creator/assets/licenses/micromark-core-commonmark.txt +26 -0
  68. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-autolink-literal.txt +26 -0
  69. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-footnote.txt +26 -0
  70. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-strikethrough.txt +26 -0
  71. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-table.txt +26 -0
  72. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-tagfilter.txt +26 -0
  73. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-task-list-item.txt +26 -0
  74. package/resources/buddy-creator/assets/licenses/micromark-extension-gfm.txt +26 -0
  75. package/resources/buddy-creator/assets/licenses/micromark-factory-destination.txt +26 -0
  76. package/resources/buddy-creator/assets/licenses/micromark-factory-label.txt +26 -0
  77. package/resources/buddy-creator/assets/licenses/micromark-factory-space.txt +26 -0
  78. package/resources/buddy-creator/assets/licenses/micromark-factory-title.txt +26 -0
  79. package/resources/buddy-creator/assets/licenses/micromark-factory-whitespace.txt +26 -0
  80. package/resources/buddy-creator/assets/licenses/micromark-util-character.txt +26 -0
  81. package/resources/buddy-creator/assets/licenses/micromark-util-chunked.txt +26 -0
  82. package/resources/buddy-creator/assets/licenses/micromark-util-classify-character.txt +26 -0
  83. package/resources/buddy-creator/assets/licenses/micromark-util-combine-extensions.txt +26 -0
  84. package/resources/buddy-creator/assets/licenses/micromark-util-decode-numeric-character-reference.txt +26 -0
  85. package/resources/buddy-creator/assets/licenses/micromark-util-decode-string.txt +26 -0
  86. package/resources/buddy-creator/assets/licenses/micromark-util-encode.txt +26 -0
  87. package/resources/buddy-creator/assets/licenses/micromark-util-html-tag-name.txt +26 -0
  88. package/resources/buddy-creator/assets/licenses/micromark-util-normalize-identifier.txt +26 -0
  89. package/resources/buddy-creator/assets/licenses/micromark-util-resolve-all.txt +26 -0
  90. package/resources/buddy-creator/assets/licenses/micromark-util-sanitize-uri.txt +26 -0
  91. package/resources/buddy-creator/assets/licenses/micromark-util-subtokenize.txt +26 -0
  92. package/resources/buddy-creator/assets/licenses/micromark-util-symbol.txt +26 -0
  93. package/resources/buddy-creator/assets/licenses/micromark-util-types.txt +26 -0
  94. package/resources/buddy-creator/assets/licenses/micromark.txt +26 -0
  95. package/resources/buddy-creator/assets/licenses/ms.txt +25 -0
  96. package/resources/buddy-creator/assets/licenses/parse-entities.txt +26 -0
  97. package/resources/buddy-creator/assets/licenses/phosphor-icons--react.txt +25 -0
  98. package/resources/buddy-creator/assets/licenses/property-information.txt +26 -0
  99. package/resources/buddy-creator/assets/licenses/react-dom.txt +25 -0
  100. package/resources/buddy-creator/assets/licenses/react-markdown.txt +25 -0
  101. package/resources/buddy-creator/assets/licenses/react.txt +25 -0
  102. package/resources/buddy-creator/assets/licenses/remark-gfm.txt +26 -0
  103. package/resources/buddy-creator/assets/licenses/remark-parse.txt +25 -0
  104. package/resources/buddy-creator/assets/licenses/remark-rehype.txt +26 -0
  105. package/resources/buddy-creator/assets/licenses/remark-stringify.txt +25 -0
  106. package/resources/buddy-creator/assets/licenses/scheduler.txt +25 -0
  107. package/resources/buddy-creator/assets/licenses/space-separated-tokens.txt +26 -0
  108. package/resources/buddy-creator/assets/licenses/stringify-entities.txt +26 -0
  109. package/resources/buddy-creator/assets/licenses/style-to-js.txt +26 -0
  110. package/resources/buddy-creator/assets/licenses/style-to-object.txt +26 -0
  111. package/resources/buddy-creator/assets/licenses/trim-lines.txt +26 -0
  112. package/resources/buddy-creator/assets/licenses/trough.txt +25 -0
  113. package/resources/buddy-creator/assets/licenses/types--debug.txt +25 -0
  114. package/resources/buddy-creator/assets/licenses/types--estree-jsx.txt +25 -0
  115. package/resources/buddy-creator/assets/licenses/types--estree.txt +25 -0
  116. package/resources/buddy-creator/assets/licenses/types--hast.txt +25 -0
  117. package/resources/buddy-creator/assets/licenses/types--mdast.txt +25 -0
  118. package/resources/buddy-creator/assets/licenses/types--ms.txt +25 -0
  119. package/resources/buddy-creator/assets/licenses/types--unist.txt +25 -0
  120. package/resources/buddy-creator/assets/licenses/ungap--structured-clone.txt +19 -0
  121. package/resources/buddy-creator/assets/licenses/unified.txt +25 -0
  122. package/resources/buddy-creator/assets/licenses/unist-util-is.txt +26 -0
  123. package/resources/buddy-creator/assets/licenses/unist-util-position.txt +26 -0
  124. package/resources/buddy-creator/assets/licenses/unist-util-stringify-position.txt +26 -0
  125. package/resources/buddy-creator/assets/licenses/unist-util-visit-parents.txt +26 -0
  126. package/resources/buddy-creator/assets/licenses/unist-util-visit.txt +26 -0
  127. package/resources/buddy-creator/assets/licenses/vfile-message.txt +26 -0
  128. package/resources/buddy-creator/assets/licenses/vfile.txt +25 -0
  129. package/resources/buddy-creator/assets/licenses/zwitch.txt +26 -0
  130. package/resources/buddy-creator/assets/preview/assets/index-B5VmkoSx.js +39 -0
  131. package/resources/buddy-creator/assets/preview/assets/index-DWKP6b-3.css +4 -0
  132. package/resources/buddy-creator/assets/preview/brand-logo.png +0 -0
  133. package/resources/buddy-creator/assets/preview/brand.css +100 -0
  134. package/resources/buddy-creator/assets/preview/index.html +5 -0
  135. package/resources/buddy-creator/assets/preview/reader.css +165 -0
  136. package/resources/buddy-creator/assets/preview/reader.js +135 -0
  137. package/resources/buddy-creator/references/artifact-schema.md +68 -0
  138. package/resources/buddy-creator/references/catalog.json +401 -0
  139. package/resources/buddy-creator/references/definition.md +24 -0
  140. package/resources/buddy-creator/references/dialogue-examples.md +133 -0
  141. package/resources/buddy-creator/references/host-guide.md +213 -0
  142. package/resources/buddy-creator/references/interview.md +235 -0
  143. package/resources/buddy-creator/references/knowledge.md +40 -0
  144. package/resources/buddy-creator/references/methods.md +77 -0
  145. package/resources/buddy-creator/references/opening.md +14 -0
  146. package/resources/buddy-creator/references/preview-panel.md +66 -0
  147. package/resources/buddy-creator/references/recovery.md +43 -0
  148. package/resources/buddy-creator/references/service.md +74 -0
  149. package/resources/buddy-creator/scripts/buddy.py +108 -0
  150. package/resources/buddy-creator/scripts/buddy_client.py +67 -0
  151. package/resources/buddy-creator/scripts/buddy_core.py +803 -0
  152. package/resources/buddy-creator/scripts/completion.py +240 -0
  153. package/resources/buddy-creator/scripts/interview.py +215 -0
  154. package/resources/buddy-creator/scripts/preview.py +577 -0
  155. package/resources/buddy-creator/scripts/preview_panel.py +80 -0
  156. package/resources/buddy-creator/scripts/sources.py +239 -0
  157. package/resources/buddy-creator/version.json +8 -0
  158. package/resources/buddy-creator.manifest.json +696 -0
@@ -0,0 +1,66 @@
1
+ # Booklet 面板检查与一次性重开提示
2
+
3
+ Booklet 面板让开发者边聊边查看已经整理的内容和当前进度。开发者可能误关,也可能选择只用对话;关闭面板不等于暂停创作。提醒只提供便利,不是继续采访或确认手册的前置条件。
4
+
5
+ ## 1. 何时检查,怎样判断
6
+
7
+ 首次创作沿用 SKILL.md 的打开流程。接续同一作品、进入下一册、准备展示手册或收到界面相关反馈时,优先使用宿主实际可用的面板/浏览器标签页查询能力,检查这个 workspace 的 `preview.url`。不逐轮反复查询,不启动后台轮询或定时提醒。
8
+
9
+ - **open**:找到对应 URL 的已打开面板或标签页。后台标签页、暂未聚焦或最小化仍算打开,不因用户在聊天就提醒。
10
+ - **closed**:宿主对相关面板/浏览器提供的完整清单中确认没有该作品界面,或开发者明确说自己关闭了它。匹配完整项目入口,允许同入口的 hash 路由;不能把其他作品的 Booklet 当成本作品。
11
+ - **unknown**:宿主不能查询界面,或只能看到前台窗口、部分标签页、查询失败。保持未知,不说“你关闭了面板”,也不据此主动询问重开。
12
+
13
+ 预览 URL 存在、HTTP 请求成功、预览进程运行、曾调用打开工具都不能证明界面现在开着;事件连接断开也不能证明关闭。查询能力必须来自实际宿主工具,不猜测 API,不为检查而打开、切换或刷新面板。
14
+
15
+ ## 2. 提醒只发生一次
16
+
17
+ 面板状态与提醒结果保存在当前 workspace 的 `preview-panel.json`,通过下述命令读写,不修改 `state.json`,不增加采访回答次数、作品 revision 或确认记录。每个作品最多一次主动询问;换宿主、恢复项目、切换阶段或再次关页都不重置。不同作品各自记录。
18
+
19
+ 使用实际 Python 和技能目录,JSON 通过文件或安全标准输入传入:
20
+
21
+ ```text
22
+ <Python路径> <技能目录>/scripts/buddy.py preview-panel --workspace <已保存目录> --input <JSON文件>
23
+ ```
24
+
25
+ | JSON 请求 | 用途 |
26
+ | --- | --- |
27
+ | `{"action":"status"}` | 读取提醒状态;默认 unknown / not_offered,读取本身不创建记录 |
28
+ | `{"action":"observe","visibility":"open","source":"host_inventory"}` | 保存实际宿主查询结果;visibility 可为 open、closed 或 unknown |
29
+ | `{"action":"observe","visibility":"closed","source":"user_report"}` | 开发者明确说关闭了界面 |
30
+ | `{"action":"observe","visibility":"unknown","source":"unavailable"}` | 无可用查询能力,不做关闭推断 |
31
+ | `{"action":"offer"}` | 刚确认关闭后,持久登记本次提醒资格;仅返回 shouldAsk=true 时可提出一次问题 |
32
+ | `{"action":"respond","response":"declined"}` | 开发者明确拒绝;accepted 表示明确要求打开,ignored 表示后续输入没有回应面板问题 |
33
+
34
+ `offer` 只接受两分钟内记录的关闭观察。过期时不沿用旧状态询问,需重新实际检查。工具不自行检测浏览器;host_inventory / user_report 必须有真实依据。`canOffer` 是候选状态,不等于可以直接发问,最终以 `offer.shouldAsk` 为准。
35
+
36
+ `offer` 在返回前把 reminder 保存为 offered。相同请求重试、回执丢失或下一轮还没处理回复时都不会再给询问资格。不能删记录、改成 not_offered 或重新记录 closed 来恢复资格。记录损坏时保留现场,不主动询问,也不重建为空白;仍可响应开发者主动要求打开界面。
37
+
38
+ ## 3. 怎样询问而不打断采访
39
+
40
+ 仅在已确认 closed、reminder 尚未用过且 `offer.shouldAsk=true` 时,提出下面这类简短提示,把作用和以后主动打开的说法一次讲清:
41
+
42
+ > Booklet 面板现在没有打开。需要我重新打开,方便你边聊边查看已整理的内容和进度吗?不需要也可以继续聊。之后想打开时,直接说“请帮我重新打开 Booklet 面板”就行。
43
+
44
+ 优先通过宿主独立的非阻塞界面询问入口发出。它是界面操作问题,不是采访目标;不新增 targetId/gapId,不把面板答复写进知识或方法。不在 `delivery.text` 后拼接第二个未保存的采访问题,不把界面操作当作手册确认。
45
+
46
+ 没有独立询问入口时,在自然停顿处单独给出这条界面提示,保留原采访待答内容;这一条不再附新采访问题。开发者继续回答原问题时立即接着创作,不要求先答面板问题,也不重发原问题。这是仅针对界面维护的旁路,不改变正常 turn_begin / turn_finish 的采访闭环。
47
+
48
+ | 后续输入 | 行为 |
49
+ | --- | --- |
50
+ | 明确同意重开,或直接说“请帮我重新打开 Booklet 面板” | 记录 accepted,按下一节实际打开;无需再次确认 |
51
+ | 明确拒绝、不需要、别再提醒 | 记录 declined,继续原创作,不再询问或重复提示口令 |
52
+ | 回答原采访问题、谈其他内容或没有回应 | 不打开、不追问。下一条无关输入到来时记录 ignored;尚无新输入时保留 offered,同样不会再次提醒 |
53
+ | “可以”“继续”等同时可能回答采访问题或确认正文的含糊回复 | 不推断同意重开,不追加澄清面板问题;按原采访语境处理,面板提醒保持已使用 |
54
+
55
+ 纯面板请求或答复走本旁路,不创建采访回合;同一消息也含真实采访答案或修订时,完整原话仍按正常协议保存,只将其中实际采访意图用于目标、次数和确认。保留原问题与版本绑定,不能用“好,打开吧”确认手册。用过一次提醒后,包括同意重开又再次关闭,也不主动再问;开发者后续明确要求打开始终有效。
56
+
57
+ 不等待固定时长判定“默认同意”,不追问“看到提示了吗”,不反复说“你随时可以打开”。主动打开的口令已包含在第一次提示中;开发者主动问起界面时可再说明。
58
+
59
+ ## 4. 实际重新打开
60
+
61
+ 1. 沿用当前 workspace 与真实预览入口,先查看面板是否已打开。已打开则按开发者明确请求定位/聚焦它,不创建重复标签页;无法查询时不声称已关闭,仍可按明确请求调用打开工具。
62
+ 2. 预览服务需要恢复时执行 `open --workspace <原目录> --no-browser`,复用当前身份;遵循 recovery.md,不换 creation-key、不新建作品、不重置进度。该调用只恢复服务,不代表已经打开界面。
63
+ 3. 用宿主实际提供的工具打开或定位 `preview.url`,优先原侧边面板。根据真实回执/查询记录 open;仅排队、失败或不能验证时保留 unknown,如实说明结果并提供可用入口,不声称成功。
64
+ 4. 继续原采访或调用方流程。打开面板不重问开场、不重放旧确认、不把旧 delivery 当作新的用户答案。面板状态和历史提醒独立,不因打开成功重新获得提醒资格。
65
+
66
+ 无法打开或端口冲突时说明实际原因,保留内容和原问题。没有面板仍可通过主对话或实际可读文件展示待校准内容;不能因为缺少面板跳过正式展示与确认。
@@ -0,0 +1,43 @@
1
+ # 中断与恢复
2
+
3
+ 此文件说明 Buddy Creator Skill 1.3.1 的 Python 项目恢复范围;不使用旧版 sessionId、workToken、LangGraph 检查点或 Node 启动器。
4
+
5
+ | 情况 | 处理方式 |
6
+ | --- | --- |
7
+ | 同一条输入的提交响应丢失 | 用原 clientKey 和原 raw 重试 turn_begin;同一身份不能换原话 |
8
+ | 已保存输入,宿主撰写中断 | 用 turn_continue 读取已有工作和 context,继续同一 turnId,不重新输入旧答案 |
9
+ | turn_finish 返回具体校验错误 | 修正本轮 patch 或 delivery 后重新提交;保留原输入与已保存来源,不创造确认 |
10
+ | 不清楚 finish 是否成功 | 先 turn_continue 或 status 查看实际回执;已成功回合不能换正文重放 |
11
+ | 用户明确暂停 | 保存 paused;停止提问,已保存内容可在下一次 open 恢复 |
12
+ | 纯答疑 | 保存用户问题和解释,保留原待答对象;解释不算采访回答,不借机推进 |
13
+ | 用户修订已有内容 | 只改受影响对象;保留旧版本和原话,原确认及依赖受影响时重新校准 |
14
+ | 资料只取得片段 | 保存 partial 和真实缺口,不把来源标为完整可用;补齐后用新 operationId 导入 |
15
+ | 用户换宿主继续 | 使用同一个 workspace;先 open,再读取保存的 delivery、原话和目标。保留 gapId、缺口次数、无新增记录及目标总次数,不重置 |
16
+ | 已收口后主动补充 | 用有本轮原话依据的 interview.resolutions 解决具体缺口并修订;只有明确针对该目标的续谈要求才用 interview.reopen |
17
+ | 所有独立目标均无法推进 | 保存具体缺口及恢复条件,使用 delivery.blocked;不能假装用户暂停或重新盘问耗尽的细节 |
18
+ | 四册已确认但成果文件生成失败 | 保留确认,处理具体错误后调用 finalize |
19
+ | 重启时四册及成果均已完成 | open/status 返回 creator_complete;结束 Skill,按调用方说明继续下一阶段,不重问采访 |
20
+ | 已完成手册缺失、被修改或不对应当前确认版本 | 返回 finalize_required;先恢复真实成果,不直接交给下一阶段 |
21
+ | 手册交付后开始技术讨论 | 退出 Skill,按调用方流程处理;不登记成 Creator 的 explanation 或采访回合 |
22
+
23
+ 重试只恢复实际保存的操作;本地程序无法恢复宿主从未发送的消息、从未提交的标注意见或外部工具内部进度。不要声称仍有未实际启动的后台任务。
24
+
25
+ ## 预览连接
26
+
27
+ 界面关闭与服务断开分别处理。接续时先按 [面板检查与重开](preview-panel.md) 查询本作品界面;确认关闭后最多主动询问一次,未回应或拒绝都不再追问,也不自动拉起。提醒保存在独立的 preview-panel.json,恢复、换宿主和服务重启不重置。用户明确说“请帮我重新打开 Booklet 面板”时直接恢复原入口;状态未知不猜测关闭。
28
+
29
+ 同一项目保存固定的本机预览入口。再次 open 优先复用原入口并启动缺失的预览进程,不能为了恢复随意删掉端口与身份记录。页面只监听本机 127.0.0.1,内容与事件接口只读。页面已加载时可重试读取更新;预览进程退出时,需要宿主重新 open 启动后刷新原地址,浏览器本身不能重启程序。
30
+
31
+ 若固定端口被其他进程占用,说明真实冲突,不停止身份未知的程序,也不承诺旧 URL 会自动跳到新端口。具体错误依据返回值处理。预览只负责显示已保存进度和公开草稿,不能修改内容或代替用户确认。本轮登记为 working 后 120 秒没有新的实际更新,页面显示“本轮还未完成”,避免无限播放像是在持续思考的动画;这不代表原输入丢失。
32
+
33
+ ## 工作目录与历史
34
+
35
+ 新项目不继承其他作品,但可以通过 `open --context` 接收显式背景;背景不会变成采访回答或确认。已有项目补入背景不清空原话、产物和确认;已经开始的采访按原进度继续。仅展示过通用开场、尚无回答时可按宿主协议保存相关的新开场,旧展示记录仍保留。此前 Python 无 Node 试用版项目可通过 `open --workspace` 原路径接续,保留预览端口与身份。原 Python 预览进程仍可用时可复用,不需要为升级搬动项目。新建默认使用当前创作目录下的 buddies/;同一 creation-key 已在原 buddies-no-node/ 匹配到项目时复用该目录,不重复创建。
36
+
37
+ 本版本不自动迁移旧 Node 0.x 工作目录或其 SQLite/LangGraph 检查点;需要参考旧作品时只导入创作者选择的资料,不改写原项目。所有新内容由 Python 工具写入本项目的 state.json 与归档、历史、成果目录。宿主不能直接修改 state.json 绕过版本、证据或确认校验。
38
+
39
+ 本版本没有旧版 interviewCycleId 重置接口。用户大幅改变定位、人群或核心任务时,先保留原话、评估受影响内容;若需要重做整个访谈,基于用户明确改向另开独立项目,保留原项目,不以重建项目规避暂停、责任边界或确认。
40
+
41
+ 1.0.0 与 Python 试用项目仍可读取;只 open 不改写状态。首次新提交以历史真实回答初始化缺口记录,原有关闭状态和确认继续保留。仍在等待回答的旧问题不需要重新问,按当前 host-guide 提交 assessment。
42
+
43
+ 完整操作字段见 [宿主调用协议](host-guide.md)。
@@ -0,0 +1,74 @@
1
+ # service
2
+
3
+ ## 7. 04 服务:两种模式,两种对话节奏
4
+
5
+ 进入服务设计时先简短解释平台固定模式,并承接疑问。此时答疑不计作服务访谈回答,不提前采访价格或承诺。创作者理解后选择智能规划或逐项讨论;已有选择在续作时保留。
6
+
7
+ ### 三个阶段的含义与生成底线
8
+
9
+ - **获客期:先免费体验。** 让新用户实际感受搭子的帮助。体验多久、包含什么以及何时结束,由创作者定义;可包含 7 天每天跟进、答疑、记住进度和修改方案。不限定一次结果,不把持续跟进判为违规。具体工具、主动触达与真人服务仍按创作者约定和真实能力说明,成本建议不是平台禁令。
10
+ - **付费期:订阅后持续使用。** 已购服务期间与 AI 搭子的对话不限次数,不设每日/每月消息配额,不用“合理使用”或结果、工具额度变相限制普通对话。真人咨询可单独约定次数、时长与响应安排;不把真人额度写成搭子额度。
11
+ - **维持期:暂不订阅,也能基础聊天。** 免费体验结束未订阅,或已购服务到期未续费时,保留历史和最基础的双向对话,如日常交流、普通问题答疑和已有结果解释。不能只留静态历史、关闭聊天或每次只提示付费。不自动包含付费交付和真人服务,已购期未结束仍继续履约。
12
+
13
+ 这三条同时适用于智能和逐项模式。蓝图的 conversationLimit 固定为 unlimited,humanConversationLimit 单独说明真人服务,maintenanceContract.basicConversation 为 true,allowedQuestions 写明可回答的基础问题。免费范围校验使用 freeScopeAgreed,excludedSteps 可为空,不强制制造排除项。规则更新不改写历史原话或认可;旧草案中的错误限制应按已有意见修订并重新展示,不能继续用旧平台规则拒绝用户。
14
+
15
+ ### 7.0 订阅时长由创作者定义
16
+
17
+ 采用单档订阅制,不限定按月。创作者可选择 14 天、8 周、3 个月、1 年等明确时长;用正整数与天/周/月/年记录,不把一个月折算成固定 30 天。没有时长信息时保留待定并只问这一项,不能从“每周一次交付”推断成按周订阅。智能模式可以提出有依据的周期建议,须标为建议、展示并待确认。
18
+
19
+ 订阅周期、交付频率、价格和额度分别记录。例如“订阅 8 周,整个周期 199 元,每周交付一次,共 8 次”;还要明确工具、真人对话及本人复核的额度按哪段时间计算;付费期 AI 对话不限次数。已有信息不重复问。到期、续费、停付边界及恢复订阅都按已购的实际周期描述,不能固定说月底或下个月;到期前继续兑现已购服务。
20
+
21
+ 仅仅把周期改长不代表可以等比例涨价、加交付或扩大额度。修改周期时核对受影响约定,依据实际表达更新蓝图、动线与手册,再展示对应版本确认。旧版默认“monthly”不等于创作者已选按月;有明确原话或对应版本确认的月度方案继续保留,历史产物不因规则更新被静默改写。
22
+
23
+ ### 7.1 智能规划:先给完整建议,再围绕意见修订
24
+
25
+ 基于 01 定义、03 方法、02 知识的优先级,生成可讨论的完整服务草案,已有确认内容原样保留。价格、交付、工具及真人额度可提出具体建议并标明建议性质,AI 对话固定不限次数;不得虚构创作者产能、外部能力或用户效果。
26
+
27
+ 第一次展示后,可以说:“这版先让用户按约定免费体验,再通过订阅持续使用;付费期和搭子聊天不限次数,暂不订阅也能基础聊天。你最想先看哪一部分?”开场是在选择讨论入口,不要求逐个重填已有字段。
28
+
29
+ | 用户输入 | 采访动作 | 收敛方式 |
30
+ | --- | --- | --- |
31
+ | “为什么建议每周一次” | 只解释当前方案和依据 | 回答后停下,保持蓝图,不追加新采访问题 |
32
+ | “免费部分不太对” | 只问一个能确定修改方向的问题 | 得到具体替代后修改,不重问全套免费内容 |
33
+ | “免费只帮他改一篇周报” | 明确修订相关范围,检查上下游一致性 | 成功保存后说明修改,标出需要重新确认的内容 |
34
+ | “这个免费设计可以” | 在目标摘要与原话中记录对免费部分的局部认可 | 不改变蓝图的正式确认状态,不当作整套蓝图确认 |
35
+ | “整套蓝图按现在这版确认” | 校验完整性、版本和确认范围 | 通过后正式确认蓝图,生成服务手册 |
36
+ | “免费体验也要每天跟进、答疑和修改方案” | 采用明确要求,已有时长则直接整理;只有不明确时才问体验结束条件 | 更新免费内容、结束条件及相关动线,不按一次结果规则拒绝 |
37
+
38
+ 智能模式只有遇到具体未决问题才追问,不能因为内部有字段就自动逐字段采访。创作者没有主动选修改点时,也不要求其逐项批准所有建议;清楚展示整套草案后,可以明确确认整套,但真实产能或能力依据的缺口仍不能由模型补造。
39
+
40
+ 智能模式中的普通局部认可用于校准讨论,不等同于逐项模式的阶段确认。正式确认使用 [宿主协议](host-guide.md) 的 turn_finish.patch.confirmations,绑定先前实际展示的对象、版本和本轮原话,并满足对应门槛;口头回复“已认可”本身不能更新正式确认状态。
41
+
42
+ ### 7.2 逐项讨论:按服务决定推进
43
+
44
+ 下表是对话主题顺序;正式九类业务门槛仍沿用主方案。每一项先读已有原话和上游材料,已明确的内容直接整理展示,只问缺口。
45
+
46
+ | 顺序 | 需要做出的决定 | 最低完整条件与何时收敛 | 典型追问 |
47
+ | --- | --- | --- | --- |
48
+ | S01 第一次免费体验 | 免费体验多久、得到什么帮助 | 来的情境、必要输入、处理步骤、体验结束条件、服务范围、之后值得继续的事明确;展示后确认 | 尚无范围时:“她刚来的这段免费体验,你愿意先帮她做到什么程度?” |
49
+ | S02 订阅服务 | 用户为什么会反复回来 | 来的原因、输入、判断、行动、结果、反馈、下一轮更新相互连贯;展示后确认 | “体验完以后,她还想继续找你,你觉得接下来最能帮上她的是什么?”;已有回答只补实际缺口 |
50
+ | S03 服务方式 | 主要是随时帮助、周期结果还是约定交付或工具次数;AI 对话始终不限次数 | 创作者明确选择;均是单档订阅,时长由创作者自定义,不产生新计费档位 | 只在用户不理解或尚未选择时解释和询问 |
51
+ | S04 节奏、交付与价格 | 一次订阅多长、该周期内得到什么、多少、多少钱 | 订阅时长、对应正数价格、交付频率、数量、额度与工具范围明确,完成确认 | 只问当前缺的一项,如“一次订阅,你想定多长时间?”;周期已定则问“这四周一共收多少钱,你有想过吗?”,沿用实际周期 |
52
+ | S05 停止付费后 | 历史之外还允许问什么 | 保证基础对话,明确可回答的普通问题;不能只留历史或付费提示 | “她要是哪次不续了,再回来问问题,你还愿意让搭子帮她聊哪些?”;基础对话始终保留 |
53
+ | S06 续费依据 | 用户看见什么值得继续 | 1—3 项具体变化、成果或下周期目标 | “这一段快结束时,她能看到什么,会觉得接着用还有帮助?” |
54
+ | S07 呈现与本人复核 | 纯对话或组件,是否创作者本人参与 | 有组件则描述展示、使用与更新;有复核则给真实产能 | 按缺口分别问;如已选择复核,只问约定时间范围内可复核多少关键结果 |
55
+ | S08 四种用户经历 | 体验后订阅、不订阅、续费、不再续约 | 每种均覆盖何时发生、继续或停止什么、怎样告诉用户,并逐种确认 | 使用下一节的具体情境提问 |
56
+ | S09 整体确认 | 三阶段、五动线、服务约定一致 | 全部门槛满足,创作者确认当前蓝图,整理九章手册 | 只回到实际未决内容,不重跑已确认主题 |
57
+
58
+ S01 信息够用时直接出候选,不提前问价格或续费;S02 保留上轮未被推翻的有据内容,不从头重新问。各项存在多个缺口时,一次只问一个,不能将整行内容抛给用户填写。用户主动说清周期、价格、本人不咨询也不复核时,直接记录;到 S07 若只缺呈现方式,就问“你设想她平时怎么用——直接在对话里说,还是还需要别的东西配合?”,不再问是否真人参与。仅说“不做咨询”而复核尚不明确时,不能把两者混为一谈。
59
+
60
+ 缺少真实产能时,不能把“创作者复核”写成已承诺服务。创作者可以明确取消本人复核或稍后补充;运行时保留相应未决状态,不能替他作决定。
61
+
62
+ ### 7.3 四条动线的情境问法
63
+
64
+ 四组情境,每组覆盖三个决定——何时发生、接下来继续或停止什么、怎样自然地告诉用户。“恢复订阅”是平台默认能力,不生成第五组问题。三个决定是覆盖要求,不是必须重复问满三轮。
65
+
66
+ 先带入已确定的用户和服务,再问当前尚缺的一个决定。日常说法、短句即可;不规定开头、段落、问号数量或字数,也不先抽象问一遍再用例子重问一遍。需要语境时主动补上,用户已经理解时直接接着讨论。工具的 transition 模式只标识服务路径采访,仍绑定一个 T. 目标。
67
+
68
+ 以已确认“每周复盘写作、按月订阅”的搭子为例,第一轮可以问:
69
+
70
+ > 假设小林回来做这次订阅的最后一次复盘,你会怎么跟她聊要不要再订一段?
71
+
72
+ 服务周期、频次和能力取自当前搭子的已确认内容,临时情境明确是假设,不能把本示例中的按月订阅强加给其他创作者。后续仅在尚未明确时,分别讨论继续或停止的服务范围、向用户传达的内容。对话围绕这次用户经历连续展开,不突然跳去问一个没有语境的配置项。
73
+
74
+ 已有回答清楚覆盖同一条动线后续决定时,直接整理这些内容供确认,不换种说法让用户再答一次;四组、每组三个决定的覆盖要求保持不变。只有创作者明确确认对应路径后,该路径才完成。
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env python3
2
+ """Run Buddy Creator Skill using only the Python standard library."""
3
+ import argparse
4
+ import json
5
+ import os
6
+ import re
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ if sys.version_info < (3, 9):
11
+ print(json.dumps({'ok': False, 'error': {'code': 'PYTHON_VERSION', 'message': '需要 Python 3.9 或更高版本;无需 Node、npm 或 pip。'}}, ensure_ascii=False))
12
+ sys.exit(1)
13
+ if hasattr(sys.stdout, 'reconfigure'):
14
+ sys.stdout.reconfigure(encoding='utf-8')
15
+ if hasattr(sys.stderr, 'reconfigure'):
16
+ sys.stderr.reconfigure(encoding='utf-8')
17
+
18
+ import buddy_core as core
19
+
20
+
21
+ def parse_input(path):
22
+ if path == '-' and sys.stdin.isatty():
23
+ raise core.BuddyError('INPUT_TTY', '请求 JSON 不接受交互式 TTY 输入;请用 --input <UTF-8 JSON文件>,或通过非 TTY 管道提交 --input -。无需发送 Ctrl-D。')
24
+ try:
25
+ raw = sys.stdin.read() if path == '-' else Path(path).expanduser().read_text(encoding='utf-8')
26
+ return json.loads(raw)
27
+ except json.JSONDecodeError as error:
28
+ raise core.BuddyError('INPUT_JSON', '请求 JSON 格式无效。', {'line': error.lineno, 'column': error.colno}) from error
29
+ except (OSError, ValueError) as error:
30
+ raise core.BuddyError('INPUT_JSON', '无法读取请求 JSON;--input 使用 UTF-8 文件路径或 - 表示标准输入。') from error
31
+
32
+
33
+ def main():
34
+ parser = argparse.ArgumentParser(description='Buddy Creator Skill:宿主采访、Python 本地保存与只读预览。')
35
+ parser.add_argument('--version', action='version', version=core.METADATA['displayName'] + ' ' + core.METADATA['version'])
36
+ sub = parser.add_subparsers(dest='command', required=True)
37
+ opened = sub.add_parser('open', help='创建或恢复 Buddy 项目')
38
+ opened.add_argument('--creation-key')
39
+ opened.add_argument('--workspace')
40
+ opened.add_argument('--context', help='可选的初始背景 JSON 文件;不是采访回答或确认')
41
+ opened.add_argument('--no-browser', action='store_true', help='由宿主打开预览,不调用系统浏览器(默认行为)')
42
+ call = sub.add_parser('call', help='提交协议请求')
43
+ call.add_argument('--workspace', required=True)
44
+ call.add_argument('--input', default='-')
45
+ panel = sub.add_parser('preview-panel', help='记录宿主观察到的面板状态与一次性重开提醒;不改变采访进度')
46
+ panel.add_argument('--workspace', required=True)
47
+ panel.add_argument('--input', default='-', help='包含 action 的 JSON 文件或非 TTY 标准输入')
48
+ for name in ['serve', 'status']:
49
+ command = sub.add_parser(name)
50
+ command.add_argument('--workspace', required=True)
51
+ args = parser.parse_args()
52
+ if args.command == 'serve':
53
+ import preview
54
+ preview.serve(Path(args.workspace).expanduser().resolve())
55
+ return
56
+ if args.command == 'open':
57
+ initial_context = parse_input(args.context) if args.context else None
58
+ if args.context and initial_context is None:
59
+ raise core.BuddyError('INITIAL_CONTEXT_SCHEMA', '初始背景必须为 JSON 对象,不能为 null。')
60
+ workspace, state = core.open_workspace(args.creation_key, args.workspace, initial_context)
61
+ import preview
62
+ try:
63
+ endpoint = preview.start(workspace)
64
+ except Exception as error:
65
+ endpoint = {'status': 'unavailable', 'message': str(error), 'recovery': '项目已经保存。允许本地端口后重试同一 open;不要更换 creation-key。'}
66
+ directive = 'continue_turn' if state.get('pendingTurnId') else ('prepare_opening' if core.needs_opening(state) else 'present_delivery')
67
+ result = {'workspace': str(workspace), 'buddyId': state['buddyId'], 'preview': endpoint, 'delivery': None if directive == 'prepare_opening' else state['deliveries'].get(state.get('currentDeliveryId')), 'directive': directive, 'context': core.context(workspace, state), 'entrypointCommand': {'executable': sys.executable, 'args': [str(Path(__file__).resolve())]}}
68
+ elif args.command == 'status':
69
+ workspace = Path(args.workspace).expanduser().resolve()
70
+ with core.workspace_lock(workspace):
71
+ state = core.load(workspace)
72
+ import preview
73
+ result = {'workspace': str(workspace), 'context': core.context(workspace, state), 'snapshot': preview.snapshot(workspace, state)}
74
+ elif args.command == 'preview-panel':
75
+ import preview_panel
76
+ result = preview_panel.call(args.workspace, parse_input(args.input))
77
+ else:
78
+ result = core.call(args.workspace, parse_input(args.input))
79
+ if args.command in ('open', 'status'):
80
+ import completion
81
+ completion.return_to_caller(workspace, state, result)
82
+ print(json.dumps(dict(ok=True, product=core.METADATA['displayName'], version=core.METADATA['version'], **result), ensure_ascii=False, indent=2, allow_nan=False), flush=True)
83
+
84
+
85
+ def cli():
86
+ try:
87
+ try:
88
+ main()
89
+ return 0
90
+ except core.BuddyError as error:
91
+ print(json.dumps({'ok': False, 'error': {'code': error.code, 'message': str(error), 'details': error.details}}, ensure_ascii=False, indent=2), flush=True)
92
+ except BrokenPipeError:
93
+ raise
94
+ except Exception as error:
95
+ match = re.match(r'^([A-Z][A-Z0-9_]+):\s*(.*)', str(error), re.DOTALL)
96
+ code, message = match.groups() if match else ('UNEXPECTED_ERROR', str(error))
97
+ print(json.dumps({'ok': False, 'error': {'code': code, 'message': message, 'recovery': '已提交的原话和历史版本仍保留。读取 status 或 turn_continue 恢复,勿创建重复项目。'}}, ensure_ascii=False, indent=2), flush=True)
98
+ return 1
99
+ except BrokenPipeError:
100
+ # Prevent a second failure when Python flushes stdout during shutdown.
101
+ with open(os.devnull, 'w') as sink:
102
+ os.dup2(sink.fileno(), sys.stdout.fileno())
103
+ print(json.dumps({'error': {'code': 'OUTPUT_CLOSED', 'message': '回执输出管道已关闭,操作可能已经保存。请先读取 status 或 turn_continue 核对结果;不要创建新回合重复提交。'}}, ensure_ascii=False), file=sys.stderr, flush=True)
104
+ return 1
105
+
106
+
107
+ if __name__ == '__main__':
108
+ sys.exit(cli())
@@ -0,0 +1,67 @@
1
+ """A workspace-bound, stdlib-only client for host-authored Creator requests."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import subprocess
6
+ import sys
7
+
8
+ from buddy_core import BuddyError
9
+
10
+
11
+ class BuddyClient:
12
+ def __init__(self, workspace, python=None, timeout=60):
13
+ self.workspace = str(Path(workspace).expanduser().resolve())
14
+ self.command = [python or sys.executable, '-B', str(Path(__file__).with_name('buddy.py'))]
15
+ self.timeout = timeout
16
+ self.context = None
17
+
18
+ def _run(self, command, payload=None):
19
+ args = self.command + [command, '--workspace', self.workspace]
20
+ raw = None
21
+ if payload is not None:
22
+ args += ['--input', '-']
23
+ raw = json.dumps(payload, ensure_ascii=False, allow_nan=False)
24
+ try:
25
+ process = subprocess.run(args, input=raw, stdin=subprocess.DEVNULL if raw is None else None,
26
+ capture_output=True, encoding='utf-8', timeout=self.timeout,
27
+ env={**os.environ, 'PYTHONDONTWRITEBYTECODE': '1'})
28
+ except (OSError, subprocess.TimeoutExpired) as error:
29
+ raise BuddyError('CLIENT_TRANSPORT', '未取得 Creator 回执;先用 status 或 turn_continue 核对结果,不自动重试写入。') from error
30
+ try:
31
+ result = json.loads(process.stdout)
32
+ except ValueError as error:
33
+ raise BuddyError('CLIENT_RESPONSE', 'Creator 回执不是完整 JSON;先核对已保存状态,不创建新回合重试。',
34
+ {'exitCode': process.returncode}) from error
35
+ if isinstance(result, dict) and result.get('ok') is False and isinstance(result.get('error'), dict):
36
+ error = result['error']
37
+ raise BuddyError(error['code'], error['message'], error.get('details'))
38
+ if process.returncode or not isinstance(result, dict) or result.get('ok') is not True:
39
+ raise BuddyError('CLIENT_RESPONSE', 'Creator 未返回成功回执;先核对已保存状态。', {'exitCode': process.returncode})
40
+ if 'context' in result:
41
+ self.context = result['context']
42
+ return result
43
+
44
+ def status(self):
45
+ return self._run('status')
46
+
47
+ def call(self, payload):
48
+ # No automatic retries: the host retains the exact clientKey/turnId and payload.
49
+ return self._run('call', payload)
50
+
51
+ def _object(self, collection, object_id):
52
+ if self.context is None:
53
+ raise BuddyError('CLIENT_CONTEXT', '先调用 status 或保存本轮输入,再从返回的版本生成引用。')
54
+ item = self.context['state'][collection].get(object_id)
55
+ if item is None:
56
+ raise BuddyError('CLIENT_REFERENCE', '当前回执中没有此对象,请核对 ID。', {'id': object_id})
57
+ return item
58
+
59
+ def artifact_ref(self, object_id):
60
+ item = self._object('artifacts', object_id)
61
+ return {'type': 'artifact', 'id': object_id, 'hash': item['hash']}
62
+
63
+ def input_ref(self, input_id, quote):
64
+ item = self._object('inputs', input_id)
65
+ if not isinstance(quote, str) or not quote.strip() or quote not in item['raw']:
66
+ raise BuddyError('EVIDENCE_QUOTE', '引用必须是该输入中的非空精确原话。', {'id': input_id})
67
+ return {'type': 'input', 'id': input_id, 'hash': item['hash'], 'quote': quote}