@techierathore/techieflow 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/.claude/commands/TechieFlow/agents/flow-master.md +2 -0
  2. package/.claude/commands/TechieFlow/agents/verifier.md +2 -2
  3. package/.claude/commands/TechieFlow/tasks/_develop-mode.md +34 -0
  4. package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +7 -5
  5. package/.claude/commands/TechieFlow/tasks/_yolo-mode.md +2 -2
  6. package/.claude/commands/TechieFlow/tasks/amend-docs.md +1 -1
  7. package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +2 -2
  8. package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +4 -4
  9. package/.claude/commands/TechieFlow/tasks/develop-end-to-end.md +30 -0
  10. package/.claude/commands/TechieFlow/tasks/fix-issues.md +1 -0
  11. package/.claude/commands/TechieFlow/tasks/mockups.md +1 -1
  12. package/.claude/commands/TechieFlow/tasks/triage-and-fix.md +2 -2
  13. package/.claude/commands/TechieFlow/tasks/verify-phase.md +12 -11
  14. package/.opencode/plugin/techieflow.js +15 -7
  15. package/.tfcore/agents/flow-master.md +2 -0
  16. package/.tfcore/agents/verifier.md +2 -2
  17. package/.tfcore/core-config.yaml +2 -1
  18. package/.tfcore/hooks/block-git.sh +25 -2
  19. package/.tfcore/hooks/guard-db.sh +106 -9
  20. package/.tfcore/hooks/guard-status-html.sh +5 -1
  21. package/.tfcore/hooks/guard-verify-deps.sh +16 -1
  22. package/.tfcore/hooks/yolo-allow.sh +27 -0
  23. package/.tfcore/standards/blazor-zoom.cs.txt +191 -0
  24. package/.tfcore/standards/coding-standards-dotnet.md +31 -3
  25. package/.tfcore/tasks/_develop-mode.md +34 -0
  26. package/.tfcore/tasks/_smoke-test-policy.md +7 -5
  27. package/.tfcore/tasks/_yolo-mode.md +2 -2
  28. package/.tfcore/tasks/amend-docs.md +1 -1
  29. package/.tfcore/tasks/day1-brownfield.md +2 -2
  30. package/.tfcore/tasks/day1-greenfield.md +4 -4
  31. package/.tfcore/tasks/develop-end-to-end.md +30 -0
  32. package/.tfcore/tasks/fix-issues.md +1 -0
  33. package/.tfcore/tasks/mockups.md +1 -1
  34. package/.tfcore/tasks/triage-and-fix.md +2 -2
  35. package/.tfcore/tasks/verify-phase.md +12 -11
  36. package/.tfcore/telemetry/SCHEMA.md +6 -2
  37. package/.tfcore/telemetry/tf-metrics.sh +19 -0
  38. package/.tfcore/templates/stack-defaults/dotnet.md +5 -1
  39. package/.tfcore/templates/v4custom/app-architecture-tmpl.md +5 -0
  40. package/.tfcore/templates/v4custom/app-brd-tmpl.md +2 -1
  41. package/.tfcore/templates/v4custom/app-checklist-tmpl.md +11 -0
  42. package/.tfcore/templates/v4custom/app-phases-tmpl.md +2 -1
  43. package/.tfcore/templates/v4custom/app-usageguide-tmpl.md +4 -1
  44. package/.tfcore/templates/v4custom/html-render-shell.md +2 -0
  45. package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
  46. package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
  47. package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
  48. package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
  49. package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
  50. package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
  51. package/.tfcore/utils/tf-appium.mjs +179 -0
  52. package/.tfcore/utils/tf-brd-status.py +10 -1
  53. package/.tfcore/utils/tf-build-list.py +16 -1
  54. package/.tfcore/utils/tf-build.sh +75 -0
  55. package/.tfcore/utils/tf-checklist-edit.py +28 -3
  56. package/.tfcore/utils/tf-day1-files.py +55 -28
  57. package/.tfcore/utils/tf-develop.sh +264 -0
  58. package/.tfcore/utils/tf-devguide-list.py +6 -2
  59. package/.tfcore/utils/tf-doc-check.py +124 -21
  60. package/.tfcore/utils/tf-doc-tests.sh +40 -3
  61. package/.tfcore/utils/tf-emit.sh +40 -10
  62. package/.tfcore/utils/tf-fix-close.sh +13 -7
  63. package/.tfcore/utils/tf-maccatalyst-check.sh +92 -0
  64. package/.tfcore/utils/tf-menus.mjs +401 -0
  65. package/.tfcore/utils/tf-mockup-parity.mjs +361 -28
  66. package/.tfcore/utils/tf-mockup-parity.sh +14 -3
  67. package/.tfcore/utils/tf-owner-text.py +27 -5
  68. package/.tfcore/utils/tf-phase.sh +24 -4
  69. package/.tfcore/utils/tf-render-html.py +19 -3
  70. package/.tfcore/utils/tf-req-ids.py +36 -0
  71. package/.tfcore/utils/tf-stack-check.sh +3 -0
  72. package/.tfcore/utils/tf-status-facts.py +90 -15
  73. package/.tfcore/utils/tf-theme.mjs +189 -0
  74. package/.tfcore/utils/tf-triage.py +30 -4
  75. package/.tfcore/utils/tf-verify-boot.sh +296 -21
  76. package/.tfcore/utils/tf-verify-emit.sh +37 -17
  77. package/.tfcore/utils/tf-verify-list.py +219 -20
  78. package/.tfcore/utils/tf-verify-list.sh +3 -1
  79. package/.tfcore/utils/tf-verify-native.mjs +307 -0
  80. package/.tfcore/utils/tf-verify-screens.mjs +184 -20
  81. package/.tfcore/utils/tf-verify-screens.sh +17 -4
  82. package/.tfcore/utils/tf-verify-tests.sh +107 -17
  83. package/.tfcore/utils/tf-verify-verdict.py +118 -15
  84. package/.tfcore/utils/tf-verify-zoom.mjs +193 -0
  85. package/.tfcore/utils/tf-verify-zoom.sh +29 -0
  86. package/.tfcore/utils/tf-yolo-alias.sh +84 -0
  87. package/.tfcore/utils/tf-yolo-perms.py +135 -0
  88. package/.tfcore/utils/tf-yolo-start.sh +44 -0
  89. package/.tfcore/utils/tf-yolo.sh +31 -3
  90. package/.tfcore/utils/tf_feedback.py +86 -18
  91. package/.tfcore/utils/tf_stack_check.py +93 -0
  92. package/README.md +2 -2
  93. package/opencode.jsonc +6 -2
  94. package/package.json +1 -1
  95. package/scripts/install.mjs +14 -3
  96. package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
  97. package/.tfcore/utils/__pycache__/tf-codex-bind.cpython-312.pyc +0 -0
  98. package/.tfcore/utils/__pycache__/tf-codex-telemetry.cpython-312.pyc +0 -0
  99. package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
  100. package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
  101. package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
  102. package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
  103. package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
  104. package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
  105. package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
  106. package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
