@gajae-code/coding-agent 0.7.9 → 0.7.11
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 +47 -0
- package/README.md +2 -0
- package/dist/types/config/keybindings.d.ts +11 -5
- package/dist/types/config/settings-schema.d.ts +10 -1
- package/dist/types/defaults/gjc-defaults.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +10 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +3 -2
- package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +1 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +1 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -4
- package/dist/types/modes/components/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/skill-message.d.ts +1 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +4 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +20 -6
- package/dist/types/modes/slash-command-visibility.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/notifications/html-format.d.ts +2 -0
- package/dist/types/notifications/recent-activity.d.ts +5 -1
- package/dist/types/notifications/telegram-daemon-control.d.ts +8 -0
- package/dist/types/notifications/telegram-reference.d.ts +4 -0
- package/dist/types/notifications/threaded-render.d.ts +1 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +8 -5
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +2 -0
- package/dist/types/utils/cmux-workspace.d.ts +52 -0
- package/dist/types/web/search/index.d.ts +4 -2
- package/dist/types/web/search/provider.d.ts +14 -0
- package/dist/types/web/search/providers/utils.d.ts +42 -13
- package/dist/types/web/search/render.d.ts +1 -0
- package/package.json +7 -7
- package/src/cli/update-cli.ts +24 -0
- package/src/cli/web-search-cli.ts +5 -7
- package/src/cli.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commands/ralplan.ts +1 -0
- package/src/config/keybindings.ts +14 -5
- package/src/config/model-profiles.ts +16 -0
- package/src/config/settings-schema.ts +11 -1
- package/src/coordinator-mcp/server.ts +9 -3
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +26 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +16 -14
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +37 -112
- package/src/defaults/gjc-defaults.ts +16 -0
- package/src/extensibility/extensions/runner.ts +2 -1
- package/src/extensibility/extensions/types.ts +11 -0
- package/src/gjc-runtime/launch-tmux.ts +209 -26
- package/src/gjc-runtime/ralplan-runtime.ts +22 -6
- package/src/gjc-runtime/restricted-role-agent-bash.ts +1 -0
- package/src/gjc-runtime/state-runtime.ts +8 -5
- package/src/gjc-runtime/state-writer.ts +5 -5
- package/src/gjc-runtime/ultragoal-guard.ts +139 -19
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -124
- package/src/gjc-runtime/workflow-manifest.generated.json +0 -8
- package/src/gjc-runtime/workflow-manifest.ts +0 -1
- package/src/hooks/skill-state.ts +33 -73
- package/src/internal-urls/docs-index.generated.ts +6 -4
- package/src/main.ts +2 -2
- package/src/migrate/executor.ts +23 -6
- package/src/modes/components/custom-editor.ts +8 -0
- package/src/modes/components/hook-selector.ts +21 -3
- package/src/modes/components/model-selector.ts +6 -2
- package/src/modes/components/settings-selector.ts +47 -5
- package/src/modes/components/skill-message.ts +18 -10
- package/src/modes/components/status-line/presets.ts +11 -0
- package/src/modes/controllers/command-controller.ts +23 -0
- package/src/modes/controllers/extension-ui-controller.ts +18 -2
- package/src/modes/controllers/input-controller.ts +41 -9
- package/src/modes/interactive-mode.ts +16 -7
- package/src/modes/prompt-action-autocomplete.ts +23 -1
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +53 -9
- package/src/modes/slash-command-visibility.ts +9 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/hotkeys-markdown.ts +5 -2
- package/src/modes/utils/ui-helpers.ts +76 -3
- package/src/notifications/html-format.ts +75 -1
- package/src/notifications/index.ts +12 -1
- package/src/notifications/recent-activity.ts +32 -8
- package/src/notifications/telegram-daemon-control.ts +9 -0
- package/src/notifications/telegram-daemon.ts +68 -25
- package/src/notifications/telegram-reference.ts +36 -14
- package/src/notifications/threaded-render.ts +14 -6
- package/src/prompts/agents/architect.md +7 -4
- package/src/prompts/agents/critic.md +15 -6
- package/src/prompts/agents/planner.md +8 -3
- package/src/prompts/system/system-prompt.md +5 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/sdk.ts +6 -11
- package/src/session/agent-session.ts +60 -17
- package/src/slash-commands/builtin-registry.ts +30 -10
- package/src/slash-commands/types.ts +2 -0
- package/src/tools/ask.ts +136 -16
- package/src/tools/bash.ts +24 -8
- package/src/utils/cmux-workspace.ts +233 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/kagi.ts +1 -1
- package/src/web/parallel.ts +2 -2
- package/src/web/search/index.ts +137 -52
- package/src/web/search/provider.ts +80 -0
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/web/search/providers/brave.ts +1 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/gemini.ts +7 -3
- package/src/web/search/providers/jina.ts +1 -1
- package/src/web/search/providers/kimi.ts +6 -1
- package/src/web/search/providers/openai-compatible.ts +1 -1
- package/src/web/search/providers/parallel.ts +1 -1
- package/src/web/search/providers/perplexity.ts +2 -2
- package/src/web/search/providers/searxng.ts +1 -1
- package/src/web/search/providers/synthetic.ts +1 -1
- package/src/web/search/providers/tavily.ts +1 -1
- package/src/web/search/providers/utils.ts +67 -17
- package/src/web/search/providers/xai.ts +1 -1
- package/src/web/search/providers/zai.ts +1 -1
- package/src/web/search/render.ts +3 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface CmuxWorkspaceRenameCommand {
|
|
2
|
+
command: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
}
|
|
5
|
+
/** Current ownership state of a cmux workspace, read back from `cmux workspace list`. */
|
|
6
|
+
export interface CmuxWorkspaceOwnership {
|
|
7
|
+
/** cmux marks a workspace `has_custom_title` once an explicit title is set (by the user or by GJC). */
|
|
8
|
+
hasCustomTitle: boolean;
|
|
9
|
+
/** The workspace's current display title. */
|
|
10
|
+
title: string;
|
|
11
|
+
}
|
|
12
|
+
export interface CmuxWorkspaceRenameProcess {
|
|
13
|
+
exited: Promise<number>;
|
|
14
|
+
kill(): void;
|
|
15
|
+
unref(): void;
|
|
16
|
+
}
|
|
17
|
+
export interface CmuxWorkspaceTitleSyncOptions {
|
|
18
|
+
env?: NodeJS.ProcessEnv;
|
|
19
|
+
isTty?: boolean;
|
|
20
|
+
which?: (command: string) => string | null;
|
|
21
|
+
spawn?: (command: string[], options: {
|
|
22
|
+
env: NodeJS.ProcessEnv;
|
|
23
|
+
stdin: "ignore";
|
|
24
|
+
stdout: "ignore";
|
|
25
|
+
stderr: "ignore";
|
|
26
|
+
}) => CmuxWorkspaceRenameProcess;
|
|
27
|
+
/** Reads the current ownership state of `workspaceId`. Injectable for tests. */
|
|
28
|
+
readOwnership?: (cmuxCommand: string, workspaceId: string, env: NodeJS.ProcessEnv) => Promise<CmuxWorkspaceOwnership | null>;
|
|
29
|
+
}
|
|
30
|
+
export declare function sanitizeCmuxWorkspaceTitle(title: string | undefined): string | undefined;
|
|
31
|
+
export declare function formatCmuxWorkspaceTitle(title: string | undefined): string | undefined;
|
|
32
|
+
export declare function buildCmuxWorkspaceRenameCommand(sessionName: string | undefined, env?: NodeJS.ProcessEnv): CmuxWorkspaceRenameCommand | null;
|
|
33
|
+
/** Parse `cmux workspace list --json --id-format both` output and return the
|
|
34
|
+
* ownership state of the workspace matching `workspaceId` (by UUID `id` or `ref`). */
|
|
35
|
+
export declare function parseCmuxWorkspaceOwnership(jsonText: string, workspaceId: string): CmuxWorkspaceOwnership | null;
|
|
36
|
+
/** Only rename when GJC owns the name:
|
|
37
|
+
* - unknown ownership (read failed) → skip (fail safe, never clobber)
|
|
38
|
+
* - already the desired title → skip (no-op)
|
|
39
|
+
* - workspace still on its default title → rename
|
|
40
|
+
* - any custom title (user- or peer-set) → skip
|
|
41
|
+
* This makes GJC name a fresh workspace once and then leave it alone, so it
|
|
42
|
+
* never overwrites a user-pinned name and multiple sessions sharing one
|
|
43
|
+
* CMUX_WORKSPACE_ID do not thrash the workspace title. */
|
|
44
|
+
export declare function shouldRenameCmuxWorkspace(ownership: CmuxWorkspaceOwnership | null, desiredTitle: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Best-effort sync of the containing cmux workspace title to the current GJC
|
|
47
|
+
* session name. Ownership-guarded: GJC reads the current workspace title and
|
|
48
|
+
* only renames a workspace that still has its default title, so it never
|
|
49
|
+
* overwrites a name the user pinned or a name a peer session (sharing the same
|
|
50
|
+
* CMUX_WORKSPACE_ID) set. Opt out with GJC_NO_CMUX_RENAME.
|
|
51
|
+
*/
|
|
52
|
+
export declare function syncCmuxWorkspaceTitle(sessionName: string | undefined, options?: CmuxWorkspaceTitleSyncOptions): Promise<void>;
|
|
@@ -44,6 +44,8 @@ export type SearchToolParams = z.infer<typeof webSearchSchema>;
|
|
|
44
44
|
export interface SearchQueryParams extends SearchToolParams {
|
|
45
45
|
provider?: SearchProviderId | "auto";
|
|
46
46
|
}
|
|
47
|
+
/** Override the hedge delay (tests). Non-finite/non-positive resets to default. */
|
|
48
|
+
export declare function setDdgHedgeDelayMs(ms: number | undefined): void;
|
|
47
49
|
/**
|
|
48
50
|
* Execute a web search query for CLI/testing workflows.
|
|
49
51
|
*
|
|
@@ -110,7 +112,7 @@ export declare class WebSearchTool implements AgentTool<typeof webSearchSchema,
|
|
|
110
112
|
/** Web search tool as CustomTool (for TUI rendering support) */
|
|
111
113
|
export declare const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRenderDetails>;
|
|
112
114
|
export declare function getSearchTools(): CustomTool<any, any>[];
|
|
113
|
-
export { getSearchProvider, setPreferredSearchProvider, setSearchFallbackProviders } from "./provider";
|
|
114
|
-
export { setSearchHardTimeoutMs } from "./providers/utils";
|
|
115
|
+
export { getConfiguredSearchProviderPreference, getSearchProvider, setPreferredSearchProvider, setSearchFallbackProviders, } from "./provider";
|
|
116
|
+
export { applyConfiguredSearchTimeout, setSearchHardTimeoutMs } from "./providers/utils";
|
|
115
117
|
export type { SearchProviderId as SearchProvider, SearchResponse } from "./types";
|
|
116
118
|
export { isConfigurableSearchProviderId, isSearchProviderPreference } from "./types";
|
|
@@ -6,8 +6,22 @@ export { SearchProvider } from "./providers/base";
|
|
|
6
6
|
export declare function getSearchProviderLabel(id: SearchProviderId): string;
|
|
7
7
|
export declare function getSearchProvider(id: SearchProviderId): Promise<SearchProvider>;
|
|
8
8
|
export declare const SEARCH_PROVIDER_ORDER: SearchProviderId[];
|
|
9
|
+
/** Drop every cached resolved chain (settings changed, tests). */
|
|
10
|
+
export declare function clearResolvedChainCache(): void;
|
|
9
11
|
export declare function setPreferredSearchProvider(provider: SearchProviderId | "auto"): void;
|
|
10
12
|
export declare function setSearchFallbackProviders(ids: readonly string[]): void;
|
|
13
|
+
/**
|
|
14
|
+
* Fire-and-forget warm-up: resolve the provider chain once so the provider
|
|
15
|
+
* modules are imported and the chain cache is primed before the first
|
|
16
|
+
* user-visible search. Errors are intentionally swallowed — prewarming must
|
|
17
|
+
* never surface a failure the real search path would handle itself.
|
|
18
|
+
*/
|
|
19
|
+
export declare function prewarmSearchProviders(options: ResolveProviderChainOptions): void;
|
|
20
|
+
export interface SearchProviderPreferenceSource {
|
|
21
|
+
get(path: "providers.webSearch" | "web_search.provider"): SearchProviderId | "auto";
|
|
22
|
+
has(path: "providers.webSearch" | "web_search.provider"): boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function getConfiguredSearchProviderPreference(settings: SearchProviderPreferenceSource): SearchProviderId | "auto";
|
|
11
25
|
export interface ResolveProviderChainOptions {
|
|
12
26
|
authStorage: AuthStorage;
|
|
13
27
|
sessionId?: string;
|
|
@@ -15,27 +15,53 @@ import { SearchProviderError, type SearchProviderId, type SearchSource } from ".
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function findCredential(storage: AgentStorage | null | undefined, envKey: string | null | undefined, ...storageProviders: string[]): string | null;
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* Pure search APIs (brave, exa, jina, tavily, searxng, synthetic, zai)
|
|
24
|
-
* settle far faster in practice; reusing the same ceiling keeps the wiring
|
|
25
|
-
* uniform without compromising correctness.
|
|
18
|
+
* Legacy hard ceiling for a single web-search round-trip, retained as the
|
|
19
|
+
* fallback for call sites that do not declare a timeout class. 300s tolerates
|
|
20
|
+
* pathological cases while still guaranteeing the session unfreezes if Bun's
|
|
21
|
+
* `AbortSignal` fails to propagate on Windows.
|
|
26
22
|
*/
|
|
27
23
|
export declare const SEARCH_HARD_TIMEOUT_MS = 300000;
|
|
24
|
+
/**
|
|
25
|
+
* Hard ceiling for pure search APIs (brave, exa, jina, tavily, kagi,
|
|
26
|
+
* parallel, searxng, synthetic, zai, duckduckgo). These settle in ~1-3s in
|
|
27
|
+
* practice; a hung TCP/TLS connection should fall through to the next
|
|
28
|
+
* provider in seconds, not minutes. Kimi is the exception: it declares an
|
|
29
|
+
* explicit ceiling aligned with its upstream 30s request budget.
|
|
30
|
+
*/
|
|
31
|
+
export declare const SEARCH_API_TIMEOUT_MS = 15000;
|
|
32
|
+
/**
|
|
33
|
+
* Hard ceiling for LLM-mediated search providers (anthropic, codex, gemini,
|
|
34
|
+
* perplexity, xai, openai-compatible). These legitimately take tens of
|
|
35
|
+
* seconds because a model performs the search and synthesizes an answer.
|
|
36
|
+
*/
|
|
37
|
+
export declare const SEARCH_LLM_TIMEOUT_MS = 120000;
|
|
38
|
+
/** Timeout class a provider declares for its outbound round-trips. */
|
|
39
|
+
export type SearchTimeoutClass = "api" | "llm";
|
|
28
40
|
/**
|
|
29
41
|
* Override the hard timeout applied to every web-search round-trip.
|
|
30
42
|
*
|
|
31
43
|
* @param ms - Hard timeout in milliseconds. Non-finite or non-positive
|
|
32
|
-
* values
|
|
44
|
+
* values clear the override so per-class defaults apply.
|
|
33
45
|
*/
|
|
34
46
|
export declare function setSearchHardTimeoutMs(ms: number | undefined): void;
|
|
47
|
+
/** Structural settings source for {@link applyConfiguredSearchTimeout}. */
|
|
48
|
+
export interface SearchTimeoutSettingSource {
|
|
49
|
+
get(path: "web_search.timeout"): unknown;
|
|
50
|
+
has(path: "web_search.timeout"): boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Install the `web_search.timeout` override from settings — but only when the
|
|
54
|
+
* user explicitly configured it. The settings schema ships a default value
|
|
55
|
+
* (300), and consuming that default via `get()` alone would reinstall a
|
|
56
|
+
* uniform 300s ceiling on every session, silently disabling the per-class
|
|
57
|
+
* timeout defaults. `has()` distinguishes loaded/overridden settings from
|
|
58
|
+
* schema defaults; unset clears any previous override so class defaults apply.
|
|
59
|
+
*/
|
|
60
|
+
export declare function applyConfiguredSearchTimeout(settings: SearchTimeoutSettingSource): void;
|
|
35
61
|
/**
|
|
36
62
|
* Compose a caller-supplied {@link AbortSignal} with a hard timeout so an
|
|
37
|
-
* outbound `fetch()` is guaranteed to settle
|
|
38
|
-
*
|
|
63
|
+
* outbound `fetch()` is guaranteed to settle even when the runtime fails to
|
|
64
|
+
* propagate cancellation to the underlying transport.
|
|
39
65
|
*
|
|
40
66
|
* Bun's WinHTTP backend on Windows is known to ignore `AbortSignal` once a
|
|
41
67
|
* TCP/TLS connection stalls (oven-sh/bun#15275, oven-sh/bun#18536); without
|
|
@@ -43,9 +69,12 @@ export declare function setSearchHardTimeoutMs(ms: number | undefined): void;
|
|
|
43
69
|
* because the user's Esc is never delivered to the native layer.
|
|
44
70
|
*
|
|
45
71
|
* @param signal - Caller cancellation signal, if any.
|
|
46
|
-
* @param
|
|
72
|
+
* @param msOrClass - Explicit timeout in milliseconds, or a
|
|
73
|
+
* {@link SearchTimeoutClass} resolved to its class default. Omitted falls
|
|
74
|
+
* back to the legacy {@link SEARCH_HARD_TIMEOUT_MS} ceiling. A user-set
|
|
75
|
+
* `web_search.timeout` overrides class defaults (but not explicit ms).
|
|
47
76
|
*/
|
|
48
|
-
export declare function withHardTimeout(signal: AbortSignal | undefined,
|
|
77
|
+
export declare function withHardTimeout(signal: AbortSignal | undefined, msOrClass?: number | SearchTimeoutClass): AbortSignal;
|
|
49
78
|
/**
|
|
50
79
|
* Map a provider's raw source list to the unified SearchSource shape,
|
|
51
80
|
* clamped to the requested result count and annotated with ageSeconds.
|
|
@@ -10,6 +10,7 @@ import type { SearchResponse } from "./types";
|
|
|
10
10
|
export interface SearchRenderDetails {
|
|
11
11
|
response: SearchResponse;
|
|
12
12
|
error?: string;
|
|
13
|
+
warning?: string;
|
|
13
14
|
}
|
|
14
15
|
/** Render web search result with tree-based layout */
|
|
15
16
|
export declare function renderSearchResult(result: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.11",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gaebal-gajae.dev",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
53
53
|
"@babel/parser": "^7.29.3",
|
|
54
54
|
"@mozilla/readability": "^0.6.0",
|
|
55
|
-
"@gajae-code/stats": "0.7.
|
|
56
|
-
"@gajae-code/agent-core": "0.7.
|
|
57
|
-
"@gajae-code/ai": "0.7.
|
|
58
|
-
"@gajae-code/natives": "0.7.
|
|
59
|
-
"@gajae-code/tui": "0.7.
|
|
60
|
-
"@gajae-code/utils": "0.7.
|
|
55
|
+
"@gajae-code/stats": "0.7.11",
|
|
56
|
+
"@gajae-code/agent-core": "0.7.11",
|
|
57
|
+
"@gajae-code/ai": "0.7.11",
|
|
58
|
+
"@gajae-code/natives": "0.7.11",
|
|
59
|
+
"@gajae-code/tui": "0.7.11",
|
|
60
|
+
"@gajae-code/utils": "0.7.11",
|
|
61
61
|
"@puppeteer/browsers": "^2.13.0",
|
|
62
62
|
"@types/turndown": "5.0.6",
|
|
63
63
|
"@xterm/headless": "^6.0.0",
|
package/src/cli/update-cli.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { pipeline } from "node:stream/promises";
|
|
|
10
10
|
import { $which, APP_NAME, isEnoent, VERSION } from "@gajae-code/utils";
|
|
11
11
|
import { $ } from "bun";
|
|
12
12
|
import chalk from "chalk";
|
|
13
|
+
import { installDefaultGjcDefinitions } from "../defaults/gjc-defaults";
|
|
13
14
|
import { theme } from "../modes/theme/theme";
|
|
14
15
|
|
|
15
16
|
const RELEASE_REPO = "Yeachan-Heo/gajae-code";
|
|
@@ -628,6 +629,29 @@ export async function runUpdateCommand(opts: { force: boolean; check: boolean })
|
|
|
628
629
|
console.error(chalk.red(`Update failed: ${err}`));
|
|
629
630
|
process.exit(1);
|
|
630
631
|
}
|
|
632
|
+
|
|
633
|
+
await refreshInstalledDefaultSkills();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Refresh opted-in on-disk default workflow skill copies after a successful
|
|
638
|
+
* update. The four default skills ship embedded in the binary, so most users
|
|
639
|
+
* need nothing here. But users who ran `gjc setup defaults` have on-disk copies
|
|
640
|
+
* under the agent dir that shadow the embedded defaults; those would otherwise
|
|
641
|
+
* go stale after an update. Only rewrite files that already exist and differ —
|
|
642
|
+
* never materialize new copies for users who never opted in.
|
|
643
|
+
*/
|
|
644
|
+
async function refreshInstalledDefaultSkills(): Promise<void> {
|
|
645
|
+
try {
|
|
646
|
+
const result = await installDefaultGjcDefinitions({ refreshOnly: true });
|
|
647
|
+
if (result.written > 0) {
|
|
648
|
+
console.log(
|
|
649
|
+
chalk.dim(`Refreshed ${result.written} local default workflow skill file(s) at ${result.targetRoot}`),
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
} catch (err) {
|
|
653
|
+
console.error(chalk.yellow(`Warning: failed to refresh local default workflow skills: ${err}`));
|
|
654
|
+
}
|
|
631
655
|
}
|
|
632
656
|
|
|
633
657
|
/**
|
|
@@ -9,13 +9,14 @@ import chalk from "chalk";
|
|
|
9
9
|
import { Settings } from "../config/settings";
|
|
10
10
|
import { initTheme, theme } from "../modes/theme/theme";
|
|
11
11
|
import {
|
|
12
|
+
getConfiguredSearchProviderPreference,
|
|
12
13
|
isConfigurableSearchProviderId,
|
|
13
14
|
isSearchProviderPreference,
|
|
14
15
|
runSearchQuery,
|
|
15
16
|
type SearchQueryParams,
|
|
16
17
|
} from "../web/search/index";
|
|
17
18
|
import { SEARCH_PROVIDER_ORDER, setPreferredSearchProvider, setSearchFallbackProviders } from "../web/search/provider";
|
|
18
|
-
import {
|
|
19
|
+
import { applyConfiguredSearchTimeout } from "../web/search/providers/utils";
|
|
19
20
|
import { renderSearchResult } from "../web/search/render";
|
|
20
21
|
import type { SearchProviderId } from "../web/search/types";
|
|
21
22
|
|
|
@@ -170,8 +171,8 @@ export async function runSearchCommand(cmd: SearchCommandArgs): Promise<void> {
|
|
|
170
171
|
|
|
171
172
|
await initTheme();
|
|
172
173
|
const settings = await Settings.init();
|
|
173
|
-
const configuredProvider = settings
|
|
174
|
-
if (
|
|
174
|
+
const configuredProvider = getConfiguredSearchProviderPreference(settings);
|
|
175
|
+
if (isSearchProviderPreference(configuredProvider)) {
|
|
175
176
|
setPreferredSearchProvider(configuredProvider);
|
|
176
177
|
}
|
|
177
178
|
const configuredFallback = settings.get("web_search.fallback");
|
|
@@ -180,10 +181,7 @@ export async function runSearchCommand(cmd: SearchCommandArgs): Promise<void> {
|
|
|
180
181
|
configuredFallback.filter(value => typeof value === "string" && isConfigurableSearchProviderId(value)),
|
|
181
182
|
);
|
|
182
183
|
}
|
|
183
|
-
|
|
184
|
-
if (typeof configuredTimeout === "number" && Number.isFinite(configuredTimeout) && configuredTimeout > 0) {
|
|
185
|
-
setSearchHardTimeoutMs(configuredTimeout * 1000);
|
|
186
|
-
}
|
|
184
|
+
applyConfiguredSearchTimeout(settings);
|
|
187
185
|
|
|
188
186
|
const params: SearchQueryParams = {
|
|
189
187
|
query: cmd.query,
|
package/src/cli.ts
CHANGED
|
@@ -141,7 +141,7 @@ class RootHelpCommand extends Command {
|
|
|
141
141
|
resume: Flags.string({ char: "r", description: "Resume a session (by ID prefix, path, or picker if omitted)" }),
|
|
142
142
|
"session-dir": Flags.string({ description: "Directory for session storage and lookup" }),
|
|
143
143
|
"no-session": Flags.boolean({ description: "Don't save session (ephemeral)" }),
|
|
144
|
-
models: Flags.string({ description: "Comma-separated model patterns for
|
|
144
|
+
models: Flags.string({ description: "Comma-separated model patterns for Alt+N cycling" }),
|
|
145
145
|
"no-tools": Flags.boolean({ description: "Disable all built-in tools" }),
|
|
146
146
|
"no-lsp": Flags.boolean({ description: "Disable LSP tools, formatting, and diagnostics" }),
|
|
147
147
|
"no-pty": Flags.boolean({ description: "Disable PTY-based interactive bash execution" }),
|
package/src/commands/launch.ts
CHANGED
|
@@ -119,7 +119,7 @@ export default class Index extends Command {
|
|
|
119
119
|
description: "Don't save session (ephemeral)",
|
|
120
120
|
}),
|
|
121
121
|
models: Flags.string({
|
|
122
|
-
description: "Comma-separated model patterns for
|
|
122
|
+
description: "Comma-separated model patterns for Alt+N cycling",
|
|
123
123
|
}),
|
|
124
124
|
"no-tools": Flags.boolean({
|
|
125
125
|
description: "Disable all built-in tools",
|
package/src/commands/ralplan.ts
CHANGED
|
@@ -8,6 +8,7 @@ export default class Ralplan extends Command {
|
|
|
8
8
|
'$ gjc ralplan "<task description>"',
|
|
9
9
|
'$ gjc ralplan --interactive --deliberate "<task description>"',
|
|
10
10
|
'$ gjc ralplan --write --stage planner --stage_n 1 --artifact "<markdown or path>"',
|
|
11
|
+
"$ gjc ralplan --write --stage critic --stage_n 1 --artifact-env GJC_RALPLAN_ARTIFACT",
|
|
11
12
|
];
|
|
12
13
|
|
|
13
14
|
async run(): Promise<void> {
|
|
@@ -22,6 +22,7 @@ interface AppKeybindings {
|
|
|
22
22
|
"app.suspend": true;
|
|
23
23
|
"app.thinking.cycle": true;
|
|
24
24
|
"app.thinking.toggle": true;
|
|
25
|
+
"app.commandPalette.open": true;
|
|
25
26
|
"app.model.cycleForward": true;
|
|
26
27
|
"app.model.cycleBackward": true;
|
|
27
28
|
"app.model.select": true;
|
|
@@ -59,6 +60,10 @@ declare module "@gajae-code/tui" {
|
|
|
59
60
|
interface Keybindings extends AppKeybindings {}
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
export function defaultMessageQueueKeysForPlatform(platform: NodeJS.Platform = process.platform): KeyId {
|
|
64
|
+
return platform === "win32" ? "alt+q" : "alt+enter";
|
|
65
|
+
}
|
|
66
|
+
|
|
62
67
|
/**
|
|
63
68
|
* All keybindings definitions: TUI + app-specific.
|
|
64
69
|
*/
|
|
@@ -88,12 +93,16 @@ export const KEYBINDINGS = {
|
|
|
88
93
|
defaultKeys: "ctrl+t",
|
|
89
94
|
description: "Toggle thinking mode",
|
|
90
95
|
},
|
|
91
|
-
"app.
|
|
96
|
+
"app.commandPalette.open": {
|
|
92
97
|
defaultKeys: "ctrl+p",
|
|
98
|
+
description: "Open command palette",
|
|
99
|
+
},
|
|
100
|
+
"app.model.cycleForward": {
|
|
101
|
+
defaultKeys: "alt+n",
|
|
93
102
|
description: "Cycle to next model",
|
|
94
103
|
},
|
|
95
104
|
"app.model.cycleBackward": {
|
|
96
|
-
defaultKeys: "shift+
|
|
105
|
+
defaultKeys: "alt+shift+n",
|
|
97
106
|
description: "Cycle to previous model",
|
|
98
107
|
},
|
|
99
108
|
"app.model.select": {
|
|
@@ -121,7 +130,7 @@ export const KEYBINDINGS = {
|
|
|
121
130
|
description: "Send follow-up message (no default; Ctrl+Enter submits)",
|
|
122
131
|
},
|
|
123
132
|
"app.message.queue": {
|
|
124
|
-
defaultKeys:
|
|
133
|
+
defaultKeys: defaultMessageQueueKeysForPlatform(),
|
|
125
134
|
description: "Queue message for next turn",
|
|
126
135
|
},
|
|
127
136
|
"app.message.dequeue": {
|
|
@@ -141,8 +150,8 @@ export const KEYBINDINGS = {
|
|
|
141
150
|
description: "Copy prompt",
|
|
142
151
|
},
|
|
143
152
|
"app.session.new": {
|
|
144
|
-
defaultKeys:
|
|
145
|
-
description: "
|
|
153
|
+
defaultKeys: "ctrl+n",
|
|
154
|
+
description: "Start a new session",
|
|
146
155
|
},
|
|
147
156
|
"app.session.tree": {
|
|
148
157
|
defaultKeys: [],
|
|
@@ -104,6 +104,13 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
104
104
|
critic: "anthropic/claude-opus-4-8:high",
|
|
105
105
|
architect: "anthropic/claude-opus-4-8:xhigh",
|
|
106
106
|
}),
|
|
107
|
+
profile("claude-fable", ["anthropic"], {
|
|
108
|
+
default: "anthropic/claude-fable-5:xhigh",
|
|
109
|
+
executor: "anthropic/claude-sonnet-5",
|
|
110
|
+
planner: "anthropic/claude-fable-5:low",
|
|
111
|
+
critic: "anthropic/claude-fable-5:high",
|
|
112
|
+
architect: "anthropic/claude-fable-5:xhigh",
|
|
113
|
+
}),
|
|
107
114
|
profile("glm-eco", ["zai"], {
|
|
108
115
|
default: "zai/glm-5.2:low",
|
|
109
116
|
executor: "zai/glm-5.2:minimal",
|
|
@@ -261,6 +268,13 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
|
|
|
261
268
|
critic: "opencode-go/mimo-v2.5-pro",
|
|
262
269
|
architect: "openai-codex/gpt-5.5:xhigh",
|
|
263
270
|
}),
|
|
271
|
+
profile("fable-opus-codex", ["anthropic", "openai-codex"], {
|
|
272
|
+
default: "anthropic/claude-fable-5:high",
|
|
273
|
+
executor: "openai-codex/gpt-5.5:high",
|
|
274
|
+
planner: "anthropic/claude-opus-4-8:medium",
|
|
275
|
+
critic: "anthropic/claude-opus-4-8:high",
|
|
276
|
+
architect: "openai-codex/gpt-5.5:xhigh",
|
|
277
|
+
}),
|
|
264
278
|
];
|
|
265
279
|
|
|
266
280
|
export interface ModelProfilePresentation {
|
|
@@ -278,6 +292,7 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
|
|
|
278
292
|
"codex-pro": { displayName: "Codex Pro", providerGroup: "CODEX" },
|
|
279
293
|
opencodego: { displayName: "OpenCodeGo", providerGroup: "OPENCODEGO" },
|
|
280
294
|
"claude-opus": { displayName: "Claude Opus", providerGroup: "CLAUDE" },
|
|
295
|
+
"claude-fable": { displayName: "Claude Fable", providerGroup: "CLAUDE" },
|
|
281
296
|
"glm-eco": { displayName: "GLM Eco", providerGroup: "GLM" },
|
|
282
297
|
"glm-medium": { displayName: "GLM Medium", providerGroup: "GLM" },
|
|
283
298
|
"glm-pro": { displayName: "GLM Pro", providerGroup: "GLM" },
|
|
@@ -299,6 +314,7 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
|
|
|
299
314
|
"minimax-pro": { displayName: "MiniMax Pro", providerGroup: "MINIMAX" },
|
|
300
315
|
"opus-codex": { displayName: "Opus + Codex", providerGroup: "COMBOS" },
|
|
301
316
|
"codex-opencodego": { displayName: "Codex + OpenCodeGo", providerGroup: "COMBOS" },
|
|
317
|
+
"fable-opus-codex": { displayName: "Fable + Opus + Codex", providerGroup: "COMBOS" },
|
|
302
318
|
};
|
|
303
319
|
|
|
304
320
|
const PROFILE_GROUP_ORDER = [
|
|
@@ -455,7 +455,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
455
455
|
// Status line
|
|
456
456
|
"statusLine.preset": {
|
|
457
457
|
type: "enum",
|
|
458
|
-
values: ["default", "minimal", "compact", "full", "nerd", "ascii", "custom"] as const,
|
|
458
|
+
values: ["default", "default-usage", "minimal", "compact", "full", "nerd", "ascii", "custom"] as const,
|
|
459
459
|
default: "default",
|
|
460
460
|
ui: {
|
|
461
461
|
tab: "appearance",
|
|
@@ -463,6 +463,11 @@ export const SETTINGS_SCHEMA = {
|
|
|
463
463
|
description: "Pre-built status line configurations",
|
|
464
464
|
options: [
|
|
465
465
|
{ value: "default", label: "Default", description: "Model, path, git, context, tokens, cost" },
|
|
466
|
+
{
|
|
467
|
+
value: "default-usage",
|
|
468
|
+
label: "Default + Usage",
|
|
469
|
+
description: "Default layout with provider usage quota",
|
|
470
|
+
},
|
|
466
471
|
{ value: "minimal", label: "Minimal", description: "Path and git only" },
|
|
467
472
|
{ value: "compact", label: "Compact", description: "Model, git, cost, context" },
|
|
468
473
|
{ value: "full", label: "Full", description: "All segments including time" },
|
|
@@ -2205,6 +2210,11 @@ export const SETTINGS_SCHEMA = {
|
|
|
2205
2210
|
ui: { tab: "tools", label: "Web Search", description: "Enable the web_search tool for web searching" },
|
|
2206
2211
|
},
|
|
2207
2212
|
|
|
2213
|
+
"web_search.provider": {
|
|
2214
|
+
type: "enum",
|
|
2215
|
+
values: ["auto", ...CONFIGURABLE_SEARCH_PROVIDER_IDS] as const,
|
|
2216
|
+
default: "auto",
|
|
2217
|
+
},
|
|
2208
2218
|
"web_search.fallback": {
|
|
2209
2219
|
type: "array",
|
|
2210
2220
|
default: EMPTY_STRING_ARRAY,
|
|
@@ -1273,9 +1273,15 @@ async function sendTmuxPromptKeys(
|
|
|
1273
1273
|
prompt: string,
|
|
1274
1274
|
runner: CommandRunner = runCommand,
|
|
1275
1275
|
): Promise<boolean> {
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1276
|
+
const bufferName = `gjc-coordinator-prompt-${randomUUID()}`;
|
|
1277
|
+
const buffered = await runner(["tmux", "set-buffer", "-b", bufferName, "--", prompt]);
|
|
1278
|
+
if (buffered.exitCode !== 0) return false;
|
|
1279
|
+
const pasted = await runner(["tmux", "paste-buffer", "-d", "-b", bufferName, "-t", target]);
|
|
1280
|
+
if (pasted.exitCode !== 0) {
|
|
1281
|
+
await runner(["tmux", "delete-buffer", "-b", bufferName]);
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
const submitted = await runner(["tmux", "send-keys", "-t", target, "Enter"]);
|
|
1279
1285
|
return submitted.exitCode === 0;
|
|
1280
1286
|
}
|
|
1281
1287
|
|
|
@@ -21,13 +21,14 @@ Deep Interview implements Ouroboros-inspired Socratic questioning with mathemati
|
|
|
21
21
|
- User wants to avoid "that's not what I meant" outcomes from autonomous execution
|
|
22
22
|
- Task is complex enough that jumping to code would waste cycles on scope discovery
|
|
23
23
|
- User wants mathematically-validated clarity before committing to execution
|
|
24
|
+
- User explicitly requests deep-interview even after being told the request is already clear, bounded, and low-risk
|
|
24
25
|
</Use_When>
|
|
25
26
|
|
|
26
27
|
<Do_Not_Use_When>
|
|
27
28
|
- User has a detailed, specific request with file paths, function names, or acceptance criteria -- execute directly
|
|
28
29
|
- User wants to explore options or brainstorm -- use `ralplan` skill instead
|
|
29
|
-
- User wants a quick fix or single change --
|
|
30
|
-
- User says "just do it" or "skip the questions" without an explicit execution path -- respect their intent by
|
|
30
|
+
- User wants a quick fix or single change -- use direct execution, not deep-interview or role-agent delegation
|
|
31
|
+
- User says "just do it" or "skip the questions" without an explicit execution path -- respect their intent by exiting deep-interview, not by writing a `pending approval` spec
|
|
31
32
|
- User already has a PRD or plan file and explicitly asks to execute it -- use the requested execution skill with that plan
|
|
32
33
|
</Do_Not_Use_When>
|
|
33
34
|
|
|
@@ -111,6 +112,27 @@ Deep Interview threshold: <resolvedThresholdPercent> (source: <resolvedThreshold
|
|
|
111
112
|
- Include both threshold and source in the final spec metadata.
|
|
112
113
|
- Read any `language` object from active deep-interview state and carry `language.instruction` forward mechanically. If absent, default to English unless `{{ARGUMENTS}}` makes another user/session language obvious or the user explicitly requests another language. Do not add language-specific special cases.
|
|
113
114
|
|
|
115
|
+
## Phase 0.5: Suitability Gate
|
|
116
|
+
|
|
117
|
+
Run this gate after the Phase 0 threshold marker and before Phase 1, brownfield exploration, `gjc state write`, Round 0, ambiguity scoring, or spec writing.
|
|
118
|
+
|
|
119
|
+
If `{{ARGUMENTS}}` is already clear, bounded, low-risk, and asks for a quick fix, single change, known file/symbol edit, explicit command, or direct answer:
|
|
120
|
+
|
|
121
|
+
1. **Stop deep-interview immediately**:
|
|
122
|
+
- First inspect current-session state with `gjc state read --mode deep-interview --json` (include `--session-id <current-session-id>` when available).
|
|
123
|
+
- Clear through `gjc state clear --force --mode deep-interview --json` only when the state is a newly seeded empty interview: no recorded `rounds`, no `spec_path`, no `handoff_from`, no final/pending spec, and no user-confirmed topology.
|
|
124
|
+
- If state already contains rounds, a spec path, handoff metadata, pending approval, or confirmed topology, do not clear it. Preserve the active interview and ask the user whether to continue, cancel, or explicitly clear the workflow.
|
|
125
|
+
- Do not initialize deep-interview state.
|
|
126
|
+
- Do not run Round 0.
|
|
127
|
+
- Do not write a pending-approval spec.
|
|
128
|
+
- Do not hand off to `ralplan`, `ultragoal`, `team`, or a role agent.
|
|
129
|
+
2. **Return the request to direct implementation**:
|
|
130
|
+
- Say briefly that deep-interview is unnecessary because the request is already clear and small.
|
|
131
|
+
- State the direct implementation path the normal coding agent should take.
|
|
132
|
+
- If the user explicitly insists on deep-interview anyway, continue to Phase 1.
|
|
133
|
+
|
|
134
|
+
This gate exists to prevent deep-interview from making easy problems harder. A small verification need does not make a request interview-worthy.
|
|
135
|
+
|
|
114
136
|
## Phase 1: Initialize
|
|
115
137
|
|
|
116
138
|
1. **Parse the user's idea** from `{{ARGUMENTS}}`
|
|
@@ -314,6 +336,8 @@ After applying `language.instruction` to the visible question, options, and gene
|
|
|
314
336
|
|
|
315
337
|
When calling `ask`, SHOULD include optional structured metadata so the runtime can record the round without manual state writes: `deepInterview.round_id?`, `deepInterview.round`, `deepInterview.component`, `deepInterview.dimension`, and `deepInterview.ambiguity`. Keep this metadata aligned with the visible Round/Component/Targeting/Ambiguity line; if metadata cannot be supplied, the legacy formatted question text remains the fallback.
|
|
316
338
|
|
|
339
|
+
If the `ask` tool returns `clarificationQuestion`, treat it as a non-answer about the displayed choices. Answer the clarification briefly from the current interview context, then call `ask` again with the exact original question, options, and `deepInterview.*` metadata. A clarification bypasses Step 2b′ auto-answer, Step 2b″ free-text refine, Step 2c ambiguity scoring, Step 2d progress reporting, and Step 2e state updates; it must not be recorded as a round answer. This does not violate the one-question-per-round rule because the round remains unresolved until the user submits a real listed option or `Other` answer.
|
|
340
|
+
|
|
317
341
|
### Step 2b′: Auto-Answer Opted-Out Questions
|
|
318
342
|
|
|
319
343
|
After the `ask` tool resolves and before ambiguity scoring, if the user opts out of answering the current question or explicitly asks the agent to decide, load `auto-answer-uncertain.md` as an internal `kind: "skill-fragment"` prompt for a fork-context architect. Pass the opted-out question, prompt-safe transcript summary, locked topology, current scores/gaps, and any auto-research candidates used for the round. The architect must return exactly one decisive answer with rationale, confidence, and explicit uncertainty. Validate the response shape before using it; if valid, record it as the tentative answer for scoring, append the round number to `auto_answered_rounds`, and mark the transcript answer as architect-assisted.
|
|
@@ -45,13 +45,15 @@ Planning artifacts and stage handoffs MUST be persisted through the ralplan CLI
|
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
47
|
gjc ralplan --write --stage <type> --stage_n <N> --artifact "markdown file path or markdown string"
|
|
48
|
+
# restricted role agents use:
|
|
49
|
+
gjc ralplan --write --stage <type> --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT
|
|
48
50
|
```
|
|
49
51
|
|
|
50
|
-
Use stage values that match the producer or artifact kind, such as `planner`, `architect`, `critic`, `revision`, `post-interview`, `adr`, or `final`. Increment `--stage_n` for each consensus-loop pass. The `--artifact` value may be either a markdown file path prepared outside `.gjc/` for ingestion or the markdown content string itself. The native `--write` handler persists markdown under `.gjc/_session-{sessionid}/plans/ralplan/<run-id>/stage-<NN>-<stage>.md`, maintains an `index.jsonl` audit log, and for `final` stages additionally writes a `pending-approval.md` copy. Direct `write`, `edit`, or `ast_edit` calls against `.gjc/_session-{sessionid}/specs`, `.gjc/_session-{sessionid}/plans`, `.gjc/_session-{sessionid}/state`, or any other `.gjc/` path are forbidden unless an explicit force override is active.
|
|
52
|
+
Use stage values that match the producer or artifact kind, such as `planner`, `architect`, `critic`, `revision`, `post-interview`, `adr`, or `final`. Increment `--stage_n` for each consensus-loop pass. The `--artifact` value may be either a markdown file path prepared outside `.gjc/` for ingestion or the markdown content string itself. The native `--write` handler also accepts `--artifact-env GJC_RALPLAN_ARTIFACT` to read markdown from that per-command env override. It persists markdown under `.gjc/_session-{sessionid}/plans/ralplan/<run-id>/stage-<NN>-<stage>.md`, maintains an `index.jsonl` audit log, and for `final` stages additionally writes a `pending-approval.md` copy. Direct `write`, `edit`, or `ast_edit` calls against `.gjc/_session-{sessionid}/specs`, `.gjc/_session-{sessionid}/plans`, `.gjc/_session-{sessionid}/state`, or any other `.gjc/` path are forbidden unless an explicit force override is active.
|
|
51
53
|
|
|
52
|
-
While ralplan is active it is a pre-approval planning phase: product-code mutation tools (`write`/`edit`/`ast_edit`) and product-mutating `bash` (e.g. `tee src/...`, redirects into the project tree) are blocked, exactly like deep-interview.
|
|
54
|
+
While ralplan is active it is a pre-approval planning phase: product-code mutation tools (`write`/`edit`/`ast_edit`) and product-mutating `bash` (e.g. `tee src/...`, redirects into the project tree) are blocked, exactly like deep-interview. Leaders may pass `--artifact` markdown inline or, when an artifact is too large to pass inline, stage it as a file in a system temp directory (`os.tmpdir()`/`$TMPDIR`, `/tmp`, `/var/tmp`) outside the project tree and pass that path — never write scratch files into the repo or `.gjc/`. Product code is mutated only after the plan is approved and execution begins.
|
|
53
55
|
|
|
54
|
-
Restricted read-only role agents (`planner`, `architect`, and `critic`) must pass markdown content
|
|
56
|
+
Restricted read-only role agents (`planner`, `architect`, and `critic`) must pass markdown content through the `GJC_RALPLAN_ARTIFACT` env override with `--artifact-env GJC_RALPLAN_ARTIFACT`; their restricted bash environment intentionally disables artifact file-path ingestion so a verdict command cannot persist arbitrary file contents.
|
|
55
57
|
|
|
56
58
|
After a role agent persists a stage artifact, its model-facing response to the caller SHOULD be receipt-only: return the `gjc ralplan --write --json` receipt (`run_id`, `path`, `stage`, `stage_n`, `sha256`, `created_at`) plus the minimal verdict/status fields the caller needs for routing, and do **not** paste the full persisted markdown back into the parent conversation. Downstream reviewers should receive the artifact path/receipt and read the persisted file themselves when they actually need the body. This preserves the audit trail while preventing Planner/Architect/Critic verdict bodies from being duplicated into the main-agent context.
|
|
57
59
|
|
|
@@ -60,7 +62,7 @@ RECEIPT-ONLY guideline: role agents (`planner`, `architect`, and `critic`) persi
|
|
|
60
62
|
This skill runs GJC planning in consensus mode for the provided arguments.
|
|
61
63
|
|
|
62
64
|
The consensus workflow:
|
|
63
|
-
1. **Planner** creates the initial plan and a compact **RALPLAN-DR summary** before review. Launch the Planner ONCE per run as a detached, resumable subagent (await it before the Architect) and record its returned subagent id as the run's persisted Planner id; persist the stage with `gjc ralplan --write --stage planner --stage_n 1 --artifact
|
|
65
|
+
1. **Planner** creates the initial plan and a compact **RALPLAN-DR summary** before review. Launch the Planner ONCE per run as a detached, resumable subagent (await it before the Architect) and record its returned subagent id as the run's persisted Planner id; persist the stage with `gjc ralplan --write --stage planner --stage_n 1 --artifact-env GJC_RALPLAN_ARTIFACT --planner-id <id> --planner-resumable <true|false>` (see **Persisted Planner** below):
|
|
64
66
|
- After persistence, return only the receipt/path plus compact planning status; do not paste the full plan markdown back to the caller unless explicitly requested.
|
|
65
67
|
- Principles (3-5)
|
|
66
68
|
- Decision Drivers (top 3)
|
|
@@ -69,26 +71,26 @@ The consensus workflow:
|
|
|
69
71
|
- Deliberate mode only: pre-mortem (3 scenarios) + expanded test plan (unit/integration/e2e/observability)
|
|
70
72
|
2. **User feedback** *(--interactive only)*: If `--interactive` is set, use the `ask` tool to present the draft plan **plus the Principles / Drivers / Options summary** before review (Proceed to review / Request changes / Skip review). Otherwise, automatically proceed to review.
|
|
71
73
|
3. **Architect** reviews for architectural soundness and must provide the strongest steelman antithesis, at least one real tradeoff tension, and (when possible) synthesis — **await completion before step 4**. In deliberate mode, Architect should explicitly flag principle violations.
|
|
72
|
-
- The Architect agent/subagent must persist its review with `gjc ralplan --write --stage architect --stage_n <N> --artifact
|
|
74
|
+
- The Architect agent/subagent must persist its review with `gjc ralplan --write --stage architect --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`, then return the receipt/path plus compact verdict/status (`CLEAR`/`WATCH`/`BLOCK`, `APPROVE`/`COMMENT`/`REQUEST CHANGES`) instead of pasting the full review body.
|
|
73
75
|
4. **Critic** evaluates against quality criteria — run only after step 3 completes. Critic must enforce principle-option consistency, fair alternatives, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. In deliberate mode, Critic must reject missing/weak pre-mortem or expanded test plan.
|
|
74
|
-
- The Critic agent/subagent must persist its evaluation with `gjc ralplan --write --stage critic --stage_n <N> --artifact
|
|
75
|
-
5. **Re-review loop** (max 5 iterations): Any non-`
|
|
76
|
+
- The Critic agent/subagent must persist its evaluation with `gjc ralplan --write --stage critic --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`, then return the receipt/path plus compact verdict/status (`OKAY`/`ITERATE`/`REJECT`) instead of pasting the full evaluation body.
|
|
77
|
+
5. **Re-review loop** (max 5 iterations): Any non-`OKAY` Critic verdict (`ITERATE` or `REJECT`) MUST run the same full closed loop:
|
|
76
78
|
a. Collect Architect + Critic feedback
|
|
77
79
|
b. Revise the plan by resuming the SAME persisted Planner subagent with consolidated Architect + Critic feedback (see **Persisted Planner** below); fall back to a fresh Planner spawn only per the fallback routing table
|
|
78
80
|
c. Return to Architect review
|
|
79
|
-
- Persist each Planner revision with `gjc ralplan --write --stage revision --stage_n <N> --artifact
|
|
81
|
+
- Persist each Planner revision with `gjc ralplan --write --stage revision --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json` before re-review, then pass the receipt/path forward instead of duplicating the full revision markdown in the parent conversation.
|
|
80
82
|
d. Return to Critic evaluation
|
|
81
|
-
e. Repeat this loop until Critic returns `
|
|
82
|
-
f. If 5 iterations are reached without `
|
|
83
|
-
6. **Post-ralplan interview** (intent reconciliation gate): After Critic returns `
|
|
83
|
+
e. Repeat this loop until Critic returns `OKAY` or 5 iterations are reached
|
|
84
|
+
f. If 5 iterations are reached without `OKAY`, present the best version to the user
|
|
85
|
+
6. **Post-ralplan interview** (intent reconciliation gate): After Critic returns `OKAY` and before the plan is finalized, reconcile the consensus plan against the user's actual intent. The goal is to make sure ralplan did not silently bake in assumptions that conflict with what the user wants.
|
|
84
86
|
a. **Collect open items** from the run: every assumption the Planner/Architect/Critic resolved by assumption rather than by stated fact, every ambiguity flagged during review, and every decision the loop made without explicit user input. Source these from the persisted `planner`/`architect`/`critic`/`revision` stage artifacts, not from memory.
|
|
85
87
|
b. **Cross-check prior context for conflicts**: glob `.gjc/_session-{sessionid}/specs/deep-interview-*.md` and other prior specs/plans/context relevant by topic. For each, list points where the consensus plan contradicts, weakens, or expands beyond a previously crystallized decision, constraint, or non-goal. Cite the conflicting artifact and line/section.
|
|
86
88
|
c. **Reconcile with the user via the `ask` tool (always, regardless of `--interactive`)**: Never stop idle with plain-text prose after the consensus loop. Every reconciliation question MUST go through the `ask` tool with contextual options plus free-text.
|
|
87
89
|
- If open items exist, confirm the open assumptions and conflicts **one at a time** with the `ask` tool, weakest/highest-impact first, polishing intent. If any confirmation reveals that the plan diverges from user intent, route the consolidated correction back into the re-review loop (step 5b Planner revision) and re-run Architect + Critic before returning here. Cap at the same 5-iteration ceiling.
|
|
88
90
|
- If the plan is crystal clear (no open assumptions or prior-context conflicts), skip straight to the step 8 final-options `ask` instead of inventing filler questions.
|
|
89
91
|
- For every confirmed open item, embed the resolved outcome into the final plan under an **## Intent Reconciliation** section so the `pending approval` artifact records each decision; record any item the user explicitly defers as an open confirmation under that same section.
|
|
90
|
-
d. Persist the reconciliation with `gjc ralplan --write --stage post-interview --stage_n <N> --artifact
|
|
91
|
-
7. On reconciliation completion, mark the plan `pending approval` unless explicit execution approval has already been captured, persist the ADR/final plan via `gjc ralplan --write --stage final --stage_n <N> --artifact
|
|
92
|
+
d. Persist the reconciliation with `gjc ralplan --write --stage post-interview --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`, then return the receipt/path plus a compact status (reconciled-clean / reconciled-with-revision / open-confirmations-pending) instead of pasting the full body.
|
|
93
|
+
7. On reconciliation completion, mark the plan `pending approval` unless explicit execution approval has already been captured, persist the ADR/final plan via `gjc ralplan --write --stage final --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT`, and do not directly edit `.gjc/_session-{sessionid}/plans`. Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups) and, when present, the **## Intent Reconciliation** section.
|
|
92
94
|
8. **Always** present the finalized plan via the `ask` tool (regardless of `--interactive`) with `workflowGate: { stage: "ralplan", kind: "approval" }` on the final question so RPC/headless clients receive a `ralplan`/`approval` workflow gate, not a deep-interview question gate. Use these options:
|
|
93
95
|
- **Refine further** — re-run the consensus loop / request changes, then return here
|
|
94
96
|
- **Approve execution via ultragoal (Recommended)** — goal-tracked autonomous execution
|
|
@@ -128,7 +130,7 @@ The Planner is a **same-session persisted subagent**: launched detached once, aw
|
|
|
128
130
|
**Recording persisted-Planner metadata** (audit/routing only — never claim `subagent list` proves resumability, since the snapshot does not expose `resumable`). Ride these optional flags on the normal `--write` for the planner/revision stage of the pass:
|
|
129
131
|
|
|
130
132
|
```
|
|
131
|
-
gjc ralplan --write --stage revision --stage_n <N> --artifact
|
|
133
|
+
gjc ralplan --write --stage revision --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT \
|
|
132
134
|
--planner-id <id> --planner-resumable <true|false> \
|
|
133
135
|
--fallback-reason <context_unavailable|not_found|no_runner|resume_failed|process_restart|missing_record> \
|
|
134
136
|
--fallback-attempted-id <id> --fallback-stage-n <N> \
|