@chinhae/atw-cli 0.5.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 (257) 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.6.0.json +442 -0
  98. package/dist/migrations/manifests/0.7.0.json +1378 -0
  99. package/dist/templates/atw/definition-of-done.md +65 -0
  100. package/dist/templates/atw/gitignore.txt +7 -0
  101. package/dist/templates/atw/index.d.ts +11 -0
  102. package/dist/templates/atw/index.d.ts.map +1 -1
  103. package/dist/templates/atw/index.js +22 -0
  104. package/dist/templates/atw/index.js.map +1 -1
  105. package/dist/templates/atw/scripts/common/active_task.py +2 -2
  106. package/dist/templates/atw/scripts/common/workflow_phase.py +33 -0
  107. package/dist/templates/atw/scripts/cross_model_review.py +3312 -0
  108. package/dist/templates/atw/scripts/decision_log.py +2002 -0
  109. package/dist/templates/atw/scripts/floor_guard.py +642 -0
  110. package/dist/templates/atw/scripts/task.py +25 -6
  111. package/dist/templates/atw/scripts/tickets.py +480 -10
  112. package/dist/templates/atw/workflow-general.md +6 -1
  113. package/dist/templates/atw/workflow.md +346 -26
  114. package/dist/templates/claude/settings.json +12 -0
  115. package/dist/templates/codex/hooks/session-start.py +2 -2
  116. package/dist/templates/common/bundled-skills/atw-askme/SKILL.md +1 -1
  117. package/dist/templates/common/bundled-skills/atw-askme-with-docs/SKILL.md +1 -1
  118. package/dist/templates/common/bundled-skills/atw-channel/SKILL.md +5 -2
  119. package/dist/templates/common/bundled-skills/atw-channel/references/command-reference.md +44 -5
  120. package/dist/templates/common/bundled-skills/atw-channel/references/progress-debugging.md +9 -0
  121. package/dist/templates/common/bundled-skills/atw-channel/references/workers.md +48 -4
  122. package/dist/templates/common/bundled-skills/atw-code-review/SKILL.md +78 -3
  123. package/dist/templates/common/bundled-skills/atw-codebase-design/SKILL.md +2 -2
  124. package/dist/templates/common/bundled-skills/atw-codebase-design/{DESIGN-IT-TWICE.md → design-it-twice.md} +3 -3
  125. package/dist/templates/common/bundled-skills/atw-cross-model-review/SKILL.md +361 -0
  126. package/dist/templates/common/bundled-skills/atw-cross-model-review/agents/openai.yaml +5 -0
  127. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/four-tiers.md +42 -0
  128. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/log-review-questions.md +38 -0
  129. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/questions.md +54 -0
  130. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/recheck-questions.md +42 -0
  131. package/dist/templates/common/bundled-skills/atw-cross-model-review/references/report-format.md +22 -0
  132. package/dist/templates/common/bundled-skills/atw-diagnosing-bugs/SKILL.md +13 -2
  133. package/dist/templates/common/bundled-skills/atw-domain-modeling/SKILL.md +2 -2
  134. package/dist/templates/common/bundled-skills/atw-handoff/SKILL.md +1 -1
  135. package/dist/templates/common/bundled-skills/atw-implement/SKILL.md +37 -1
  136. package/dist/templates/common/bundled-skills/atw-implement-all/SKILL.md +219 -24
  137. package/dist/templates/common/bundled-skills/atw-implement-spec/SKILL.md +237 -18
  138. package/dist/templates/common/bundled-skills/atw-improve-codebase-architecture/SKILL.md +2 -2
  139. package/dist/templates/common/bundled-skills/atw-init-repo/SKILL.md +1 -1
  140. package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw-local.md +7 -1
  141. package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw.md +7 -1
  142. package/dist/templates/common/bundled-skills/atw-map/SKILL.md +1 -1
  143. package/dist/templates/common/bundled-skills/atw-meta/SKILL.md +1 -1
  144. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/add-project-local-conventions.md +4 -3
  145. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/change-skills-or-commands.md +21 -16
  146. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/change-task-lifecycle.md +1 -1
  147. package/dist/templates/common/bundled-skills/atw-meta/references/customize-local/overview.md +1 -0
  148. package/dist/templates/common/bundled-skills/atw-meta/references/local-architecture/task-system.md +2 -1
  149. package/dist/templates/common/bundled-skills/atw-meta/references/platform-files/hooks-and-settings.md +1 -0
  150. package/dist/templates/common/bundled-skills/atw-noob-mode/SKILL.md +1 -1
  151. package/dist/templates/common/bundled-skills/atw-performance/SKILL.md +134 -0
  152. package/dist/templates/common/bundled-skills/atw-performance/agents/openai.yaml +3 -0
  153. package/dist/templates/common/bundled-skills/atw-performance/references/optimization-patterns.md +250 -0
  154. package/dist/templates/common/bundled-skills/atw-performance/references/performance-checklist.md +238 -0
  155. package/dist/templates/common/bundled-skills/atw-performance/references/performance-optimization.md +264 -0
  156. package/dist/templates/common/bundled-skills/atw-prototype/SKILL.md +3 -3
  157. package/dist/templates/common/bundled-skills/atw-prototype/{LOGIC.md → logic.md} +1 -1
  158. package/dist/templates/common/bundled-skills/atw-prototype/references/design-direction.md +91 -58
  159. package/dist/templates/common/bundled-skills/atw-prototype/references/direction-persuasion.md +25 -0
  160. package/dist/templates/common/bundled-skills/atw-prototype/references/style-explorer.md +1 -1
  161. package/dist/templates/common/bundled-skills/atw-prototype/ui.md +194 -0
  162. package/dist/templates/common/bundled-skills/atw-reflect/SKILL.md +77 -0
  163. package/dist/templates/common/bundled-skills/atw-reflect/agents/openai.yaml +5 -0
  164. package/dist/templates/common/bundled-skills/atw-reflect/references/divergent-reviewer.md +38 -0
  165. package/dist/templates/common/bundled-skills/atw-reflect/references/judgment-reviewer.md +37 -0
  166. package/dist/templates/common/bundled-skills/atw-reflect/references/synthesizer.md +63 -0
  167. package/dist/templates/common/bundled-skills/atw-reflect/references/tooling-reviewer.md +48 -0
  168. package/dist/templates/common/bundled-skills/atw-security/SKILL.md +248 -0
  169. package/dist/templates/common/bundled-skills/atw-security/agents/openai.yaml +3 -0
  170. package/dist/templates/common/bundled-skills/atw-security/references/hardening-patterns.md +324 -0
  171. package/dist/templates/common/bundled-skills/atw-security/references/security-checklist.md +248 -0
  172. package/dist/templates/common/bundled-skills/atw-session-insight/SKILL.md +1 -1
  173. package/dist/templates/common/bundled-skills/atw-session-insight/references/cli-quick-reference.md +11 -2
  174. package/dist/templates/common/bundled-skills/atw-spec/SKILL.md +1 -1
  175. package/dist/templates/common/bundled-skills/atw-tdd/SKILL.md +25 -0
  176. package/dist/templates/common/bundled-skills/atw-tickets/SKILL.md +1 -1
  177. package/dist/templates/common/bundled-skills/atw-triage/SKILL.md +5 -5
  178. package/dist/templates/common/bundled-skills/atw-ui/SKILL.md +99 -31
  179. package/dist/templates/common/bundled-skills/atw-ui/references/accessibility-checklist.md +160 -0
  180. package/dist/templates/common/bundled-skills/atw-ui/references/layout-and-viewport.md +7 -5
  181. package/dist/templates/common/bundled-skills/atw-ui/references/media.md +117 -20
  182. package/dist/templates/common/bundled-skills/atw-ui/references/motion.md +47 -23
  183. package/dist/templates/common/bundled-skills/atw-ui/references/play.md +18 -0
  184. package/dist/templates/common/bundled-skills/atw-ui/references/reviewer-manual.md +73 -0
  185. package/dist/templates/common/bundled-skills/atw-ui/references/scroll-narrative.md +98 -0
  186. package/dist/templates/common/bundled-skills/atw-ui/references/tools.md +46 -0
  187. package/dist/templates/common/bundled-skills/atw-ui/references/visual-language.md +40 -24
  188. package/dist/templates/common/bundled-skills/atw-ui/references/visual-review.md +26 -82
  189. package/dist/templates/common/bundled-skills/atw-ui/scripts/shoot.mjs +1721 -0
  190. package/dist/templates/common/bundled-skills/atw-verify-skill/SKILL.md +58 -0
  191. package/dist/templates/common/bundled-skills/atw-verify-skill/agents/openai.yaml +5 -0
  192. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/README.md +47 -0
  193. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/create-note.md +39 -0
  194. package/dist/templates/common/bundled-skills/atw-verify-skill/references/feature-map-example/search.md +45 -0
  195. package/dist/templates/common/bundled-skills/atw-verify-skill/references/generate.md +107 -0
  196. package/dist/templates/common/bundled-skills/atw-verify-skill/references/health-check.md +100 -0
  197. package/dist/templates/common/bundled-skills/atw-verify-skill/references/verify-skill-template.md +99 -0
  198. package/dist/templates/common/bundled-skills/atw-what-happened/SKILL.md +49 -0
  199. package/dist/templates/common/bundled-skills/atw-what-happened/agents/openai.yaml +5 -0
  200. package/dist/templates/common/commands/finish-work.md +10 -1
  201. package/dist/templates/common/commands/start.md +3 -1
  202. package/dist/templates/common/skills/update-spec.md +40 -4
  203. package/dist/templates/copilot/hooks/session-start.py +2 -2
  204. package/dist/templates/dsh/DSH.md +2 -2
  205. package/dist/templates/markdown/spec/backend/quality-guidelines.md.txt +10 -0
  206. package/dist/templates/markdown/spec/frontend/quality-guidelines.md.txt +10 -0
  207. package/dist/templates/omp/config.yml +15 -0
  208. package/dist/templates/omp/extensions/atw/index.ts.txt +135 -18
  209. package/dist/templates/omp/index.d.ts +1 -0
  210. package/dist/templates/omp/index.d.ts.map +1 -1
  211. package/dist/templates/omp/index.js +3 -0
  212. package/dist/templates/omp/index.js.map +1 -1
  213. package/dist/templates/opencode/lib/session-utils.js +4 -2
  214. package/dist/templates/opencode/plugins/inject-workflow-state.js +5 -1
  215. package/dist/templates/pi/extensions/atw/index.ts.txt +80 -7
  216. package/dist/templates/shared-hooks/index.d.ts +8 -1
  217. package/dist/templates/shared-hooks/index.d.ts.map +1 -1
  218. package/dist/templates/shared-hooks/index.js +8 -0
  219. package/dist/templates/shared-hooks/index.js.map +1 -1
  220. package/dist/templates/shared-hooks/inject-workflow-state.py +131 -14
  221. package/dist/templates/shared-hooks/session-start.py +4 -2
  222. package/dist/templates/shared-hooks/show-notice.py +135 -0
  223. package/dist/templates/snow/SNOW.md +1 -1
  224. package/dist/utils/initialized.d.ts +9 -0
  225. package/dist/utils/initialized.d.ts.map +1 -0
  226. package/dist/utils/initialized.js +24 -0
  227. package/dist/utils/initialized.js.map +1 -0
  228. package/dist/utils/managed-removal.d.ts +2 -0
  229. package/dist/utils/managed-removal.d.ts.map +1 -1
  230. package/dist/utils/managed-removal.js +23 -3
  231. package/dist/utils/managed-removal.js.map +1 -1
  232. package/dist/utils/optional-skills.d.ts +2 -0
  233. package/dist/utils/optional-skills.d.ts.map +1 -1
  234. package/dist/utils/optional-skills.js +1 -1
  235. package/dist/utils/optional-skills.js.map +1 -1
  236. package/dist/utils/template-hash.d.ts.map +1 -1
  237. package/dist/utils/template-hash.js +3 -0
  238. package/dist/utils/template-hash.js.map +1 -1
  239. package/dist/utils/uninstall-scrubbers.d.ts +10 -0
  240. package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
  241. package/dist/utils/uninstall-scrubbers.js +50 -0
  242. package/dist/utils/uninstall-scrubbers.js.map +1 -1
  243. package/dist/utils/verify-entries.d.ts +51 -0
  244. package/dist/utils/verify-entries.d.ts.map +1 -0
  245. package/dist/utils/verify-entries.js +399 -0
  246. package/dist/utils/verify-entries.js.map +1 -0
  247. package/package.json +2 -2
  248. package/dist/templates/common/bundled-skills/atw-prototype/UI.md +0 -174
  249. package/dist/templates/common/bundled-skills/atw-prototype/references/visual-language.md +0 -88
  250. package/dist/templates/common/bundled-skills/atw-prototype/references/visual-review.md +0 -63
  251. package/dist/templates/common/bundled-skills/atw-ui/references/imagery.md +0 -69
  252. /package/dist/templates/common/bundled-skills/atw-codebase-design/{DEEPENING.md → deepening.md} +0 -0
  253. /package/dist/templates/common/bundled-skills/atw-domain-modeling/{ADR-FORMAT.md → adr-format.md} +0 -0
  254. /package/dist/templates/common/bundled-skills/atw-domain-modeling/{GLOSSARY-FORMAT.md → glossary-format.md} +0 -0
  255. /package/dist/templates/common/bundled-skills/atw-improve-codebase-architecture/{HTML-REPORT.md → html-report.md} +0 -0
  256. /package/dist/templates/common/bundled-skills/atw-triage/{AGENT-BRIEF.md → agent-brief.md} +0 -0
  257. /package/dist/templates/common/bundled-skills/atw-triage/{OUT-OF-SCOPE.md → out-of-scope.md} +0 -0
