@gaia-ai/ui 0.6.5 → 0.8.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.
@@ -1,3 +1,3 @@
1
1
  export { type HereContext, isTicketIdentifier, isUuid, normalizeRepoUrl, type ParsedTarget, parseTarget, parseTicketIdentifierFromBranch, resolveHere, type TargetInputs, UI_VIEW_MODES, type UiInitialRoute, type UiViewMode, } from './route.js';
2
2
  export { cmdUi, default, type RunGaiaUiOptions, type UiDeps } from './ui.js';
3
- export { type HomeConnection, type ProjectRooting, resolveAgentCommand, resolveDefaultProject, resolveProjectRooting, } from './ui-home.js';
3
+ export { type HomeConnection, type ProjectRooting, resolveAgentCommand, resolveProjectRooting, } from './ui-home.js';
package/dist/src/index.js CHANGED
@@ -7,4 +7,4 @@
7
7
  // GAIA-223: the deep-link grammar (the pure CLI seam).
8
8
  export { isTicketIdentifier, isUuid, normalizeRepoUrl, parseTarget, parseTicketIdentifierFromBranch, resolveHere, UI_VIEW_MODES, } from './route.js';
9
9
  export { cmdUi, default } from './ui.js';
10
- export { resolveAgentCommand, resolveDefaultProject, resolveProjectRooting, } from './ui-home.js';
10
+ export { resolveAgentCommand, resolveProjectRooting, } from './ui-home.js';
@@ -3,6 +3,6 @@ export const commands = (acc) => [
3
3
  {
4
4
  name: 'ui',
5
5
  plugin: '@gaia-ai/ui',
6
- describe: 'interactive terminal UI: project dashboard, ticket list, ticket detail',
6
+ describe: 'interactive terminal UI: dashboard, projects, tickets',
7
7
  },
8
8
  ];
package/dist/src/route.js CHANGED
@@ -253,7 +253,7 @@ export async function resolveHere(ctx) {
253
253
  const got = remote === '' ? '' : `, got '${remote}'`;
254
254
  throw new Error(`gaia ui --here: could not derive a project from '${ctx.cwd}' — ` +
255
255
  `no usable git remote (looked at 'origin'${got}).\n` +
256
- " Run 'gaia ui' for the dashboard, or 'gaia ui project:<name>' to open one directly.");
256
+ " Run 'gaia ui' and press Tab for the project list, or 'gaia ui project:<name>' to open one directly.");
257
257
  }
258
258
  return {
259
259
  kind: 'project',
@@ -1,4 +1,4 @@
1
- import { type ConductorRegistryEntry, type MachineContext } from '@gaia-ai/core';
1
+ import { type MachineContext } from '@gaia-ai/core';
2
2
  /**
3
3
  * The resolved control-plane connection passed to the renderer launcher.
4
4
  * A structural mirror of `@gaia-ai/addon-gaia-ui`'s `HomeConnection` — kept
@@ -20,12 +20,6 @@ export interface HomeConnection {
20
20
  */
21
21
  writeProfile?: string | undefined;
22
22
  }
23
- /**
24
- * Resolve the default project (opaque group) from the conductor registry: the
25
- * `preferred` project when it is registered, else the first entry's project.
26
- * `undefined` when the registry is empty — a valid empty cockpit.
27
- */
28
- export declare function resolveDefaultProject(entries: ConductorRegistryEntry[], preferred?: string): string | undefined;
29
23
  /**
30
24
  * Resolve the agent-launch command builder from the home config. Turns a TUI
31
25
  * prompt into the shell command herdr runs. Defaults to `claude` (an empty
@@ -38,11 +32,18 @@ export declare function resolveAgentCommand(machine: Partial<MachineContext> & {
38
32
  /** Where `gaia ui` roots the agents it launches, and which project they belong to. */
39
33
  export interface ProjectRooting {
40
34
  cwd: string;
41
- project?: string | undefined;
42
- ownConductorMachineId?: string | undefined;
35
+ /**
36
+ * The `.gaia` dir of the project we are standing in, when there is one.
37
+ *
38
+ * The ui hands this over rather than a resolved project name: matching it
39
+ * against a conductor's `workspace_root` needs the control plane, and the
40
+ * client that reaches it first exists in the renderer (GAIA-232). Undefined
41
+ * outside any project.
42
+ */
43
+ gaiaDir?: string | undefined;
43
44
  }
