hstack 0.16.0 → 0.17.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 (132) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +73 -163
  3. package/VERSION +1 -1
  4. package/dist/commands/doctor.js +18 -8
  5. package/dist/commands/doctor.js.map +1 -1
  6. package/dist/commands/init.js +7 -3
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/update.js +9 -2
  9. package/dist/commands/update.js.map +1 -1
  10. package/dist/lib/descriptions.js +10 -17
  11. package/dist/lib/descriptions.js.map +1 -1
  12. package/dist/lib/wire.js +166 -138
  13. package/dist/lib/wire.js.map +1 -1
  14. package/dist/manifest.js +41 -29
  15. package/dist/manifest.js.map +1 -1
  16. package/package.json +3 -4
  17. package/template/.claude/agents/adversarial-reviewer.md +42 -50
  18. package/template/.claude/agents/app-architect.md +44 -96
  19. package/template/.claude/agents/data-architect.md +46 -89
  20. package/template/.claude/agents/test-strategist.md +39 -68
  21. package/template/.claude/skills/hstack-adr-new/SKILL.md +23 -73
  22. package/template/.claude/skills/hstack-adversarial-review/SKILL.md +46 -152
  23. package/template/.claude/skills/hstack-adversarial-review/references/finding-categories.md +80 -103
  24. package/template/.claude/skills/hstack-app-architecture/SKILL.md +43 -68
  25. package/template/.claude/skills/hstack-commit/SKILL.md +27 -68
  26. package/template/.claude/skills/hstack-data-architecture/SKILL.md +34 -56
  27. package/template/.claude/skills/hstack-promote/SKILL.md +55 -0
  28. package/template/.claude/skills/hstack-story/SKILL.md +58 -0
  29. package/template/.claude/skills/hstack-test-audit/SKILL.md +64 -0
  30. package/template/.claude/skills/hstack-wrap/SKILL.md +48 -0
  31. package/template/.claude/skills/hstack-wrap/references/pr-description.md +45 -0
  32. package/template/KERNEL.md +75 -318
  33. package/template/templates/adr.md +2 -18
  34. package/template/templates/app-architecture.md +38 -50
  35. package/template/templates/data-architecture.md +38 -89
  36. package/template/templates/infrastructure.md +54 -148
  37. package/template/templates/roadmap.md +6 -12
  38. package/template/templates/story.md +7 -13
  39. package/template/templates/tech-debt.md +2 -37
  40. package/template/templates/tech-stack.md +2 -12
  41. package/template/.claude/agents/data-specialist.md +0 -81
  42. package/template/.claude/agents/implementer.md +0 -97
  43. package/template/.claude/agents/kernel-fit-analyst.md +0 -129
  44. package/template/.claude/agents/planner.md +0 -75
  45. package/template/.claude/agents/product-discovery.md +0 -129
  46. package/template/.claude/agents/product-manager.md +0 -76
  47. package/template/.claude/agents/researcher.md +0 -87
  48. package/template/.claude/agents/security-reviewer.md +0 -88
  49. package/template/.claude/agents/spec-author.md +0 -89
  50. package/template/.claude/agents/stack-architect.md +0 -108
  51. package/template/.claude/agents/ui-ux-briefer.md +0 -75
  52. package/template/.claude/agents/verifier.md +0 -81
  53. package/template/.claude/skills/hstack-branch/SKILL.md +0 -76
  54. package/template/.claude/skills/hstack-brownfield-init/SKILL.md +0 -131
  55. package/template/.claude/skills/hstack-change-new/SKILL.md +0 -84
  56. package/template/.claude/skills/hstack-change-plan/SKILL.md +0 -108
  57. package/template/.claude/skills/hstack-configure/SKILL.md +0 -99
  58. package/template/.claude/skills/hstack-coord/SKILL.md +0 -95
  59. package/template/.claude/skills/hstack-data-review/SKILL.md +0 -93
  60. package/template/.claude/skills/hstack-finalize/SKILL.md +0 -140
  61. package/template/.claude/skills/hstack-flag/SKILL.md +0 -99
  62. package/template/.claude/skills/hstack-greenfield-init/SKILL.md +0 -148
  63. package/template/.claude/skills/hstack-help/SKILL.md +0 -153
  64. package/template/.claude/skills/hstack-implement/SKILL.md +0 -155
  65. package/template/.claude/skills/hstack-kernel-fit-promote/SKILL.md +0 -125
  66. package/template/.claude/skills/hstack-kernel-fit-scan/SKILL.md +0 -142
  67. package/template/.claude/skills/hstack-kernel-fit-scan/references/slack-setup.md +0 -42
  68. package/template/.claude/skills/hstack-kernel-fit-triage/SKILL.md +0 -121
  69. package/template/.claude/skills/hstack-module-spec/SKILL.md +0 -78
  70. package/template/.claude/skills/hstack-product-discovery/SKILL.md +0 -75
  71. package/template/.claude/skills/hstack-research/SKILL.md +0 -115
  72. package/template/.claude/skills/hstack-scaffold/SKILL.md +0 -113
  73. package/template/.claude/skills/hstack-security-review/SKILL.md +0 -95
  74. package/template/.claude/skills/hstack-ship/SKILL.md +0 -119
  75. package/template/.claude/skills/hstack-stack-decide/SKILL.md +0 -81
  76. package/template/.claude/skills/hstack-story-draft/SKILL.md +0 -92
  77. package/template/.claude/skills/hstack-tech-debt-new/SKILL.md +0 -95
  78. package/template/.claude/skills/hstack-tech-debt-resolve/SKILL.md +0 -123
  79. package/template/.claude/skills/hstack-tech-debt-stale/SKILL.md +0 -86
  80. package/template/.claude/skills/hstack-tech-debt-wontfix/SKILL.md +0 -77
  81. package/template/.claude/skills/hstack-telemetry/SKILL.md +0 -73
  82. package/template/.claude/skills/hstack-test-plan/SKILL.md +0 -159
  83. package/template/.claude/skills/hstack-ui-brief/SKILL.md +0 -81
  84. package/template/.claude/skills/hstack-verify/SKILL.md +0 -128
  85. package/template/scripts/compute-merge-readiness.mjs +0 -780
  86. package/template/scripts/coord/coord_scan.py +0 -681
  87. package/template/scripts/run-gates.sh +0 -388
  88. package/template/scripts/telemetry/__init__.py +0 -6
  89. package/template/scripts/telemetry/insights/__init__.py +0 -0
  90. package/template/scripts/telemetry/insights/contract_drift.py +0 -137
  91. package/template/scripts/telemetry/insights/kernel_fit.py +0 -438
  92. package/template/scripts/telemetry/insights/overengineering.py +0 -115
  93. package/template/scripts/telemetry/insights/quality_outcomes.py +0 -131
  94. package/template/scripts/telemetry/insights/token_economics.py +0 -302
  95. package/template/scripts/telemetry/insights/workflow_shape.py +0 -198
  96. package/template/scripts/telemetry/parsers/__init__.py +0 -0
  97. package/template/scripts/telemetry/parsers/bodies.py +0 -87
  98. package/template/scripts/telemetry/parsers/commits.py +0 -219
  99. package/template/scripts/telemetry/parsers/frontmatter.py +0 -322
  100. package/template/scripts/telemetry/parsers/sidecars.py +0 -61
  101. package/template/scripts/telemetry/parsers/transcripts.py +0 -294
  102. package/template/scripts/telemetry/render.py +0 -460
  103. package/template/scripts/telemetry/report.py +0 -150
  104. package/template/scripts/telemetry/run_kernel_fit.py +0 -95
  105. package/template/scripts/telemetry/session_id.py +0 -139
  106. package/template/scripts/validate-spec.mjs +0 -3303
  107. package/template/templates/adversarial-review.md +0 -54
  108. package/template/templates/bootstrap.md +0 -105
  109. package/template/templates/change-spec.md +0 -82
  110. package/template/templates/ci-cd.md +0 -41
  111. package/template/templates/coord-message.md +0 -56
  112. package/template/templates/data-review.md +0 -54
  113. package/template/templates/discovery/brainstorm.md +0 -69
  114. package/template/templates/discovery/forcing-questions.md +0 -76
  115. package/template/templates/discovery/project-brief.md +0 -53
  116. package/template/templates/figma-handoff.md +0 -38
  117. package/template/templates/glossary.md +0 -20
  118. package/template/templates/hardening-checklist.md +0 -73
  119. package/template/templates/incident-runbook.md +0 -57
  120. package/template/templates/kernel-fit-finding.md +0 -63
  121. package/template/templates/kernel-fit-flag.md +0 -52
  122. package/template/templates/module-spec.md +0 -49
  123. package/template/templates/persona.md +0 -38
  124. package/template/templates/plan.md +0 -53
  125. package/template/templates/product-brief.md +0 -56
  126. package/template/templates/security-review.md +0 -63
  127. package/template/templates/telemetry-sidecar.md +0 -227
  128. package/template/templates/test-plan.md +0 -119
  129. package/template/templates/threat-model.md +0 -54
  130. package/template/templates/ui-brief.md +0 -49
  131. package/template/templates/verification.md +0 -63
  132. package/template/templates/vision.md +0 -34
