@gajae-code/coding-agent 0.2.4 → 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.
- package/CHANGELOG.md +27 -0
- package/README.md +1 -1
- package/dist/types/async/job-manager.d.ts +145 -2
- package/dist/types/commands/harness.d.ts +37 -0
- package/dist/types/config/settings-schema.d.ts +13 -3
- package/dist/types/config/settings.d.ts +3 -1
- package/dist/types/deep-interview/render-middleware.d.ts +5 -0
- package/dist/types/discovery/helpers.d.ts +1 -0
- package/dist/types/exec/bash-executor.d.ts +8 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +2 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +24 -0
- package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +137 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
- package/dist/types/harness-control-plane/classifier.d.ts +13 -0
- package/dist/types/harness-control-plane/control-endpoint.d.ts +30 -0
- package/dist/types/harness-control-plane/finalize.d.ts +47 -0
- package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
- package/dist/types/harness-control-plane/operate.d.ts +35 -0
- package/dist/types/harness-control-plane/owner.d.ts +46 -0
- package/dist/types/harness-control-plane/preserve.d.ts +19 -0
- package/dist/types/harness-control-plane/receipts.d.ts +88 -0
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
- package/dist/types/harness-control-plane/seams.d.ts +21 -0
- package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
- package/dist/types/harness-control-plane/storage.d.ts +53 -0
- package/dist/types/harness-control-plane/types.d.ts +162 -0
- package/dist/types/hooks/skill-keywords.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +2 -29
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/components/skill-hud/render.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/theme/defaults/index.d.ts +45 -9477
- package/dist/types/modes/theme/theme.d.ts +1 -5
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +8 -0
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/skill-state/active-state.d.ts +3 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
- package/dist/types/skill-state/workflow-state-contract.d.ts +24 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/types.d.ts +56 -3
- package/dist/types/tools/bash-allowed-prefixes.d.ts +5 -0
- package/dist/types/tools/bash.d.ts +24 -0
- package/dist/types/tools/cron.d.ts +110 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/monitor.d.ts +54 -0
- package/dist/types/tools/subagent.d.ts +11 -1
- package/dist/types/web/search/index.d.ts +1 -0
- package/dist/types/web/search/provider.d.ts +11 -4
- package/dist/types/web/search/providers/duckduckgo.d.ts +57 -0
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +522 -6
- package/src/cli/agents-cli.ts +3 -0
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/config-cli.ts +10 -2
- package/src/cli.ts +2 -0
- package/src/commands/harness.ts +592 -0
- package/src/commands/team.ts +36 -39
- package/src/config/settings-schema.ts +15 -2
- package/src/config/settings.ts +49 -7
- package/src/deep-interview/render-middleware.ts +366 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +9 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/team/SKILL.md +47 -21
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +78 -11
- package/src/discovery/helpers.ts +5 -0
- package/src/eval/js/shared/rewrite-imports.ts +1 -2
- package/src/exec/bash-executor.ts +20 -9
- package/src/extensibility/custom-tools/types.ts +1 -0
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +40 -21
- package/src/gjc-runtime/goal-mode-request.ts +11 -3
- package/src/gjc-runtime/ralplan-runtime.ts +27 -10
- package/src/gjc-runtime/restricted-role-agent-bash.ts +5 -0
- package/src/gjc-runtime/state-graph.ts +86 -0
- package/src/gjc-runtime/state-migrations.ts +132 -0
- package/src/gjc-runtime/state-renderer.ts +345 -0
- package/src/gjc-runtime/state-runtime.ts +733 -21
- package/src/gjc-runtime/state-validation.ts +49 -0
- package/src/gjc-runtime/state-writer.ts +718 -0
- package/src/gjc-runtime/team-runtime.ts +1083 -89
- package/src/gjc-runtime/ultragoal-runtime.ts +348 -19
- package/src/gjc-runtime/workflow-manifest.generated.json +1497 -0
- package/src/gjc-runtime/workflow-manifest.ts +425 -0
- package/src/harness-control-plane/classifier.ts +128 -0
- package/src/harness-control-plane/control-endpoint.ts +137 -0
- package/src/harness-control-plane/finalize.ts +222 -0
- package/src/harness-control-plane/frame-mapper.ts +286 -0
- package/src/harness-control-plane/operate.ts +225 -0
- package/src/harness-control-plane/owner.ts +553 -0
- package/src/harness-control-plane/preserve.ts +102 -0
- package/src/harness-control-plane/receipts.ts +216 -0
- package/src/harness-control-plane/rpc-adapter.ts +276 -0
- package/src/harness-control-plane/seams.ts +39 -0
- package/src/harness-control-plane/session-lease.ts +388 -0
- package/src/harness-control-plane/state-machine.ts +97 -0
- package/src/harness-control-plane/storage.ts +257 -0
- package/src/harness-control-plane/types.ts +214 -0
- package/src/hooks/skill-keywords.ts +4 -2
- package/src/hooks/skill-state.ts +25 -42
- package/src/internal-urls/docs-index.generated.ts +6 -4
- package/src/lsp/render.ts +1 -1
- package/src/modes/acp/acp-agent.ts +1 -1
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/assistant-message.ts +5 -1
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/hook-selector.ts +72 -2
- package/src/modes/components/skill-hud/render.ts +7 -2
- package/src/modes/controllers/event-controller.ts +71 -6
- package/src/modes/controllers/extension-ui-controller.ts +6 -0
- package/src/modes/controllers/input-controller.ts +19 -3
- package/src/modes/controllers/selector-controller.ts +3 -2
- package/src/modes/interactive-mode.ts +21 -2
- package/src/modes/theme/defaults/index.ts +0 -196
- package/src/modes/theme/theme.ts +35 -35
- package/src/modes/types.ts +2 -0
- package/src/prompts/agents/architect.md +5 -1
- package/src/prompts/agents/critic.md +5 -1
- package/src/prompts/agents/executor.md +13 -0
- package/src/prompts/agents/frontmatter.md +1 -0
- package/src/prompts/agents/planner.md +5 -1
- package/src/prompts/tools/bash.md +9 -0
- package/src/prompts/tools/cron.md +25 -0
- package/src/prompts/tools/monitor.md +30 -0
- package/src/prompts/tools/subagent.md +33 -3
- package/src/runtime-mcp/oauth-flow.ts +4 -2
- package/src/sdk.ts +7 -0
- package/src/session/agent-session.ts +247 -38
- package/src/session/session-manager.ts +13 -1
- package/src/session/streaming-output.ts +21 -0
- package/src/skill-state/active-state.ts +222 -78
- package/src/skill-state/deep-interview-mutation-guard.ts +91 -13
- package/src/skill-state/initial-phase.ts +2 -0
- package/src/skill-state/workflow-state-contract.ts +26 -0
- package/src/task/agents.ts +1 -0
- package/src/task/executor.ts +51 -8
- package/src/task/index.ts +120 -8
- package/src/task/render.ts +6 -3
- package/src/task/types.ts +57 -3
- package/src/tools/ask.ts +28 -7
- package/src/tools/bash-allowed-prefixes.ts +169 -0
- package/src/tools/bash.ts +190 -29
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/cron.ts +665 -0
- package/src/tools/index.ts +20 -2
- package/src/tools/monitor.ts +136 -0
- package/src/tools/subagent.ts +255 -64
- package/src/vim/engine.ts +3 -3
- package/src/web/search/index.ts +31 -18
- package/src/web/search/provider.ts +57 -12
- package/src/web/search/providers/duckduckgo.ts +279 -0
- package/src/web/search/types.ts +2 -0
- package/src/modes/theme/dark.json +0 -95
- package/src/modes/theme/defaults/alabaster.json +0 -93
- package/src/modes/theme/defaults/amethyst.json +0 -96
- package/src/modes/theme/defaults/anthracite.json +0 -93
- package/src/modes/theme/defaults/basalt.json +0 -91
- package/src/modes/theme/defaults/birch.json +0 -95
- package/src/modes/theme/defaults/dark-abyss.json +0 -91
- package/src/modes/theme/defaults/dark-arctic.json +0 -104
- package/src/modes/theme/defaults/dark-aurora.json +0 -95
- package/src/modes/theme/defaults/dark-catppuccin.json +0 -107
- package/src/modes/theme/defaults/dark-cavern.json +0 -91
- package/src/modes/theme/defaults/dark-copper.json +0 -95
- package/src/modes/theme/defaults/dark-cosmos.json +0 -90
- package/src/modes/theme/defaults/dark-cyberpunk.json +0 -102
- package/src/modes/theme/defaults/dark-dracula.json +0 -98
- package/src/modes/theme/defaults/dark-eclipse.json +0 -91
- package/src/modes/theme/defaults/dark-ember.json +0 -95
- package/src/modes/theme/defaults/dark-equinox.json +0 -90
- package/src/modes/theme/defaults/dark-forest.json +0 -96
- package/src/modes/theme/defaults/dark-github.json +0 -105
- package/src/modes/theme/defaults/dark-gruvbox.json +0 -112
- package/src/modes/theme/defaults/dark-lavender.json +0 -95
- package/src/modes/theme/defaults/dark-lunar.json +0 -89
- package/src/modes/theme/defaults/dark-midnight.json +0 -95
- package/src/modes/theme/defaults/dark-monochrome.json +0 -94
- package/src/modes/theme/defaults/dark-monokai.json +0 -98
- package/src/modes/theme/defaults/dark-nebula.json +0 -90
- package/src/modes/theme/defaults/dark-nord.json +0 -97
- package/src/modes/theme/defaults/dark-ocean.json +0 -101
- package/src/modes/theme/defaults/dark-one.json +0 -100
- package/src/modes/theme/defaults/dark-poimandres.json +0 -141
- package/src/modes/theme/defaults/dark-rainforest.json +0 -91
- package/src/modes/theme/defaults/dark-reef.json +0 -91
- package/src/modes/theme/defaults/dark-retro.json +0 -92
- package/src/modes/theme/defaults/dark-rose-pine.json +0 -96
- package/src/modes/theme/defaults/dark-sakura.json +0 -95
- package/src/modes/theme/defaults/dark-slate.json +0 -95
- package/src/modes/theme/defaults/dark-solarized.json +0 -97
- package/src/modes/theme/defaults/dark-solstice.json +0 -90
- package/src/modes/theme/defaults/dark-starfall.json +0 -91
- package/src/modes/theme/defaults/dark-sunset.json +0 -99
- package/src/modes/theme/defaults/dark-swamp.json +0 -90
- package/src/modes/theme/defaults/dark-synthwave.json +0 -103
- package/src/modes/theme/defaults/dark-taiga.json +0 -91
- package/src/modes/theme/defaults/dark-terminal.json +0 -95
- package/src/modes/theme/defaults/dark-tokyo-night.json +0 -101
- package/src/modes/theme/defaults/dark-tundra.json +0 -91
- package/src/modes/theme/defaults/dark-twilight.json +0 -91
- package/src/modes/theme/defaults/dark-volcanic.json +0 -91
- package/src/modes/theme/defaults/graphite.json +0 -92
- package/src/modes/theme/defaults/light-arctic.json +0 -107
- package/src/modes/theme/defaults/light-aurora-day.json +0 -91
- package/src/modes/theme/defaults/light-canyon.json +0 -91
- package/src/modes/theme/defaults/light-catppuccin.json +0 -106
- package/src/modes/theme/defaults/light-cirrus.json +0 -90
- package/src/modes/theme/defaults/light-coral.json +0 -95
- package/src/modes/theme/defaults/light-cyberpunk.json +0 -96
- package/src/modes/theme/defaults/light-dawn.json +0 -90
- package/src/modes/theme/defaults/light-dunes.json +0 -91
- package/src/modes/theme/defaults/light-eucalyptus.json +0 -95
- package/src/modes/theme/defaults/light-forest.json +0 -100
- package/src/modes/theme/defaults/light-frost.json +0 -95
- package/src/modes/theme/defaults/light-github.json +0 -115
- package/src/modes/theme/defaults/light-glacier.json +0 -91
- package/src/modes/theme/defaults/light-gruvbox.json +0 -108
- package/src/modes/theme/defaults/light-haze.json +0 -90
- package/src/modes/theme/defaults/light-honeycomb.json +0 -95
- package/src/modes/theme/defaults/light-lagoon.json +0 -91
- package/src/modes/theme/defaults/light-lavender.json +0 -95
- package/src/modes/theme/defaults/light-meadow.json +0 -91
- package/src/modes/theme/defaults/light-mint.json +0 -95
- package/src/modes/theme/defaults/light-monochrome.json +0 -101
- package/src/modes/theme/defaults/light-ocean.json +0 -99
- package/src/modes/theme/defaults/light-one.json +0 -99
- package/src/modes/theme/defaults/light-opal.json +0 -91
- package/src/modes/theme/defaults/light-orchard.json +0 -91
- package/src/modes/theme/defaults/light-paper.json +0 -95
- package/src/modes/theme/defaults/light-poimandres.json +0 -141
- package/src/modes/theme/defaults/light-prism.json +0 -90
- package/src/modes/theme/defaults/light-retro.json +0 -98
- package/src/modes/theme/defaults/light-sand.json +0 -95
- package/src/modes/theme/defaults/light-savanna.json +0 -91
- package/src/modes/theme/defaults/light-solarized.json +0 -102
- package/src/modes/theme/defaults/light-soleil.json +0 -90
- package/src/modes/theme/defaults/light-sunset.json +0 -99
- package/src/modes/theme/defaults/light-synthwave.json +0 -98
- package/src/modes/theme/defaults/light-tokyo-night.json +0 -111
- package/src/modes/theme/defaults/light-wetland.json +0 -91
- package/src/modes/theme/defaults/light-zenith.json +0 -89
- package/src/modes/theme/defaults/limestone.json +0 -94
- package/src/modes/theme/defaults/mahogany.json +0 -97
- package/src/modes/theme/defaults/marble.json +0 -93
- package/src/modes/theme/defaults/obsidian.json +0 -91
- package/src/modes/theme/defaults/onyx.json +0 -91
- package/src/modes/theme/defaults/pearl.json +0 -93
- package/src/modes/theme/defaults/porcelain.json +0 -91
- package/src/modes/theme/defaults/quartz.json +0 -96
- package/src/modes/theme/defaults/sandstone.json +0 -95
- package/src/modes/theme/defaults/titanium.json +0 -90
- package/src/modes/theme/light.json +0 -93
package/src/commands/team.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Args, Command, Flags } from "@gajae-code/utils/cli";
|
|
2
|
+
import { renderTeamStatusMarkdown } from "../gjc-runtime/state-renderer";
|
|
2
3
|
import {
|
|
3
4
|
buildTeamHudSummary,
|
|
4
5
|
executeGjcTeamApiOperation,
|
|
5
6
|
type GjcTeamSnapshot,
|
|
6
7
|
listGjcTeams,
|
|
7
|
-
|
|
8
|
+
monitorGjcTeamSnapshot,
|
|
8
9
|
parseTeamLaunchArgs,
|
|
10
|
+
persistGjcTeamModeStateSummary,
|
|
9
11
|
readGjcTeamEvents,
|
|
10
12
|
readGjcTeamSnapshot,
|
|
11
13
|
shutdownGjcTeam,
|
|
@@ -31,6 +33,7 @@ async function syncTeamHud(snapshot: GjcTeamSnapshot): Promise<void> {
|
|
|
31
33
|
hud: await buildTeamHudSummary(snapshot, events.at(-1)),
|
|
32
34
|
source: "gjc-team",
|
|
33
35
|
});
|
|
36
|
+
await persistGjcTeamModeStateSummary(snapshot, process.cwd());
|
|
34
37
|
} catch {
|
|
35
38
|
// HUD sync is best-effort and must not change command semantics.
|
|
36
39
|
}
|
|
@@ -42,29 +45,6 @@ function formatTaskCounts(counts: Record<string, number>): string {
|
|
|
42
45
|
.join(" ");
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
function formatNotificationSummary(snapshot: GjcTeamSnapshot): string {
|
|
46
|
-
const summary = snapshot.notification_summary;
|
|
47
|
-
return `notifications: total=${summary.total} replay_eligible=${summary.replay_eligible} pending=${summary.by_state.pending} queued=${summary.by_state.queued} deferred=${summary.by_state.deferred} failed=${summary.by_state.failed}`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function formatAwaitingIntegrationNextStep(snapshot: GjcTeamSnapshot): string[] {
|
|
51
|
-
if (snapshot.phase !== "awaiting_integration") return [];
|
|
52
|
-
return [
|
|
53
|
-
"next: worker tasks are completed, but integration still needs leader attention before the team is complete",
|
|
54
|
-
];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function formatIntegrationSummary(snapshot: {
|
|
58
|
-
integration_by_worker?: Record<string, { status?: string; conflict_files?: string[] }>;
|
|
59
|
-
}): string[] {
|
|
60
|
-
const entries = Object.entries(snapshot.integration_by_worker ?? {});
|
|
61
|
-
if (entries.length === 0) return ["integration: no attempts recorded"];
|
|
62
|
-
return entries.map(([worker, state]) => {
|
|
63
|
-
const files = state.conflict_files?.length ? ` files=${state.conflict_files.join(",")}` : "";
|
|
64
|
-
return `integration: ${worker} ${state.status ?? "unknown"}${files}`;
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
48
|
function parseInputFlag(argv: string[]): Record<string, unknown> {
|
|
69
49
|
const index = argv.indexOf("--input");
|
|
70
50
|
if (index < 0) return {};
|
|
@@ -76,12 +56,13 @@ function parseInputFlag(argv: string[]): Record<string, unknown> {
|
|
|
76
56
|
}
|
|
77
57
|
|
|
78
58
|
export default class Team extends Command {
|
|
79
|
-
static description =
|
|
59
|
+
static description =
|
|
60
|
+
"Run native GJC tmux team orchestration from inside an existing tmux/GJC --tmux session; --dry-run writes ephemeral .gjc/state/team state only";
|
|
80
61
|
static strict = false;
|
|
81
62
|
|
|
82
63
|
static args = {
|
|
83
64
|
action: Args.string({
|
|
84
|
-
description: "start (default), status, list, shutdown, resume, or api",
|
|
65
|
+
description: "start (default), status, monitor, list, shutdown, resume, or api",
|
|
85
66
|
required: false,
|
|
86
67
|
}),
|
|
87
68
|
};
|
|
@@ -96,8 +77,10 @@ export default class Team extends Command {
|
|
|
96
77
|
};
|
|
97
78
|
|
|
98
79
|
static examples = [
|
|
80
|
+
"gjc --tmux # start/attach the required tmux-backed leader session first",
|
|
99
81
|
'gjc team 3:executor "Implement the approved plan"',
|
|
100
82
|
"gjc team status <team-name> --json",
|
|
83
|
+
"gjc team monitor <team-name> --json",
|
|
101
84
|
'gjc team api claim-task --input \'{"team_name":"demo","worker_id":"worker-1"}\' --json',
|
|
102
85
|
'gjc team 2:executor --dry-run --json "Preview state only"',
|
|
103
86
|
"gjc team shutdown <team-name>",
|
|
@@ -119,26 +102,36 @@ export default class Team extends Command {
|
|
|
119
102
|
return;
|
|
120
103
|
}
|
|
121
104
|
|
|
122
|
-
if (action === "status"
|
|
105
|
+
if (action === "status") {
|
|
106
|
+
const teamName = rest.find(arg => !arg.startsWith("--"));
|
|
107
|
+
if (!teamName) throw new Error("missing_team_name");
|
|
108
|
+
const snapshot = await readGjcTeamSnapshot(teamName);
|
|
109
|
+
if (json) {
|
|
110
|
+
writeJson(snapshot);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
writeText([
|
|
114
|
+
renderTeamStatusMarkdown(snapshot).trimEnd(),
|
|
115
|
+
"- mode: read-only status; use `gjc team monitor <team>` or `gjc team resume <team>` for recovery/integration",
|
|
116
|
+
]);
|
|
117
|
+
void formatTaskCounts(snapshot.task_counts);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (action === "monitor" || action === "resume") {
|
|
123
122
|
const teamName = rest.find(arg => !arg.startsWith("--"));
|
|
124
123
|
if (!teamName) throw new Error("missing_team_name");
|
|
125
|
-
const snapshot = await
|
|
124
|
+
const snapshot = await monitorGjcTeamSnapshot(teamName);
|
|
126
125
|
await syncTeamHud(snapshot);
|
|
127
126
|
if (json) {
|
|
128
127
|
writeJson(snapshot);
|
|
129
128
|
return;
|
|
130
129
|
}
|
|
131
130
|
writeText([
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
`tmux: ${snapshot.tmux_target || snapshot.tmux_session}`,
|
|
135
|
-
`state: ${snapshot.state_dir}`,
|
|
136
|
-
`tasks: ${snapshot.task_total} (${formatTaskCounts(snapshot.task_counts)})`,
|
|
137
|
-
`workers: ${snapshot.workers.map(worker => `${worker.id}:${worker.status}`).join(" ")}`,
|
|
138
|
-
formatNotificationSummary(snapshot),
|
|
139
|
-
...formatAwaitingIntegrationNextStep(snapshot),
|
|
140
|
-
...formatIntegrationSummary(snapshot),
|
|
131
|
+
renderTeamStatusMarkdown(snapshot).trimEnd(),
|
|
132
|
+
"- mode: mutating monitor; liveness recovery and integration may have run",
|
|
141
133
|
]);
|
|
134
|
+
void formatTaskCounts(snapshot.task_counts);
|
|
142
135
|
return;
|
|
143
136
|
}
|
|
144
137
|
|
|
@@ -162,8 +155,12 @@ export default class Team extends Command {
|
|
|
162
155
|
"Supported operations:",
|
|
163
156
|
"send-message broadcast mailbox-list mailbox-mark-delivered mailbox-mark-notified notification-list notification-read notification-replay notification-mark-pane-attempt worker-startup-ack",
|
|
164
157
|
"create-task read-task list-tasks update-task claim-task transition-task-status release-task-claim",
|
|
165
|
-
"read-config read-manifest read-worker-status read-worker-heartbeat update-worker-heartbeat write-worker-inbox write-worker-identity",
|
|
166
|
-
"append-event read-events await-event write-shutdown-request read-shutdown-ack read-monitor-snapshot write-monitor-snapshot read-task-approval write-task-approval",
|
|
158
|
+
"read-config read-manifest read-worker-status update-worker-status read-worker-heartbeat recover-stale-claims update-worker-heartbeat write-worker-inbox write-worker-identity",
|
|
159
|
+
"append-event read-events read-traces await-event write-shutdown-request read-shutdown-ack read-monitor-snapshot write-monitor-snapshot read-task-approval write-task-approval",
|
|
160
|
+
"Completion example:",
|
|
161
|
+
'transition-task-status --input \'{"team_name":"demo","task_id":"task-1","to":"completed","claim_token":"...","completion_evidence":{"summary":"done","items":[{"kind":"command","status":"passed","summary":"focused tests passed","command":"bun test packages/coding-agent/test/gjc-runtime/team-runtime.test.ts"}]}}\' --json',
|
|
162
|
+
'Review-only completion may use {"kind":"inspection","status":"verified","summary":"review passed","location":"agent://review"}.',
|
|
163
|
+
'Typed lane task example: create-task --input \'{"team_name":"demo","subject":"Verify delivery","description":"Run verification","owner":"worker-1","lane":"verification","required_role":"executor","depends_on":["task-1"]}\' --json',
|
|
167
164
|
]);
|
|
168
165
|
return;
|
|
169
166
|
}
|
|
@@ -147,6 +147,7 @@ interface StringDef {
|
|
|
147
147
|
interface NumberDef {
|
|
148
148
|
type: "number";
|
|
149
149
|
default: number;
|
|
150
|
+
validate?: (value: number) => boolean;
|
|
150
151
|
ui?: UiNumber;
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -319,6 +320,12 @@ export const SETTINGS_SCHEMA = {
|
|
|
319
320
|
|
|
320
321
|
cycleOrder: { type: "array", default: DEFAULT_CYCLE_ORDER },
|
|
321
322
|
|
|
323
|
+
"gjc.deepInterview.ambiguityThreshold": {
|
|
324
|
+
type: "number",
|
|
325
|
+
default: 0.05,
|
|
326
|
+
validate: (value: number) => Number.isFinite(value) && value > 0 && value <= 1,
|
|
327
|
+
},
|
|
328
|
+
|
|
322
329
|
// ────────────────────────────────────────────────────────────────────────
|
|
323
330
|
// Appearance
|
|
324
331
|
// ────────────────────────────────────────────────────────────────────────
|
|
@@ -337,7 +344,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
337
344
|
|
|
338
345
|
"theme.light": {
|
|
339
346
|
type: "string",
|
|
340
|
-
default: "
|
|
347
|
+
default: "blue-crab",
|
|
341
348
|
ui: {
|
|
342
349
|
tab: "appearance",
|
|
343
350
|
label: "Light Theme",
|
|
@@ -2530,6 +2537,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2530
2537
|
type: "enum",
|
|
2531
2538
|
values: [
|
|
2532
2539
|
"auto",
|
|
2540
|
+
"duckduckgo",
|
|
2533
2541
|
"exa",
|
|
2534
2542
|
"brave",
|
|
2535
2543
|
"jina",
|
|
@@ -2554,7 +2562,12 @@ export const SETTINGS_SCHEMA = {
|
|
|
2554
2562
|
{
|
|
2555
2563
|
value: "auto",
|
|
2556
2564
|
label: "Auto",
|
|
2557
|
-
description: "
|
|
2565
|
+
description: "Active model's native search if its creds exist, else keyless DuckDuckGo",
|
|
2566
|
+
},
|
|
2567
|
+
{
|
|
2568
|
+
value: "duckduckgo",
|
|
2569
|
+
label: "DuckDuckGo",
|
|
2570
|
+
description: "Keyless default — no API key or OAuth required",
|
|
2558
2571
|
},
|
|
2559
2572
|
{ value: "exa", label: "Exa", description: "Uses Exa API when EXA_API_KEY is set" },
|
|
2560
2573
|
{ value: "brave", label: "Brave", description: "Requires BRAVE_API_KEY" },
|
package/src/config/settings.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* import { settings } from "./settings";
|
|
6
6
|
*
|
|
7
7
|
* const enabled = settings.get("compaction.enabled"); // sync read
|
|
8
|
-
* settings.set("theme.dark", "
|
|
8
|
+
* settings.set("theme.dark", "red-claw"); // sync write, saves in background
|
|
9
9
|
*
|
|
10
10
|
* For tests:
|
|
11
11
|
* const isolated = Settings.isolated({ "compaction.enabled": false });
|
|
@@ -17,6 +17,7 @@ import * as path from "node:path";
|
|
|
17
17
|
import {
|
|
18
18
|
getAgentDbPath,
|
|
19
19
|
getAgentDir,
|
|
20
|
+
getCustomThemesDir,
|
|
20
21
|
getProjectDir,
|
|
21
22
|
isEnoent,
|
|
22
23
|
logger,
|
|
@@ -100,6 +101,14 @@ function setByPath(obj: RawSettings, segments: string[], value: unknown): void {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
const PATH_SCOPED_ARRAY_SETTINGS = new Set<SettingPath>(["enabledModels", "disabledProviders"]);
|
|
104
|
+
const LEGACY_THEME_NAME_REPLACEMENTS = {
|
|
105
|
+
dark: "red-claw",
|
|
106
|
+
light: "blue-crab",
|
|
107
|
+
} as const;
|
|
108
|
+
|
|
109
|
+
function isLegacyThemeName(name: string): name is keyof typeof LEGACY_THEME_NAME_REPLACEMENTS {
|
|
110
|
+
return name === "dark" || name === "light";
|
|
111
|
+
}
|
|
103
112
|
|
|
104
113
|
type PathScopedStringArrayEntry = {
|
|
105
114
|
path?: unknown;
|
|
@@ -288,6 +297,11 @@ export class Settings {
|
|
|
288
297
|
return getDefault(path);
|
|
289
298
|
}
|
|
290
299
|
|
|
300
|
+
/** Check whether a setting is present in loaded settings/overrides rather than coming from schema defaults. */
|
|
301
|
+
has(path: SettingPath): boolean {
|
|
302
|
+
return getByPath(this.#merged, path.split(".")) !== undefined;
|
|
303
|
+
}
|
|
304
|
+
|
|
291
305
|
/**
|
|
292
306
|
* Set a setting value (sync).
|
|
293
307
|
* Updates global settings and queues a background save.
|
|
@@ -587,6 +601,25 @@ export class Settings {
|
|
|
587
601
|
}
|
|
588
602
|
}
|
|
589
603
|
|
|
604
|
+
#hasCustomThemeFile(name: string): boolean {
|
|
605
|
+
try {
|
|
606
|
+
return fs.existsSync(path.join(getCustomThemesDir(this.#agentDir), `${name}.json`));
|
|
607
|
+
} catch {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
#migrateLegacyBuiltInThemeName(name: string): string {
|
|
613
|
+
if (isLegacyThemeName(name) && !this.#hasCustomThemeFile(name)) {
|
|
614
|
+
return LEGACY_THEME_NAME_REPLACEMENTS[name];
|
|
615
|
+
}
|
|
616
|
+
return name;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
#getThemeSlotForName(name: string): "dark" | "light" {
|
|
620
|
+
return isLightTheme(name, this.#agentDir) ? "light" : "dark";
|
|
621
|
+
}
|
|
622
|
+
|
|
590
623
|
/** Apply schema migrations to raw settings */
|
|
591
624
|
#migrateRawSettings(raw: RawSettings): RawSettings {
|
|
592
625
|
// queueMode -> steeringMode
|
|
@@ -606,13 +639,22 @@ export class Settings {
|
|
|
606
639
|
// Migrate old flat "theme" string to nested theme.dark/theme.light
|
|
607
640
|
if (typeof raw.theme === "string") {
|
|
608
641
|
const oldTheme = raw.theme;
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
642
|
+
const migratedTheme = this.#migrateLegacyBuiltInThemeName(oldTheme);
|
|
643
|
+
if (oldTheme === "dark" && migratedTheme === "red-claw") {
|
|
644
|
+
raw.theme = { dark: migratedTheme };
|
|
645
|
+
} else if (oldTheme === "light" && migratedTheme === "blue-crab") {
|
|
646
|
+
raw.theme = { light: migratedTheme };
|
|
612
647
|
} else {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
648
|
+
const slot = this.#getThemeSlotForName(migratedTheme);
|
|
649
|
+
raw.theme = { [slot]: migratedTheme };
|
|
650
|
+
}
|
|
651
|
+
} else if (raw.theme && typeof raw.theme === "object" && !Array.isArray(raw.theme)) {
|
|
652
|
+
const themeObj = raw.theme as Record<string, unknown>;
|
|
653
|
+
if (typeof themeObj.dark === "string") {
|
|
654
|
+
themeObj.dark = this.#migrateLegacyBuiltInThemeName(themeObj.dark);
|
|
655
|
+
}
|
|
656
|
+
if (typeof themeObj.light === "string") {
|
|
657
|
+
themeObj.light = this.#migrateLegacyBuiltInThemeName(themeObj.light);
|
|
616
658
|
}
|
|
617
659
|
}
|
|
618
660
|
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { type Component, Container, Markdown, Spacer, Text } from "@gajae-code/tui";
|
|
2
|
+
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
3
|
+
|
|
4
|
+
interface RoundQuestionModel {
|
|
5
|
+
kind: "round-question";
|
|
6
|
+
round: string;
|
|
7
|
+
component?: string;
|
|
8
|
+
targeting?: string;
|
|
9
|
+
mode?: string;
|
|
10
|
+
whyNow?: string;
|
|
11
|
+
ambiguity?: string;
|
|
12
|
+
question: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface TopologyQuestionModel {
|
|
16
|
+
kind: "topology-question";
|
|
17
|
+
context?: string;
|
|
18
|
+
components: Array<{ name: string; description: string }>;
|
|
19
|
+
question: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ProgressDimension {
|
|
23
|
+
name: string;
|
|
24
|
+
score: string;
|
|
25
|
+
weight: string;
|
|
26
|
+
weighted: string;
|
|
27
|
+
gap: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface ProgressModel {
|
|
31
|
+
kind: "progress";
|
|
32
|
+
round: string;
|
|
33
|
+
dimensions: ProgressDimension[];
|
|
34
|
+
ambiguity?: string;
|
|
35
|
+
topology?: string;
|
|
36
|
+
ontology?: string;
|
|
37
|
+
nextTarget?: string;
|
|
38
|
+
status?: string;
|
|
39
|
+
extra?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface ThresholdModel {
|
|
43
|
+
kind: "threshold";
|
|
44
|
+
threshold: string;
|
|
45
|
+
source: string;
|
|
46
|
+
rest: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type DeepInterviewModel = RoundQuestionModel | TopologyQuestionModel | ProgressModel | ThresholdModel;
|
|
50
|
+
|
|
51
|
+
function normalizeText(text: string): string {
|
|
52
|
+
return text.trim().replaceAll("\r\n", "\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function stripMarkdownEmphasis(value: string): string {
|
|
56
|
+
return value.replace(/\*\*/g, "").replace(/^"|"$/g, "").trim();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseRoundQuestion(text: string): RoundQuestionModel | null {
|
|
60
|
+
const normalized = normalizeText(text);
|
|
61
|
+
const lines = normalized.split("\n");
|
|
62
|
+
const headerIndex = lines.findIndex(line => /^Round\s+\d+\s+\|/i.test(line.trim()));
|
|
63
|
+
if (headerIndex < 0) return null;
|
|
64
|
+
const headerLine = lines[headerIndex]?.trim() ?? "";
|
|
65
|
+
const body = lines
|
|
66
|
+
.slice(headerIndex + 1)
|
|
67
|
+
.join("\n")
|
|
68
|
+
.trim();
|
|
69
|
+
if (!body) return null;
|
|
70
|
+
|
|
71
|
+
const componentMatch =
|
|
72
|
+
/^Round\s+(\d+)\s+\|\s+Component:\s*(.*?)\s+\|\s+Targeting:\s*(.*?)\s+\|\s+Why now:\s*(.*?)\s+\|\s+Ambiguity:\s*(.+?)%?\s*$/i.exec(
|
|
73
|
+
headerLine,
|
|
74
|
+
);
|
|
75
|
+
if (componentMatch) {
|
|
76
|
+
return {
|
|
77
|
+
kind: "round-question",
|
|
78
|
+
round: componentMatch[1] ?? "?",
|
|
79
|
+
component: componentMatch[2]?.trim(),
|
|
80
|
+
targeting: componentMatch[3]?.trim(),
|
|
81
|
+
whyNow: componentMatch[4]?.trim(),
|
|
82
|
+
ambiguity: componentMatch[5]?.trim(),
|
|
83
|
+
question: body,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const targetingMatch =
|
|
88
|
+
/^Round\s+(\d+)\s+\|\s+Targeting:\s*(.*?)\s+\|\s+Why now:\s*(.*?)\s+\|\s+Ambiguity:\s*(.+?)%?\s*$/i.exec(
|
|
89
|
+
headerLine,
|
|
90
|
+
);
|
|
91
|
+
if (targetingMatch) {
|
|
92
|
+
return {
|
|
93
|
+
kind: "round-question",
|
|
94
|
+
round: targetingMatch[1] ?? "?",
|
|
95
|
+
targeting: targetingMatch[2]?.trim(),
|
|
96
|
+
whyNow: targetingMatch[3]?.trim(),
|
|
97
|
+
ambiguity: targetingMatch[4]?.trim(),
|
|
98
|
+
question: body,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const modeMatch = /^Round\s+(\d+)\s+\|\s+(.*?)\s+\|\s+Ambiguity:\s*(.+?)%?\s*$/i.exec(headerLine);
|
|
103
|
+
if (modeMatch) {
|
|
104
|
+
return {
|
|
105
|
+
kind: "round-question",
|
|
106
|
+
round: modeMatch[1] ?? "?",
|
|
107
|
+
mode: modeMatch[2]?.trim(),
|
|
108
|
+
ambiguity: modeMatch[3]?.trim(),
|
|
109
|
+
question: body,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function parseTopologyQuestion(text: string): TopologyQuestionModel | null {
|
|
117
|
+
const normalized = normalizeText(text);
|
|
118
|
+
const lines = normalized.split("\n");
|
|
119
|
+
const headerIndex = lines.findIndex(line =>
|
|
120
|
+
/^Round\s+0\s+\|\s+Topology confirmation\s+\|\s+Ambiguity:\s+not scored yet/i.test(line.trim()),
|
|
121
|
+
);
|
|
122
|
+
if (headerIndex < 0) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const components: TopologyQuestionModel["components"] = [];
|
|
126
|
+
const contextLines: string[] = [];
|
|
127
|
+
const questionLines: string[] = [];
|
|
128
|
+
let inQuestion = false;
|
|
129
|
+
for (const line of lines.slice(headerIndex + 1)) {
|
|
130
|
+
const trimmed = line.trim();
|
|
131
|
+
const component = /^\s*\d+\.\s+([^:]+):\s+(.+)$/.exec(line);
|
|
132
|
+
if (component) {
|
|
133
|
+
components.push({ name: component[1]?.trim() ?? "", description: component[2]?.trim() ?? "" });
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (/\?$/.test(trimmed)) inQuestion = true;
|
|
137
|
+
if (!trimmed) continue;
|
|
138
|
+
if (inQuestion) questionLines.push(trimmed);
|
|
139
|
+
else contextLines.push(trimmed);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
kind: "topology-question",
|
|
143
|
+
context: contextLines.join("\n") || undefined,
|
|
144
|
+
components,
|
|
145
|
+
question: questionLines.join("\n"),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function splitMarkdownTableRow(line: string): string[] {
|
|
150
|
+
const trimmed = line.trim();
|
|
151
|
+
if (!trimmed.startsWith("|") || !trimmed.endsWith("|")) return [];
|
|
152
|
+
return trimmed
|
|
153
|
+
.slice(1, -1)
|
|
154
|
+
.split("|")
|
|
155
|
+
.map(cell => stripMarkdownEmphasis(cell));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function parseProgress(text: string): ProgressModel | null {
|
|
159
|
+
const normalized = normalizeText(text);
|
|
160
|
+
const roundMatch = /^Round\s+(\d+)\s+complete\./i.exec(normalized);
|
|
161
|
+
if (!roundMatch) return null;
|
|
162
|
+
|
|
163
|
+
const lines = normalized.split("\n");
|
|
164
|
+
const dimensions: ProgressDimension[] = [];
|
|
165
|
+
const extraLines: string[] = [];
|
|
166
|
+
let ambiguity: string | undefined;
|
|
167
|
+
let topology: string | undefined;
|
|
168
|
+
let ontology: string | undefined;
|
|
169
|
+
let nextTarget: string | undefined;
|
|
170
|
+
let status: string | undefined;
|
|
171
|
+
|
|
172
|
+
for (const [index, line] of lines.entries()) {
|
|
173
|
+
if (index === 0) continue;
|
|
174
|
+
const trimmed = line.trim();
|
|
175
|
+
const cells = splitMarkdownTableRow(line);
|
|
176
|
+
if (cells.length >= 5) {
|
|
177
|
+
const [name = "", score = "", weight = "", weighted = "", gap = ""] = cells;
|
|
178
|
+
if (/^-+$/.test(name) || /^Dimension$/i.test(name)) continue;
|
|
179
|
+
if (/^Ambiguity$/i.test(name)) {
|
|
180
|
+
ambiguity = weighted || score || gap;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
dimensions.push({ name, score, weight, weighted, gap });
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const topologyMatch = /^\*\*Topology:\*\*\s*(.+)$/.exec(trimmed);
|
|
187
|
+
if (topologyMatch) {
|
|
188
|
+
topology = topologyMatch[1]?.trim();
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const ontologyMatch = /^\*\*Ontology:\*\*\s*(.+)$/.exec(trimmed);
|
|
192
|
+
if (ontologyMatch) {
|
|
193
|
+
ontology = ontologyMatch[1]?.trim();
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const nextTargetMatch = /^\*\*Next target:\*\*\s*(.+)$/.exec(trimmed);
|
|
197
|
+
if (nextTargetMatch) {
|
|
198
|
+
nextTarget = nextTargetMatch[1]?.trim();
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (/^(Clarity threshold met!|Focusing next question on:)/i.test(trimmed)) {
|
|
202
|
+
status = trimmed;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (trimmed) extraLines.push(trimmed);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (dimensions.length === 0 && !ambiguity && !topology && !ontology && !nextTarget) return null;
|
|
209
|
+
return {
|
|
210
|
+
kind: "progress",
|
|
211
|
+
round: roundMatch[1] ?? "?",
|
|
212
|
+
dimensions,
|
|
213
|
+
ambiguity,
|
|
214
|
+
topology,
|
|
215
|
+
ontology,
|
|
216
|
+
nextTarget,
|
|
217
|
+
status,
|
|
218
|
+
extra: extraLines.join("\n") || undefined,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function parseThreshold(text: string): ThresholdModel | null {
|
|
223
|
+
const normalized = normalizeText(text);
|
|
224
|
+
const match = /^Deep Interview threshold:\s*(.*?)\s*\(source:\s*(.*?)\)\s*$/im.exec(normalized.split("\n")[0] ?? "");
|
|
225
|
+
if (!match) return null;
|
|
226
|
+
return {
|
|
227
|
+
kind: "threshold",
|
|
228
|
+
threshold: match[1]?.trim() ?? "",
|
|
229
|
+
source: match[2]?.trim() ?? "",
|
|
230
|
+
rest: normalized.split("\n").slice(1).join("\n").trim(),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function parseDeepInterview(text: string): DeepInterviewModel | null {
|
|
235
|
+
return parseProgress(text) ?? parseTopologyQuestion(text) ?? parseRoundQuestion(text) ?? parseThreshold(text);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function addLabel(container: Container, label: string, value: string | undefined, uiTheme: Theme): void {
|
|
239
|
+
if (!value) return;
|
|
240
|
+
container.addChild(new Spacer(1));
|
|
241
|
+
container.addChild(new Text(uiTheme.fg("accent", uiTheme.bold(label)), 0, 0));
|
|
242
|
+
container.addChild(
|
|
243
|
+
new Markdown(value, 2, 0, getMarkdownTheme(), { color: (text: string) => uiTheme.fg("toolOutput", text) }),
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function renderPipeSummary(title: string, value: string | undefined): string | undefined {
|
|
248
|
+
if (!value) return undefined;
|
|
249
|
+
return (
|
|
250
|
+
value
|
|
251
|
+
.split("|")
|
|
252
|
+
.map(part => part.trim())
|
|
253
|
+
.filter(Boolean)
|
|
254
|
+
.map(part => `- ${part}`)
|
|
255
|
+
.join("\n") || title
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function renderModel(model: DeepInterviewModel, uiTheme: Theme): Component {
|
|
260
|
+
const container = new Container();
|
|
261
|
+
if (model.kind === "round-question") {
|
|
262
|
+
const meta = [
|
|
263
|
+
`Round ${model.round}`,
|
|
264
|
+
model.ambiguity ? `Ambiguity ${model.ambiguity.replace(/%$/, "")}%` : undefined,
|
|
265
|
+
]
|
|
266
|
+
.filter(Boolean)
|
|
267
|
+
.join(" · ");
|
|
268
|
+
container.addChild(new Text(uiTheme.fg("toolTitle", uiTheme.bold(`Deep Interview · ${meta}`)), 0, 0));
|
|
269
|
+
addLabel(container, "Component", model.component, uiTheme);
|
|
270
|
+
addLabel(container, "Mode", model.mode, uiTheme);
|
|
271
|
+
addLabel(container, "Target", model.targeting, uiTheme);
|
|
272
|
+
addLabel(container, "Why now", model.whyNow, uiTheme);
|
|
273
|
+
addLabel(container, "Question", model.question, uiTheme);
|
|
274
|
+
return container;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (model.kind === "topology-question") {
|
|
278
|
+
container.addChild(
|
|
279
|
+
new Text(uiTheme.fg("toolTitle", uiTheme.bold("Deep Interview · Round 0 · Topology confirmation")), 0, 0),
|
|
280
|
+
);
|
|
281
|
+
addLabel(container, "Ambiguity", "Not scored yet", uiTheme);
|
|
282
|
+
addLabel(container, "Reading", model.context, uiTheme);
|
|
283
|
+
if (model.components.length > 0) {
|
|
284
|
+
const components = model.components
|
|
285
|
+
.map((component, index) => `${index + 1}. **${component.name}**\n ${component.description}`)
|
|
286
|
+
.join("\n\n");
|
|
287
|
+
addLabel(container, "Components", components, uiTheme);
|
|
288
|
+
}
|
|
289
|
+
addLabel(container, "Question", model.question, uiTheme);
|
|
290
|
+
return container;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (model.kind === "progress") {
|
|
294
|
+
container.addChild(
|
|
295
|
+
new Text(uiTheme.fg("toolTitle", uiTheme.bold(`Deep Interview · Round ${model.round} complete`)), 0, 0),
|
|
296
|
+
);
|
|
297
|
+
addLabel(container, "Ambiguity", model.ambiguity, uiTheme);
|
|
298
|
+
if (model.dimensions.length > 0) {
|
|
299
|
+
container.addChild(new Spacer(1));
|
|
300
|
+
container.addChild(new Text(uiTheme.fg("accent", uiTheme.bold("Clarity")), 0, 0));
|
|
301
|
+
for (const dimension of model.dimensions) {
|
|
302
|
+
const body = [`Score ${dimension.score} · weight ${dimension.weight} · weighted ${dimension.weighted}`];
|
|
303
|
+
if (dimension.gap) body.push(/^clear$/i.test(dimension.gap) ? "Clear" : `Gap: ${dimension.gap}`);
|
|
304
|
+
addLabel(container, dimension.name, body.join("\n"), uiTheme);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
addLabel(container, "Topology", renderPipeSummary("Topology", model.topology), uiTheme);
|
|
308
|
+
addLabel(container, "Ontology", renderPipeSummary("Ontology", model.ontology), uiTheme);
|
|
309
|
+
addLabel(container, "Next target", model.nextTarget, uiTheme);
|
|
310
|
+
addLabel(container, "Status", model.status, uiTheme);
|
|
311
|
+
addLabel(container, "Additional details", model.extra, uiTheme);
|
|
312
|
+
return container;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
container.addChild(new Text(uiTheme.fg("toolTitle", uiTheme.bold("Deep Interview · Started")), 0, 0));
|
|
316
|
+
addLabel(container, "Threshold", `${model.threshold} · source: ${model.source}`, uiTheme);
|
|
317
|
+
addLabel(container, "Details", model.rest, uiTheme);
|
|
318
|
+
return container;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function renderDeepInterviewAssistantText(text: string, uiTheme: Theme): Component | null {
|
|
322
|
+
const model = parseDeepInterview(text);
|
|
323
|
+
if (!model || model.kind === "round-question" || model.kind === "topology-question") return null;
|
|
324
|
+
return renderModel(model, uiTheme);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function renderDeepInterviewAskQuestion(question: string, uiTheme: Theme): Component | null {
|
|
328
|
+
const model = parseTopologyQuestion(question) ?? parseRoundQuestion(question);
|
|
329
|
+
if (!model) return null;
|
|
330
|
+
return renderModel(model, uiTheme);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function formatDeepInterviewSelectorPrompt(question: string): string | null {
|
|
334
|
+
const model = parseTopologyQuestion(question) ?? parseRoundQuestion(question);
|
|
335
|
+
if (!model) return null;
|
|
336
|
+
if (model.kind === "topology-question") {
|
|
337
|
+
const componentLines =
|
|
338
|
+
model.components.length > 0
|
|
339
|
+
? [
|
|
340
|
+
"Components:",
|
|
341
|
+
...model.components.map(
|
|
342
|
+
(component, index) => `${index + 1}. ${component.name} — ${component.description}`,
|
|
343
|
+
),
|
|
344
|
+
]
|
|
345
|
+
: [];
|
|
346
|
+
return [
|
|
347
|
+
"Deep Interview · Round 0 · Topology confirmation",
|
|
348
|
+
"Ambiguity: not scored yet",
|
|
349
|
+
model.context ? `Reading:\n${model.context}` : undefined,
|
|
350
|
+
...componentLines,
|
|
351
|
+
model.question ? `Question:\n${model.question}` : undefined,
|
|
352
|
+
]
|
|
353
|
+
.filter((line): line is string => Boolean(line))
|
|
354
|
+
.join("\n\n");
|
|
355
|
+
}
|
|
356
|
+
return [
|
|
357
|
+
`Deep Interview · Round ${model.round}${model.ambiguity ? ` · Ambiguity ${model.ambiguity.replace(/%$/, "")}%` : ""}`,
|
|
358
|
+
model.component ? `Component: ${model.component}` : undefined,
|
|
359
|
+
model.mode ? `Mode: ${model.mode}` : undefined,
|
|
360
|
+
model.targeting ? `Target: ${model.targeting}` : undefined,
|
|
361
|
+
model.whyNow ? `Why now: ${model.whyNow}` : undefined,
|
|
362
|
+
model.question,
|
|
363
|
+
]
|
|
364
|
+
.filter((line): line is string => Boolean(line))
|
|
365
|
+
.join("\n");
|
|
366
|
+
}
|