44
45
  /**
45
- * Decide the agents' working directory + project (GAIA-219 AC-9).
46
+ * Decide the agents' working directory (GAIA-219 AC-9).
46
47
  *
47
48
  * The trigger is the **cwd**, deliberately not the winning connection config:
48
49
  * "which connection did we resolve" and "which project am I standing in" are
@@ -50,17 +51,22 @@ export interface ProjectRooting {
50
51
  * silently relocate the agents' working directory.
51
52
  *
52
53
  * Standing **in** a project (core's walk-up finds a `.gaia/` project dir that
53
- * is not the user's own `~/.gaia`), the cwd is that repo's **root** and the
54
- * matching registry entry supplies the project + machine id; a project with no
55
- * registry entry still roots at its repo root. Otherwise the registry default
56
- * applies verbatim, exactly as before this ticket.
54
+ * is not the user's own `~/.gaia`), the cwd is that repo's **root**. Otherwise
55
+ * the agents root at the home dir and the cockpit opens with no default group —
56
+ * an honest empty cockpit rather than a guess.
57
+ *
58
+ * The project name and this machine's conductor id used to come from a local
59
+ * registry file, which GAIA-232 deleted. Both now resolve in the renderer's
60
+ * `run()` — `Entity/conductor/Data/own-conductor.ts`, reached through the
61
+ * launcher's `gaiaDir` — from the conductor record whose `workspace_root`
62
+ * equals this dir: the same data, from the store that also knows whether the
63
+ * conductor is alive, and matched by the same exact normalised path comparison
64
+ * the registry lookup used.
57
65
  *
58
66
  * The marker is core's `findProjectGaiaDir` — "is there a `.gaia/` here" —
59
67
  * NOT the connection walk-up `findGaiaConfig`. Since GAIA-230 the latter skips
60
68
  * an engine-only `conductor.config.js`, which GAIA-218 makes the canonical repo
61
- * shape, so keying on it would leave the most common project rooted in the
62
- * registry default. Which connection won and which project we stand in stay two
63
- * separate questions, as this function's whole point requires.
69
+ * shape, so keying on it would leave the most common project rooted at home.
64
70
  *
65
71
  * `~/.gaia` is excluded for the same reason core labels it `home`: the walk-up
66
72
  * reaches it from every directory under `$HOME`, so counting it would make
@@ -69,4 +75,4 @@ export interface ProjectRooting {
69
75
  * Pure with respect to precedence — the walk-up is core's; this helper owns no
70
76
  * filesystem probe of its own.
71
77
  */
72
- export declare function resolveProjectRooting(cwd: string, entries: ConductorRegistryEntry[], homeDir: string): ProjectRooting;
78
+ export declare function resolveProjectRooting(cwd: string, homeDir: string): ProjectRooting;
@@ -5,17 +5,6 @@
5
5
  // holds no connection-precedence rule of its own (AC-6).
6
6
  import { dirname, resolve } from 'node:path';
7
7
  import { findProjectGaiaDir, homeGaiaDir, shellQuote, } from '@gaia-ai/core';