@@ -39,9 +39,34 @@ EVERYTHING = re.compile(r"(?i)\beverything else is (?:now )?fixed\b|\ball (?:the
39
39
  CLOSED = re.compile(r"(?i)(✅|\bclosed (?:on )?\d{4}-\d{2}-\d{2}|is \*\*closed\*\*|\bwill not fix\b|\bwont-fix\b)")
40
40
  MERGED = re.compile(r"(?i)^\s*merged into\b")
41
41
  FIXED_IN_BODY = re.compile(r"(?i)\bfixed upstream\b")
42
+ STILL_OPEN = re.compile(r"(?i)\bopen\b|\bnot (?:yet )?(?:fixed|resolved)\b|\bunfixed\b")
42
43
  BLOCKS = re.compile(r"(?im)^\s*[-*]\s*\*\*Blocks:?\*\*:?\s*(yes|no)\b")
43
44
 
44
45
 
46
+ def ids_in(t):
47
+ """Every id the text names, shorthand included: "TR-RAG-028/029/030" and "TR-RAG-031..034"
48
+ (or "031–034") name each id they cover, not only the first."""
49
+ out = []
50
+ for m in re.finditer(r"(?<![\w-])([A-Z][A-Z0-9]*(?:-[A-Z]+)*)-(\d{2,})(?!\d)((?:/\d{2,})+|(?:\.\.|…|–|—)\d{2,})?", t):
51
+ pre, n, tail = m.group(1), m.group(2), m.group(3) or ""
52
+ out.append(f"{pre}-{n}")
53
+ w = len(n)
54
+ if tail.startswith("/"):
55
+ out += [f"{pre}-{x.zfill(w)}" for x in tail.strip("/").split("/")]
56
+ elif tail:
57
+ hi = int(re.sub(r"^\D+", "", tail))
58
+ if int(n) < hi <= int(n) + 50:
59
+ out += [f"{pre}-{str(x).zfill(w)}" for x in range(int(n) + 1, hi + 1)]
60
+ return out
61
+
62
+
63
+ def unquoted(t):
64
+ """The text without fenced code, inline code and double-quoted phrases."""
65
+ t = re.sub(r"(?ms)^[ \t]*```.*?^[ \t]*```", "", t)
66
+ t = re.sub(r"`[^`\n]*`", "", t)
67
+ return re.sub(r"[\"“][^\"”\n]*[\"”]", "", t)
68
+
69
+
45
70
  def entries(path):
46
71
  """-> [{id, title, state, since, blocks, start, end}] in file order."""
47
72
  text = open(path, encoding="utf-8", errors="replace").read()
@@ -51,9 +76,12 @@ def entries(path):
51
76
  end = heads[i + 1].start() if i + 1 < len(heads) else len(text)
52
77
  nxt = re.search(r"(?m)^#{1,%d}\s" % len(m.group(1)), text[m.end():end])
53
78
  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")
79
+ # quoted or code text is somebody's words, not a mark: `fixed upstream` in a repro, a test name
80
+ head = unquoted(body[:2500])
81
+ title = unquoted(m.group(3))
82
+ # the mark is read in the heading as well (TechieRag TF-001: "— **fixed upstream 2026-10-03**")
83
+ state = ("closed" if CLOSED.search(head) or CLOSED.search(title) or MERGED.search(m.group(3))
84
+ else "fixed" if FIXED_IN_BODY.search(head) or FIXED_IN_BODY.search(title) else "open")
57
85
  b = BLOCKS.search(body)
58
86
  out.setdefault(m.group(2), {"id": m.group(2), "title": m.group(3).strip(" `"), "state": state,
59
87
  "since": "", "blocks": b.group(1).lower() if b else "",
@@ -73,18 +101,33 @@ def reply_blocks(text):
73
101
  "## Resolution status (TechieFlow team, 2026-09-09)", "## Replies from …", or a quoted
74
102
  "> ## ✅ RESOLVED LIBRARY-SIDE 2026-08-31" — and runs to the next heading of its level,
75
103
  or, when quoted, to the end of the quote."""
76
- out = []
104
+ out, seen = [], set()
105
+ date_of = lambda h: (re.search(r"\d{4}-\d{2}-\d{2}", h) or [""])[0]
77
106
  for m in REPLY_HEAD.finditer(text):
107
+ if m.start() in seen:
108
+ continue
78
109
  rest = text[m.end():]
79
110
  if m.group(1): # quoted: the block is the run of '>' lines after the heading's own line
80
111
  rest = rest[1:] if rest.startswith("\n") else rest
81
112
  q = re.match(r"(?:[ \t]*>[^\n]*\n?)*", rest)
82
- body = re.sub(r"(?m)^[ \t]*>[ \t]?", "", q.group(0))
113
+ out.append((date_of(m.group(0)), re.sub(r"(?m)^[ \t]*>[ \t]?", "", q.group(0))))
114
+ continue
115
+ level = len(re.match(r"[ \t]*(#+)", m.group(0)).group(1))
116
+ nxt = re.search(r"(?m)^#{1,%d}\s" % level, rest)
117
+ section = rest[: nxt.start()] if nxt else rest
118
+ if level == 2:
119
+ # A "## Replies from …" section holds dated "### " blocks, written months apart. Each is its
120
+ # own reply: an "all others fixed" kept in an old one must not reach entries a newer block
121
+ # merely names (TechieRag TF-001: Sevak's TR-RAG-047 read fixed when TR-RAG-048 was replied to).
122
+ subs = list(re.finditer(r"(?m)^###[ \t]+[^\n]*", section))
123
+ out.append((date_of(m.group(0)), section[: subs[0].start()] if subs else section))
124
+ for j, sm in enumerate(subs):
125
+ end = subs[j + 1].start() if j + 1 < len(subs) else len(section)
126
+ seen.add(m.end() + sm.start())
127
+ # the heading line stays in: "### TF-025 — fixed 2026-09-19" names the entry it answers
128
+ out.append((date_of(sm.group(0)) or date_of(m.group(0)), section[sm.start(): end]))
83
129
  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))
130
+ out.append((date_of(m.group(0)), section))
88
131
  return out
89
132
 
90
133
 
@@ -98,24 +141,44 @@ def replied_fixed(text, known):
98
141
  items = re.split(r"\n\s*\n|\n(?=\s*[-*]\s)|\n(?=\s*\|)", body)
99
142
  mentioned = []
100
143
  for it in items:
101
- ids = ANY_ID.findall(it)
144
+ ids = ids_in(it)
102
145
  mentioned += ids
103
146
  if not ids:
104
147
  continue
105
148
  if it.lstrip().startswith("|"):
106
- first = ANY_ID.findall(it.split("|")[1] if it.count("|") > 1 else "")
149
+ first = ids_in(it.split("|")[1] if it.count("|") > 1 else "")
107
150
  out += [(i, date) for i in first]
108
- elif FIXED_WORDS.search(it):
109
- out += [(i, date) for i in ids]
151
+ elif FIXED_WORDS.search(unquoted(it)) and not EVERYTHING.search(unquoted(it)):
152
+ # "Open: TR-RAG-001, TR-RAG-002. All others fixed": the ids are the exceptions.
153
+ # Read clause by clause: "1 OPEN: TR-RAG-001 …; 2 FIXED: TR-RAG-005 …" fixes 005 only,
154
+ # and "TR-RAG-035 (minor, OPEN)" beside "TR-RAG-034 (minor, FIXED)" stays open.
155
+ # A "fixed" clause naming no id answers the clause before it: "TR-RAG-010: every
156
+ # assembly carries its version. Fixed in 1.1.2." And "TR-RAG-017/019 → BRD-126 (fixed);
157
+ # TR-RAG-020…022 → BRD-127" fixes 017 and 019 only.
158
+ prev = []
159
+ for cl in re.split(r"(?<=[.;])\s+|\s+·\s+", it):
160
+ own_ids = ids_in(cl)
161
+ bare = unquoted(cl)
162
+ if FIXED_WORDS.search(bare) and not STILL_OPEN.search(bare):
163
+ out += [(i, date) for i in (own_ids or prev)]
164
+ if own_ids:
165
+ prev = [] if STILL_OPEN.search(bare) else own_ids
110
166
  for it in items:
111
- if EVERYTHING.search(it) and mentioned:
167
+ # a quoted or code "all others fixed" is somebody's words being cited, not this reply's
168
+ # claim: "…only because of the July "All others fixed app-side" line" fixed 13 entries the
169
+ # same block listed as still open (TechieRag TF-002)
170
+ if EVERYTHING.search(unquoted(it)) and mentioned:
171
+ # bounded by the ids of its own item, which it never covers ("all others"); an item
172
+ # naming none takes the block's. A running log in one block once let a July "all
173
+ # others fixed" reach every entry its later lines named (TechieRag TF-001).
174
+ own = ids_in(it)
112
175
  top = {}
113
- for i in mentioned:
176
+ for i in own or mentioned:
114
177
  p, n = i.rsplit("-", 1)
115
178
  top[p] = max(top.get(p, 0), int(n))
116
179
  for k in known:
117
180
  p, n = k.rsplit("-", 1)
118
- if p in top and int(n) <= top[p]:
181
+ if p in top and int(n) <= top[p] and k not in own:
119
182
  out.append((k, date))
120
183
  return out
121
184
 
@@ -132,12 +195,17 @@ def upstream_of(path, app=None):
132
195
 
133
196
 
134
197
  def state_map(root):
135
- """Every entry id across the project's feedback files -> (state, "<file>, <date>")."""
198
+ """Every entry id across the project's feedback files -> [(state, "<file>, <date>", names)], one
199
+ item per file holding it. Every consumer numbers its own entries from 1, so a library with nine
200
+ consumer files can hold nine TR-002s (TrBlazeUI TF-006); `names` are the two project names in the
201
+ file's name (Lekhak and TrBlazeUI in Lekhak-TrBlazeUI-Feedback.md), which a line names to say whose."""
136
202
  out = {}
137
203
  for f in files(root):
138
204
  rel = os.path.relpath(f, root).replace(os.sep, "/")
205
+ names = tuple(os.path.basename(f)[: -len("-Feedback.md")].split("-"))
139
206
  for e in entries(f):
140
- out[e["id"]] = (e["state"], f"{rel}" + (f", Resolution status {e['since']}" if e["since"] else ""))
207
+ out.setdefault(e["id"], []).append(
208
+ (e["state"], f"{rel}" + (f", Resolution status {e['since']}" if e["since"] else ""), names))
141
209
  return out
142
210
 
143
211
 
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env python3
2
+ """tf_stack_check.py — does the code use the packages the Architecture requires? (2026-10-08)
3
+
4
+ bash .tfcore/utils/tf-stack-check.sh <App> [--json-out <file>]
5
+
6
+ Reads the `Required packages:` line of docs/<App>-Architecture.md (Stack decisions), which copies the
7
+ answer set's own line: each package in backticks, or "none". Then looks for each package in the
8
+ project's dependency files, whatever the stack: .csproj/.fsproj/.vbproj, Directory.*.props,
9
+ packages.config, package.json, requirements*.txt, pyproject.toml, Pipfile, go.mod, Cargo.toml, Gemfile,
10
+ pom.xml, build.gradle(.kts), composer.json, pubspec.yaml. Build output, dependencies, tests/.artifacts
11
+ and the framework itself are not searched.
12
+
13
+ Why: in the first *develop-end-to-end proof on OpenCode the model searched for a package by the wrong
14
+ name, decided the mandated UI library was not published, built the screen with plain components and
15
+ had every row Verified. Nothing checked that the code used what the Stack decisions named.
16
+
17
+ Prints PASS (every package referenced), FAIL (with the packages missing), or NONE (no line, or
18
+ "none"). tf-verify-verdict.py runs it itself and fails every row's build check on FAIL.
19
+ Exit 0 PASS or NONE · 1 FAIL · 2 could not run. Python 3 standard library only.
20
+ """
21
+ import json
22
+ import os
23
+ import re
24
+ import sys
25
+
26
+ PRUNE = {"bin", "obj", "node_modules", ".git", ".artifacts", ".tfcore", ".claude", ".opencode", "dist",
27
+ "packages", "TestResults", ".venv", "venv", "target", "vendor", "OldDocs"}
28
+ NAMES = re.compile(r"(?i)^(?:.*\.(?:cs|fs|vb)proj|directory\.(?:packages|build)\.props|packages\.config|package\.json|"
29
+ r"requirements[\w.-]*\.txt|pyproject\.toml|pipfile|go\.mod|cargo\.toml|gemfile|pom\.xml|"
30
+ r"build\.gradle(?:\.kts)?|composer\.json|pubspec\.yaml)$")
31
+
32
+
33
+ def required(root, app):
34
+ """-> (list of packages, the Architecture path) ; [] when there is no line or it says none."""
35
+ p = os.path.join(root, "docs", f"{app}-Architecture.md")
36
+ if not os.path.isfile(p):
37
+ return [], p
38
+ for line in open(p, encoding="utf-8", errors="replace"):
39
+ m = re.match(r"\s*(?:[-*]\s*)?\**Required packages:?\**:?\s*(.*)$", line, re.I)
40
+ if m:
41
+ return re.findall(r"`([^`\s]+)`", m.group(1)), p
42
+ return [], p
43
+
44
+
45
+ def manifests(root):
46
+ for d, dirs, files in os.walk(root):
47
+ dirs[:] = [x for x in dirs if x not in PRUNE and not x.startswith(".")]
48
+ for f in files:
49
+ if NAMES.match(f):
50
+ yield os.path.join(d, f)
51
+
52
+
53
+ def check(root, app):
54
+ pkgs, arch = required(root, app)
55
+ if not pkgs:
56
+ return {"verdict": "NONE", "architecture": os.path.relpath(arch, root), "required": [], "missing": [], "found": {}}
57
+ found = {}
58
+ files = list(manifests(root))
59
+ for f in files:
60
+ try:
61
+ text = open(f, encoding="utf-8", errors="replace").read()
62
+ except OSError:
63
+ continue
64
+ for pk in pkgs:
65
+ if re.search(r"(?<![A-Za-z0-9._-])%s(?![A-Za-z0-9._-])" % re.escape(pk), text, re.I):
66
+ found.setdefault(pk, os.path.relpath(f, root))
67
+ missing = [pk for pk in pkgs if pk not in found]
68
+ return {"verdict": "FAIL" if missing else "PASS", "architecture": os.path.relpath(arch, root),
69
+ "required": pkgs, "missing": missing, "found": found, "files_searched": len(files)}
70
+
71
+
72
+ def main(argv):
73
+ if len(argv) < 2 or argv[1] in ("-h", "--help"):
74
+ print(__doc__)
75
+ return 0 if len(argv) >= 2 else 2
76
+ app = argv[1]
77
+ r = check(os.getcwd(), app)
78
+ if "--json-out" in argv:
79
+ out = argv[argv.index("--json-out") + 1]
80
+ os.makedirs(os.path.dirname(out) or ".", exist_ok=True)
81
+ json.dump(r, open(out, "w"), indent=1)
82
+ if r["verdict"] == "NONE":
83
+ print(f"NONE {app}: {r['architecture']} lists no required packages")
84
+ elif r["verdict"] == "PASS":
85
+ print(f"PASS {app}: " + ", ".join(f"{k} in {v}" for k, v in r["found"].items()))
86
+ else:
87
+ print(f"FAIL {app}: the Architecture requires {', '.join(r['missing'])}; no project file references "
88
+ + ("it" if len(r["missing"]) == 1 else "them") + f" ({r['files_searched']} searched). Add the package; never replace it with a workaround")
89
+ return 1 if r["verdict"] == "FAIL" else 0
90
+
91
+
92
+ if __name__ == "__main__":
93
+ sys.exit(main(sys.argv))
package/README.md CHANGED
@@ -86,7 +86,7 @@ flowchart TD
86
86
 
87
87
  **An existing application** is the same after day-1: `*day1-brownfield` reads the code, writes the same documents plus the DevGuide, and migrates any existing plan into the one checklist.
88
88
 
89
- **Which command next?** Pick by the weakest open row: anything unbuilt means `*build-phase`; all built but not verified means `*verify all`; all verified means `*handoff-phase`. When in doubt, build. If a session died mid-phase, the status file is stale — run `*refresh-status {App}` first, which rebuilds it from the checklist, the working tree and a fresh build.
89
+ **Which command next?** Pick by the weakest open row: anything unbuilt means `*build-phase`; all built but not verified means `*verify all`; all verified means `*handoff-phase`. When in doubt, build. A project in phases verifies one phase at a time: `*verify all {App} --phase 1` grades phase 1's checklist, and `--phase all` grades every phase in turn. Without `--phase` it grades the phase in progress. PROJECT-STATUS prints the exact line to paste. If a session died mid-phase, the status file is stale — run `*refresh-status {App}` first, which rebuilds it from the checklist, the working tree and a fresh build.
90
90
 
91
91
  **Running unattended.** Do not start a long run as a bare harness command. The supervisor survives a usage limit, a crash and an agent that stops early:
92
92
 
@@ -110,7 +110,7 @@ Replace `{App}` with your application name. Claude Code takes the long persona p
110
110
  | A change to an existing project | `… analyst *amend-docs {App} "<what changed>"` | `/flow-analyst *amend-docs {App} "…"` |
111
111
  | Brainstorm, brief, research | `… analyst *brainstorm {topic}` · `*create-project-brief` · `*research-prompt {topic}` | same, on `/flow-analyst` |
112
112
  | Build everything open | `/TechieFlow:agents:flow-master *build-phase {App}` | `/flow-master *build-phase {App}` |
113
- | Verify | `/TechieFlow:agents:verifier *verify ui\|functional\|all` | `/flow-verifier *verify all` |
113
+ | Verify | `/TechieFlow:agents:verifier *verify ui\|functional\|all {App} [--phase N\|all]` | `/flow-verifier *verify all {App} [--phase N\|all]` |
114
114
  | Bugs: log and analyse only | `… flow-master *triage-issues {App} {evidence} [verify]` | `/flow-master *triage-issues …` |
115
115
  | Bugs: fix them | `… flow-master *fix-issues {App} {folder}` | `/flow-master *fix-issues …` |
116
116
  | Bugs: the whole sequence unattended | `… flow-master *triage-and-fix {App} {evidence}` | `/flow-master *triage-and-fix …` |
package/opencode.jsonc CHANGED
@@ -589,7 +589,7 @@
589
589
  "sudo *": "ask"
590
590
  }
591
591
  },
592
- "description": "Vidur — autonomous verification (*verify ui|functional|all); writes verdicts into the checklist Requirements Status tables"
592
+ "description": "Vidur — autonomous verification (*verify ui|functional|all [--phase N|all]); writes verdicts into the checklist Requirements Status tables"
593
593
  },
