@xerktech/claude-hud 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 (60) hide show
  1. package/README.md +50 -0
  2. package/dist/_broker/attached.js +78 -0
  3. package/dist/_broker/attached.js.map +1 -0
  4. package/dist/_broker/audio-codec.js +82 -0
  5. package/dist/_broker/audio-codec.js.map +1 -0
  6. package/dist/_broker/audio-session.js +81 -0
  7. package/dist/_broker/audio-session.js.map +1 -0
  8. package/dist/_broker/auth.js +32 -0
  9. package/dist/_broker/auth.js.map +1 -0
  10. package/dist/_broker/bus.js +76 -0
  11. package/dist/_broker/bus.js.map +1 -0
  12. package/dist/_broker/cert.js +72 -0
  13. package/dist/_broker/cert.js.map +1 -0
  14. package/dist/_broker/claude.js +160 -0
  15. package/dist/_broker/claude.js.map +1 -0
  16. package/dist/_broker/cli.js +134 -0
  17. package/dist/_broker/cli.js.map +1 -0
  18. package/dist/_broker/cors.js +48 -0
  19. package/dist/_broker/cors.js.map +1 -0
  20. package/dist/_broker/hooks.js +196 -0
  21. package/dist/_broker/hooks.js.map +1 -0
  22. package/dist/_broker/index.js +48 -0
  23. package/dist/_broker/index.js.map +1 -0
  24. package/dist/_broker/intent-dispatcher.js +86 -0
  25. package/dist/_broker/intent-dispatcher.js.map +1 -0
  26. package/dist/_broker/intent.js +127 -0
  27. package/dist/_broker/intent.js.map +1 -0
  28. package/dist/_broker/jsonl-tail.js +185 -0
  29. package/dist/_broker/jsonl-tail.js.map +1 -0
  30. package/dist/_broker/mdns.js +41 -0
  31. package/dist/_broker/mdns.js.map +1 -0
  32. package/dist/_broker/projects.js +161 -0
  33. package/dist/_broker/projects.js.map +1 -0
  34. package/dist/_broker/qr.js +11 -0
  35. package/dist/_broker/qr.js.map +1 -0
  36. package/dist/_broker/routes.js +379 -0
  37. package/dist/_broker/routes.js.map +1 -0
  38. package/dist/_broker/server.js +325 -0
  39. package/dist/_broker/server.js.map +1 -0
  40. package/dist/_broker/sessions-store.js +50 -0
  41. package/dist/_broker/sessions-store.js.map +1 -0
  42. package/dist/_broker/sessions.js +792 -0
  43. package/dist/_broker/sessions.js.map +1 -0
  44. package/dist/_broker/store.js +79 -0
  45. package/dist/_broker/store.js.map +1 -0
  46. package/dist/_broker/stt.js +93 -0
  47. package/dist/_broker/stt.js.map +1 -0
  48. package/dist/broker-bin.js +37 -0
  49. package/dist/broker-bin.js.map +1 -0
  50. package/dist/broker-lifecycle.js +186 -0
  51. package/dist/broker-lifecycle.js.map +1 -0
  52. package/dist/index.js +189 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/paths.js +17 -0
  55. package/dist/paths.js.map +1 -0
  56. package/dist/wrapper/index.js +263 -0
  57. package/dist/wrapper/index.js.map +1 -0
  58. package/dist/wrapper/slug.js +5 -0
  59. package/dist/wrapper/slug.js.map +1 -0
  60. package/package.json +70 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Voice intent parser. Maps a Whisper transcript to one of the four supported
