@tryglen/cli 0.1.2 → 0.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.
Files changed (71) hide show
  1. package/README.md +80 -0
  2. package/dist/_utils/agent-adapter.d.ts +19 -0
  3. package/dist/_utils/agent-adapter.js +50 -0
  4. package/dist/_utils/agent-adapter.js.map +1 -0
  5. package/dist/_utils/build-turn-body.d.ts +2 -1
  6. package/dist/_utils/build-turn-body.js +3 -0
  7. package/dist/_utils/build-turn-body.js.map +1 -1
  8. package/dist/_utils/config.d.ts +2 -0
  9. package/dist/_utils/config.js +2 -0
  10. package/dist/_utils/config.js.map +1 -1
  11. package/dist/_utils/git-context.d.ts +10 -0
  12. package/dist/_utils/git-context.js +32 -0
  13. package/dist/_utils/git-context.js.map +1 -0
  14. package/dist/_utils/glen-headers.d.ts +8 -0
  15. package/dist/_utils/glen-headers.js +8 -0
  16. package/dist/_utils/glen-headers.js.map +1 -0
  17. package/dist/_utils/http.d.ts +1 -0
  18. package/dist/_utils/http.js +1 -0
  19. package/dist/_utils/http.js.map +1 -1
  20. package/dist/_utils/idempotency-key.d.ts +1 -1
  21. package/dist/_utils/idempotency-key.js +6 -2
  22. package/dist/_utils/idempotency-key.js.map +1 -1
  23. package/dist/_utils/state-line.d.ts +3 -0
  24. package/dist/_utils/state-line.js +10 -0
  25. package/dist/_utils/state-line.js.map +1 -0
  26. package/dist/_utils/state.d.ts +13 -0
  27. package/dist/_utils/state.js +41 -0
  28. package/dist/_utils/state.js.map +1 -0
  29. package/dist/_utils/update.d.ts +11 -0
  30. package/dist/_utils/update.js +122 -0
  31. package/dist/_utils/update.js.map +1 -0
  32. package/dist/_utils/version.d.ts +2 -0
  33. package/dist/_utils/version.js +10 -0
  34. package/dist/_utils/version.js.map +1 -0
  35. package/dist/_utils/which.d.ts +6 -0
  36. package/dist/_utils/which.js +16 -0
  37. package/dist/_utils/which.js.map +1 -0
  38. package/dist/bin.js +105 -9
  39. package/dist/bin.js.map +1 -1
  40. package/dist/commands/doctor.d.ts +13 -0
  41. package/dist/commands/doctor.js +91 -0
  42. package/dist/commands/doctor.js.map +1 -0
  43. package/dist/commands/incognito.d.ts +3 -0
  44. package/dist/commands/incognito.js +19 -0
  45. package/dist/commands/incognito.js.map +1 -0
  46. package/dist/commands/ingest.d.ts +1 -1
  47. package/dist/commands/ingest.js +59 -9
  48. package/dist/commands/ingest.js.map +1 -1
  49. package/dist/commands/install.js +113 -26
  50. package/dist/commands/install.js.map +1 -1
  51. package/dist/commands/login.js +26 -1
  52. package/dist/commands/login.js.map +1 -1
  53. package/dist/commands/logout.js +2 -0
  54. package/dist/commands/logout.js.map +1 -1
  55. package/dist/commands/org.d.ts +4 -0
  56. package/dist/commands/org.js +80 -0
  57. package/dist/commands/org.js.map +1 -0
  58. package/dist/commands/search.js +29 -5
  59. package/dist/commands/search.js.map +1 -1
  60. package/dist/commands/session-start.d.ts +19 -0
  61. package/dist/commands/session-start.js +57 -0
  62. package/dist/commands/session-start.js.map +1 -0
  63. package/dist/commands/status.js +24 -5
  64. package/dist/commands/status.js.map +1 -1
  65. package/dist/commands/statusline.d.ts +13 -0
  66. package/dist/commands/statusline.js +38 -0
  67. package/dist/commands/statusline.js.map +1 -0
  68. package/dist/commands/update.d.ts +7 -0
  69. package/dist/commands/update.js +35 -0
  70. package/dist/commands/update.js.map +1 -0
  71. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -1,10 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