594
594
  "trblazeui": {
595
595
  "prompt": "You are the TrBlazeUI sub-agent. Read `.opencode/command/trblazeui.md` (fall back, in order, to `.claude/commands/trblazeui.md`, `.claude/trblazeui.md`, `.trblazeui/TrBlazeUI-AI-Reference.md`) and fully adopt that persona and its operating guidelines — it is the authoritative persona. NuGet credentials are user-level (Windows native `%AppData%\\NuGet\\NuGet.Config`, macOS/Linux `$HOME/.nuget/NuGet/NuGet.Config`, Docker `/root/.nuget/NuGet/NuGet.Config` from `%USERPROFILE%\\.opencode-docker\\nuget\\NuGet.Config`); never put a PAT in the repository. In Docker, never assume a Windows config is decryptable and never install `maui-tizen`; verify the mounted config and use the Docker build ladder. If neither file exists (TrBlazeUI NuGet not yet deployed), report the exact source/auth failure instead of blindly running `dotnet build`. Otherwise act as an expert .NET/Blazor developer specializing in the TrBlazeUI component library: build UI screens to match the approved mockups (`docs/{AppName}-UIDesign.md` + `docs/mockups/*.html`), follow `docs/{AppName}-Coding-Standards.md`, tag work with `[REQ-UI-N]` in the checklist Remarks, log TrBlazeUI gaps to `docs/{AppName}-TrBlazeUI-Feedback.md`. Never run git or gh.",
@@ -833,6 +833,10 @@
833
833
  "template": "{file:./.tfcore/tasks/split-brd.md}",
834
834
  "description": "Split the BRD into the one docs/{App}-Checklist.md (REQ-UI/FN/RAG/NFR-* in a single Requirements Status table)."
835
835
  },