3
+ * commands from CLAUDE.md §8.4 + PLAN.md M6:
4
+ *
5
+ * - `new session in <project> [to <prompt>]` / `start <project> [...]`
6
+ * - `end this session` (focused) / `end session in <project>`
7
+ * - `focus <project>` / `switch to <project>`
8
+ * - otherwise: write the transcript to the focused session's stdin.
9
+ *
10
+ * Project labels are matched fuzzily via fuse.js with a 0.4 threshold so the
11
+ * user can say "myapp" for a project labelled "My App". Ambiguous matches
12
+ * (multiple candidates within the threshold) surface as `kind: 'ambiguous'`
13
+ * so the UI can route to a pre-filtered Project Picker rather than guessing.
14
+ */
15
+ import Fuse from 'fuse.js';
16
+ const DEFAULT_THRESHOLD = 0.4;
17
+ /**
18
+ * Spawn-verb stripper. We match in two passes because there are several
19
+ * ways the user can phrase a spawn:
20
+ *
21
+ * - "new session in <project>"
22
+ * - "start a new session for <project>"
23
+ * - "create session <project>"
24
+ * - "spawn session <project>"
25
+ * - "start <project>" (bare verb form)
26
+ *
27
+ * After stripping the verb (and optional preposition), the remainder is the
28
+ * project phrase, optionally followed by " to <prompt>" / " saying <prompt>".
29
+ */
30
+ const SPAWN_VERB_PATTERN = /^(?:new\s+session|start(?:\s+a)?(?:\s+new)?\s+session|create\s+session|spawn\s+session|start)\s*(?:in|on|for|with|using)?\s*/i;
31
+ const SPAWN_PROMPT_PATTERN = /^(?<project>.+?)(?:\s+(?:to|and|saying)\s+(?<prompt>.+))?$/i;
32
+ const END_PATTERN = /^end\s+(?:this\s+)?session(?:\s+(?:in|on|for)\s+(?<project>.+))?$/i;
33
+ const FOCUS_PATTERN = /^(?:focus(?:\s+on)?|switch(?:\s+to)?|open|show)\s+(?<project>.+)$/i;
34
+ /**
35
+ * Parse a transcript into a structured intent. The matcher is intentionally
36
+ * conservative: anything that doesn't look like a command is treated as
37
+ * free-form text and forwarded to the focused session.
38
+ */
39
+ export function parseIntent(rawTranscript, projects, opts = {}) {
40
+ const transcript = normaliseTranscript(rawTranscript);
41
+ if (transcript.length === 0)
42
+ return { kind: 'noop', reason: 'empty transcript' };
43
+ const fuse = new Fuse(projects, {
44
+ keys: ['label', 'id'],
45
+ threshold: opts.fuzzyThreshold ?? DEFAULT_THRESHOLD,
46
+ includeScore: true,
47
+ ignoreLocation: true,
48
+ });
49
+ // End — checked first because "end session" overlaps with "new session".
50
+ const endMatch = END_PATTERN.exec(transcript);
51
+ if (endMatch) {
52
+ const projectPhrase = endMatch.groups?.project?.trim();
53
+ if (!projectPhrase)
54
+ return { kind: 'end_focused' };
55
+ const matched = fuzzyMatch(fuse, projectPhrase);
56
+ if (matched.kind === 'one')
57
+ return { kind: 'end_project', project: matched.project };
58
+ if (matched.kind === 'many')
59
+ return { kind: 'ambiguous', candidates: matched.candidates, verb: 'end' };
60
+ return { kind: 'send', text: transcript };
61
+ }
62
+ // Focus / switch.
63
+ const focusMatch = FOCUS_PATTERN.exec(transcript);
64
+ if (focusMatch) {
65
+ const projectPhrase = focusMatch.groups?.project?.trim();
66
+ if (projectPhrase) {
67
+ const matched = fuzzyMatch(fuse, projectPhrase);
68
+ if (matched.kind === 'one')
69
+ return { kind: 'focus', project: matched.project };
70
+ if (matched.kind === 'many')
71
+ return { kind: 'ambiguous', candidates: matched.candidates, verb: 'focus' };
72
+ }
73
+ // Fall through — "switch to" / "open" with no fuzzy match is just free-form.
74
+ return { kind: 'send', text: transcript };
75
+ }
76
+ // Spawn — only fires when the transcript explicitly opens with a spawn verb.
77
+ // Bare "<project name>" alone shouldn't spawn a session by accident.
78
+ const verbMatch = SPAWN_VERB_PATTERN.exec(transcript);
79
+ if (verbMatch) {
80
+ const remainder = transcript.slice(verbMatch[0].length).trim();
81
+ if (remainder.length === 0)
82
+ return { kind: 'send', text: transcript };
83
+ const promptMatch = SPAWN_PROMPT_PATTERN.exec(remainder);
84
+ const projectPhrase = promptMatch?.groups?.project?.trim();
85
+ const prompt = promptMatch?.groups?.prompt?.trim();
86
+ if (projectPhrase) {
87
+ const matched = fuzzyMatch(fuse, projectPhrase);
88
+ if (matched.kind === 'one') {
89
+ const intent = { kind: 'spawn', project: matched.project };
90
+ if (prompt && prompt.length > 0)
91
+ intent.prompt = prompt;
92
+ return intent;
93
+ }
94
+ if (matched.kind === 'many')
95
+ return { kind: 'ambiguous', candidates: matched.candidates, verb: 'spawn' };
96
+ }
97
+ return { kind: 'send', text: transcript };
98
+ }
99
+ return { kind: 'send', text: transcript };
100
+ }
101
+ /**
102
+ * Run the fuzzy match. Returns the single clear winner, the candidate list
103
+ * when scores are tied (within `tiebreakMargin` of the best), or none.
104
+ */
105
+ function fuzzyMatch(fuse, phrase) {
106
+ const tiebreakMargin = 0.1;
107
+ const results = fuse.search(phrase, { limit: 5 });
108
+ if (results.length === 0)
109
+ return { kind: 'none' };
110
+ const best = results[0];
111
+ if (best.score === undefined)
112
+ return { kind: 'one', project: best.item };
113
+ const ties = results.filter(r => r.score !== undefined && r.score - best.score <= tiebreakMargin);
114
+ if (ties.length <= 1)
115
+ return { kind: 'one', project: best.item };
116
+ return { kind: 'many', candidates: ties.map(r => r.item) };
117
+ }
118
+ /** Trim, collapse whitespace, drop a single trailing period (Whisper habit). */
119
+ function normaliseTranscript(raw) {
120
+ let t = raw.trim().replace(/\s+/g, ' ');
121
+ // Whisper often emits a trailing period — strip it so the regexes anchor cleanly.
122
+ while (t.endsWith('.') || t.endsWith('!') || t.endsWith('?') || t.endsWith(',')) {
123
+ t = t.slice(0, -1).trim();
124
+ }
125
+ return t;
126
+ }
127
+ //# sourceMappingURL=intent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent.js","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,185 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import chokidar from 'chokidar';
4
+ export class JsonlTailer {
5
+ filePath;
6
+ onEvent;
7
+ onError;
8
+ fsImpl;
9
+ watchFactory;
10
+ watcher;
11
+ lastByte = 0;
12
+ partial = '';
13
+ reading = false;
14
+ pendingRead = false;
15
+ stopped = false;
16
+ constructor(opts) {
17
+ this.filePath = opts.filePath;
18
+ this.onEvent = opts.onEvent;
19
+ this.onError = opts.onError ?? (err => console.warn(`[jsonl-tail] ${err.message}`));
20
+ this.fsImpl = opts.fsImpl ?? fs;
21
+ this.watchFactory = opts.watch ?? chokidar.watch.bind(chokidar);
22
+ }
23
+ /**
24
+ * Begin watching the file. If the file already exists, we read from byte 0
25
+ * to capture the backlog before subscribing to change events.
26
+ */
27
+ async start() {
28
+ if (this.stopped)
29
+ throw new Error('JsonlTailer already stopped');
30
+ if (this.watcher)
31
+ return;
32
+ // chokidar's `awaitWriteFinish` slows initial detection too much; we
33
+ // instead watch the parent dir for `add` and the file itself for `change`.
34
+ this.watcher = this.watchFactory(this.filePath, {
35
+ persistent: true,
36
+ ignoreInitial: false,
37
+ awaitWriteFinish: false,
38
+ });
39
+ this.watcher.on('add', () => {
40
+ // File appeared (or already existed at startup). Start reading from 0.
41
+ this.lastByte = 0;
42
+ this.partial = '';
43
+ void this.readDelta();
44
+ });
45
+ this.watcher.on('change', () => void this.readDelta());
46
+ this.watcher.on('unlink', () => {
47
+ // Claude Code rewrote / cleared the file. Reset cursor; the next 'add'
48
+ // re-reads from the top.
49
+ this.lastByte = 0;
50
+ this.partial = '';
51
+ });
52
+ this.watcher.on('error', err => this.onError(err));
53
+ }
54
+ async stop() {
55
+ this.stopped = true;
56
+ if (this.watcher) {
57
+ const w = this.watcher;
58
+ this.watcher = undefined;
59
+ try {
60
+ await w.close();
61
+ }
62
+ catch (err) {
63
+ this.onError(err);
64
+ }
65
+ }
66
+ }
67
+ /**
68
+ * Read everything from `lastByte` to the file's current size and feed parsed
69
+ * lines to `onEvent`. Re-entrant-safe: if a change event lands while a read
70
+ * is in flight, we set a pending flag and run a single follow-up read.
71
+ */
72
+ async readDelta() {
73
+ if (this.stopped)
74
+ return;
75
+ if (this.reading) {
76
+ this.pendingRead = true;
77
+ return;
78
+ }
79
+ this.reading = true;
80
+ try {
81
+ let stat;
82
+ try {
83
+ stat = await this.fsImpl.promises.stat(this.filePath);
84
+ }
85
+ catch (err) {
86
+ // File deleted mid-watch — give up this round; chokidar's unlink will
87
+ // reset the cursor.
88
+ if (err.code !== 'ENOENT') {
89
+ this.onError(err);
90
+ }
91
+ return;
92
+ }
93
+ if (stat.size < this.lastByte) {
94
+ // File shrank — claude --clear, or a fresh session reused the path.
95
+ this.lastByte = 0;
96
+ this.partial = '';
97
+ }
98
+ if (stat.size === this.lastByte)
99
+ return;
100
+ const handle = await this.fsImpl.promises.open(this.filePath, 'r');
101
+ try {
102
+ const length = stat.size - this.lastByte;
103
+ const buf = Buffer.alloc(length);
104
+ await handle.read(buf, 0, length, this.lastByte);
105
+ this.lastByte = stat.size;
106
+ this.processChunk(buf.toString('utf8'));
107
+ }
108
+ finally {
109
+ await handle.close();
110
+ }
111
+ }
112
+ catch (err) {
113
+ this.onError(err);
114
+ }
115
+ finally {
116
+ this.reading = false;
117
+ if (this.pendingRead) {
118
+ this.pendingRead = false;
119
+ void this.readDelta();
120
+ }
121
+ }
122
+ }
123
+ processChunk(chunk) {
124
+ this.partial += chunk;
125
+ let nl;
126
+ while ((nl = this.partial.indexOf('\n')) >= 0) {
127
+ const line = this.partial.slice(0, nl).trim();
128
+ this.partial = this.partial.slice(nl + 1);
129
+ if (!line)
130
+ continue;
131
+ this.emitLine(line);
132
+ }
133
+ }
134
+ emitLine(line) {
135
+ try {
136
+ const parsed = JSON.parse(line);
137
+ if (typeof parsed === 'object' && parsed !== null) {
138
+ this.onEvent(parsed);
139
+ }
140
+ }
141
+ catch (err) {
142
+ this.onError(new Error(`bad jsonl line: ${err.message}`));
143
+ }
144
+ }
145
+ }
146
+ /**
147
+ * Build Claude Code's project-directory slug from an absolute cwd.
148
+ *
149
+ * Claude Code stores each session's `.jsonl` under
150
+ * `~/.claude/projects/<slug>/<uuid>.jsonl`. The slug is derived by replacing
151
+ * every path separator and `:` in the absolute cwd with `-`:
152
+ *
153
+ * /home/user/ClaudeHUD → home-user-ClaudeHUD
154
+ * C:\Users\me\Code\ClaudeHUD → C--Users-me-Code-ClaudeHUD
155
+ *
156
+ * Note the double-dash that comes from `:` and `\` collapsing on Windows is
157
+ * intentional — it matches the on-disk layout produced by Claude Code itself.
158
+ *
159
+ * Lives here (next to the tailer) so the broker and the CLI wrapper can both
160
+ * import it without a circular package dependency.
161
+ */
162
+ export function claudeCodeProjectSlug(absoluteCwd) {
163
+ // Replace each separator/`:` individually (not via a class) so `C:\` produces
164
+ // `C--` rather than `C-`.
165
+ const out = [];
166
+ for (const ch of absoluteCwd) {
167
+ if (ch === '/' || ch === '\\' || ch === ':') {
168
+ out.push('-');
169
+ }
170
+ else {
171
+ out.push(ch);
172
+ }
173
+ }
174
+ return out.join('').replace(/^-+/, '');
175
+ }
176
+ /**
177
+ * Default directory under which Claude Code writes session jsonl files.
178
+ * Overridable via `CLAUDE_PROJECTS_DIR` (used by tests).
179
+ */
180
+ export function claudeProjectsRoot(home = process.env.HOME ?? '') {
181
+ if (process.env.CLAUDE_PROJECTS_DIR)
182
+ return process.env.CLAUDE_PROJECTS_DIR;
183
+ return path.join(home, '.claude', 'projects');
184
+ }
185
+ //# sourceMappingURL=jsonl-tail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl-tail.js","sourceRoot":"","sources":["../src/jsonl-tail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { Bonjour } from 'bonjour-service';
2
+ export function announce(opts) {
3
+ const bonjour = new Bonjour();
4
+ const service = bonjour.publish({
5
+ name: opts.name,
6
+ type: 'claude-broker',
7
+ protocol: 'tcp',
8
+ port: opts.port,
9
+ host: opts.hostname,
10
+ txt: opts.txt ?? {},
11
+ });
12
+ return {
13
+ async stop() {
14
+ await new Promise(resolve => {
15
+ let done = false;
16
+ const finish = () => {
17
+ if (done)
18
+ return;
19
+ done = true;
20
+ bonjour.destroy();
21
+ resolve();
22
+ };
23
+ try {
24
+ if (typeof service.stop === 'function') {
25
+ service.stop(finish);
26
+ }
27
+ else {
28
+ finish();
29
+ return;
30
+ }
31
+ }
32
+ catch {
33
+ finish();
34
+ return;
35
+ }
36
+ setTimeout(finish, 1000).unref();
37
+ });
38
+ },
39
+ };
40
+ }
41
+ //# sourceMappingURL=mdns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mdns.js","sourceRoot":"","sources":["../src/mdns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAgB,MAAM,iBAAiB,CAAA;AAavD,MAAM,UAAU,QAAQ,CAAC,IAAiB;IACxC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;IAC7B,MAAM,OAAO,GAAY,OAAO,CAAC,OAAO,CAAC;QACvC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,eAAe;QACrB,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,QAAQ;QACnB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE;KACpB,CAAC,CAAA;IAEF,OAAO;QACL,KAAK,CAAC,IAAI;YACR,MAAM,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE;gBAChC,IAAI,IAAI,GAAG,KAAK,CAAA;gBAChB,MAAM,MAAM,GAAG,GAAS,EAAE;oBACxB,IAAI,IAAI;wBAAE,OAAM;oBAChB,IAAI,GAAG,IAAI,CAAA;oBACX,OAAO,CAAC,OAAO,EAAE,CAAA;oBACjB,OAAO,EAAE,CAAA;gBACX,CAAC,CAAA;gBACD,IAAI,CAAC;oBACH,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;wBACvC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;oBACtB,CAAC;yBAAM,CAAC;wBACN,MAAM,EAAE,CAAA;wBACR,OAAM;oBACR,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,EAAE,CAAA;oBACR,OAAM;gBACR,CAAC;gBACD,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAA;YAClC,CAAC,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,161 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ const DEFAULT_STATE_DIR = process.env.CLAUDE_HUD_DIR ?? path.join(os.homedir(), '.claude-hud');
5
+ const LABEL_CAP = 64;
6
+ export class ProjectsStore {
7
+ dir;
8
+ file;
9
+ cache = null;
10
+ constructor(dir = DEFAULT_STATE_DIR) {
11
+ this.dir = dir;
12
+ this.file = path.join(dir, 'projects.json');
13
+ }
14
+ load() {
15
+ if (this.cache)
16
+ return this.cache;
17
+ fs.mkdirSync(this.dir, { recursive: true });
18
+ let parsed = {};
19
+ if (fs.existsSync(this.file)) {
20
+ try {
21
+ parsed = JSON.parse(fs.readFileSync(this.file, 'utf8'));
22
+ }
23
+ catch (err) {
24
+ const backup = `${this.file}.corrupt-${Date.now()}`;
25
+ fs.renameSync(this.file, backup);
26
+ console.warn(`[projects] projects.json was unreadable (${err.message}); moved to ${backup}`);
27
+ }
28
+ }
29
+ this.cache = withDefaults(parsed);
30
+ this.save();
31
+ return this.cache;
32
+ }
33
+ save() {
34
+ if (!this.cache)
35
+ return;
36
+ fs.mkdirSync(this.dir, { recursive: true });
37
+ const tmp = `${this.file}.tmp`;
38
+ fs.writeFileSync(tmp, `${JSON.stringify(this.cache, null, 2)}\n`, { mode: 0o600 });
39
+ fs.renameSync(tmp, this.file);
40
+ }
41
+ list() {
42
+ const file = this.load();
43
+ if (file.parentDir) {
44
+ // Auto-discover top-level dirs in parentDir, but never override manual entries
45
+ // — manual labels win on either matching id OR matching cwd.
46
+ const discovered = discoverProjects(file.parentDir);
47
+ const manualIds = new Set(file.projects.map(p => p.id));
48
+ const manualCwds = new Set(file.projects.map(p => p.cwd));
49
+ const extra = discovered.filter(p => !manualIds.has(p.id) && !manualCwds.has(p.cwd));
50
+ return [...file.projects, ...extra];
51
+ }
52
+ return [...file.projects];
53
+ }
54
+ get(id) {
55
+ return this.list().find(p => p.id === id);
56
+ }
57
+ /**
58
+ * Idempotent registration used by the `claude-hud` CLI on every invocation.
59
+ * Derives `id = slugify(basename(cwd))`; if that id already exists, returns
60
+ * the existing project (whether or not the cwd matches — the user may have
61
+ * renamed the dir). On a fresh id, adds the project and persists.
62
+ */
63
+ ensure(input) {
64
+ const cwd = path.resolve(input.cwd);
65
+ const base = path.basename(cwd) || cwd;
66
+ const label = sanitiseLabel(input.label ?? base);
67
+ const id = slugify(label) || slugify(base);
68
+ if (!id)
69
+ throw new Error('cannot derive id from empty label');
70
+ const file = this.load();
71
+ const existing = file.projects.find(p => p.id === id);
72
+ if (existing)
73
+ return existing;
74
+ const project = { id, label, cwd, defaults: input.defaults };
75
+ file.projects.push(project);
76
+ this.save();
77
+ return project;
78
+ }
79
+ add(input) {
80
+ const cwd = path.resolve(input.cwd);
81
+ const label = sanitiseLabel(input.label ?? (path.basename(cwd) || cwd));
82
+ const id = input.id ?? slugify(label);
83
+ if (!id)
84
+ throw new Error('cannot derive id from empty label');
85
+ const file = this.load();
86
+ if (file.projects.some(p => p.id === id)) {
87
+ throw new Error(`project ${id} already exists`);
88
+ }
89
+ const project = { id, label, cwd, defaults: input.defaults };
90
+ file.projects.push(project);
91
+ this.save();
92
+ return project;
93
+ }
94
+ remove(id) {
95
+ const file = this.load();
96
+ const before = file.projects.length;
97
+ file.projects = file.projects.filter(p => p.id !== id);
98
+ if (file.projects.length === before)
99
+ return false;
100
+ this.save();
101
+ return true;
102
+ }
103
+ setParentDir(dir) {
104
+ const file = this.load();
105
+ if (dir === undefined) {
106
+ delete file.parentDir;
107
+ }
108
+ else {
109
+ file.parentDir = path.resolve(dir);
110
+ }
111
+ this.save();
112
+ }
113
+ }
114
+ function withDefaults(input) {
115
+ const projects = Array.isArray(input.projects)
116
+ ? input.projects.filter(isValidProject).map(normaliseProject)
117
+ : [];
118
+ const parentDir = typeof input.parentDir === 'string' ? input.parentDir : undefined;
119
+ return { version: 1, projects, parentDir };
120
+ }
121
+ function isValidProject(p) {
122
+ if (!p || typeof p !== 'object')
123
+ return false;
124
+ const r = p;
125
+ return typeof r.id === 'string' && typeof r.cwd === 'string';
126
+ }
127
+ function normaliseProject(p) {
128
+ return {
129
+ id: p.id,
130
+ label: sanitiseLabel(typeof p.label === 'string' ? p.label : p.id),
131
+ cwd: p.cwd,
132
+ defaults: p.defaults,
133
+ };
134
+ }
135
+ export function sanitiseLabel(label) {
136
+ const trimmed = label.trim().replace(/\s+/g, ' ');
137
+ return trimmed.length > LABEL_CAP ? trimmed.slice(0, LABEL_CAP) : trimmed;
138
+ }
139
+ export function slugify(label) {
140
+ return label
141
+ .toLowerCase()
142
+ .replace(/[^a-z0-9]+/g, '-')
143
+ .replace(/^-+|-+$/g, '');
144
+ }
145
+ function discoverProjects(parentDir) {
146
+ const resolved = path.resolve(parentDir);
147
+ let entries;
148
+ try {
149
+ entries = fs.readdirSync(resolved, { withFileTypes: true });
150
+ }
151
+ catch {
152
+ return [];
153
+ }
154
+ return entries
155
+ .filter(e => e.isDirectory() && !e.name.startsWith('.'))
156
+ .map(e => {
157
+ const cwd = path.join(resolved, e.name);
158
+ return { id: slugify(e.name) || e.name, label: sanitiseLabel(e.name), cwd };
159
+ });
160
+ }
161
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../src/projects.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import qrcode from 'qrcode-terminal';
2
+ export function buildOnboardingPayload(p) {
3
+ return JSON.stringify(p);
4
+ }
5
+ export function printOnboardingQr(p) {
6
+ const payload = buildOnboardingPayload(p);
7
+ qrcode.generate(payload, { small: true }, (out) => {
8
+ process.stdout.write(`${out}\n`);
9
+ });
10
+ }
11
+ //# sourceMappingURL=qr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr.js","sourceRoot":"","sources":["../src/qr.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,iBAAiB,CAAA;AAOpC,MAAM,UAAU,sBAAsB,CAAC,CAAoB;IACzD,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAC1B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,CAAoB;IACpD,MAAM,OAAO,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAA;IACzC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,GAAW,EAAE,EAAE;QACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC"}