hstack 0.1.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 (99) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +169 -0
  4. package/VERSION +1 -0
  5. package/dist/cli.js +55 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/doctor.js +155 -0
  8. package/dist/commands/doctor.js.map +1 -0
  9. package/dist/commands/init.js +87 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/update.js +118 -0
  12. package/dist/commands/update.js.map +1 -0
  13. package/dist/lib/diff.js +120 -0
  14. package/dist/lib/diff.js.map +1 -0
  15. package/dist/lib/git.js +26 -0
  16. package/dist/lib/git.js.map +1 -0
  17. package/dist/lib/paths.js +35 -0
  18. package/dist/lib/paths.js.map +1 -0
  19. package/dist/lib/wire.js +314 -0
  20. package/dist/lib/wire.js.map +1 -0
  21. package/dist/manifest.js +53 -0
  22. package/dist/manifest.js.map +1 -0
  23. package/package.json +54 -0
  24. package/template/.claude/agents/adversarial-reviewer.md +123 -0
  25. package/template/.claude/agents/data-specialist.md +126 -0
  26. package/template/.claude/agents/implementer.md +154 -0
  27. package/template/.claude/agents/planner.md +110 -0
  28. package/template/.claude/agents/product-manager.md +111 -0
  29. package/template/.claude/agents/researcher.md +123 -0
  30. package/template/.claude/agents/security-reviewer.md +123 -0
  31. package/template/.claude/agents/spec-author.md +118 -0
  32. package/template/.claude/agents/test-strategist.md +129 -0
  33. package/template/.claude/agents/ui-ux-briefer.md +105 -0
  34. package/template/.claude/agents/verifier.md +109 -0
  35. package/template/.claude/skills/hstack-adr-new/SKILL.md +118 -0
  36. package/template/.claude/skills/hstack-adversarial-review/SKILL.md +187 -0
  37. package/template/.claude/skills/hstack-branch/SKILL.md +103 -0
  38. package/template/.claude/skills/hstack-change-new/SKILL.md +109 -0
  39. package/template/.claude/skills/hstack-change-plan/SKILL.md +119 -0
  40. package/template/.claude/skills/hstack-commit/SKILL.md +122 -0
  41. package/template/.claude/skills/hstack-configure/SKILL.md +126 -0
  42. package/template/.claude/skills/hstack-data-review/SKILL.md +132 -0
  43. package/template/.claude/skills/hstack-finalize/SKILL.md +159 -0
  44. package/template/.claude/skills/hstack-help/SKILL.md +174 -0
  45. package/template/.claude/skills/hstack-implement/SKILL.md +185 -0
  46. package/template/.claude/skills/hstack-init/SKILL.md +152 -0
  47. package/template/.claude/skills/hstack-module-spec/SKILL.md +105 -0
  48. package/template/.claude/skills/hstack-research/SKILL.md +145 -0
  49. package/template/.claude/skills/hstack-security-review/SKILL.md +133 -0
  50. package/template/.claude/skills/hstack-ship/SKILL.md +128 -0
  51. package/template/.claude/skills/hstack-story-draft/SKILL.md +117 -0
  52. package/template/.claude/skills/hstack-tech-debt-new/SKILL.md +122 -0
  53. package/template/.claude/skills/hstack-tech-debt-resolve/SKILL.md +158 -0
  54. package/template/.claude/skills/hstack-tech-debt-stale/SKILL.md +113 -0
  55. package/template/.claude/skills/hstack-tech-debt-wontfix/SKILL.md +104 -0
  56. package/template/.claude/skills/hstack-telemetry/SKILL.md +96 -0
  57. package/template/.claude/skills/hstack-test-plan/SKILL.md +182 -0
  58. package/template/.claude/skills/hstack-ui-brief/SKILL.md +108 -0
  59. package/template/.claude/skills/hstack-verify/SKILL.md +139 -0
  60. package/template/CLAUDE.md +390 -0
  61. package/template/scripts/telemetry/__init__.py +6 -0
  62. package/template/scripts/telemetry/insights/__init__.py +0 -0
  63. package/template/scripts/telemetry/insights/contract_drift.py +137 -0
  64. package/template/scripts/telemetry/insights/overengineering.py +115 -0
  65. package/template/scripts/telemetry/insights/quality_outcomes.py +131 -0
  66. package/template/scripts/telemetry/insights/token_economics.py +129 -0
  67. package/template/scripts/telemetry/insights/workflow_shape.py +198 -0
  68. package/template/scripts/telemetry/parsers/__init__.py +0 -0
  69. package/template/scripts/telemetry/parsers/bodies.py +87 -0
  70. package/template/scripts/telemetry/parsers/commits.py +219 -0
  71. package/template/scripts/telemetry/parsers/frontmatter.py +322 -0
  72. package/template/scripts/telemetry/parsers/transcripts.py +181 -0
  73. package/template/scripts/telemetry/render.py +311 -0
  74. package/template/scripts/telemetry/report.py +112 -0
  75. package/template/templates/adr.md +38 -0
  76. package/template/templates/adversarial-review.md +54 -0
  77. package/template/templates/change-spec.md +80 -0
  78. package/template/templates/ci-cd.md +27 -0
  79. package/template/templates/data-architecture.md +35 -0
  80. package/template/templates/data-review.md +54 -0
  81. package/template/templates/figma-handoff.md +38 -0
  82. package/template/templates/glossary.md +20 -0
  83. package/template/templates/hardening-checklist.md +73 -0
  84. package/template/templates/incident-runbook.md +57 -0
  85. package/template/templates/infrastructure.md +190 -0
  86. package/template/templates/module-spec.md +49 -0
  87. package/template/templates/mvp-scope.md +34 -0
  88. package/template/templates/persona.md +38 -0
  89. package/template/templates/plan.md +49 -0
  90. package/template/templates/security-review.md +63 -0
  91. package/template/templates/story.md +37 -0
  92. package/template/templates/tech-debt.md +61 -0
  93. package/template/templates/tech-stack.md +41 -0
  94. package/template/templates/telemetry-sidecar.md +184 -0
  95. package/template/templates/test-plan.md +119 -0
  96. package/template/templates/threat-model.md +54 -0
  97. package/template/templates/ui-brief.md +49 -0
  98. package/template/templates/verification.md +63 -0
  99. package/template/templates/vision.md +34 -0