836
+ "techieflow:tasks:develop-end-to-end": {
837
+ "template": "{file:./.tfcore/tasks/develop-end-to-end.md}",
838
+ "description": "A new app from a brief to UAT, unattended: Day 1 both stages, build with inline verify and fixes, handoff; tf-develop.sh commits and pushes after each phase and writes the Build Report with time and tokens per phase. Driven by /flow-master."
839
+ },
836
840
  "techieflow:tasks:build-phase": {
837
841
  "template": "{file:./.tfcore/tasks/build-phase.md}",
838
842
  "description": "The single unified build: implement every open REQ (UI/FN/RAG/NFR) from the one Checklist by clustering and calling /trblazeui (from mockups) + /techierag as sub-agents; self-smoke (data + visual); chain the verifier. Driven by /flow-master."
@@ -855,7 +859,7 @@
855
859
  },
856
860
  "techieflow:tasks:verify-phase": {
857
861
  "template": "{file:./.tfcore/tasks/verify-phase.md}",
858
- "description": "Verifies every checklist row in a scope (ui | functional | all | REQ list) against the running app: boots it, runs the seven checks in order through the tf-verify-* scripts, writes the verdicts into the checklist and the telemetry. YOLO by default. Driven by /verifier."
862
+ "description": "Verifies every checklist row in a scope (ui | functional | all | REQ list) of one phase's checklist, or each phase in turn with --phase all, against the running app: boots it, runs the seven checks in order through the tf-verify-* scripts, writes the verdicts into the checklist and the telemetry. YOLO by default. Driven by /verifier."
859
863
  },