@@ -20,20 +20,23 @@ const BATCH_FLAG_HINT = "Pass --force to overwrite, --skip-all to keep your vers
20
20
  // Import templates for comparison
21
21
  import { getAllScripts, getAllAgents,
22
22
  // Configuration
23
- configYamlTemplate, gitignoreTemplate, bundledWorkflowFor, } from "../templates/atw/index.js";
23
+ configYamlTemplate, gitignoreTemplate, bundledWorkflowFor, getModeAtwFiles, } from "../templates/atw/index.js";
24
24
  import { agentsMdFor } from "../templates/markdown/index.js";
25
25
  import { COPILOT_INSTRUCTIONS_BLOCK_END, COPILOT_INSTRUCTIONS_BLOCK_START, COPILOT_INSTRUCTIONS_PATH, getCopilotInstructions, } from "../templates/copilot/index.js";
26
26
  import { ALL_MANAGED_DIRS, getConfiguredPlatforms, collectPlatformTemplates, } from "../configurators/index.js";
27
27
  import { replacePythonCommandLiterals } from "../configurators/shared.js";
28
28
  import { preserveCodexAgentModelKeys } from "../configurators/codex.js";
29
+ import { preserveOmpProjectConfig } from "../configurators/omp.js";
29
30
  import { ensureGitattributes } from "../configurators/workflow.js";
