@techierathore/techieflow 1.0.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 +97 -0
- package/.claude/commands/TechieFlow/agents/architect.md +84 -0
- package/.claude/commands/TechieFlow/agents/flow-master.md +217 -0
- package/.claude/commands/TechieFlow/agents/verifier.md +72 -0
- package/.claude/commands/TechieFlow/tasks/_metrics-emit-gate.md +138 -0
- package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +98 -0
- package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +78 -0
- package/.claude/commands/TechieFlow/tasks/_yolo-mode.md +77 -0
- package/.claude/commands/TechieFlow/tasks/advanced-elicitation.md +117 -0
- package/.claude/commands/TechieFlow/tasks/amend-docs.md +149 -0
- package/.claude/commands/TechieFlow/tasks/author-brd.md +79 -0
- package/.claude/commands/TechieFlow/tasks/build-phase.md +231 -0
- package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +278 -0
- package/.claude/commands/TechieFlow/tasks/create-doc.md +101 -0
- package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +507 -0
- package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +224 -0
- package/.claude/commands/TechieFlow/tasks/devguide.md +224 -0
- package/.claude/commands/TechieFlow/tasks/document-project.md +343 -0
- package/.claude/commands/TechieFlow/tasks/execute-checklist.md +86 -0
- package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +136 -0
- package/.claude/commands/TechieFlow/tasks/fix-issues.md +134 -0
- package/.claude/commands/TechieFlow/tasks/generate-html.md +115 -0
- package/.claude/commands/TechieFlow/tasks/handoff-phase.md +126 -0
- package/.claude/commands/TechieFlow/tasks/index-docs.md +173 -0
- package/.claude/commands/TechieFlow/tasks/kb-mode-interaction.md +75 -0
- package/.claude/commands/TechieFlow/tasks/log-miss.md +160 -0
- package/.claude/commands/TechieFlow/tasks/metrics-report.md +143 -0
- package/.claude/commands/TechieFlow/tasks/mockups.md +122 -0
- package/.claude/commands/TechieFlow/tasks/productguide.md +105 -0
- package/.claude/commands/TechieFlow/tasks/refresh-status.md +135 -0
- package/.claude/commands/TechieFlow/tasks/render-workflow-docs.md +117 -0
- package/.claude/commands/TechieFlow/tasks/shard-doc.md +185 -0
- package/.claude/commands/TechieFlow/tasks/split-brd.md +131 -0
- package/.claude/commands/TechieFlow/tasks/triage-issues.md +157 -0
- package/.claude/commands/TechieFlow/tasks/verify-phase.md +579 -0
- package/.claude/commands/generate-html.md +124 -0
- package/.codex/config.toml +46 -0
- package/.codex/hooks.json +63 -0
- package/.codex/rules/techieflow.rules +8 -0
- package/.opencode/command/generate-html.md +129 -0
- package/.opencode/plugin/techieflow.js +350 -0
- package/.tfcore/TOKEN-GUIDE.md +59 -0
- package/.tfcore/agents/analyst.md +97 -0
- package/.tfcore/agents/architect.md +84 -0
- package/.tfcore/agents/flow-master.md +217 -0
- package/.tfcore/agents/verifier.md +72 -0
- package/.tfcore/checklists/architect-checklist.md +438 -0
- package/.tfcore/core-config.yaml +47 -0
- package/.tfcore/data/brainstorming-techniques.md +36 -0
- package/.tfcore/data/elicitation-methods.md +154 -0
- package/.tfcore/data/techieflow-kb.md +807 -0
- package/.tfcore/data/technical-preferences.md +3 -0
- package/.tfcore/enhanced-ide-development-workflow.md +248 -0
- package/.tfcore/hooks/block-git.sh +301 -0
- package/.tfcore/hooks/codex-adapter.py +248 -0
- package/.tfcore/hooks/guard-artifacts.sh +101 -0
- package/.tfcore/hooks/guard-status-html.sh +102 -0
- package/.tfcore/hooks/guard-status.sh +124 -0
- package/.tfcore/hooks/guard-verify.sh +108 -0
- package/.tfcore/hooks/metrics-session.sh +129 -0
- package/.tfcore/hooks/session-pointer.sh +52 -0
- package/.tfcore/hooks/sweep-artifacts.sh +198 -0
- package/.tfcore/install-manifest.yaml +231 -0
- package/.tfcore/routing.yaml +105 -0
- package/.tfcore/tasks/_metrics-emit-gate.md +138 -0
- package/.tfcore/tasks/_smoke-test-policy.md +98 -0
- package/.tfcore/tasks/_status-update-gate.md +78 -0
- package/.tfcore/tasks/_yolo-mode.md +77 -0
- package/.tfcore/tasks/advanced-elicitation.md +117 -0
- package/.tfcore/tasks/amend-docs.md +149 -0
- package/.tfcore/tasks/author-brd.md +79 -0
- package/.tfcore/tasks/build-phase.md +231 -0
- package/.tfcore/tasks/create-deep-research-prompt.md +278 -0
- package/.tfcore/tasks/create-doc.md +101 -0
- package/.tfcore/tasks/day1-brownfield.md +507 -0
- package/.tfcore/tasks/day1-greenfield.md +224 -0
- package/.tfcore/tasks/devguide.md +224 -0
- package/.tfcore/tasks/document-project.md +343 -0
- package/.tfcore/tasks/execute-checklist.md +86 -0
- package/.tfcore/tasks/facilitate-brainstorming-session.md +136 -0
- package/.tfcore/tasks/fix-issues.md +134 -0
- package/.tfcore/tasks/generate-html.md +115 -0
- package/.tfcore/tasks/handoff-phase.md +126 -0
- package/.tfcore/tasks/index-docs.md +173 -0
- package/.tfcore/tasks/kb-mode-interaction.md +75 -0
- package/.tfcore/tasks/log-miss.md +160 -0
- package/.tfcore/tasks/metrics-report.md +143 -0
- package/.tfcore/tasks/mockups.md +122 -0
- package/.tfcore/tasks/productguide.md +105 -0
- package/.tfcore/tasks/refresh-status.md +135 -0
- package/.tfcore/tasks/render-workflow-docs.md +117 -0
- package/.tfcore/tasks/shard-doc.md +185 -0
- package/.tfcore/tasks/split-brd.md +131 -0
- package/.tfcore/tasks/triage-issues.md +157 -0
- package/.tfcore/tasks/verify-phase.md +579 -0
- package/.tfcore/telemetry/SCHEMA.md +678 -0
- package/.tfcore/telemetry/install-metrics.sh +390 -0
- package/.tfcore/telemetry/pre-commit +94 -0
- package/.tfcore/telemetry/tf-metrics.sh +1415 -0
- package/.tfcore/templates/architecture-tmpl.yaml +650 -0
- package/.tfcore/templates/brainstorming-output-tmpl.yaml +156 -0
- package/.tfcore/templates/brownfield-architecture-tmpl.yaml +476 -0
- package/.tfcore/templates/competitor-analysis-tmpl.yaml +306 -0
- package/.tfcore/templates/front-end-architecture-tmpl.yaml +218 -0
- package/.tfcore/templates/fullstack-architecture-tmpl.yaml +823 -0
- package/.tfcore/templates/market-research-tmpl.yaml +252 -0
- package/.tfcore/templates/project-brief-tmpl.yaml +221 -0
- package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +61 -0
- package/.tfcore/templates/v4custom/app-architecture-tmpl.md +127 -0
- package/.tfcore/templates/v4custom/app-brd-tmpl.md +204 -0
- package/.tfcore/templates/v4custom/app-checklist-tmpl.md +90 -0
- package/.tfcore/templates/v4custom/app-claude-md-tmpl.md +33 -0
- package/.tfcore/templates/v4custom/app-devguide-tmpl.md +167 -0
- package/.tfcore/templates/v4custom/app-editorconfig-tmpl.editorconfig +44 -0
- package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +31 -0
- package/.tfcore/templates/v4custom/app-productguide-tmpl.md +54 -0
- package/.tfcore/templates/v4custom/app-project-status-tmpl.md +98 -0
- package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +48 -0
- package/.tfcore/templates/v4custom/app-usageguide-tmpl.md +67 -0
- package/.tfcore/templates/v4custom/build-invocation-ladder.md +190 -0
- package/.tfcore/templates/v4custom/html-render-shell.md +502 -0
- package/.tfcore/templates/v4custom/metrics-report-template.md +273 -0
- package/.tfcore/templates/v4custom/opencode-operating-contract.md +56 -0
- package/.tfcore/user-guide.md +633 -0
- package/.tfcore/utils/techieflow-doc-template.md +325 -0
- package/.tfcore/utils/tf-assets.sh +310 -0
- package/.tfcore/utils/tf-codex-bind.py +196 -0
- package/.tfcore/utils/tf-codex-telemetry.py +60 -0
- package/.tfcore/utils/tf-emit.sh +1075 -0
- package/.tfcore/utils/tf-gitignore-audit.sh +281 -0
- package/.tfcore/utils/tf-goal.sh +421 -0
- package/.tfcore/utils/tf-harness.sh +160 -0
- package/.tfcore/utils/tf-mockup-parity.mjs +555 -0
- package/.tfcore/utils/tf-mockup-parity.sh +65 -0
- package/.tfcore/utils/tf-opencode-audit.py +126 -0
- package/.tfcore/utils/tf-perf.sh +241 -0
- package/.tfcore/utils/tf-render-html.py +715 -0
- package/.tfcore/utils/tf-render-html.sh +28 -0
- package/.tfcore/utils/tf-routing-bind.sh +216 -0
- package/.tfcore/utils/tf-routing.sh +247 -0
- package/.tfcore/utils/tf-yolo.sh +119 -0
- package/.tfcore/working-in-the-brownfield.md +606 -0
- package/LICENSE +201 -0
- package/README.md +1212 -0
- package/WORKFLOW.html +1986 -0
- package/docs/TechieFlow-Installation.md +299 -0
- package/opencode.jsonc +929 -0
- package/package.json +60 -0
- package/scripts/install.mjs +992 -0
- package/scripts/test-install.mjs +505 -0
- package/scripts/validate.mjs +155 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# TechieFlow telemetry — SessionEnd hook -> docs/metrics/sessions.jsonl
|
|
3
|
+
#
|
|
4
|
+
# Wired in .claude/settings.json → hooks.SessionEnd (no matcher — SessionEnd has
|
|
5
|
+
# no tool to match on). The event name was verified against the installed Claude
|
|
6
|
+
# Code version (2.1.226) rather than assumed; see DECISIONS.md.
|
|
7
|
+
#
|
|
8
|
+
# Reads the hook payload on stdin: {"session_id","transcript_path","cwd",
|
|
9
|
+
# "hook_event_name","reason"}. Locates the transcript, sums token usage, emits ONE
|
|
10
|
+
# session record through tf-emit.sh.
|
|
11
|
+
#
|
|
12
|
+
# SILENT ON ANY FAILURE — exits 0 unconditionally, prints nothing. Telemetry has
|
|
13
|
+
# no veto: a SessionEnd hook that errors would surface noise at the exact moment
|
|
14
|
+
# the owner is closing a session. Same fail-open posture as guard-status.sh /
|
|
15
|
+
# guard-verify.sh. TF_METRICS_DEBUG=1 to see why an event was dropped.
|
|
16
|
+
#
|
|
17
|
+
# PRIVACY: reads the transcript ONLY for usage counters, model id, and timestamps.
|
|
18
|
+
# No message content, no prompt text, no tool input is ever read out or stored.
|
|
19
|
+
|
|
20
|
+
INPUT="$(cat 2>/dev/null)"
|
|
21
|
+
[[ -n "$INPUT" ]] || exit 0
|
|
22
|
+
command -v python3 >/dev/null 2>&1 || exit 0
|
|
23
|
+
|
|
24
|
+
TF_ERR=/dev/null
|
|
25
|
+
[[ -n "${TF_METRICS_DEBUG:-}" ]] && TF_ERR=/dev/stderr
|
|
26
|
+
|
|
27
|
+
PROG="$(cat <<'PY'
|
|
28
|
+
import json, os, sys
|
|
29
|
+
|
|
30
|
+
def bail(msg):
|
|
31
|
+
if os.environ.get("TF_METRICS_DEBUG"):
|
|
32
|
+
sys.stderr.write("metrics-session: %s\n" % msg)
|
|
33
|
+
raise SystemExit(0)
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
payload = json.loads(sys.stdin.read())
|
|
37
|
+
except Exception as e:
|
|
38
|
+
bail("payload is not JSON (%s)" % e)
|
|
39
|
+
|
|
40
|
+
root = payload.get("cwd") or os.environ.get("CLAUDE_PROJECT_DIR") or ""
|
|
41
|
+
if not root or not os.path.isdir(root):
|
|
42
|
+
bail("no usable cwd in payload")
|
|
43
|
+
|
|
44
|
+
# Only emit into a repo the framework has actually set up — the scaffold/update
|
|
45
|
+
# scripts create docs/metrics/. Without it, a session in an unrelated directory
|
|
46
|
+
# would litter.
|
|
47
|
+
met = os.path.join(root, "docs", "metrics")
|
|
48
|
+
if not os.path.isdir(met):
|
|
49
|
+
bail("no docs/metrics in %s — framework refresh has not run here" % root)
|
|
50
|
+
|
|
51
|
+
tpath = payload.get("transcript_path") or ""
|
|
52
|
+
if not tpath or not os.path.isfile(tpath):
|
|
53
|
+
bail("transcript not found at %r" % tpath)
|
|
54
|
+
|
|
55
|
+
model = None
|
|
56
|
+
inp = out = cread = ccreate = 0
|
|
57
|
+
first = last = None
|
|
58
|
+
try:
|
|
59
|
+
with open(tpath, encoding="utf-8", errors="replace") as fh:
|
|
60
|
+
for line in fh:
|
|
61
|
+
line = line.strip()
|
|
62
|
+
if not line:
|
|
63
|
+
continue
|
|
64
|
+
try:
|
|
65
|
+
r = json.loads(line)
|
|
66
|
+
except Exception:
|
|
67
|
+
continue
|
|
68
|
+
ts = r.get("timestamp")
|
|
69
|
+
if ts:
|
|
70
|
+
if first is None:
|
|
71
|
+
first = ts
|
|
72
|
+
last = ts
|
|
73
|
+
if r.get("type") != "assistant":
|
|
74
|
+
continue
|
|
75
|
+
m = r.get("message") or {}
|
|
76
|
+
if m.get("model"):
|
|
77
|
+
model = m["model"]
|
|
78
|
+
u = m.get("usage") or {}
|
|
79
|
+
inp += u.get("input_tokens") or 0
|
|
80
|
+
out += u.get("output_tokens") or 0
|
|
81
|
+
cread += u.get("cache_read_input_tokens") or 0
|
|
82
|
+
ccreate += u.get("cache_creation_input_tokens") or 0
|
|
83
|
+
except Exception as e:
|
|
84
|
+
bail("could not read transcript (%s)" % e)
|
|
85
|
+
|
|
86
|
+
def secs(a, b):
|
|
87
|
+
try:
|
|
88
|
+
import datetime
|
|
89
|
+
f = lambda s: datetime.datetime.strptime(s.replace("Z", "+0000")[:19] + "+0000",
|
|
90
|
+
"%Y-%m-%dT%H:%M:%S%z")
|
|
91
|
+
return max(0, int((f(b) - f(a)).total_seconds()))
|
|
92
|
+
except Exception:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
rec = {
|
|
96
|
+
"kind": "session",
|
|
97
|
+
"session_id": payload.get("session_id"),
|
|
98
|
+
"harness": "claude-code",
|
|
99
|
+
"model": model,
|
|
100
|
+
"duration_s": secs(first, last) if (first and last) else None,
|
|
101
|
+
"input_tokens": inp,
|
|
102
|
+
"output_tokens": out,
|
|
103
|
+
"cache_read_tokens": cread,
|
|
104
|
+
"cache_creation_tokens": ccreate,
|
|
105
|
+
# NEVER estimated from a rate card — the transcript carries no cost. SCHEMA.md §4.
|
|
106
|
+
"cost_usd": None,
|
|
107
|
+
}
|
|
108
|
+
if last:
|
|
109
|
+
rec["ts"] = last[:19] + "Z"
|
|
110
|
+
|
|
111
|
+
sys.stdout.write(json.dumps(rec, separators=(",", ":")))
|
|
112
|
+
sys.stdout.write("\n")
|
|
113
|
+
sys.stdout.write(root)
|
|
114
|
+
sys.stdout.write("\n")
|
|
115
|
+
PY
|
|
116
|
+
)"
|
|
117
|
+
|
|
118
|
+
OUT="$(printf '%s' "$INPUT" | python3 -c "$PROG" 2>"$TF_ERR")" || exit 0
|
|
119
|
+
[[ -n "$OUT" ]] || exit 0
|
|
120
|
+
|
|
121
|
+
REC="$(printf '%s' "$OUT" | sed -n '1p')"
|
|
122
|
+
ROOT="$(printf '%s' "$OUT" | sed -n '2p')"
|
|
123
|
+
[[ -n "$REC" && -n "$ROOT" ]] || exit 0
|
|
124
|
+
|
|
125
|
+
EMIT="$ROOT/.tfcore/utils/tf-emit.sh"
|
|
126
|
+
[[ -f "$EMIT" ]] || exit 0
|
|
127
|
+
printf '%s' "$REC" | TF_METRICS_ROOT="$ROOT" bash "$EMIT" sessions >/dev/null 2>"$TF_ERR"
|
|
128
|
+
|
|
129
|
+
exit 0
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# TechieFlow hook — records the Claude Code session pointer so telemetry can
|
|
3
|
+
# attribute tokens to a run (docs/Telemetry-Hooks.md §2; DECISIONS.md 2026-08-20).
|
|
4
|
+
#
|
|
5
|
+
# Wired in .claude/settings.json → hooks.SessionStart + hooks.UserPromptSubmit.
|
|
6
|
+
# Payload carries {session_id, transcript_path, cwd, hook_event_name, model?}.
|
|
7
|
+
# Writes .tfcore/.session/claude-code.json = {session_id, transcript_path,
|
|
8
|
+
# model?, ts} (gitignored; last-writer-wins is fine for a single-owner repo).
|
|
9
|
+
# tf-emit.sh reads it to compute a per-run token window over the transcript.
|
|
10
|
+
#
|
|
11
|
+
# HARD RULE — NO VETO. Exits 0 unconditionally; on any failure the pointer is
|
|
12
|
+
# simply not refreshed and the run record degrades to tokens_scope:"none".
|
|
13
|
+
# The OpenCode equivalent is written by .opencode/plugin/techieflow.js.
|
|
14
|
+
|
|
15
|
+
INPUT="$(cat)"
|
|
16
|
+
command -v python3 >/dev/null 2>&1 || exit 0
|
|
17
|
+
|
|
18
|
+
TF_HOOK_INPUT="$INPUT" python3 - <<'PY' 2>/dev/null
|
|
19
|
+
import json, os, datetime
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
data = json.loads(os.environ.get("TF_HOOK_INPUT", ""))
|
|
23
|
+
except Exception:
|
|
24
|
+
raise SystemExit(0)
|
|
25
|
+
|
|
26
|
+
root = os.environ.get("CLAUDE_PROJECT_DIR") or data.get("cwd") or ""
|
|
27
|
+
if not root or not os.path.isdir(os.path.join(root, ".tfcore")):
|
|
28
|
+
raise SystemExit(0)
|
|
29
|
+
|
|
30
|
+
sid = data.get("session_id")
|
|
31
|
+
tpath = data.get("transcript_path")
|
|
32
|
+
if not sid or not tpath:
|
|
33
|
+
raise SystemExit(0)
|
|
34
|
+
|
|
35
|
+
rec = {
|
|
36
|
+
"session_id": sid,
|
|
37
|
+
"transcript_path": tpath,
|
|
38
|
+
"ts": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
39
|
+
}
|
|
40
|
+
if data.get("model"):
|
|
41
|
+
rec["model"] = data["model"]
|
|
42
|
+
|
|
43
|
+
d = os.path.join(root, ".tfcore", ".session")
|
|
44
|
+
try:
|
|
45
|
+
os.makedirs(d, exist_ok=True)
|
|
46
|
+
with open(os.path.join(d, "claude-code.json"), "w") as fh:
|
|
47
|
+
json.dump(rec, fh)
|
|
48
|
+
fh.write("\n")
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
PY
|
|
52
|
+
exit 0
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# TechieFlow hook — automatic sweep of expired run material (added 2026-08-26).
|
|
3
|
+
#
|
|
4
|
+
# Wired as a SessionStart hook in .claude/settings.json (Claude Code), as
|
|
5
|
+
# `codex-adapter.py session-start` (Codex) and on the first root
|
|
6
|
+
# `session.created` in .opencode/plugin/techieflow.js (OpenCode).
|
|
7
|
+
#
|
|
8
|
+
# WHY: verify-phase.md §1 pins every run artifact under tests/.artifacts/ and
|
|
9
|
+
# the guard-artifacts.sh PreToolUse hook enforces it. That fixed WHERE the
|
|
10
|
+
# material lands but not that it ever leaves: Playwright wipes only its own
|
|
11
|
+
# outputDir, so per-cluster subfolders, harness scripts and multi-hundred-MB
|
|
12
|
+
# host logs accumulate forever (TechieBlog: 1.1 GB under tests/.artifacts/ +
|
|
13
|
+
# 101 MB of .verify/*.log, most of it two weeks stale). Run material is only
|
|
14
|
+
# needed until its verify pass has written the checklist Remarks — after that
|
|
15
|
+
# it is litter. This hook deletes it once it ages out, so nobody has to.
|
|
16
|
+
#
|
|
17
|
+
# POLICY
|
|
18
|
+
# Sweep roots (all gitignored, all machine-generated, never work product):
|
|
19
|
+
# tests/.artifacts/ .verify/
|
|
20
|
+
# Files older than the retention window are deleted; directories left empty
|
|
21
|
+
# are removed. Newer files are untouched — a run in flight is never disturbed
|
|
22
|
+
# (its files are minutes old), and a mixed-age dir like tests/.artifacts/harness/
|
|
23
|
+
# keeps its recent scripts and loses its stale ones.
|
|
24
|
+
# Repo-root legacy dirs (banned outright by the artifact-location rule, kept
|
|
25
|
+
# only by the .gitignore for legacy trees) are removed REGARDLESS of age:
|
|
26
|
+
# test-results/ test-results-*/ scripts-*/ playwright-report/
|
|
27
|
+
# The project's own tracked scripts/ is never touched (needs the hyphen).
|
|
28
|
+
# tests/verify/ (promoted, tracked specs) is never touched (outside the roots).
|
|
29
|
+
#
|
|
30
|
+
# RETENTION: default 7 days. Override per project with `artifactRetentionDays: N`
|
|
31
|
+
# in .tfcore/core-config.yaml, or per run with TF_ARTIFACT_RETENTION_DAYS=N.
|
|
32
|
+
# 0 disables the age sweep (legacy root dirs are still removed).
|
|
33
|
+
# TF_SWEEP_DRY_RUN=1 prints what would go and deletes nothing.
|
|
34
|
+
# Throttled to one sweep per hour per project via .tfcore/.session/sweep.stamp
|
|
35
|
+
# (TF_SWEEP_FORCE=1 bypasses) so harnesses that re-fire session-start per prompt
|
|
36
|
+
# do not walk the tree every turn.
|
|
37
|
+
#
|
|
38
|
+
# HARD RULES — NO VETO, NO ESCAPE. Exits 0 unconditionally (a sweep that fails
|
|
39
|
+
# must never block a session). Every path is resolved and checked to sit under
|
|
40
|
+
# the project root; symlinks are never followed; nothing outside the listed
|
|
41
|
+
# roots is ever considered. stdout (a one-line summary, only when something was
|
|
42
|
+
# removed) is surfaced into the session so the agent knows the sweep ran.
|
|
43
|
+
|
|
44
|
+
INPUT="$(cat 2>/dev/null)"
|
|
45
|
+
command -v python3 >/dev/null 2>&1 || exit 0
|
|
46
|
+
|
|
47
|
+
TF_HOOK_INPUT="$INPUT" python3 - <<'PY' 2>/dev/null
|
|
48
|
+
import json, os, re, shutil, sys, time, fnmatch
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
data = json.loads(os.environ.get("TF_HOOK_INPUT") or "{}")
|
|
52
|
+
except Exception:
|
|
53
|
+
data = {}
|
|
54
|
+
|
|
55
|
+
root = os.environ.get("CLAUDE_PROJECT_DIR") or os.environ.get("TF_PROJECT_DIR") or data.get("cwd") or ""
|
|
56
|
+
if not root or not os.path.isdir(os.path.join(root, ".tfcore")):
|
|
57
|
+
raise SystemExit(0)
|
|
58
|
+
root = os.path.realpath(root)
|
|
59
|
+
|
|
60
|
+
DRY = os.environ.get("TF_SWEEP_DRY_RUN") == "1"
|
|
61
|
+
|
|
62
|
+
# Throttle: at most one sweep per hour per project, whichever harness fires it
|
|
63
|
+
# (Codex re-fires session-start on every prompt; a walk of a big tree on every
|
|
64
|
+
# turn is waste). TF_SWEEP_FORCE=1 bypasses. Dry runs never touch the stamp.
|
|
65
|
+
stamp = os.path.join(root, ".tfcore", ".session", "sweep.stamp")
|
|
66
|
+
if not DRY and os.environ.get("TF_SWEEP_FORCE") != "1":
|
|
67
|
+
try:
|
|
68
|
+
if time.time() - os.stat(stamp).st_mtime < 3600:
|
|
69
|
+
raise SystemExit(0)
|
|
70
|
+
except FileNotFoundError:
|
|
71
|
+
pass
|
|
72
|
+
try:
|
|
73
|
+
os.makedirs(os.path.dirname(stamp), exist_ok=True)
|
|
74
|
+
with open(stamp, "w") as fh:
|
|
75
|
+
fh.write(time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) + "\n")
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
# ---- retention -------------------------------------------------------------
|
|
80
|
+
def retention_days():
|
|
81
|
+
env = os.environ.get("TF_ARTIFACT_RETENTION_DAYS")
|
|
82
|
+
if env is not None and env.strip() != "":
|
|
83
|
+
try:
|
|
84
|
+
return max(0, int(env))
|
|
85
|
+
except ValueError:
|
|
86
|
+
pass
|
|
87
|
+
try:
|
|
88
|
+
with open(os.path.join(root, ".tfcore", "core-config.yaml"), encoding="utf-8", errors="replace") as fh:
|
|
89
|
+
for line in fh:
|
|
90
|
+
m = re.match(r"^\s*artifactRetentionDays\s*:\s*(\d+)\s*(#.*)?$", line)
|
|
91
|
+
if m:
|
|
92
|
+
return max(0, int(m.group(1)))
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
95
|
+
return 7
|
|
96
|
+
|
|
97
|
+
DAYS = retention_days()
|
|
98
|
+
CUTOFF = time.time() - DAYS * 86400
|
|
99
|
+
|
|
100
|
+
SWEEP_ROOTS = [os.path.join("tests", ".artifacts"), ".verify"]
|
|
101
|
+
LEGACY_ROOT_GLOBS = ["test-results", "test-results-*", "scripts-*", "playwright-report"]
|
|
102
|
+
|
|
103
|
+
removed_files = 0
|
|
104
|
+
removed_dirs = 0
|
|
105
|
+
removed_bytes = 0
|
|
106
|
+
legacy = []
|
|
107
|
+
|
|
108
|
+
def inside(p):
|
|
109
|
+
rp = os.path.realpath(p)
|
|
110
|
+
return rp == root or rp.startswith(root + os.sep)
|
|
111
|
+
|
|
112
|
+
def rm_file(p):
|
|
113
|
+
global removed_files, removed_bytes
|
|
114
|
+
try:
|
|
115
|
+
sz = os.lstat(p).st_size
|
|
116
|
+
if not DRY:
|
|
117
|
+
os.unlink(p)
|
|
118
|
+
removed_files += 1
|
|
119
|
+
removed_bytes += sz
|
|
120
|
+
except Exception:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
def rm_tree(p):
|
|
124
|
+
"""Delete a directory tree. Symlinks are unlinked, never followed."""
|
|
125
|
+
global removed_dirs, removed_bytes, removed_files
|
|
126
|
+
if os.path.islink(p):
|
|
127
|
+
rm_file(p)
|
|
128
|
+
return
|
|
129
|
+
for dp, dns, fns in os.walk(p, topdown=False, followlinks=False):
|
|
130
|
+
for f in fns:
|
|
131
|
+
rm_file(os.path.join(dp, f))
|
|
132
|
+
for d in dns:
|
|
133
|
+
fp = os.path.join(dp, d)
|
|
134
|
+
if os.path.islink(fp):
|
|
135
|
+
rm_file(fp)
|
|
136
|
+
else:
|
|
137
|
+
try:
|
|
138
|
+
if not DRY:
|
|
139
|
+
os.rmdir(fp)
|
|
140
|
+
except Exception:
|
|
141
|
+
pass
|
|
142
|
+
try:
|
|
143
|
+
if not DRY:
|
|
144
|
+
os.rmdir(p)
|
|
145
|
+
removed_dirs += 1
|
|
146
|
+
except Exception:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
# ---- 1. age sweep under the sanctioned roots --------------------------------
|
|
150
|
+
if DAYS > 0:
|
|
151
|
+
for rel in SWEEP_ROOTS:
|
|
152
|
+
top = os.path.join(root, rel)
|
|
153
|
+
if os.path.islink(top) or not os.path.isdir(top) or not inside(top):
|
|
154
|
+
continue
|
|
155
|
+
for dp, dns, fns in os.walk(top, topdown=False, followlinks=False):
|
|
156
|
+
if not inside(dp):
|
|
157
|
+
continue
|
|
158
|
+
for f in fns:
|
|
159
|
+
fp = os.path.join(dp, f)
|
|
160
|
+
try:
|
|
161
|
+
st = os.lstat(fp)
|
|
162
|
+
except Exception:
|
|
163
|
+
continue
|
|
164
|
+
if st.st_mtime < CUTOFF:
|
|
165
|
+
rm_file(fp)
|
|
166
|
+
# remove dirs emptied by this sweep (never the root itself)
|
|
167
|
+
if dp != top:
|
|
168
|
+
try:
|
|
169
|
+
if not os.listdir(dp):
|
|
170
|
+
if not DRY:
|
|
171
|
+
os.rmdir(dp)
|
|
172
|
+
removed_dirs += 1
|
|
173
|
+
except Exception:
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
# ---- 2. banned repo-root legacy dirs — any age -------------------------------
|
|
177
|
+
try:
|
|
178
|
+
for name in os.listdir(root):
|
|
179
|
+
p = os.path.join(root, name)
|
|
180
|
+
if not os.path.isdir(p) or os.path.islink(p):
|
|
181
|
+
continue
|
|
182
|
+
if any(fnmatch.fnmatch(name, g) for g in LEGACY_ROOT_GLOBS) and inside(p):
|
|
183
|
+
legacy.append(name)
|
|
184
|
+
rm_tree(p)
|
|
185
|
+
except Exception:
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
# ---- summary (stdout -> session context) -------------------------------------
|
|
189
|
+
if removed_files or removed_dirs or legacy:
|
|
190
|
+
mb = removed_bytes / (1024 * 1024)
|
|
191
|
+
parts = [f"TechieFlow sweep-artifacts{' (DRY RUN)' if DRY else ''}: removed {removed_files} file(s), "
|
|
192
|
+
f"{removed_dirs} dir(s), {mb:.1f} MB — run material older than {DAYS}d under "
|
|
193
|
+
f"tests/.artifacts/ and .verify/"]
|
|
194
|
+
if legacy:
|
|
195
|
+
parts.append("banned repo-root legacy dirs removed: " + ", ".join(sorted(legacy)))
|
|
196
|
+
print("; ".join(parts))
|
|
197
|
+
PY
|
|
198
|
+
exit 0
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
version: 4.44.3
|
|
2
|
+
installed_at: '2026-02-17T06:47:38.320Z'
|
|
3
|
+
install_type: full
|
|
4
|
+
agent: null
|
|
5
|
+
ides_setup:
|
|
6
|
+
- claude-code
|
|
7
|
+
- opencode
|
|
8
|
+
expansion_packs: []
|
|
9
|
+
files:
|
|
10
|
+
- path: .tfcore\working-in-the-brownfield.md
|
|
11
|
+
hash: 07cc8eb6fc664bb8
|
|
12
|
+
modified: false
|
|
13
|
+
- path: .tfcore\user-guide.md
|
|
14
|
+
hash: f83fb2a5bc8bfe4b
|
|
15
|
+
modified: false
|
|
16
|
+
- path: .tfcore\enhanced-ide-development-workflow.md
|
|
17
|
+
hash: 39beb3516c070e2b
|
|
18
|
+
modified: false
|
|
19
|
+
- path: .tfcore\core-config.yaml
|
|
20
|
+
hash: 38fbeb4b05ef067d
|
|
21
|
+
modified: false
|
|
22
|
+
- path: .tfcore\workflows\greenfield-ui.yaml
|
|
23
|
+
hash: 2b595f235c095790
|
|
24
|
+
modified: false
|
|
25
|
+
- path: .tfcore\workflows\greenfield-service.yaml
|
|
26
|
+
hash: 1ea8b8d218f8caef
|
|
27
|
+
modified: false
|
|
28
|
+
- path: .tfcore\workflows\greenfield-fullstack.yaml
|
|
29
|
+
hash: df34e60ccfac2624
|
|
30
|
+
modified: false
|
|
31
|
+
- path: .tfcore\workflows\brownfield-ui.yaml
|
|
32
|
+
hash: 8a8068093272d00f
|
|
33
|
+
modified: false
|
|
34
|
+
- path: .tfcore\workflows\brownfield-service.yaml
|
|
35
|
+
hash: a3cd68897852876e
|
|
36
|
+
modified: false
|
|
37
|
+
- path: .tfcore\workflows\brownfield-fullstack.yaml
|
|
38
|
+
hash: 9716ad5956a37037
|
|
39
|
+
modified: false
|
|
40
|
+
- path: .tfcore\utils\workflow-management.md
|
|
41
|
+
hash: c2196880f2281f84
|
|
42
|
+
modified: false
|
|
43
|
+
- path: .tfcore\utils\techieflow-doc-template.md
|
|
44
|
+
hash: 34df6ead8b91abfc
|
|
45
|
+
modified: false
|
|
46
|
+
- path: .tfcore\templates\story-tmpl.yaml
|
|
47
|
+
hash: 11e79b87ff700c8f
|
|
48
|
+
modified: false
|
|
49
|
+
- path: .tfcore\templates\qa-gate-tmpl.yaml
|
|
50
|
+
hash: 80bd060708208984
|
|
51
|
+
modified: false
|
|
52
|
+
- path: .tfcore\templates\project-brief-tmpl.yaml
|
|
53
|
+
hash: 31ba176a6ea087ab
|
|
54
|
+
modified: false
|
|
55
|
+
- path: .tfcore\templates\prd-tmpl.yaml
|
|
56
|
+
hash: 963116f9ab9f4b70
|
|
57
|
+
modified: false
|
|
58
|
+
- path: .tfcore\templates\market-research-tmpl.yaml
|
|
59
|
+
hash: ad46d980371caeed
|
|
60
|
+
modified: false
|
|
61
|
+
- path: .tfcore\templates\fullstack-architecture-tmpl.yaml
|
|
62
|
+
hash: cb00c49c284dc7cb
|
|
63
|
+
modified: false
|
|
64
|
+
- path: .tfcore\templates\front-end-spec-tmpl.yaml
|
|
65
|
+
hash: b1513524db76eeda
|
|
66
|
+
modified: false
|
|
67
|
+
- path: .tfcore\templates\front-end-architecture-tmpl.yaml
|
|
68
|
+
hash: 413a2d9541cb0645
|
|
69
|
+
modified: false
|
|
70
|
+
- path: .tfcore\templates\competitor-analysis-tmpl.yaml
|
|
71
|
+
hash: d0b263b0f9de8221
|
|
72
|
+
modified: false
|
|
73
|
+
- path: .tfcore\templates\brownfield-prd-tmpl.yaml
|
|
74
|
+
hash: 5b89b783cd2cacdb
|
|
75
|
+
modified: false
|
|
76
|
+
- path: .tfcore\templates\brownfield-architecture-tmpl.yaml
|
|
77
|
+
hash: 24af1ba7d17e68da
|
|
78
|
+
modified: false
|
|
79
|
+
- path: .tfcore\templates\brainstorming-output-tmpl.yaml
|
|
80
|
+
hash: ba806c97165c8178
|
|
81
|
+
modified: false
|
|
82
|
+
- path: .tfcore\templates\architecture-tmpl.yaml
|
|
83
|
+
hash: e66f63be1af30585
|
|
84
|
+
modified: false
|
|
85
|
+
- path: .tfcore\tasks\validate-next-story.md
|
|
86
|
+
hash: 75e84133d364d973
|
|
87
|
+
modified: false
|
|
88
|
+
- path: .tfcore\tasks\trace-requirements.md
|
|
89
|
+
hash: cb3e06cc0b957948
|
|
90
|
+
modified: false
|
|
91
|
+
- path: .tfcore\tasks\test-design.md
|
|
92
|
+
hash: bcd13a95d296ce22
|
|
93
|
+
modified: false
|
|
94
|
+
- path: .tfcore\tasks\shard-doc.md
|
|
95
|
+
hash: a83c900f64ea3d4f
|
|
96
|
+
modified: false
|
|
97
|
+
- path: .tfcore\tasks\risk-profile.md
|
|
98
|
+
hash: addf5d143cfe6f12
|
|
99
|
+
modified: false
|
|
100
|
+
- path: .tfcore\tasks\review-story.md
|
|
101
|
+
hash: dc366f2bd2211e11
|
|
102
|
+
modified: false
|
|
103
|
+
- path: .tfcore\tasks\qa-gate.md
|
|
104
|
+
hash: b0a768f56f425d9f
|
|
105
|
+
modified: false
|
|
106
|
+
- path: .tfcore\tasks\nfr-assess.md
|
|
107
|
+
hash: b9011d038b81a69b
|
|
108
|
+
modified: false
|
|
109
|
+
- path: .tfcore\tasks\kb-mode-interaction.md
|
|
110
|
+
hash: 4d6b921c24ba4999
|
|
111
|
+
modified: false
|
|
112
|
+
- path: .tfcore\tasks\index-docs.md
|
|
113
|
+
hash: 70b1d526b19d015e
|
|
114
|
+
modified: false
|
|
115
|
+
- path: .tfcore\tasks\generate-ai-frontend-prompt.md
|
|
116
|
+
hash: ca4cabd824ea1b60
|
|
117
|
+
modified: false
|
|
118
|
+
- path: .tfcore\tasks\facilitate-brainstorming-session.md
|
|
119
|
+
hash: 38594d876614e077
|
|
120
|
+
modified: false
|
|
121
|
+
- path: .tfcore\tasks\execute-checklist.md
|
|
122
|
+
hash: 8a704b6f2bc52e12
|
|
123
|
+
modified: false
|
|
124
|
+
- path: .tfcore\tasks\document-project.md
|
|
125
|
+
hash: 98f8790d20e83cf3
|
|
126
|
+
modified: false
|
|
127
|
+
- path: .tfcore\tasks\create-next-story.md
|
|
128
|
+
hash: 99e5cc3237a9cffd
|
|
129
|
+
modified: false
|
|
130
|
+
- path: .tfcore\tasks\create-doc.md
|
|
131
|
+
hash: 0a6aeba58cd7a3e4
|
|
132
|
+
modified: false
|
|
133
|
+
- path: .tfcore\tasks\create-deep-research-prompt.md
|
|
134
|
+
hash: 6d05224a13df6047
|
|
135
|
+
modified: false
|
|
136
|
+
- path: .tfcore\tasks\create-brownfield-story.md
|
|
137
|
+
hash: a70e435c8aafbf23
|
|
138
|
+
modified: false
|
|
139
|
+
- path: .tfcore\tasks\correct-course.md
|
|
140
|
+
hash: 0e6d3227b1aac200
|
|
141
|
+
modified: false
|
|
142
|
+
- path: .tfcore\tasks\brownfield-create-story.md
|
|
143
|
+
hash: 873dbf0760039028
|
|
144
|
+
modified: false
|
|
145
|
+
- path: .tfcore\tasks\brownfield-create-epic.md
|
|
146
|
+
hash: 7b95c09793f16e1a
|
|
147
|
+
modified: false
|
|
148
|
+
- path: .tfcore\tasks\apply-qa-fixes.md
|
|
149
|
+
hash: 70e6143a80f7296b
|
|
150
|
+
modified: false
|
|
151
|
+
- path: .tfcore\tasks\advanced-elicitation.md
|
|
152
|
+
hash: d39118bf32237a21
|
|
153
|
+
modified: false
|
|
154
|
+
- path: .tfcore\data\test-priorities-matrix.md
|
|
155
|
+
hash: 1dd5698a46ab054e
|
|
156
|
+
modified: false
|
|
157
|
+
- path: .tfcore\data\test-levels-framework.md
|
|
158
|
+
hash: f814f8efed0e96e1
|
|
159
|
+
modified: false
|
|
160
|
+
- path: .tfcore\data\technical-preferences.md
|
|
161
|
+
hash: a829f3172a10b396
|
|
162
|
+
modified: false
|
|
163
|
+
- path: .tfcore\data\elicitation-methods.md
|
|
164
|
+
hash: 8c3ca9b84c8784c9
|
|
165
|
+
modified: false
|
|
166
|
+
- path: .tfcore\data\brainstorming-techniques.md
|
|
167
|
+
hash: 62b0bf50648906b0
|
|
168
|
+
modified: false
|
|
169
|
+
- path: .tfcore\data\techieflow-kb.md
|
|
170
|
+
hash: c23a5fc5c6508c5c
|
|
171
|
+
modified: false
|
|
172
|
+
- path: .tfcore\checklists\story-draft-checklist.md
|
|
173
|
+
hash: 0bc8a90678dba318
|
|
174
|
+
modified: false
|
|
175
|
+
- path: .tfcore\checklists\story-dod-checklist.md
|
|
176
|
+
hash: df403478049b6958
|
|
177
|
+
modified: false
|
|
178
|
+
- path: .tfcore\checklists\po-master-checklist.md
|
|
179
|
+
hash: 8354985e56053c48
|
|
180
|
+
modified: false
|
|
181
|
+
- path: .tfcore\checklists\pm-checklist.md
|
|
182
|
+
hash: b53f0270312713d2
|
|
183
|
+
modified: false
|
|
184
|
+
- path: .tfcore\checklists\change-checklist.md
|
|
185
|
+
hash: fb2d071796c8f8b6
|
|
186
|
+
modified: false
|
|
187
|
+
- path: .tfcore\checklists\architect-checklist.md
|
|
188
|
+
hash: 15ef7d01b0e31c3f
|
|
189
|
+
modified: false
|
|
190
|
+
- path: .tfcore\agents\ux-expert.md
|
|
191
|
+
hash: 5de34d36ca9a747c
|
|
192
|
+
modified: false
|
|
193
|
+
- path: .tfcore\agents\sm.md
|
|
194
|
+
hash: bd6543412a550438
|
|
195
|
+
modified: false
|
|
196
|
+
- path: .tfcore\agents\qa.md
|
|
197
|
+
hash: 4ad40f692ee9c9fe
|
|
198
|
+
modified: false
|
|
199
|
+
- path: .tfcore\agents\po.md
|
|
200
|
+
hash: 02a321a754de36e3
|
|
201
|
+
modified: false
|
|
202
|
+
- path: .tfcore\agents\pm.md
|
|
203
|
+
hash: eef91ef67b74c2cf
|
|
204
|
+
modified: false
|
|
205
|
+
- path: .tfcore\agents\dev.md
|
|
206
|
+
hash: 944c6b2750c3f0a7
|
|
207
|
+
modified: false
|
|
208
|
+
- path: .tfcore\agents\flow-master.md
|
|
209
|
+
hash: 1adabdf959dddf55
|
|
210
|
+
modified: false
|
|
211
|
+
- path: .tfcore\agents\flow-master.md
|
|
212
|
+
hash: 7e7928ef673b5f79
|
|
213
|
+
modified: false
|
|
214
|
+
- path: .tfcore\agents\architect.md
|
|
215
|
+
hash: f5d211e80b8e160d
|
|
216
|
+
modified: false
|
|
217
|
+
- path: .tfcore\agents\analyst.md
|
|
218
|
+
hash: 220103a5b3af7eb1
|
|
219
|
+
modified: false
|
|
220
|
+
- path: .tfcore\agent-teams\team-no-ui.yaml
|
|
221
|
+
hash: 00cbffc4106cbe1e
|
|
222
|
+
modified: false
|
|
223
|
+
- path: .tfcore\agent-teams\team-ide-minimal.yaml
|
|
224
|
+
hash: 424972103dfde87d
|
|
225
|
+
modified: false
|
|
226
|
+
- path: .tfcore\agent-teams\team-fullstack.yaml
|
|
227
|
+
hash: 14c7a2d8bc7ceb6f
|
|
228
|
+
modified: false
|
|
229
|
+
- path: .tfcore\agent-teams\team-all.yaml
|
|
230
|
+
hash: db5b0ff0a9c9c2e8
|
|
231
|
+
modified: false
|