@@ -1,95 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Run the kernel-fit detection layer in isolation and dump JSON to stdout.
3
-
4
- Thin wrapper around `telemetry.insights.kernel_fit.compute()`. Used by
5
- `/hstack:kernel-fit-scan` to obtain the structured evidence blob the
6
- `kernel-fit-analyst` subagent consumes, without producing the full
7
- telemetry report.
8
-
9
- Usage:
10
- python scripts/telemetry/run_kernel_fit.py [--repo <path>] [--window <days>]
11
-
12
- Defaults match `report.py`: `--repo` is cwd; `--window` is 30 days.
13
-
14
- The output is a single JSON object whose top-level keys mirror the
15
- `compute()` return value (`existing_open_findings_by_pattern`, and one
16
- key per pattern). The scan Skill reads stdout, parses, and passes the
17
- blob to the subagent.
18
-
19
- Read-only. No writes, no git side-effects.
20
- """
21
-
22
- from __future__ import annotations
23
-
24
- import argparse
25
- import json
26
- import sys
27
- from datetime import datetime, timedelta, timezone
28
- from pathlib import Path
29
-
30
- # Match report.py's import-path bootstrap so this script can be invoked
31
- # from any directory.
32
- _THIS = Path(__file__).resolve()
33
- _SCRIPTS = _THIS.parent.parent
34
- if str(_SCRIPTS) not in sys.path:
35
- sys.path.insert(0, str(_SCRIPTS))
36
-
37
- from telemetry.parsers import frontmatter, commits, transcripts # noqa: E402
38
- from telemetry.insights import kernel_fit # noqa: E402
39
-
40
-
41
- def main(argv: list[str] | None = None) -> int:
42
- parser = argparse.ArgumentParser(description="Run kernel-fit detection and dump JSON.")
43
- parser.add_argument("--repo", type=Path, default=Path.cwd(),
44
- help="Consuming-repo root (default: cwd).")
45
- parser.add_argument("--window", type=int, default=30,
46
- help="Limit git/transcript history to last N days (default: 30; 0 = all).")
47
- args = parser.parse_args(argv)
48
-
49
- repo = args.repo.resolve()
50
- hstack_root = repo / "hstack"
51
- if not hstack_root.is_dir():
52
- # Permit running against the template repo itself. CLAUDE.md is the
53
- # pre-ADR-0010 kernel name — still accepted so an un-migrated tree
54
- # keeps scanning.
55
- if (repo / "specs").is_dir() and (
56
- (repo / "KERNEL.md").is_file() or (repo / "CLAUDE.md").is_file()
57
- ):
58
- hstack_root = repo
59
- else:
60
- print(f"error: no hstack/ directory at {repo}", file=sys.stderr)
61
- return 1
62
-
63
- window_days: int | None = args.window if args.window > 0 else None
64
- since_dt: datetime | None = (
65
- datetime.now(timezone.utc) - timedelta(days=window_days)
66
- ) if window_days else None
67
-
68
- changes = frontmatter.load_change_artifacts(hstack_root)
69
- tech_debt = frontmatter.load_tech_debt(hstack_root)
70
- adrs = frontmatter.load_adrs(hstack_root)
71
- module_specs = frontmatter.load_module_specs(hstack_root)
72
- git_commits = commits.parse_commits(repo, since_days=window_days)
73
- session_rows = transcripts.collect_session_rows([repo], since=since_dt)
74
-
75
- findings_dir = hstack_root / "kernel-fit" / "findings"
76
-
77
- result = kernel_fit.compute(
78
- commits=git_commits,
79
- changes=changes,
80
- tech_debt=tech_debt,
81
- adrs=adrs,
82
- module_specs=module_specs,
83
- session_rows=session_rows,
84
- findings_dir=findings_dir,
85
- )
86
-
87
- # Path objects are not JSON-serializable; strip them where they appear
88
- # (existing-findings paths get re-derived by the analyst from the id).
89
- json.dump(result, sys.stdout, default=str, indent=2)
90
- sys.stdout.write("\n")
91
- return 0
92
-
93
-
94
- if __name__ == "__main__":
95
- sys.exit(main())
@@ -1,139 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Resolve the active Claude Code session id — the one heuristic, in one place.
3
-
4
- Per ADR-0009 § Neutral: `/hstack:flag` resolved the session id inline in its own
5
- prose; the five sidecar-emitting Skills would have duplicated that prose a sixth
6
- time. They call this module instead.
7
-
8
- The heuristic (unchanged from ADR-0005): Claude Code writes one `*.jsonl`
9
- transcript per session under `~/.claude/projects/<encoded-cwd>/`, where
10
- `<encoded-cwd>` is the absolute working directory with `/` replaced by `-`. The
11
- most recently modified file in that directory is the active session; the session
12
- id is its basename.
13
-
14
- Both the heuristic and the transcript layout are harness implementation details,
15
- not a contract (ADR-0009 § Negative). Every failure mode resolves to
16
- `session_id: null` — never a guess, never a halt. A null session id means the
17
- phase reports as *unmeasured*, which is the whole point of the null-not-zero
18
- rule.
19
-
20
- CLI:
21
- python3 scripts/telemetry/session_id.py # JSON on stdout, always exit 0
22
- python3 scripts/telemetry/session_id.py --cwd DIR # resolve for another dir
23
-
24
- Output:
25
- {"session_id": "062b8fe8-…" | null,
26
- "transcript_path": "/Users/…/062b8fe8-….jsonl" | null,
27
- "message_count": 1423,
28
- "source": "transcript" | "unresolved",
29
- "fallback_id": "a3f9c1d2",
30
- "now": "2026-08-15T09:12:44Z"}
31
-
32
- `now` is the UTC stamp at resolution time, so a Skill opening a phase gets its
33
- `session_id` and its `phase_opened_at` from one call. `fallback_id` is short
34
- random hex for callers that need a non-null id anyway (`/hstack:flag` writes
35
- `fallback-<id>`); sidecar emitters ignore it and write `null`.
36
- """
37
-
38
- from __future__ import annotations
39
-
40
- import argparse
41
- import json
42
- import os
43
- import sys
44
- from datetime import datetime, timezone
45
- from pathlib import Path
46
-
47
-
48
- PROJECTS_ROOT = Path.home() / ".claude" / "projects"
49
-
50
-
51
- def encoded_cwd(cwd: Path | str | None = None) -> str:
52
- """`/Users/jane/code/moso` → `-Users-jane-code-moso`."""
53
- p = Path(cwd) if cwd is not None else Path.cwd()
54
- return str(p.resolve()).replace("/", "-")
55
-
56
-
57
- def project_dir(cwd: Path | str | None = None, projects_root: Path | None = None) -> Path:
58
- root = projects_root or PROJECTS_ROOT
59
- return root / encoded_cwd(cwd)
60
-
61
-
62
- def utc_now_iso() -> str:
63
- """ISO-8601 UTC, second precision, `Z` suffix — the sidecar timestamp format."""
64
- return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
65
-
66
-
67
- def resolve_session(cwd: Path | str | None = None,
68
- projects_root: Path | None = None) -> dict:
69
- """Resolve the active session for `cwd`. Never raises; never halts."""
70
- out = {
71
- "session_id": None,
72
- "transcript_path": None,
73
- "message_count": 0,
74
- "source": "unresolved",
75
- "fallback_id": os.urandom(4).hex(),
76
- "now": utc_now_iso(),
77
- }
78
- d = project_dir(cwd, projects_root)
79
- try:
80
- candidates = [f for f in d.glob("*.jsonl") if f.is_file()]
81
- except OSError:
82
- return out
83
- if not candidates:
84
- return out
85
- try:
86
- newest = max(candidates, key=lambda f: f.stat().st_mtime)
87
- except OSError:
88
- return out
89
- out["session_id"] = newest.stem
90
- out["transcript_path"] = str(newest)
91
- out["source"] = "transcript"
92
- out["message_count"] = _count_lines(newest)
93
- return out
94
-
95
-
96
- def _count_lines(path: Path) -> int:
97
- try:
98
- with open(path, "rb") as fh:
99
- return sum(1 for _ in fh)
100
- except OSError:
101
- return 0
102
-
103
-
104
- def transcript_for_session(session_id: str | None,
105
- projects_root: Path | None = None) -> Path | None:
106
- """Locate a transcript by session id, across every project directory.
107
-
108
- The sidecar carries the session id, not the path: a Conductor worktree can be
109
- renamed or removed between the write and the read, and the encoded-cwd
110
- directory with it. Returns None when no transcript is found — retention
111
- sweep, another machine, or a harness layout change. None means *unmeasured*.
112
- """
113
- if not session_id or not isinstance(session_id, str):
114
- return None
115
- if session_id.startswith("fallback-"):
116
- return None
117
- root = projects_root or PROJECTS_ROOT
118
- if not root.is_dir():
119
- return None
120
- for d in sorted(root.iterdir()):
121
- if not d.is_dir():
122
- continue
123
- candidate = d / f"{session_id}.jsonl"
124
- if candidate.is_file():
125
- return candidate
126
- return None
127
-
128
-
129
- def main(argv: list[str] | None = None) -> int:
130
- parser = argparse.ArgumentParser(description="Resolve the active Claude Code session id.")
131
- parser.add_argument("--cwd", type=Path, default=None,
132
- help="Directory to resolve for (default: current working directory).")
133
- args = parser.parse_args(argv)
134
- print(json.dumps(resolve_session(args.cwd), indent=1))
135
- return 0
136
-
137
-
138
- if __name__ == "__main__":
139
- sys.exit(main())