@chinhae/atw-cli 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/README.md +128 -451
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +24 -1
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/commands/channel/adapters/claude.d.ts +44 -2
  6. package/dist/commands/channel/adapters/claude.d.ts.map +1 -1
  7. package/dist/commands/channel/adapters/claude.js +124 -23
  8. package/dist/commands/channel/adapters/claude.js.map +1 -1
  9. package/dist/commands/channel/adapters/codex.d.ts +36 -6
  10. package/dist/commands/channel/adapters/codex.d.ts.map +1 -1
  11. package/dist/commands/channel/adapters/codex.js +150 -10
  12. package/dist/commands/channel/adapters/codex.js.map +1 -1
  13. package/dist/commands/channel/adapters/index.d.ts +33 -4
  14. package/dist/commands/channel/adapters/index.d.ts.map +1 -1
  15. package/dist/commands/channel/adapters/index.js +159 -6
  16. package/dist/commands/channel/adapters/index.js.map +1 -1
  17. package/dist/commands/channel/adapters/omp.d.ts +106 -0
  18. package/dist/commands/channel/adapters/omp.d.ts.map +1 -0
  19. package/dist/commands/channel/adapters/omp.js +405 -0
  20. package/dist/commands/channel/adapters/omp.js.map +1 -0
  21. package/dist/commands/channel/adapters/pi.d.ts +123 -0
  22. package/dist/commands/channel/adapters/pi.d.ts.map +1 -0
  23. package/dist/commands/channel/adapters/pi.js +387 -0
  24. package/dist/commands/channel/adapters/pi.js.map +1 -0
  25. package/dist/commands/channel/adapters/types.d.ts +16 -0
  26. package/dist/commands/channel/adapters/types.d.ts.map +1 -1
  27. package/dist/commands/channel/agent-loader.d.ts +1 -0
  28. package/dist/commands/channel/agent-loader.d.ts.map +1 -1
  29. package/dist/commands/channel/agent-loader.js +45 -5
  30. package/dist/commands/channel/agent-loader.js.map +1 -1
  31. package/dist/commands/channel/dev-parse-trace.d.ts +1 -1
  32. package/dist/commands/channel/dev-parse-trace.d.ts.map +1 -1
  33. package/dist/commands/channel/dev-parse-trace.js +25 -2
  34. package/dist/commands/channel/dev-parse-trace.js.map +1 -1
  35. package/dist/commands/channel/index.d.ts.map +1 -1
  36. package/dist/commands/channel/index.js +44 -12
  37. package/dist/commands/channel/index.js.map +1 -1
  38. package/dist/commands/channel/run.d.ts +39 -2
  39. package/dist/commands/channel/run.d.ts.map +1 -1
  40. package/dist/commands/channel/run.js +227 -68
  41. package/dist/commands/channel/run.js.map +1 -1
  42. package/dist/commands/channel/spawn.d.ts +7 -2
  43. package/dist/commands/channel/spawn.d.ts.map +1 -1
  44. package/dist/commands/channel/spawn.js +35 -1
  45. package/dist/commands/channel/spawn.js.map +1 -1
  46. package/dist/commands/channel/store/events.d.ts +2 -2
  47. package/dist/commands/channel/store/events.d.ts.map +1 -1
  48. package/dist/commands/channel/store/events.js +1 -1
  49. package/dist/commands/channel/store/events.js.map +1 -1
  50. package/dist/commands/channel/supervisor/shutdown.d.ts +2 -0
  51. package/dist/commands/channel/supervisor/shutdown.d.ts.map +1 -1
  52. package/dist/commands/channel/supervisor/shutdown.js +6 -1
  53. package/dist/commands/channel/supervisor/shutdown.js.map +1 -1
  54. package/dist/commands/channel/supervisor.d.ts +2 -2
  55. package/dist/commands/channel/supervisor.d.ts.map +1 -1
  56. package/dist/commands/channel/supervisor.js +26 -3
  57. package/dist/commands/channel/supervisor.js.map +1 -1
  58. package/dist/commands/init.d.ts.map +1 -1
  59. package/dist/commands/init.js +6 -1
  60. package/dist/commands/init.js.map +1 -1
  61. package/dist/commands/mem.d.ts +2 -1
  62. package/dist/commands/mem.d.ts.map +1 -1
  63. package/dist/commands/mem.js +162 -13
  64. package/dist/commands/mem.js.map +1 -1
  65. package/dist/commands/mode.d.ts.map +1 -1
  66. package/dist/commands/mode.js +5 -3
  67. package/dist/commands/mode.js.map +1 -1
  68. package/dist/commands/uninstall.d.ts +5 -3
  69. package/dist/commands/uninstall.d.ts.map +1 -1
  70. package/dist/commands/uninstall.js +40 -16
  71. package/dist/commands/uninstall.js.map +1 -1
  72. package/dist/commands/update.d.ts +34 -0
  73. package/dist/commands/update.d.ts.map +1 -1
  74. package/dist/commands/update.js +209 -11
  75. package/dist/commands/update.js.map +1 -1
  76. package/dist/commands/verify.d.ts +18 -0
  77. package/dist/commands/verify.d.ts.map +1 -0
  78. package/dist/commands/verify.js +75 -0
  79. package/dist/commands/verify.js.map +1 -0
  80. package/dist/configurators/index.d.ts.map +1 -1
  81. package/dist/configurators/index.js +3 -2
  82. package/dist/configurators/index.js.map +1 -1
  83. package/dist/configurators/omp.d.ts +22 -0
  84. package/dist/configurators/omp.d.ts.map +1 -1
  85. package/dist/configurators/omp.js +101 -2
  86. package/dist/configurators/omp.js.map +1 -1
  87. package/dist/configurators/shared.d.ts.map +1 -1
  88. package/dist/configurators/shared.js +39 -7
  89. package/dist/configurators/shared.js.map +1 -1
  90. package/dist/configurators/workflow.d.ts.map +1 -1
  91. package/dist/configurators/workflow.js +5 -1
  92. package/dist/configurators/workflow.js.map +1 -1
  93. package/dist/constants/paths.d.ts +4 -0
  94. package/dist/constants/paths.d.ts.map +1 -1
  95. package/dist/constants/paths.js +4 -0
  96. package/dist/constants/paths.js.map +1 -1
  97. package/dist/migrations/manifests/0.7.0.json +1378 -0
  98. package/dist/templates/atw/definition-of-done.md +65 -0
  99. package/dist/templates/atw/gitignore.txt +7 -0
  100. package/dist/templates/atw/index.d.ts +11 -0
  101. package/dist/templates/atw/index.d.ts.map +1 -1
  102. package/dist/templates/atw/index.js +22 -0
  103. package/dist/templates/atw/index.js.map +1 -1
  104. package/dist/templates/atw/scripts/common/active_task.py +2 -2
  105. package/dist/templates/atw/scripts/common/workflow_phase.py +33 -0
  106. package/dist/templates/atw/scripts/cross_model_review.py +3312 -0
  107. package/dist/templates/atw/scripts/decision_log.py +2002 -0
  108. package/dist/templates/atw/scripts/floor_guard.py +642 -0
  109. package/dist/templates/atw/scripts/task.py +25 -6
  110. package/dist/templates/atw/scripts/tickets.py +480 -10
  111. package/dist/templates/atw/workflow-general.md +6 -1
  112. package/dist/templates/atw/workflow.md +346 -26
  113. package/dist/templates/claude/settings.json +12 -0
  114. package/dist/templates/codex/hooks/session-start.py +2 -2
  115. package/dist/templates/common/bundled-skills/atw-askme/SKILL.md +1 -1
  116. package/dist/templates/common/bundled-skills/atw-askme-with-docs/SKILL.md +1 -1
  117. package/dist/templates/common/bundled-skills/atw-channel/SKILL.md +5 -2
  118. package/dist/templates/common/bundled-skills/atw-channel/references/command-reference.md +44 -5
  119. package/dist/templates/common/bundled-skills/atw-channel/references/progress-debugging.md +9 -0
  120. package/dist/templates/common/bundled-skills/atw-channel/references/workers.md +48 -4
  121. package/dist/templates/common/bundled-skills/atw-code-review/SKILL.md +78 -3
  122. package/dist/templates/common/bundled-skills/atw-codebase-design/SKILL.md +2 -2
  123. package/dist/templates/common/bundled-skills/atw-codebase-design/{DESIGN-IT-TWICE.md → design-it-twice.md} +3 -3
  124. package/dist/templates/common/bundled-skills/atw-cross-model-review/SKILL.md +361 -0
  125. package/dist/templates/common/bundled-skills/atw-cross-model-review/agents/openai.yaml +5 -0
  126. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/four-tiers.md +42 -0
  127. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/log-review-questions.md +38 -0
  128. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/questions.md +54 -0
  129. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/recheck-questions.md +42 -0
  130. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/report-format.md +22 -0
  131. package/dist/templates/common/bundled-skills/atw-diagnosing-bugs/SKILL.md +13 -2
  132. package/dist/templates/common/bundled-skills/atw-domain-modeling/SKILL.md +2 -2
  133. package/dist/templates/common/bundled-skills/atw-handoff/SKILL.md +1 -1
  134. package/dist/templates/common/bundled-skills/atw-implement/SKILL.md +37 -1
  135. package/dist/templates/common/bundled-skills/atw-implement-all/SKILL.md +219 -24
  136. package/dist/templates/common/bundled-skills/atw-implement-spec/SKILL.md +237 -18
  137. package/dist/templates/common/bundled-skills/atw-improve-codebase-architecture/SKILL.md +2 -2
  138. package/dist/templates/common/bundled-skills/atw-init-repo/SKILL.md +1 -1
  139. package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw-local.md +7 -1
  140. package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw.md +7 -1
  141. package/dist/templates/common/bundled-skills/atw-map/SKILL.md +1 -1
  142. package/dist/templates/common/bundled-skills/atw-meta/SKILL.md +1 -1
  143. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/add-project-local-conventions.md +4 -3
  144. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/change-skills-or-commands.md +21 -16
  145. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/change-task-lifecycle.md +1 -1
  146. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/overview.md +1 -0
  147. package/dist/templates/common/bundled-skills/atw-meta/references/local-architecture/task-system.md +2 -1
  148. package/dist/templates/common/bundled-skills/atw-meta/references/platform-files/hooks-and-settings.md +1 -0
  149. package/dist/templates/common/bundled-skills/atw-noob-mode/SKILL.md +1 -1
  150. package/dist/templates/common/bundled-skills/atw-performance/SKILL.md +134 -0
  151. package/dist/templates/common/bundled-skills/atw-performance/agents/openai.yaml +3 -0
  152. package/dist/templates/common/bundled-skills/atw-performance/references/optimization-patterns.md +250 -0
  153. package/dist/templates/common/bundled-skills/atw-performance/references/performance-checklist.md +238 -0
  154. package/dist/templates/common/bundled-skills/atw-performance/references/performance-optimization.md +264 -0
  155. package/dist/templates/common/bundled-skills/atw-prototype/SKILL.md +3 -3
  156. package/dist/templates/common/bundled-skills/atw-prototype/{LOGIC.md → logic.md} +1 -1
  157. package/dist/templates/common/bundled-skills/atw-prototype/references/design-direction.md +1 -1
  158. package/dist/templates/common/bundled-skills/atw-prototype/references/style-explorer.md +1 -1
  159. package/dist/templates/common/bundled-skills/atw-prototype/{UI.md → ui.md} +4 -4
  160. package/dist/templates/common/bundled-skills/atw-reflect/SKILL.md +77 -0
  161. package/dist/templates/common/bundled-skills/atw-reflect/agents/openai.yaml +5 -0
  162. package/dist/templates/common/bundled-skills/atw-reflect/references/divergent-reviewer.md +38 -0
  163. package/dist/templates/common/bundled-skills/atw-reflect/references/judgment-reviewer.md +37 -0
  164. package/dist/templates/common/bundled-skills/atw-reflect/references/synthesizer.md +63 -0
  165. package/dist/templates/common/bundled-skills/atw-reflect/references/tooling-reviewer.md +48 -0
  166. package/dist/templates/common/bundled-skills/atw-security/SKILL.md +248 -0
  167. package/dist/templates/common/bundled-skills/atw-security/agents/openai.yaml +3 -0
  168. package/dist/templates/common/bundled-skills/atw-security/references/hardening-patterns.md +324 -0
  169. package/dist/templates/common/bundled-skills/atw-security/references/security-checklist.md +248 -0
  170. package/dist/templates/common/bundled-skills/atw-session-insight/SKILL.md +1 -1
  171. package/dist/templates/common/bundled-skills/atw-session-insight/references/cli-quick-reference.md +11 -2
  172. package/dist/templates/common/bundled-skills/atw-spec/SKILL.md +1 -1
  173. package/dist/templates/common/bundled-skills/atw-tdd/SKILL.md +25 -0
  174. package/dist/templates/common/bundled-skills/atw-tickets/SKILL.md +1 -1
  175. package/dist/templates/common/bundled-skills/atw-triage/SKILL.md +5 -5
  176. package/dist/templates/common/bundled-skills/atw-ui/SKILL.md +54 -1
  177. package/dist/templates/common/bundled-skills/atw-ui/references/accessibility-checklist.md +160 -0
  178. package/dist/templates/common/bundled-skills/atw-verify-skill/SKILL.md +58 -0
  179. package/dist/templates/common/bundled-skills/atw-verify-skill/agents/openai.yaml +5 -0
  180. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/README.md +47 -0
  181. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/create-note.md +39 -0
  182. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/search.md +45 -0
  183. package/dist/templates/common/bundled-skills/atw-verify-skill/references/generate.md +107 -0
  184. package/dist/templates/common/bundled-skills/atw-verify-skill/references/health-check.md +100 -0
  185. package/dist/templates/common/bundled-skills/atw-verify-skill/references/verify-skill-template.md +99 -0
  186. package/dist/templates/common/bundled-skills/atw-what-happened/SKILL.md +49 -0
  187. package/dist/templates/common/bundled-skills/atw-what-happened/agents/openai.yaml +5 -0
  188. package/dist/templates/common/commands/finish-work.md +10 -1
  189. package/dist/templates/common/commands/start.md +3 -1
  190. package/dist/templates/common/skills/update-spec.md +40 -4
  191. package/dist/templates/copilot/hooks/session-start.py +2 -2
  192. package/dist/templates/dsh/DSH.md +2 -2
  193. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +10 -0
  194. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +10 -0
  195. package/dist/templates/omp/config.yml +15 -0
  196. package/dist/templates/omp/extensions/atw/index.ts.txt +135 -18
  197. package/dist/templates/omp/index.d.ts +1 -0
  198. package/dist/templates/omp/index.d.ts.map +1 -1
  199. package/dist/templates/omp/index.js +3 -0
  200. package/dist/templates/omp/index.js.map +1 -1
  201. package/dist/templates/opencode/lib/session-utils.js +4 -2
  202. package/dist/templates/opencode/plugins/inject-workflow-state.js +5 -1
  203. package/dist/templates/pi/extensions/atw/index.ts.txt +80 -7
  204. package/dist/templates/shared-hooks/index.d.ts +8 -1
  205. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  206. package/dist/templates/shared-hooks/index.js +8 -0
  207. package/dist/templates/shared-hooks/index.js.map +1 -1
  208. package/dist/templates/shared-hooks/inject-workflow-state.py +131 -14
  209. package/dist/templates/shared-hooks/session-start.py +4 -2
  210. package/dist/templates/shared-hooks/show-notice.py +135 -0
  211. package/dist/templates/snow/SNOW.md +1 -1
  212. package/dist/utils/initialized.d.ts +9 -0
  213. package/dist/utils/initialized.d.ts.map +1 -0
  214. package/dist/utils/initialized.js +24 -0
  215. package/dist/utils/initialized.js.map +1 -0
  216. package/dist/utils/managed-removal.d.ts +2 -0
  217. package/dist/utils/managed-removal.d.ts.map +1 -1
  218. package/dist/utils/managed-removal.js +23 -3
  219. package/dist/utils/managed-removal.js.map +1 -1
  220. package/dist/utils/optional-skills.d.ts +2 -0
  221. package/dist/utils/optional-skills.d.ts.map +1 -1
  222. package/dist/utils/optional-skills.js +1 -1
  223. package/dist/utils/optional-skills.js.map +1 -1
  224. package/dist/utils/template-hash.d.ts.map +1 -1
  225. package/dist/utils/template-hash.js +3 -0
  226. package/dist/utils/template-hash.js.map +1 -1
  227. package/dist/utils/uninstall-scrubbers.d.ts +10 -0
  228. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  229. package/dist/utils/uninstall-scrubbers.js +50 -0
  230. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  231. package/dist/utils/verify-entries.d.ts +51 -0
  232. package/dist/utils/verify-entries.d.ts.map +1 -0
  233. package/dist/utils/verify-entries.js +399 -0
  234. package/dist/utils/verify-entries.js.map +1 -0
  235. package/package.json +2 -2
  236. /package/dist/templates/common/bundled-skills/atw-codebase-design/{DEEPENING.md → deepening.md} +0 -0
  237. /package/dist/templates/common/bundled-skills/atw-domain-modeling/{ADR-FORMAT.md → adr-format.md} +0 -0
  238. /package/dist/templates/common/bundled-skills/atw-domain-modeling/{GLOSSARY-FORMAT.md → glossary-format.md} +0 -0
  239. /package/dist/templates/common/bundled-skills/atw-improve-codebase-architecture/{HTML-REPORT.md → html-report.md} +0 -0
  240. /package/dist/templates/common/bundled-skills/atw-triage/{AGENT-BRIEF.md → agent-brief.md} +0 -0
  241. /package/dist/templates/common/bundled-skills/atw-triage/{OUT-OF-SCOPE.md → out-of-scope.md} +0 -0
