@chinhae/atw-cli 0.3.6 → 0.4.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.
- package/README.md +188 -15
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +25 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +154 -17
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mode.d.ts +21 -0
- package/dist/commands/mode.d.ts.map +1 -0
- package/dist/commands/mode.js +401 -0
- package/dist/commands/mode.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +20 -13
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/workflow.d.ts +17 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +9 -3
- package/dist/commands/workflow.js.map +1 -1
- package/dist/configurators/claude.d.ts +3 -2
- package/dist/configurators/claude.d.ts.map +1 -1
- package/dist/configurators/claude.js +3 -3
- package/dist/configurators/claude.js.map +1 -1
- package/dist/configurators/codex.d.ts +4 -3
- package/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +10 -10
- package/dist/configurators/codex.js.map +1 -1
- package/dist/configurators/index.d.ts +9 -3
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +95 -17
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/shared.d.ts +13 -0
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +25 -0
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/workflow.d.ts +15 -1
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +23 -5
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/configurators/zcode.d.ts +1 -1
- package/dist/configurators/zcode.d.ts.map +1 -1
- package/dist/configurators/zcode.js +2 -2
- package/dist/configurators/zcode.js.map +1 -1
- package/dist/constants/paths.d.ts +4 -0
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +4 -0
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.4.0.json +9 -0
- package/dist/templates/atw/index.d.ts +10 -1
- package/dist/templates/atw/index.d.ts.map +1 -1
- package/dist/templates/atw/index.js +11 -1
- package/dist/templates/atw/index.js.map +1 -1
- package/dist/templates/atw/scripts/add_session.py +34 -13
- package/dist/templates/atw/scripts/common/active_task.py +2 -2
- package/dist/templates/atw/scripts/common/config.py +13 -0
- package/dist/templates/atw/scripts/common/developer.py +15 -4
- package/dist/templates/atw/scripts/common/noob_mode.py +35 -15
- package/dist/templates/atw/scripts/common/packages_context.py +16 -3
- package/dist/templates/atw/scripts/common/session_context.py +77 -19
- package/dist/templates/atw/scripts/common/task_store.py +78 -27
- package/dist/templates/atw/scripts/noob_mode.py +16 -4
- package/dist/templates/atw/scripts/task.py +99 -38
- package/dist/templates/atw/workflow-general.md +554 -0
- package/dist/templates/atw/workflow.md +1 -1
- package/dist/templates/codex/hooks/session-start.py +69 -18
- package/dist/templates/common/bundled-skills/atw-noob-mode/SKILL.md +6 -4
- package/dist/templates/common/commands/start.md +43 -0
- package/dist/templates/copilot/hooks/session-start.py +69 -18
- package/dist/templates/dsh/DSH.md +43 -0
- package/dist/templates/markdown/agents.md +11 -0
- package/dist/templates/markdown/index.d.ts +9 -0
- package/dist/templates/markdown/index.d.ts.map +1 -1
- package/dist/templates/markdown/index.js +14 -1
- package/dist/templates/markdown/index.js.map +1 -1
- package/dist/templates/markdown/spec/conventions.md.txt +13 -0
- package/dist/templates/opencode/lib/atw-context.js +19 -0
- package/dist/templates/opencode/lib/session-utils.js +79 -24
- package/dist/templates/opencode/plugins/inject-workflow-state.js +18 -8
- package/dist/templates/pi/extensions/atw/index.ts.txt +20 -9
- package/dist/templates/shared-hooks/inject-shell-session-context.py +3 -3
- package/dist/templates/shared-hooks/inject-workflow-state.py +21 -2
- package/dist/templates/shared-hooks/session-start.py +70 -22
- package/dist/templates/snow/SNOW.md +30 -0
- package/dist/templates/snow/hooks/write-atw-context.py +64 -0
- package/dist/utils/manifest-prune.d.ts +2 -1
- package/dist/utils/manifest-prune.d.ts.map +1 -1
- package/dist/utils/manifest-prune.js +10 -7
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/optional-skills.d.ts.map +1 -1
- package/dist/utils/optional-skills.js +2 -1
- package/dist/utils/optional-skills.js.map +1 -1
- package/dist/utils/project-mode.d.ts +30 -0
- package/dist/utils/project-mode.d.ts.map +1 -0
- package/dist/utils/project-mode.js +86 -0
- package/dist/utils/project-mode.js.map +1 -0
- package/dist/utils/workflow-resolver.d.ts +9 -5
- package/dist/utils/workflow-resolver.d.ts.map +1 -1
- package/dist/utils/workflow-resolver.js +25 -11
- package/dist/utils/workflow-resolver.js.map +1 -1
- package/package.json +3 -3
|
@@ -132,6 +132,24 @@ def read_file(path: Path, fallback: str = "") -> str:
|
|
|
132
132
|
return fallback
|
|
133
133
|
|
|
134
134
|
|
|
135
|
+
def _conventions_context(project_dir: Path) -> str:
|
|
136
|
+
"""The `<conventions>` block of a `general` project, else "".
|
|
137
|
+
|
|
138
|
+
A general project has one flat conventions file instead of per-package,
|
|
139
|
+
per-layer guideline indexes. An install whose scripts predate the helper is
|
|
140
|
+
treated as a dev project.
|
|
141
|
+
"""
|
|
142
|
+
scripts_dir = project_dir / ".atw" / "scripts"
|
|
143
|
+
if str(scripts_dir) not in sys.path:
|
|
144
|
+
sys.path.insert(0, str(scripts_dir))
|
|
145
|
+
try:
|
|
146
|
+
from common.session_context import conventions_context # type: ignore[import-not-found]
|
|
147
|
+
|
|
148
|
+
return conventions_context(project_dir)
|
|
149
|
+
except Exception:
|
|
150
|
+
return ""
|
|
151
|
+
|
|
152
|
+
|
|
135
153
|
def _resolve_context_key(project_dir: Path, hook_input: dict) -> str | None:
|
|
136
154
|
scripts_dir = project_dir / ".atw" / "scripts"
|
|
137
155
|
if str(scripts_dir) not in sys.path:
|
|
@@ -203,9 +221,20 @@ def _resolve_task_dir(atw_dir: Path, task_ref: str) -> Path:
|
|
|
203
221
|
return atw_dir / "tasks" / path_obj
|
|
204
222
|
|
|
205
223
|
|
|
206
|
-
def _get_task_status(atw_dir: Path, hook_input: dict) -> str:
|
|
224
|
+
def _get_task_status(atw_dir: Path, hook_input: dict, general: bool = False) -> str:
|
|
225
|
+
"""Compact active-task status and next action.
|
|
226
|
+
|
|
227
|
+
In a `general` project the AI decides on its own whether a request gets a
|
|
228
|
+
task, so the no-task line defers to the workflow instead of telling it to
|
|
229
|
+
ask first.
|
|
230
|
+
"""
|
|
207
231
|
active = _resolve_active_task(atw_dir, hook_input)
|
|
208
232
|
if not active.task_path:
|
|
233
|
+
if general:
|
|
234
|
+
return (
|
|
235
|
+
"Status: NO ACTIVE TASK\n"
|
|
236
|
+
"Next: Do what <atw-workflow> above says for a session with no current task."
|
|
237
|
+
)
|
|
209
238
|
return (
|
|
210
239
|
"Status: NO ACTIVE TASK\n"
|
|
211
240
|
"Next: Classify the current turn and ask for task-creation consent "
|
|
@@ -232,6 +261,13 @@ def _get_task_status(atw_dir: Path, hook_input: dict) -> str:
|
|
|
232
261
|
task_status = task_data.get("status", "unknown")
|
|
233
262
|
|
|
234
263
|
if task_status == "completed":
|
|
264
|
+
if general:
|
|
265
|
+
# general 的任务不记分支;在有远端的仓库里不带这个开关归档会被拒绝。
|
|
266
|
+
return (
|
|
267
|
+
f"Status: COMPLETED\nTask: {task_title}\n"
|
|
268
|
+
"Next: The archive was interrupted. Finish it with "
|
|
269
|
+
f"`python3 ./.atw/scripts/task.py archive {task_dir.name} --skip-branch-validation`."
|
|
270
|
+
)
|
|
235
271
|
return (
|
|
236
272
|
f"Status: COMPLETED\nTask: {task_title}\n"
|
|
237
273
|
f"Next: Archive with `python3 ./.atw/scripts/task.py archive {task_dir.name}` "
|
|
@@ -249,6 +285,12 @@ def _get_task_status(atw_dir: Path, hook_input: dict) -> str:
|
|
|
249
285
|
if not has_prd:
|
|
250
286
|
# 这条不以任务状态为门,任何阶段缺 prd.md 都会进——所以状态名要照实报,
|
|
251
287
|
# 不能像以前那样硬编码成一个词表里已经没有的阶段。
|
|
288
|
+
if general:
|
|
289
|
+
# general 不装头脑风暴技能,prd.md 由 AI 照流程自己写。
|
|
290
|
+
return (
|
|
291
|
+
f"Status: {task_status.upper()}\nTask: {task_title}\nPresent: {present_line}\n"
|
|
292
|
+
"Next: Write prd.md as <atw-workflow> above says before task.py start."
|
|
293
|
+
)
|
|
252
294
|
return (
|
|
253
295
|
f"Status: {task_status.upper()}\nTask: {task_title}\nPresent: {present_line}\n"
|
|
254
296
|
"Next: Load atw-brainstorm and write prd.md before task.py start."
|
|
@@ -281,6 +323,9 @@ def _run_git(repo_root: Path, args: list[str]) -> str:
|
|
|
281
323
|
|
|
282
324
|
|
|
283
325
|
def _format_git_state(repo_root: Path) -> str:
|
|
326
|
+
# 普通文件夹没有分支,也谈不上干净:照实说,不编一个状态出来。
|
|
327
|
+
if _run_git(repo_root, ["rev-parse", "--is-inside-work-tree"]) != "true":
|
|
328
|
+
return "Git: not a git repository."
|
|
284
329
|
branch = _run_git(repo_root, ["branch", "--show-current"]) or "(detached)"
|
|
285
330
|
dirty_lines = [
|
|
286
331
|
line for line in _run_git(repo_root, ["status", "--porcelain"]).splitlines()
|
|
@@ -453,7 +498,10 @@ def main() -> None:
|
|
|
453
498
|
configure_project_encoding(project_dir)
|
|
454
499
|
|
|
455
500
|
atw_dir = project_dir / ".atw"
|
|
456
|
-
|
|
501
|
+
# A general project keeps no guideline indexes — whatever a switch from dev
|
|
502
|
+
# left under `.atw/spec/` is not offered there.
|
|
503
|
+
conventions = _conventions_context(project_dir)
|
|
504
|
+
spec_index_paths = [] if conventions else _collect_spec_index_paths(atw_dir)
|
|
457
505
|
|
|
458
506
|
output = StringIO()
|
|
459
507
|
|
|
@@ -473,24 +521,27 @@ ATW compact SessionStart context. Use it to orient the session; load details on
|
|
|
473
521
|
output.write(_build_workflow_toc(atw_dir / "workflow.md"))
|
|
474
522
|
output.write("\n</atw-workflow>\n\n")
|
|
475
523
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
524
|
+
if conventions:
|
|
525
|
+
output.write(f"{conventions}\n\n")
|
|
526
|
+
else:
|
|
527
|
+
output.write("<guidelines>\n")
|
|
528
|
+
output.write(
|
|
529
|
+
"Task context order for implementation/check: jsonl entries -> `prd.md`.\n\n"
|
|
530
|
+
)
|
|
480
531
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
532
|
+
if spec_index_paths:
|
|
533
|
+
output.write("## Available indexes (read on demand)\n")
|
|
534
|
+
for p in spec_index_paths:
|
|
535
|
+
output.write(f"- {p}\n")
|
|
536
|
+
output.write("\n")
|
|
537
|
+
|
|
538
|
+
output.write(
|
|
539
|
+
"Discover more via: "
|
|
540
|
+
"`python3 ./.atw/scripts/get_context.py --mode packages`\n"
|
|
541
|
+
)
|
|
542
|
+
output.write("</guidelines>\n\n")
|
|
492
543
|
|
|
493
|
-
task_status = _get_task_status(atw_dir, hook_input)
|
|
544
|
+
task_status = _get_task_status(atw_dir, hook_input, general=bool(conventions))
|
|
494
545
|
output.write(f"<task-status>\n{task_status}\n</task-status>\n\n")
|
|
495
546
|
|
|
496
547
|
output.write("""<ready>
|
|
@@ -7,18 +7,20 @@ description: "小白模式:给不懂技术的用户全程用大白话沟通—
|
|
|
7
7
|
|
|
8
8
|
The user is not an engineer. They are still the one in charge: they decide what gets built, what is good enough, and what must not happen. They can only do that when they understand what you tell them and what you are about to do. An update they cannot read leaves them able to approve your work but not to review it — and then your choices ship unexamined.
|
|
9
9
|
|
|
10
|
-
Noob mode keeps every reply, in every ATW stage, in language they can act on.
|
|
10
|
+
Noob mode keeps every reply, in every ATW stage, in language they can act on. In a `general` project it is on from the start; in a `dev` project it starts when it is switched on. Either way it lasts until the user switches it off.
|
|
11
11
|
|
|
12
12
|
## Switching
|
|
13
13
|
|
|
14
|
+
Each user is in one of three states: switched on, switched off, or never chose. On and off are the user's own choice and always win. A user who never chose gets the project's default — on in a `general` project, off in a `dev` one (a project whose `.atw/config.yaml` has no `mode` entry is `dev`).
|
|
15
|
+
|
|
14
16
|
The user's words or the argument decide the action. With neither, turn it on — unless you loaded this skill yourself because the `<noob-mode>` reminder told you to, which is "Already on" below.
|
|
15
17
|
|
|
16
18
|
- **On** — `/atw-noob-mode`, "开启小白模式", "turn on noob mode", or the user says they cannot follow the terms, an approval prompt, an error or command output. Run `python3 ./.atw/scripts/noob_mode.py on`, then confirm in the user's language, for example: "小白模式已开启。接下来我会用大白话说明我在做什么、需要你决定什么、有什么风险。随时可以说“关闭小白模式”。" Apply everything below from that reply on, then do the catch-up in "Catching up".
|
|
17
|
-
- **Off** — `/atw-noob-mode off`, "关闭小白模式", "turn off noob mode". Run `python3 ./.atw/scripts/noob_mode.py off`, confirm in one sentence, and stop applying these rules.
|
|
18
|
-
- **Status** — `python3 ./.atw/scripts/noob_mode.py status`.
|
|
19
|
+
- **Off** — `/atw-noob-mode off`, "关闭小白模式", "turn off noob mode". Run `python3 ./.atw/scripts/noob_mode.py off`, confirm in one sentence, and stop applying these rules. Off is recorded as the user's choice: the project default will not turn it back on, and neither will a change of project mode.
|
|
20
|
+
- **Status** — `python3 ./.atw/scripts/noob_mode.py status`. It prints whether the mode is on, and a `Source:` line saying whether that is the user's own setting or the project default.
|
|
19
21
|
- **Already on** — you loaded this skill yourself because the `<noob-mode>` reminder told you to. Nothing is being switched: run no script and announce nothing. Read on, and apply all of it starting with the reply you are about to write.
|
|
20
22
|
|
|
21
|
-
The
|
|
23
|
+
The choice is personal: it is a line in this user's own `.atw/.developer`, which is never committed, so teammates are unaffected. While it is on, ATW hands you a `<noob-mode>` reminder at session start and on every turn — that is what makes the mode last through long conversations and new sessions. The reminder carries only the core of these rules. When the text of this skill is not in front of you — a new session, or a long conversation that has been summarized — load it before you reply: the reminder alone leaves out most of what is here, limits included. If the script fails, tell the user plainly and still follow the rules for this conversation.
|
|
22
24
|
|
|
23
25
|
Never switch it off on your own, and never drift out of it because a stage is long or technical. Only the user ends it.
|
|
24
26
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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;
|
|
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
|
-
|
|
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,
|
|
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
|