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