860
864
  "techieflow:tasks:handoff-phase": {
861
865
  "template": "{file:./.tfcore/tasks/handoff-phase.md}",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techierathore/techieflow",
3
- "version": "1.1.0",
3
+ "version": "1.2.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": {
@@ -313,7 +313,7 @@ const canonicalSettings = String.raw`{
313
313
  ],
314
314
  "ask": [],
315
315
  "deny": [
316
- "Bash(rm -rf /)", "Bash(rm -rf /*)", "Bash(rm -rf ~)", "Bash(rm -rf ~/*)",
316
+ "Bash(rm -rf /)", "Bash(rm -rf ~)",
317
317
  "Bash(git commit*)", "Bash(git push*)", "Bash(git add*)", "Bash(git rm*)",
318
318
  "Bash(git mv*)", "Bash(git reset*)", "Bash(git checkout*)", "Bash(git switch*)",
319
319
  "Bash(git restore*)", "Bash(git merge*)", "Bash(git rebase*)", "Bash(git cherry-pick*)",
@@ -392,6 +392,15 @@ const canonicalSettings = String.raw`{
392
392
  "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/guard-verify.sh\""
393
393
  }
394
394
  ]
395
+ },
396
+ {
397
+ "matcher": "*",
398
+ "hooks": [
399
+ {
400
+ "type": "command",
401
+ "command": "bash \"$CLAUDE_PROJECT_DIR/.tfcore/hooks/yolo-allow.sh\""
402
+ }
403
+ ]
395
404
  }