8
- /**
9
- * Resolve the default project (opaque group) from the conductor registry: the
10
- * `preferred` project when it is registered, else the first entry's project.
11
- * `undefined` when the registry is empty — a valid empty cockpit.
12
- */
13
- export function resolveDefaultProject(entries, preferred) {
14
- if (preferred && entries.some((e) => e.project === preferred)) {
15
- return preferred;
16
- }
17
- return entries[0]?.project;
18
- }
19
8
  /**
20
9
  * Resolve the agent-launch command builder from the home config. Turns a TUI
21
10
  * prompt into the shell command herdr runs. Defaults to `claude` (an empty
@@ -27,7 +16,7 @@ export function resolveAgentCommand(machine) {
27
16
  return (prompt) => prompt.trim() === '' ? bin : `${bin} ${shellQuote(prompt)}`;
28
17
  }
29
18
  /**
30
- * Decide the agents' working directory + project (GAIA-219 AC-9).
19
+ * Decide the agents' working directory (GAIA-219 AC-9).
31
20
  *
32
21
  * The trigger is the **cwd**, deliberately not the winning connection config:
33
22
  * "which connection did we resolve" and "which project am I standing in" are
@@ -35,17 +24,22 @@ export function resolveAgentCommand(machine) {
35
24
  * silently relocate the agents' working directory.
36
25
  *
37
26
  * Standing **in** a project (core's walk-up finds a `.gaia/` project dir that
38
- * is not the user's own `~/.gaia`), the cwd is that repo's **root** and the
39
- * matching registry entry supplies the project + machine id; a project with no
40
- * registry entry still roots at its repo root. Otherwise the registry default
41
- * applies verbatim, exactly as before this ticket.
27
+ * is not the user's own `~/.gaia`), the cwd is that repo's **root**. Otherwise
28
+ * the agents root at the home dir and the cockpit opens with no default group —
29
+ * an honest empty cockpit rather than a guess.
30
+ *
31
+ * The project name and this machine's conductor id used to come from a local
32
+ * registry file, which GAIA-232 deleted. Both now resolve in the renderer's
33
+ * `run()` — `Entity/conductor/Data/own-conductor.ts`, reached through the
34
+ * launcher's `gaiaDir` — from the conductor record whose `workspace_root`
35
+ * equals this dir: the same data, from the store that also knows whether the
36
+ * conductor is alive, and matched by the same exact normalised path comparison
37
+ * the registry lookup used.
42
38
  *
43
39
  * The marker is core's `findProjectGaiaDir` — "is there a `.gaia/` here" —
44
40
  * NOT the connection walk-up `findGaiaConfig`. Since GAIA-230 the latter skips
45
41
  * an engine-only `conductor.config.js`, which GAIA-218 makes the canonical repo
46
- * shape, so keying on it would leave the most common project rooted in the
47
- * registry default. Which connection won and which project we stand in stay two
48
- * separate questions, as this function's whole point requires.
42
+ * shape, so keying on it would leave the most common project rooted at home.
49
43
  *
50
44
  * `~/.gaia` is excluded for the same reason core labels it `home`: the walk-up
51
45
  * reaches it from every directory under `$HOME`, so counting it would make
@@ -54,25 +48,12 @@ export function resolveAgentCommand(machine) {
54
48
  * Pure with respect to precedence — the walk-up is core's; this helper owns no
55
49
  * filesystem probe of its own.
56
50
  */
57
- export function resolveProjectRooting(cwd, entries, homeDir) {
51
+ export function resolveProjectRooting(cwd, homeDir) {
58
52
  const gaiaDir = findProjectGaiaDir(cwd);
59
53
  // Core owns where `.gaia` lives; the ui never spells the path itself.
60
54
  const homeDotGaia = resolve(homeGaiaDir(homeDir));
61
55
  if (gaiaDir !== undefined && resolve(gaiaDir) !== homeDotGaia) {
62
- // Registry entries store the `.gaia` dir (the conductor's checkoutRoot),
63
- // so that — not the repo root — is what an entry is matched on.
64
- const entry = entries.find((e) => resolve(e.path) === resolve(gaiaDir));
65
- return {
66
- cwd: dirname(gaiaDir),
67
- project: entry?.project,
68
- ownConductorMachineId: entry?.id,
69
- };
56
+ return { cwd: dirname(gaiaDir), gaiaDir };
70
57
  }
71
- const project = resolveDefaultProject(entries);
72
- const projectEntry = entries.find((e) => e.project === project);
73
- return {
74
- cwd: projectEntry?.path ?? homeDir,
75
- project,
76
- ownConductorMachineId: projectEntry?.id,
77
- };
58
+ return { cwd: homeDir };
78
59
  }
package/dist/src/ui.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type AgentLaunchHost } from '@gaia-ai/addon-herdr';
2
- import { type ConductorRegistryEntry, type GaiaCommandHost, type GaiaCommandPlugin, type GaiaConnectionConfig, type MachineContext } from '@gaia-ai/core';
2
+ import { type GaiaCommandHost, type GaiaCommandPlugin, type GaiaConnectionConfig, type MachineContext } from '@gaia-ai/core';
3
3
  import { type UiInitialRoute } from './route.js';
