@groundtrack/hooks 0.1.11 → 0.1.12

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.
@@ -1,7 +1,7 @@
1
1
  import type { ImportAgent, ImportVisibility } from "./types.js";
2
2
  export declare const IMPORT_USAGE: string;
3
3
  type ImportArguments = Readonly<{
4
- agent: ImportAgent;
4
+ agent: ImportAgent | null;
5
5
  repoDir?: string;
6
6
  homeDir?: string;
7
7
  allRepositories: boolean;
@@ -1,7 +1,7 @@
1
- import { resolve } from "node:path";
1
+ import { basename, resolve } from "node:path";
2
2
  import * as prompts from "@clack/prompts";
3
- import { DEFAULT_SESSION_IMPORT_AGENT, isSessionImportAgent, SESSION_IMPORT_AGENTS, } from "../agents.js";
4
- import { environment } from "../setup/agents.js";
3
+ import { isSessionImportAgent, SESSION_IMPORT_AGENTS } from "../agents.js";
4
+ import { discoverAgents, environment, savedAgents, } from "../setup/agents.js";
5
5
  import { CANONICAL_IMPORT_MAX_BYTES } from "./canonical.js";
6
6
  import { uploadAgentSession } from "./client.js";
7
7
  import { discoverSessions, normalizeRepositoryUrl, repositoryIdentity } from "./discovery.js";
@@ -17,7 +17,7 @@ export const IMPORT_USAGE = `Usage:
17
17
  [--visibility private|organization] [--dry-run] [--yes] [--json]
18
18
 
19
19
  Import options:
20
- --agent VALUE ${importAgentDescription} (default: ${DEFAULT_SESSION_IMPORT_AGENT})
20
+ --agent VALUE ${importAgentDescription}; omit in a terminal to choose
21
21
  --repo PATH Import sessions for this Git repository (default: current repository)
22
22
  --all-repos Import sessions associated with any Git repository
23
23
  --include-unscoped With --all-repos, also import sessions outside a Git repository
@@ -58,7 +58,7 @@ export function parseImportArguments(argv) {
58
58
  };
59
59
  let repoDir;
60
60
  let homeDir;
61
- let agent = DEFAULT_SESSION_IMPORT_AGENT;
61
+ let agent = null;
62
62
  let allRepositories = false;
63
63
  let includeUnscoped = false;
64
64
  let includeSubagents = false;
@@ -197,14 +197,8 @@ function sessionDateRange(sessions) {
197
197
  }
198
198
  : null;
199
199
  }
200
- export async function runImportCli(argv) {
201
- const args = parseImportArguments(argv);
202
- if (args.help) {
203
- process.stdout.write(IMPORT_USAGE);
204
- return;
205
- }
206
- const context = environment({ repoDir: args.repoDir, homeDir: args.homeDir });
207
- const format = sessionImportFormat(args.agent);
200
+ async function runAgentImport(args, agent, context, interactive) {
201
+ const format = sessionImportFormat(agent);
208
202
  const home = resolve(context.env[format.homeEnvironmentVariable] ||
209
203
  resolve(context.homeDir, format.defaultHomeDirectory));
210
204
  const repository = args.allRepositories ? undefined : repositoryIdentity(context.repoDir);
@@ -259,7 +253,7 @@ export async function runImportCli(argv) {
259
253
  }
260
254
  const scope = args.allRepositories ? "all repositories" : repository.root;
261
255
  const plan = {
262
- agent: args.agent,
256
+ agent,
263
257
  scope,
264
258
  selected: prepared.length,
265
259
  alreadyImported: prepared.filter((item) => item.alreadyImported).length,
@@ -284,9 +278,8 @@ export async function runImportCli(argv) {
284
278
  });
285
279
  if (preparationErrors.length)
286
280
  process.exitCode = 1;
287
- return;
281
+ return true;
288
282
  }
289
- const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && !args.json);
290
283
  if (!args.json)
291
284
  printPlan({
292
285
  displayName: format.displayName,
@@ -307,7 +300,7 @@ export async function runImportCli(argv) {
307
300
  });
308
301
  if (prompts.isCancel(approved) || !approved) {
309
302
  prompts.cancel("Nothing uploaded.");
310
- return;
303
+ return false;
311
304
  }
312
305
  }
313
306
  let imported = 0;
@@ -358,8 +351,53 @@ export async function runImportCli(argv) {
358
351
  process.stdout.write(`Imported ${imported}; duplicates ${duplicate}; skipped ${result.skipped}; failed ${failures.length}.\n${result.sessionsUrl}\n`);
359
352
  if (failures.length)
360
353
  process.exitCode = 1;
354
+ return true;
361
355
  }
362
356
  finally {
363
357
  discovery.cleanup?.();
364
358
  }
365
359
  }
360
+ async function chooseImportAgents(args, context, interactive) {
361
+ if (args.agent)
362
+ return [args.agent];
363
+ if (!interactive)
364
+ throw new Error("Choose an agent with --agent, or run groundtrack import in an interactive terminal to choose from a list.");
365
+ const discovered = discoverAgents(context);
366
+ const saved = savedAgents(context)?.filter(isSessionImportAgent) ?? null;
367
+ prompts.intro(`Import coding-agent sessions · ${basename(context.repoDir)}`);
368
+ const selection = await prompts.multiselect({
369
+ message: "Which coding agents do you want to import from?",
370
+ options: SESSION_IMPORT_AGENTS.map((agent) => {
371
+ const state = discovered.find((candidate) => candidate.id === agent.id);
372
+ return {
373
+ value: agent.id,
374
+ label: agent.name,
375
+ hint: state.configured ? "configured" : state.detected ? "detected" : "not detected",
376
+ };
377
+ }),
378
+ initialValues: saved ??
379
+ discovered
380
+ .filter((agent) => agent.detected && isSessionImportAgent(agent.id))
381
+ .map((agent) => agent.id),
382
+ required: false,
383
+ });
384
+ if (prompts.isCancel(selection) || selection.length === 0) {
385
+ prompts.cancel("No agents selected. Nothing imported.");
386
+ return [];
387
+ }
388
+ return selection;
389
+ }
390
+ export async function runImportCli(argv) {
391
+ const args = parseImportArguments(argv);
392
+ if (args.help) {
393
+ process.stdout.write(IMPORT_USAGE);
394
+ return;
395
+ }
396
+ const context = environment({ repoDir: args.repoDir, homeDir: args.homeDir });
397
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY && !args.json);
398
+ const agents = await chooseImportAgents(args, context, interactive);
399
+ for (const agent of agents) {
400
+ if (!(await runAgentImport(args, agent, context, interactive)))
401
+ return;
402
+ }
403
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundtrack/hooks",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Lifecycle hook utilities for Groundtrack MCP clients.",
5
5
  "keywords": [
6
6
  "claude-code",