2
  import { cac } from "cac";
6
- const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
7
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
3
+ import { cliVersion } from "./_utils/version.js";
8
4
  const cli = cac("glen");
9
5
  cli.command("login", "Connect glen via browser sign-in").action(async () => {
10
6
  const { login } = await import("./commands/login.js");
@@ -40,14 +36,49 @@ cli
40
36
  process.exit(result.error.code);
41
37
  }
42
38
  });
39
+ cli
40
+ .command("incognito <state>", "on|off — while on, glen records nothing (recall still works)")
41
+ .action(async (state) => {
42
+ const { incognito } = await import("./commands/incognito.js");
43
+ const r = await incognito(state);
44
+ if (!r.ok) {
45
+ process.stderr.write(`${r.error}\n`);
46
+ process.exit(1);
47
+ }
48
+ });
43
49
  cli
44
50
  .command("ingest", "Ingest a turn from stdin (used by agent hooks)")
45
- .action(async () => {
51
+ .option("--agent <agent>", "Agent type: claude-code | codex")
52
+ .action(async (options) => {
46
53
  const { ingest } = await import("./commands/ingest.js");
47
- const result = await ingest();
54
+ const result = await ingest(options.agent);
48
55
  if (!result.ok)
49
56
  process.exit(result.error.code);
50
57
  });
58
+ cli
59
+ .command("org <action> [slug]", "Manage organizations: list | switch [slug]")
60
+ .action(async (action, slug) => {
61
+ if (action === "list") {
62
+ const { orgList } = await import("./commands/org.js");
63
+ const r = await orgList();
64
+ if (!r.ok) {
65
+ process.stderr.write(`${r.error}\n`);
66
+ process.exit(1);
67
+ }
68
+ return;
69
+ }
70
+ if (action === "switch") {
71
+ const { orgSwitch } = await import("./commands/org.js");
72
+ const r = await orgSwitch(slug);
73
+ if (!r.ok) {
74
+ process.stderr.write(`${r.error}\n`);
75
+ process.exit(1);
76
+ }
77
+ return;
78
+ }
79
+ process.stderr.write(`Unknown subcommand "${action}". Use: glen org list | glen org switch [slug]\n`);
80
+ process.exit(1);
81
+ });
51
82
  cli
52
83
  .command("install", "Detect coding agents and install glen plugins")
53
84
  .action(async () => {
@@ -57,9 +88,74 @@ cli
57
88
  process.exit(1);
58
89
  });
59
90
  cli.command("version", "Print the CLI version").action(() => {
60
- process.stdout.write(`${pkg.version}\n`);
91
+ process.stdout.write(`${cliVersion}\n`);
92
+ });
93
+ cli
94
+ .command("update", "Update the glen CLI to the latest version")
95
+ .action(async () => {
96
+ const { update } = await import("./commands/update.js");
97
+ const result = await update();
98
+ if (!result.ok) {
99
+ process.stderr.write(`${result.error}\n`);
100
+ process.exit(1);
101
+ }
102
+ });
103
+ cli
104
+ .command("doctor", "Show version, update status, session state, and plugin hints")
105
+ .option("--auto", "Quiet mode: run background update + plugin upgrades (used by hooks)")
106
+ .action(async (options) => {
107
+ const { doctor } = await import("./commands/doctor.js");
108
+ const result = await doctor(options.auto === true);
109
+ if (!result.ok) {
110
+ process.stderr.write(`${result.error}\n`);
111
+ process.exit(1);
112
+ }
113
+ });
114
+ cli
115
+ .command("statusline", "Output a single status line for the agent's UI (used by hooks)")
116
+ .action(async () => {
117
+ try {
118
+ const { statusline } = await import("./commands/statusline.js");
119
+ await statusline();
120
+ }
121
+ catch {
122
+ // Statusline must never break the host UI — output the safe fallback.
123
+ process.stdout.write("🏔️ glen — run `glen org switch`");
124
+ }
125
+ process.exit(0);
126
+ });
127
+ cli
128
+ .command("session-start", "Output hook JSON for the agent's SessionStart event (used by hooks)")
129
+ .option("--agent <agent>", "Agent type (e.g. claude-code) — accepted for uniform hook lines, currently unused")
130
+ .action(async () => {
131
+ try {
132
+ const { sessionStart } = await import("./commands/session-start.js");
133
+ await sessionStart();
134
+ }
135
+ catch {
136
+ // Hook must never block a session — output a safe no-op JSON response.
137
+ process.stdout.write(JSON.stringify({
138
+ hookSpecificOutput: {
139
+ hookEventName: "SessionStart",
140
+ additionalContext: "[glen] not connected — run `glen login`",
141
+ },
142
+ }));
143
+ }
144
+ process.exit(0);
145
+ });
146
+ // Internal: spawned detached by maybeSpawnBackgroundUpdate to run npm install
147
+ // without blocking the calling turn.
148
+ cli.command("__self-update", "(internal)").action(async () => {
149
+ const { runSelfUpdate } = await import("./_utils/update.js");
150
+ runSelfUpdate();
151
+ });
152
+ // Unknown commands must fail loudly — cac's default is a silent exit 0, which
153
+ // reads as success (e.g. the removed `glen private` would silently no-op).
154
+ cli.on("command:*", () => {
155
+ process.stderr.write(`glen: unknown command "${cli.args[0]}". Run \`glen --help\` for usage.\n`);
156
+ process.exit(1);
61
157
  });
62
158
  cli.help();
63
- cli.version(pkg.version);
159
+ cli.version(cliVersion);
64
160
  cli.parse();
65
161
  //# sourceMappingURL=bin.js.map
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { type Result } from "../_utils/result.js";
2
+ /**
3
+ * `glen doctor [--auto]`
4
+ *
5
+ * Default mode: prints version info, latest, install channel, session state
6
+ * summary, and plugin staleness hints.
7
+ *
8
+ * `--auto` (quiet) mode: runs maybeSpawnBackgroundUpdate and best-effort
9
+ * detached spawns for plugin update commands. No stdout output. Used by the
10
+ * session-start hook so it never blocks a turn.
11
+ */
12
+ declare const doctor: (auto?: boolean) => Promise<Result<void, string>>;
13
+ export { doctor };
@@ -0,0 +1,91 @@
1
+ import { spawn } from "node:child_process";
2
+ import { ok } from "../_utils/result.js";
3
+ import { readState } from "../_utils/state.js";
4
+ import { fetchLatest, isNpmGlobalInstall, maybeSpawnBackgroundUpdate, } from "../_utils/update.js";
5
+ import { cliVersion } from "../_utils/version.js";
6
+ import { isInstalled } from "../_utils/which.js";
7
+ /**
8
+ * `glen doctor [--auto]`
9
+ *
10
+ * Default mode: prints version info, latest, install channel, session state
11
+ * summary, and plugin staleness hints.
12
+ *
13
+ * `--auto` (quiet) mode: runs maybeSpawnBackgroundUpdate and best-effort
14
+ * detached spawns for plugin update commands. No stdout output. Used by the
15
+ * session-start hook so it never blocks a turn.
16
+ */
17
+ const doctor = async (auto = false) => {
18
+ if (auto) {
19
+ // Quiet auto mode — best-effort, fire-and-forget.
20
+ await maybeSpawnBackgroundUpdate();
21
+ // Upgrade claude plugin if claude is installed
22
+ if (isInstalled("claude")) {
23
+ try {
24
+ const c = spawn("claude", ["plugin", "update", "glen@glen"], {
25
+ detached: true,
26
+ stdio: "ignore",
27
+ });
28
+ c.unref();
29
+ }
30
+ catch {
31
+ /* best-effort */
32
+ }
33
+ }
34
+ // Upgrade codex plugin if codex is installed
35
+ if (isInstalled("codex")) {
36
+ try {
37
+ const c = spawn("codex", ["plugin", "marketplace", "upgrade", "glen"], {
38
+ detached: true,
39
+ stdio: "ignore",
40
+ });
41
+ c.unref();
42
+ }
43
+ catch {
44
+ /* best-effort */
45
+ }
46
+ }
47
+ return ok(undefined);
48
+ }
49
+ // Normal diagnostic mode
50
+ process.stdout.write("glen doctor\n\n");
51
+ process.stdout.write(`Version: ${cliVersion}\n`);
52
+ const latest = await fetchLatest();
53
+ process.stdout.write(`Latest: ${latest ?? "(could not reach npm registry)"}\n`);
54
+ const channel = isNpmGlobalInstall()
55
+ ? "npm global"
56
+ : "other (local/pnpm/yarn/…)";
57
+ process.stdout.write(`Install channel: ${channel}\n`);
58
+ if (latest && latest !== cliVersion) {
59
+ process.stdout.write(`\nUpdate available: v${latest}.\n`);
60
+ if (isNpmGlobalInstall()) {
61
+ process.stdout.write(" Run `glen update` to install it.\n");
62
+ }
63
+ else {
64
+ process.stdout.write(" Update with your package manager (e.g. npm i -g @tryglen/cli@latest).\n");
65
+ }
66
+ }
67
+ const state = await readState();
68
+ process.stdout.write("\nSession state:\n");
69
+ if (state) {
70
+ process.stdout.write(` org: ${state.activeOrgSlug ?? state.activeOrgId}\n`);
71
+ process.stdout.write(` incognito: ${state.incognito ? "ON" : "off"}\n`);
72
+ }
73
+ else {
74
+ process.stdout.write(" (no active session — run `glen login` and `glen org switch`)\n");
75
+ }
76
+ process.stdout.write("\nPlugin status:\n");
77
+ const claudeInstalled = isInstalled("claude");
78
+ const codexInstalled = isInstalled("codex");
79
+ if (claudeInstalled) {
80
+ process.stdout.write(" claude: detected — run `claude plugin update glen@glen` to update\n");
81
+ }
82
+ if (codexInstalled) {
83
+ process.stdout.write(" codex: detected — run `codex plugin marketplace upgrade glen` to update\n");
84
+ }
85
+ if (!claudeInstalled && !codexInstalled) {
86
+ process.stdout.write(" No coding agents detected. Run `glen install` after installing Claude Code or Codex.\n");
87
+ }
88
+ return ok(undefined);
89
+ };
90
+ export { doctor };
91
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type Result } from "../_utils/result.js";
2
+ declare const incognito: (stateArg: string) => Promise<Result<void, string>>;
3
+ export { incognito };
@@ -0,0 +1,19 @@
1
+ import { err, ok } from "../_utils/result.js";
2
+ import { readState, writeState } from "../_utils/state.js";
3
+ const incognito = async (stateArg) => {
4
+ if (stateArg !== "on" && stateArg !== "off")
5
+ return err("Usage: glen incognito <on|off>");
6
+ const current = await readState();
7
+ if (!current)
8
+ return err("No active organization. Run `glen org switch` (or `glen login`).");
9
+ const next = { ...current, incognito: stateArg === "on" };
10
+ const w = await writeState(next);
11
+ if (!w.ok)
12
+ return err("Failed to write state file.");
13
+ process.stdout.write(next.incognito
14
+ ? "🏔️ Incognito ON — glen records nothing until you run `glen incognito off`.\n"
15
+ : "🏔️ Incognito off — glen is recording again.\n");
16
+ return ok(undefined);
17
+ };
18
+ export { incognito };
19
+ //# sourceMappingURL=incognito.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incognito.js","sourceRoot":"","sources":["../../src/commands/incognito.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,GAAG,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,SAAS,GAAG,KAAK,EAAE,QAAgB,EAAiC,EAAE;IAC1E,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,KAAK;QACzC,OAAO,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,MAAM,SAAS,EAAE,CAAC;IAClC,IAAI,CAAC,OAAO;QACV,OAAO,GAAG,CACR,kEAAkE,CACnE,CAAC;IACJ,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,QAAQ,KAAK,IAAI,EAAE,CAAC;IAC1D,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,CAAC,CAAC,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,6BAA6B,CAAC,CAAC;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,IAAI,CAAC,SAAS;QACZ,CAAC,CAAC,+EAA+E;QACjF,CAAC,CAAC,gDAAgD,CACrD,CAAC;IACF,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;AACvB,CAAC,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -3,5 +3,5 @@ type IngestError = {
3
3
  readonly code: number;
4
4
  readonly message: string;
5
5
  };