4
4
  import { type HomeConnection } from './ui-home.js';
5
5
  /** Prompt-level agent service the renderer consumes (mirror of gaia-ui's `TuiAgentService`). */
@@ -38,7 +38,15 @@ export interface RunGaiaUiOptions {
38
38
  project?: string | undefined;
39
39
  agents?: TuiAgentService | undefined;
40
40
  currentUser?: string | undefined;
41
- ownConductorMachineId?: string | undefined;
41
+ /**
42
+ * The `.gaia` dir of the project the ui was started in, when there is one.
43
+ *
44
+ * Replaces the `ownConductorMachineId` the local registry used to supply
45
+ * (GAIA-232): the renderer matches this against each conductor's
46
+ * `workspace_root` to find which record is this machine's own — it holds the
47
+ * JSON:API client, and this entry does not.
48
+ */
49
+ gaiaDir?: string | undefined;
42
50
  createPrompt?: string | undefined;
43
51
  buildProgram: (opts: {
44
52
  plugins: unknown[];
@@ -62,8 +70,6 @@ export interface BuildWriteClientOptions {
62
70
  export interface UiDeps {
63
71
  /** Machine context (identity + connection); default: read from disk. */
64
72
  machine?: Partial<MachineContext>;
65
- /** Conductor registry entries; default: `listRegisteredConductors()`. */
66
- registryEntries?: ConductorRegistryEntry[];
67
73
  /** herdr-backed agent host; default: `herdrAgentHost(exec herdr)`. */
68
74
  agentHost?: AgentLaunchHost;
69
75
  /** The renderer launcher; default: the dynamically-resolved `runGaiaUi`. */
@@ -83,6 +89,8 @@ export interface UiDeps {
83
89
  printConfig?: boolean;
84
90
  /** Sink for `--print-config`; default: `process.stdout.write`. */
85
91
  stdout?: (s: string) => void;
92
+ /** Daily update lookup; injectable for startup ordering tests. */
93
+ updateCheck?: () => Promise<string | null>;
86
94
  /** Positional `[target]` (`GAIA-221`, `ticket:…`, `project:…`, `run:…`). */
87
95
  target?: string | undefined;
88
96
  /** `--ticket <id>`. */
package/dist/src/ui.js CHANGED
@@ -2,7 +2,7 @@ import { createRequire } from 'node:module';
2
2
  import { homedir } from 'node:os';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { herdrAgentHost } from '@gaia-ai/addon-herdr';
5
- import { CommandRunner, createLogger, exec, listRegisteredConductors, loadGaiaConfig, machineContextPath, readMachineContext, setDefaultCommandRunner, } from '@gaia-ai/core';
5
+ import { CommandRunner, createLogger, exec, fetchUpdateNotice, loadGaiaConfig, machineContextPath, readMachineContext, resolveCliVersion, setDefaultCommandRunner, } from '@gaia-ai/core';
6
6
  import { buildProgram as buildDropshProgram, resolveAuth } from 'dropsh';
7
7
  import { createHttpClient, createJsonApiClient } from 'dropsh/plugin';
8
8
  import { parseTarget, resolveHere, UI_VIEW_MODES, } from './route.js';
@@ -57,11 +57,13 @@ async function readCurrentBranch(cwd) {
57
57
  * with no `origin`, it yields '' and the ladder produces its named error.
58
58
  *
59
59
  * Revision 4 replaced the previous rung (an `import()` of the repo's engine
60
- * `conductor.config.js` plus a conductor-registry longest-prefix fallback) with
61
- * this one read: the registry cannot answer inside a worktree (its paths point
62
- * at `.gaia` DIRS, and a worktree has its own unregistered one), the config read
63
- * needed a dynamic import of a possibly secret-bearing module, and the remote
64
- * works in a fresh clone that has no `.gaia/` at all.
60
+ * `conductor.config.js` plus a longest-prefix fallback over the then-still-alive
61
+ * local conductor registry) with this one read: neither could answer inside a
62
+ * worktree (both keyed on `.gaia` DIRS, and a worktree has its own), the config
63
+ * read needed a dynamic import of a possibly secret-bearing module, and the
64
+ * remote works in a fresh clone that has no `.gaia/` at all. Revision 5 then
65
+ * deleted that registry outright, which only removes an alternative this rung
66
+ * had already rejected.
65
67
  */
66
68
  async function readOriginRemoteUrl(cwd) {
67
69
  try {
@@ -90,8 +92,19 @@ function resolveExplicitConfig(flag) {
90
92
  }
91
93
  return undefined;
92
94
  }
93
- /** The `--print-config` block (AC-7): which file won, and what it points at. */
94
- function formatConnectionReport(connection, rooting) {
95
+ /**
96
+ * The `--print-config` block (AC-7): which file won, and what it points at.
97
+ *
98
+ * `project` reports the `--project` flag, and `gaia_dir` the project dir the
99
+ * rooting resolved. GAIA-232 split the two on purpose: the DEFAULT project is no
100
+ * longer knowable here. It used to be read out of `~/.gaia/conductors.json`,
101
+ * which this ticket deleted, and its replacement is the conductor record whose
102
+ * `workspace_root` equals `gaia_dir` — a control-plane read, and this command
103
+ * holds no JSON:API client (GAIA-223 spec Finding 1). Printing `gaia_dir` is
104
+ * therefore the honest form: it names the exact input the renderer resolves the
105
+ * project from, instead of a `project:` line that could only ever say `(none)`.
106
+ */
107
+ function formatConnectionReport(connection, rooting, project) {
95
108
  const yesNo = (b) => (b ? 'yes' : 'no');
96
109
  const lines = [
97
110
  ['connection', connection.config_path],
@@ -100,7 +113,11 @@ function formatConnectionReport(connection, rooting) {
100
113
  ['legacy', yesNo(connection.legacy)],
101
114
  ['base_url', connection.site.base_url],
102
115
  ['cwd', rooting.cwd],
103
- ['project', rooting.project ?? '(none)'],
116
+ ['gaia_dir', rooting.gaiaDir ?? '(none)'],
117
+ [
118
+ 'project',
119
+ project ?? (rooting.gaiaDir ? '(resolved from gaia_dir)' : '(none)'),
120
+ ],
104
121
  ];
105
122
  return `${lines.map(([k, v]) => `${`${k}:`.padEnd(12)}${v}`).join('\n')}\n`;
106
123
  }
@@ -186,9 +203,6 @@ export async function cmdUi(deps = {}) {
186
203
  fail(err instanceof Error ? err.message : String(err));
187
204
  return;
188
205
  }
189
- // Read only AFTER the route: the whole grammar + ladder is offline, so a
190
- // parse/conflict/no-remote failure must not even touch the registry file.
191
- const entries = deps.registryEntries ?? (await listRegisteredConductors());
192
206
  const mcPath = machineContextPath();
193
207
  // The machine context still supplies identity (`user_id`) + the agent binary;
194
208
  // it is NOT the connection any more — core resolves that.
@@ -209,6 +223,14 @@ export async function cmdUi(deps = {}) {
209
223
  process.exitCode = 1;
210
224
  return;
211
225
  }
226
+ const notice = deps.updateCheck
227
+ ? await deps.updateCheck()
228
+ : deps.runUi
229
+ ? null
230
+ : await fetchUpdateNotice(resolveCliVersion());
231
+ if (notice !== null) {
232
+ (deps.stdout ?? ((line) => process.stdout.write(line)))(`${notice}\n`);
233
+ }
212
234
  const connection = {
213
235
  baseUrl: resolved.site.base_url,
214
236
  jsonapiPrefix: resolved.site.jsonapi_prefix,
@@ -242,9 +264,8 @@ export async function cmdUi(deps = {}) {
242
264
  `(${err instanceof Error ? err.message : String(err)}); ` +
243
265
  `edits will be attempted as '${connection.authProfile}'.`);
244
266
  }
245
- const rooting = resolveProjectRooting(cwd, entries, homedir());
246
- const ownConductorMachineId = rooting.ownConductorMachineId;
247
- const project = rooting.project;
267
+ const rooting = resolveProjectRooting(cwd, homedir());
268
+ const project = deps.project;
248
269
  // AC-7, always on: the resolution lands in the log even when the TUI takes
249
270
  // over the screen a moment later.
250
271
  logger.info({
@@ -254,13 +275,17 @@ export async function cmdUi(deps = {}) {
254
275
  legacy: resolved.legacy,
255
276
  base_url: resolved.site.base_url,
256
277
  cwd: rooting.cwd,
278
+ // The `.gaia` dir, not a project name: since GAIA-232 the default project
279
+ // is resolved in the renderer from the conductor record whose
280
+ // `workspace_root` is this dir. `project` here is the flag alone.
281
+ gaia_dir: rooting.gaiaDir,
257
282
  project,
258
283
  }, 'gaia ui: resolved connection');
259
284
  // AC-7, the human surface: print and exit 0 WITHOUT booting the TUI, so a
260
285
  // wrong-control-plane situation is diagnosable at a prompt.
261
286
  if (deps.printConfig) {
262
287
  const write = deps.stdout ?? ((s) => process.stdout.write(s));
263
- write(formatConnectionReport(resolved, rooting));
288
+ write(formatConnectionReport(resolved, rooting, project));
264
289
  return;
265
290
  }
266
291
  const agentCommand = resolveAgentCommand(machine);
@@ -315,7 +340,7 @@ export async function cmdUi(deps = {}) {
315
340
  ...(project !== undefined ? { project } : {}),
316
341
  agents,
317
342
  ...(machine.user_id ? { currentUser: machine.user_id } : {}),
318
- ...(ownConductorMachineId ? { ownConductorMachineId } : {}),
343
+ ...(rooting.gaiaDir ? { gaiaDir: rooting.gaiaDir } : {}),
319
344
  // The plugins are real constructed `DropSHPlugin[]` now (core built them),
320
345
  // matching dropsh's declared contract — F3's substance, a descriptor handed
321
346
  // to an API expecting instances, is gone. The two casts that remain are
@@ -343,11 +368,11 @@ export async function cmdUi(deps = {}) {
343
368
  const uiCommandPlugin = {
344
369
  kind: 'command',
345
370
  name: 'ui',
346
- describe: 'interactive terminal UI: project dashboard, ticket list, ticket detail',
371
+ describe: 'interactive terminal UI: dashboard, projects, tickets',
347
372
  register(program, host) {
348
373
  program
349
374
  .command('ui')
350
- .description('interactive terminal UI: project dashboard, ticket list, ticket detail')
375
+ .description('interactive terminal UI: dashboard, projects, tickets')
351
376
  .argument('[target]', 'deep-link target: GAIA-221 · ticket:<id|uuid> · project:<name|uuid> · run:<uuid>')
352
377
  .option('--ticket <id>', 'open a ticket by identifier or uuid')
353
378
  .option('--project <id>', 'open a project by name or uuid')
@@ -358,7 +383,7 @@ const uiCommandPlugin = {
358
383
  .option('--print-config', 'print the resolved connection config and exit, without launching the UI')
359
384
  .addHelpText('after', `
360
385
  Targets (at most ONE of the positional, --ticket, --project, --run, --here):
361
- gaia ui the project dashboard
386
+ gaia ui the dashboard
362
387
  gaia ui GAIA-221 a ticket by identifier (case-insensitive)
363
388
  gaia ui ticket:GAIA-221 the same, said explicitly
364
389
  gaia ui ticket:<uuid> a ticket by uuid
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaia-ai/ui",
3
- "version": "0.6.5",
3
+ "version": "0.8.0",
4
4
  "description": "GAIA project-first cockpit: the `gaia ui` command plugin (renderer resolved dynamically).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,8 +26,8 @@
26
26
  "directory": "gaia-cli/ui"
27
27
  },
28
28
  "dependencies": {
29
- "@gaia-ai/core": "^0.6.5",
30
- "@gaia-ai/addon-herdr": "^0.6.5",
29
+ "@gaia-ai/core": "^0.8.0",
30
+ "@gaia-ai/addon-herdr": "^0.8.0",
31
31
  "commander": "^12.1.0",
32
32
  "dropsh": "^0.5.8"
33
33
  }