@gajae-code/coding-agent 0.2.2 → 0.2.3
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 +28 -0
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/commands/deep-interview.d.ts +41 -0
- package/dist/types/commands/setup.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +36 -0
- package/dist/types/discovery/helpers.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +18 -0
- package/dist/types/hooks/skill-state.d.ts +5 -0
- package/dist/types/memories/index.d.ts +1 -1
- package/dist/types/memory-backend/local-backend.d.ts +3 -3
- package/dist/types/modes/components/hook-selector.d.ts +7 -0
- package/dist/types/modes/components/settings-selector.d.ts +0 -2
- package/dist/types/modes/utils/context-usage.d.ts +6 -2
- package/dist/types/sdk.d.ts +6 -2
- package/dist/types/session/agent-session.d.ts +45 -1
- package/dist/types/session/session-manager.d.ts +3 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +1 -0
- package/dist/types/setup/provider-onboarding.d.ts +29 -5
- package/dist/types/skill-state/active-state.d.ts +26 -1
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
- package/dist/types/skill-state/initial-phase.d.ts +12 -0
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/task/types.d.ts +11 -0
- package/dist/types/tools/index.d.ts +20 -1
- package/dist/types/tools/skill.d.ts +47 -0
- package/dist/types/utils/changelog.d.ts +18 -2
- package/package.json +7 -7
- package/src/cli/setup-cli.ts +26 -12
- package/src/cli.ts +1 -0
- package/src/commands/deep-interview.ts +25 -2
- package/src/commands/setup.ts +2 -0
- package/src/commands/state.ts +1 -0
- package/src/config/settings-schema.ts +41 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +19 -1
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -0
- package/src/defaults/gjc/skills/team/SKILL.md +10 -0
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +10 -0
- package/src/discovery/helpers.ts +24 -1
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +268 -1
- package/src/gjc-runtime/state-runtime.ts +173 -4
- package/src/hooks/skill-state.ts +8 -6
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/internal-urls/memory-protocol.ts +3 -2
- package/src/main.ts +2 -3
- package/src/memories/index.ts +2 -1
- package/src/memory-backend/local-backend.ts +14 -6
- package/src/modes/components/hook-selector.ts +156 -1
- package/src/modes/components/settings-selector.ts +5 -12
- package/src/modes/controllers/command-controller.ts +2 -3
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/selector-controller.ts +4 -11
- package/src/modes/utils/context-usage.ts +66 -17
- package/src/prompts/agents/architect.md +3 -0
- package/src/prompts/agents/executor.md +2 -0
- package/src/prompts/agents/frontmatter.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +6 -0
- package/src/prompts/tools/skill.md +28 -0
- package/src/prompts/tools/task.md +3 -0
- package/src/sdk.ts +50 -10
- package/src/session/agent-session.ts +204 -21
- package/src/session/session-manager.ts +9 -1
- package/src/setup/model-onboarding-guidance.ts +6 -3
- package/src/setup/provider-onboarding.ts +177 -16
- package/src/skill-state/active-state.ts +150 -25
- package/src/skill-state/deep-interview-mutation-guard.ts +11 -24
- package/src/skill-state/initial-phase.ts +17 -0
- package/src/slash-commands/builtin-registry.ts +51 -13
- package/src/slash-commands/helpers/context-report.ts +123 -13
- package/src/task/agents.ts +1 -0
- package/src/task/executor.ts +9 -1
- package/src/task/index.ts +91 -4
- package/src/task/types.ts +6 -0
- package/src/tools/ask.ts +2 -0
- package/src/tools/index.ts +23 -1
- package/src/tools/skill.ts +153 -0
- package/src/utils/changelog.ts +67 -44
|
@@ -5,6 +5,7 @@ import { type TaskSimpleMode } from "./simple-mode";
|
|
|
5
5
|
import type { NestedRepoPatch } from "./worktree";
|
|
6
6
|
/** Source of an agent definition */
|
|
7
7
|
export type AgentSource = "bundled" | "user" | "project";
|
|
8
|
+
export type ForkContextPolicy = "forbidden" | "allowed";
|
|
8
9
|
/** Maximum output bytes per agent */
|
|
9
10
|
export declare const MAX_OUTPUT_BYTES: number;
|
|
10
11
|
/** Maximum output lines per agent */
|
|
@@ -40,6 +41,7 @@ export declare const taskItemSchema: z.ZodObject<{
|
|
|
40
41
|
id: z.ZodString;
|
|
41
42
|
description: z.ZodString;
|
|
42
43
|
assignment: z.ZodString;
|
|
44
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
43
45
|
}, z.core.$strip>;
|
|
44
46
|
export type TaskItem = z.infer<typeof taskItemSchema>;
|
|
45
47
|
export declare const taskSchema: z.ZodObject<{
|
|
@@ -48,6 +50,7 @@ export declare const taskSchema: z.ZodObject<{
|
|
|
48
50
|
id: z.ZodString;
|
|
49
51
|
description: z.ZodString;
|
|
50
52
|
assignment: z.ZodString;
|
|
53
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
51
54
|
}, z.core.$strip>>;
|
|
52
55
|
}, z.core.$strip>;
|
|
53
56
|
export declare const taskSchemaNoIsolation: z.ZodObject<{
|
|
@@ -56,6 +59,7 @@ export declare const taskSchemaNoIsolation: z.ZodObject<{
|
|
|
56
59
|
id: z.ZodString;
|
|
57
60
|
description: z.ZodString;
|
|
58
61
|
assignment: z.ZodString;
|
|
62
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
59
63
|
}, z.core.$strip>>;
|
|
60
64
|
}, z.core.$strip>;
|
|
61
65
|
declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
|
|
@@ -64,6 +68,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
|
|
|
64
68
|
id: z.ZodString;
|
|
65
69
|
description: z.ZodString;
|
|
66
70
|
assignment: z.ZodString;
|
|
71
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
67
72
|
}, z.core.$strip>>;
|
|
68
73
|
}, z.core.$strip>, z.ZodObject<{
|
|
69
74
|
agent: z.ZodString;
|
|
@@ -71,6 +76,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
|
|
|
71
76
|
id: z.ZodString;
|
|
72
77
|
description: z.ZodString;
|
|
73
78
|
assignment: z.ZodString;
|
|
79
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
74
80
|
}, z.core.$strip>>;
|
|
75
81
|
}, z.core.$strip>, z.ZodObject<{
|
|
76
82
|
agent: z.ZodString;
|
|
@@ -78,6 +84,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
|
|
|
78
84
|
id: z.ZodString;
|
|
79
85
|
description: z.ZodString;
|
|
80
86
|
assignment: z.ZodString;
|
|
87
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
81
88
|
}, z.core.$strip>>;
|
|
82
89
|
}, z.core.$strip>, z.ZodObject<{
|
|
83
90
|
agent: z.ZodString;
|
|
@@ -85,6 +92,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
|
|
|
85
92
|
id: z.ZodString;
|
|
86
93
|
description: z.ZodString;
|
|
87
94
|
assignment: z.ZodString;
|
|
95
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
88
96
|
}, z.core.$strip>>;
|
|
89
97
|
}, z.core.$strip>, z.ZodObject<{
|
|
90
98
|
agent: z.ZodString;
|
|
@@ -92,6 +100,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
|
|
|
92
100
|
id: z.ZodString;
|
|
93
101
|
description: z.ZodString;
|
|
94
102
|
assignment: z.ZodString;
|
|
103
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
95
104
|
}, z.core.$strip>>;
|
|
96
105
|
}, z.core.$strip>, z.ZodObject<{
|
|
97
106
|
agent: z.ZodString;
|
|
@@ -99,6 +108,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
|
|
|
99
108
|
id: z.ZodString;
|
|
100
109
|
description: z.ZodString;
|
|
101
110
|
assignment: z.ZodString;
|
|
111
|
+
inheritContext: z.ZodOptional<z.ZodBoolean>;
|
|
102
112
|
}, z.core.$strip>>;
|
|
103
113
|
}, z.core.$strip>];
|
|
104
114
|
type DynamicTaskSchema = (typeof ALL_TASK_SCHEMAS)[number];
|
|
@@ -150,6 +160,7 @@ export interface AgentDefinition {
|
|
|
150
160
|
blocking?: boolean;
|
|
151
161
|
autoloadSkills?: string[];
|
|
152
162
|
hide?: boolean;
|
|
163
|
+
forkContext?: ForkContextPolicy;
|
|
153
164
|
source: AgentSource;
|
|
154
165
|
filePath?: string;
|
|
155
166
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTelemetryConfig, AgentTool } from "@gajae-code/agent-core";
|
|
2
|
-
import type { ToolChoice } from "@gajae-code/ai";
|
|
2
|
+
import type { Model, ToolChoice } from "@gajae-code/ai";
|
|
3
3
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
4
4
|
import type { Settings } from "../config/settings";
|
|
5
5
|
import type { Skill } from "../extensibility/skills";
|
|
@@ -7,10 +7,12 @@ import type { GoalModeState, GoalRuntime } from "../goals";
|
|
|
7
7
|
import type { HindsightSessionState } from "../hindsight/state";
|
|
8
8
|
import type { PlanModeState } from "../plan-mode/state";
|
|
9
9
|
import type { AgentRegistry } from "../registry/agent-registry";
|
|
10
|
+
import type { ForkContextSeed, ForkContextSeedOptions } from "../session/agent-session";
|
|
10
11
|
import type { ArtifactManager } from "../session/artifacts";
|
|
11
12
|
import type { ClientBridge } from "../session/client-bridge";
|
|
12
13
|
import type { CustomMessage } from "../session/messages";
|
|
13
14
|
import type { ToolChoiceQueue } from "../session/tool-choice-queue";
|
|
15
|
+
import type { SkillActiveEntry } from "../skill-state/active-state";
|
|
14
16
|
import type { AgentOutputManager } from "../task/output-manager";
|
|
15
17
|
import type { DiscoverableTool, DiscoverableToolSearchIndex } from "../tool-discovery/tool-index";
|
|
16
18
|
import type { EventBus } from "../utils/event-bus";
|
|
@@ -48,6 +50,7 @@ export * from "./resolve";
|
|
|
48
50
|
export * from "./review";
|
|
49
51
|
export * from "./search";
|
|
50
52
|
export * from "./search-tool-bm25";
|
|
53
|
+
export * from "./skill";
|
|
51
54
|
export * from "./ssh";
|
|
52
55
|
export * from "./subagent";
|
|
53
56
|
export * from "./todo-write";
|
|
@@ -77,6 +80,12 @@ export interface ToolSession {
|
|
|
77
80
|
workspaceTree?: WorkspaceTree;
|
|
78
81
|
/** Pre-loaded skills */
|
|
79
82
|
skills?: Skill[];
|
|
83
|
+
/** Currently executing skill prompt, when this tool session is inside one. */
|
|
84
|
+
getActiveSkillState?: () => Pick<SkillActiveEntry, "skill" | "session_id"> | undefined;
|
|
85
|
+
/** Get the active skill prompt's current phase so the skill tool can apply
|
|
86
|
+
* its terminal-phase chain guard. Returns the raw phase string or undefined
|
|
87
|
+
* when no active skill (or accessor) is available. */
|
|
88
|
+
getActiveSkillPhase?: () => string | undefined;
|
|
80
89
|
/** Pre-loaded prompt templates */
|
|
81
90
|
promptTemplates?: PromptTemplate[];
|
|
82
91
|
/** Whether LSP integrations are enabled */
|
|
@@ -122,6 +131,8 @@ export interface ToolSession {
|
|
|
122
131
|
getSessionSpawns: () => string | null;
|
|
123
132
|
/** Get resolved model string if explicitly set for this session */
|
|
124
133
|
getModelString?: () => string | undefined;
|
|
134
|
+
/** Current model, when selected. */
|
|
135
|
+
model?: Model;
|
|
125
136
|
/** Get the current session model string, regardless of how it was chosen */
|
|
126
137
|
getActiveModelString?: () => string | undefined;
|
|
127
138
|
/** Auth storage for passing to subagents (avoids re-discovery) */
|
|
@@ -204,9 +215,17 @@ export interface ToolSession {
|
|
|
204
215
|
conflictHistory?: import("./conflict-detect").ConflictHistory;
|
|
205
216
|
/** Queue a hidden message to be injected at the next agent turn. */
|
|
206
217
|
queueDeferredMessage?(message: CustomMessage): void;
|
|
218
|
+
/** Dispatch a custom message through the active session. Used by the `skill`
|
|
219
|
+
* tool to dispatch another skill prompt same-turn after recording a handoff. */
|
|
220
|
+
sendCustomMessage?(message: Pick<CustomMessage, "customType" | "content" | "display" | "details" | "attribution">, options?: {
|
|
221
|
+
triggerTurn?: boolean;
|
|
222
|
+
deliverAs?: "steer" | "followUp" | "nextTurn";
|
|
223
|
+
}): Promise<void>;
|
|
207
224
|
/** Get the active OpenTelemetry config so subagent dispatch can forward
|
|
208
225
|
* the parent's tracer/hooks with the subagent's own identity stamped. */
|
|
209
226
|
getTelemetry?: () => AgentTelemetryConfig | undefined;
|
|
227
|
+
/** Build a sanitized fork-context seed for task subagents. */
|
|
228
|
+
buildForkContextSeed?: (options: ForkContextSeedOptions) => Promise<ForkContextSeed>;
|
|
210
229
|
}
|
|
211
230
|
export type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool | null>;
|
|
212
231
|
export type BuiltinToolLoadMode = "essential" | "discoverable";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Tool — agent-initiated skill chaining.
|
|
3
|
+
*
|
|
4
|
+
* Lets the agent hand off to another available skill in the current turn. The
|
|
5
|
+
* callee's SKILL.md is dispatched through the same custom-message path used by
|
|
6
|
+
* `/skill:<name>` typing, as a user-attribution message delivered same-turn
|
|
7
|
+
* (without `deliverAs: "nextTurn"`). Before dispatch, the tool calls
|
|
8
|
+
* `gjc state <caller> handoff --to <callee>` in-process via the state-runtime
|
|
9
|
+
* function so caller and callee mode-states plus `skill-active-state.json`
|
|
10
|
+
* transition atomically.
|
|
11
|
+
*
|
|
12
|
+
* Chaining is refused unless the caller's `current_phase` is in
|
|
13
|
+
* `{complete, completed, handoff, failed, cancelled, canceled, inactive}`. The
|
|
14
|
+
* agent declares readiness either by writing `current_phase: "handoff"` to its
|
|
15
|
+
* mode-state or by running the handoff verb directly.
|
|
16
|
+
*/
|
|
17
|
+
import type { AgentTool, AgentToolResult } from "@gajae-code/agent-core";
|
|
18
|
+
import * as z from "zod/v4";
|
|
19
|
+
import type { ToolSession } from ".";
|
|
20
|
+
declare const skillSchema: z.ZodObject<{
|
|
21
|
+
name: z.ZodString;
|
|
22
|
+
args: z.ZodOptional<z.ZodString>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
type SkillToolInput = z.infer<typeof skillSchema>;
|
|
25
|
+
export interface SkillToolDetails {
|
|
26
|
+
name: string;
|
|
27
|
+
path: string;
|
|
28
|
+
args?: string;
|
|
29
|
+
lineCount: number;
|
|
30
|
+
}
|
|
31
|
+
export declare class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails> {
|
|
32
|
+
#private;
|
|
33
|
+
readonly name = "skill";
|
|
34
|
+
readonly label = "Skill";
|
|
35
|
+
readonly summary = "Chain into another available skill in the current turn";
|
|
36
|
+
readonly loadMode = "discoverable";
|
|
37
|
+
readonly description: string;
|
|
38
|
+
readonly parameters: z.ZodObject<{
|
|
39
|
+
name: z.ZodString;
|
|
40
|
+
args: z.ZodOptional<z.ZodString>;
|
|
41
|
+
}, z.core.$strip>;
|
|
42
|
+
readonly strict = true;
|
|
43
|
+
constructor(session: ToolSession);
|
|
44
|
+
static createIf(session: ToolSession): SkillTool | null;
|
|
45
|
+
execute(_toolCallId: string, input: SkillToolInput, signal?: AbortSignal): Promise<AgentToolResult<SkillToolDetails>>;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
@@ -5,10 +5,26 @@ export interface ChangelogEntry {
|
|
|
5
5
|
content: string;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Parse changelog entries from CHANGELOG.md
|
|
9
|
-
* Scans for ## lines and collects content until next ## or EOF
|
|
8
|
+
* Parse changelog entries from a CHANGELOG.md text body.
|
|
9
|
+
* Scans for ## lines and collects content until next ## or EOF.
|
|
10
|
+
* Pure and synchronous so it can be reused by the embedded display path.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseChangelogContent(content: string): ChangelogEntry[];
|
|
13
|
+
/**
|
|
14
|
+
* Parse changelog entries from a CHANGELOG.md file on disk.
|
|
15
|
+
* Returns [] on ENOENT; logs and returns [] on other read/parse errors.
|
|
10
16
|
*/
|
|
11
17
|
export declare function parseChangelog(changelogPath: string): Promise<ChangelogEntry[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Return changelog entries from the CHANGELOG.md that shipped with this binary.
|
|
20
|
+
*
|
|
21
|
+
* The text is embedded at build time via `with { type: "text" }`, so the
|
|
22
|
+
* displayed changelog is deterministic across compiled binaries, source-tree
|
|
23
|
+
* dev runs, and `GJC_PACKAGE_DIR` / `PI_PACKAGE_DIR` overrides (which scope to
|
|
24
|
+
* optional package assets like docs/examples and do not influence the
|
|
25
|
+
* binary-identity changelog).
|
|
26
|
+
*/
|
|
27
|
+
export declare function getDisplayChangelogEntries(): ChangelogEntry[];
|
|
12
28
|
/**
|
|
13
29
|
* Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
|
|
14
30
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.3",
|
|
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",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
49
49
|
"@babel/parser": "^7.29.3",
|
|
50
50
|
"@mozilla/readability": "^0.6.0",
|
|
51
|
-
"@gajae-code/stats": "0.2.
|
|
52
|
-
"@gajae-code/agent-core": "0.2.
|
|
53
|
-
"@gajae-code/ai": "0.2.
|
|
54
|
-
"@gajae-code/natives": "0.2.
|
|
55
|
-
"@gajae-code/tui": "0.2.
|
|
56
|
-
"@gajae-code/utils": "0.2.
|
|
51
|
+
"@gajae-code/stats": "0.2.3",
|
|
52
|
+
"@gajae-code/agent-core": "0.2.3",
|
|
53
|
+
"@gajae-code/ai": "0.2.3",
|
|
54
|
+
"@gajae-code/natives": "0.2.3",
|
|
55
|
+
"@gajae-code/tui": "0.2.3",
|
|
56
|
+
"@gajae-code/utils": "0.2.3",
|
|
57
57
|
"@puppeteer/browsers": "^2.13.0",
|
|
58
58
|
"@types/turndown": "5.0.6",
|
|
59
59
|
"@xterm/headless": "^6.0.0",
|
package/src/cli/setup-cli.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { theme } from "../modes/theme/theme";
|
|
17
17
|
import {
|
|
18
18
|
addApiCompatibleProvider,
|
|
19
|
+
formatProviderPresetList,
|
|
19
20
|
formatProviderSetupResult,
|
|
20
21
|
parseProviderCompatibility,
|
|
21
22
|
} from "../setup/provider-onboarding";
|
|
@@ -28,6 +29,7 @@ export interface SetupCommandArgs {
|
|
|
28
29
|
json?: boolean;
|
|
29
30
|
check?: boolean;
|
|
30
31
|
force?: boolean;
|
|
32
|
+
preset?: string;
|
|
31
33
|
compat?: string;
|
|
32
34
|
provider?: string;
|
|
33
35
|
baseUrl?: string;
|
|
@@ -42,6 +44,7 @@ const VALID_COMPONENTS: SetupComponent[] = ["defaults", "hooks", "provider", "py
|
|
|
42
44
|
function hasProviderSetupFlags(flags: SetupCommandArgs["flags"]): boolean {
|
|
43
45
|
return (
|
|
44
46
|
flags.compat !== undefined ||
|
|
47
|
+
flags.preset !== undefined ||
|
|
45
48
|
flags.provider !== undefined ||
|
|
46
49
|
flags.baseUrl !== undefined ||
|
|
47
50
|
flags.apiKeyEnv !== undefined ||
|
|
@@ -57,7 +60,7 @@ function rejectProviderFlagsOutsideProvider(component: SetupComponent, flags: Se
|
|
|
57
60
|
console.error(chalk.red("Provider setup flags require the explicit `provider` component."));
|
|
58
61
|
console.error(
|
|
59
62
|
chalk.dim(
|
|
60
|
-
`Run: ${APP_NAME} setup provider --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> --model <id>`,
|
|
63
|
+
`Run: ${APP_NAME} setup provider --preset <minimax|glm> or ${APP_NAME} setup provider --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> --model <id>`,
|
|
61
64
|
),
|
|
62
65
|
);
|
|
63
66
|
process.exit(1);
|
|
@@ -87,6 +90,8 @@ export function parseSetupArgs(args: string[]): SetupCommandArgs | undefined {
|
|
|
87
90
|
flags.force = true;
|
|
88
91
|
} else if (arg === "--compat") {
|
|
89
92
|
flags.compat = args[++i];
|
|
93
|
+
} else if (arg === "--preset") {
|
|
94
|
+
flags.preset = args[++i];
|
|
90
95
|
} else if (arg === "--provider") {
|
|
91
96
|
flags.provider = args[++i];
|
|
92
97
|
} else if (arg === "--base-url") {
|
|
@@ -190,6 +195,7 @@ export async function runSetupCommand(cmd: SetupCommandArgs): Promise<void> {
|
|
|
190
195
|
async function handleProviderSetup(flags: {
|
|
191
196
|
json?: boolean;
|
|
192
197
|
force?: boolean;
|
|
198
|
+
preset?: string;
|
|
193
199
|
compat?: string;
|
|
194
200
|
provider?: string;
|
|
195
201
|
baseUrl?: string;
|
|
@@ -199,20 +205,25 @@ async function handleProviderSetup(flags: {
|
|
|
199
205
|
}): Promise<void> {
|
|
200
206
|
try {
|
|
201
207
|
const missing: string[] = [];
|
|
202
|
-
if (!flags.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
if (!flags.preset) {
|
|
209
|
+
if (!flags.compat) missing.push("--compat");
|
|
210
|
+
if (!flags.provider) missing.push("--provider");
|
|
211
|
+
if (!flags.baseUrl) missing.push("--base-url");
|
|
212
|
+
if (!flags.apiKeyEnv) missing.push("--api-key-env");
|
|
213
|
+
if (!flags.model || flags.model.length === 0) missing.push("--model");
|
|
214
|
+
}
|
|
207
215
|
if (missing.length > 0) {
|
|
208
|
-
throw new Error(
|
|
216
|
+
throw new Error(
|
|
217
|
+
`Missing required provider setup option(s): ${missing.join(", ")}. Or use --preset <preset>.\nAvailable presets:\n${formatProviderPresetList()}`,
|
|
218
|
+
);
|
|
209
219
|
}
|
|
210
220
|
const result = await addApiCompatibleProvider({
|
|
211
|
-
compatibility: parseProviderCompatibility(flags.compat
|
|
212
|
-
|
|
213
|
-
|
|
221
|
+
compatibility: flags.compat ? parseProviderCompatibility(flags.compat) : undefined,
|
|
222
|
+
preset: flags.preset,
|
|
223
|
+
providerId: flags.provider,
|
|
224
|
+
baseUrl: flags.baseUrl,
|
|
214
225
|
apiKeyEnv: flags.apiKeyEnv,
|
|
215
|
-
models: flags.model
|
|
226
|
+
models: flags.model,
|
|
216
227
|
modelsPath: flags.modelsPath,
|
|
217
228
|
force: flags.force,
|
|
218
229
|
});
|
|
@@ -400,18 +411,21 @@ ${chalk.bold("Usage:")}
|
|
|
400
411
|
${chalk.bold("Components:")}
|
|
401
412
|
defaults Install bundled GJC default workflow skills (default)
|
|
402
413
|
hooks Optional: install GJC native Codex UserPromptSubmit/Stop skill-state hooks
|
|
403
|
-
provider Optional: add
|
|
414
|
+
provider Optional: add a preset, OpenAI-compatible, or Anthropic-compatible API provider
|
|
404
415
|
python Optional: verify a Python 3 interpreter is reachable for code execution
|
|
405
416
|
stt Optional: install speech-to-text dependencies (openai-whisper, recording tools)
|
|
406
417
|
|
|
407
418
|
|
|
408
419
|
${chalk.bold("Provider example:")}
|
|
420
|
+
${APP_NAME} setup provider --preset minimax
|
|
421
|
+
${APP_NAME} setup provider --preset glm
|
|
409
422
|
MY_PROVIDER_KEY=sk-... ${APP_NAME} setup provider --compat openai --provider my-oai --base-url https://api.example.com/v1 --api-key-env MY_PROVIDER_KEY --model gpt-example
|
|
410
423
|
|
|
411
424
|
${chalk.bold("Options:")}
|
|
412
425
|
-c, --check Check if dependencies are installed without installing
|
|
413
426
|
-f, --force Overwrite existing default workflow skill files
|
|
414
427
|
--json Output status as JSON
|
|
428
|
+
--preset Provider preset: minimax, minimax-cn, or glm (aliases include minimax-code and zai)
|
|
415
429
|
--compat Provider compatibility: openai or anthropic
|
|
416
430
|
--provider Provider id to add to models.yml
|
|
417
431
|
--base-url Provider API base URL
|
package/src/cli.ts
CHANGED
|
@@ -44,6 +44,7 @@ const commands: CommandEntry[] = [
|
|
|
44
44
|
load: () => import("./commands/contribution-prep").then(m => m.default),
|
|
45
45
|
},
|
|
46
46
|
{ name: "deep-interview", load: () => import("./commands/deep-interview").then(m => m.default) },
|
|
47
|
+
{ name: "update", load: () => import("./commands/update").then(m => m.default) },
|
|
47
48
|
{ name: "launch", load: () => import("./commands/launch").then(m => m.default) },
|
|
48
49
|
];
|
|
49
50
|
|
|
@@ -1,10 +1,33 @@
|
|
|
1
|
-
import { Command } from "@gajae-code/utils/cli";
|
|
1
|
+
import { Command, Flags } from "@gajae-code/utils/cli";
|
|
2
2
|
import { runNativeDeepInterviewCommand } from "../gjc-runtime/deep-interview-runtime";
|
|
3
3
|
|
|
4
4
|
export default class DeepInterview extends Command {
|
|
5
5
|
static description = "Run native GJC deep-interview workflow";
|
|
6
6
|
static strict = false;
|
|
7
|
-
static
|
|
7
|
+
static flags = {
|
|
8
|
+
quick: Flags.boolean({ description: "Seed a quick deep-interview run" }),
|
|
9
|
+
standard: Flags.boolean({ description: "Seed a standard deep-interview run" }),
|
|
10
|
+
deep: Flags.boolean({ description: "Seed a deep deep-interview run" }),
|
|
11
|
+
threshold: Flags.string({ description: "Override ambiguity threshold for kickoff" }),
|
|
12
|
+
"threshold-source": Flags.string({ description: "Describe the threshold override source" }),
|
|
13
|
+
"session-id": Flags.string({
|
|
14
|
+
description: "Route state/spec handoff through a session-scoped .gjc state directory",
|
|
15
|
+
}),
|
|
16
|
+
write: Flags.boolean({ description: "Persist a final deep-interview spec through the sanctioned GJC CLI/API" }),
|
|
17
|
+
stage: Flags.string({ description: 'Spec stage for --write (currently "final")' }),
|
|
18
|
+
slug: Flags.string({ description: "Safe slug for .gjc/specs/deep-interview-<slug>.md" }),
|
|
19
|
+
spec: Flags.string({ description: "Final spec markdown or a path to the final spec markdown" }),
|
|
20
|
+
handoff: Flags.string({ description: 'After --write, hand off to a workflow target (currently "ralplan")' }),
|
|
21
|
+
deliberate: Flags.boolean({
|
|
22
|
+
description: "Shortcut for --write handoff to ralplan in deliberate consensus mode",
|
|
23
|
+
}),
|
|
24
|
+
json: Flags.boolean({ description: "Output JSON" }),
|
|
25
|
+
};
|
|
26
|
+
static examples = [
|
|
27
|
+
'$ gjc deep-interview --standard "<idea>"',
|
|
28
|
+
"$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md",
|
|
29
|
+
"$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md --deliberate",
|
|
30
|
+
];
|
|
8
31
|
|
|
9
32
|
async run(): Promise<void> {
|
|
10
33
|
const result = await runNativeDeepInterviewCommand(this.argv, process.cwd());
|
package/src/commands/setup.ts
CHANGED
|
@@ -22,6 +22,7 @@ export default class Setup extends Command {
|
|
|
22
22
|
check: Flags.boolean({ char: "c", description: "Check if dependencies are installed" }),
|
|
23
23
|
force: Flags.boolean({ char: "f", description: "Overwrite existing default workflow skill files" }),
|
|
24
24
|
json: Flags.boolean({ description: "Output status as JSON" }),
|
|
25
|
+
preset: Flags.string({ description: "Provider preset: minimax, minimax-cn, or glm" }),
|
|
25
26
|
compat: Flags.string({ description: "Provider compatibility: openai or anthropic" }),
|
|
26
27
|
provider: Flags.string({ description: "Provider id to add to models.yml" }),
|
|
27
28
|
"base-url": Flags.string({ description: "Provider API base URL" }),
|
|
@@ -38,6 +39,7 @@ export default class Setup extends Command {
|
|
|
38
39
|
json: flags.json,
|
|
39
40
|
check: flags.check,
|
|
40
41
|
force: flags.force,
|
|
42
|
+
preset: flags.preset,
|
|
41
43
|
compat: flags.compat,
|
|
42
44
|
provider: flags.provider,
|
|
43
45
|
baseUrl: flags["base-url"],
|
package/src/commands/state.ts
CHANGED
|
@@ -11,6 +11,7 @@ export default class State extends Command {
|
|
|
11
11
|
"$ gjc state deep-interview read --json",
|
|
12
12
|
'$ gjc state ralplan write --input \'{"phase":"approval","active":true}\' --json',
|
|
13
13
|
"$ gjc state team contract",
|
|
14
|
+
"$ gjc state deep-interview handoff --to ralplan --json",
|
|
14
15
|
];
|
|
15
16
|
|
|
16
17
|
async run(): Promise<void> {
|
|
@@ -1940,6 +1940,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
1940
1940
|
},
|
|
1941
1941
|
},
|
|
1942
1942
|
|
|
1943
|
+
"skill.enabled": {
|
|
1944
|
+
type: "boolean",
|
|
1945
|
+
default: true,
|
|
1946
|
+
ui: {
|
|
1947
|
+
tab: "tools",
|
|
1948
|
+
label: "Skill",
|
|
1949
|
+
description: "Enable the skill tool so the agent can chain into another available skill on its next turn",
|
|
1950
|
+
},
|
|
1951
|
+
},
|
|
1952
|
+
|
|
1943
1953
|
// Fetching and browser
|
|
1944
1954
|
"fetch.enabled": {
|
|
1945
1955
|
type: "boolean",
|
|
@@ -2344,6 +2354,37 @@ export const SETTINGS_SCHEMA = {
|
|
|
2344
2354
|
},
|
|
2345
2355
|
},
|
|
2346
2356
|
|
|
2357
|
+
"task.forkContext.enabled": {
|
|
2358
|
+
type: "boolean",
|
|
2359
|
+
default: false,
|
|
2360
|
+
ui: {
|
|
2361
|
+
tab: "tasks",
|
|
2362
|
+
label: "Fork Context for Subagents",
|
|
2363
|
+
description:
|
|
2364
|
+
"Allow explicitly opted-in subagents to start from a sanitized snapshot of parent context when both the agent and task item also opt in.",
|
|
2365
|
+
},
|
|
2366
|
+
},
|
|
2367
|
+
|
|
2368
|
+
"task.forkContext.maxMessages": {
|
|
2369
|
+
type: "number",
|
|
2370
|
+
default: 50,
|
|
2371
|
+
ui: {
|
|
2372
|
+
tab: "tasks",
|
|
2373
|
+
label: "Fork Context Max Messages",
|
|
2374
|
+
description: "Maximum parent messages copied into an explicitly opted-in subagent fork-context seed.",
|
|
2375
|
+
},
|
|
2376
|
+
},
|
|
2377
|
+
|
|
2378
|
+
"task.forkContext.maxTokens": {
|
|
2379
|
+
type: "number",
|
|
2380
|
+
default: 0,
|
|
2381
|
+
ui: {
|
|
2382
|
+
tab: "tasks",
|
|
2383
|
+
label: "Fork Context Max Tokens",
|
|
2384
|
+
description: "Approximate token cap for fork-context seeds. 0 uses 25% of the target model context window.",
|
|
2385
|
+
},
|
|
2386
|
+
},
|
|
2387
|
+
|
|
2347
2388
|
"task.maxRecursionDepth": {
|
|
2348
2389
|
type: "number",
|
|
2349
2390
|
default: 2,
|
|
@@ -389,8 +389,9 @@ When ambiguity ≤ threshold (or hard cap / early exit):
|
|
|
389
389
|
1. **Generate the specification** using opus model with the prompt-safe transcript. If the full interview transcript or initial context is too large, include the summary plus all concrete decisions, acceptance criteria, unresolved gaps, and ontology snapshots; never overflow the prompt with raw oversized context.
|
|
390
390
|
2. **Write the final spec through the workflow CLI**: persist the artifact at `.gjc/specs/deep-interview-{slug}.md`
|
|
391
391
|
- Always use this exact final spec path. Do not write temporary working files to the repo root or other ad hoc paths; repos may allowlist `.gjc/` for planning artifacts while protecting product branches.
|
|
392
|
-
- Use the
|
|
392
|
+
- Use the native deep-interview write command with `--write --stage final --slug {slug} --spec <markdown-or-path> [--json]` for artifact and state persistence; direct `.gjc/` file edits are forbidden unless an explicit force override is active.
|
|
393
393
|
- Persist the final `spec_path` in state when available so downstream skills and resumed sessions can pass the artifact path explicitly.
|
|
394
|
+
- If the user preselected the deliberate ralplan path, use the native deep-interview write command with `--write --stage final --slug {slug} --spec <markdown-or-path> --deliberate [--json]` so the final spec is persisted before deep-interview hands off to ralplan.
|
|
394
395
|
|
|
395
396
|
Spec structure:
|
|
396
397
|
|
|
@@ -515,6 +516,23 @@ After the spec is written, mark it `pending approval` and present execution opti
|
|
|
515
516
|
|
|
516
517
|
**IMPORTANT:** On explicit execution selection, **MUST** use the chosen bundled GJC workflow skill entrypoint (`/skill:ralplan` or `/skill:team`) inside the agent session. `gjc ralplan` is a native CLI that accepts the documented skill flags and seeds local `.gjc/state` receipts; agent sessions should still drive the consensus loop through `/skill:ralplan`. `gjc team` is a native tmux runtime command and may be used only when the Team workflow explicitly requires the CLI runtime. Do NOT implement directly. The deep-interview agent is a requirements agent, not an execution agent. If oversized initial context was summarized, pass the spec and prompt-safe summary forward, not the raw oversized source material. Without explicit execution selection, stop with the spec marked `pending approval`.
|
|
517
518
|
|
|
519
|
+
### Phase 5b: Handoff before chain
|
|
520
|
+
|
|
521
|
+
Before invoking `/skill:ralplan`, `/skill:team`, or `/skill:ultragoal`, the final spec must already be persisted through the native deep-interview write command. For ordinary user-selected handoff, mark deep-interview ready for the skill tool's chain guard:
|
|
522
|
+
|
|
523
|
+
```
|
|
524
|
+
gjc state deep-interview write --input '{"current_phase":"handoff"}' --json
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
For a preselected deliberate ralplan path, prefer the single sanctioned bridge command instead:
|
|
528
|
+
|
|
529
|
+
```
|
|
530
|
+
gjc \
|
|
531
|
+
deep-interview --write --stage final --slug {slug} --spec <markdown-or-path> --deliberate --json
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
That command persists `.gjc/specs/deep-interview-{slug}.md`, seeds ralplan in deliberate mode, and performs the safe deep-interview → ralplan state handoff. Skipping spec persistence leaves the Phase 5 chain blocked by design.
|
|
535
|
+
|
|
518
536
|
### Approval-Gated Refinement Path (Recommended)
|
|
519
537
|
|
|
520
538
|
```
|
|
@@ -72,6 +72,14 @@ The consensus workflow:
|
|
|
72
72
|
7. *(--interactive only)* User chooses: Approve team execution, Request changes, or Reject
|
|
73
73
|
8. *(--interactive only)* On approval: invoke `/skill:team` for execution -- never implement directly
|
|
74
74
|
|
|
75
|
+
Before invoking `/skill:team` or `/skill:ultragoal`, mark ralplan ready for handoff so the skill tool's chain guard permits the transition:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
gjc state ralplan write --input '{"current_phase":"handoff"}' --json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The skill tool then dispatches the execution skill same-turn and runs `gjc state ralplan handoff --to <team|ultragoal> --json` in-process to atomically demote ralplan, promote the callee, and sync both `skill-active-state.json` files. You do not need to run the handoff verb yourself.
|
|
82
|
+
|
|
75
83
|
> **Important:** Steps 3 and 4 MUST run sequentially. Do NOT issue both agent Task calls in the same parallel batch. Always await the Architect result before issuing the Critic Task.
|
|
76
84
|
|
|
77
85
|
Follow the Plan skill's full documentation for consensus mode details.
|
|
@@ -394,3 +394,13 @@ Two cleanup paths exist and must not be confused:
|
|
|
394
394
|
**Good:** The user changes only the output shape or downstream delivery step (for example `make a PR`). Preserve earlier non-conflicting workflow constraints and apply the update locally.
|
|
395
395
|
|
|
396
396
|
**Bad:** The user says `continue`, and the workflow restarts discovery or stops before the missing verification/evidence is gathered.
|
|
397
|
+
|
|
398
|
+
## Handoff back to planning or persistence
|
|
399
|
+
|
|
400
|
+
When the team task-set completes OR the user requests return to planning/persistence, mark team ready for handoff so the skill tool's chain guard permits the transition:
|
|
401
|
+
|
|
402
|
+
```
|
|
403
|
+
gjc state team write --input '{"current_phase":"handoff"}' --json
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
The skill tool then dispatches `/skill:ralplan`, `/skill:deep-interview`, or `/skill:ultragoal` same-turn and runs `gjc state team handoff --to <ralplan|deep-interview|ultragoal> --json` in-process to atomically demote team, promote the callee, and sync both `skill-active-state.json` files. You do not need to run the handoff verb yourself.
|
|
@@ -192,6 +192,16 @@ Receipts are freshness-scoped:
|
|
|
192
192
|
- Normal later `goal_started` or clean receipt-backed `goal_checkpointed` events for other goals do not stale older per-goal receipts.
|
|
193
193
|
- Appending required goals or changing final required-goal state stales final aggregate receipts. Final aggregate completion requires a fresh final aggregate receipt proving no incomplete, blocked, or `review_blocked` required goals remain.
|
|
194
194
|
|
|
195
|
+
## Handoff back to planning
|
|
196
|
+
|
|
197
|
+
When the aggregate ultragoal is complete OR the user requests return to planning/clarification, mark ultragoal ready for handoff so the skill tool's chain guard permits the backward transition:
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
gjc state ultragoal write --input '{"current_phase":"handoff"}' --json
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The skill tool then dispatches `/skill:ralplan` or `/skill:deep-interview` same-turn and runs `gjc state ultragoal handoff --to <ralplan|deep-interview> --json` in-process to atomically demote ultragoal, promote the callee, and sync both `skill-active-state.json` files. You do not need to run the handoff verb yourself.
|
|
204
|
+
|
|
195
205
|
## Constraints
|
|
196
206
|
|
|
197
207
|
- The shell command cannot directly invoke interactive `/goal`; it emits a model-facing handoff for the active GJC agent.
|
package/src/discovery/helpers.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getConfigDirName,
|
|
9
9
|
getPluginsDir,
|
|
10
10
|
getProjectDir,
|
|
11
|
+
logger,
|
|
11
12
|
parseFrontmatter,
|
|
12
13
|
tryParseJson,
|
|
13
14
|
} from "@gajae-code/utils";
|
|
@@ -16,6 +17,7 @@ import { invalidate as invalidateFsCache, readDirEntries, readFile } from "../ca
|
|
|
16
17
|
import { parseRuleConditionAndScope, type Rule, type RuleFrontmatter } from "../capability/rule";
|
|
17
18
|
import type { Skill, SkillFrontmatter } from "../capability/skill";
|
|
18
19
|
import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
|
|
20
|
+
import type { ForkContextPolicy } from "../task/types";
|
|
19
21
|
import { parseThinkingLevel } from "../thinking";
|
|
20
22
|
|
|
21
23
|
import { buildPluginDirRoot } from "./plugin-dir-roots";
|
|
@@ -214,6 +216,7 @@ export interface ParsedAgentFields {
|
|
|
214
216
|
autoloadSkills?: string[];
|
|
215
217
|
blocking?: boolean;
|
|
216
218
|
hide?: boolean;
|
|
219
|
+
forkContext?: ForkContextPolicy;
|
|
217
220
|
}
|
|
218
221
|
|
|
219
222
|
/**
|
|
@@ -267,10 +270,30 @@ export function parseAgentFields(frontmatter: Record<string, unknown>): ParsedAg
|
|
|
267
270
|
const model = parseModelList(frontmatter.model);
|
|
268
271
|
const blocking = parseBoolean(frontmatter.blocking);
|
|
269
272
|
const hide = parseBoolean(frontmatter.hide);
|
|
273
|
+
const forkContext = parseForkContextPolicy(frontmatter.forkContext);
|
|
270
274
|
const autoloadSkills = parseArrayOrCSV(frontmatter.autoloadSkills)
|
|
271
275
|
?.map(s => s.trim())
|
|
272
276
|
.filter(Boolean);
|
|
273
|
-
return {
|
|
277
|
+
return {
|
|
278
|
+
name,
|
|
279
|
+
description,
|
|
280
|
+
tools,
|
|
281
|
+
spawns,
|
|
282
|
+
model,
|
|
283
|
+
output,
|
|
284
|
+
thinkingLevel,
|
|
285
|
+
blocking,
|
|
286
|
+
autoloadSkills,
|
|
287
|
+
hide,
|
|
288
|
+
forkContext,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function parseForkContextPolicy(value: unknown): ForkContextPolicy | undefined {
|
|
293
|
+
if (value === undefined) return undefined;
|
|
294
|
+
if (value === "forbidden" || value === "allowed") return value;
|
|
295
|
+
logger.warn("Invalid agent forkContext frontmatter; expected 'allowed' or 'forbidden', ignoring", { value });
|
|
296
|
+
return undefined;
|
|
274
297
|
}
|
|
275
298
|
|
|
276
299
|
async function globIf(
|
|
@@ -110,6 +110,12 @@ export interface ExtensionUIDialogOptions {
|
|
|
110
110
|
onExternalEditor?: () => void;
|
|
111
111
|
/** Optional footer hint text rendered by interactive selector */
|
|
112
112
|
helpText?: string;
|
|
113
|
+
/**
|
|
114
|
+
* For interactive TUI select dialogs, render the focused option across
|
|
115
|
+
* multiple rows instead of truncating it. This is a select-only rendering
|
|
116
|
+
* hint; non-TUI bridges (RPC, ACP) drop it and do not serialize it.
|
|
117
|
+
*/
|
|
118
|
+
wrapFocused?: boolean;
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
/** Raw terminal input listener for extensions. */
|