6
- declare const ingest: () => Promise<Result<void, IngestError>>;
6
+ declare const ingest: (agent?: string) => Promise<Result<void, IngestError>>;
7
7
  export { ingest };
@@ -1,11 +1,18 @@
1
1
  import { readFile } from "node:fs/promises";
2
+ import { adaptAgentEvent } from "../_utils/agent-adapter.js";
2
3
  import { buildTurnBody } from "../_utils/build-turn-body.js";
3
4
  import { config } from "../_utils/config.js";
4
- import { resolveAuthHeader } from "../_utils/credentials.js";
5
+ import { readCredentials } from "../_utils/credentials.js";
5
6
  import { formatMemory } from "../_utils/format-memory.js";
7
+ import { gitContext } from "../_utils/git-context.js";
8
+ import { buildGlenHeaders } from "../_utils/glen-headers.js";
6
9
  import { httpJson } from "../_utils/http.js";
7
10
  import { parseTranscript } from "../_utils/parse-transcript.js";
8
11
  import { err, fromPromise, ok } from "../_utils/result.js";
12
+ import { formatStateLine } from "../_utils/state-line.js";
13
+ import { readState } from "../_utils/state.js";
14
+ import { handle426AndReexec, maybeSpawnBackgroundUpdate, } from "../_utils/update.js";
15
+ import { cliVersion } from "../_utils/version.js";
9
16
  const readStdin = async () => {
10
17
  const chunks = [];
11
18
  for await (const chunk of process.stdin)
@@ -18,7 +25,8 @@ const readTranscriptLines = async (path) => {
18
25
  return [];
19
26
  return r.value.split("\n").filter(Boolean);
20
27
  };
21
- const ingest = async () => {
28
+ const ingest = async (agent) => {
29
+ void maybeSpawnBackgroundUpdate();
22
30
  const stdinRaw = await readStdin();
23
31
  const event = (() => {
24
32
  try {
@@ -30,31 +38,71 @@ const ingest = async () => {
30
38
  })();
31
39
  if (!event?.prompt || !event?.session_id)
32
40
  return ok(undefined);
33
- const header = await resolveAuthHeader();
34
- if (!header)
41
+ const creds = await readCredentials();
42
+ if (!creds)
35
43
  return err({ code: 1, message: "not connected" });
44
+ const state = await readState();
36
45
  const transcriptPath = typeof event.transcript_path === "string"
37
46
  ? event.transcript_path
38
47
  : undefined;
39
- const { priorAssistant, nextUserMessageIndex } = parseTranscript(transcriptPath ? await readTranscriptLines(transcriptPath) : []);
48
+ const transcriptLines = transcriptPath
49
+ ? await readTranscriptLines(transcriptPath)
50
+ : [];
51
+ // Normalise the agent name — only known values are forwarded.
52
+ const agentName = agent === "claude-code" || agent === "codex" ? agent : undefined;
53
+ // Resolve the cwd from the event (hook provides it) or fall back.
54
+ const eventCwd = typeof event.cwd === "string" ? event.cwd : process.cwd();
55
+ // Adapt the event based on agent type.
56
+ const adapted = adaptAgentEvent(agentName, event, transcriptLines);
57
+ // For codex without a transcript, derive idempotency from event.turn_id when
58
+ // available; otherwise fall back to parseTranscript's nextUserMessageIndex.
59
+ const nextUserMessageIndex = agentName === "codex" && !adapted.usesTranscript
60
+ ? typeof event.turn_id === "string" && event.turn_id
61
+ ? event.turn_id
62
+ : parseTranscript(transcriptLines).nextUserMessageIndex
63
+ : parseTranscript(transcriptLines).nextUserMessageIndex;
64
+ // Assemble additionalContext. Apply the empty-context rule: send undefined
65
+ // when the object would be completely empty ({}).
66
+ const workspaceCtx = gitContext(eventCwd);
67
+ const hasSessionFields = Object.keys(adapted.sessionFields).length > 0;
68
+ const additionalContext = (() => {
69
+ const ctx = {};
70
+ if (agentName)
71
+ ctx.agent = { name: agentName };
72
+ // workspace: always include if we have at least cwd
73
+ if (Object.keys(workspaceCtx).length > 0)
74
+ ctx.workspace = workspaceCtx;
75
+ if (hasSessionFields)
76
+ ctx.session = adapted.sessionFields;
77
+ return Object.keys(ctx).length > 0 ? ctx : undefined;
78
+ })();
40
79
  const body = buildTurnBody({
41
80
  sessionId: event.session_id,
42
81
  prompt: event.prompt,
43
- priorAssistant,
82
+ priorAssistant: adapted.priorAssistant,
44
83
  nextUserMessageIndex,
84
+ ...(additionalContext !== undefined ? { additionalContext } : {}),
45
85
  });
46
86
  const res = await httpJson(config.turnUrl, {
47
87
  method: "POST",
48
88
  headers: {
49
89
  "content-type": "application/json",
50
- [header.headerName]: header.headerValue,
90
+ ...buildGlenHeaders({ key: creds.key, state, version: cliVersion }),
51
91
  },
52
92
  body: JSON.stringify(body),
53
93
  timeoutMs: config.ingestTimeoutMs,
54
94
  });
55
95
  if (!res.ok)
56
96
  return err({ code: 3, message: "ingest failed (network error)" });
97
+ if (res.value.status === 426)
98
+ handle426AndReexec(process.argv.slice(2));
57
99
  if (res.value.status === 401 || res.value.status === 403) {
100
+ if (res.value.rawText.includes("no active organization")) {
101
+ // Server reports no org bound — hint the user and fail open so the hook
102
+ // never blocks the turn.
103
+ process.stderr.write("[glen] no active organization — run `glen org switch`\n");
104
+ return ok(undefined);
105
+ }
58
106
  // Don't clear credentials here — a concurrent `glen login` may have just
59
107
  // saved a fresh key. Return code 2 so the hook can trigger re-auth.
60
108
  return err({ code: 2, message: "key rejected" });
@@ -64,9 +112,11 @@ const ingest = async () => {
64
112
  process.stderr.write(`glen: ingest failed (HTTP ${res.value.status}). Memory was not recorded this turn.\n`);
65
113
  return ok(undefined);
66
114
  }
115
+ const stateLine = formatStateLine(state);
67
116
  const memory = formatMemory(res.value.json);
68
- if (memory)
69
- process.stdout.write(memory);
117
+ const out = [stateLine, memory].filter(Boolean).join("\n\n");
118
+ if (out)
119
+ process.stdout.write(out);
70
120
  return ok(undefined);
71
121
  };
72
122
  export { ingest };
@@ -1 +1 @@
1
- {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/commands/ingest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAe,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAC;AAIxE,MAAM,SAAS,GAAG,KAAK,IAAqB,EAAE;IAC5C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK;QAAE,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;IACtE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,KAAK,EAC/B,IAAY,EACgB,EAAE;IAC9B,MAAM,CAAC,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC,CAAC,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACrB,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,KAAK,IAAwC,EAAE;IAC5D,MAAM,QAAQ,GAAG,MAAM,SAAS,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAA4B,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IACL,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,KAAK,EAAE,UAAU;QAAE,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IAE/D,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;IACzC,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;IAE/D,MAAM,cAAc,GAClB,OAAO,KAAK,CAAC,eAAe,KAAK,QAAQ;QACvC,CAAC,CAAC,KAAK,CAAC,eAAe;QACvB,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,EAAE,cAAc,EAAE,oBAAoB,EAAE,GAAG,eAAe,CAC9D,cAAc,CAAC,CAAC,CAAC,MAAM,mBAAmB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAChE,CAAC;IACF,MAAM,IAAI,GAAG,aAAa,CAAC;QACzB,SAAS,EAAE,KAAK,CAAC,UAAoB;QACrC,MAAM,EAAE,KAAK,CAAC,MAAgB;QAC9B,cAAc;QACd,oBAAoB;KACrB,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE;QACzC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,WAAW;SACxC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC,eAAe;KAClC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE;QACT,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC,CAAC;IAEpE,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACzD,yEAAyE;QACzE,qEAAqE;QACrE,OAAO,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,mFAAmF;QACnF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,GAAG,CAAC,KAAK,CAAC,MAAM,yCAAyC,CACvF,CAAC;QACF,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,MAAM;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEzC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;AACvB,CAAC,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/commands/ingest.ts"],"names":[],"mappings":"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"}
@@ -1,32 +1,116 @@
1
1
  import { execFileSync } from "node:child_process";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
2
5
  import prompts from "prompts";
3
- import { err, ok } from "../_utils/result.js";
6
+ import { err, fromPromise, ok } from "../_utils/result.js";
7
+ import { isInstalled } from "../_utils/which.js";
8
+ // ---------------------------------------------------------------------------
9
+ // Settings path (GLEN_CLAUDE_SETTINGS_PATH env var is for test overrides only)
10
+ // ---------------------------------------------------------------------------
11
+ const claudeSettingsPath = () => process.env.GLEN_CLAUDE_SETTINGS_PATH ??
12
+ join(homedir(), ".claude", "settings.json");
4
13
  const AGENTS = [
5
14
  {
6
15
  name: "Claude Code",
7
16
  bin: "claude",
8
- installArgs: ["plugin", "install", "tryglen/glen-claude-code-plugin"],
17
+ steps: [
18
+ ["plugin", "marketplace", "add", "Glen-Web-App/glen-claude-code-plugin"],
19
+ ["plugin", "install", "glen@glen"],
20
+ ],
21
+ postInstall: "claude",
9
22
  },
10
23
  {
11
24
  name: "Codex",
12
25
  bin: "codex",
13
- installArgs: ["plugin", "install", "tryglen/glen-codex-plugin"],
26
+ steps: [
27
+ ["plugin", "marketplace", "add", "Glen-Web-App/glen-codex-plugin"],
28
+ ["plugin", "add", "glen"],
29
+ ],
30
+ postInstall: "codex",
14
31
  },
15
32
  ];
16
- const isInstalled = (bin) => {
17
- const r = (() => {
18
- try {
19
- execFileSync("which", [bin], { stdio: "ignore" });
20
- return true;
33
+ // ---------------------------------------------------------------------------
34
+ // Step label: last two args make a readable name ("marketplace add", "install glen@glen")
35
+ // ---------------------------------------------------------------------------
36
+ const stepLabel = (step) => step.slice(1).join(" ");
37
+ // ---------------------------------------------------------------------------
38
+ // Statusline opt-in for Claude Code
39
+ // ---------------------------------------------------------------------------
40
+ const handleClaudeStatusline = async () => {
41
+ const response = await prompts({
42
+ type: "confirm",
43
+ name: "statusline",
44
+ message: "Show glen in your Claude Code status line?",
45
+ initial: true,
46
+ });
47
+ if (!response.statusline)
48
+ return;
49
+ const filePath = claudeSettingsPath();
50
+ // Read the existing settings file; missing → treat as {}
51
+ const rawResult = await fromPromise(readFile(filePath, "utf-8"));
52
+ let existingSettings = {};
53
+ let parseFailed = false;
54
+ if (rawResult.ok) {
55
+ const parseResult = await fromPromise(new Promise((resolve) => resolve(JSON.parse(rawResult.value))));
56
+ if (parseResult.ok &&
57
+ parseResult.value &&
58
+ typeof parseResult.value === "object") {
59
+ existingSettings = parseResult.value;
21
60
  }
22
- catch {
23
- return false;
61
+ else {
62
+ // Parse failure — treat as existing-with-statusline (don't touch)
63
+ parseFailed = true;
24
64
  }
25
- })();
26
- return r;
65
+ }
66
+ // If statusLine already exists, print snippet + merge note; do NOT touch
67
+ if (parseFailed || "statusLine" in existingSettings) {
68
+ process.stdout.write(" ℹ statusLine already configured. To add glen, merge this into your existing command:\n");
69
+ process.stdout.write(` "statusLine": { "type": "command", "command": "glen statusline" }\n`);
70
+ process.stdout.write(` (manual merge required — glen won't overwrite your existing statusLine)\n`);
71
+ return;
72
+ }
73
+ // Write back with statusLine key added — atomic tmp+rename
74
+ const updated = {
75
+ ...existingSettings,
76
+ statusLine: { type: "command", command: "glen statusline" },
77
+ };
78
+ const mk = await fromPromise(mkdir(dirname(filePath), { recursive: true }));
79
+ if (!mk.ok) {
80
+ process.stdout.write(" ✗ Could not create .claude directory.\n");
81
+ return;
82
+ }
83
+ const tmp = `${filePath}.tmp`;
84
+ const w = await fromPromise(writeFile(tmp, JSON.stringify(updated, null, 2)));
85
+ if (!w.ok) {
86
+ process.stdout.write(" ✗ Could not write settings file.\n");
87
+ return;
88
+ }
89
+ const mv = await fromPromise(rename(tmp, filePath));
90
+ if (!mv.ok) {
91
+ process.stdout.write(" ✗ Could not finalize settings file.\n");
92
+ return;
93
+ }
94
+ process.stdout.write(" ✓ statusline configured\n");
27
95
  };
96
+ // ---------------------------------------------------------------------------
97
+ // Post-install actions per agent
98
+ // ---------------------------------------------------------------------------
99
+ const handlePostInstall = async (agent) => {
100
+ if (agent.postInstall === "codex") {
101
+ process.stdout.write("\n ⚠ One-time step: open Codex, run /hooks, and trust the glen hooks\n" +
102
+ " (Codex skips untrusted hooks silently — glen won't work until you do).\n");
103
+ }
104
+ else if (agent.postInstall === "claude") {
105
+ await handleClaudeStatusline();
106
+ process.stdout.write("\n Tip: enable auto-update for the glen marketplace in /plugin → Marketplaces.\n");
107
+ }
108
+ };
109
+ // ---------------------------------------------------------------------------
110
+ // Main install command
111
+ // ---------------------------------------------------------------------------
28
112
  const install = async () => {
29
- process.stdout.write("\n 🌿 glen — install agent plugins\n\n");
113
+ process.stdout.write("\n 🏔️ glen — install agent plugins\n\n");
30
114
  const detected = AGENTS.filter((a) => isInstalled(a.bin));
31
115
  if (detected.length === 0) {
32
116
  process.stdout.write(" ⚠ Could not find Claude Code or Codex installed.\n");
@@ -56,19 +140,22 @@ const install = async () => {
56
140
  return ok(undefined);
57
141
  }
58
142
  for (const agent of selected) {
59
- process.stdout.write(` Installing ${agent.name} plugin... `);
60
- const installResult = (() => {
61
- try {
62
- execFileSync(agent.bin, agent.installArgs, {
63
- stdio: "ignore",
64
- });
65
- return true;
66
- }
67
- catch {
68
- return false;
69
- }
70
- })();
71
- process.stdout.write(installResult ? "✓\n" : "✗ (failed)\n");
143
+ process.stdout.write(`\n Installing ${agent.name} plugin:\n`);
144
+ for (const step of agent.steps) {
145
+ const label = stepLabel(step);
146
+ process.stdout.write(` ${label}... `);
147
+ const stepOk = (() => {
148
+ try {
149
+ execFileSync(agent.bin, step, { stdio: "ignore" });
150
+ return true;
151
+ }
152
+ catch {
153
+ return false;
154
+ }
155
+ })();
156
+ process.stdout.write(stepOk ? "✓\n" : "✗ (failed — continuing)\n");
157
+ }
158
+ await handlePostInstall(agent);
72
159
  }
73
160
  process.stdout.write("\n Done! Start a conversation in either agent — glen memory is active.\n");
74
161
  process.stdout.write(" Run `glen login` if you haven't connected yet.\n\n");