@chinhae/atw-cli 0.3.6 → 0.5.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 (110) hide show
  1. package/README.md +191 -17
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +25 -1
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/commands/init.d.ts +3 -0
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +154 -17
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/mode.d.ts +21 -0
  10. package/dist/commands/mode.d.ts.map +1 -0
  11. package/dist/commands/mode.js +401 -0
  12. package/dist/commands/mode.js.map +1 -0
  13. package/dist/commands/update.d.ts +7 -0
  14. package/dist/commands/update.d.ts.map +1 -1
  15. package/dist/commands/update.js +20 -13
  16. package/dist/commands/update.js.map +1 -1
  17. package/dist/commands/workflow.d.ts +17 -0
  18. package/dist/commands/workflow.d.ts.map +1 -1
  19. package/dist/commands/workflow.js +9 -3
  20. package/dist/commands/workflow.js.map +1 -1
  21. package/dist/configurators/claude.d.ts +3 -2
  22. package/dist/configurators/claude.d.ts.map +1 -1
  23. package/dist/configurators/claude.js +3 -3
  24. package/dist/configurators/claude.js.map +1 -1
  25. package/dist/configurators/codex.d.ts +4 -3
  26. package/dist/configurators/codex.d.ts.map +1 -1
  27. package/dist/configurators/codex.js +10 -10
  28. package/dist/configurators/codex.js.map +1 -1
  29. package/dist/configurators/index.d.ts +9 -3
  30. package/dist/configurators/index.d.ts.map +1 -1
  31. package/dist/configurators/index.js +95 -17
  32. package/dist/configurators/index.js.map +1 -1
  33. package/dist/configurators/shared.d.ts +13 -0
  34. package/dist/configurators/shared.d.ts.map +1 -1
  35. package/dist/configurators/shared.js +25 -0
  36. package/dist/configurators/shared.js.map +1 -1
  37. package/dist/configurators/workflow.d.ts +15 -1
  38. package/dist/configurators/workflow.d.ts.map +1 -1
  39. package/dist/configurators/workflow.js +23 -5
  40. package/dist/configurators/workflow.js.map +1 -1
  41. package/dist/configurators/zcode.d.ts +1 -1
  42. package/dist/configurators/zcode.d.ts.map +1 -1
  43. package/dist/configurators/zcode.js +2 -2
  44. package/dist/configurators/zcode.js.map +1 -1
  45. package/dist/constants/paths.d.ts +4 -0
  46. package/dist/constants/paths.d.ts.map +1 -1
  47. package/dist/constants/paths.js +4 -0
  48. package/dist/constants/paths.js.map +1 -1
  49. package/dist/migrations/manifests/0.4.0.json +9 -0
  50. package/dist/migrations/manifests/0.5.0.json +9 -0
  51. package/dist/templates/atw/config.yaml +7 -6
  52. package/dist/templates/atw/index.d.ts +10 -1
  53. package/dist/templates/atw/index.d.ts.map +1 -1
  54. package/dist/templates/atw/index.js +11 -1
  55. package/dist/templates/atw/index.js.map +1 -1
  56. package/dist/templates/atw/scripts/add_session.py +34 -13
  57. package/dist/templates/atw/scripts/common/active_task.py +2 -2
  58. package/dist/templates/atw/scripts/common/config.py +13 -0
  59. package/dist/templates/atw/scripts/common/developer.py +15 -4
  60. package/dist/templates/atw/scripts/common/noob_mode.py +35 -15
  61. package/dist/templates/atw/scripts/common/packages_context.py +16 -3
  62. package/dist/templates/atw/scripts/common/session_context.py +77 -19
  63. package/dist/templates/atw/scripts/common/task_store.py +78 -27
  64. package/dist/templates/atw/scripts/noob_mode.py +16 -4
  65. package/dist/templates/atw/scripts/task.py +99 -38
  66. package/dist/templates/atw/workflow-general.md +554 -0
  67. package/dist/templates/atw/workflow.md +49 -19
  68. package/dist/templates/codex/hooks/session-start.py +69 -18
  69. package/dist/templates/common/bundled-skills/atw-implement/SKILL.md +8 -0
  70. package/dist/templates/common/bundled-skills/atw-implement-all/SKILL.md +93 -0
  71. package/dist/templates/common/bundled-skills/atw-implement-all/agents/openai.yaml +5 -0
  72. package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw-local.md +3 -1
  73. package/dist/templates/common/bundled-skills/atw-init-repo/references/issue-tracker-atw.md +3 -1
  74. package/dist/templates/common/bundled-skills/atw-noob-mode/SKILL.md +7 -5
  75. package/dist/templates/common/bundled-skills/atw-tickets/SKILL.md +2 -0
  76. package/dist/templates/common/commands/continue.md +1 -1
  77. package/dist/templates/common/commands/start.md +43 -0
  78. package/dist/templates/copilot/hooks/session-start.py +69 -18
  79. package/dist/templates/dsh/DSH.md +43 -0
  80. package/dist/templates/markdown/agents.md +11 -0
  81. package/dist/templates/markdown/index.d.ts +9 -0
  82. package/dist/templates/markdown/index.d.ts.map +1 -1
  83. package/dist/templates/markdown/index.js +14 -1
  84. package/dist/templates/markdown/index.js.map +1 -1
  85. package/dist/templates/markdown/spec/conventions.md.txt +13 -0
  86. package/dist/templates/opencode/lib/atw-context.js +19 -0
  87. package/dist/templates/opencode/lib/session-utils.js +79 -24
  88. package/dist/templates/opencode/plugins/inject-workflow-state.js +18 -8
  89. package/dist/templates/pi/extensions/atw/index.ts.txt +20 -9
  90. package/dist/templates/shared-hooks/inject-shell-session-context.py +3 -3
  91. package/dist/templates/shared-hooks/inject-workflow-state.py +23 -4
  92. package/dist/templates/shared-hooks/session-start.py +70 -22
  93. package/dist/templates/snow/SNOW.md +31 -1
  94. package/dist/templates/snow/hooks/write-atw-context.py +64 -0
  95. package/dist/utils/manifest-prune.d.ts +2 -1
  96. package/dist/utils/manifest-prune.d.ts.map +1 -1
  97. package/dist/utils/manifest-prune.js +10 -7
  98. package/dist/utils/manifest-prune.js.map +1 -1
  99. package/dist/utils/optional-skills.d.ts.map +1 -1
  100. package/dist/utils/optional-skills.js +2 -1
  101. package/dist/utils/optional-skills.js.map +1 -1
  102. package/dist/utils/project-mode.d.ts +30 -0
  103. package/dist/utils/project-mode.d.ts.map +1 -0
  104. package/dist/utils/project-mode.js +86 -0
  105. package/dist/utils/project-mode.js.map +1 -0
  106. package/dist/utils/workflow-resolver.d.ts +9 -5
  107. package/dist/utils/workflow-resolver.d.ts.map +1 -1
  108. package/dist/utils/workflow-resolver.js +25 -11
  109. package/dist/utils/workflow-resolver.js.map +1 -1
  110. package/package.json +3 -3
