easy-coding-harness 0.5.3 → 0.6.1
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/CHANGELOG.md +27 -0
- package/README.md +13 -11
- package/dist/cli.js +134 -2
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/claude/agents/ec-implementer.md +3 -0
- package/templates/codex/agents/ec-implementer.toml +3 -0
- package/templates/common/bundled-skills/ec-init/SKILL.md +1 -1
- package/templates/common/bundled-skills/ec-meta/references/local-architecture/README.md +12 -5
- package/templates/common/skills/ec-analysis/SKILL.md +87 -51
- package/templates/common/skills/ec-brainstorming/SKILL.md +2 -1
- package/templates/common/skills/ec-git/SKILL.md +1 -1
- package/templates/common/skills/ec-implementing/SKILL.md +42 -13
- package/templates/common/skills/ec-memory/SKILL.md +46 -20
- package/templates/common/skills/ec-reviewing/SKILL.md +10 -6
- package/templates/common/skills/ec-task-close/SKILL.md +1 -1
- package/templates/common/skills/ec-task-management/SKILL.md +1 -1
- package/templates/common/skills/ec-verification/SKILL.md +25 -26
- package/templates/common/skills/ec-workflow/SKILL.md +119 -69
- package/templates/main-constraint/AGENTS.md.tpl +20 -12
- package/templates/main-constraint/CLAUDE.md.tpl +20 -12
- package/templates/qoder/agents/ec-implementer.md +3 -0
- package/templates/runtime/memory/SHORT_MEMORY_TEMPLATE.md +2 -0
- package/templates/runtime/templates/dev-spec-skeleton.md +41 -46
- package/templates/shared-hooks/easy_coding_state.py +1040 -39
- package/templates/shared-hooks/inject-workflow-state.py +1 -176
- package/templates/shared-hooks/session-start.py +2 -1
|
@@ -4,103 +4,9 @@ import os
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
import sys
|
|
6
6
|
|
|
7
|
-
from easy_coding_state import
|
|
7
|
+
from easy_coding_state import load_session
|
|
8
8
|
from easy_coding_status import build_status_context
|
|
9
9
|
|
|
10
|
-
PROMPT_KEYS = ("prompt", "user_prompt", "userPrompt", "message", "text", "input")
|
|
11
|
-
|
|
12
|
-
NEGATIVE_OR_REVISION_PATTERNS = (
|
|
13
|
-
"修改",
|
|
14
|
-
"调整",
|
|
15
|
-
"重写",
|
|
16
|
-
"重新",
|
|
17
|
-
"补充",
|
|
18
|
-
"换个",
|
|
19
|
-
"换一",
|
|
20
|
-
"不要",
|
|
21
|
-
"别",
|
|
22
|
-
"取消",
|
|
23
|
-
"暂停",
|
|
24
|
-
"等一下",
|
|
25
|
-
"先别",
|
|
26
|
-
"有问题",
|
|
27
|
-
"还有问题",
|
|
28
|
-
"不对",
|
|
29
|
-
"不是",
|
|
30
|
-
"不行",
|
|
31
|
-
"继续修复",
|
|
32
|
-
"需要修复",
|
|
33
|
-
"再修复",
|
|
34
|
-
"修一下",
|
|
35
|
-
"修正",
|
|
36
|
-
"?",
|
|
37
|
-
"?",
|
|
38
|
-
"revise",
|
|
39
|
-
"change",
|
|
40
|
-
"cancel",
|
|
41
|
-
"pause",
|
|
42
|
-
"wait",
|
|
43
|
-
"stop",
|
|
44
|
-
"don't",
|
|
45
|
-
"do not",
|
|
46
|
-
"hold",
|
|
47
|
-
"problem",
|
|
48
|
-
"issue",
|
|
49
|
-
"keep fixing",
|
|
50
|
-
"continue fixing",
|
|
51
|
-
"needs fixing",
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
WAITING_CONFIRM_PATTERNS = (
|
|
55
|
-
"确认",
|
|
56
|
-
"开始",
|
|
57
|
-
"执行",
|
|
58
|
-
"实施",
|
|
59
|
-
"按方案",
|
|
60
|
-
"按计划",
|
|
61
|
-
"没问题",
|
|
62
|
-
"可以",
|
|
63
|
-
"同意",
|
|
64
|
-
"继续",
|
|
65
|
-
"go ahead",
|
|
66
|
-
"proceed",
|
|
67
|
-
"start",
|
|
68
|
-
"implement",
|
|
69
|
-
"approve",
|
|
70
|
-
"approved",
|
|
71
|
-
"yes",
|
|
72
|
-
"ok",
|
|
73
|
-
"okay",
|
|
74
|
-
"confirm",
|
|
75
|
-
"looks good",
|
|
76
|
-
"ship it",
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
VERIFICATION_ACCEPT_PATTERNS = (
|
|
80
|
-
"验收通过",
|
|
81
|
-
"接受",
|
|
82
|
-
"通过",
|
|
83
|
-
"可以结束",
|
|
84
|
-
"归档",
|
|
85
|
-
"完成",
|
|
86
|
-
"确认完成",
|
|
87
|
-
"没问题",
|
|
88
|
-
"可以",
|
|
89
|
-
"同意",
|
|
90
|
-
"accept",
|
|
91
|
-
"accepted",
|
|
92
|
-
"approve",
|
|
93
|
-
"approved",
|
|
94
|
-
"looks good",
|
|
95
|
-
"complete",
|
|
96
|
-
"finish",
|
|
97
|
-
"archive",
|
|
98
|
-
"done",
|
|
99
|
-
"ship it",
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
WAITING_CONFIRM_EXACT = {"1", "1.", "开始实施", "确认执行"}
|
|
103
|
-
|
|
104
10
|
|
|
105
11
|
def configure_stdio() -> None:
|
|
106
12
|
for stream in (sys.stdin, sys.stdout, sys.stderr):
|
|
@@ -115,56 +21,6 @@ def read_payload() -> dict:
|
|
|
115
21
|
return {}
|
|
116
22
|
|
|
117
23
|
|
|
118
|
-
def coerce_prompt_text(value: object) -> str:
|
|
119
|
-
if isinstance(value, str):
|
|
120
|
-
return value
|
|
121
|
-
if isinstance(value, list):
|
|
122
|
-
return "\n".join(part for item in value if (part := coerce_prompt_text(item)))
|
|
123
|
-
if isinstance(value, dict):
|
|
124
|
-
parts: list[str] = []
|
|
125
|
-
for key in PROMPT_KEYS:
|
|
126
|
-
if key in value:
|
|
127
|
-
text = coerce_prompt_text(value[key])
|
|
128
|
-
if text:
|
|
129
|
-
parts.append(text)
|
|
130
|
-
if "content" in value:
|
|
131
|
-
text = coerce_prompt_text(value["content"])
|
|
132
|
-
if text:
|
|
133
|
-
parts.append(text)
|
|
134
|
-
return "\n".join(parts)
|
|
135
|
-
return ""
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def extract_user_prompt(payload: dict) -> str:
|
|
139
|
-
for key in PROMPT_KEYS:
|
|
140
|
-
if key not in payload:
|
|
141
|
-
continue
|
|
142
|
-
text = coerce_prompt_text(payload[key])
|
|
143
|
-
if text:
|
|
144
|
-
return text
|
|
145
|
-
return ""
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def normalize_prompt(text: str) -> str:
|
|
149
|
-
return " ".join(text.casefold().strip().split())
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def has_any(text: str, patterns: tuple[str, ...]) -> bool:
|
|
153
|
-
return any(pattern in text for pattern in patterns)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def infer_confirmed_transition(current_stage: str, prompt: str) -> str | None:
|
|
157
|
-
normalized = normalize_prompt(prompt)
|
|
158
|
-
if not normalized or has_any(normalized, NEGATIVE_OR_REVISION_PATTERNS):
|
|
159
|
-
return None
|
|
160
|
-
if current_stage == "WAITING_CONFIRM":
|
|
161
|
-
if normalized in WAITING_CONFIRM_EXACT or has_any(normalized, WAITING_CONFIRM_PATTERNS):
|
|
162
|
-
return "IMPLEMENT"
|
|
163
|
-
if current_stage == "VERIFICATION" and has_any(normalized, VERIFICATION_ACCEPT_PATTERNS):
|
|
164
|
-
return "MEMORY_SHORT"
|
|
165
|
-
return None
|
|
166
|
-
|
|
167
|
-
|
|
168
24
|
def find_ec_root(start: Path) -> Path | None:
|
|
169
25
|
current = start.resolve()
|
|
170
26
|
while True:
|
|
@@ -190,35 +46,6 @@ def detect_agent() -> str:
|
|
|
190
46
|
return "unknown"
|
|
191
47
|
|
|
192
48
|
|
|
193
|
-
def preflight_confirmed_transition(root: Path, session: dict, payload: dict, agent: str) -> dict:
|
|
194
|
-
prompt = extract_user_prompt(payload)
|
|
195
|
-
if not prompt:
|
|
196
|
-
return session
|
|
197
|
-
|
|
198
|
-
state = snapshot_state(root, session=session)
|
|
199
|
-
task_id = state.get("current_task")
|
|
200
|
-
if not task_id or state.get("task_missing") or state.get("is_terminal"):
|
|
201
|
-
return session
|
|
202
|
-
|
|
203
|
-
target_stage = infer_confirmed_transition(str(state.get("status") or ""), prompt)
|
|
204
|
-
if not target_stage:
|
|
205
|
-
return session
|
|
206
|
-
|
|
207
|
-
try:
|
|
208
|
-
transition_task(
|
|
209
|
-
root,
|
|
210
|
-
target_stage,
|
|
211
|
-
agent,
|
|
212
|
-
task_id=str(task_id),
|
|
213
|
-
session_file=str(state["session_file"]),
|
|
214
|
-
)
|
|
215
|
-
except StateError:
|
|
216
|
-
return load_session(root) or session
|
|
217
|
-
|
|
218
|
-
# Read after write so the status line renders the authoritative latest stage.
|
|
219
|
-
return load_session(root) or session
|
|
220
|
-
|
|
221
|
-
|
|
222
49
|
def emit(event_name: str, context: str) -> None:
|
|
223
50
|
print(
|
|
224
51
|
json.dumps(
|
|
@@ -249,8 +76,6 @@ def main() -> int:
|
|
|
249
76
|
|
|
250
77
|
event_name = payload.get("hook_event_name") or payload.get("hookEventName") or "UserPromptSubmit"
|
|
251
78
|
agent = detect_agent()
|
|
252
|
-
if event_name == "UserPromptSubmit":
|
|
253
|
-
session = preflight_confirmed_transition(root, session, payload, agent)
|
|
254
79
|
emit(event_name, build_status_context(root, session, agent))
|
|
255
80
|
return 0
|
|
256
81
|
|
|
@@ -5,7 +5,7 @@ from datetime import datetime, timezone
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
|
-
from easy_coding_state import load_session, write_session
|
|
8
|
+
from easy_coding_state import load_session, normalize_legacy_task, write_session
|
|
9
9
|
from easy_coding_status import build_status_context
|
|
10
10
|
|
|
11
11
|
|
|
@@ -118,6 +118,7 @@ def migrate_legacy_state(root: Path, agent: str) -> dict | None:
|
|
|
118
118
|
task["test_strategy_confirmed"] = True
|
|
119
119
|
if old_state.get("repo_paths"):
|
|
120
120
|
task["repo_paths"] = old_state["repo_paths"]
|
|
121
|
+
normalize_legacy_task(task)
|
|
121
122
|
write_json(task_path, task)
|
|
122
123
|
|
|
123
124
|
# Remove legacy state.json
|