30
31
  import { pruneOrphanManifestKeys } from "../utils/manifest-prune.js";
31
32
  import { loadProjectMode } from "../utils/project-mode.js";
32
33
  import { fetchRegistrySpecTemplates, collectDirectoryFiles, removeDirectory, parseRegistrySource, probeRegistryIndex, downloadTemplateById, } from "../utils/template-fetcher.js";
33
34
  import { loadSpecRegistryConfig } from "../utils/registry-config.js";
34
35
  import { fetchOptionalSkills, getInstalledOptionalSkillDirs, installOptionalSkills, loadOptionalSkillRecord, } from "../utils/optional-skills.js";
36
+ import { syncVerifyEntriesAfterInstall } from "./verify.js";
35
37
  import { cleanupEmptyDirs, ATW_BLOCK_END, ATW_BLOCK_START, } from "../utils/managed-paths.js";
36
38
  export { cleanupEmptyDirs, ATW_BLOCK_END, ATW_BLOCK_START, } from "../utils/managed-paths.js";
39
+ import { isAtwInitialized } from "../utils/initialized.js";
37
40
  const CLAUDE_SETTINGS_PATH = ".claude/settings.json";
38
41
  const LEGACY_UNTRACKED_AGENTS_MD_BLOCK_HASHES = new Set([
39
42
  // Older installs wrote AGENTS.md without hash-tracking it.
@@ -42,12 +45,26 @@ const LEGACY_UNTRACKED_AGENTS_MD_BLOCK_HASHES = new Set([
42
45
  // false "modified by you" conflict.
43
46
  "c1f511b1cfc1902f2147da159f09cc51f380b0c9e341cdb3ac5dea5233f3e307",
44
47
  ]);
48
+ // `.atw/.gitignore` is never hash-tracked (EXCLUDE_FROM_HASH), so a copy that
49
+ // is byte-identical to an earlier template is recognised by these hashes and
50
+ // upgraded like an unedited file instead of being reported as "modified by you".
51
+ const LEGACY_ATW_GITIGNORE_HASHES = new Set([
52
+ // The only content released before the verify skill evidence line: every
53
+ // tag v0.1.0–v0.6.0 ships this same templates/atw/gitignore.txt, written
54
+ // verbatim by init (checked with `git show <tag>:…/gitignore.txt`).
55
+ "3bc42434cbe61c937a1c26ea3c880b496bfad4e7d683750169e27ade509aa798",
56
+ // Second version, with verify/*/evidence/ (commit 6a7d491, unreleased but
57
+ // installed in this repo), before the task folders' verify-evidence/ lines.
58
+ "cf51d374198c4a2bac0cba41e59fdacb95cff70be781636de92f3dbd68945c2e",
59
+ ]);
45
60
  // Paths that should never be touched (true user data)
46
61
  // spec/ is user-customized content created during init; update should never modify it
47
62
  const PROTECTED_PATHS = [
48
63
  `${DIR_NAMES.WORKFLOW}/${DIR_NAMES.WORKSPACE}`, // workspace/
49
64
  `${DIR_NAMES.WORKFLOW}/${DIR_NAMES.TASKS}`, // tasks/
50
65
  `${DIR_NAMES.WORKFLOW}/${DIR_NAMES.SPEC}`, // spec/
66
+ `${DIR_NAMES.WORKFLOW}/${DIR_NAMES.VERIFY}`, // verify/
67
+ `${DIR_NAMES.WORKFLOW}/${DIR_NAMES.SKILL_NOTES}`, // skill-notes/
51
68
  `${DIR_NAMES.WORKFLOW}/.developer`,
52
69
  `${DIR_NAMES.WORKFLOW}/.current-task`,
53
70
  ];
@@ -113,6 +130,9 @@ function buildCopilotInstructionsTemplate(cwd) {
113
130
  return buildManagedBlockTemplate(cwd, COPILOT_INSTRUCTIONS_PATH, getCopilotInstructions(), COPILOT_INSTRUCTIONS_BLOCK_START, COPILOT_INSTRUCTIONS_BLOCK_END);
114
131
  }
115
132
  function isKnownUntrackedTemplate(relativePath, existingContent) {
133
+ if (relativePath === `${DIR_NAMES.WORKFLOW}/.gitignore`) {
134
+ return LEGACY_ATW_GITIGNORE_HASHES.has(computeHash(existingContent));
135
+ }
116
136
  if (relativePath !== FILE_NAMES.AGENTS) {
117
137
  return false;
118
138
  }
@@ -137,6 +157,110 @@ function isSafeUntrackedCopilotInstructionsMerge(relativePath, existingContent,
137
157
  function isProtectedPath(filePath) {
138
158
  return PROTECTED_PATHS.some((pp) => filePath === pp || filePath.startsWith(pp.endsWith("/") ? pp : pp + "/"));
139
159
  }
160
+ /** Temporary name of a case-only rename between its two steps. */
161
+ const CASE_RENAME_SUFFIX = ".atw-case-rename";
162
+ /** The current template path that differs from `relativePath` only in case. */
163
+ function caseOnlyVariant(relativePath, currentTemplatePaths) {
164
+ const lower = relativePath.toLowerCase();
165
+ for (const candidate of currentTemplatePaths) {
166
+ if (candidate !== relativePath && candidate.toLowerCase() === lower) {
167
+ return candidate;
168
+ }
169
+ }
170
+ return undefined;
171
+ }
172
+ /**
173
+ * Receipt rule for a case-only rename, used both in memory and on disk: the
174
+ * old name's record moves to the new name unless the new name already has
175
+ * one. Both describe the same file; the new name's is the later write.
176
+ */
177
+ function moveCaseRenameRecord(hashes, from, to) {
178
+ const result = { ...hashes };
179
+ const recorded = result[from];
180
+ if (recorded !== undefined && result[to] === undefined) {
181
+ result[to] = recorded;
182
+ }
183
+ Reflect.deleteProperty(result, from);
184
+ return result;
185
+ }
186
+ /**
187
+ * Finish case-only renames a previous run left between its two steps: the
188
+ * file sits under `<old>.atw-case-rename` and neither name exists. Runs before
189
+ * the change analysis so the file is judged under its new name; a dry run only
190
+ * reports. A temp file next to an existing file is left alone and reported.
191
+ */
192
+ function finishInterruptedCaseRenames(migrations, cwd, currentTemplatePaths, hashes, dryRun) {
193
+ let result = hashes;
194
+ for (const item of migrations) {
195
+ if (item.type !== "safe-file-delete")
196
+ continue;
197
+ const to = caseOnlyVariant(item.from, currentTemplatePaths);
198
+ if (!to)
199
+ continue;
200
+ const temp = path.join(cwd, item.from + CASE_RENAME_SUFFIX);
201
+ if (!fs.existsSync(temp))
202
+ continue;
203
+ const target = path.join(cwd, to);
204
+ if (fs.existsSync(path.join(cwd, item.from)) || fs.existsSync(target)) {
205
+ console.log(chalk.yellow(` ! ${item.from}${CASE_RENAME_SUFFIX} is left from an interrupted rename, but ${to} exists; compare the two and delete the one you do not need`));
206
+ continue;
207
+ }
208
+ console.log(chalk.cyan(` ↻ ${item.from}${CASE_RENAME_SUFFIX} → ${to} (finishing an interrupted case-only rename)`));
209
+ result = moveCaseRenameRecord(result, item.from, to);
210
+ if (!dryRun) {
211
+ fs.renameSync(temp, target);
212
+ saveHashes(cwd, moveCaseRenameRecord(loadHashes(cwd), item.from, to));
213
+ }
214
+ }
215
+ return result;
216
+ }
217
+ /** Whether a file with exactly this name (case included) is in its directory. */
218
+ function existsWithExactName(fullPath) {
219
+ try {
220
+ return fs
221
+ .readdirSync(path.dirname(fullPath))
222
+ .includes(path.basename(fullPath));
223
+ }
224
+ catch {
225
+ return false;
226
+ }
227
+ }
228
+ /**
229
+ * The current template path that differs from `relativePath` only in case and
230
+ * is the same file on disk — which happens only on a case-insensitive
231
+ * filesystem (macOS and Windows by default), where `UI.md` and `ui.md` name
232
+ * one file.
233
+ */
234
+ function findCaseOnlyTwin(cwd, relativePath, currentTemplatePaths) {
235
+ const candidate = caseOnlyVariant(relativePath, currentTemplatePaths);
236
+ if (!candidate)
237
+ return undefined;
238
+ try {
239
+ const a = fs.statSync(path.join(cwd, relativePath));
240
+ const b = fs.statSync(path.join(cwd, candidate));
241
+ return a.ino === b.ino && a.dev === b.dev ? candidate : undefined;
242
+ }
243
+ catch (error) {
244
+ // 只有「新名字不存在」说明是两个文件;别的读不了的错误不能当成不是同一个文件
245
+ if (error.code === "ENOENT")
246
+ return undefined;
247
+ throw error;
248
+ }
249
+ }
250
+ /**
251
+ * Move the receipt entry of each case-only rename to the new name in memory,
252
+ * so `analyzeChanges` judges the file under its new name by the hash recorded
253
+ * for it: pristine → auto-update, edited → the usual conflict prompt.
254
+ */
255
+ function applyCaseRenamesToHashes(classified, hashes) {
256
+ let result = hashes;
257
+ for (const c of classified) {
258
+ if (c.action !== "case-rename" || !c.caseRenameTo)
259
+ continue;
260
+ result = moveCaseRenameRecord(result, c.item.from, c.caseRenameTo);
261
+ }
262
+ return result;
263
+ }
140
264
  /**
141
265
  * Collect and classify safe-file-delete migrations
142
266
  *
@@ -169,8 +293,9 @@ bypassUpdateSkip = false) {
169
293
  const results = [];
170
294
  for (const item of safeDeletes) {
171
295
  const fullPath = path.join(cwd, item.from);
172
- // Check: file exists?
173
- if (!fs.existsSync(fullPath)) {
296
+ // Check: file exists? On a case-insensitive disk `UI.md` also "exists"
297
+ // once the file is `ui.md`, so the exact name has to be in the listing.
298
+ if (!fs.existsSync(fullPath) || !existsWithExactName(fullPath)) {
174
299
  results.push({ item, action: "skip-missing" });
175
300
  continue;
176
301
  }
@@ -188,7 +313,21 @@ bypassUpdateSkip = false) {
188
313
  if (!bypassUpdateSkip &&
189
314
  skipPaths.some((skip) => item.from === skip ||
190
315
  item.from.startsWith(skip.endsWith("/") ? skip : skip + "/"))) {
191
- results.push({ item, action: "skip-update-skip" });
316
+ results.push({
317
+ item,
318
+ action: "skip-update-skip",
319
+ renamedUpstreamTo: caseOnlyVariant(item.from, currentTemplatePaths),
320
+ });
321
+ continue;
322
+ }
323
+ // Check: is this the same file as a current template that differs only in
324
+ // case? Deleting it would delete the template too — its write was already
325
+ // judged "unchanged" or "changed" against this very file. Rename it to the
326
+ // template's case instead, whatever its content: no content changes here,
327
+ // the template write that follows applies the usual edited-file rules.
328
+ const caseRenameTo = findCaseOnlyTwin(cwd, item.from, currentTemplatePaths);
329
+ if (caseRenameTo) {
330
+ results.push({ item, action: "case-rename", caseRenameTo });
192
331
  continue;
193
332
  }
194
333
  // Check: hash matches allowed_hashes?
@@ -204,7 +343,11 @@ bypassUpdateSkip = false) {
204
343
  results.push({ item, action: "delete" });
205
344
  }
206
345
  else {
207
- results.push({ item, action: "skip-modified" });
346
+ results.push({
347
+ item,
348
+ action: "skip-modified",
349
+ renamedUpstreamTo: caseOnlyVariant(item.from, currentTemplatePaths),
350
+ });
208
351
  }
209
352
  }
210
353
  catch {
@@ -218,14 +361,19 @@ bypassUpdateSkip = false) {
218
361
  */
219
362
  function printSafeFileDeleteSummary(classified) {
220
363
  const toDelete = classified.filter((c) => c.action === "delete");
364
+ const caseRenames = classified.filter((c) => c.action === "case-rename");
221
365
  const modified = classified.filter((c) => c.action === "skip-modified");
222
366
  const updateSkip = classified.filter((c) => c.action === "skip-update-skip");
223
367
  if (toDelete.length === 0 &&
368
+ caseRenames.length === 0 &&
224
369
  modified.length === 0 &&
225
370
  updateSkip.length === 0) {
226
371
  return;
227
372
  }
228
373
  console.log(chalk.cyan(" Deprecated commands cleanup:"));
374
+ for (const c of caseRenames) {
375
+ console.log(chalk.green(` ↻ ${c.item.from} → ${c.caseRenameTo ?? ""} (case-only rename)`));
376
+ }
229
377
  if (toDelete.length > 0) {
230
378
  for (const c of toDelete) {
231
379
  console.log(chalk.green(` ✕ ${c.item.from}${c.item.description ? ` (${c.item.description})` : ""}`));
@@ -233,12 +381,16 @@ function printSafeFileDeleteSummary(classified) {
233
381
  }
234
382
  if (modified.length > 0) {
235
383
  for (const c of modified) {
236
- console.log(chalk.yellow(` ? ${c.item.from} (modified, skipped)`));
384
+ console.log(chalk.yellow(c.renamedUpstreamTo
385
+ ? ` ? ${c.item.from} (modified, kept): ${c.item.from} was renamed upstream to ${c.renamedUpstreamTo}; your edits in ${c.item.from} no longer take effect — move them into ${c.renamedUpstreamTo} if you still want them`
386
+ : ` ? ${c.item.from} (modified, skipped)`));
237
387
  }
238
388
  }
239
389
  if (updateSkip.length > 0) {
240
390
  for (const c of updateSkip) {
241
- console.log(chalk.gray(` ○ ${c.item.from} (skipped, update.skip)`));
391
+ console.log(chalk.gray(c.renamedUpstreamTo
392
+ ? ` ○ ${c.item.from} (skipped, update.skip): renamed upstream to ${c.renamedUpstreamTo}, which is skipped too`
393
+ : ` ○ ${c.item.from} (skipped, update.skip)`));
242
394
  }
243
395
  }
244
396
  console.log("");
@@ -249,6 +401,30 @@ function printSafeFileDeleteSummary(classified) {
249
401
  function executeSafeFileDeletes(classified, cwd) {
250
402
  const toDelete = classified.filter((c) => c.action === "delete");
251
403
  let deleted = 0;
404
+ for (const c of classified) {
405
+ if (c.action !== "case-rename" || !c.caseRenameTo)
406
+ continue;
407
+ const from = path.join(cwd, c.item.from);
408
+ // Through a temporary name: a direct case-only rename is not honoured by
409
+ // every case-insensitive filesystem. An interruption between the two
410
+ // steps is finished by the next update (finishInterruptedCaseRenames).
411
+ const temp = from + CASE_RENAME_SUFFIX;
412
+ try {
413
+ fs.renameSync(from, temp);
414
+ }
415
+ catch {
416
+ // File may have been removed between classify and execute
417
+ continue;
418
+ }
419
+ try {
420
+ fs.renameSync(temp, path.join(cwd, c.caseRenameTo));
421
+ }
422
+ catch (error) {
423
+ fs.renameSync(temp, from);
424
+ throw error;
425
+ }
426
+ saveHashes(cwd, moveCaseRenameRecord(loadHashes(cwd), c.item.from, c.caseRenameTo));
427
+ }
252
428
  for (const c of toDelete) {
253
429
  const fullPath = path.join(cwd, c.item.from);
254
430
  try {
@@ -651,7 +827,7 @@ async function collectRegistrySpecTemplates(cwd) {
651
827
  }
652
828
  return result.files;
653
829
  }
654
- async function collectTemplateFiles(cwd, mode, extraPlatforms,
830
+ export async function collectTemplateFiles(cwd, mode, extraPlatforms,
655
831
  /**
656
832
  * Bypass `update.skip` when collecting templates. Enable this for breaking
657
833
  * releases so newly added files (e.g. a new `continue.md`) and template
@@ -692,6 +868,11 @@ bypassUpdateSkip = false) {
692
868
  // platform routing markers outside [workflow-state:*] blocks are also
693
869
  // script-consumed.
694
870
  files.set(`${DIR_NAMES.WORKFLOW}/workflow.md`, bundledWorkflowFor(mode));
871
+ // Mode-only .atw/ files (dev: the default definition of done). The project's
872
+ // own .atw/spec/definition-of-done.md is user content and never collected.
873
+ for (const [file, content] of getModeAtwFiles(mode)) {
874
+ files.set(file, content);
875
+ }
695
876
  // workspace/index.md stays excluded — it's runtime-appended by add_session.py
696
877
  // (journal index) and has no script-parsed structure.
697
878
  files.set(FILE_NAMES.AGENTS, buildAgentsMdTemplate(cwd, mode));
@@ -715,6 +896,11 @@ bypassUpdateSkip = false) {
715
896
  if (platforms.has("codex")) {
716
897
  preserveCodexAgentModelKeys(cwd, files);
717
898
  }
899
+ // `.omp/config.yml` may be the project's own omp config: compare against
900
+ // ATW's settings merged into it, not against the bare template.
901
+ if (platforms.has("omp")) {
902
+ preserveOmpProjectConfig(cwd, files);
903
+ }
718
904
  preserveExistingClaudeStatusLine(cwd, files);
719
905
  for (const [filePath, content] of await collectRegistrySpecTemplates(cwd)) {
720
906
  files.set(filePath, content);
@@ -745,7 +931,7 @@ bypassUpdateSkip = false) {
745
931
  * - User didn't modify + template updated = auto-update (autoUpdateFiles)
746
932
  * - User modified = needs confirmation (changedFiles)
747
933
  */
748
- function analyzeChanges(cwd, hashes, templates) {
934
+ export function analyzeChanges(cwd, hashes, templates) {
749
935
  const result = {
750
936
  newFiles: [],
751
937
  unchangedFiles: [],
@@ -1772,7 +1958,7 @@ async function updateOptionalSkills(cwd, dryRun) {
1772
1958
  export async function update(options) {
1773
1959
  const cwd = process.cwd();
1774
1960
  // Check if ATW is initialized
1775
- if (!fs.existsSync(path.join(cwd, DIR_NAMES.WORKFLOW))) {
1961
+ if (!isAtwInitialized(cwd)) {
1776
1962
  console.log(chalk.red("Error: ATW not initialized in this directory."));
1777
1963
  console.log(chalk.gray("Run 'atw init' first."));
1778
1964
  return;
@@ -1876,8 +2062,17 @@ export async function update(options) {
1876
2062
  // Collect safe-file-delete items from ALL manifests (hash match is the safety net)
1877
2063
  // This runs regardless of version — unknown version still gets safe cleanup
1878
2064
  const allMigrations = getAllMigrations();
2065
+ hashes = finishInterruptedCaseRenames(allMigrations, cwd, new Set(templates.keys()), hashes, options.dryRun === true);
1879
2066
  const safeFileDeletes = collectSafeFileDeletes(allMigrations, cwd, skipPaths, new Set(templates.keys()), getInstalledOptionalSkillDirs(cwd), breakingBypass);
1880
- const hasSafeDeletes = safeFileDeletes.filter((c) => c.action === "delete").length > 0;
2067
+ const hasSafeDeletes = safeFileDeletes.some((c) => c.action === "delete" || c.action === "case-rename");
2068
+ hashes = applyCaseRenamesToHashes(safeFileDeletes, hashes);
2069
+ // update.skip 里的旧名字:用户要 ATW 别碰这个文件,只改大小写的新名字是同一个文件
2070
+ // (不分大小写的磁盘)或它的接班(分大小写的磁盘),一并跳过
2071
+ for (const c of safeFileDeletes) {
2072
+ if (c.action === "skip-update-skip" && c.renamedUpstreamTo) {
2073
+ templates.delete(c.renamedUpstreamTo);
2074
+ }
2075
+ }
1881
2076
  // Check for pending regular migrations (skip if unknown version)
1882
2077
  let pendingMigrations = isUnknownVersion
1883
2078
  ? []
@@ -2011,6 +2206,7 @@ export async function update(options) {
2011
2206
  }
2012
2207
  }
2013
2208
  await updateOptionalSkills(cwd, options.dryRun);
2209
+ syncVerifyEntriesAfterInstall(cwd, options.dryRun);
2014
2210
  return;
2015
2211
  }
2016
2212
  // Show what this operation will do
@@ -2056,6 +2252,7 @@ export async function update(options) {
2056
2252
  // Dry run mode
2057
2253
  if (options.dryRun) {
2058
2254
  await updateOptionalSkills(cwd, true);
2255
+ syncVerifyEntriesAfterInstall(cwd, true);
2059
2256
  console.log(chalk.gray("[Dry run] No changes made."));
2060
2257
  return;
2061
2258
  }
@@ -2242,6 +2439,7 @@ export async function update(options) {
2242
2439
  console.log(chalk.gray("\nTip: Review .new files and merge changes manually if needed."));
2243
2440
  }
2244
2441
  await updateOptionalSkills(cwd);
2442
+ syncVerifyEntriesAfterInstall(cwd);
2245
2443
  // Create migration task if there are breaking changes with migration guides
2246
2444
  if (cliVsProject > 0 && projectVersion !== "unknown") {
2247
2445
  const metadata = getMigrationMetadata(projectVersion, cliVersion);