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.
- package/CHANGELOG.md +37 -0
- package/README.md +73 -163
- package/VERSION +1 -1
- package/dist/commands/doctor.js +18 -8
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +7 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.js +9 -2
- package/dist/commands/update.js.map +1 -1
- package/dist/lib/descriptions.js +10 -17
- package/dist/lib/descriptions.js.map +1 -1
- package/dist/lib/wire.js +166 -138
- package/dist/lib/wire.js.map +1 -1
- package/dist/manifest.js +41 -29
- package/dist/manifest.js.map +1 -1
- package/package.json +3 -4
- package/template/.claude/agents/adversarial-reviewer.md +42 -50
- package/template/.claude/agents/app-architect.md +44 -96
- package/template/.claude/agents/data-architect.md +46 -89
- package/template/.claude/agents/test-strategist.md +39 -68
- package/template/.claude/skills/hstack-adr-new/SKILL.md +23 -73
- package/template/.claude/skills/hstack-adversarial-review/SKILL.md +46 -152
- package/template/.claude/skills/hstack-adversarial-review/references/finding-categories.md +80 -103
- package/template/.claude/skills/hstack-app-architecture/SKILL.md +43 -68
- package/template/.claude/skills/hstack-commit/SKILL.md +27 -68
- package/template/.claude/skills/hstack-data-architecture/SKILL.md +34 -56
- package/template/.claude/skills/hstack-promote/SKILL.md +55 -0
- package/template/.claude/skills/hstack-story/SKILL.md +58 -0
- package/template/.claude/skills/hstack-test-audit/SKILL.md +64 -0
- package/template/.claude/skills/hstack-wrap/SKILL.md +48 -0
- package/template/.claude/skills/hstack-wrap/references/pr-description.md +45 -0
- package/template/KERNEL.md +75 -318
- package/template/templates/adr.md +2 -18
- package/template/templates/app-architecture.md +38 -50
- package/template/templates/data-architecture.md +38 -89
- package/template/templates/infrastructure.md +54 -148
- package/template/templates/roadmap.md +6 -12
- package/template/templates/story.md +7 -13
- package/template/templates/tech-debt.md +2 -37
- package/template/templates/tech-stack.md +2 -12
- package/template/.claude/agents/data-specialist.md +0 -81
- package/template/.claude/agents/implementer.md +0 -97
- package/template/.claude/agents/kernel-fit-analyst.md +0 -129
- package/template/.claude/agents/planner.md +0 -75
- package/template/.claude/agents/product-discovery.md +0 -129
- package/template/.claude/agents/product-manager.md +0 -76
- package/template/.claude/agents/researcher.md +0 -87
- package/template/.claude/agents/security-reviewer.md +0 -88
- package/template/.claude/agents/spec-author.md +0 -89
- package/template/.claude/agents/stack-architect.md +0 -108
- package/template/.claude/agents/ui-ux-briefer.md +0 -75
- package/template/.claude/agents/verifier.md +0 -81
- package/template/.claude/skills/hstack-branch/SKILL.md +0 -76
- package/template/.claude/skills/hstack-brownfield-init/SKILL.md +0 -131
- package/template/.claude/skills/hstack-change-new/SKILL.md +0 -84
- package/template/.claude/skills/hstack-change-plan/SKILL.md +0 -108
- package/template/.claude/skills/hstack-configure/SKILL.md +0 -99
- package/template/.claude/skills/hstack-coord/SKILL.md +0 -95
- package/template/.claude/skills/hstack-data-review/SKILL.md +0 -93
- package/template/.claude/skills/hstack-finalize/SKILL.md +0 -140
- package/template/.claude/skills/hstack-flag/SKILL.md +0 -99
- package/template/.claude/skills/hstack-greenfield-init/SKILL.md +0 -148
- package/template/.claude/skills/hstack-help/SKILL.md +0 -153
- package/template/.claude/skills/hstack-implement/SKILL.md +0 -155
- package/template/.claude/skills/hstack-kernel-fit-promote/SKILL.md +0 -125
- package/template/.claude/skills/hstack-kernel-fit-scan/SKILL.md +0 -142
- package/template/.claude/skills/hstack-kernel-fit-scan/references/slack-setup.md +0 -42
- package/template/.claude/skills/hstack-kernel-fit-triage/SKILL.md +0 -121
- package/template/.claude/skills/hstack-module-spec/SKILL.md +0 -78
- package/template/.claude/skills/hstack-product-discovery/SKILL.md +0 -75
- package/template/.claude/skills/hstack-research/SKILL.md +0 -115
- package/template/.claude/skills/hstack-scaffold/SKILL.md +0 -113
- package/template/.claude/skills/hstack-security-review/SKILL.md +0 -95
- package/template/.claude/skills/hstack-ship/SKILL.md +0 -119
- package/template/.claude/skills/hstack-stack-decide/SKILL.md +0 -81
- package/template/.claude/skills/hstack-story-draft/SKILL.md +0 -92
- package/template/.claude/skills/hstack-tech-debt-new/SKILL.md +0 -95
- package/template/.claude/skills/hstack-tech-debt-resolve/SKILL.md +0 -123
- package/template/.claude/skills/hstack-tech-debt-stale/SKILL.md +0 -86
- package/template/.claude/skills/hstack-tech-debt-wontfix/SKILL.md +0 -77
- package/template/.claude/skills/hstack-telemetry/SKILL.md +0 -73
- package/template/.claude/skills/hstack-test-plan/SKILL.md +0 -159
- package/template/.claude/skills/hstack-ui-brief/SKILL.md +0 -81
- package/template/.claude/skills/hstack-verify/SKILL.md +0 -128
- package/template/scripts/compute-merge-readiness.mjs +0 -780
- package/template/scripts/coord/coord_scan.py +0 -681
- package/template/scripts/run-gates.sh +0 -388
- package/template/scripts/telemetry/__init__.py +0 -6
- package/template/scripts/telemetry/insights/__init__.py +0 -0
- package/template/scripts/telemetry/insights/contract_drift.py +0 -137
- package/template/scripts/telemetry/insights/kernel_fit.py +0 -438
- package/template/scripts/telemetry/insights/overengineering.py +0 -115
- package/template/scripts/telemetry/insights/quality_outcomes.py +0 -131
- package/template/scripts/telemetry/insights/token_economics.py +0 -302
- package/template/scripts/telemetry/insights/workflow_shape.py +0 -198
- package/template/scripts/telemetry/parsers/__init__.py +0 -0
- package/template/scripts/telemetry/parsers/bodies.py +0 -87
- package/template/scripts/telemetry/parsers/commits.py +0 -219
- package/template/scripts/telemetry/parsers/frontmatter.py +0 -322
- package/template/scripts/telemetry/parsers/sidecars.py +0 -61
- package/template/scripts/telemetry/parsers/transcripts.py +0 -294
- package/template/scripts/telemetry/render.py +0 -460
- package/template/scripts/telemetry/report.py +0 -150
- package/template/scripts/telemetry/run_kernel_fit.py +0 -95
- package/template/scripts/telemetry/session_id.py +0 -139
- package/template/scripts/validate-spec.mjs +0 -3303
- package/template/templates/adversarial-review.md +0 -54
- package/template/templates/bootstrap.md +0 -105
- package/template/templates/change-spec.md +0 -82
- package/template/templates/ci-cd.md +0 -41
- package/template/templates/coord-message.md +0 -56
- package/template/templates/data-review.md +0 -54
- package/template/templates/discovery/brainstorm.md +0 -69
- package/template/templates/discovery/forcing-questions.md +0 -76
- package/template/templates/discovery/project-brief.md +0 -53
- package/template/templates/figma-handoff.md +0 -38
- package/template/templates/glossary.md +0 -20
- package/template/templates/hardening-checklist.md +0 -73
- package/template/templates/incident-runbook.md +0 -57
- package/template/templates/kernel-fit-finding.md +0 -63
- package/template/templates/kernel-fit-flag.md +0 -52
- package/template/templates/module-spec.md +0 -49
- package/template/templates/persona.md +0 -38
- package/template/templates/plan.md +0 -53
- package/template/templates/product-brief.md +0 -56
- package/template/templates/security-review.md +0 -63
- package/template/templates/telemetry-sidecar.md +0 -227
- package/template/templates/test-plan.md +0 -119
- package/template/templates/threat-model.md +0 -54
- package/template/templates/ui-brief.md +0 -49
- package/template/templates/verification.md +0 -63
- 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())
|