kira-glasses-bridge 2.1.0 → 2.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kira-glasses-bridge",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Connects your Mac to Kira, the assistant for Even G2 glasses: Claude Code sessions, files, folders and apps, by voice.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/git.mjs CHANGED
@@ -153,3 +153,31 @@ async function summary(before, root, files, diff, truncated, commits, treeAfter)
153
153
  truncated,
154
154
  }
155
155
  }
156
+
157
+ /**
158
+ * Make a project folder ready: create it, then either clone the repo into it
159
+ * (SSH first, HTTPS as fallback) or start a fresh git repo with a README.
160
+ */
161
+ export async function setupProjectFolder({ dir, name, description, sshUrl, httpsUrl }) {
162
+ const { mkdir, readdir, writeFile, stat } = await import('node:fs/promises')
163
+ const { execFile } = await import('node:child_process')
164
+ const run = (args, cwd) =>
165
+ new Promise(resolve => execFile('git', args, { cwd, maxBuffer: 8 * 1024 * 1024, timeout: 120_000 }, (err, stdout, stderr) => resolve({ ok: !err, out: String(stdout ?? ''), err: String(stderr ?? err?.message ?? '') })))
166
+ await mkdir(dir, { recursive: true })
167
+ const entries = (await readdir(dir)).filter(e => e !== '.DS_Store')
168
+ const already = await stat(`${dir}/.git`).then(s => s.isDirectory()).catch(() => false)
169
+ if (already) return { path: display(dir), existing: true, note: 'already a git repository' }
170
+ if (sshUrl || httpsUrl) {
171
+ if (entries.length) return { error: `${display(dir)} is not empty; choose an empty folder to clone into` }
172
+ let r = sshUrl ? await run(['clone', sshUrl, dir], dir) : { ok: false, err: 'no ssh url' }
173
+ if (!r.ok && httpsUrl) r = await run(['clone', httpsUrl, dir], dir)
174
+ if (!r.ok) return { error: `could not clone: ${r.err.trim().split('\n').pop()}` }
175
+ return { path: display(dir), cloned: true }
176
+ }
177
+ const r = await run(['init', '-q'], dir)
178
+ if (!r.ok) return { error: `git init failed: ${r.err.trim()}` }
179
+ if (!entries.includes('README.md')) await writeFile(`${dir}/README.md`, `# ${name ?? display(dir).split('/').pop()}\n${description ? `\n${description}\n` : ''}`)
180
+ await run(['add', '-A'], dir)
181
+ const c = await run(['commit', '-q', '-m', 'Initial commit'], dir)
182
+ return { path: display(dir), initialized: true, committed: c.ok, ...(c.ok ? {} : { note: 'git init done; the first commit needs git user.name and user.email configured' }) }
183
+ }
package/src/index.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  // machine on the user's behalf. Env: KIRA_BRIDGE_URL, KIRA_BRIDGE_TOKEN.
4
4
  import WebSocket from 'ws'
5
5
  import { listProjects, listSessions, readSession } from './claude-sessions.mjs'
6
- import { snapshot, changesSince, display } from './git.mjs'
6
+ import { snapshot, changesSince, display, setupProjectFolder } from './git.mjs'
7
7
  import { resolveCwd } from './claude-run.mjs'
8
8
  import { agentById, AGENTS } from './agents/index.mjs'
9
9
  import { normalizeAnswer } from './agents/protocol.mjs'
@@ -118,6 +118,25 @@ const tools = {
118
118
  return tools.run_agent({ ...args, agent: 'claude' }, ws)
119
119
  },
120
120
 
121
+ /** A new project's folder on this Mac: created, cloned from its repo or started fresh. */
122
+ async setup_project_folder(args) {
123
+ const dir = resolveUserPath(String(args.path ?? ''))
124
+ return setupProjectFolder({ dir, name: args.name, description: args.description, sshUrl: args.ssh_url, httpsUrl: args.https_url })
125
+ },
126
+
127
+ /** Open Terminal on this Mac in a folder and run a command there (used to hand a Claude session to the person). */
128
+ async open_terminal(args) {
129
+ const dir = resolveUserPath(String(args.cwd ?? ''))
130
+ const command = String(args.command ?? '').replace(/[^\w\s./:@=-]/g, '')
131
+ if (process.platform !== 'darwin') return { error: 'opening a terminal is only supported on macOS' }
132
+ const { execFile } = await import('node:child_process')
133
+ const script = `tell application "Terminal"
134
+ activate
135
+ do script "cd ${JSON.stringify(dir)} && ${command}"
136
+ end tell`
137
+ return new Promise(resolve => execFile('osascript', ['-e', script], err => resolve(err ? { error: err.message } : { opened: true, cwd: display(dir), command })))
138
+ },
139
+
121
140
  /** Read-only git facts for a folder: repo, branch, head, dirty files. */
122
141
  async git_status(args) {
123
142
  const s = await snapshot(resolveCwd(args.path ?? args.cwd))