@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.
Files changed (151) hide show
  1. package/.claude/commands/TechieFlow/agents/analyst.md +97 -0
  2. package/.claude/commands/TechieFlow/agents/architect.md +84 -0
  3. package/.claude/commands/TechieFlow/agents/flow-master.md +217 -0
  4. package/.claude/commands/TechieFlow/agents/verifier.md +72 -0
  5. package/.claude/commands/TechieFlow/tasks/_metrics-emit-gate.md +138 -0
  6. package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +98 -0
  7. package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +78 -0
  8. package/.claude/commands/TechieFlow/tasks/_yolo-mode.md +77 -0
  9. package/.claude/commands/TechieFlow/tasks/advanced-elicitation.md +117 -0
  10. package/.claude/commands/TechieFlow/tasks/amend-docs.md +149 -0
  11. package/.claude/commands/TechieFlow/tasks/author-brd.md +79 -0
  12. package/.claude/commands/TechieFlow/tasks/build-phase.md +231 -0
  13. package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +278 -0
  14. package/.claude/commands/TechieFlow/tasks/create-doc.md +101 -0
  15. package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +507 -0
  16. package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +224 -0
  17. package/.claude/commands/TechieFlow/tasks/devguide.md +224 -0
  18. package/.claude/commands/TechieFlow/tasks/document-project.md +343 -0
  19. package/.claude/commands/TechieFlow/tasks/execute-checklist.md +86 -0
  20. package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +136 -0
  21. package/.claude/commands/TechieFlow/tasks/fix-issues.md +134 -0
  22. package/.claude/commands/TechieFlow/tasks/generate-html.md +115 -0
  23. package/.claude/commands/TechieFlow/tasks/handoff-phase.md +126 -0
  24. package/.claude/commands/TechieFlow/tasks/index-docs.md +173 -0
  25. package/.claude/commands/TechieFlow/tasks/kb-mode-interaction.md +75 -0
  26. package/.claude/commands/TechieFlow/tasks/log-miss.md +160 -0
  27. package/.claude/commands/TechieFlow/tasks/metrics-report.md +143 -0
  28. package/.claude/commands/TechieFlow/tasks/mockups.md +122 -0
  29. package/.claude/commands/TechieFlow/tasks/productguide.md +105 -0
  30. package/.claude/commands/TechieFlow/tasks/refresh-status.md +135 -0
  31. package/.claude/commands/TechieFlow/tasks/render-workflow-docs.md +117 -0
  32. package/.claude/commands/TechieFlow/tasks/shard-doc.md +185 -0
  33. package/.claude/commands/TechieFlow/tasks/split-brd.md +131 -0
  34. package/.claude/commands/TechieFlow/tasks/triage-issues.md +157 -0
  35. package/.claude/commands/TechieFlow/tasks/verify-phase.md +579 -0
  36. package/.claude/commands/generate-html.md +124 -0
  37. package/.codex/config.toml +46 -0
  38. package/.codex/hooks.json +63 -0
  39. package/.codex/rules/techieflow.rules +8 -0
  40. package/.opencode/command/generate-html.md +129 -0
  41. package/.opencode/plugin/techieflow.js +350 -0
  42. package/.tfcore/TOKEN-GUIDE.md +59 -0
  43. package/.tfcore/agents/analyst.md +97 -0
  44. package/.tfcore/agents/architect.md +84 -0
  45. package/.tfcore/agents/flow-master.md +217 -0
  46. package/.tfcore/agents/verifier.md +72 -0
  47. package/.tfcore/checklists/architect-checklist.md +438 -0
  48. package/.tfcore/core-config.yaml +47 -0
  49. package/.tfcore/data/brainstorming-techniques.md +36 -0
  50. package/.tfcore/data/elicitation-methods.md +154 -0
  51. package/.tfcore/data/techieflow-kb.md +807 -0
  52. package/.tfcore/data/technical-preferences.md +3 -0
  53. package/.tfcore/enhanced-ide-development-workflow.md +248 -0
  54. package/.tfcore/hooks/block-git.sh +301 -0
  55. package/.tfcore/hooks/codex-adapter.py +248 -0
  56. package/.tfcore/hooks/guard-artifacts.sh +101 -0
  57. package/.tfcore/hooks/guard-status-html.sh +102 -0
  58. package/.tfcore/hooks/guard-status.sh +124 -0
  59. package/.tfcore/hooks/guard-verify.sh +108 -0
  60. package/.tfcore/hooks/metrics-session.sh +129 -0
  61. package/.tfcore/hooks/session-pointer.sh +52 -0
  62. package/.tfcore/hooks/sweep-artifacts.sh +198 -0
  63. package/.tfcore/install-manifest.yaml +231 -0
  64. package/.tfcore/routing.yaml +105 -0
  65. package/.tfcore/tasks/_metrics-emit-gate.md +138 -0
  66. package/.tfcore/tasks/_smoke-test-policy.md +98 -0
  67. package/.tfcore/tasks/_status-update-gate.md +78 -0
  68. package/.tfcore/tasks/_yolo-mode.md +77 -0
  69. package/.tfcore/tasks/advanced-elicitation.md +117 -0
  70. package/.tfcore/tasks/amend-docs.md +149 -0
  71. package/.tfcore/tasks/author-brd.md +79 -0
  72. package/.tfcore/tasks/build-phase.md +231 -0
  73. package/.tfcore/tasks/create-deep-research-prompt.md +278 -0
  74. package/.tfcore/tasks/create-doc.md +101 -0
  75. package/.tfcore/tasks/day1-brownfield.md +507 -0
  76. package/.tfcore/tasks/day1-greenfield.md +224 -0
  77. package/.tfcore/tasks/devguide.md +224 -0
  78. package/.tfcore/tasks/document-project.md +343 -0
  79. package/.tfcore/tasks/execute-checklist.md +86 -0
  80. package/.tfcore/tasks/facilitate-brainstorming-session.md +136 -0
  81. package/.tfcore/tasks/fix-issues.md +134 -0
  82. package/.tfcore/tasks/generate-html.md +115 -0
  83. package/.tfcore/tasks/handoff-phase.md +126 -0
  84. package/.tfcore/tasks/index-docs.md +173 -0
  85. package/.tfcore/tasks/kb-mode-interaction.md +75 -0
  86. package/.tfcore/tasks/log-miss.md +160 -0
  87. package/.tfcore/tasks/metrics-report.md +143 -0
  88. package/.tfcore/tasks/mockups.md +122 -0
  89. package/.tfcore/tasks/productguide.md +105 -0
  90. package/.tfcore/tasks/refresh-status.md +135 -0
  91. package/.tfcore/tasks/render-workflow-docs.md +117 -0
  92. package/.tfcore/tasks/shard-doc.md +185 -0
  93. package/.tfcore/tasks/split-brd.md +131 -0
  94. package/.tfcore/tasks/triage-issues.md +157 -0
  95. package/.tfcore/tasks/verify-phase.md +579 -0
  96. package/.tfcore/telemetry/SCHEMA.md +678 -0
  97. package/.tfcore/telemetry/install-metrics.sh +390 -0
  98. package/.tfcore/telemetry/pre-commit +94 -0
  99. package/.tfcore/telemetry/tf-metrics.sh +1415 -0
  100. package/.tfcore/templates/architecture-tmpl.yaml +650 -0
  101. package/.tfcore/templates/brainstorming-output-tmpl.yaml +156 -0
  102. package/.tfcore/templates/brownfield-architecture-tmpl.yaml +476 -0
  103. package/.tfcore/templates/competitor-analysis-tmpl.yaml +306 -0
  104. package/.tfcore/templates/front-end-architecture-tmpl.yaml +218 -0
  105. package/.tfcore/templates/fullstack-architecture-tmpl.yaml +823 -0
  106. package/.tfcore/templates/market-research-tmpl.yaml +252 -0
  107. package/.tfcore/templates/project-brief-tmpl.yaml +221 -0
  108. package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +61 -0
  109. package/.tfcore/templates/v4custom/app-architecture-tmpl.md +127 -0
  110. package/.tfcore/templates/v4custom/app-brd-tmpl.md +204 -0
  111. package/.tfcore/templates/v4custom/app-checklist-tmpl.md +90 -0
  112. package/.tfcore/templates/v4custom/app-claude-md-tmpl.md +33 -0
  113. package/.tfcore/templates/v4custom/app-devguide-tmpl.md +167 -0
  114. package/.tfcore/templates/v4custom/app-editorconfig-tmpl.editorconfig +44 -0
  115. package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +31 -0
  116. package/.tfcore/templates/v4custom/app-productguide-tmpl.md +54 -0
  117. package/.tfcore/templates/v4custom/app-project-status-tmpl.md +98 -0
  118. package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +48 -0
  119. package/.tfcore/templates/v4custom/app-usageguide-tmpl.md +67 -0
  120. package/.tfcore/templates/v4custom/build-invocation-ladder.md +190 -0
  121. package/.tfcore/templates/v4custom/html-render-shell.md +502 -0
  122. package/.tfcore/templates/v4custom/metrics-report-template.md +273 -0
  123. package/.tfcore/templates/v4custom/opencode-operating-contract.md +56 -0
  124. package/.tfcore/user-guide.md +633 -0
  125. package/.tfcore/utils/techieflow-doc-template.md +325 -0
  126. package/.tfcore/utils/tf-assets.sh +310 -0
  127. package/.tfcore/utils/tf-codex-bind.py +196 -0
  128. package/.tfcore/utils/tf-codex-telemetry.py +60 -0
  129. package/.tfcore/utils/tf-emit.sh +1075 -0
  130. package/.tfcore/utils/tf-gitignore-audit.sh +281 -0
  131. package/.tfcore/utils/tf-goal.sh +421 -0
  132. package/.tfcore/utils/tf-harness.sh +160 -0
  133. package/.tfcore/utils/tf-mockup-parity.mjs +555 -0
  134. package/.tfcore/utils/tf-mockup-parity.sh +65 -0
  135. package/.tfcore/utils/tf-opencode-audit.py +126 -0
  136. package/.tfcore/utils/tf-perf.sh +241 -0
  137. package/.tfcore/utils/tf-render-html.py +715 -0
  138. package/.tfcore/utils/tf-render-html.sh +28 -0
  139. package/.tfcore/utils/tf-routing-bind.sh +216 -0
  140. package/.tfcore/utils/tf-routing.sh +247 -0
  141. package/.tfcore/utils/tf-yolo.sh +119 -0
  142. package/.tfcore/working-in-the-brownfield.md +606 -0
  143. package/LICENSE +201 -0
  144. package/README.md +1212 -0
  145. package/WORKFLOW.html +1986 -0
  146. package/docs/TechieFlow-Installation.md +299 -0
  147. package/opencode.jsonc +929 -0
  148. package/package.json +60 -0
  149. package/scripts/install.mjs +992 -0
  150. package/scripts/test-install.mjs +505 -0
  151. 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