@@ -40,7 +40,46 @@ function buildContextKey(platformName: string, kind: string, value: string): str
40
40
  return safeValue ? `${platformName}_${safeValue}` : `${platformName}_${hashValue(value)}`;
41
41
  }
42
42
 
43
- function deriveContextKey(ctx?: { sessionManager?: { getSessionId?: () => string | undefined; getSessionFile?: () => string | undefined } }): string | null {
43
+ type SessionCtx = { sessionManager?: { getSessionId?: () => string | undefined; getSessionFile?: () => string | undefined } };
44
+
45
+ // omp 子代理的会话文件放在父会话的附件目录里(<父>.jsonl → <父>/<名字>.jsonl),
46
+ // omp 自己找根会话也按这个规矩往上走(最多 8 层)。同进程的子代理不设
47
+ // PI_BLOCKED_AGENT,只能这样认。另起的分支会话与父会话同级,不算子代理。
48
+ function outermostSessionFile(ctx?: SessionCtx): string | null {
49
+ const sessionFile = ctx?.sessionManager?.getSessionFile?.();
50
+ if (!sessionFile) return null;
51
+ let current = resolve(sessionFile);
52
+ let root: string | null = null;
53
+ for (let depth = 0; depth < 8; depth++) {
54
+ const parentFile = `${dirname(current)}.jsonl`;
55
+ if (!existsSync(parentFile)) break;
56
+ current = parentFile;
57
+ root = parentFile;
58
+ }
59
+ return root;
60
+ }
61
+
62
+ function sessionHeaderId(sessionFile: string): string | null {
63
+ const prefix = readFilePrefix(sessionFile, 16 * 1024);
64
+ if (!prefix) return null;
65
+ for (const line of prefix.data.toString("utf-8").split("\n")) {
66
+ try {
67
+ const entry = JSON.parse(line) as { type?: unknown; id?: unknown };
68
+ if (entry.type === "session" && typeof entry.id === "string" && entry.id) return entry.id;
69
+ } catch {
70
+ // 不是完整的一行 JSON(读到前缀末尾被截断),接着看下一行
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+
76
+ // 子代理自己的编号没绑任务,沿用最外层会话的编号
77
+ function deriveContextKey(ctx?: SessionCtx): string | null {
78
+ const outermostFile = outermostSessionFile(ctx);
79
+ const outermostSessionId = outermostFile ? sessionHeaderId(outermostFile) : null;
80
+ if (outermostSessionId) {
81
+ return buildContextKey("omp", "session", outermostSessionId);
82
+ }
44
83
  const sessionId = ctx?.sessionManager?.getSessionId?.();
45
84
  if (sessionId) {
46
85
  return buildContextKey("omp", "session", sessionId);
@@ -779,6 +818,37 @@ function shouldSkipWorkflowState(
779
818
  // single event cascade (input, before_agent_start, and context fire closely)
780
819
  // ---------------------------------------------------------------------------
781
820
 
821
+ // Mirrors DECISION_LOG_REMINDER / has_decision_log() in the shared per-turn
822
+ // Python hook: same sentence, implement or accept only.
823
+ const DECISION_LOG_REMINDER =
824
+ "This task has a decision log: keep recording it at key points with `python3 ./.atw/scripts/decision_log.py`; in this conversation, the start row (`decision_log.py start`) goes in just before the first row you log — `add` and the other writing commands write it first when it is missing, so a conversation with nothing to log writes none; read the last rows they print, and when another conversation wrote in between they write only the start row and stop: read it, then run the same command again.";
825
+ // Mirrors DECISION_LOG_ARCHIVE_REMINDER in the shared per-turn Python hook:
826
+ // same sentence, accept only (with a log), placed before DECISION_LOG_REMINDER.
827
+ const DECISION_LOG_ARCHIVE_REMINDER =
828
+ "This task has a decision log: once accepted, after the acceptance row and before `task.py archive`, log one row for the archive (`decision_log.py add`); it goes into the archive commit with the task.";
829
+ // Mirrors DECISION_LOG_REPORT_REMINDER in the shared per-turn Python hook:
830
+ // same sentence, implement or accept (with a log), just before DECISION_LOG_REMINDER.
831
+ const DECISION_LOG_REPORT_REMINDER =
832
+ "This task has a decision log: when the user asks how things stand or for a report (汇报一下, 现在什么情况, what's the status, report), rebuild it from the task directory in four blocks — `python3 ./.atw/scripts/decision_log.py pending --report` prints the draft; post it in plain words, keep the plain-words lines it carries with noob mode on, and fill in what it marks. This is the task's report, not `atw-what-happened`: that skill only the user starts, and it tells what changed in this chat since their last message.";
833
+
834
+ // Mirrors DECISION_LOG_NO_TASK_REPORT_REMINDER / any_task_has_decision_log() in
835
+ // the shared per-turn Python hook: same sentence, no current task only.
836
+ const DECISION_LOG_NO_TASK_REPORT_REMINDER =
837
+ "No task is current, but some task under `.atw/tasks/` has a decision log: when the user asks how things stand or for a report (汇报一下, 现在什么情况, what's the status, report), rebuild it from that task's directory in four blocks — list the active tasks with `python3 ./.atw/scripts/task.py list` and see which of their directories hold a `decision-log.md` (if the user named the task or said enough to tell which one, take that one; if not, only one, take it; several, ask the user which), then `python3 ./.atw/scripts/decision_log.py pending --report --task-dir <dir>` prints the draft; post it in plain words, keep the plain-words lines it carries with noob mode on, and fill in what it marks. This is the task's report, not `atw-what-happened`: that skill only the user starts, and it tells what changed in this chat since their last message.";
838
+
839
+ // 只看 .atw/tasks/ 下一层、只认文件,归档里的不算;读不了当作没有,不打断这一轮
840
+ function anyTaskHasDecisionLog(projectRoot: string): boolean {
841
+ const tasksDir = join(projectRoot, ".atw", "tasks");
842
+ try {
843
+ return readdirSync(tasksDir).some((name) => {
844
+ const p = join(tasksDir, name, "decision-log.md");
845
+ return existsSync(p) && statSync(p).isFile();
846
+ });
847
+ } catch {
848
+ return false;
849
+ }
850
+ }
851
+
782
852
  const SESSION_OVERVIEW_TEXT =
783
853
  "ATW workflow system active. Use skills and agents as directed by the workflow state.";
784
854
 
@@ -797,9 +867,10 @@ class TurnContextCache {
797
867
  this.key = null;
798
868
  }
799
869
 
800
- get(projectRoot: string, contextKey: string | null): { workflowMsg: string } {
870
+ // 决定日志只由主对话写:子代理的每轮提示不带日志提醒
871
+ get(projectRoot: string, contextKey: string | null, subAgent: boolean): { workflowMsg: string } {
801
872
  const now = Date.now();
802
- const cacheKey = `${projectRoot}:${contextKey ?? ""}`;
873
+ const cacheKey = `${projectRoot}:${contextKey ?? ""}:${subAgent}`;
803
874
  if (
804
875
  this.key === cacheKey &&
805
876
  now - this.timestamp < TurnContextCache.TTL_MS
@@ -807,7 +878,20 @@ class TurnContextCache {
807
878
  return { workflowMsg: this.workflowMsg };
808
879
  }
809
880
 
810
- const { status } = resolveActiveTaskStatus(projectRoot, contextKey);
881
+ const { status, taskDir } = resolveActiveTaskStatus(projectRoot, contextKey);
882
+ // 只认文件,与共享钩子的 is_file() 一致
883
+ const logPath = taskDir !== null ? join(taskDir, "decision-log.md") : null;
884
+ const decisionLog =
885
+ !subAgent &&
886
+ (status === "implement" || status === "accept") &&
887
+ logPath !== null &&
888
+ existsSync(logPath) &&
889
+ statSync(logPath).isFile();
890
+ const tail = decisionLog
891
+ ? `${status === "accept" ? `\n${DECISION_LOG_ARCHIVE_REMINDER}` : ""}\n${DECISION_LOG_REPORT_REMINDER}\n${DECISION_LOG_REMINDER}`
892
+ : !subAgent && status === "no_task" && anyTaskHasDecisionLog(projectRoot)
893
+ ? `\n${DECISION_LOG_NO_TASK_REPORT_REMINDER}`
894
+ : "";
811
895
 
812
896
  const workflowPath = join(projectRoot, ".atw", "workflow.md");
813
897
  let workflowMd = "";
@@ -818,11 +902,11 @@ class TurnContextCache {
818
902
  const blocks = parseWorkflowStateBlocks(workflowMd);
819
903
  const activeBlock = blocks.find((b) => b.status === status);
820
904
  if (activeBlock) {
821
- workflowBody = `[workflow-state:${activeBlock.status}]\n${activeBlock.content}\n[/workflow-state:${activeBlock.status}]`;
905
+ workflowBody = `[workflow-state:${activeBlock.status}]\n${selectPlatformBlocks(activeBlock.content, "Oh My Pi")}${tail}\n[/workflow-state:${activeBlock.status}]`;
822
906
  }
823
907
  }
824
908
  if (!workflowBody) {
825
- workflowBody = "Refer to workflow.md for current step.";
909
+ workflowBody = `Refer to workflow.md for current step.${tail}`;
826
910
  }
827
911
 
828
912
  // When skip keyword is present, skip workflow state injection this turn
@@ -859,6 +943,31 @@ function parseWorkflowStateBlocks(markdown: string): WorkflowStateBlock[] {
859
943
  return blocks;
860
944
  }
861
945
 
946
+ // Mirrors select_platform_blocks() in .atw/scripts/common/workflow_phase.py:
947
+ // keep only the `[Platform, ...]` blocks naming this platform, or every block
948
+ // when none does; marker lines are always dropped.
949
+ const PLATFORM_MARKER_RE = /^\[(\/?)([A-Za-z][^[\]]*)\]\s*$/;
950
+ function selectPlatformBlocks(body: string, platform: string): string {
951
+ const norm = (s: string) => s.toLowerCase().replace(/[-_ ]/g, "");
952
+ const names = (m: RegExpMatchArray) => (m[2] ?? "").split(",").map((n) => norm(n.trim()));
953
+ const lines = body.split("\n");
954
+ const anyMatch = lines.some((l) => {
955
+ const m = l.match(PLATFORM_MARKER_RE);
956
+ return !!m && m[1] !== "/" && names(m).includes(norm(platform));
957
+ });
958
+ const out: string[] = [];
959
+ let keep = true;
960
+ for (const line of lines) {
961
+ const m = line.match(PLATFORM_MARKER_RE);
962
+ if (m) {
963
+ keep = m[1] === "/" || !anyMatch || names(m).includes(norm(platform));
964
+ continue;
965
+ }
966
+ if (keep) out.push(line);
967
+ }
968
+ return out.join("\n");
969
+ }
970
+
862
971
  // ---------------------------------------------------------------------------
863
972
  // Sub-agent detection
864
973
  // ---------------------------------------------------------------------------
@@ -890,6 +999,11 @@ export default function(pi: ExtensionAPI): void {
890
999
  const turnCache = new TurnContextCache();
891
1000
  const agentType = detectAgentType();
892
1001
  const isSubAgent = agentType !== null;
1002
+ // 同进程子代理(omp 18 的 task 工具)不设 PI_BLOCKED_AGENT,按会话文件认
1003
+ const isSubAgentSession = (ctx?: SessionCtx): boolean => isSubAgent || outermostSessionFile(ctx) !== null;
1004
+ // Same switch as the other platforms' hooks: channel sets ATW_HOOKS=0 on
1005
+ // its helpers, which must not receive prompts written for the main AI.
1006
+ const hooksDisabled = process.env.ATW_HOOKS === "0" || process.env.ATW_DISABLE_HOOKS === "1";
893
1007
  let taskContextCache: TaskContextCache | null = null;
894
1008
 
895
1009
  // Tracks compaction boundaries — context handler skips scanning when no
@@ -914,12 +1028,13 @@ export default function(pi: ExtensionAPI): void {
914
1028
  };
915
1029
 
916
1030
  pi.on("session_start", async (_event, ctx) => {
1031
+ if (hooksDisabled) return;
917
1032
  projectRoot = findProjectRoot(ctx.cwd);
918
1033
  const contextKey = rememberContextKey(ctx);
919
1034
 
920
1035
  if (!projectRoot) return;
921
1036
 
922
- if (isSubAgent) {
1037
+ if (isSubAgentSession(ctx)) {
923
1038
  // Sub-agent: inject precise task context once
924
1039
  const { taskDir } = resolveActiveTaskStatus(projectRoot, contextKey);
925
1040
  if (taskDir) {
@@ -964,6 +1079,7 @@ export default function(pi: ExtensionAPI): void {
964
1079
  });
965
1080
 
966
1081
  pi.on("before_agent_start", async (_event, ctx) => {
1082
+ if (hooksDisabled) return;
967
1083
  if (!projectRoot) {
968
1084
  projectRoot = findProjectRoot(ctx.cwd);
969
1085
  }
@@ -971,7 +1087,7 @@ export default function(pi: ExtensionAPI): void {
971
1087
  const contextKey = rememberContextKey(ctx);
972
1088
 
973
1089
  // Persistent injection: workflow state for this turn
974
- const cached = turnCache.get(projectRoot, contextKey);
1090
+ const cached = turnCache.get(projectRoot, contextKey, isSubAgentSession(ctx));
975
1091
  lastInjectionTs = Date.now();
976
1092
 
977
1093
  // Skip turn: inject nothing (escape hatch)
@@ -990,6 +1106,7 @@ export default function(pi: ExtensionAPI): void {
990
1106
  // and post-compaction agent.continue() paths). Acts as a safety net when
991
1107
  // before_agent_start's persisted message was removed by compaction.
992
1108
  pi.on("context", async (event, ctx) => {
1109
+ if (hooksDisabled) return;
993
1110
  if (!projectRoot) return;
994
1111
  const contextKey = rememberContextKey(ctx);
995
1112
 
@@ -1020,7 +1137,7 @@ export default function(pi: ExtensionAPI): void {
1020
1137
 
1021
1138
  // Resolve the turn state before the fast path: a skip turn must still
1022
1139
  // run breadcrumb cleanup even when nothing else changed.
1023
- const cached = turnCache.get(projectRoot, contextKey);
1140
+ const cached = turnCache.get(projectRoot, contextKey, isSubAgentSession(ctx));
1024
1141
  const skipping = !cached.workflowMsg;
1025
1142
 
1026
1143
  // Fast path: no task change, no compaction, not skipping — all persisted context is current.
@@ -1060,18 +1177,18 @@ export default function(pi: ExtensionAPI): void {
1060
1177
  };
1061
1178
  });
1062
1179
 
1063
- // OMP passes Bash event.input through to the tool execution parameters, so
1064
- // inject the session key through the shell-agnostic env field. An explicit
1065
- // per-call value wins over the derived key.
1180
+ // omp 18 的 bash 工具参数没有 env,往 event.input.env 里塞的编号到不了 shell
1181
+ // (工单 19 核实)。改成在命令前 export:编号只含 [A-Za-z0-9._-],不用加引号;
1182
+ // 命令里自己赋值的写在后面,照旧以它为准。调用自带 env 编号时(老版本 omp
1183
+ // 认 env)不动。按文档返回 input,也改原对象,后面的扩展看到的是同一条命令。
1066
1184
  pi.on("tool_call", (event, ctx) => {
1067
1185
  if (event.toolName !== "bash") return;
1068
1186
  const contextKey = rememberContextKey(ctx);
1069
1187
  if (!contextKey) return;
1070
- const input = event.input as { env?: Record<string, string> };
1071
- input.env = {
1072
- ATW_CONTEXT_ID: contextKey,
1073
- ...input.env,
1074
- };
1188
+ const input = event.input as { command: string; env?: Record<string, string> };
1189
+ if (input.env?.ATW_CONTEXT_ID) return;
1190
+ input.command = `export ATW_CONTEXT_ID=${contextKey}\n${input.command}`;
1191
+ return { input };
1075
1192
  });
1076
1193
 
1077
1194
  pi.on("input", async (event, ctx) => {
@@ -1089,6 +1206,6 @@ export default function(pi: ExtensionAPI): void {
1089
1206
  // Record the turn's skip decision and pre-warm the cache so
1090
1207
  // before_agent_start and context can use it
1091
1208
  turnCache.beginTurn(skipThisTurn);
1092
- turnCache.get(projectRoot, contextKey);
1209
+ turnCache.get(projectRoot, contextKey, isSubAgentSession(ctx));
1093
1210
  });
1094
1211
  }
@@ -1,4 +1,5 @@
1
1
  import type { AgentTemplate } from "../template-utils.js";
2
2
  export declare function getAllAgents(): AgentTemplate[];
3
3
  export declare function getExtensionTemplate(): string;
4
+ export declare function getConfigTemplate(): string;
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/omp/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAE9C;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/omp/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAE9C;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
@@ -6,4 +6,7 @@ export function getAllAgents() {
6
6
  export function getExtensionTemplate() {
7
7
  return readTemplate("extensions/atw/index.ts.txt");
8
8
  }
9
+ export function getConfigTemplate() {
10
+ return readTemplate("config.yml");
11
+ }
9
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/omp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG5D,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE7E,MAAM,UAAU,YAAY;IAC1B,OAAO,YAAY,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,YAAY,CAAC,6BAA6B,CAAC,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/omp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG5D,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE7E,MAAM,UAAU,YAAY;IAC1B,OAAO,YAAY,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,YAAY,CAAC,6BAA6B,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,YAAY,CAAC,YAAY,CAAC,CAAC;AACpC,CAAC"}
@@ -36,8 +36,10 @@ function getTaskStatus(ctx, platformInput = null, general = false) {
36
36
  }
37
37
  return (
38
38
  "Status: NO ACTIVE TASK\n" +
39
- "Next-Action: Classify the current turn before creating any ATW task. " +
40
- "Simple conversation / small task asks only whether this turn should create an ATW task. " +
39
+ "Next-Action: If the request is about a task that already exists — the user names it, or asks to continue or accept and only one open task fits — make it this session's task first, as the no-task breadcrumb says; do not ask whether to create a new one. " +
40
+ "Otherwise classify the current turn before creating any ATW task. " +
41
+ "Simple conversation / small task asks whether this turn should create an ATW task. " +
42
+ "In that ask, say what each answer means: without a task there is no requirements document (`prd.md`) and no formal acceptance (each requirement checked with evidence, the user signs off); a task adds writing the requirements, the user confirming the spec, implementing against it, and that acceptance before archiving. " +
41
43
  "Complex task asks whether task creation and planning are allowed."
42
44
  )
43
45
  }
@@ -131,6 +131,7 @@ function promptHasSkipKeyword(text, keyword) {
131
131
  * buildBreadcrumb so users see the broken state and fix workflow.md
132
132
  * rather than the plugin silently masking it.
133
133
  */
134
+ const PLATFORM_MARKER_LINE_RE = /^\[\/?[A-Za-z][^[\]]*\]\s*(?:\n|$)/gm
134
135
  function loadBreadcrumbs(directory) {
135
136
  const workflowPath = join(directory, ".atw", "workflow.md")
136
137
  if (!existsSync(workflowPath)) return {}
@@ -143,7 +144,10 @@ function loadBreadcrumbs(directory) {
143
144
  const result = {}
144
145
  for (const match of content.matchAll(TAG_RE)) {
145
146
  const status = match[1]
146
- const body = match[2].trim()
147
+ // OpenCode has no `[Platform, ...]` block of its own in any breadcrumb, so
148
+ // it gets every block's lines (each names its tool) with the marker lines
149
+ // dropped, like select_platform_blocks() for such platforms.
150
+ const body = match[2].replace(PLATFORM_MARKER_LINE_RE, "").trim()
147
151
  if (body) result[status] = body
148
152
  }
149
153
  return result
@@ -4,6 +4,7 @@ import {
4
4
  openSync,
5
5
  readFileSync,
6
6
  readSync,
7
+ readdirSync,
7
8
  realpathSync,
8
9
  renameSync,
9
10
  statSync,
@@ -1180,6 +1181,55 @@ function inheritSessionPointer(
1180
1181
  // ── Workflow State Breadcrumb ─────────────────────────────────────────
1181
1182
  const WF_RE =
1182
1183
  /\[workflow-state:([A-Za-z0-9_-]+)\]\s*\n([\s\S]*?)\n\s*\[\/workflow-state:\1\]/g;
1184
+ // Mirrors select_platform_blocks() in .atw/scripts/common/workflow_phase.py:
1185
+ // keep only the `[Platform, ...]` blocks naming this platform, or every block
1186
+ // when none does; marker lines are always dropped.
1187
+ const PLATFORM_MARKER_RE = /^\[(\/?)([A-Za-z][^[\]]*)\]\s*$/;
1188
+ function selectPlatformBlocks(body: string, platform: string): string {
1189
+ const norm = (s: string) => s.toLowerCase().replace(/[-_ ]/g, "");
1190
+ const names = (m: RegExpMatchArray) => (m[2] ?? "").split(",").map((n) => norm(n.trim()));
1191
+ const lines = body.split("\n");
1192
+ const anyMatch = lines.some((l) => {
1193
+ const m = l.match(PLATFORM_MARKER_RE);
1194
+ return !!m && m[1] !== "/" && names(m).includes(norm(platform));
1195
+ });
1196
+ const out: string[] = [];
1197
+ let keep = true;
1198
+ for (const line of lines) {
1199
+ const m = line.match(PLATFORM_MARKER_RE);
1200
+ if (m) {
1201
+ keep = m[1] === "/" || !anyMatch || names(m).includes(norm(platform));
1202
+ continue;
1203
+ }
1204
+ if (keep) out.push(line);
1205
+ }
1206
+ return out.join("\n");
1207
+ }
1208
+ // Mirrors DECISION_LOG_REMINDER / has_decision_log() in the shared per-turn
1209
+ // Python hook: same sentence, implement or accept only.
1210
+ const DECISION_LOG_REMINDER =
1211
+ "This task has a decision log: keep recording it at key points with `python3 ./.atw/scripts/decision_log.py`; in this conversation, the start row (`decision_log.py start`) goes in just before the first row you log — `add` and the other writing commands write it first when it is missing, so a conversation with nothing to log writes none; read the last rows they print, and when another conversation wrote in between they write only the start row and stop: read it, then run the same command again.";
1212
+ // Mirrors DECISION_LOG_ARCHIVE_REMINDER in the shared per-turn Python hook:
1213
+ // same sentence, accept only (with a log), placed before DECISION_LOG_REMINDER.
1214
+ const DECISION_LOG_ARCHIVE_REMINDER =
1215
+ "This task has a decision log: once accepted, after the acceptance row and before `task.py archive`, log one row for the archive (`decision_log.py add`); it goes into the archive commit with the task.";
1216
+ // Mirrors DECISION_LOG_REPORT_REMINDER in the shared per-turn Python hook:
1217
+ // same sentence, implement or accept (with a log), just before DECISION_LOG_REMINDER.
1218
+ const DECISION_LOG_REPORT_REMINDER =
1219
+ "This task has a decision log: when the user asks how things stand or for a report (汇报一下, 现在什么情况, what's the status, report), rebuild it from the task directory in four blocks — `python3 ./.atw/scripts/decision_log.py pending --report` prints the draft; post it in plain words, keep the plain-words lines it carries with noob mode on, and fill in what it marks. This is the task's report, not `atw-what-happened`: that skill only the user starts, and it tells what changed in this chat since their last message.";
1220
+ // Mirrors DECISION_LOG_NO_TASK_REPORT_REMINDER / any_task_has_decision_log() in
1221
+ // the shared per-turn Python hook: same sentence, no current task only.
1222
+ const DECISION_LOG_NO_TASK_REPORT_REMINDER =
1223
+ "No task is current, but some task under `.atw/tasks/` has a decision log: when the user asks how things stand or for a report (汇报一下, 现在什么情况, what's the status, report), rebuild it from that task's directory in four blocks — list the active tasks with `python3 ./.atw/scripts/task.py list` and see which of their directories hold a `decision-log.md` (if the user named the task or said enough to tell which one, take that one; if not, only one, take it; several, ask the user which), then `python3 ./.atw/scripts/decision_log.py pending --report --task-dir <dir>` prints the draft; post it in plain words, keep the plain-words lines it carries with noob mode on, and fill in what it marks. This is the task's report, not `atw-what-happened`: that skill only the user starts, and it tells what changed in this chat since their last message.";
1224
+ // 只看 .atw/tasks/ 下一层,归档里的不算
1225
+ function anyTaskHasDecisionLog(root: string): boolean {
1226
+ const tasks = join(root, ".atw", "tasks");
1227
+ try {
1228
+ return readdirSync(tasks).some((name) => exists(join(tasks, name, "decision-log.md")));
1229
+ } catch {
1230
+ return false;
1231
+ }
1232
+ }
1183
1233
  function workflowBreadcrumb(root: string, key: string | null): string {
1184
1234
  const wf = readText(join(root, ".atw", "workflow.md"));
1185
1235
  if (!wf) return "";
@@ -1191,7 +1241,9 @@ function workflowBreadcrumb(root: string, key: string | null): string {
1191
1241
  }
1192
1242
  const dir = readTaskDir(root, key);
1193
1243
  let header = "Status: no_task",
1194
- lookup = "no_task";
1244
+ lookup = "no_task",
1245
+ decisionLog = false,
1246
+ accept = false;
1195
1247
  if (dir) {
1196
1248
  try {
1197
1249
  const d = JSON.parse(readText(join(dir, "task.json"))) as JsonObject;
@@ -1200,11 +1252,23 @@ function workflowBreadcrumb(root: string, key: string | null): string {
1200
1252
  if (status) {
1201
1253
  header = `Task: ${id} (${status})`;
1202
1254
  lookup = status;
1255
+ decisionLog =
1256
+ (status === "implement" || status === "accept") &&
1257
+ exists(join(dir, "decision-log.md"));
1258
+ accept = status === "accept";
1203
1259
  }
1204
1260
  } catch {}
1205
1261
  }
1206
- const body = templates[lookup] ?? "Refer to workflow.md for current step.";
1207
- return `<workflow-state>\n${header}\n${body}\n</workflow-state>`;
1262
+ const body = selectPlatformBlocks(
1263
+ templates[lookup] ?? "Refer to workflow.md for current step.",
1264
+ "Pi",
1265
+ );
1266
+ const tail = decisionLog
1267
+ ? `${accept ? `\n${DECISION_LOG_ARCHIVE_REMINDER}` : ""}\n${DECISION_LOG_REPORT_REMINDER}\n${DECISION_LOG_REMINDER}`
1268
+ : lookup === "no_task" && anyTaskHasDecisionLog(root)
1269
+ ? `\n${DECISION_LOG_NO_TASK_REPORT_REMINDER}`
1270
+ : "";
1271
+ return `<workflow-state>\n${header}\n${body}${tail}\n</workflow-state>`;
1208
1272
  }
1209
1273
 
1210
1274
  // ── Session Overview ───────────────────────────────────────────────────
@@ -1250,16 +1314,16 @@ function buildStartupContext(
1250
1314
  ): string {
1251
1315
  const workflow = workflowOverview(root, key);
1252
1316
  // get_context.py puts a <conventions> block in the overview only for a
1253
- // `general` project. Its workflow has its own way to pick up unfinished
1254
- // work; the bare `task.py start` below is one the script refuses there.
1317
+ // `general` project: there <ready> defers to <atw-workflow>, which has its
1318
+ // own way to pick up unfinished work. In dev mode <ready> points to the
1319
+ // per-turn no-task breadcrumb for binding an already-existing task.
1255
1320
  const ready = overview.includes("\n<conventions>\n")
1256
1321
  ? [
1257
1322
  "Do what <atw-workflow> above says for the current task's status. With no current task, it also says when to create one and how to pick up unfinished work.",
1258
1323
  ]
1259
1324
  : [
1260
1325
  "Use the current workflow state to decide whether to create, continue, or skip an ATW task.",
1261
- "When CURRENT TASK is (none) but ACTIVE TASKS lists exactly one task assigned to you that is already at the implement or accept stage, this window is merely not bound to it yet: run `python3 ./.atw/scripts/task.py start <task-dir>` for that task, then continue from its stage. Do not ask whether to create a new task in that case.",
1262
- "If several tasks qualify, or the only candidate is still in a planning stage (discover / specify / slice), ask which task to bind instead of running start: start moves a planning-stage task straight into implement.",
1326
+ "With no current task, if the request is about a task that already exists — the user names it, or asks to continue or accept and only one open task fits — make it this session's task first, as the no-task breadcrumb says; do not ask whether to create a new one.",
1263
1327
  ];
1264
1328
  return [
1265
1329
  "<session-context>\nATW compact SessionStart context. Use it to orient the session; load details on demand.\n</session-context>",
@@ -2032,6 +2096,10 @@ export default function atwExtension(pi: {
2032
2096
  return undefined;
2033
2097
  });
2034
2098
  pi.on?.("before_agent_start", (event, ctx) => {
2099
+ // Same switch as the other platforms' hooks: channel sets ATW_HOOKS=0 on
2100
+ // its helpers, which must not receive prompts written for the main AI.
2101
+ if (process.env.ATW_HOOKS === "0" || process.env.ATW_DISABLE_HOOKS === "1")
2102
+ return undefined;
2035
2103
  const k = getKey(event, ctx);
2036
2104
  const key = cacheKey(k, ctx);
2037
2105
  const cur = (event as { systemPrompt?: string }).systemPrompt ?? "";
@@ -2089,4 +2157,9 @@ export default function atwExtension(pi: {
2089
2157
  pi.on?.("context", (event, ctx) => {
2090
2158
  getKey(event, ctx);
2091
2159
  });
2160
+ // Compaction can fold the persisted runtime-context message into its
2161
+ // summary. Forget what was sent so the next turn re-sends it unchanged.
2162
+ pi.on?.("session_compact", () => {
2163
+ lastSentRuntimeCtx.clear();
2164
+ });
2092
2165
  }
@@ -11,7 +11,7 @@ export interface HookScript {
11
11
  /** Script content — no placeholders, ready to write directly */
12
12
  content: string;
13
13
  }
14
- export type SharedHookName = "session-start.py" | "inject-shell-session-context.py" | "inject-workflow-state.py" | "inject-subagent-context.py";
14
+ export type SharedHookName = "session-start.py" | "inject-shell-session-context.py" | "inject-workflow-state.py" | "inject-subagent-context.py" | "show-notice.py";
15
15
  export type SharedHookPlatform = "claude" | "cursor" | "codex" | "gemini" | "qoder" | "copilot" | "codebuddy" | "droid" | "kiro" | "trae" | "zcode";
16
16
  /**
17
17
  * Which shared hooks each platform actually invokes. Single source of truth
@@ -51,6 +51,13 @@ export type SharedHookPlatform = "claude" | "cursor" | "codex" | "gemini" | "qod
51
51
  * (per-turn breadcrumb), and `inject-subagent-context.py` (sub-agent
52
52
  * spawn). The scripts emit a plain-text Kiro branch — Kiro adds a hook's
53
53
  * stdout directly to the conversation context (no JSON envelope).
54
+ * - `show-notice.py` — Claude Code only (PostToolUse, matcher `Bash`): after
55
+ * `cross_model_review.py afk-notice|prepare|prepare-log` it hands the
56
+ * `notice` the script wrote to `.atw/.runtime/hook-notice.json` to Claude
57
+ * Code as `systemMessage`, which Claude Code shows the user without the
58
+ * model in between (task decision-log-afk ticket 26: Claude Code 2.1.302
59
+ * rewrote the paragraph instead of posting it). Other platforms keep the
60
+ * skills' "post it word for word".
54
61
  * - Claude Code `statusLine` is intentionally not installed by default.
55
62
  * Users can add their own statusLine command in `.claude/settings.json`,
56
63
  * or opt in to the ATW one via `atw init --with-statusline`
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,iCAAiC,GACjC,0BAA0B,GAC1B,4BAA4B,CAAC;AAEjC,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,CAAC;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,kBAAkB,EAClB,SAAS,cAAc,EAAE,CAoD1B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,UAAU,EAAE,CAWnD;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,kBAAkB,GAC3B,UAAU,EAAE,CAGd"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,iCAAiC,GACjC,0BAA0B,GAC1B,4BAA4B,GAC5B,gBAAgB,CAAC;AAErB,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,CAAC;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,kBAAkB,EAClB,SAAS,cAAc,EAAE,CAqD1B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,UAAU,EAAE,CAWnD;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,kBAAkB,GAC3B,UAAU,EAAE,CAGd"}
@@ -51,6 +51,13 @@ function readTemplate(relativePath) {
51
51
  * (per-turn breadcrumb), and `inject-subagent-context.py` (sub-agent
52
52
  * spawn). The scripts emit a plain-text Kiro branch — Kiro adds a hook's
53
53
  * stdout directly to the conversation context (no JSON envelope).
54
+ * - `show-notice.py` — Claude Code only (PostToolUse, matcher `Bash`): after
55
+ * `cross_model_review.py afk-notice|prepare|prepare-log` it hands the
56
+ * `notice` the script wrote to `.atw/.runtime/hook-notice.json` to Claude
57
+ * Code as `systemMessage`, which Claude Code shows the user without the
58
+ * model in between (task decision-log-afk ticket 26: Claude Code 2.1.302
59
+ * rewrote the paragraph instead of posting it). Other platforms keep the
60
+ * skills' "post it word for word".
54
61
  * - Claude Code `statusLine` is intentionally not installed by default.
55
62
  * Users can add their own statusLine command in `.claude/settings.json`,
56
63
  * or opt in to the ATW one via `atw init --with-statusline`
@@ -67,6 +74,7 @@ export const SHARED_HOOKS_BY_PLATFORM = {
67
74
  "session-start.py",
68
75
  "inject-workflow-state.py",
69
76
  "inject-subagent-context.py",
77
+ "show-notice.py",
70
78
  ],
71
79
  cursor: [
72
80
  "session-start.py",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,SAAS,YAAY,CAAC,YAAoB;IACxC,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAGjC;IACF,MAAM,EAAE;QACN,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,MAAM,EAAE;QACN,kBAAkB;QAClB,iCAAiC;QACjC,4BAA4B;KAC7B;IACD,KAAK,EAAE,CAAC,0BAA0B,EAAE,4BAA4B,CAAC;IACjE,MAAM,EAAE;QACN,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;KAC3B;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;KAC3B;IACD,OAAO,EAAE,CAAC,0BAA0B,CAAC;IACrC,SAAS,EAAE;QACT,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,IAAI,EAAE;QACJ,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,IAAI,EAAE;QACJ,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;KAC3B;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;QAC1B,4BAA4B;KAC7B;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC;SACjC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAChC,IAAI,EAAE,CAAC;IAEV,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAC7C,QAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpE,OAAO,oBAAoB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,SAAS,YAAY,CAAC,YAAoB;IACxC,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;AA6BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAGjC;IACF,MAAM,EAAE;QACN,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;QAC5B,gBAAgB;KACjB;IACD,MAAM,EAAE;QACN,kBAAkB;QAClB,iCAAiC;QACjC,4BAA4B;KAC7B;IACD,KAAK,EAAE,CAAC,0BAA0B,EAAE,4BAA4B,CAAC;IACjE,MAAM,EAAE;QACN,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;KAC3B;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;KAC3B;IACD,OAAO,EAAE,CAAC,0BAA0B,CAAC;IACrC,SAAS,EAAE;QACT,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,IAAI,EAAE;QACJ,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;KAC7B;IACD,IAAI,EAAE;QACJ,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;KAC3B;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,iCAAiC;QACjC,0BAA0B;QAC1B,4BAA4B;KAC7B;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC;SACjC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAChC,IAAI,EAAE,CAAC;IAEV,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAC7C,QAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpE,OAAO,oBAAoB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC"}