@lumoai/cli 1.15.0 → 1.18.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.
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.normalizeTaskId = normalizeTaskId;
37
+ exports.slugify = slugify;
38
+ exports.deriveBranchName = deriveBranchName;
39
+ exports.deriveWorktreeDirName = deriveWorktreeDirName;
40
+ exports.parseWorktreePorcelain = parseWorktreePorcelain;
41
+ exports.findWorktreeByTaskId = findWorktreeByTaskId;
42
+ exports.runGit = runGit;
43
+ exports.gitOutput = gitOutput;
44
+ exports.getGitDir = getGitDir;
45
+ exports.getGitCommonDir = getGitCommonDir;
46
+ exports.isMainCheckout = isMainCheckout;
47
+ exports.getRepoRoot = getRepoRoot;
48
+ /**
49
+ * Normalize a user-supplied task reference to canonical `LUM-<n>` form.
50
+ * Accepts `LUM-267`, `lum-267`, and bare `267`. Returns null for anything
51
+ * that is not a recognizable task id.
52
+ */
53
+ function normalizeTaskId(input) {
54
+ const trimmed = input.trim();
55
+ const prefixed = trimmed.match(/^lum-?(\d+)$/i);
56
+ if (prefixed)
57
+ return `LUM-${prefixed[1]}`;
58
+ const bare = trimmed.match(/^(\d+)$/);
59
+ if (bare)
60
+ return `LUM-${bare[1]}`;
61
+ return null;
62
+ }
63
+ /**
64
+ * Turn an arbitrary human slug into a branch/dir-safe segment: lowercase,
65
+ * non-alphanumeric runs collapse to a single dash, leading/trailing dashes
66
+ * stripped.
67
+ */
68
+ function slugify(s) {
69
+ return s
70
+ .trim()
71
+ .toLowerCase()
72
+ .replace(/[^a-z0-9]+/g, '-')
73
+ .replace(/^-+|-+$/g, '');
74
+ }
75
+ /** `lumo/LUM-267` or `lumo/LUM-267-<slug>`. */
76
+ function deriveBranchName(taskId, slug) {
77
+ const s = slug ? slugify(slug) : '';
78
+ return s ? `lumo/${taskId}-${s}` : `lumo/${taskId}`;
79
+ }
80
+ /** `.worktrees/` dir basename: `LUM-267` or `LUM-267-<slug>`. */
81
+ function deriveWorktreeDirName(taskId, slug) {
82
+ const s = slug ? slugify(slug) : '';
83
+ return s ? `${taskId}-${s}` : taskId;
84
+ }
85
+ const path = __importStar(require("path"));
86
+ /**
87
+ * Parse `git worktree list --porcelain` output into structured entries.
88
+ * Records are separated by blank lines; a detached worktree has a `detached`
89
+ * line instead of `branch`. Tolerates a final record with no trailing blank.
90
+ */
91
+ function parseWorktreePorcelain(output) {
92
+ const entries = [];
93
+ let cur = null;
94
+ const flush = () => {
95
+ if (cur)
96
+ entries.push(cur);
97
+ cur = null;
98
+ };
99
+ for (const line of output.split('\n')) {
100
+ if (line.startsWith('worktree ')) {
101
+ flush();
102
+ cur = { path: line.slice('worktree '.length), head: '', branch: null };
103
+ }
104
+ else if (!cur) {
105
+ continue;
106
+ }
107
+ else if (line.startsWith('HEAD ')) {
108
+ cur.head = line.slice('HEAD '.length);
109
+ }
110
+ else if (line.startsWith('branch ')) {
111
+ cur.branch = line.slice('branch '.length).replace(/^refs\/heads\//, '');
112
+ }
113
+ else if (line === '') {
114
+ flush();
115
+ }
116
+ }
117
+ flush();
118
+ return entries;
119
+ }
120
+ /**
121
+ * Find the worktree belonging to a task id. Matches either the branch
122
+ * (`lumo/LUM-267` exactly or `lumo/LUM-267-*`) or the directory basename
123
+ * (`LUM-267` exactly or `LUM-267-*`).
124
+ */
125
+ function findWorktreeByTaskId(entries, taskId) {
126
+ return (entries.find(e => {
127
+ const base = path.basename(e.path);
128
+ const branchMatch = e.branch === `lumo/${taskId}` ||
129
+ (e.branch?.startsWith(`lumo/${taskId}-`) ?? false);
130
+ const dirMatch = base === taskId || base.startsWith(`${taskId}-`);
131
+ return branchMatch || dirMatch;
132
+ }) ?? null);
133
+ }
134
+ const child_process_1 = require("child_process");
135
+ /** Low-level git runner — returns the raw spawn result, never throws on exit. */
136
+ function runGit(args, cwd) {
137
+ // 120s is a safety net against a genuine hang (e.g. an index.lock deadlock),
138
+ // NOT a tight bound: this wrapper runs legitimately-slow ops like `git fetch`
139
+ // and `git worktree add`, which must not be killed prematurely. On timeout
140
+ // spawnSync populates `r.error`, which gitOutput already rethrows.
141
+ return (0, child_process_1.spawnSync)('git', args, { cwd, encoding: 'utf8', timeout: 120_000 });
142
+ }
143
+ /**
144
+ * Run git and return trimmed stdout; THROWS with stderr on non-zero exit or
145
+ * spawn error. NOTE: unlike the same-named `gitOutput` in `cli/src/lib/git-task.ts`
146
+ * (which swallows failures and returns `''`), this one surfaces failures by
147
+ * throwing — callers must handle/expect the throw.
148
+ */
149
+ function gitOutput(args, cwd) {
150
+ const r = runGit(args, cwd);
151
+ if (r.error)
152
+ throw r.error;
153
+ if (r.status !== 0) {
154
+ throw new Error((r.stderr || '').trim() || `git ${args.join(' ')} failed`);
155
+ }
156
+ return (r.stdout ?? '').trim();
157
+ }
158
+ /** Absolute path to the worktree-local `.git` (file or dir). */
159
+ function getGitDir(cwd) {
160
+ return gitOutput(['rev-parse', '--absolute-git-dir'], cwd);
161
+ }
162
+ /** Absolute path to the shared `.git` (same as git-dir in the main checkout). */
163
+ function getGitCommonDir(cwd) {
164
+ const common = gitOutput(['rev-parse', '--git-common-dir'], cwd);
165
+ return path.resolve(cwd ?? process.cwd(), common);
166
+ }
167
+ /** True only in the main checkout (git-dir === common-dir). */
168
+ function isMainCheckout(cwd) {
169
+ return getGitDir(cwd) === getGitCommonDir(cwd);
170
+ }
171
+ /** Absolute repo root of the current working tree. */
172
+ function getRepoRoot(cwd) {
173
+ return gitOutput(['rev-parse', '--show-toplevel'], cwd);
174
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumoai/cli",
3
- "version": "1.15.0",
3
+ "version": "1.18.0",
4
4
  "description": "Lumo CLI — manage tasks and sessions from the terminal",
5
5
  "license": "MIT",
6
6
  "author": "cli@uselumo.ai",