@techierathore/techieflow 1.0.3 → 1.1.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/.claude/commands/TechieFlow/agents/analyst.md +5 -8
- package/.claude/commands/TechieFlow/agents/architect.md +1 -10
- package/.claude/commands/TechieFlow/agents/flow-master.md +1 -0
- package/.claude/commands/TechieFlow/agents/verifier.md +1 -0
- package/.claude/commands/TechieFlow/tasks/_owner-language.md +27 -0
- package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +1 -1
- package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +4 -3
- package/.claude/commands/TechieFlow/tasks/amend-docs.md +6 -5
- package/.claude/commands/TechieFlow/tasks/build-phase.md +1 -1
- package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +4 -0
- package/.claude/commands/TechieFlow/tasks/create-doc.md +4 -0
- package/.claude/commands/TechieFlow/tasks/create-project-brief.md +22 -0
- package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +3 -3
- package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +3 -3
- package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +4 -0
- package/.claude/commands/TechieFlow/tasks/fix-issues.md +2 -2
- package/.claude/commands/TechieFlow/tasks/generate-html.md +7 -3
- package/.claude/commands/TechieFlow/tasks/handoff-phase.md +20 -18
- package/.claude/commands/TechieFlow/tasks/metrics-report.md +2 -0
- package/.claude/commands/TechieFlow/tasks/mockups.md +1 -1
- package/.claude/commands/TechieFlow/tasks/productguide.md +2 -2
- package/.claude/commands/TechieFlow/tasks/render-workflow-docs.md +1 -1
- package/.claude/commands/TechieFlow/tasks/triage-issues.md +2 -2
- package/.claude/commands/TechieFlow/tasks/verify-phase.md +1 -1
- package/.opencode/plugin/techieflow.js +181 -9
- package/.tfcore/agents/analyst.md +5 -8
- package/.tfcore/agents/architect.md +1 -10
- package/.tfcore/agents/flow-master.md +1 -0
- package/.tfcore/agents/verifier.md +1 -0
- package/.tfcore/hooks/block-git.sh +4 -2
- package/.tfcore/hooks/guard-build.sh +5 -1
- package/.tfcore/hooks/guard-db.sh +6 -4
- package/.tfcore/hooks/guard-metrics.sh +13 -1
- package/.tfcore/hooks/guard-status-html.sh +86 -2
- package/.tfcore/hooks/guard-verify-deps.sh +124 -0
- package/.tfcore/hooks/guard-verify.sh +8 -3
- package/.tfcore/hooks/sweep-artifacts.sh +46 -0
- package/.tfcore/routing.yaml +61 -0
- package/.tfcore/standards/owner-words.txt +23 -0
- package/.tfcore/tasks/_owner-language.md +27 -0
- package/.tfcore/tasks/_smoke-test-policy.md +1 -1
- package/.tfcore/tasks/_status-update-gate.md +4 -3
- package/.tfcore/tasks/amend-docs.md +6 -5
- package/.tfcore/tasks/build-phase.md +1 -1
- package/.tfcore/tasks/create-deep-research-prompt.md +4 -0
- package/.tfcore/tasks/create-doc.md +4 -0
- package/.tfcore/tasks/create-project-brief.md +22 -0
- package/.tfcore/tasks/day1-brownfield.md +3 -3
- package/.tfcore/tasks/day1-greenfield.md +3 -3
- package/.tfcore/tasks/facilitate-brainstorming-session.md +4 -0
- package/.tfcore/tasks/fix-issues.md +2 -2
- package/.tfcore/tasks/generate-html.md +7 -3
- package/.tfcore/tasks/handoff-phase.md +20 -18
- package/.tfcore/tasks/metrics-report.md +2 -0
- package/.tfcore/tasks/mockups.md +1 -1
- package/.tfcore/tasks/productguide.md +2 -2
- package/.tfcore/tasks/render-workflow-docs.md +1 -1
- package/.tfcore/tasks/triage-issues.md +2 -2
- package/.tfcore/tasks/verify-phase.md +1 -1
- package/.tfcore/telemetry/SCHEMA.md +61 -1
- package/.tfcore/telemetry/install-metrics.sh +3 -2
- package/.tfcore/telemetry/tf-metrics.sh +435 -21
- package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +6 -0
- package/.tfcore/templates/v4custom/app-brd-tmpl.md +5 -3
- package/.tfcore/templates/v4custom/app-brief-tmpl.md +65 -0
- package/.tfcore/templates/v4custom/app-decision-request-tmpl.md +85 -0
- package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +86 -31
- package/.tfcore/templates/v4custom/app-phase-brd-tmpl.md +94 -0
- package/.tfcore/templates/v4custom/app-phase-uidesign-tmpl.md +60 -0
- package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +4 -2
- package/.tfcore/templates/v4custom/build-subagent-prompt.md +2 -2
- package/.tfcore/templates/v4custom/html-render-shell.md +4 -2
- package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-perf-grade.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
- package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
- package/.tfcore/utils/tf-assets-browser.mjs +96 -0
- package/.tfcore/utils/tf-assets.sh +97 -12
- package/.tfcore/utils/tf-brd-status.py +14 -5
- package/.tfcore/utils/tf-build-list.py +99 -11
- package/.tfcore/utils/tf-build-list.sh +5 -3
- package/.tfcore/utils/tf-build.sh +88 -15
- package/.tfcore/utils/tf-ci-repro.py +685 -0
- package/.tfcore/utils/tf-ci-repro.sh +24 -0
- package/.tfcore/utils/tf-devguide-list.py +47 -22
- package/.tfcore/utils/tf-doc-check.py +242 -10
- package/.tfcore/utils/tf-doc-tests.sh +79 -0
- package/.tfcore/utils/tf-emit.sh +413 -19
- package/.tfcore/utils/tf-feedback.sh +13 -0
- package/.tfcore/utils/tf-fix-close.sh +113 -31
- package/.tfcore/utils/tf-gitignore-audit.sh +50 -3
- package/.tfcore/utils/tf-goal.sh +105 -18
- package/.tfcore/utils/tf-harness.sh +9 -4
- package/.tfcore/utils/tf-lock.sh +29 -0
- package/.tfcore/utils/tf-log-miss.py +34 -8
- package/.tfcore/utils/tf-login.mjs +167 -0
- package/.tfcore/utils/tf-misses-md.py +24 -21
- package/.tfcore/utils/tf-misses-md.sh +5 -4
- package/.tfcore/utils/tf-mockup-parity.mjs +427 -63
- package/.tfcore/utils/tf-mockup-parity.sh +14 -2
- package/.tfcore/utils/tf-model-pick.sh +475 -0
- package/.tfcore/utils/tf-owner-text.py +233 -0
- package/.tfcore/utils/tf-owner-text.sh +14 -0
- package/.tfcore/utils/tf-phase.sh +33 -1
- package/.tfcore/utils/tf-portable.sh +230 -0
- package/.tfcore/utils/tf-render-html.py +80 -2
- package/.tfcore/utils/tf-routing-bind.sh +87 -1
- package/.tfcore/utils/tf-routing.sh +7 -0
- package/.tfcore/utils/tf-selfcheck.sh +356 -0
- package/.tfcore/utils/tf-split-brd.py +17 -2
- package/.tfcore/utils/tf-status-evidence.sh +11 -9
- package/.tfcore/utils/tf-status-facts.py +133 -44
- package/.tfcore/utils/tf-triage.py +62 -16
- package/.tfcore/utils/tf-triage.sh +2 -0
- package/.tfcore/utils/tf-verify-boot.sh +263 -43
- package/.tfcore/utils/tf-verify-emit.sh +20 -0
- package/.tfcore/utils/tf-verify-env.sh +33 -4
- package/.tfcore/utils/tf-verify-list.py +75 -8
- package/.tfcore/utils/tf-verify-screens.mjs +254 -43
- package/.tfcore/utils/tf-verify-screens.sh +18 -4
- package/.tfcore/utils/tf-verify-tests.sh +272 -28
- package/.tfcore/utils/tf-verify-verdict.py +59 -11
- package/.tfcore/utils/tf_defect.py +27 -0
- package/.tfcore/utils/tf_feedback.py +209 -0
- package/.tfcore/utils/tf_roadmap.py +35 -0
- package/README.md +19 -5
- package/docs/TechieFlow-Installation.md +5 -3
- package/opencode.jsonc +24 -0
- package/package.json +1 -1
- package/scripts/install.mjs +4 -0
- package/.tfcore/templates/architecture-tmpl.yaml +0 -650
- package/.tfcore/templates/brownfield-architecture-tmpl.yaml +0 -476
- package/.tfcore/templates/competitor-analysis-tmpl.yaml +0 -306
- package/.tfcore/templates/front-end-architecture-tmpl.yaml +0 -218
- package/.tfcore/templates/fullstack-architecture-tmpl.yaml +0 -823
- package/.tfcore/templates/market-research-tmpl.yaml +0 -252
- package/.tfcore/templates/project-brief-tmpl.yaml +0 -221
- package/.tfcore/utils/techieflow-doc-template.md +0 -325
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""tf_feedback.py — the one reader of a project's upstream feedback files (2026-09-11).
|
|
3
|
+
|
|
4
|
+
bash .tfcore/utils/tf-feedback.sh <App> every entry and its state
|
|
5
|
+
bash .tfcore/utils/tf-feedback.sh <App> --waiting fixed upstream, not yet re-checked here
|
|
6
|
+
bash .tfcore/utils/tf-feedback.sh <App> --close <ID> "<what you ran and what it showed>"
|
|
7
|
+
|
|
8
|
+
An entry is one of three things, and every script that reports on these files reads them here:
|
|
9
|
+
|
|
10
|
+
open nobody upstream has answered it
|
|
11
|
+
fixed the upstream team's reply ("## Resolution status (…, <date>)") lists it, or its own body
|
|
12
|
+
says "fixed upstream" — the fix exists, and this project has not yet re-checked it
|
|
13
|
+
closed this project re-checked it and marked it closed (✅, "Closed <date>", "will not fix")
|
|
14
|
+
|
|
15
|
+
Why one reader. Until 2026-09-11 three scripts read these files three ways, and the one that writes
|
|
16
|
+
PROJECT-STATUS counted every `###` heading as an entry and accepted only a closing format no file
|
|
17
|
+
used: TfLens's status read "TechieFlow: 62 open of 62" for a file of 27 entries with 16 fixed, and
|
|
18
|
+
the agent reported two fixed problems to the owner as open (MISS-TechieFlow-20260911-04).
|
|
19
|
+
|
|
20
|
+
--close writes one line under the entry's heading and nothing else. Python 3 standard library only.
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import datetime
|
|
25
|
+
import glob
|
|
26
|
+
import os
|
|
27
|
+
import re
|
|
28
|
+
import sys
|
|
29
|
+
|
|
30
|
+
# a heading is one line: [ \t], never \s, which runs past the line break and makes the next line
|
|
31
|
+
# of a bare "## TF-013" its title — its closing line included (TF-028)
|
|
32
|
+
ENTRY_HEAD = re.compile(r"(?m)^(#{2,3})[ \t]+`?([A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*-\d+)\b[` \t—–-]*(.*)$")
|
|
33
|
+
REPLY_HEAD = re.compile(r"(?mi)^([ \t]*>[ \t]*)?#{2,3}[ \t]+[^\n]*\b(?:resolution status|resolved|repl(?:y|ies) from)\b[^\n]*")
|
|
34
|
+
ANY_ID = re.compile(r"(?<![\w-])([A-Z][A-Z0-9]*(?:-[A-Z]+)*-\d{2,})(?!\d)")
|
|
35
|
+
FIXED_WORDS = re.compile(r"(?i)\b(fixed|resolved|corrected|closes|closed|wired)\b")
|
|
36
|
+
EVERYTHING = re.compile(r"(?i)\beverything else is (?:now )?fixed\b|\ball (?:the )?others? (?:are )?(?:now )?fixed\b")
|
|
37
|
+
# a project's own mark that it re-checked the fix, that it will never be fixed, or that the entry
|
|
38
|
+
# was folded into another
|
|
39
|
+
CLOSED = re.compile(r"(?i)(✅|\bclosed (?:on )?\d{4}-\d{2}-\d{2}|is \*\*closed\*\*|\bwill not fix\b|\bwont-fix\b)")
|
|
40
|
+
MERGED = re.compile(r"(?i)^\s*merged into\b")
|
|
41
|
+
FIXED_IN_BODY = re.compile(r"(?i)\bfixed upstream\b")
|
|
42
|
+
BLOCKS = re.compile(r"(?im)^\s*[-*]\s*\*\*Blocks:?\*\*:?\s*(yes|no)\b")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def entries(path):
|
|
46
|
+
"""-> [{id, title, state, since, blocks, start, end}] in file order."""
|
|
47
|
+
text = open(path, encoding="utf-8", errors="replace").read()
|
|
48
|
+
heads = list(ENTRY_HEAD.finditer(text))
|
|
49
|
+
out = {}
|
|
50
|
+
for i, m in enumerate(heads):
|
|
51
|
+
end = heads[i + 1].start() if i + 1 < len(heads) else len(text)
|
|
52
|
+
nxt = re.search(r"(?m)^#{1,%d}\s" % len(m.group(1)), text[m.end():end])
|
|
53
|
+
body = text[m.end(): m.end() + nxt.start()] if nxt else text[m.end():end]
|
|
54
|
+
head = body[:2500]
|
|
55
|
+
state = ("closed" if CLOSED.search(head) or MERGED.search(m.group(3))
|
|
56
|
+
else "fixed" if FIXED_IN_BODY.search(head) else "open")
|
|
57
|
+
b = BLOCKS.search(body)
|
|
58
|
+
out.setdefault(m.group(2), {"id": m.group(2), "title": m.group(3).strip(" `"), "state": state,
|
|
59
|
+
"since": "", "blocks": b.group(1).lower() if b else "",
|
|
60
|
+
"start": m.start(), "end": m.end() + len(body)})
|
|
61
|
+
# the upstream team's replies, in the shapes they have actually been written in
|
|
62
|
+
for rid, date in replied_fixed(text, list(out)):
|
|
63
|
+
e = out.get(rid)
|
|
64
|
+
if e and e["state"] == "open":
|
|
65
|
+
e["state"] = "fixed"
|
|
66
|
+
if e and not e["since"] and date:
|
|
67
|
+
e["since"] = date
|
|
68
|
+
return list(out.values())
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def reply_blocks(text):
|
|
72
|
+
"""-> [(date, block text)]. A reply is a heading naming a resolution or a reply —
|
|
73
|
+
"## Resolution status (TechieFlow team, 2026-09-09)", "## Replies from …", or a quoted
|
|
74
|
+
"> ## ✅ RESOLVED LIBRARY-SIDE 2026-08-31" — and runs to the next heading of its level,
|
|
75
|
+
or, when quoted, to the end of the quote."""
|
|
76
|
+
out = []
|
|
77
|
+
for m in REPLY_HEAD.finditer(text):
|
|
78
|
+
rest = text[m.end():]
|
|
79
|
+
if m.group(1): # quoted: the block is the run of '>' lines after the heading's own line
|
|
80
|
+
rest = rest[1:] if rest.startswith("\n") else rest
|
|
81
|
+
q = re.match(r"(?:[ \t]*>[^\n]*\n?)*", rest)
|
|
82
|
+
body = re.sub(r"(?m)^[ \t]*>[ \t]?", "", q.group(0))
|
|
83
|
+
else:
|
|
84
|
+
nxt = re.search(r"(?m)^##\s", rest)
|
|
85
|
+
body = rest[: nxt.start()] if nxt else rest
|
|
86
|
+
d = re.search(r"\d{4}-\d{2}-\d{2}", m.group(0))
|
|
87
|
+
out.append((d.group(0) if d else "", body))
|
|
88
|
+
return out
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def replied_fixed(text, known):
|
|
92
|
+
"""-> [(id, date)] for every entry a reply says is fixed. Inside a reply, an item — a table
|
|
93
|
+
row, a bullet, or a paragraph — fixes the ids it names when it is a table row or says fixed,
|
|
94
|
+
resolved, corrected or closes; "everything else is fixed" covers every entry up to the highest
|
|
95
|
+
number the reply mentions (entries filed after it stay open)."""
|
|
96
|
+
out = []
|
|
97
|
+
for date, body in reply_blocks(text):
|
|
98
|
+
items = re.split(r"\n\s*\n|\n(?=\s*[-*]\s)|\n(?=\s*\|)", body)
|
|
99
|
+
mentioned = []
|
|
100
|
+
for it in items:
|
|
101
|
+
ids = ANY_ID.findall(it)
|
|
102
|
+
mentioned += ids
|
|
103
|
+
if not ids:
|
|
104
|
+
continue
|
|
105
|
+
if it.lstrip().startswith("|"):
|
|
106
|
+
first = ANY_ID.findall(it.split("|")[1] if it.count("|") > 1 else "")
|
|
107
|
+
out += [(i, date) for i in first]
|
|
108
|
+
elif FIXED_WORDS.search(it):
|
|
109
|
+
out += [(i, date) for i in ids]
|
|
110
|
+
for it in items:
|
|
111
|
+
if EVERYTHING.search(it) and mentioned:
|
|
112
|
+
top = {}
|
|
113
|
+
for i in mentioned:
|
|
114
|
+
p, n = i.rsplit("-", 1)
|
|
115
|
+
top[p] = max(top.get(p, 0), int(n))
|
|
116
|
+
for k in known:
|
|
117
|
+
p, n = k.rsplit("-", 1)
|
|
118
|
+
if p in top and int(n) <= top[p]:
|
|
119
|
+
out.append((k, date))
|
|
120
|
+
return out
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def files(root, app=None):
|
|
124
|
+
pat = f"{app}-*-Feedback.md" if app else "*-Feedback.md"
|
|
125
|
+
return sorted(glob.glob(os.path.join(root, "docs", pat)))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def upstream_of(path, app=None):
|
|
129
|
+
base = os.path.basename(path)
|
|
130
|
+
m = re.match(rf"^{re.escape(app)}-(.+)-Feedback\.md$", base) if app else re.match(r"^[^-]+-(.+)-Feedback\.md$", base)
|
|
131
|
+
return m.group(1) if m else base
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def state_map(root):
|
|
135
|
+
"""Every entry id across the project's feedback files -> (state, "<file>, <date>")."""
|
|
136
|
+
out = {}
|
|
137
|
+
for f in files(root):
|
|
138
|
+
rel = os.path.relpath(f, root).replace(os.sep, "/")
|
|
139
|
+
for e in entries(f):
|
|
140
|
+
out[e["id"]] = (e["state"], f"{rel}" + (f", Resolution status {e['since']}" if e["since"] else ""))
|
|
141
|
+
return out
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def summary_line(path, app):
|
|
145
|
+
es = entries(path)
|
|
146
|
+
n = {s: [e["id"] for e in es if e["state"] == s] for s in ("open", "fixed", "closed")}
|
|
147
|
+
rel = "docs/" + os.path.basename(path)
|
|
148
|
+
parts = [f"{len(n['open'])} open"]
|
|
149
|
+
if n["fixed"]:
|
|
150
|
+
ids = ", ".join(n["fixed"][:3]) + (" …" if len(n["fixed"]) > 3 else "")
|
|
151
|
+
parts.append(f"{len(n['fixed'])} fixed upstream, not yet re-checked ({ids})")
|
|
152
|
+
parts.append(f"{len(n['closed'])} closed")
|
|
153
|
+
return f"- {upstream_of(path, app)}: " + " · ".join(parts) + f" — {rel}"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def close(path, rid, evidence):
|
|
157
|
+
text = open(path, encoding="utf-8").read()
|
|
158
|
+
e = next((x for x in entries(path) if x["id"] == rid), None)
|
|
159
|
+
if not e:
|
|
160
|
+
return f"tf-feedback: no entry {rid} in {os.path.basename(path)}", 2
|
|
161
|
+
if e["state"] == "closed":
|
|
162
|
+
return f"tf-feedback: {rid} is already closed; nothing written", 0
|
|
163
|
+
if not evidence.strip():
|
|
164
|
+
return "tf-feedback: say what you ran and what it showed; a close with no evidence is refused", 2
|
|
165
|
+
line_end = text.index("\n", e["start"]) + 1
|
|
166
|
+
today = datetime.date.today().isoformat()
|
|
167
|
+
mark = f"\n> ✅ **Closed {today}** — re-checked here: {evidence.strip()}\n"
|
|
168
|
+
open(path, "w", encoding="utf-8").write(text[:line_end] + mark + text[line_end:])
|
|
169
|
+
return f"tf-feedback: {rid} closed in {os.path.basename(path)}", 0
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def main(argv):
|
|
173
|
+
if not argv or argv[0] in ("-h", "--help"):
|
|
174
|
+
print(__doc__)
|
|
175
|
+
return 0 if argv else 2
|
|
176
|
+
app, rest = argv[0], argv[1:]
|
|
177
|
+
root = os.getcwd()
|
|
178
|
+
fs = files(root, app)
|
|
179
|
+
if not fs:
|
|
180
|
+
print(f"tf-feedback: no docs/{app}-*-Feedback.md here")
|
|
181
|
+
return 0
|
|
182
|
+
if rest[:1] == ["--close"]:
|
|
183
|
+
if len(rest) < 3:
|
|
184
|
+
print('usage: tf-feedback.sh <App> --close <ID> "<what you ran and what it showed>"')
|
|
185
|
+
return 2
|
|
186
|
+
for f in fs:
|
|
187
|
+
if any(e["id"] == rest[1] for e in entries(f)):
|
|
188
|
+
msg, rc = close(f, rest[1], rest[2])
|
|
189
|
+
print(msg)
|
|
190
|
+
return rc
|
|
191
|
+
print(f"tf-feedback: no entry {rest[1]} in any docs/{app}-*-Feedback.md")
|
|
192
|
+
return 2
|
|
193
|
+
waiting = rest[:1] == ["--waiting"]
|
|
194
|
+
for f in fs:
|
|
195
|
+
es = entries(f)
|
|
196
|
+
if waiting:
|
|
197
|
+
for e in es:
|
|
198
|
+
if e["state"] == "fixed":
|
|
199
|
+
print(f"{e['id']} fixed upstream{' ' + e['since'] if e['since'] else ''}, waiting to be "
|
|
200
|
+
f"re-checked here — {os.path.basename(f)}")
|
|
201
|
+
continue
|
|
202
|
+
print(summary_line(f, app))
|
|
203
|
+
for e in es:
|
|
204
|
+
print(f" {e['id']:<12} {e['state']:<7} {e['since'] or '':<10} {e['title'][:80]}")
|
|
205
|
+
return 0
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
if __name__ == "__main__":
|
|
209
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""tf_roadmap.py — which checklist rows are roadmap items, out of this phase's scope.
|
|
2
|
+
|
|
3
|
+
A row is a roadmap item when its acceptance line in the checklist's detail section carries
|
|
4
|
+
"Roadmap — not in this phase's scope" (any dash, any case). Such a row is not work for this
|
|
5
|
+
phase: it stays off the build list and out of the "not built yet" count, and it never makes
|
|
6
|
+
*build-phase the next command. Only the owner moves it, through *amend-docs.
|
|
7
|
+
|
|
8
|
+
AppManager's REQ-NFR-008 carried the marker at In Progress; tf-build-list.py put it on the
|
|
9
|
+
working list with a builder prompt, a builder built it against the owner's decision, and
|
|
10
|
+
tf-status-facts.py named *build-phase as the next command for it forever (TF-017).
|
|
11
|
+
tests/regression/run.sh am_017. Imported by tf-build-list.py and tf-status-facts.py.
|
|
12
|
+
"""
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
MARK = re.compile(r"roadmap\s*[—–-]+\s*not in this phase['’]?s scope", re.I)
|
|
16
|
+
ACCEPTANCE = re.compile(r"\*Acceptance:?\*|^\s*[-*]\s*Acceptance:", re.I)
|
|
17
|
+
OWNER = re.compile(r"<a id=['\"]d-(req-[a-z]+-\d+)['\"]|\*\*`?(REQ-[A-Z]+-\d+)`?\*\*", re.I)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def ids(text):
|
|
21
|
+
"""The set of REQ ids whose acceptance line carries the roadmap marker.
|
|
22
|
+
|
|
23
|
+
The owner of an acceptance line is the nearest REQ anchor or bold REQ id above it, in the
|
|
24
|
+
detail section; status-table rows (lines starting with `|`) are never read."""
|
|
25
|
+
out, owner = set(), None
|
|
26
|
+
for line in text.splitlines():
|
|
27
|
+
if line.lstrip().startswith("|"):
|
|
28
|
+
continue
|
|
29
|
+
if re.match(r"^#{1,2}\s", line):
|
|
30
|
+
owner = None
|
|
31
|
+
for m in OWNER.finditer(line):
|
|
32
|
+
owner = (m.group(1) or m.group(2)).upper()
|
|
33
|
+
if owner and ACCEPTANCE.search(line) and MARK.search(line):
|
|
34
|
+
out.add(owner)
|
|
35
|
+
return out
|
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@ Three things make it different from prompting an agent by hand:
|
|
|
27
27
|
- **Rules that were ignored became hooks.** Eight guards refuse an action outright and print why: any git write, a malformed status file, `Verified` without a verify run, a hand edit of a telemetry file, a database write outside build and fix, run material at the repository root, a backgrounded build in unattended mode, and ending a turn with the status gate incomplete.
|
|
28
28
|
- **The work is measured.** Five append-only streams per project record what each command cost, which check caught which failure, and what each miss cost to fix.
|
|
29
29
|
|
|
30
|
+
The framework has an opinion about how agents talk to people. Every project's `AGENTS.md` (written at day-1) tells the agent to tag each claim it is unsure of as Certain, Likely or Guessing; to say the thing you will not want to hear first; to skip warm-up and start with the most useful sentence; and to disagree in a fixed shape ("I disagree because … I would do … The risk in your approach is …") and then do what you decide.
|
|
31
|
+
|
|
30
32
|
The framework is technology-neutral. It asks a set of stack questions at day-1 and records the answers in the project's Architecture document; an answer set can fill them in (the owner's .NET set ships as `docs/TechieFlow-Stack-Defaults-DotNet.md`).
|
|
31
33
|
|
|
32
34
|
---
|
|
@@ -35,6 +37,8 @@ The framework is technology-neutral. It asks a set of stack questions at day-1 a
|
|
|
35
37
|
|
|
36
38
|
Prerequisites are one-time per machine and live in [`docs/TechieFlow-Setup.md`](docs/TechieFlow-Setup.md): the toolchain, headless Chromium, and — for mobile heads — the Appium device hosts. **`python3` is required**; the guard hooks fail open without it.
|
|
37
39
|
|
|
40
|
+
It runs on Linux, WSL and a stock Mac. On a Mac the scripts use Apple's own bash 3.2 and BSD tools, so Homebrew, a newer bash and GNU coreutils are not needed; `python3` comes with the Command Line Tools (`xcode-select --install`).
|
|
41
|
+
|
|
38
42
|
Copy the framework in; never `npm install` it. All three scripts locate the framework from their own directory, so invoke whichever machine's copy you are on.
|
|
39
43
|
|
|
40
44
|
```bash
|
|
@@ -126,10 +130,11 @@ Replace `{App}` with your application name. Claude Code takes the long persona p
|
|
|
126
130
|
|
|
127
131
|
## 5. What it produces
|
|
128
132
|
|
|
129
|
-
Every per-project document is named after the application: `docs/<App>-BRD.md`, `-Architecture.md`, `-Checklist.md`, `-UIDesign.md`, `-Coding-Standards.md`, `-UsageGuide.md`, `-DevGuide.md`, `-ProductGuide.md`, `-Deployment-Checklist.md`, plus `PROJECT-STATUS.md` and `CLAUDE.md` at the root, mockups under `docs/mockups/`, and screenshots under `docs/screenshots/<App>/`.
|
|
133
|
+
Every per-project document is named after the application: `docs/<App>-Brief.md`, `-BRD.md`, `-Architecture.md`, `-Checklist.md`, `-UIDesign.md`, `-Coding-Standards.md`, `-UsageGuide.md`, `-DevGuide.md`, `-ProductGuide.md`, `-Deployment-Checklist.md`, `-Misses.md`, `-Decision-Request.md`, `-<Upstream>-Feedback.md`, plus `PROJECT-STATUS.md` and `CLAUDE.md` at the root, mockups under `docs/mockups/`, and screenshots under `docs/screenshots/<App>/`.
|
|
130
134
|
|
|
131
135
|
| Document | Who reads it |
|
|
132
136
|
|---|---|
|
|
137
|
+
| Brief | You, before anything is built. One page: what it is, who for, must do, out of scope. Day-1 reads it. |
|
|
133
138
|
| BRD, Architecture, UIDesign and the mockups | You, at the day-1 review. This is the cheap moment to redirect. |
|
|
134
139
|
| Checklist | Agents only. One table, one row per requirement, the single source of truth. Never rendered to HTML. |
|
|
135
140
|
| Coding Standards | Every implementing agent, and the verifier's standards check. |
|
|
@@ -138,9 +143,11 @@ Every per-project document is named after the application: `docs/<App>-BRD.md`,
|
|
|
138
143
|
| DevGuide | A developer tracing a bug: every screen to its code, with the line to break on. |
|
|
139
144
|
| ProductGuide | The end user. |
|
|
140
145
|
| Deployment Checklist | Whoever puts it on the host, after UAT. |
|
|
141
|
-
|
|
|
146
|
+
| Misses | You and the agents. Rebuilt from the record whenever something is logged as missed. |
|
|
147
|
+
| Decision Request | You. Every choice an agent cannot make: options, what each costs, a recommendation, and a block to paste back. It is never read back by an agent. |
|
|
148
|
+
| Feedback, one file per upstream | That team — each library, and the framework itself. Every entry says in its first word whether it blocks your work. Reporting is never refused; a non-blocking entry never stops a run. |
|
|
142
149
|
|
|
143
|
-
Each document has a required shape and a size budget for the application's size (Small, Medium or Large), enforced by `bash .tfcore/utils/tf-doc-check.sh --app {App}`. Add `--warn` to get a report instead of a refusal, which is what you want on a project that predates the schemas. Human documents render to HTML by script; the checklist
|
|
150
|
+
Each document has a required shape and a size budget for the application's size (Small, Medium or Large), enforced by `bash .tfcore/utils/tf-doc-check.sh --app {App}`. Add `--warn` to get a report instead of a refusal, which is what you want on a project that predates the schemas. Human documents render to HTML by script; an agent document never does — the checklist and the miss list are refused by name.
|
|
144
151
|
|
|
145
152
|
---
|
|
146
153
|
|
|
@@ -152,7 +159,7 @@ The shipped configuration allows every file operation and all of Bash with no pr
|
|
|
152
159
|
|
|
153
160
|
Five append-only streams per project under `docs/metrics/`, read with `*metrics {App}`: command runs, verification verdicts, misses, chat sessions, and your own commits. They answer five questions — the first-pass rate, which check caught the failure, the escape rate, what misses cost to fix, and the effort per phase. Definitions, real figures and the sentence to say about each are in [`docs/TechieFlow-Telemetry-Explained.md`](docs/TechieFlow-Telemetry-Explained.md); the field contract is `.tfcore/telemetry/SCHEMA.md`.
|
|
154
161
|
|
|
155
|
-
Standing rules: provenance never merges, only identifiers and counts are recorded, telemetry has no veto, and an agent never writes what it cannot know.
|
|
162
|
+
Standing rules: provenance never merges, only identifiers and counts are recorded, telemetry has no veto, and an agent never writes what it cannot know. **Tokens are what is recorded; money is worked out when the report runs.** A record stores what the provider itself charged — only a metered provider charges per token — and never a computed price. The report separately prints a **list price**, the published rate applied to the tokens actually measured, which is the one figure that compares a Claude phase with an OpenCode one. A price is never called a bill, and a monthly plan's allowance is counted apart from both.
|
|
156
163
|
|
|
157
164
|
## 8. Running phases on cheaper models
|
|
158
165
|
|
|
@@ -164,7 +171,14 @@ bash .tfcore/utils/tf-routing.sh on
|
|
|
164
171
|
bash .tfcore/utils/tf-routing.sh set-tier verify-phase economy
|
|
165
172
|
```
|
|
166
173
|
|
|
167
|
-
|
|
174
|
+
Each tier also names the models to try **instead**, while its own is out of quota. When a usage limit is hit, the supervisor parks that model until its stated reset, moves to the next model in the tier's chain, re-binds so the sub-agents move with it, and carries on; only when every model in the chain is limited does it wait. `--no-fallback` restores the old wait-for-reset behaviour.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
bash .tfcore/utils/tf-model-pick.sh status # what each tier would run right now
|
|
178
|
+
bash .tfcore/utils/tf-model-pick.sh chain standard opencode # the order it falls back in
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Drift is observed, never enforced: a run on the wrong model is recorded as such and nothing blocks. The step-by-step setup, and what a cost figure means on each kind of plan, are in the full guide: [`docs/TechieFlow-Routing-Guide.md`](docs/TechieFlow-Routing-Guide.md).
|
|
168
182
|
|
|
169
183
|
---
|
|
170
184
|
|
|
@@ -16,14 +16,16 @@ You need four things on the machine.
|
|
|
16
16
|
| Tool | Why | Check |
|
|
17
17
|
|---|---|---|
|
|
18
18
|
| Node.js 20 or newer | Runs the installer. Nothing is added to your project. | `node --version` |
|
|
19
|
-
| bash | The framework's guard hooks and helper scripts run under bash. macOS and Linux have it. On Windows, work inside WSL or Git Bash. | `bash --version` |
|
|
19
|
+
| bash | The framework's guard hooks and helper scripts run under bash. macOS and Linux have it; the bash 3.2 that comes with macOS is enough. On Windows, work inside WSL or Git Bash. | `bash --version` |
|
|
20
20
|
| Python 3 | Powers the HTML renderer, the telemetry writer and the guard hooks. | `python3 --version` |
|
|
21
|
-
| Claude Code or OpenCode | The harness that runs the agents. Either one. Both work from the same install. | `claude --version` or `opencode --version` |
|
|
21
|
+
| Claude Code or OpenCode | The harness that runs the agents. Either one. Both work from the same install. The framework targets OpenCode 2; its plugin still loads on OpenCode 1.x. | `claude --version` or `opencode --version` |
|
|
22
22
|
|
|
23
23
|
For a .NET project you also need the .NET SDK. Run `dotnet --version` to check.
|
|
24
24
|
|
|
25
25
|
You do not need rsync, git on the command line, or a clone of this repository.
|
|
26
26
|
|
|
27
|
+
**On a Mac.** A stock Mac is enough. The scripts run on Apple's own bash 3.2 and the `sed`, `date`, `stat` and `xargs` that come with macOS. You do not need Homebrew, a newer bash or GNU coreutils for them. The one thing to add is Python 3: it comes with Apple's Command Line Tools, so run `xcode-select --install` once if `python3 --version` asks for them. perl and rsync already ship with macOS. Your project's own tools, such as the .NET SDK, are installed as usual.
|
|
28
|
+
|
|
27
29
|
---
|
|
28
30
|
|
|
29
31
|
## 1. What gets installed
|
|
@@ -312,7 +314,7 @@ It installs the framework if it is not there yet, then removes the leftover pack
|
|
|
312
314
|
|---|---|
|
|
313
315
|
| The framework is under `node_modules/@techierathore/techieflow/` and nowhere else | You ran `npm install`. See section 9. Run `npx @techierathore/techieflow@latest install`. |
|
|
314
316
|
| `bash was not found` | On Windows, run the command inside WSL or Git Bash. |
|
|
315
|
-
| `python3 was not found` | Install Python 3 and run the command again. On macOS: `brew install python3`. On Ubuntu or WSL: `sudo apt-get install -y python3`. |
|
|
317
|
+
| `python3 was not found` | Install Python 3 and run the command again. On macOS: `xcode-select --install` (Apple's Command Line Tools include python3), or `brew install python3`. On Ubuntu or WSL: `sudo apt-get install -y python3`. |
|
|
316
318
|
| `Refusing to install into the framework itself` | You ran the command inside a clone of this repository. Pass `--target=<your project>`. |
|
|
317
319
|
| `does not look installed` on update | The folder has no `.tfcore/`. Run `install` first. |
|
|
318
320
|
| Claude Code does not show the `/TechieFlow:agents:analyst` command | Restart Claude Code in the project folder. Check that `.claude/commands/TechieFlow/agents/analyst.md` exists. |
|
package/opencode.jsonc
CHANGED
|
@@ -897,6 +897,30 @@
|
|
|
897
897
|
"template": "{file:./.tfcore/tasks/create-doc.md}",
|
|
898
898
|
"description": "Create a document from a YAML-driven TechieFlow template (interactive, section by section)."
|
|
899
899
|
},
|
|
900
|
+
"techieflow:tasks:verify": {
|
|
901
|
+
"template": "{file:./.tfcore/tasks/verify-phase.md}",
|
|
902
|
+
"description": "The verifier's *verify. Boot the app, run the seven checks on every row in scope, write the verdicts."
|
|
903
|
+
},
|
|
904
|
+
"techieflow:tasks:metrics": {
|
|
905
|
+
"template": "{file:./.tfcore/tasks/metrics-report.md}",
|
|
906
|
+
"description": "The flow-master's *metrics. Read the telemetry streams and write the metrics report."
|
|
907
|
+
},
|
|
908
|
+
"techieflow:tasks:brainstorm": {
|
|
909
|
+
"template": "{file:./.tfcore/tasks/facilitate-brainstorming-session.md}",
|
|
910
|
+
"description": "The analyst's *brainstorm. Run a structured brainstorming session."
|
|
911
|
+
},
|
|
912
|
+
"techieflow:tasks:research-prompt": {
|
|
913
|
+
"template": "{file:./.tfcore/tasks/create-deep-research-prompt.md}",
|
|
914
|
+
"description": "The analyst's *research-prompt. Generate a well-structured research prompt."
|
|
915
|
+
},
|
|
916
|
+
"techieflow:tasks:research": {
|
|
917
|
+
"template": "{file:./.tfcore/tasks/create-deep-research-prompt.md}",
|
|
918
|
+
"description": "The architect's *research. Generate a well-structured research prompt."
|
|
919
|
+
},
|
|
920
|
+
"techieflow:tasks:create-project-brief": {
|
|
921
|
+
"template": "{file:./.tfcore/tasks/create-project-brief.md}",
|
|
922
|
+
"description": "Write the one-page project brief that day-1 takes as input: product, users, must-do, out of scope. The analyst's *create-project-brief."
|
|
923
|
+
},
|
|
900
924
|
"techieflow:tasks:create-deep-research-prompt": {
|
|
901
925
|
"template": "{file:./.tfcore/tasks/create-deep-research-prompt.md}",
|
|
902
926
|
"description": "Generate well-structured research prompts."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techierathore/techieflow",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "TechieFlow: a spec-driven development framework for AI coding agents, for Claude Code and OpenCode. One command installs it into any project.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
package/scripts/install.mjs
CHANGED
|
@@ -369,6 +369,10 @@ const canonicalSettings = String.raw`{
|
|
|
369
369
|
{
|
|
370
370
|
"type": "command",
|
|
371
371
|
"command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-build.sh\""
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
"type": "command",
|
|
375
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify-deps.sh\""
|
|
372
376
|
}
|
|
373
377
|
]
|
|
374
378
|
},
|