@pruddiman/hem 0.0.1-beta-5671db0

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agents/arbiter-agent.d.ts +72 -0
  3. package/dist/agents/arbiter-agent.js +149 -0
  4. package/dist/agents/architecture-agent.d.ts +148 -0
  5. package/dist/agents/architecture-agent.js +459 -0
  6. package/dist/agents/base-agent.d.ts +44 -0
  7. package/dist/agents/base-agent.js +57 -0
  8. package/dist/agents/crossref-agent.d.ts +140 -0
  9. package/dist/agents/crossref-agent.js +560 -0
  10. package/dist/agents/crossref-arbiter-agent.d.ts +72 -0
  11. package/dist/agents/crossref-arbiter-agent.js +147 -0
  12. package/dist/agents/documentation-agent.d.ts +55 -0
  13. package/dist/agents/documentation-agent.js +159 -0
  14. package/dist/agents/exploration-agent.d.ts +58 -0
  15. package/dist/agents/exploration-agent.js +102 -0
  16. package/dist/agents/grouping-agent.d.ts +167 -0
  17. package/dist/agents/grouping-agent.js +557 -0
  18. package/dist/agents/index-agent.d.ts +86 -0
  19. package/dist/agents/index-agent.js +360 -0
  20. package/dist/agents/organization-agent.d.ts +144 -0
  21. package/dist/agents/organization-agent.js +607 -0
  22. package/dist/auth.d.ts +372 -0
  23. package/dist/auth.js +1072 -0
  24. package/dist/broadcast-mcp.d.ts +21 -0
  25. package/dist/broadcast-mcp.js +59 -0
  26. package/dist/changelog.d.ts +85 -0
  27. package/dist/changelog.js +223 -0
  28. package/dist/decision-queue.d.ts +173 -0
  29. package/dist/decision-queue.js +265 -0
  30. package/dist/diff-scope.d.ts +24 -0
  31. package/dist/diff-scope.js +28 -0
  32. package/dist/discovery.d.ts +54 -0
  33. package/dist/discovery.js +405 -0
  34. package/dist/grouping.d.ts +37 -0
  35. package/dist/grouping.js +343 -0
  36. package/dist/helpers/format.d.ts +5 -0
  37. package/dist/helpers/format.js +13 -0
  38. package/dist/helpers/index.d.ts +11 -0
  39. package/dist/helpers/index.js +11 -0
  40. package/dist/helpers/parsing.d.ts +52 -0
  41. package/dist/helpers/parsing.js +128 -0
  42. package/dist/helpers/paths.d.ts +41 -0
  43. package/dist/helpers/paths.js +67 -0
  44. package/dist/helpers/strings.d.ts +45 -0
  45. package/dist/helpers/strings.js +97 -0
  46. package/dist/index.d.ts +135 -0
  47. package/dist/index.js +1087 -0
  48. package/dist/merge-utils.d.ts +22 -0
  49. package/dist/merge-utils.js +34 -0
  50. package/dist/orchestrator.d.ts +194 -0
  51. package/dist/orchestrator.js +1169 -0
  52. package/dist/output.d.ts +106 -0
  53. package/dist/output.js +243 -0
  54. package/dist/progress.d.ts +228 -0
  55. package/dist/progress.js +644 -0
  56. package/dist/providers/copilot.d.ts +247 -0
  57. package/dist/providers/copilot.js +598 -0
  58. package/dist/providers/index.d.ts +15 -0
  59. package/dist/providers/index.js +12 -0
  60. package/dist/providers/opencode.d.ts +156 -0
  61. package/dist/providers/opencode.js +416 -0
  62. package/dist/providers/types.d.ts +156 -0
  63. package/dist/providers/types.js +16 -0
  64. package/dist/resources.d.ts +76 -0
  65. package/dist/resources.js +151 -0
  66. package/dist/search-index.d.ts +71 -0
  67. package/dist/search-index.js +187 -0
  68. package/dist/search-mcp.d.ts +25 -0
  69. package/dist/search-mcp.js +100 -0
  70. package/dist/server-utils.d.ts +56 -0
  71. package/dist/server-utils.js +135 -0
  72. package/dist/session.d.ts +227 -0
  73. package/dist/session.js +370 -0
  74. package/dist/types.d.ts +272 -0
  75. package/dist/types.js +5 -0
  76. package/dist/worktree.d.ts +82 -0
  77. package/dist/worktree.js +187 -0
  78. package/package.json +45 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Git worktree utilities for the --worktree flag.
