@gajae-code/coding-agent 0.1.3 → 0.2.1

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 (50) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/types/cli/skills-cli.d.ts +9 -0
  3. package/dist/types/commands/gjc-runtime-bridge.d.ts +24 -0
  4. package/dist/types/commands/skills.d.ts +26 -0
  5. package/dist/types/config/model-registry.d.ts +31 -2
  6. package/dist/types/config/models-config-schema.d.ts +39 -0
  7. package/dist/types/gjc-runtime/launch-tmux.d.ts +23 -0
  8. package/dist/types/gjc-runtime/team-runtime.d.ts +35 -1
  9. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +15 -10
  10. package/dist/types/hooks/skill-state.d.ts +4 -1
  11. package/dist/types/modes/components/model-selector.d.ts +21 -1
  12. package/dist/types/skill-state/active-state.d.ts +19 -0
  13. package/dist/types/skill-state/workflow-hud.d.ts +62 -0
  14. package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
  15. package/package.json +7 -7
  16. package/src/cli/args.ts +14 -0
  17. package/src/cli/skills-cli.ts +88 -0
  18. package/src/cli.ts +1 -0
  19. package/src/commands/deep-interview.ts +21 -2
  20. package/src/commands/gjc-runtime-bridge.ts +161 -15
  21. package/src/commands/ralplan.ts +21 -2
  22. package/src/commands/skills.ts +48 -0
  23. package/src/commands/team.ts +54 -3
  24. package/src/commands/ultragoal.ts +21 -1
  25. package/src/commit/agentic/index.ts +1 -0
  26. package/src/commit/pipeline.ts +1 -0
  27. package/src/config/model-registry.ts +259 -8
  28. package/src/config/models-config-schema.ts +18 -0
  29. package/src/defaults/gjc/skills/deep-interview/SKILL.md +6 -6
  30. package/src/defaults/gjc/skills/ralplan/SKILL.md +5 -9
  31. package/src/defaults/gjc/skills/team/SKILL.md +4 -4
  32. package/src/defaults/gjc/skills/ultragoal/SKILL.md +8 -8
  33. package/src/gjc-runtime/launch-tmux.ts +73 -2
  34. package/src/gjc-runtime/team-runtime.ts +285 -34
  35. package/src/gjc-runtime/ultragoal-guard.ts +43 -1
  36. package/src/gjc-runtime/ultragoal-runtime.ts +307 -187
  37. package/src/hooks/skill-state.ts +4 -1
  38. package/src/internal-urls/docs-index.generated.ts +1 -1
  39. package/src/main.ts +10 -1
  40. package/src/modes/components/model-selector.ts +109 -28
  41. package/src/modes/components/skill-hud/render.ts +35 -8
  42. package/src/modes/controllers/selector-controller.ts +42 -2
  43. package/src/prompts/system/system-prompt.md +5 -4
  44. package/src/sdk.ts +1 -0
  45. package/src/session/agent-session.ts +6 -0
  46. package/src/setup/provider-onboarding.ts +2 -0
  47. package/src/skill-state/active-state.ts +104 -4
  48. package/src/skill-state/workflow-hud.ts +160 -0
  49. package/src/slash-commands/acp-builtins.ts +11 -2
  50. package/src/slash-commands/builtin-registry.ts +16 -1
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Handles `gjc skills` for inspecting bundled workflow skill definitions.
3
+ */
4
+ import {
5
+ DEFAULT_GJC_DEFINITION_NAMES,
6
+ type EmbeddedDefaultGjcSkill,
7
+ getEmbeddedDefaultGjcSkills,
8
+ } from "../defaults/gjc-defaults";
9
+
10
+ export type SkillsAction = "list" | "read";
11
+
12
+ export interface SkillsCommandArgs {
13
+ action: SkillsAction;
14
+ name?: string;
15
+ flags?: {
16
+ json?: boolean;
17
+ };
18
+ }
19
+
20
+ interface SkillsListEntry {
21
+ name: string;
22
+ description: string;
23
+ path: string;
24
+ source: string;
25
+ }
26
+
27
+ interface SkillsReadEntry extends SkillsListEntry {
28
+ content: string;
29
+ }
30
+
31
+ function getEmbeddedSkill(name: string): EmbeddedDefaultGjcSkill | undefined {
32
+ return getEmbeddedDefaultGjcSkills().find(skill => skill.name === name);
33
+ }
34
+
35
+ function listEmbeddedSkills(): SkillsListEntry[] {
36
+ return getEmbeddedDefaultGjcSkills().map(skill => ({
37
+ name: skill.name,
38
+ description: skill.description,
39
+ path: skill.filePath,
40
+ source: skill.source,
41
+ }));
42
+ }
43
+
44
+ function writeJson(value: unknown): void {
45
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
46
+ }
47
+
48
+ export async function runSkillsCommand(cmd: SkillsCommandArgs): Promise<void> {
49
+ if (cmd.action === "list") {
50
+ const skills = listEmbeddedSkills();
51
+ if (cmd.flags?.json) {
52
+ writeJson({ skills });
53
+ return;
54
+ }
55
+ for (const skill of skills) {
56
+ process.stdout.write(`${skill.name}\t${skill.description}\t${skill.path}\n`);
57
+ }
58
+ return;
59
+ }
60
+
61
+ const name = cmd.name?.trim();
62
+ if (!name) {
63
+ process.stderr.write(`error: skill name is required for read (${DEFAULT_GJC_DEFINITION_NAMES.join(", ")})\n`);
64
+ process.exitCode = 1;
65
+ return;
66
+ }
67
+
68
+ const skill = getEmbeddedSkill(name);
69
+ if (!skill) {
70
+ process.stderr.write(`error: unknown embedded skill "${name}" (${DEFAULT_GJC_DEFINITION_NAMES.join(", ")})\n`);
71
+ process.exitCode = 1;
72
+ return;
73
+ }
74
+
75
+ const entry: SkillsReadEntry = {
76
+ name: skill.name,
77
+ description: skill.description,
78
+ path: skill.filePath,
79
+ source: skill.source,
80
+ content: skill.content,
81
+ };
82
+ if (cmd.flags?.json) {
83
+ writeJson(entry);
84
+ return;
85
+ }
86
+ process.stdout.write(skill.content);
87
+ if (!skill.content.endsWith("\n")) process.stdout.write("\n");
88
+ }
package/src/cli.ts CHANGED
@@ -34,6 +34,7 @@ const commands: CommandEntry[] = [
34
34
  { name: "question", load: () => import("./commands/question").then(m => m.default) },
35
35
  { name: "state", load: () => import("./commands/state").then(m => m.default) },
36
36
  { name: "setup", load: () => import("./commands/setup").then(m => m.default) },
37
+ { name: "skills", load: () => import("./commands/skills").then(m => m.default) },
37
38
  { name: "team", load: () => import("./commands/team").then(m => m.default) },
38
39
  { name: "ultragoal", load: () => import("./commands/ultragoal").then(m => m.default) },
39
40
  { name: "ralplan", load: () => import("./commands/ralplan").then(m => m.default) },
@@ -1,5 +1,6 @@
1
1
  import { Command } from "@gajae-code/utils/cli";
2
- import { runBridgedRuntimeEndpoint } from "./gjc-runtime-bridge";
2
+ import { syncSkillActiveState } from "../skill-state/active-state";
3
+ import { runGjcRuntimeBridgeWithHudSidecar } from "./gjc-runtime-bridge";
3
4
 
4
5
  export default class DeepInterview extends Command {
5
6
  static description = "Run private GJC deep-interview workflow commands";
@@ -7,6 +8,24 @@ export default class DeepInterview extends Command {
7
8
  static examples = ["$ gjc deep-interview --help"];
8
9
 
9
10
  async run(): Promise<void> {
10
- await runBridgedRuntimeEndpoint("deep-interview", this.argv);
11
+ const cwd = process.cwd();
12
+ const result = await runGjcRuntimeBridgeWithHudSidecar("deep-interview", this.argv, {
13
+ cwd,
14
+ sidecarSkill: "deep-interview",
15
+ onHudPayload: payload =>
16
+ syncSkillActiveState({
17
+ cwd,
18
+ skill: "deep-interview",
19
+ active: payload.active ?? true,
20
+ phase: payload.phase,
21
+ sessionId: payload.session_id,
22
+ threadId: payload.thread_id,
23
+ turnId: payload.turn_id,
24
+ hud: payload.hud,
25
+ source: "gjc-runtime-bridge",
26
+ }),
27
+ });
28
+ if (result.error) process.stderr.write(`${result.error}\n`);
29
+ process.exitCode = result.status;
11
30
  }
12
31
  }
@@ -1,15 +1,46 @@
1
- import { spawnSync } from "node:child_process";
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
2
3
  import { existsSync } from "node:fs";
4
+ import * as fs from "node:fs/promises";
5
+ import * as os from "node:os";
6
+ import * as path from "node:path";
7
+ import { normalizeWorkflowHudSummary, type WorkflowHudSummary } from "../skill-state/active-state";
3
8
 
4
9
  const BRIDGE_ENV = "GJC_RUNTIME_BINARY";
5
10
  const LEGACY_BRIDGE_ENV = "GJC_LEGACY_RUNTIME_BINARY";
6
11
  const GUARD_ENV = "GJC_RUNTIME_BRIDGE_ACTIVE";
12
+ export const WORKFLOW_HUD_PROTOCOL = "workflow-hud-summary-v1";
7
13
 
8
14
  export interface GjcRuntimeBridgeResult {
9
15
  status: number;
10
16
  error?: string;
11
17
  }
12
18
 
19
+ const SKILL_ENTRYPOINT_ENDPOINTS = new Set(["deep-interview", "ralplan"]);
20
+
21
+ export interface WorkflowHudBridgePayload {
22
+ version: 1;
23
+ skill: string;
24
+ phase?: string;
25
+ active?: boolean;
26
+ session_id?: string;
27
+ thread_id?: string;
28
+ turn_id?: string;
29
+ hud: WorkflowHudSummary;
30
+ }
31
+
32
+ export interface GjcRuntimeHudBridgeResult extends GjcRuntimeBridgeResult {
33
+ hudPayload?: WorkflowHudBridgePayload;
34
+ }
35
+
36
+ export interface GjcRuntimeHudBridgeOptions {
37
+ cwd?: string;
38
+ env?: NodeJS.ProcessEnv;
39
+ sidecarSkill: string;
40
+ onHudPayload?: (payload: WorkflowHudBridgePayload) => Promise<void> | void;
41
+ pollIntervalMs?: number;
42
+ }
43
+
13
44
  function candidateBinaries(env: NodeJS.ProcessEnv): string[] {
14
45
  return [env[BRIDGE_ENV], env[LEGACY_BRIDGE_ENV]].filter(
15
46
  (value): value is string => typeof value === "string" && value.trim().length > 0,
@@ -24,6 +55,61 @@ function canAttempt(command: string): boolean {
24
55
  return !isPathLike(command) || existsSync(command);
25
56
  }
26
57
 
58
+ function unavailableBridgeResult(
59
+ endpoint: string,
60
+ env: NodeJS.ProcessEnv,
61
+ attempted: string[],
62
+ ): GjcRuntimeBridgeResult {
63
+ const configured = [env[BRIDGE_ENV], env[LEGACY_BRIDGE_ENV]].filter(Boolean).join(", ");
64
+ const guidance = SKILL_ENTRYPOINT_ENDPOINTS.has(endpoint)
65
+ ? `Inside a GJC agent session, invoke /skill:${endpoint} instead so the bundled skill is loaded directly.`
66
+ : `Configure ${BRIDGE_ENV} with a GJC-compatible private runtime binary for the ${endpoint} endpoint.`;
67
+ return {
68
+ status: 1,
69
+ error: [
70
+ `gjc ${endpoint} is a private runtime bridge command.`,
71
+ guidance,
72
+ `Only private runtime deployments should call this bridge command; configure them with ${BRIDGE_ENV}.`,
73
+ configured
74
+ ? `Configured runtime candidates failed: ${configured}.`
75
+ : "No private GJC runtime binary was configured.",
76
+ attempted.length > 0 ? `Attempted: ${attempted.join(", ")}.` : undefined,
77
+ ]
78
+ .filter(Boolean)
79
+ .join("\n"),
80
+ };
81
+ }
82
+
83
+ export function normalizeWorkflowHudBridgePayload(
84
+ raw: unknown,
85
+ expectedSkill: string,
86
+ ): WorkflowHudBridgePayload | null {
87
+ if (!raw || typeof raw !== "object") return null;
88
+ const record = raw as Record<string, unknown>;
89
+ if (record.version !== 1 || record.skill !== expectedSkill) return null;
90
+ const hud = normalizeWorkflowHudSummary(record.hud);
91
+ if (!hud) return null;
92
+ return {
93
+ version: 1,
94
+ skill: expectedSkill,
95
+ phase: typeof record.phase === "string" && record.phase.trim() ? record.phase.trim() : undefined,
96
+ active: typeof record.active === "boolean" ? record.active : undefined,
97
+ session_id:
98
+ typeof record.session_id === "string" && record.session_id.trim() ? record.session_id.trim() : undefined,
99
+ thread_id: typeof record.thread_id === "string" && record.thread_id.trim() ? record.thread_id.trim() : undefined,
100
+ turn_id: typeof record.turn_id === "string" && record.turn_id.trim() ? record.turn_id.trim() : undefined,
101
+ hud,
102
+ };
103
+ }
104
+
105
+ async function readHudPayload(sidecarPath: string, expectedSkill: string): Promise<WorkflowHudBridgePayload | null> {
106
+ try {
107
+ return normalizeWorkflowHudBridgePayload(JSON.parse(await Bun.file(sidecarPath).text()), expectedSkill);
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+
27
113
  export function runGjcRuntimeBridge(
28
114
  endpoint: string,
29
115
  args: string[],
@@ -58,20 +144,80 @@ export function runGjcRuntimeBridge(
58
144
  return { status: child.status ?? (child.signal ? 1 : 0) };
59
145
  }
60
146
 
61
- const configured = [env[BRIDGE_ENV], env[LEGACY_BRIDGE_ENV]].filter(Boolean).join(", ");
62
- return {
63
- status: 1,
64
- error: [
65
- `gjc ${endpoint} requires the private GJC runtime endpoint implementation.`,
66
- `Set ${BRIDGE_ENV} to a GJC-compatible runtime binary.`,
67
- configured
68
- ? `Configured runtime candidates failed: ${configured}.`
69
- : "No gjc runtime binary was found on PATH.",
70
- attempted.length > 0 ? `Attempted: ${attempted.join(", ")}.` : undefined,
71
- ]
72
- .filter(Boolean)
73
- .join("\n"),
74
- };
147
+ return unavailableBridgeResult(endpoint, env, attempted);
148
+ }
149
+
150
+ export async function runGjcRuntimeBridgeWithHudSidecar(
151
+ endpoint: string,
152
+ args: string[],
153
+ options: GjcRuntimeHudBridgeOptions,
154
+ ): Promise<GjcRuntimeHudBridgeResult> {
155
+ const env = options.env ?? process.env;
156
+ if (env[GUARD_ENV] === "1") return { status: 1, error: `Refusing recursive gjc runtime bridge for ${endpoint}.` };
157
+
158
+ const attempted: string[] = [];
159
+ for (const binary of candidateBinaries(env)) {
160
+ const command = binary.trim();
161
+ if (!canAttempt(command)) continue;
162
+ attempted.push(command);
163
+ const sidecarDir = await fs.mkdtemp(path.join(os.tmpdir(), "gjc-workflow-hud-"));
164
+ const sidecarPath = path.join(sidecarDir, `${options.sidecarSkill}-${randomUUID()}.json`);
165
+ let latestPayload: WorkflowHudBridgePayload | undefined;
166
+ let lastRaw = "";
167
+ const publishPayload = async (): Promise<void> => {
168
+ let raw = "";
169
+ try {
170
+ raw = await Bun.file(sidecarPath).text();
171
+ } catch {
172
+ return;
173
+ }
174
+ if (!raw || raw === lastRaw) return;
175
+ lastRaw = raw;
176
+ const payload = await readHudPayload(sidecarPath, options.sidecarSkill);
177
+ if (!payload) return;
178
+ latestPayload = payload;
179
+ try {
180
+ await options.onHudPayload?.(payload);
181
+ } catch {
182
+ // HUD sidecar sync is best-effort and must not change child command semantics.
183
+ }
184
+ };
185
+ try {
186
+ const child = spawn(command, [endpoint, ...args], {
187
+ cwd: options.cwd,
188
+ stdio: "inherit",
189
+ env: {
190
+ ...env,
191
+ [GUARD_ENV]: "1",
192
+ GJC_WORKFLOW_HUD_PROTOCOL: WORKFLOW_HUD_PROTOCOL,
193
+ GJC_WORKFLOW_HUD_SIDECAR: sidecarPath,
194
+ GJC_WORKFLOW_HUD_SKILL: options.sidecarSkill,
195
+ },
196
+ });
197
+ const interval = setInterval(() => {
198
+ void publishPayload();
199
+ }, options.pollIntervalMs ?? 100);
200
+ const exit = Promise.withResolvers<{ status: number; error?: string; code?: string }>();
201
+ child.on("error", error => {
202
+ const fsError = error as NodeJS.ErrnoException;
203
+ exit.resolve({ status: 1, error: error.message, code: fsError.code });
204
+ });
205
+ child.on("exit", (code, signal) => exit.resolve({ status: code ?? (signal ? 1 : 0) }));
206
+ const result = await exit.promise;
207
+ clearInterval(interval);
208
+ await publishPayload();
209
+ if (result.code === "ENOENT") continue;
210
+ return {
211
+ status: result.status,
212
+ ...(result.error ? { error: result.error } : {}),
213
+ ...(latestPayload ? { hudPayload: latestPayload } : {}),
214
+ };
215
+ } finally {
216
+ await fs.rm(sidecarDir, { recursive: true, force: true });
217
+ }
218
+ }
219
+
220
+ return unavailableBridgeResult(endpoint, env, attempted);
75
221
  }
76
222
 
77
223
  export async function runBridgedRuntimeEndpoint(endpoint: string, args: string[]): Promise<void> {
@@ -1,5 +1,6 @@
1
1
  import { Command } from "@gajae-code/utils/cli";
2
- import { runBridgedRuntimeEndpoint } from "./gjc-runtime-bridge";
2
+ import { syncSkillActiveState } from "../skill-state/active-state";
3
+ import { runGjcRuntimeBridgeWithHudSidecar } from "./gjc-runtime-bridge";
3
4
 
4
5
  export default class Ralplan extends Command {
5
6
  static description = "Run private GJC RALPLAN workflow commands";
@@ -7,6 +8,24 @@ export default class Ralplan extends Command {
7
8
  static examples = ["$ gjc ralplan --help"];
8
9
 
9
10
  async run(): Promise<void> {
10
- await runBridgedRuntimeEndpoint("ralplan", this.argv);
11
+ const cwd = process.cwd();
12
+ const result = await runGjcRuntimeBridgeWithHudSidecar("ralplan", this.argv, {
13
+ cwd,
14
+ sidecarSkill: "ralplan",
15
+ onHudPayload: payload =>
16
+ syncSkillActiveState({
17
+ cwd,
18
+ skill: "ralplan",
19
+ active: payload.active ?? true,
20
+ phase: payload.phase,
21
+ sessionId: payload.session_id,
22
+ threadId: payload.thread_id,
23
+ turnId: payload.turn_id,
24
+ hud: payload.hud,
25
+ source: "gjc-runtime-bridge",
26
+ }),
27
+ });
28
+ if (result.error) process.stderr.write(`${result.error}\n`);
29
+ process.exitCode = result.status;
11
30
  }
12
31
  }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Inspect bundled workflow skills.
3
+ */
4
+ import { Args, Command, Flags, renderCommandHelp } from "@gajae-code/utils/cli";
5
+ import { runSkillsCommand, type SkillsAction, type SkillsCommandArgs } from "../cli/skills-cli";
6
+
7
+ const ACTIONS: SkillsAction[] = ["list", "read"];
8
+
9
+ export default class Skills extends Command {
10
+ static description = "Inspect bundled GJC workflow skills";
11
+
12
+ static args = {
13
+ action: Args.string({
14
+ description: "Skills action",
15
+ required: false,
16
+ options: ACTIONS,
17
+ }),
18
+ name: Args.string({
19
+ description: "Bundled skill name to read",
20
+ required: false,
21
+ }),
22
+ };
23
+
24
+ static flags = {
25
+ json: Flags.boolean({ description: "Output JSON" }),
26
+ };
27
+
28
+ static examples = [
29
+ "# List bundled workflow skills\n gjc skills list",
30
+ "# Read an embedded workflow skill without requiring .gjc files\n gjc skills read ultragoal",
31
+ "# Machine-readable embedded skill content\n gjc skills read ralplan --json",
32
+ ];
33
+
34
+ async run(): Promise<void> {
35
+ const { args, flags } = await this.parse(Skills);
36
+ if (!args.action) {
37
+ renderCommandHelp("gjc", "skills", Skills);
38
+ return;
39
+ }
40
+
41
+ const cmd: SkillsCommandArgs = {
42
+ action: args.action as SkillsAction,
43
+ name: args.name,
44
+ flags: { json: flags.json },
45
+ };
46
+ await runSkillsCommand(cmd);
47
+ }
48
+ }
@@ -1,12 +1,17 @@
1
1
  import { Args, Command, Flags } from "@gajae-code/utils/cli";
2
2
  import {
3
+ buildTeamHudSummary,
3
4
  executeGjcTeamApiOperation,
5
+ type GjcTeamSnapshot,
4
6
  listGjcTeams,
5
7
  monitorGjcTeam,
6
8
  parseTeamLaunchArgs,
9
+ readGjcTeamEvents,
10
+ readGjcTeamSnapshot,
7
11
  shutdownGjcTeam,
8
12
  startGjcTeam,
9
13
  } from "../gjc-runtime/team-runtime";
14
+ import { syncSkillActiveState } from "../skill-state/active-state";
10
15
 
11
16
  function writeJson(value: unknown): void {
12
17
  process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
@@ -15,6 +20,38 @@ function writeJson(value: unknown): void {
15
20
  function writeText(lines: string[]): void {
16
21
  process.stdout.write(`${lines.join("\n")}\n`);
17
22
  }
23
+ async function syncTeamHud(snapshot: GjcTeamSnapshot): Promise<void> {
24
+ try {
25
+ const events = await readGjcTeamEvents(snapshot.team_name);
26
+ await syncSkillActiveState({
27
+ cwd: process.cwd(),
28
+ skill: "team",
29
+ active: snapshot.phase !== "complete" && snapshot.phase !== "cancelled",
30
+ phase: snapshot.phase,
31
+ hud: await buildTeamHudSummary(snapshot, events.at(-1)),
32
+ source: "gjc-team",
33
+ });
34
+ } catch {
35
+ // HUD sync is best-effort and must not change command semantics.
36
+ }
37
+ }
38
+
39
+ function formatTaskCounts(counts: Record<string, number>): string {
40
+ return Object.entries(counts)
41
+ .map(([status, count]) => `${status}=${count}`)
42
+ .join(" ");
43
+ }
44
+
45
+ function formatIntegrationSummary(snapshot: {
46
+ integration_by_worker?: Record<string, { status?: string; conflict_files?: string[] }>;
47
+ }): string[] {
48
+ const entries = Object.entries(snapshot.integration_by_worker ?? {});
49
+ if (entries.length === 0) return ["integration: no attempts recorded"];
50
+ return entries.map(([worker, state]) => {
51
+ const files = state.conflict_files?.length ? ` files=${state.conflict_files.join(",")}` : "";
52
+ return `integration: ${worker} ${state.status ?? "unknown"}${files}`;
53
+ });
54
+ }
18
55
 
19
56
  function parseInputFlag(argv: string[]): Record<string, unknown> {
20
57
  const index = argv.indexOf("--input");
@@ -69,6 +106,7 @@ export default class Team extends Command {
69
106
  const teamName = rest.find(arg => !arg.startsWith("--"));
70
107
  if (!teamName) throw new Error("missing_team_name");
71
108
  const snapshot = await monitorGjcTeam(teamName);
109
+ await syncTeamHud(snapshot);
72
110
  if (json) {
73
111
  writeJson(snapshot);
74
112
  return;
@@ -76,9 +114,11 @@ export default class Team extends Command {
76
114
  writeText([
77
115
  `team: ${snapshot.team_name}`,
78
116
  `phase: ${snapshot.phase}`,
79
- `tmux: ${snapshot.tmux_session}`,
117
+ `tmux: ${snapshot.tmux_target || snapshot.tmux_session}`,
80
118
  `state: ${snapshot.state_dir}`,
81
- `tasks: ${snapshot.task_total}`,
119
+ `tasks: ${snapshot.task_total} (${formatTaskCounts(snapshot.task_counts)})`,
120
+ `workers: ${snapshot.workers.map(worker => `${worker.id}:${worker.status}`).join(" ")}`,
121
+ ...formatIntegrationSummary(snapshot),
82
122
  ]);
83
123
  return;
84
124
  }
@@ -87,6 +127,7 @@ export default class Team extends Command {
87
127
  const teamName = rest.find(arg => !arg.startsWith("--"));
88
128
  if (!teamName) throw new Error("missing_team_name");
89
129
  const snapshot = await shutdownGjcTeam(teamName);
130
+ await syncTeamHud(snapshot);
90
131
  if (json) {
91
132
  writeJson(snapshot);
92
133
  return;
@@ -108,13 +149,23 @@ export default class Team extends Command {
108
149
  return;
109
150
  }
110
151
  const input = parseInputFlag(rest);
111
- writeJson(await executeGjcTeamApiOperation(operation, input));
152
+ const result = await executeGjcTeamApiOperation(operation, input);
153
+ const teamName = String(input.team_name ?? input.teamName ?? "").trim();
154
+ if (teamName) {
155
+ try {
156
+ await syncTeamHud(await readGjcTeamSnapshot(teamName));
157
+ } catch {
158
+ // API operations without a resolvable snapshot leave HUD state unchanged.
159
+ }
160
+ }
161
+ writeJson(result);
112
162
  return;
113
163
  }
114
164
 
115
165
  const startArgs = action === "start" ? rest : this.argv;
116
166
  const options = parseTeamLaunchArgs(startArgs);
117
167
  const snapshot = await startGjcTeam({ ...options, dryRun });
168
+ await syncTeamHud(snapshot);
118
169
  if (json) {
119
170
  writeJson(snapshot);
120
171
  return;
@@ -6,7 +6,13 @@ import {
6
6
  writeCurrentSessionGoalModeState,
7
7
  writePendingGoalModeRequest,
8
8
  } from "../gjc-runtime/goal-mode-request";
9
- import { runNativeUltragoalCommand } from "../gjc-runtime/ultragoal-runtime";
9
+ import {
10
+ buildUltragoalHudSummary,
11
+ getUltragoalStatus,
12
+ readUltragoalLedger,
13
+ runNativeUltragoalCommand,
14
+ } from "../gjc-runtime/ultragoal-runtime";
15
+ import { syncSkillActiveState } from "../skill-state/active-state";
10
16
 
11
17
  export default class Ultragoal extends Command {
12
18
  static description = "Run native GJC Ultragoal workflow commands";
@@ -19,6 +25,20 @@ export default class Ultragoal extends Command {
19
25
  if (result.stdout) process.stdout.write(result.stdout);
20
26
  if (result.stderr) process.stderr.write(result.stderr);
21
27
  process.exitCode = result.status;
28
+ try {
29
+ const summary = await getUltragoalStatus(process.cwd());
30
+ const ledger = await readUltragoalLedger(process.cwd());
31
+ await syncSkillActiveState({
32
+ cwd: process.cwd(),
33
+ skill: "ultragoal",
34
+ active: summary.exists && summary.status !== "complete",
35
+ phase: summary.status,
36
+ hud: buildUltragoalHudSummary(summary, ledger.at(-1)),
37
+ source: "gjc-ultragoal",
38
+ });
39
+ } catch {
40
+ // HUD sync is best-effort and must not change command semantics.
41
+ }
22
42
  if (result.status !== 0 || !shouldActivateGoalMode) return;
23
43
 
24
44
  const cwd = process.cwd();
@@ -31,6 +31,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
31
31
  process.stdout.write("● Resolving model...\n");
32
32
  const modelRegistry = new ModelRegistry(authStorage);
33
33
  await modelRegistry.refresh();
34
+ modelRegistry.applyConfiguredModelBindings(settings);
34
35
  const stagedFilesPromise = (async () => {
35
36
  let stagedFiles = await git.diff.changedFiles(cwd, { cached: true });
36
37
  if (stagedFiles.length === 0) {
@@ -45,6 +45,7 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
45
45
  const authStorage = await discoverAuthStorage();
46
46
  const modelRegistry = new ModelRegistry(authStorage);
47
47
  await modelRegistry.refresh();
48
+ modelRegistry.applyConfiguredModelBindings(settings);
48
49
 
49
50
  const {
50
51
  model: primaryModel,