@@ -1,3 +1,4 @@
1
+ {{^MODE_GENERAL}}
1
2
  # Start Session
2
3
 
3
4
  Initialize an ATW-managed development session. This platform has no session-start hook, so manually load the equivalent compact context by following these steps.
@@ -66,3 +67,45 @@ Load the step detail before acting on it:
66
67
  | Learned something worth capturing | `atw-update-spec` |
67
68
 
68
69
  Full rules + anti-rationalization table in `.atw/workflow.md`.
70
+ {{/MODE_GENERAL}}
71
+ {{#MODE_GENERAL}}
72
+ # Start Session
73
+
74
+ Load this project's ATW context. This platform has no session-start hook, so do it by hand with the steps below.
75
+
76
+ ---
77
+
78
+ ## Step 1: Current state
79
+ Who you are working with, the current task, unfinished tasks, where the journal is, and the project's conventions.
80
+
81
+ ```bash
82
+ {{PYTHON_CMD}} ./.atw/scripts/get_context.py
83
+ ```
84
+
85
+ If this output includes a line beginning `ATW update available:`, copy the full line verbatim when summarizing session context. Do not shorten operational command hints.
86
+
87
+ ## Step 2: Workflow overview
88
+ How work goes in this project, and what to do at each task status.
89
+
90
+ ```bash
91
+ {{PYTHON_CMD}} ./.atw/scripts/get_context.py --mode phase
92
+ ```
93
+
94
+ Full guide in `.atw/workflow.md` (read on demand).
95
+
96
+ ## Step 3: Conventions
97
+ `.atw/spec/conventions.md` is the one place this project keeps what the user asked for more than once. Step 1 printed it inside `<conventions>`: follow it in every reply. If that block was missing, read the file itself:
98
+
99
+ ```bash
100
+ cat .atw/spec/conventions.md
101
+ ```
102
+
103
+ ## Step 4: Decide next action
104
+ Step 1 gave you the current task and its `status`; Step 2 told you what the workflow does at that status. Do that. With no current task, do what the workflow says for a session that has none.
105
+
106
+ To read one step of the workflow in full:
107
+
108
+ ```bash
109
+ {{PYTHON_CMD}} ./.atw/scripts/get_context.py --mode phase --step <X.Y> --platform {{CLI_FLAG}}
110
+ ```
111
+ {{/MODE_GENERAL}}
@@ -131,6 +131,24 @@ def read_file(path: Path, fallback: str = "") -> str:
131
131
  return fallback
132
132
 
133
133
 
134
+ def _conventions_context(project_dir: Path) -> str:
135
+ """The `<conventions>` block of a `general` project, else "".
136
+
137
+ A general project has one flat conventions file instead of per-package,
138
+ per-layer guideline indexes. An install whose scripts predate the helper is
139
+ treated as a dev project.
140
+ """
141
+ scripts_dir = project_dir / ".atw" / "scripts"
142
+ if str(scripts_dir) not in sys.path:
143
+ sys.path.insert(0, str(scripts_dir))
144
+ try:
145
+ from common.session_context import conventions_context # type: ignore[import-not-found]
146
+
147
+ return conventions_context(project_dir)
148
+ except Exception:
149
+ return ""
150
+
151
+
134
152
  def _resolve_context_key(project_dir: Path, hook_input: dict) -> str | None:
135
153
  scripts_dir = project_dir / ".atw" / "scripts"
136
154
  if str(scripts_dir) not in sys.path:
@@ -202,9 +220,20 @@ def _resolve_task_dir(atw_dir: Path, task_ref: str) -> Path:
202
220
  return atw_dir / "tasks" / path_obj
203
221
 
204
222
 
205
- def _get_task_status(atw_dir: Path, hook_input: dict) -> str:
223
+ def _get_task_status(atw_dir: Path, hook_input: dict, general: bool = False) -> str:
224
+ """Compact active-task status and next action.
225
+
226
+ In a `general` project the AI decides on its own whether a request gets a
227
+ task, so the no-task line defers to the workflow instead of telling it to
228
+ ask first.
229
+ """
206
230
  active = _resolve_active_task(atw_dir, hook_input)
207
231
  if not active.task_path:
232
+ if general:
233
+ return (
234
+ "Status: NO ACTIVE TASK\n"
235
+ "Next: Do what <atw-workflow> above says for a session with no current task."
236
+ )
208
237
  return (
209
238
  "Status: NO ACTIVE TASK\n"
210
239
  "Next: Classify the current turn and ask for task-creation consent "
@@ -231,6 +260,13 @@ def _get_task_status(atw_dir: Path, hook_input: dict) -> str:
231
260
  task_status = task_data.get("status", "unknown")
232
261
 
233
262
  if task_status == "completed":
263
+ if general:
264
+ # general 的任务不记分支;在有远端的仓库里不带这个开关归档会被拒绝。
265
+ return (
266
+ f"Status: COMPLETED\nTask: {task_title}\n"
267
+ "Next: The archive was interrupted. Finish it with "
268
+ f"`python3 ./.atw/scripts/task.py archive {task_dir.name} --skip-branch-validation`."
269
+ )
234
270
  return (
235
271
  f"Status: COMPLETED\nTask: {task_title}\n"
236
272
  f"Next: Archive with `python3 ./.atw/scripts/task.py archive {task_dir.name}` "
@@ -248,6 +284,12 @@ def _get_task_status(atw_dir: Path, hook_input: dict) -> str:
248
284
  if not has_prd:
249
285
  # 这条不以任务状态为门,任何阶段缺 prd.md 都会进——所以状态名要照实报,
250
286
  # 不能像以前那样硬编码成一个词表里已经没有的阶段。
287
+ if general:
288
+ # general 不装头脑风暴技能,prd.md 由 AI 照流程自己写。
289
+ return (
290
+ f"Status: {task_status.upper()}\nTask: {task_title}\nPresent: {present_line}\n"
291
+ "Next: Write prd.md as <atw-workflow> above says before task.py start."
292
+ )
251
293
  return (
252
294
  f"Status: {task_status.upper()}\nTask: {task_title}\nPresent: {present_line}\n"
253
295
  "Next: Load atw-brainstorm and write prd.md before task.py start."
@@ -280,6 +322,9 @@ def _run_git(repo_root: Path, args: list[str]) -> str:
280
322
 
281
323
 
282
324
  def _format_git_state(repo_root: Path) -> str:
325
+ # 普通文件夹没有分支,也谈不上干净:照实说,不编一个状态出来。
326
+ if _run_git(repo_root, ["rev-parse", "--is-inside-work-tree"]) != "true":
327
+ return "Git: not a git repository."
283
328
  branch = _run_git(repo_root, ["branch", "--show-current"]) or "(detached)"
284
329
  dirty_lines = [
285
330
  line for line in _run_git(repo_root, ["status", "--porcelain"]).splitlines()
@@ -452,7 +497,10 @@ def main() -> None:
452
497
  configure_project_encoding(project_dir)
453
498
 
454
499
  atw_dir = project_dir / ".atw"
455
- spec_index_paths = _collect_spec_index_paths(atw_dir)
500
+ # A general project keeps no guideline indexes — whatever a switch from dev
501
+ # left under `.atw/spec/` is not offered there.
502
+ conventions = _conventions_context(project_dir)
503
+ spec_index_paths = [] if conventions else _collect_spec_index_paths(atw_dir)
456
504
 
457
505
  output = StringIO()
458
506
 
@@ -470,24 +518,27 @@ ATW compact SessionStart context. Use it to orient the session; load details on
470
518
  output.write(_build_workflow_toc(atw_dir / "workflow.md"))
471
519
  output.write("\n</atw-workflow>\n\n")
472
520
 
473
- output.write("<guidelines>\n")
474
- output.write(
475
- "Task context order for implementation/check: jsonl entries -> `prd.md`.\n\n"
476
- )
521
+ if conventions:
522
+ output.write(f"{conventions}\n\n")
523
+ else:
524
+ output.write("<guidelines>\n")
525
+ output.write(
526
+ "Task context order for implementation/check: jsonl entries -> `prd.md`.\n\n"
527
+ )
477
528
 
478
- if spec_index_paths:
479
- output.write("## Available indexes (read on demand)\n")
480
- for p in spec_index_paths:
481
- output.write(f"- {p}\n")
482
- output.write("\n")
483
-
484
- output.write(
485
- "Discover more via: "
486
- "`python3 ./.atw/scripts/get_context.py --mode packages`\n"
487
- )
488
- output.write("</guidelines>\n\n")
529
+ if spec_index_paths:
530
+ output.write("## Available indexes (read on demand)\n")
531
+ for p in spec_index_paths:
532
+ output.write(f"- {p}\n")
533
+ output.write("\n")
534
+
535
+ output.write(
536
+ "Discover more via: "
537
+ "`python3 ./.atw/scripts/get_context.py --mode packages`\n"
538
+ )
539
+ output.write("</guidelines>\n\n")
489
540
 
490
- task_status = _get_task_status(atw_dir, hook_input)
541
+ task_status = _get_task_status(atw_dir, hook_input, general=bool(conventions))
491
542
  output.write(f"<task-status>\n{task_status}\n</task-status>\n\n")
492
543
 
493
544
  output.write("""<ready>
@@ -4,6 +4,7 @@ dsh is a **class-2 pull-based** ATW host: no session-start hook auto-injects
4
4
  workflow context, so the agent loads the ATW skills on demand through its
5
5
  skill-loader tool.
6
6
 
7
+ {{^MODE_GENERAL}}
7
8
  | Capability | Status |
8
9
  | --- | --- |
9
10
  | Skills (`.agents/skills/atw-*/SKILL.md`) | Works — dsh discovers this shared root natively |
@@ -44,6 +45,46 @@ In dsh:
44
45
  - `.atw/` — specs, tasks, workspace memory, and the shared scripts the
45
46
  skills invoke (`get_context.py`, `task.py`, ...).
46
47
 
48
+ {{/MODE_GENERAL}}
49
+ {{#MODE_GENERAL}}
50
+ This project is in ATW's `general` mode, for work that is not code: you say
51
+ what you want in your own words, and the workflow in `.atw/workflow.md` has
52
+ the agent do the rest.
53
+
54
+ | Capability | Status |
55
+ | --- | --- |
56
+ | Skills (`.agents/skills/atw-*/SKILL.md`) | Works — dsh discovers this shared root natively |
57
+ | Entry skill (`.dsh/skills/atw-start/SKILL.md`) | Works — dsh's own project skill root (highest rank) |
58
+ | Context hooks | None — pull-based: skills read `.atw/` files directly |
59
+ | Sub-agents | None |
60
+
61
+ ## Quick start
62
+
63
+ ```bash
64
+ atw init --dsh --mode general -u your-name
65
+ dsh web # or: dsh --profile headless "plan ..."
66
+ ```
67
+
68
+ In dsh:
69
+
70
+ 1. Open a session in the project root and say what you want done. The agent
71
+ should first load the `atw-start` skill, which reads the current task,
72
+ the workflow and the project's conventions from `.atw/`.
73
+ 2. There is nothing else to ask for by name. The workflow has the agent keep
74
+ the task record, show you what it wrote down before it starts, check the
75
+ result with you, and file it when you accept.
76
+
77
+ ## File map
78
+
79
+ - `.agents/skills/` — the six skills of `general` mode: `atw-interview`,
80
+ `atw-research`, `atw-session-insight`, `atw-noob-mode`, `atw-askme`,
81
+ `atw-handoff`.
82
+ - `.dsh/skills/` — the dsh-private entry skill `atw-start`.
83
+ - `.atw/` — tasks, the conventions file (`.atw/spec/conventions.md`),
84
+ workspace memory, and the shared scripts the skills invoke
85
+ (`get_context.py`, `task.py`, ...).
86
+
87
+ {{/MODE_GENERAL}}
47
88
  ## Notes
48
89
 
49
90
  - Skill scripts pass `--platform dsh` to `get_context.py`; the value is used
@@ -51,6 +92,8 @@ In dsh:
51
92
  - The shipped `minimal` agent preset composes only `bash` +
52
93
  `str_replace_editor`; the default presets include `web_search` and the
53
94
  filesystem/terminal tools the skills assume.
95
+ {{^MODE_GENERAL}}
54
96
  - dsh has no project-level sub-agent definition surface, so ATW ships no
55
97
  `atw-implement-agent` / `atw-review` agent prompts
56
98
  here — the workflow skills run those phases inline in the main session.
99
+ {{/MODE_GENERAL}}
@@ -5,16 +5,27 @@ These instructions are for AI assistants working in this project.
5
5
 
6
6
  This project is managed by ATW. The working knowledge you need lives under `.atw/`:
7
7
 
8
+ {{^MODE_GENERAL}}
8
9
  - `.atw/workflow.md` — development phases, when to create tasks, skill routing
9
10
  - `.atw/spec/` — package- and layer-scoped coding guidelines (read before writing code in a given layer)
11
+ {{/MODE_GENERAL}}
12
+ {{#MODE_GENERAL}}
13
+ - `.atw/workflow.md` — how work goes here: when to keep a task, what to write down for the user to confirm, how to check and file the result
14
+ - `.atw/spec/conventions.md` — what the user asked for more than once (follow it in every reply; add to it when they correct you)
15
+ {{/MODE_GENERAL}}
10
16
  - `.atw/workspace/` — per-developer journals and session traces
11
17
  - `.atw/tasks/` — active and archived tasks (PRDs, research, jsonl context)
12
18
 
19
+ {{^MODE_GENERAL}}
13
20
  ATW commands are written by bare name throughout `.atw/` — `start`, `continue`, `finish-work`. The prefix that invokes them is your platform's, not ATW's: Claude Code uses `/atw:`, Cursor and Pi `/atw-`, Codex `$`, Copilot a plain `/`, and a few hosts expose them as skills instead. Prefer an available ATW command over manual steps; not every platform exposes every command.
14
21
 
15
22
  If you're using Codex or another agent-capable tool, additional project-scoped helpers may live in:
16
23
  - `.agents/skills/` — reusable ATW skills
17
24
  - `.codex/agents/` — optional custom subagents
25
+ {{/MODE_GENERAL}}
26
+ {{#MODE_GENERAL}}
27
+ The user moves the work along by saying what they want, not by running ATW commands: you create, advance and archive tasks yourself, as the workflow says. The one ATW command is `start`, which loads this context on platforms that do not load it when a session begins; the prefix that invokes it is your platform's, not ATW's.
28
+ {{/MODE_GENERAL}}
18
29
 
19
30
  Managed by ATW. Edits outside this block are preserved; edits inside may be overwritten by a future `atw update`.
20
31
 
@@ -4,6 +4,14 @@
4
4
  * These are GENERIC templates for new projects.
5
5
  * Structure templates use .md.txt extension as they are generic templates.
6
6
  */
7
+ import type { ProjectMode } from "../../utils/project-mode.js";
8
+ /**
9
+ * 某个项目模式的根 `AGENTS.md`,即该模式下这个文件的 ATW 原版内容。
10
+ *
11
+ * 初始化、升级和模式切换都从这里取,所以三处给出的是同一份。
12
+ */
13
+ export declare function agentsMdFor(mode: ProjectMode): string;
14
+ /** dev 项目(以及没有模式配置项的老项目)的 `AGENTS.md`。 */
7
15
  export declare const agentsMdContent: string;
8
16
  export declare const workspaceIndexContent: string;
9
17
  export declare const agentProgressIndexContent: string;
@@ -25,4 +33,5 @@ export declare const frontendDesignSystemContent: string;
25
33
  export declare const guidesIndexContent: string;
26
34
  export declare const guidesCrossLayerThinkingGuideContent: string;
27
35
  export declare const guidesCodeReuseThinkingGuideContent: string;
36
+ export declare const conventionsContent: string;
28
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/markdown/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqBH,eAAO,MAAM,eAAe,EAAE,MAAuC,CAAC;AAGtE,eAAO,MAAM,qBAAqB,EAAE,MACK,CAAC;AAG1C,eAAO,MAAM,yBAAyB,QAAwB,CAAC;AAG/D,eAAO,MAAM,wBAAwB,EAAE,MACH,CAAC;AAQrC,eAAO,MAAM,mBAAmB,EAAE,MAEjC,CAAC;AACF,eAAO,MAAM,gCAAgC,EAAE,MAE9C,CAAC;AACF,eAAO,MAAM,gCAAgC,EAAE,MAE9C,CAAC;AACF,eAAO,MAAM,+BAA+B,EAAE,MAE7C,CAAC;AACF,eAAO,MAAM,+BAA+B,EAAE,MAE7C,CAAC;AACF,eAAO,MAAM,2BAA2B,EAAE,MAEzC,CAAC;AAGF,eAAO,MAAM,oBAAoB,EAAE,MAElC,CAAC;AACF,eAAO,MAAM,iCAAiC,EAAE,MAE/C,CAAC;AACF,eAAO,MAAM,yBAAyB,EAAE,MAEvC,CAAC;AACF,eAAO,MAAM,6BAA6B,EAAE,MAE3C,CAAC;AACF,eAAO,MAAM,kCAAkC,EAAE,MAEhD,CAAC;AACF,eAAO,MAAM,gCAAgC,EAAE,MAE9C,CAAC;AACF,eAAO,MAAM,8BAA8B,EAAE,MAE5C,CAAC;AACF,eAAO,MAAM,2BAA2B,EAAE,MAEzC,CAAC;AAGF,eAAO,MAAM,kBAAkB,EAAE,MAEhC,CAAC;AACF,eAAO,MAAM,oCAAoC,EAAE,MAElD,CAAC;AACF,eAAO,MAAM,mCAAmC,EAAE,MAEjD,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/markdown/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAmB/D;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAErD;AAED,0CAA0C;AAC1C,eAAO,MAAM,eAAe,EAAE,MAA2B,CAAC;AAG1D,eAAO,MAAM,qBAAqB,EAAE,MACK,CAAC;AAG1C,eAAO,MAAM,yBAAyB,QAAwB,CAAC;AAG/D,eAAO,MAAM,wBAAwB,EAAE,MACH,CAAC;AAQrC,eAAO,MAAM,mBAAmB,EAAE,MAEjC,CAAC;AACF,eAAO,MAAM,gCAAgC,EAAE,MAE9C,CAAC;AACF,eAAO,MAAM,gCAAgC,EAAE,MAE9C,CAAC;AACF,eAAO,MAAM,+BAA+B,EAAE,MAE7C,CAAC;AACF,eAAO,MAAM,+BAA+B,EAAE,MAE7C,CAAC;AACF,eAAO,MAAM,2BAA2B,EAAE,MAEzC,CAAC;AAGF,eAAO,MAAM,oBAAoB,EAAE,MAElC,CAAC;AACF,eAAO,MAAM,iCAAiC,EAAE,MAE/C,CAAC;AACF,eAAO,MAAM,yBAAyB,EAAE,MAEvC,CAAC;AACF,eAAO,MAAM,6BAA6B,EAAE,MAE3C,CAAC;AACF,eAAO,MAAM,kCAAkC,EAAE,MAEhD,CAAC;AACF,eAAO,MAAM,gCAAgC,EAAE,MAE9C,CAAC;AACF,eAAO,MAAM,8BAA8B,EAAE,MAE5C,CAAC;AACF,eAAO,MAAM,2BAA2B,EAAE,MAEzC,CAAC;AAGF,eAAO,MAAM,kBAAkB,EAAE,MAEhC,CAAC;AACF,eAAO,MAAM,oCAAoC,EAAE,MAElD,CAAC;AACF,eAAO,MAAM,mCAAmC,EAAE,MAEjD,CAAC;AAGF,eAAO,MAAM,kBAAkB,EAAE,MAEhC,CAAC"}
@@ -7,6 +7,7 @@
7
7
  import { readFileSync } from "node:fs";
8
8
  import { dirname, join } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
+ import { resolveModeBlocks } from "../../configurators/shared.js";
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = dirname(__filename);
12
13
  /**
@@ -19,7 +20,17 @@ function readLocalTemplate(filename) {
19
20
  // =============================================================================
20
21
  // Root files for new projects
21
22
  // =============================================================================
22
- export const agentsMdContent = readLocalTemplate("agents.md");
23
+ const agentsMdTemplate = readLocalTemplate("agents.md");
24
+ /**
25
+ * 某个项目模式的根 `AGENTS.md`,即该模式下这个文件的 ATW 原版内容。
26
+ *
27
+ * 初始化、升级和模式切换都从这里取,所以三处给出的是同一份。
28
+ */
29
+ export function agentsMdFor(mode) {
30
+ return resolveModeBlocks(agentsMdTemplate, mode);
31
+ }
32
+ /** dev 项目(以及没有模式配置项的老项目)的 `AGENTS.md`。 */
33
+ export const agentsMdContent = agentsMdFor("dev");
23
34
  // Workspace index template (developer work records)
24
35
  export const workspaceIndexContent = readLocalTemplate("workspace-index.md");
25
36
  // Backwards compatibility alias
@@ -50,4 +61,6 @@ export const frontendDesignSystemContent = readLocalTemplate("spec/frontend/desi
50
61
  export const guidesIndexContent = readLocalTemplate("spec/guides/index.md.txt");
51
62
  export const guidesCrossLayerThinkingGuideContent = readLocalTemplate("spec/guides/cross-layer-thinking-guide.md.txt");
52
63
  export const guidesCodeReuseThinkingGuideContent = readLocalTemplate("spec/guides/code-reuse-thinking-guide.md.txt");
64
+ // general 项目的约定位置:一个平铺的文件,不分包、不分层
65
+ export const conventionsContent = readLocalTemplate("spec/conventions.md.txt");
53
66
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/markdown/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,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;;GAEG;AACH,SAAS,iBAAiB,CAAC,QAAgB;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC3C,OAAO,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC;AAED,gFAAgF;AAChF,8BAA8B;AAC9B,gFAAgF;AAEhF,MAAM,CAAC,MAAM,eAAe,GAAW,iBAAiB,CAAC,WAAW,CAAC,CAAC;AAEtE,oDAAoD;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAChC,iBAAiB,CAAC,oBAAoB,CAAC,CAAC;AAE1C,gCAAgC;AAChC,MAAM,CAAC,MAAM,yBAAyB,GAAG,qBAAqB,CAAC;AAE/D,2DAA2D;AAC3D,MAAM,CAAC,MAAM,wBAAwB,GACnC,iBAAiB,CAAC,eAAe,CAAC,CAAC;AAErC,gFAAgF;AAChF,0DAA0D;AAC1D,wEAAwE;AACxE,gFAAgF;AAEhF,uCAAuC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAW,iBAAiB,CAC1D,2BAA2B,CAC5B,CAAC;AACF,MAAM,CAAC,MAAM,gCAAgC,GAAW,iBAAiB,CACvE,yCAAyC,CAC1C,CAAC;AACF,MAAM,CAAC,MAAM,gCAAgC,GAAW,iBAAiB,CACvE,yCAAyC,CAC1C,CAAC;AACF,MAAM,CAAC,MAAM,+BAA+B,GAAW,iBAAiB,CACtE,wCAAwC,CACzC,CAAC;AACF,MAAM,CAAC,MAAM,+BAA+B,GAAW,iBAAiB,CACtE,wCAAwC,CACzC,CAAC;AACF,MAAM,CAAC,MAAM,2BAA2B,GAAW,iBAAiB,CAClE,oCAAoC,CACrC,CAAC;AAEF,wCAAwC;AACxC,MAAM,CAAC,MAAM,oBAAoB,GAAW,iBAAiB,CAC3D,4BAA4B,CAC7B,CAAC;AACF,MAAM,CAAC,MAAM,iCAAiC,GAAW,iBAAiB,CACxE,0CAA0C,CAC3C,CAAC;AACF,MAAM,CAAC,MAAM,yBAAyB,GAAW,iBAAiB,CAChE,kCAAkC,CACnC,CAAC;AACF,MAAM,CAAC,MAAM,6BAA6B,GAAW,iBAAiB,CACpE,sCAAsC,CACvC,CAAC;AACF,MAAM,CAAC,MAAM,kCAAkC,GAAW,iBAAiB,CACzE,2CAA2C,CAC5C,CAAC;AACF,MAAM,CAAC,MAAM,gCAAgC,GAAW,iBAAiB,CACvE,yCAAyC,CAC1C,CAAC;AACF,MAAM,CAAC,MAAM,8BAA8B,GAAW,iBAAiB,CACrE,uCAAuC,CACxC,CAAC;AACF,MAAM,CAAC,MAAM,2BAA2B,GAAW,iBAAiB,CAClE,oCAAoC,CACrC,CAAC;AAEF,mBAAmB;AACnB,MAAM,CAAC,MAAM,kBAAkB,GAAW,iBAAiB,CACzD,0BAA0B,CAC3B,CAAC;AACF,MAAM,CAAC,MAAM,oCAAoC,GAAW,iBAAiB,CAC3E,+CAA+C,CAChD,CAAC;AACF,MAAM,CAAC,MAAM,mCAAmC,GAAW,iBAAiB,CAC1E,8CAA8C,CAC/C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/markdown/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ # 约定 / Conventions
2
+
3
+ 这个项目的使用者不止一次提过的要求:东西要怎么写、怎么起名、怎么排版、怎么检查,以及不想要什么。AI 每次开始对话都会先读这个文件,并照着做。
4
+
5
+ AI 自己也会往里记。你纠正了它,或者把某个要求又说了一遍,它就在这里记下一行,这样同样的话不用说第二遍。哪一条不再适用了,你可以自己改掉或删掉。
6
+
7
+ What the user of this project has asked for more than once: how they want things written, named, laid out or checked, and what they do not want. The AI reads this file at the start of every session and follows it.
8
+
9
+ The AI adds to it as well. When the user corrects it, or repeats a preference, it writes that down here as one short line, so nobody has to say it again. Edit or delete any line yourself once it stops being true.
10
+
11
+ ## 说定的事 / What we agreed
12
+
13
+ (还没有。/ Nothing yet.)
@@ -144,6 +144,25 @@ function unquoteYaml(s) {
144
144
  return s
145
145
  }
146
146
 
147
+ /**
148
+ * Whether the project is in `general` mode. Mirrors
149
+ * `common.config.get_project_mode()`: a top-level `mode:` key in
150
+ * .atw/config.yaml; a missing key or any other value is `dev`.
151
+ */
152
+ export function isGeneralProject(directory) {
153
+ let text
154
+ try {
155
+ text = readFileSync(join(directory, ".atw", "config.yaml"), "utf-8")
156
+ } catch {
157
+ return false
158
+ }
159
+ for (const line of text.split(/\r?\n/)) {
160
+ const m = line.match(/^mode\s*:\s*(.*)$/)
161
+ if (m) return unquoteYaml(stripInlineComment(m[1]).trim()) === "general"
162
+ }
163
+ return false
164
+ }
165
+
147
166
  /**
148
167
  * Line-based parser for ONLY the `context_injection:` block of
149
168
  * `.atw/config.yaml`. Not a general YAML parser — mirrors
@@ -3,7 +3,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from "fs"
3
3
  import { join } from "path"
4
4
  import { execFileSync } from "child_process"
5
5
  import { platform } from "os"
6
- import { debugLog } from "./atw-context.js"
6
+ import { debugLog, isGeneralProject } from "./atw-context.js"
7
7
 
8
8
  const PYTHON_CMD = platform() === "win32" ? "python" : "python3"
9
9
 
@@ -18,10 +18,22 @@ The acknowledgment must not alter the language used for the remainder of the res
18
18
  This notice is one-shot: do not repeat it after the first visible assistant reply in this session.
19
19
  </first-reply-notice>`
20
20
 
21
- function getTaskStatus(ctx, platformInput = null) {
21
+ /**
22
+ * Compact active-task status and next action. In a `general` project the AI
23
+ * decides on its own whether a request gets a task, so the no-task line defers
24
+ * to the workflow instead of telling it to ask first — keep the wording in step
25
+ * with `_get_task_status` in the shared session-start hook.
26
+ */
27
+ function getTaskStatus(ctx, platformInput = null, general = false) {
22
28
  const active = ctx.getActiveTask(platformInput)
23
29
  const taskRef = active.taskPath
24
30
  if (!taskRef) {
31
+ if (general) {
32
+ return (
33
+ "Status: NO ACTIVE TASK\n" +
34
+ "Next-Action: Do what <atw-workflow> above says for a session with no current task."
35
+ )
36
+ }
25
37
  return (
26
38
  "Status: NO ACTIVE TASK\n" +
27
39
  "Next-Action: Classify the current turn before creating any ATW task. " +
@@ -50,6 +62,11 @@ function getTaskStatus(ctx, platformInput = null) {
50
62
  const taskStatus = taskData.status || "unknown"
51
63
 
52
64
  if (taskStatus === "completed") {
65
+ if (general) {
66
+ // A general task records no branch; without the switch the archive is
67
+ // refused in a repository that has a remote.
68
+ return `Status: COMPLETED\nTask: ${taskTitle}\nNext-Action: The archive was interrupted — re-run \`task.py archive <task> --skip-branch-validation\` to finish it.`
69
+ }
53
70
  return `Status: COMPLETED\nTask: ${taskTitle}\nNext-Action: The archive was interrupted — re-run \`task.py archive <task>\` to finish it. If the working tree is dirty, commit or set the task back to \`implement\` first; there is no wrap-up commit step.`
54
71
  }
55
72
 
@@ -253,11 +270,16 @@ function buildCompactCurrentState(ctx, platformInput, specIndexPaths) {
253
270
  const lines = []
254
271
  lines.push(`Developer: ${readDeveloper(directory)}`)
255
272
 
256
- const branch = runGit(directory, ["branch", "--show-current"]) || "(detached)"
257
- const dirtyCount = runGit(directory, ["status", "--porcelain"])
258
- .split(/\r?\n/)
259
- .filter(line => line.trim()).length
260
- lines.push(`Git: branch ${branch}; ${dirtyCount === 0 ? "clean" : `dirty ${dirtyCount} paths`}.`)
273
+ // 普通文件夹没有分支,也谈不上干净:照实说,不编一个状态出来。
274
+ if (runGit(directory, ["rev-parse", "--is-inside-work-tree"]) !== "true") {
275
+ lines.push("Git: not a git repository.")
276
+ } else {
277
+ const branch = runGit(directory, ["branch", "--show-current"]) || "(detached)"
278
+ const dirtyCount = runGit(directory, ["status", "--porcelain"])
279
+ .split(/\r?\n/)
280
+ .filter(line => line.trim()).length
281
+ lines.push(`Git: branch ${branch}; ${dirtyCount === 0 ? "clean" : `dirty ${dirtyCount} paths`}.`)
282
+ }
261
283
 
262
284
  const active = ctx.getActiveTask(platformInput)
263
285
  if (active.taskPath) {
@@ -312,6 +334,32 @@ function buildCompactCurrentState(ctx, platformInput, specIndexPaths) {
312
334
  return lines.join("\n")
313
335
  }
314
336
 
337
+ const CONVENTIONS_PATH = ".atw/spec/conventions.md"
338
+
339
+ /**
340
+ * The <conventions> block of a `general` project, else "". Mirrors
341
+ * conventions_context() in .atw/scripts/common/session_context.py — keep the two
342
+ * in step: a general project has one flat conventions file instead of
343
+ * per-package, per-layer guideline indexes, and always gets the block, so the
344
+ * AI knows where the first entry goes.
345
+ */
346
+ function buildConventionsContext(directory) {
347
+ if (!isGeneralProject(directory)) return ""
348
+ const header =
349
+ `Source: ${CONVENTIONS_PATH} — what this project's user asked for more than ` +
350
+ "once. Follow it in every reply."
351
+ let body = ""
352
+ try {
353
+ body = readFileSync(join(directory, CONVENTIONS_PATH), "utf-8").trim()
354
+ } catch {
355
+ // Not written yet.
356
+ }
357
+ if (!body) {
358
+ return `<conventions>\n${header} Nothing is written there yet.\n</conventions>`
359
+ }
360
+ return `<conventions>\n${header}\n\n${body}\n</conventions>`
361
+ }
362
+
315
363
  export function buildSessionContext(ctx, platformInput = null) {
316
364
  const directory = ctx.directory
317
365
  const contextKey = typeof ctx.getContextKey === "function"
@@ -320,7 +368,10 @@ export function buildSessionContext(ctx, platformInput = null) {
320
368
 
321
369
  const config = loadAtwConfig(directory, contextKey)
322
370
  const allowedPkgs = resolveSpecScope(config)
323
- const paths = collectSpecIndexPaths(directory, allowedPkgs)
371
+ // A general project keeps no guideline indexes — whatever a switch from dev
372
+ // left under `.atw/spec/` is not offered there.
373
+ const conventions = buildConventionsContext(directory)
374
+ const paths = conventions ? [] : collectSpecIndexPaths(directory, allowedPkgs)
324
375
 
325
376
  const parts = []
326
377
 
@@ -374,26 +425,30 @@ ATW compact SessionStart context. Use it to orient the session; load details on
374
425
  parts.push("</atw-workflow>")
375
426
  }
376
427
 
377
- parts.push("<guidelines>")
378
- parts.push(
379
- "Task context order for implementation/check: jsonl entries -> `prd.md`.\n"
380
- )
428
+ if (conventions) {
429
+ parts.push(conventions)
430
+ } else {
431
+ parts.push("<guidelines>")
432
+ parts.push(
433
+ "Task context order for implementation/check: jsonl entries -> `prd.md`.\n"
434
+ )
381
435
 
382
- if (paths.length > 0) {
383
- parts.push("## Available indexes (read on demand)")
384
- for (const p of paths) {
385
- parts.push(`- ${p}`)
436
+ if (paths.length > 0) {
437
+ parts.push("## Available indexes (read on demand)")
438
+ for (const p of paths) {
439
+ parts.push(`- ${p}`)
440
+ }
441
+ parts.push("")
386
442
  }
387
- parts.push("")
388
- }
389
443
 
390
- parts.push(
391
- "Discover more via: " +
392
- "`python3 ./.atw/scripts/get_context.py --mode packages`"
393
- )
394
- parts.push("</guidelines>")
444
+ parts.push(
445
+ "Discover more via: " +
446
+ "`python3 ./.atw/scripts/get_context.py --mode packages`"
447
+ )
448
+ parts.push("</guidelines>")
449
+ }
395
450
 
396
- const taskStatus = getTaskStatus(ctx, platformInput)
451
+ const taskStatus = getTaskStatus(ctx, platformInput, Boolean(conventions))
397
452
  parts.push(`<task-status>\n${taskStatus}\n</task-status>`)
398
453
 
399
454
  parts.push(`<ready>
@@ -29,7 +29,12 @@ import {
29
29
  platformInputFromMessages,
30
30
  prependEphemeralText,
31
31
  } from "../lib/context-visibility.js"
32
- import { AtwContext, debugLog, isAtwSubagent } from "../lib/atw-context.js"
32
+ import {
33
+ AtwContext,
34
+ debugLog,
35
+ isAtwSubagent,
36
+ isGeneralProject,
37
+ } from "../lib/atw-context.js"
33
38
 
34
39
  // Supports STATUS values with letters, digits, underscores, hyphens
35
40
  // (so "in-review" / "blocked-by-team" work alongside "task_error").
@@ -177,18 +182,23 @@ const NOOB_MODE_FALLBACK =
177
182
  "language, and every term, command or path explained where it appears."
178
183
 
179
184
  /**
180
- * The <noob-mode> block for a developer who switched noob mode on, else "".
181
- * Mirrors noob_mode_reminder() in .atw/scripts/common/noob_mode.py: the
182
- * switch is a `noob_mode=on` line in the gitignored .atw/.developer.
185
+ * The <noob-mode> block while noob mode is on for this developer, else "".
186
+ * Mirrors is_noob_mode_on() / noob_mode_reminder() in
187
+ * .atw/scripts/common/noob_mode.py — keep the two in step: a `noob_mode=on`
188
+ * or `noob_mode=off` line in the gitignored .atw/.developer is the
189
+ * developer's own choice and wins; without one the project mode decides
190
+ * (on for `general`, off for `dev`).
183
191
  */
184
192
  function buildNoobModeReminder(directory, templates) {
185
- let developer
193
+ let choice = null
186
194
  try {
187
- developer = readFileSync(join(directory, ".atw", ".developer"), "utf-8")
195
+ const developer = readFileSync(join(directory, ".atw", ".developer"), "utf-8")
196
+ const m = developer.match(/^noob_mode=(.*)$/m)
197
+ if (m) choice = m[1].trim() === "on"
188
198
  } catch {
189
- return ""
199
+ // No identity file: this developer never chose.
190
200
  }
191
- if (!/^noob_mode=on\s*$/m.test(developer)) return ""
201
+ if (!(choice ?? isGeneralProject(directory))) return ""
192
202
  return `<noob-mode>\n${templates.noob_mode || NOOB_MODE_FALLBACK}\n</noob-mode>`
193
203
  }
194
204
 
@@ -1249,18 +1249,24 @@ function buildStartupContext(
1249
1249
  overview: string,
1250
1250
  ): string {
1251
1251
  const workflow = workflowOverview(root, key);
1252
+ // 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.
1255
+ const ready = overview.includes("\n<conventions>\n")
1256
+ ? [
1257
+ "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
+ ]
1259
+ : [
1260
+ "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.",
1263
+ ];
1252
1264
  return [
1253
1265
  "<session-context>\nATW compact SessionStart context. Use it to orient the session; load details on demand.\n</session-context>",
1254
1266
  FIRST_REPLY_NOTICE,
1255
1267
  overview,
1256
1268
  workflow,
1257
- [
1258
- "<ready>",
1259
- "Use the current workflow state to decide whether to create, continue, or skip an ATW task.",
1260
- "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.",
1261
- "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.",
1262
- "</ready>",
1263
- ].join("\n"),
1269
+ ["<ready>", ...ready, "</ready>"].join("\n"),
1264
1270
  ]
1265
1271
  .filter(Boolean)
1266
1272
  .join("\n\n");
@@ -1983,9 +1989,14 @@ export default function atwExtension(pi: {
1983
1989
  // Events
1984
1990
  pi.on?.("session_start", (event, ctx) => {
1985
1991
  const k = getKey(event, ctx);
1986
- inheritSessionPointer(resolveRoot(ctx), k, event);
1992
+ const root = resolveRoot(ctx);
1993
+ inheritSessionPointer(root, k, event);
1994
+ // A `general` project installs no /atw-continue prompt.
1995
+ const canContinue = exists(join(root, ".pi", "prompts", "atw-continue.md"));
1987
1996
  ctx?.ui?.notify?.(
1988
- "ATW project context is available. Use /atw-start to bootstrap or /atw-continue to resume.",
1997
+ canContinue
1998
+ ? "ATW project context is available. Use /atw-start to bootstrap or /atw-continue to resume."
1999
+ : "ATW project context is available. Use /atw-start to bootstrap.",
1989
2000
  "info",
1990
2001
  );
1991
2002
  });