396
405
  ],
397
406
  "SessionStart": [
@@ -723,7 +732,9 @@ function migrateLegacyLayout() {
723
732
  const rootConfig = join(target, "opencode.jsonc");
724
733
  if (existsSync(rootConfig) && legacyConfigPattern.test(read(rootConfig))) {
725
734
  copyFileSync(rootConfig, `${rootConfig}.bak`);
726
- copyFileSync(join(sourceRoot, "opencode.jsonc"), rootConfig);
735
+ // the content only, as the shell's `cp` onto an existing file does: copyFileSync also copies the
736
+ // template's mode, so an executable checkout of opencode.jsonc gave a different file (CI, 2026-10-04)
737
+ writeFileSync(rootConfig, readFileSync(join(sourceRoot, "opencode.jsonc")));
727
738
  say(" opencode.jsonc — legacy version backed up to opencode.jsonc.bak, replaced with current template");
728
739
  }
729
740
  say("");
@@ -742,7 +753,7 @@ function refreshRootOpenCodeConfig() {
742
753
  if (dryRun) say(" opencode.jsonc — WOULD refresh from template (no project-only content; old file → .bak)");
743
754
  else {
744
755
  copyFileSync(rootConfig, `${rootConfig}.bak`);
745
- copyFileSync(join(sourceRoot, "opencode.jsonc"), rootConfig);
756
+ writeFileSync(rootConfig, readFileSync(join(sourceRoot, "opencode.jsonc"))); // keeps its mode, as `cp` does
746
757
  say(" opencode.jsonc — refreshed from template (no project-only content; old file → opencode.jsonc.bak)");
747
758
  replaced = true;
748
759
  }