@@ -0,0 +1,118 @@
1
+ import pc from "picocolors";
2
+ import prompts from "prompts";
3
+ import fs from "fs-extra";
4
+ import { readFileSync } from "node:fs";
5
+ import { resolve } from "node:path";
6
+ import { findGitRoot, isWorkingTreeClean } from "../lib/git.js";
7
+ import { packageTemplateDir, packageVersionFile } from "../lib/paths.js";
8
+ import { planUpdate, pruneNoopActions, planSummary, renderPlan, executePlan, } from "../lib/wire.js";
9
+ function readPackageVersion() {
10
+ return readFileSync(packageVersionFile(), "utf8").trim();
11
+ }
12
+ export async function runUpdate(opts) {
13
+ if (process.platform === "win32") {
14
+ console.error(pc.red("hstack update does not yet support Windows (symlinks need admin / developer mode). Tracked for v2."));
15
+ return 2;
16
+ }
17
+ // 1. Precondition: cwd inside a git repo
18
+ const cwd = process.cwd();
19
+ const gitRoot = await findGitRoot(cwd);
20
+ if (!gitRoot) {
21
+ console.error(pc.red("Not inside a git repository. hstack update must run from a git repo."));
22
+ return 2;
23
+ }
24
+ // 2. Precondition: <consumer>/hstack/ exists (i.e. init has been run)
25
+ const consumerHstack = resolve(gitRoot, "hstack");
26
+ if (!(await fs.pathExists(consumerHstack))) {
27
+ console.error(pc.red(`${consumerHstack} does not exist. Run \`npx hstack init\` first.`));
28
+ return 2;
29
+ }
30
+ // 3. Precondition: working tree clean (unless --force)
31
+ if (!opts.force) {
32
+ const clean = await isWorkingTreeClean(gitRoot);
33
+ if (!clean) {
34
+ console.error(pc.red("Working tree is not clean. Commit or stash your changes, or re-run with --force."));
35
+ return 2;
36
+ }
37
+ }
38
+ // 4. Compute plan, then prune actions that would be no-ops at execute time.
39
+ const templateDir = packageTemplateDir();
40
+ const newVersion = readPackageVersion();
41
+ const rawActions = await planUpdate(gitRoot, templateDir, newVersion);
42
+ const actions = await pruneNoopActions(rawActions, newVersion);
43
+ // 5. Detect installed version (best-effort)
44
+ const installedVersionFile = resolve(consumerHstack, "VERSION");
45
+ const installedVersion = (await fs.pathExists(installedVersionFile))
46
+ ? (await fs.readFile(installedVersionFile, "utf8")).trim()
47
+ : "unknown";
48
+ // 6. Short-circuit if nothing to do
49
+ if (actions.length === 0) {
50
+ console.log(pc.green(`Already up to date (v${newVersion}).`));
51
+ return 0;
52
+ }
53
+ // 7. Render plan
54
+ const summary = planSummary(actions);
55
+ console.log(pc.bold(`hstack update: v${installedVersion} -> v${newVersion}`));
56
+ console.log("");
57
+ renderSummary(summary);
58
+ console.log("");
59
+ if (opts.verbose || summary["remove-file"] || summary["remove-symlink"]) {
60
+ console.log(pc.dim("Detail:"));
61
+ console.log(filterDetailedActions(actions, opts.verbose ?? false, gitRoot));
62
+ console.log("");
63
+ }
64
+ if (opts.dryRun) {
65
+ console.log(pc.dim("Dry run — no changes made."));
66
+ return 0;
67
+ }
68
+ // 8. Confirm
69
+ if (!opts.yes) {
70
+ const { ok } = await prompts({
71
+ type: "confirm",
72
+ name: "ok",
73
+ message: "Proceed?",
74
+ initial: true,
75
+ });
76
+ if (!ok) {
77
+ console.log(pc.dim("Cancelled."));
78
+ return 2;
79
+ }
80
+ }
81
+ // 9. Execute
82
+ try {
83
+ await executePlan(actions);
84
+ }
85
+ catch (err) {
86
+ console.error(pc.red(`update failed: ${err instanceof Error ? err.message : String(err)}`));
87
+ console.error(pc.dim("Partial state may exist. Inspect with `git status`; revert with `git restore`."));
88
+ return 1;
89
+ }
90
+ console.log("");
91
+ console.log(pc.green(`Updated to v${newVersion}.`));
92
+ console.log("Review with " + pc.cyan("git diff") + " and commit when satisfied.");
93
+ return 0;
94
+ }
95
+ function renderSummary(summary) {
96
+ const fmt = (label, n, color) => n > 0 ? ` ${color(`${n} ${label}`)}` : null;
97
+ const lines = [
98
+ fmt("framework file(s) added", summary["add-file"] ?? 0, pc.green),
99
+ fmt("framework file(s) modified", summary["overwrite-file"] ?? 0, pc.yellow),
100
+ fmt("framework file(s) removed", summary["remove-file"] ?? 0, pc.red),
101
+ fmt("symlink(s) added", summary["symlink"] ?? 0, pc.green),
102
+ fmt("symlink(s) removed", summary["remove-symlink"] ?? 0, pc.red),
103
+ ].filter(Boolean);
104
+ if (lines.length === 0) {
105
+ console.log(pc.dim(" (no file-level changes; idempotent checks only)"));
106
+ }
107
+ else {
108
+ for (const l of lines)
109
+ console.log(l);
110
+ }
111
+ }
112
+ function filterDetailedActions(actions, verbose, consumerRoot) {
113
+ const interesting = actions.filter((a) => verbose
114
+ ? true
115
+ : a.kind === "remove-file" || a.kind === "remove-symlink");
116
+ return renderPlan(interesting, consumerRoot);
117
+ }
118
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ import fs from "fs-extra";
2
+ import { resolve, relative, join } from "node:path";
3
+ import { readdir, readFile } from "node:fs/promises";
4
+ /**
5
+ * Walk a directory recursively, returning a Map of relative-path → absolute-path
6
+ * for every regular file. Symlinks are NOT followed (they are reported as files
7
+ * with their link content as "value" for symlink-to-symlink comparison).
8
+ *
9
+ * Used by update to diff template/ vs <consumer>/hstack/.
10
+ */
11
+ export async function walkFiles(root) {
12
+ const out = new Map();
13
+ if (!(await fs.pathExists(root)))
14
+ return out;
15
+ await walk(root, root, out);
16
+ return out;
17
+ }
18
+ async function walk(base, dir, out) {
19
+ const entries = await readdir(dir, { withFileTypes: true });
20
+ for (const entry of entries) {
21
+ const abs = join(dir, entry.name);
22
+ if (entry.isDirectory()) {
23
+ await walk(base, abs, out);
24
+ }
25
+ else if (entry.isFile() || entry.isSymbolicLink()) {
26
+ out.set(relative(base, abs), abs);
27
+ }
28
+ }
29
+ }
30
+ /** True iff the two files have identical byte content. */
31
+ export async function filesEqual(a, b) {
32
+ const [bufA, bufB] = await Promise.all([readFile(a), readFile(b)]);
33
+ if (bufA.length !== bufB.length)
34
+ return false;
35
+ return bufA.equals(bufB);
36
+ }
37
+ /**
38
+ * Diff the framework subtree of `template/` against `<consumer>/hstack/`.
39
+ * Only paths under FRAMEWORK_PATHS are considered.
40
+ *
41
+ * `frameworkPaths` is the list of framework subpaths (relative to template root
42
+ * and to consumer hstack root). Both top-level files (e.g., "CLAUDE.md") and
43
+ * directories (e.g., "templates/") are supported.
44
+ */
45
+ export async function diffFramework(templateDir, consumerHstackDir, frameworkPaths) {
46
+ const diffs = [];
47
+ for (const p of frameworkPaths) {
48
+ const isDir = p.endsWith("/");
49
+ const cleanPath = isDir ? p.slice(0, -1) : p;
50
+ const templatePath = resolve(templateDir, cleanPath);
51
+ const consumerPath = resolve(consumerHstackDir, cleanPath);
52
+ if (isDir) {
53
+ const templateFiles = await walkFiles(templatePath);
54
+ const consumerFiles = await walkFiles(consumerPath);
55
+ // ADD or OVERWRITE
56
+ for (const [rel, tabs] of templateFiles) {
57
+ const cabs = consumerFiles.get(rel);
58
+ const fullRel = join(cleanPath, rel);
59
+ if (!cabs) {
60
+ diffs.push({
61
+ kind: "add",
62
+ from: tabs,
63
+ to: resolve(consumerPath, rel),
64
+ relpath: fullRel,
65
+ });
66
+ }
67
+ else if (!(await filesEqual(tabs, cabs))) {
68
+ diffs.push({
69
+ kind: "overwrite",
70
+ from: tabs,
71
+ to: cabs,
72
+ relpath: fullRel,
73
+ });
74
+ }
75
+ }
76
+ // REMOVE
77
+ for (const [rel, cabs] of consumerFiles) {
78
+ if (!templateFiles.has(rel)) {
79
+ diffs.push({
80
+ kind: "remove",
81
+ to: cabs,
82
+ relpath: join(cleanPath, rel),
83
+ });
84
+ }
85
+ }
86
+ }
87
+ else {
88
+ // Single file
89
+ const tExists = await fs.pathExists(templatePath);
90
+ const cExists = await fs.pathExists(consumerPath);
91
+ if (tExists && !cExists) {
92
+ diffs.push({
93
+ kind: "add",
94
+ from: templatePath,
95
+ to: consumerPath,
96
+ relpath: cleanPath,
97
+ });
98
+ }
99
+ else if (tExists && cExists) {
100
+ if (!(await filesEqual(templatePath, consumerPath))) {
101
+ diffs.push({
102
+ kind: "overwrite",
103
+ from: templatePath,
104
+ to: consumerPath,
105
+ relpath: cleanPath,
106
+ });
107
+ }
108
+ }
109
+ else if (!tExists && cExists) {
110
+ diffs.push({
111
+ kind: "remove",
112
+ to: consumerPath,
113
+ relpath: cleanPath,
114
+ });
115
+ }
116
+ }
117
+ }
118
+ return diffs;
119
+ }
120
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { execa } from "execa";
2
+ import { resolve, dirname } from "node:path";
3
+ import fs from "fs-extra";
4
+ /** Walk up from `start` to find the repo root containing `.git`. Returns null if not in a git repo. */
5
+ export async function findGitRoot(start) {
6
+ let dir = resolve(start);
7
+ while (true) {
8
+ if (await fs.pathExists(resolve(dir, ".git")))
9
+ return dir;
10
+ const parent = dirname(dir);
11
+ if (parent === dir)
12
+ return null;
13
+ dir = parent;
14
+ }
15
+ }
16
+ /** True if working tree has no uncommitted changes (staged or unstaged). */
17
+ export async function isWorkingTreeClean(cwd) {
18
+ try {
19
+ const { stdout } = await execa("git", ["status", "--porcelain"], { cwd });
20
+ return stdout.trim() === "";
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,MAAM,UAAU,CAAC;AAE1B,uGAAuG;AACvG,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAa;IAC7C,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAW;IAClD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1E,OAAO,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import { dirname, resolve } from "node:path";
3
+ import fs from "fs-extra";
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ /**
6
+ * Locate the package's `template/` directory.
7
+ *
8
+ * In production install (`npm install hstack`): dist/cli.js lives at
9
+ * <package>/dist/cli.js, so template/ is at ../template relative to dist/.
10
+ *
11
+ * In local dev (`npx tsx src/cli.ts`): src/lib/paths.ts is at <repo>/src/lib/,
12
+ * so template/ is at ../../template relative to this file.
13
+ */
14
+ export function packageTemplateDir() {
15
+ // dist/lib/paths.js → ../../template (two levels up)
16
+ const fromDist = resolve(__dirname, "..", "..", "template");
17
+ if (fs.existsSync(fromDist))
18
+ return fromDist;
19
+ // dist/cli.js layout fallback: dist/lib/ might not exist; try one up
20
+ const fromDistFlat = resolve(__dirname, "..", "template");
21
+ if (fs.existsSync(fromDistFlat))
22
+ return fromDistFlat;
23
+ throw new Error(`Cannot locate hstack template/ directory. Looked in:\n ${fromDist}\n ${fromDistFlat}\nThis is a packaging bug; please file an issue.`);
24
+ }
25
+ /** Locate the package's VERSION file. */
26
+ export function packageVersionFile() {
27
+ const fromDist = resolve(__dirname, "..", "..", "VERSION");
28
+ if (fs.existsSync(fromDist))
29
+ return fromDist;
30
+ const fromDistFlat = resolve(__dirname, "..", "VERSION");
31
+ if (fs.existsSync(fromDistFlat))
32
+ return fromDistFlat;
33
+ throw new Error("Cannot locate VERSION file");
34
+ }
35
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/lib/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,MAAM,UAAU,CAAC;AAE1B,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB;IAChC,qDAAqD;IACrD,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IAC5D,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE7C,qEAAqE;IACrE,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IAC1D,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,YAAY,CAAC;IAErD,MAAM,IAAI,KAAK,CACb,2DAA2D,QAAQ,OAAO,YAAY,kDAAkD,CACzI,CAAC;AACJ,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,kBAAkB;IAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAC3D,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC7C,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IACzD,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,YAAY,CAAC;IACrD,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,314 @@
1
+ import fs from "fs-extra";
2
+ import { resolve, relative, dirname } from "node:path";
3
+ import { readdir, symlink, readlink, lstat, unlink } from "node:fs/promises";
4
+ import { diffFramework } from "./diff.js";
5
+ import { FRAMEWORK_PATHS } from "../manifest.js";
6
+ export const KERNEL_IMPORT_LINE = "> **Engineering workflow:** all changes in this repo are governed by hstack. See @hstack/CLAUDE.md.";
7
+ export const GITIGNORE_TELEMETRY_LINE = "**/.telemetry/";
8
+ /**
9
+ * Compute the full plan for `hstack init` against `consumerRoot`,
10
+ * using `templateDir` as the source of framework files.
11
+ *
12
+ * Pure function — does not touch disk except to read the skills directory.
13
+ */
14
+ export async function planInit(consumerRoot, templateDir, packageVersion) {
15
+ const actions = [];
16
+ // 1. Copy the entire template/ tree into <consumer>/hstack/
17
+ actions.push({
18
+ kind: "copy-template",
19
+ from: templateDir,
20
+ to: resolve(consumerRoot, "hstack"),
21
+ });
22
+ // 2. Symlink <consumer>/.claude/agents -> hstack/.claude/agents
23
+ actions.push({
24
+ kind: "symlink",
25
+ from: "../hstack/.claude/agents",
26
+ to: resolve(consumerRoot, ".claude", "agents"),
27
+ });
28
+ // 3. Per-skill symlinks under <consumer>/.claude/skills/hstack-*
29
+ const skillsDir = resolve(templateDir, ".claude", "skills");
30
+ const skillEntries = await readdir(skillsDir);
31
+ for (const name of skillEntries) {
32
+ if (!name.startsWith("hstack-"))
33
+ continue;
34
+ actions.push({
35
+ kind: "symlink",
36
+ from: `../../hstack/.claude/skills/${name}`,
37
+ to: resolve(consumerRoot, ".claude", "skills", name),
38
+ });
39
+ }
40
+ // 4. Append kernel-import line to CLAUDE.md (create if missing)
41
+ actions.push({
42
+ kind: "append-line",
43
+ file: resolve(consumerRoot, "CLAUDE.md"),
44
+ line: KERNEL_IMPORT_LINE,
45
+ createIfMissing: true,
46
+ matchOn: "@hstack/CLAUDE.md",
47
+ });
48
+ // 5. Append telemetry gitignore line (create if missing)
49
+ actions.push({
50
+ kind: "append-line",
51
+ file: resolve(consumerRoot, ".gitignore"),
52
+ line: GITIGNORE_TELEMETRY_LINE,
53
+ createIfMissing: true,
54
+ });
55
+ // 6. Stamp the installed-version marker so update can compare later.
56
+ actions.push({
57
+ kind: "write-version",
58
+ to: resolve(consumerRoot, "hstack", "VERSION"),
59
+ version: packageVersion,
60
+ });
61
+ return actions;
62
+ }
63
+ /**
64
+ * Compute the full plan for `hstack update` against `consumerRoot`.
65
+ * Diffs framework files in template/ vs the consumer's hstack/, plus
66
+ * the .claude/ symlink delta, plus idempotent CLAUDE.md / .gitignore
67
+ * line checks, plus the VERSION marker stamp.
68
+ */
69
+ export async function planUpdate(consumerRoot, templateDir, packageVersion) {
70
+ const actions = [];
71
+ const consumerHstack = resolve(consumerRoot, "hstack");
72
+ // 1. File-level diff for framework paths.
73
+ const fileDiffs = await diffFramework(templateDir, consumerHstack, FRAMEWORK_PATHS);
74
+ for (const d of fileDiffs) {
75
+ if (d.kind === "add") {
76
+ actions.push({ kind: "add-file", from: d.from, to: d.to, relpath: d.relpath });
77
+ }
78
+ else if (d.kind === "overwrite") {
79
+ actions.push({ kind: "overwrite-file", from: d.from, to: d.to, relpath: d.relpath });
80
+ }
81
+ else if (d.kind === "remove") {
82
+ actions.push({ kind: "remove-file", to: d.to, relpath: d.relpath });
83
+ }
84
+ }
85
+ // 2. Symlink delta for .claude/skills/hstack-*
86
+ const templateSkills = new Set(await listTemplateSkills(templateDir));
87
+ const consumerLinks = new Set(await listConsumerSkillLinks(consumerRoot));
88
+ for (const name of templateSkills) {
89
+ if (!consumerLinks.has(name)) {
90
+ actions.push({
91
+ kind: "symlink",
92
+ from: `../../hstack/.claude/skills/${name}`,
93
+ to: resolve(consumerRoot, ".claude", "skills", name),
94
+ });
95
+ }
96
+ }
97
+ for (const name of consumerLinks) {
98
+ if (!templateSkills.has(name)) {
99
+ actions.push({
100
+ kind: "remove-symlink",
101
+ to: resolve(consumerRoot, ".claude", "skills", name),
102
+ });
103
+ }
104
+ }
105
+ // 3. Agents dir symlink — idempotent (symlink action skips if already correct).
106
+ actions.push({
107
+ kind: "symlink",
108
+ from: "../hstack/.claude/agents",
109
+ to: resolve(consumerRoot, ".claude", "agents"),
110
+ });
111
+ // 4. CLAUDE.md import line — idempotent re-check.
112
+ actions.push({
113
+ kind: "append-line",
114
+ file: resolve(consumerRoot, "CLAUDE.md"),
115
+ line: KERNEL_IMPORT_LINE,
116
+ createIfMissing: true,
117
+ matchOn: "@hstack/CLAUDE.md",
118
+ });
119
+ // 5. .gitignore telemetry line — idempotent re-check.
120
+ actions.push({
121
+ kind: "append-line",
122
+ file: resolve(consumerRoot, ".gitignore"),
123
+ line: GITIGNORE_TELEMETRY_LINE,
124
+ createIfMissing: true,
125
+ });
126
+ // 6. VERSION marker.
127
+ actions.push({
128
+ kind: "write-version",
129
+ to: resolve(consumerRoot, "hstack", "VERSION"),
130
+ version: packageVersion,
131
+ });
132
+ return actions;
133
+ }
134
+ /**
135
+ * Filter out actions that would be no-ops at execute time (e.g., a symlink
136
+ * action whose target is already present and correct). Returns a new array
137
+ * containing only actions that will produce a visible change.
138
+ *
139
+ * Used by `hstack update` so plan output, summary counts, and the confirm
140
+ * prompt reflect only real work — not idempotent re-checks.
141
+ */
142
+ export async function pruneNoopActions(actions, packageVersion) {
143
+ const out = [];
144
+ for (const a of actions) {
145
+ if (a.kind === "symlink") {
146
+ const stat = await lstat(a.to).catch(() => null);
147
+ if (stat?.isSymbolicLink()) {
148
+ const target = await readlink(a.to);
149
+ if (target === a.from)
150
+ continue;
151
+ }
152
+ }
153
+ else if (a.kind === "append-line") {
154
+ const exists = await fs.pathExists(a.file);
155
+ if (exists) {
156
+ const content = await fs.readFile(a.file, "utf8");
157
+ const probe = a.matchOn ?? a.line;
158
+ if (content.includes(probe))
159
+ continue;
160
+ }
161
+ }
162
+ else if (a.kind === "write-version") {
163
+ const exists = await fs.pathExists(a.to);
164
+ if (exists) {
165
+ const cur = (await fs.readFile(a.to, "utf8")).trim();
166
+ if (cur === packageVersion)
167
+ continue;
168
+ }
169
+ }
170
+ out.push(a);
171
+ }
172
+ return out;
173
+ }
174
+ /**
175
+ * Enumerate the skill names (relative dir names) under `templateDir/.claude/skills`
176
+ * that match the hstack-* prefix.
177
+ */
178
+ export async function listTemplateSkills(templateDir) {
179
+ const skillsDir = resolve(templateDir, ".claude", "skills");
180
+ if (!(await fs.pathExists(skillsDir)))
181
+ return [];
182
+ const entries = await readdir(skillsDir);
183
+ return entries.filter((n) => n.startsWith("hstack-")).sort();
184
+ }
185
+ /**
186
+ * Enumerate the hstack-* skill symlinks currently present under
187
+ * `<consumer>/.claude/skills`.
188
+ */
189
+ export async function listConsumerSkillLinks(consumerRoot) {
190
+ const dir = resolve(consumerRoot, ".claude", "skills");
191
+ if (!(await fs.pathExists(dir)))
192
+ return [];
193
+ const entries = await readdir(dir);
194
+ return entries.filter((n) => n.startsWith("hstack-")).sort();
195
+ }
196
+ /** Render an action plan as human-readable bullets. */
197
+ export function renderPlan(actions, consumerRoot) {
198
+ return actions
199
+ .map((a) => {
200
+ switch (a.kind) {
201
+ case "copy-template":
202
+ return ` copy ${rel(a.to, consumerRoot)}/ (framework files)`;
203
+ case "add-file":
204
+ return ` add hstack/${a.relpath}`;
205
+ case "overwrite-file":
206
+ return ` edit hstack/${a.relpath}`;
207
+ case "remove-file":
208
+ return ` rm hstack/${a.relpath}`;
209
+ case "symlink":
210
+ return ` link ${rel(a.to, consumerRoot)} -> ${a.from}`;
211
+ case "remove-symlink":
212
+ return ` unlink ${rel(a.to, consumerRoot)}`;
213
+ case "append-line":
214
+ return ` check ${rel(a.file, consumerRoot)} (idempotent append)`;
215
+ case "write-version":
216
+ return ` write ${rel(a.to, consumerRoot)} (${a.version})`;
217
+ }
218
+ })
219
+ .join("\n");
220
+ }
221
+ /** Group a plan by action kind, returning {kind: count}. Useful for summaries. */
222
+ export function planSummary(actions) {
223
+ const out = {};
224
+ for (const a of actions)
225
+ out[a.kind] = (out[a.kind] ?? 0) + 1;
226
+ return out;
227
+ }
228
+ function rel(p, root) {
229
+ const r = relative(root, p);
230
+ return r === "" ? "." : r;
231
+ }
232
+ /** Validate that every action is safe to execute. Returns array of blocker messages; empty = ok. */
233
+ export async function validatePlan(actions) {
234
+ const blockers = [];
235
+ for (const a of actions) {
236
+ if (a.kind === "copy-template") {
237
+ if (await fs.pathExists(a.to)) {
238
+ blockers.push(`${a.to} already exists. Run \`hstack update\` instead, or remove the directory.`);
239
+ }
240
+ }
241
+ if (a.kind === "symlink") {
242
+ if (await fs.pathExists(a.to)) {
243
+ const stat = await lstat(a.to).catch(() => null);
244
+ if (stat?.isSymbolicLink()) {
245
+ const target = await readlink(a.to);
246
+ if (target !== a.from) {
247
+ blockers.push(`${a.to} is a symlink pointing at ${target}, expected ${a.from}. Remove it or use --force.`);
248
+ }
249
+ // matching symlink — skip silently at execute time
250
+ }
251
+ else {
252
+ blockers.push(`${a.to} exists and is not a symlink. Remove it manually before init.`);
253
+ }
254
+ }
255
+ }
256
+ // append-line is always safe (idempotent at execute time)
257
+ }
258
+ return blockers;
259
+ }
260
+ /** Execute the plan. Aborts on first error; does not roll back. */
261
+ export async function executePlan(actions) {
262
+ for (const a of actions) {
263
+ switch (a.kind) {
264
+ case "copy-template":
265
+ await fs.copy(a.from, a.to, { dereference: false });
266
+ break;
267
+ case "add-file":
268
+ case "overwrite-file":
269
+ await fs.ensureDir(dirname(a.to));
270
+ await fs.copy(a.from, a.to, { dereference: false, overwrite: true });
271
+ break;
272
+ case "remove-file":
273
+ await fs.remove(a.to);
274
+ break;
275
+ case "symlink": {
276
+ const stat = await lstat(a.to).catch(() => null);
277
+ if (stat?.isSymbolicLink()) {
278
+ const target = await readlink(a.to);
279
+ if (target === a.from)
280
+ break;
281
+ await unlink(a.to);
282
+ }
283
+ await fs.ensureDir(dirname(a.to));
284
+ await symlink(a.from, a.to);
285
+ break;
286
+ }
287
+ case "remove-symlink": {
288
+ const stat = await lstat(a.to).catch(() => null);
289
+ if (stat?.isSymbolicLink())
290
+ await unlink(a.to);
291
+ break;
292
+ }
293
+ case "append-line":
294
+ await appendLineIdempotent(a.file, a.line, a.createIfMissing, a.matchOn);
295
+ break;
296
+ case "write-version":
297
+ await fs.ensureDir(dirname(a.to));
298
+ await fs.writeFile(a.to, a.version + "\n");
299
+ break;
300
+ }
301
+ }
302
+ }
303
+ async function appendLineIdempotent(file, line, createIfMissing, matchOn) {
304
+ const exists = await fs.pathExists(file);
305
+ if (!exists && !createIfMissing)
306
+ return;
307
+ const existing = exists ? await fs.readFile(file, "utf8") : "";
308
+ const probe = matchOn ?? line;
309
+ if (existing.includes(probe))
310
+ return;
311
+ const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
312
+ await fs.writeFile(file, existing + sep + line + "\n");
313
+ }
314
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../src/lib/wire.ts"],"names":[],"mappings":"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"}