3
+ *
4
+ * Creates an isolated git worktree under `.hem/worktree/` on a fresh branch,
5
+ * runs the pipeline there, then commits and pushes the branch for review.
6
+ *
7
+ * All functions use execSync (same pattern as changelog.ts).
8
+ */
9
+ /**
10
+ * Returns the absolute path to the root of the git repository containing `cwd`.
11
+ *
12
+ * @throws If `cwd` is not inside a git repository.
13
+ */
14
+ export declare function getRepoRoot(cwd: string): string;
15
+ /**
16
+ * Returns the name of the currently checked-out branch.
17
+ *
18
+ * @param repoRoot - Absolute path to the git repository root.
19
+ * @throws If the repo is in a detached HEAD state or git is unavailable.
20
+ */
21
+ export declare function getCurrentBranch(repoRoot: string): string;
22
+ /**
23
+ * Generates a unique worktree branch name based on the current branch and a
24
+ * timestamp.
25
+ *
26
+ * Format: `hem/<sanitized-base>-<YYYYMMDD-HHmmss>`
27
+ *
28
+ * The base branch name is sanitized to be a valid git ref component:
29
+ * slashes and non-alphanumeric characters (except `-`) are replaced with `-`,
30
+ * and leading/trailing dashes are removed.
31
+ *
32
+ * @param baseBranch - The currently checked-out branch name.
33
+ */
34
+ export declare function generateWorktreeBranch(baseBranch: string): string;
35
+ /**
36
+ * Ensures `pattern` appears in `<repoRoot>/.gitignore`.
37
+ *
38
+ * If the file does not exist it is created. If the pattern is already present
39
+ * (exact line match) this is a no-op.
40
+ *
41
+ * @param repoRoot - Absolute path to the git repository root.
42
+ * @param pattern - The gitignore pattern to add (e.g. `.hem/worktree/`).
43
+ */
44
+ export declare function ensureGitignored(repoRoot: string, pattern: string): Promise<void>;
45
+ /**
46
+ * Creates a git worktree at `.hem/worktree/` inside `repoRoot` checked out on
47
+ * a new branch `branchName`.
48
+ *
49
+ * If `.hem/worktree/` already exists (from a previous interrupted run), it is
50
+ * forcibly removed first.
51
+ *
52
+ * @param repoRoot - Absolute path to the git repository root.
53
+ * @param branchName - New branch name to create (e.g. `hem/main-20260322-143000`).
54
+ * @returns Absolute path to the created worktree directory.
55
+ */
56
+ export declare function setupWorktree(repoRoot: string, branchName: string): string;
57
+ /**
58
+ * Stages all changes in the worktree and creates a commit.
59
+ *
60
+ * If there are no changes to commit (clean working tree) this is a no-op.
61
+ *
62
+ * @param worktreePath - Absolute path to the worktree directory.
63
+ * @param message - Commit message.
64
+ */
65
+ export declare function commitWorktree(worktreePath: string, message: string): void;
66
+ /**
67
+ * Pushes `branchName` from the worktree to `origin`.
68
+ *
69
+ * @param worktreePath - Absolute path to the worktree directory.
70
+ * @param branchName - Branch to push (e.g. `hem/main-20260322-143000`).
71
+ */
72
+ export declare function pushWorktree(worktreePath: string, branchName: string): void;
73
+ /**
74
+ * Removes the worktree at `worktreePath` from the git worktree list and deletes
75
+ * its directory.
76
+ *
77
+ * This is best-effort — errors are swallowed so cleanup never aborts the run.
78
+ *
79
+ * @param repoRoot - Absolute path to the git repository root.
80
+ * @param worktreePath - Absolute path to the worktree directory.
81
+ */
82
+ export declare function cleanupWorktree(repoRoot: string, worktreePath: string): void;
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Git worktree utilities for the --worktree flag.
3
+ *
4
+ * Creates an isolated git worktree under `.hem/worktree/` on a fresh branch,
5
+ * runs the pipeline there, then commits and pushes the branch for review.
6
+ *
7
+ * All functions use execSync (same pattern as changelog.ts).
8
+ */
9
+ import { join } from "node:path";
10
+ import { readFile, writeFile } from "node:fs/promises";
11
+ import { execSync } from "node:child_process";
12
+ // ── Repo helpers ───────────────────────────────────────────────────────
13
+ /**
14
+ * Returns the absolute path to the root of the git repository containing `cwd`.
15
+ *
16
+ * @throws If `cwd` is not inside a git repository.
17
+ */
18
+ export function getRepoRoot(cwd) {
19
+ return execSync("git rev-parse --show-toplevel", {
20
+ cwd,
21
+ encoding: "utf-8",
22
+ timeout: 5_000,
23
+ }).trim();
24
+ }
25
+ /**
26
+ * Returns the name of the currently checked-out branch.
27
+ *
28
+ * @param repoRoot - Absolute path to the git repository root.
29
+ * @throws If the repo is in a detached HEAD state or git is unavailable.
30
+ */
31
+ export function getCurrentBranch(repoRoot) {
32
+ return execSync("git rev-parse --abbrev-ref HEAD", {
33
+ cwd: repoRoot,
34
+ encoding: "utf-8",
35
+ timeout: 5_000,
36
+ }).trim();
37
+ }
38
+ // ── Branch naming ──────────────────────────────────────────────────────
39
+ /**
40
+ * Generates a unique worktree branch name based on the current branch and a
41
+ * timestamp.
42
+ *
43
+ * Format: `hem/<sanitized-base>-<YYYYMMDD-HHmmss>`
44
+ *
45
+ * The base branch name is sanitized to be a valid git ref component:
46
+ * slashes and non-alphanumeric characters (except `-`) are replaced with `-`,
47
+ * and leading/trailing dashes are removed.
48
+ *
49
+ * @param baseBranch - The currently checked-out branch name.
50
+ */
51
+ export function generateWorktreeBranch(baseBranch) {
52
+ const sanitized = baseBranch
53
+ .replace(/[^a-zA-Z0-9-]/g, "-")
54
+ .replace(/-+/g, "-")
55
+ .replace(/^-|-$/g, "");
56
+ const now = new Date();
57
+ const pad = (n) => String(n).padStart(2, "0");
58
+ const datePart = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`;
59
+ const timePart = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
60
+ return `hem/${sanitized}-${datePart}-${timePart}`;
61
+ }
62
+ // ── .gitignore ─────────────────────────────────────────────────────────
63
+ /**
64
+ * Ensures `pattern` appears in `<repoRoot>/.gitignore`.
65
+ *
66
+ * If the file does not exist it is created. If the pattern is already present
67
+ * (exact line match) this is a no-op.
68
+ *
69
+ * @param repoRoot - Absolute path to the git repository root.
70
+ * @param pattern - The gitignore pattern to add (e.g. `.hem/worktree/`).
71
+ */
72
+ export async function ensureGitignored(repoRoot, pattern) {
73
+ const gitignorePath = join(repoRoot, ".gitignore");
74
+ let content = "";
75
+ try {
76
+ content = await readFile(gitignorePath, "utf-8");
77
+ }
78
+ catch {
79
+ // File doesn't exist — will be created below
80
+ }
81
+ const lines = content.split("\n");
82
+ if (lines.some((line) => line.trim() === pattern)) {
83
+ return; // Already present
84
+ }
85
+ const newContent = content.length > 0 && !content.endsWith("\n")
86
+ ? `${content}\n${pattern}\n`
87
+ : `${content}${pattern}\n`;
88
+ await writeFile(gitignorePath, newContent, "utf-8");
89
+ }
90
+ // ── Worktree lifecycle ─────────────────────────────────────────────────
91
+ /**
92
+ * Creates a git worktree at `.hem/worktree/` inside `repoRoot` checked out on
93
+ * a new branch `branchName`.
94
+ *
95
+ * If `.hem/worktree/` already exists (from a previous interrupted run), it is
96
+ * forcibly removed first.
97
+ *
98
+ * @param repoRoot - Absolute path to the git repository root.
99
+ * @param branchName - New branch name to create (e.g. `hem/main-20260322-143000`).
100
+ * @returns Absolute path to the created worktree directory.
101
+ */
102
+ export function setupWorktree(repoRoot, branchName) {
103
+ const worktreePath = join(repoRoot, ".hem", "worktree");
104
+ // Remove stale worktree if present
105
+ try {
106
+ execSync(`git worktree remove "${worktreePath}" --force`, {
107
+ cwd: repoRoot,
108
+ encoding: "utf-8",
109
+ timeout: 15_000,
110
+ stdio: "pipe",
111
+ });
112
+ }
113
+ catch {
114
+ // Didn't exist — that's fine
115
+ }
116
+ execSync(`git worktree add "${worktreePath}" -b "${branchName}"`, {
117
+ cwd: repoRoot,
118
+ encoding: "utf-8",
119
+ timeout: 30_000,
120
+ });
121
+ return worktreePath;
122
+ }
123
+ /**
124
+ * Stages all changes in the worktree and creates a commit.
125
+ *
126
+ * If there are no changes to commit (clean working tree) this is a no-op.
127
+ *
128
+ * @param worktreePath - Absolute path to the worktree directory.
129
+ * @param message - Commit message.
130
+ */
131
+ export function commitWorktree(worktreePath, message) {
132
+ // Stage everything
133
+ execSync("git add -A", {
134
+ cwd: worktreePath,
135
+ encoding: "utf-8",
136
+ timeout: 15_000,
137
+ });
138
+ // Check if there's anything to commit
139
+ const status = execSync("git status --porcelain", {
140
+ cwd: worktreePath,
141
+ encoding: "utf-8",
142
+ timeout: 10_000,
143
+ }).trim();
144
+ if (status.length === 0) {
145
+ return; // Nothing to commit
146
+ }
147
+ execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, {
148
+ cwd: worktreePath,
149
+ encoding: "utf-8",
150
+ timeout: 30_000,
151
+ });
152
+ }
153
+ /**
154
+ * Pushes `branchName` from the worktree to `origin`.
155
+ *
156
+ * @param worktreePath - Absolute path to the worktree directory.
157
+ * @param branchName - Branch to push (e.g. `hem/main-20260322-143000`).
158
+ */
159
+ export function pushWorktree(worktreePath, branchName) {
160
+ execSync(`git push origin "${branchName}"`, {
161
+ cwd: worktreePath,
162
+ encoding: "utf-8",
163
+ timeout: 60_000,
164
+ });
165
+ }
166
+ /**
167
+ * Removes the worktree at `worktreePath` from the git worktree list and deletes
168
+ * its directory.
169
+ *
170
+ * This is best-effort — errors are swallowed so cleanup never aborts the run.
171
+ *
172
+ * @param repoRoot - Absolute path to the git repository root.
173
+ * @param worktreePath - Absolute path to the worktree directory.
174
+ */
175
+ export function cleanupWorktree(repoRoot, worktreePath) {
176
+ try {
177
+ execSync(`git worktree remove "${worktreePath}" --force`, {
178
+ cwd: repoRoot,
179
+ encoding: "utf-8",
180
+ timeout: 15_000,
181
+ stdio: "pipe",
182
+ });
183
+ }
184
+ catch {
185
+ // Best-effort — ignore errors
186
+ }
187
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@pruddiman/hem",
3
+ "version": "0.0.1-beta-5671db0",
4
+ "type": "module",
5
+ "bin": {
6
+ "hem": "./dist/index.js"
7
+ },
8
+ "engines": {
9
+ "node": ">=20.0.0"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "tsc && node ./dist/index.js",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest",
19
+ "typecheck": "tsc --noEmit",
20
+ "lint": "tsc --noEmit",
21
+ "publish:beta": "node scripts/publish-beta.mjs"
22
+ },
23
+ "dependencies": {
24
+ "@github/copilot-sdk": "^0.2.2",
25
+ "@modelcontextprotocol/sdk": "^1.26.0",
26
+ "@opencode-ai/sdk": "^1.14.19",
27
+ "better-sqlite3": "^12.8.0",
28
+ "commander": "^14.0.3",
29
+ "fast-glob": "^3.3.3",
30
+ "ink": "^6.7.0",
31
+ "ink-spinner": "^5.0.0",
32
+ "p-limit": "^7.3.0",
33
+ "react": "^19.2.4",
34
+ "vectra": "^0.12.3"
35
+ },
36
+ "devDependencies": {
37
+ "@types/better-sqlite3": "^7.6.13",
38
+ "@types/node": "^25.2.3",
39
+ "@types/react": "^19.2.14",
40
+ "@vitest/coverage-v8": "^4.0.18",
41
+ "ink-testing-library": "^4.0.0",
42
+ "typescript": "^5.9.3",
43
+ "vitest": "^4.0.18"
44
+ }
45
+ }