@gajae-code/coding-agent 0.2.2 → 0.2.4
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 +45 -8600
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/cli/update-cli.d.ts +3 -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 +56 -0
- package/dist/types/defaults/gjc-defaults.d.ts +19 -6
- 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 +3 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/theme/defaults/index.d.ts +126 -0
- package/dist/types/modes/theme/theme.d.ts +5 -0
- package/dist/types/modes/types.d.ts +1 -0
- 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/update-cli.ts +67 -16
- 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 +63 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +58 -5
- package/src/defaults/gjc/skills/deep-interview/auto-answer-uncertain.md +37 -0
- package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +42 -0
- 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 +19 -6
- package/src/defaults/gjc-defaults.ts +68 -16
- package/src/discovery/helpers.ts +24 -1
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +312 -1
- package/src/gjc-runtime/state-runtime.ts +175 -5
- package/src/goals/tools/goal-tool.ts +5 -1
- package/src/hooks/skill-state.ts +8 -6
- package/src/internal-urls/docs-index.generated.ts +6 -4
- package/src/internal-urls/memory-protocol.ts +3 -2
- package/src/main.ts +2 -3
- package/src/memories/index.ts +6 -4
- 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 +16 -12
- package/src/modes/controllers/command-controller.ts +3 -4
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/selector-controller.ts +69 -9
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/theme/defaults/blue-crab.json +126 -0
- package/src/modes/theme/defaults/index.ts +2 -0
- package/src/modes/theme/theme.ts +40 -1
- package/src/modes/types.ts +1 -0
- 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/memories/unavailable.md +9 -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 +54 -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 +62 -14
- 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
|
@@ -3,12 +3,14 @@ import * as fs from "node:fs/promises";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import type { WorkflowHudSummary } from "../skill-state/active-state";
|
|
5
5
|
import {
|
|
6
|
+
applyHandoffToActiveState,
|
|
6
7
|
CANONICAL_GJC_WORKFLOW_SKILLS,
|
|
7
8
|
type CanonicalGjcWorkflowSkill,
|
|
8
9
|
listActiveSkills,
|
|
9
10
|
readVisibleSkillActiveState,
|
|
10
11
|
syncSkillActiveState,
|
|
11
12
|
} from "../skill-state/active-state";
|
|
13
|
+
import { initialPhaseForSkill } from "../skill-state/initial-phase";
|
|
12
14
|
import {
|
|
13
15
|
buildDeepInterviewHudSummary,
|
|
14
16
|
buildRalplanHudSummary,
|
|
@@ -60,11 +62,11 @@ function hasFlag(args: readonly string[], flag: string): boolean {
|
|
|
60
62
|
return args.includes(flag);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
const FLAGS_WITH_VALUES = new Set(["--input", "--mode", "--session-id", "--thread-id", "--turn-id"]);
|
|
64
|
-
const ACTION_NAMES = new Set(["read", "write", "clear", "contract"]);
|
|
65
|
+
const FLAGS_WITH_VALUES = new Set(["--input", "--mode", "--session-id", "--thread-id", "--turn-id", "--to"]);
|
|
66
|
+
const ACTION_NAMES = new Set(["read", "write", "clear", "contract", "handoff"]);
|
|
65
67
|
|
|
66
68
|
interface ParsedInvocation {
|
|
67
|
-
action: "read" | "write" | "clear" | "contract";
|
|
69
|
+
action: "read" | "write" | "clear" | "contract" | "handoff";
|
|
68
70
|
positionalSkill?: string;
|
|
69
71
|
}
|
|
70
72
|
|
|
@@ -169,10 +171,20 @@ async function resolveSelectors(
|
|
|
169
171
|
}
|
|
170
172
|
if (mode) assertKnownMode(mode);
|
|
171
173
|
|
|
172
|
-
|
|
174
|
+
const explicitSessionId = flagValue(args, "--session-id");
|
|
175
|
+
// Session-id resolution order: explicit --session-id flag, then payload
|
|
176
|
+
// session_id, then GJC_SESSION_ID env var (set by AgentSession.sdk for
|
|
177
|
+
// agent-initiated CLI invocations). The env-var default keeps shell
|
|
178
|
+
// snippets in skill docs short while still routing writes/reads to the
|
|
179
|
+
// caller's session-scoped state files.
|
|
180
|
+
let sessionId = explicitSessionId !== undefined ? explicitSessionId.trim() || undefined : undefined;
|
|
173
181
|
if (!sessionId && payload && typeof payload.session_id === "string") {
|
|
174
182
|
sessionId = payload.session_id.trim() || undefined;
|
|
175
183
|
}
|
|
184
|
+
if (!sessionId && explicitSessionId === undefined) {
|
|
185
|
+
const envSessionId = process.env.GJC_SESSION_ID?.trim();
|
|
186
|
+
if (envSessionId) sessionId = envSessionId;
|
|
187
|
+
}
|
|
176
188
|
if (sessionId) assertSafePathComponent(sessionId, "session-id");
|
|
177
189
|
|
|
178
190
|
const threadId = flagValue(args, "--thread-id")?.trim() || undefined;
|
|
@@ -396,7 +408,6 @@ async function syncWorkflowSkillState(options: {
|
|
|
396
408
|
// HUD sync is best-effort and must not change command semantics.
|
|
397
409
|
}
|
|
398
410
|
}
|
|
399
|
-
|
|
400
411
|
async function handleRead(
|
|
401
412
|
args: readonly string[],
|
|
402
413
|
cwd: string,
|
|
@@ -527,6 +538,163 @@ async function handleClear(
|
|
|
527
538
|
return { status: 0, stdout: `${JSON.stringify(cleared, null, 2)}\n` };
|
|
528
539
|
}
|
|
529
540
|
|
|
541
|
+
/**
|
|
542
|
+
* `handoff` exists in two distinct roles:
|
|
543
|
+
* - As a verb: this CLI action, which atomically transitions caller→callee.
|
|
544
|
+
* Writes the callee mode-state first, the caller mode-state second, then
|
|
545
|
+
* syncs both `skill-active-state.json` files. Every intermediate crashed
|
|
546
|
+
* state remains HUD-coherent: the active-state file either reflects the
|
|
547
|
+
* old skill entirely or the new skill entirely, never both as active.
|
|
548
|
+
* - As a phase: `current_phase: "handoff"` is set by this verb when demoting
|
|
549
|
+
* the caller. Agents writing `current_phase: "handoff"` manually via
|
|
550
|
+
* `gjc state <skill> write` are declaring "I am ready to be handed off";
|
|
551
|
+
* the next agent-initiated `skill` tool call will then satisfy the phase
|
|
552
|
+
* guard and may chain.
|
|
553
|
+
*
|
|
554
|
+
* `handoff` is in the terminal-phase set used by `isTerminalModeState` and by
|
|
555
|
+
* the skill tool's chain guard. A manual `current_phase: "handoff"` write does
|
|
556
|
+
* NOT mark `active: false` — only this verb does that — so a skill that wrote
|
|
557
|
+
* the phase remains in `skill-active-state.json` until a chain call (or
|
|
558
|
+
* explicit `clear`) demotes it.
|
|
559
|
+
*/
|
|
560
|
+
async function handleHandoff(
|
|
561
|
+
args: readonly string[],
|
|
562
|
+
cwd: string,
|
|
563
|
+
positionalSkill: string | undefined,
|
|
564
|
+
): Promise<StateCommandResult> {
|
|
565
|
+
const selectors = await resolveSelectors(args, cwd, positionalSkill);
|
|
566
|
+
const { sessionId, threadId, turnId } = selectors;
|
|
567
|
+
const caller = selectors.mode ?? (await inferModeFromActiveState(cwd, sessionId));
|
|
568
|
+
if (!caller) {
|
|
569
|
+
throw new StateCommandError(
|
|
570
|
+
2,
|
|
571
|
+
"gjc state handoff requires --mode <caller>, positional <caller>, input.skill, or an active workflow in .gjc/state/skill-active-state.json",
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
const calleeRaw = flagValue(args, "--to")?.trim();
|
|
575
|
+
if (!calleeRaw) {
|
|
576
|
+
throw new StateCommandError(2, "gjc state handoff requires --to <callee>");
|
|
577
|
+
}
|
|
578
|
+
assertKnownMode(calleeRaw);
|
|
579
|
+
const callee = calleeRaw as CanonicalGjcWorkflowSkill;
|
|
580
|
+
if (callee === caller) {
|
|
581
|
+
throw new StateCommandError(2, `gjc state handoff: --to must differ from caller (both are "${caller}")`);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const callerPath = modeStateFile(cwd, caller, sessionId);
|
|
585
|
+
const calleePath = modeStateFile(cwd, callee, sessionId);
|
|
586
|
+
const existingCaller = await readJsonFile(callerPath);
|
|
587
|
+
if (!existingCaller) {
|
|
588
|
+
throw new StateCommandError(
|
|
589
|
+
2,
|
|
590
|
+
`gjc state ${caller} handoff: caller is not active (no mode-state file at ${callerPath})`,
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
const existingCallee = (await readJsonFile(calleePath)) ?? {};
|
|
594
|
+
|
|
595
|
+
const handoffAt = nowIso();
|
|
596
|
+
const callerReceipt = buildWorkflowStateReceipt({
|
|
597
|
+
cwd,
|
|
598
|
+
skill: caller,
|
|
599
|
+
owner: "gjc-state-cli",
|
|
600
|
+
command: `gjc state ${caller} handoff --to ${callee}`,
|
|
601
|
+
sessionId,
|
|
602
|
+
nowIso: handoffAt,
|
|
603
|
+
});
|
|
604
|
+
const calleeReceipt = buildWorkflowStateReceipt({
|
|
605
|
+
cwd,
|
|
606
|
+
skill: callee,
|
|
607
|
+
owner: "gjc-state-cli",
|
|
608
|
+
command: `gjc state ${caller} handoff --to ${callee}`,
|
|
609
|
+
sessionId,
|
|
610
|
+
nowIso: handoffAt,
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
const calleeInitial = initialPhaseForSkill(callee);
|
|
614
|
+
const mergedCalleeState: Record<string, unknown> = {
|
|
615
|
+
...existingCallee,
|
|
616
|
+
skill: callee,
|
|
617
|
+
version: typeof existingCallee.version === "number" ? existingCallee.version : 1,
|
|
618
|
+
active: true,
|
|
619
|
+
current_phase: calleeInitial,
|
|
620
|
+
handoff_from: caller,
|
|
621
|
+
handoff_at: handoffAt,
|
|
622
|
+
updated_at: handoffAt,
|
|
623
|
+
receipt: calleeReceipt,
|
|
624
|
+
};
|
|
625
|
+
if (sessionId && typeof mergedCalleeState.session_id !== "string") {
|
|
626
|
+
mergedCalleeState.session_id = sessionId;
|
|
627
|
+
}
|
|
628
|
+
const mergedCallerState: Record<string, unknown> = {
|
|
629
|
+
...existingCaller,
|
|
630
|
+
skill: caller,
|
|
631
|
+
active: false,
|
|
632
|
+
current_phase: "handoff",
|
|
633
|
+
handoff_to: callee,
|
|
634
|
+
handoff_at: handoffAt,
|
|
635
|
+
updated_at: handoffAt,
|
|
636
|
+
receipt: callerReceipt,
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
// Atomic write order (architecture blocker AR-3): mode-state files first,
|
|
640
|
+
// then a single atomic active-state mutation per file (session before root)
|
|
641
|
+
// via applyHandoffToActiveState. The single-write transaction prevents the
|
|
642
|
+
// HUD from observing a window where neither caller nor callee is active,
|
|
643
|
+
// and write order keeps the session-scoped source of truth ahead of the
|
|
644
|
+
// root aggregate. strict:true on the active-state read tolerates ENOENT
|
|
645
|
+
// only; corrupt JSON / IO failures propagate as non-zero CLI status.
|
|
646
|
+
await writeJsonAtomic(calleePath, mergedCalleeState);
|
|
647
|
+
await writeJsonAtomic(callerPath, mergedCallerState);
|
|
648
|
+
await applyHandoffToActiveState({
|
|
649
|
+
cwd,
|
|
650
|
+
nowIso: handoffAt,
|
|
651
|
+
strict: true,
|
|
652
|
+
caller: {
|
|
653
|
+
cwd,
|
|
654
|
+
skill: caller,
|
|
655
|
+
active: false,
|
|
656
|
+
phase: "handoff",
|
|
657
|
+
sessionId,
|
|
658
|
+
threadId,
|
|
659
|
+
turnId,
|
|
660
|
+
source: "gjc-state-cli",
|
|
661
|
+
hud: buildHudForMode(caller, mergedCallerState),
|
|
662
|
+
handoff_to: callee,
|
|
663
|
+
handoff_at: handoffAt,
|
|
664
|
+
receipt: callerReceipt,
|
|
665
|
+
},
|
|
666
|
+
callee: {
|
|
667
|
+
cwd,
|
|
668
|
+
skill: callee,
|
|
669
|
+
active: true,
|
|
670
|
+
phase: calleeInitial,
|
|
671
|
+
sessionId,
|
|
672
|
+
threadId,
|
|
673
|
+
turnId,
|
|
674
|
+
source: "gjc-state-cli",
|
|
675
|
+
hud: buildHudForMode(callee, mergedCalleeState),
|
|
676
|
+
handoff_from: caller,
|
|
677
|
+
handoff_at: handoffAt,
|
|
678
|
+
receipt: calleeReceipt,
|
|
679
|
+
},
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
return {
|
|
683
|
+
status: 0,
|
|
684
|
+
stdout: `${JSON.stringify(
|
|
685
|
+
{
|
|
686
|
+
from: caller,
|
|
687
|
+
to: callee,
|
|
688
|
+
handoff_at: handoffAt,
|
|
689
|
+
caller_state: mergedCallerState,
|
|
690
|
+
callee_state: mergedCalleeState,
|
|
691
|
+
},
|
|
692
|
+
null,
|
|
693
|
+
2,
|
|
694
|
+
)}\n`,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
530
698
|
async function handleContract(
|
|
531
699
|
args: readonly string[],
|
|
532
700
|
cwd: string,
|
|
@@ -552,6 +720,8 @@ export async function runNativeStateCommand(args: string[], cwd = process.cwd())
|
|
|
552
720
|
return await handleClear(args, cwd, parsed.positionalSkill);
|
|
553
721
|
case "contract":
|
|
554
722
|
return await handleContract(args, cwd, parsed.positionalSkill);
|
|
723
|
+
case "handoff":
|
|
724
|
+
return await handleHandoff(args, cwd, parsed.positionalSkill);
|
|
555
725
|
default:
|
|
556
726
|
return { status: 2, stderr: `Unknown gjc state command: ${parsed.action}\n` };
|
|
557
727
|
}
|
|
@@ -16,7 +16,11 @@ import { validateGoalObjective } from "../runtime";
|
|
|
16
16
|
import type { Goal, GoalStatus, GoalToolDetails } from "../state";
|
|
17
17
|
|
|
18
18
|
const goalSchema = z.object({
|
|
19
|
-
op: z
|
|
19
|
+
op: z
|
|
20
|
+
.enum(["create", "get", "complete", "resume", "drop"])
|
|
21
|
+
.describe(
|
|
22
|
+
"op: get | create | complete | drop | resume — drop clears the active goal without exiting goal mode (tool stays callable for the next create)",
|
|
23
|
+
),
|
|
20
24
|
objective: z.string().describe("goal objective").optional(),
|
|
21
25
|
});
|
|
22
26
|
|
package/src/hooks/skill-state.ts
CHANGED
|
@@ -112,6 +112,9 @@ export interface ModeState {
|
|
|
112
112
|
thread_id?: string;
|
|
113
113
|
cwd?: string;
|
|
114
114
|
updated_at?: string;
|
|
115
|
+
handoff_from?: string;
|
|
116
|
+
handoff_to?: string;
|
|
117
|
+
handoff_at?: string;
|
|
115
118
|
[key: string]: unknown;
|
|
116
119
|
}
|
|
117
120
|
|
|
@@ -220,11 +223,10 @@ function encodeStatePathSegment(value: string): string {
|
|
|
220
223
|
return encodeURIComponent(value).replaceAll(".", "%2E");
|
|
221
224
|
}
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
226
|
+
import { initialPhaseForSkill } from "../skill-state/initial-phase";
|
|
227
|
+
|
|
228
|
+
// Re-export for existing callers and tests that imported it from this module.
|
|
229
|
+
export { initialPhaseForSkill };
|
|
228
230
|
|
|
229
231
|
function modeStateFileName(skill: GjcWorkflowSkill): string {
|
|
230
232
|
return `${skill}-state.json`;
|
|
@@ -347,7 +349,7 @@ function isTerminalModeState(state: ModeState | null): boolean {
|
|
|
347
349
|
const phase = String(state.current_phase ?? "")
|
|
348
350
|
.trim()
|
|
349
351
|
.toLowerCase();
|
|
350
|
-
return ["complete", "completed", "failed", "cancelled", "canceled", "inactive"].includes(phase);
|
|
352
|
+
return ["complete", "completed", "handoff", "failed", "cancelled", "canceled", "inactive"].includes(phase);
|
|
351
353
|
}
|
|
352
354
|
|
|
353
355
|
async function readVisibleModeState(
|