@gajae-code/coding-agent 0.2.1 → 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 +59 -1
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/commands/contribution-prep.d.ts +18 -0
- package/dist/types/commands/deep-interview.d.ts +41 -0
- package/dist/types/commands/session.d.ts +24 -0
- package/dist/types/commands/setup.d.ts +3 -0
- package/dist/types/config/model-registry.d.ts +2 -2
- package/dist/types/config/models-config-schema.d.ts +17 -9
- package/dist/types/config/settings-schema.d.ts +37 -24
- 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 +33 -0
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +12 -11
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +25 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +13 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +37 -5
- package/dist/types/gjc-runtime/tmux-common.d.ts +41 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +17 -0
- package/dist/types/goals/runtime.d.ts +3 -9
- package/dist/types/goals/state.d.ts +3 -6
- package/dist/types/goals/tools/goal-tool.d.ts +1 -69
- 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/components/status-line/types.d.ts +0 -3
- package/dist/types/modes/components/status-line.d.ts +0 -3
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -12
- package/dist/types/modes/theme/defaults/index.d.ts +0 -2
- package/dist/types/modes/theme/theme.d.ts +1 -2
- package/dist/types/modes/types.d.ts +1 -7
- 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 +47 -1
- package/dist/types/session/contribution-prep.d.ts +47 -0
- 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 +30 -1
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +6 -1
- package/dist/types/skill-state/initial-phase.d.ts +12 -0
- package/dist/types/skill-state/workflow-hud.d.ts +9 -4
- package/dist/types/skill-state/workflow-state-contract.d.ts +34 -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/args.ts +3 -2
- package/src/cli/setup-cli.ts +26 -12
- package/src/cli.ts +7 -1
- package/src/commands/contribution-prep.ts +41 -0
- package/src/commands/deep-interview.ts +30 -23
- package/src/commands/launch.ts +10 -1
- package/src/commands/ralplan.ts +10 -22
- package/src/commands/session.ts +150 -0
- package/src/commands/setup.ts +2 -0
- package/src/commands/state.ts +15 -4
- package/src/commands/team.ts +23 -3
- package/src/config/model-registry.ts +10 -2
- package/src/config/models-config-schema.ts +120 -102
- package/src/config/settings-schema.ts +42 -25
- package/src/config.ts +1 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +32 -13
- package/src/defaults/gjc/skills/ralplan/SKILL.md +22 -2
- package/src/defaults/gjc/skills/team/SKILL.md +39 -7
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +33 -25
- package/src/discovery/helpers.ts +24 -1
- package/src/eval/py/prelude.py +1 -1
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +546 -0
- package/src/gjc-runtime/goal-mode-request.ts +2 -19
- package/src/gjc-runtime/launch-tmux.ts +83 -43
- package/src/gjc-runtime/ralplan-runtime.ts +460 -0
- package/src/gjc-runtime/state-runtime.ts +731 -0
- package/src/gjc-runtime/team-runtime.ts +708 -52
- package/src/gjc-runtime/tmux-common.ts +119 -0
- package/src/gjc-runtime/tmux-sessions.ts +165 -0
- package/src/gjc-runtime/ultragoal-guard.ts +6 -3
- package/src/gjc-runtime/ultragoal-runtime.ts +5 -4
- package/src/goals/runtime.ts +38 -144
- package/src/goals/state.ts +36 -7
- package/src/goals/tools/goal-tool.ts +15 -172
- package/src/hooks/skill-state.ts +39 -18
- package/src/internal-urls/docs-index.generated.ts +5 -4
- 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/components/skill-hud/render.ts +4 -0
- package/src/modes/components/status-line/segments.ts +5 -16
- package/src/modes/components/status-line/types.ts +0 -3
- package/src/modes/components/status-line.ts +0 -6
- package/src/modes/controllers/command-controller.ts +27 -4
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +0 -15
- package/src/modes/controllers/selector-controller.ts +4 -11
- package/src/modes/interactive-mode.ts +18 -219
- package/src/modes/theme/defaults/dark-poimandres.json +0 -1
- package/src/modes/theme/defaults/light-poimandres.json +0 -1
- package/src/modes/theme/theme.ts +0 -6
- package/src/modes/types.ts +1 -7
- 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/goals/goal-continuation.md +1 -4
- package/src/prompts/goals/goal-mode-active.md +3 -5
- package/src/prompts/system/subagent-system-prompt.md +6 -0
- package/src/prompts/system/system-prompt.md +5 -7
- package/src/prompts/tools/goal.md +4 -4
- package/src/prompts/tools/skill.md +28 -0
- package/src/prompts/tools/task.md +3 -0
- package/src/sdk.ts +51 -11
- package/src/session/agent-session.ts +222 -21
- package/src/session/contribution-prep.ts +320 -0
- 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 +188 -25
- package/src/skill-state/deep-interview-mutation-guard.ts +72 -21
- package/src/skill-state/initial-phase.ts +17 -0
- package/src/skill-state/workflow-hud.ts +23 -5
- package/src/skill-state/workflow-state-contract.ts +121 -0
- package/src/slash-commands/builtin-registry.ts +75 -25
- package/src/slash-commands/helpers/context-report.ts +123 -13
- package/src/task/agents.ts +1 -0
- package/src/task/commands.ts +1 -5
- 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/gh.ts +212 -2
- package/src/tools/index.ts +25 -6
- package/src/tools/skill.ts +153 -0
- package/src/utils/changelog.ts +67 -44
- package/dist/types/commands/gjc-runtime-bridge.d.ts +0 -30
- package/dist/types/commands/question.d.ts +0 -7
- package/dist/types/modes/loop-limit.d.ts +0 -22
- package/src/commands/gjc-runtime-bridge.ts +0 -227
- package/src/commands/question.ts +0 -12
- package/src/modes/loop-limit.ts +0 -140
- package/src/prompts/commands/orchestrate.md +0 -49
- package/src/prompts/goals/goal-budget-limit.md +0 -16
- package/src/prompts/tools/create-goal.md +0 -3
- package/src/prompts/tools/get-goal.md +0 -3
- package/src/prompts/tools/update-goal.md +0 -3
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export const GJC_DEFAULT_TMUX_SESSION = "gajae_code";
|
|
2
|
+
export const GJC_TMUX_SESSION_PREFIX = `${GJC_DEFAULT_TMUX_SESSION}_`;
|
|
3
|
+
export const GJC_TMUX_COMMAND_ENV = "GJC_TMUX_COMMAND";
|
|
4
|
+
export const GJC_TMUX_PROFILE_ENV = "GJC_TMUX_PROFILE";
|
|
5
|
+
export const GJC_TMUX_MOUSE_ENV = "GJC_MOUSE";
|
|
6
|
+
export const GJC_TMUX_PROFILE_OPTION = "@gjc-profile";
|
|
7
|
+
export const GJC_TMUX_PROFILE_VALUE = "1";
|
|
8
|
+
export const GJC_TMUX_BRANCH_OPTION = "@gjc-branch";
|
|
9
|
+
export const GJC_TMUX_BRANCH_SLUG_OPTION = "@gjc-branch-slug";
|
|
10
|
+
export const GJC_TMUX_PROJECT_OPTION = "@gjc-project";
|
|
11
|
+
|
|
12
|
+
export interface GjcTmuxProfileCommand {
|
|
13
|
+
description: string;
|
|
14
|
+
args: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TmuxCommandResult {
|
|
18
|
+
exitCode: number | null;
|
|
19
|
+
stdout?: string;
|
|
20
|
+
stderr?: string;
|
|
21
|
+
signalCode?: string | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type TmuxCommandRunner = (args: string[]) => TmuxCommandResult;
|
|
25
|
+
|
|
26
|
+
export function envDisabled(value: string | undefined): boolean {
|
|
27
|
+
const normalized = value?.trim().toLowerCase();
|
|
28
|
+
return normalized === "0" || normalized === "false" || normalized === "off" || normalized === "no";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveGjcTmuxCommand(env: NodeJS.ProcessEnv = process.env): string {
|
|
32
|
+
return env[GJC_TMUX_COMMAND_ENV]?.trim() || env.GJC_TEAM_TMUX_COMMAND?.trim() || "tmux";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function sanitizeTmuxToken(value: string): string {
|
|
36
|
+
return (
|
|
37
|
+
value
|
|
38
|
+
.toLowerCase()
|
|
39
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
40
|
+
.replace(/-+/g, "-")
|
|
41
|
+
.replace(/^-|-$/g, "") || "default"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function buildGjcTmuxSessionSlug(value: string): string {
|
|
46
|
+
return sanitizeTmuxToken(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function randomTmuxSessionSuffix(): string {
|
|
50
|
+
return Math.random().toString(36).slice(2, 10);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function buildGjcTmuxSessionName(
|
|
54
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
55
|
+
context: { branch?: string | null; now?: number; id?: string } = {},
|
|
56
|
+
): string {
|
|
57
|
+
const explicit = env.GJC_TMUX_SESSION?.trim();
|
|
58
|
+
if (explicit) return explicit;
|
|
59
|
+
const timestamp = (context.now ?? Date.now()).toString(36);
|
|
60
|
+
const id = context.id ?? randomTmuxSessionSuffix();
|
|
61
|
+
const branchSlug = context.branch ? `${buildGjcTmuxSessionSlug(context.branch)}_` : "";
|
|
62
|
+
return `${GJC_TMUX_SESSION_PREFIX}${branchSlug}${timestamp}_${id}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function buildGjcTmuxRequiredProfileCommands(
|
|
66
|
+
target: string,
|
|
67
|
+
metadata: { branch?: string | null; branchSlug?: string | null; project?: string | null } = {},
|
|
68
|
+
): GjcTmuxProfileCommand[] {
|
|
69
|
+
const commands: GjcTmuxProfileCommand[] = [
|
|
70
|
+
{
|
|
71
|
+
description: "mark GJC tmux ownership",
|
|
72
|
+
args: ["set-option", "-t", target, GJC_TMUX_PROFILE_OPTION, GJC_TMUX_PROFILE_VALUE],
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
if (metadata.branch)
|
|
76
|
+
commands.push({
|
|
77
|
+
description: "record GJC branch identity",
|
|
78
|
+
args: ["set-option", "-t", target, GJC_TMUX_BRANCH_OPTION, metadata.branch],
|
|
79
|
+
});
|
|
80
|
+
if (metadata.branchSlug)
|
|
81
|
+
commands.push({
|
|
82
|
+
description: "record GJC branch slug",
|
|
83
|
+
args: ["set-option", "-t", target, GJC_TMUX_BRANCH_SLUG_OPTION, metadata.branchSlug],
|
|
84
|
+
});
|
|
85
|
+
if (metadata.project)
|
|
86
|
+
commands.push({
|
|
87
|
+
description: "record GJC project identity",
|
|
88
|
+
args: ["set-option", "-t", target, GJC_TMUX_PROJECT_OPTION, metadata.project],
|
|
89
|
+
});
|
|
90
|
+
return commands;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function buildGjcTmuxProfileCommands(
|
|
94
|
+
target: string,
|
|
95
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
96
|
+
metadata: { branch?: string | null; branchSlug?: string | null; project?: string | null } = {},
|
|
97
|
+
): GjcTmuxProfileCommand[] {
|
|
98
|
+
const commands = buildGjcTmuxRequiredProfileCommands(target, metadata);
|
|
99
|
+
if (envDisabled(env[GJC_TMUX_PROFILE_ENV])) return commands;
|
|
100
|
+
commands.push(
|
|
101
|
+
{ description: "enable tmux clipboard integration", args: ["set-option", "-t", target, "set-clipboard", "on"] },
|
|
102
|
+
{
|
|
103
|
+
description: "make copy-mode selection readable",
|
|
104
|
+
args: ["set-window-option", "-t", target, "mode-style", "fg=colour231,bg=colour60"],
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
if (!envDisabled(env[GJC_TMUX_MOUSE_ENV]))
|
|
108
|
+
commands.unshift({
|
|
109
|
+
description: "enable tmux mouse scrolling",
|
|
110
|
+
args: ["set-option", "-t", target, "mouse", "on"],
|
|
111
|
+
});
|
|
112
|
+
return commands;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function normalizeTmuxCreatedAt(raw: string): string {
|
|
116
|
+
const seconds = Number.parseInt(raw, 10);
|
|
117
|
+
if (!Number.isFinite(seconds) || seconds <= 0) return raw;
|
|
118
|
+
return new Date(seconds * 1000).toISOString();
|
|
119
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildGjcTmuxProfileCommands,
|
|
3
|
+
buildGjcTmuxSessionName,
|
|
4
|
+
GJC_TMUX_BRANCH_OPTION,
|
|
5
|
+
GJC_TMUX_BRANCH_SLUG_OPTION,
|
|
6
|
+
GJC_TMUX_PROFILE_OPTION,
|
|
7
|
+
GJC_TMUX_PROFILE_VALUE,
|
|
8
|
+
GJC_TMUX_PROJECT_OPTION,
|
|
9
|
+
normalizeTmuxCreatedAt,
|
|
10
|
+
resolveGjcTmuxCommand,
|
|
11
|
+
} from "./tmux-common";
|
|
12
|
+
|
|
13
|
+
export interface GjcTmuxSessionStatus {
|
|
14
|
+
name: string;
|
|
15
|
+
attached: boolean;
|
|
16
|
+
windows: number;
|
|
17
|
+
panes: number;
|
|
18
|
+
bindings: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
branch?: string;
|
|
21
|
+
branchSlug?: string;
|
|
22
|
+
project?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function runTmux(args: string[], env: NodeJS.ProcessEnv = process.env): string {
|
|
26
|
+
const tmuxCommand = resolveGjcTmuxCommand(env);
|
|
27
|
+
const result = Bun.spawnSync([tmuxCommand, ...args], { stdout: "pipe", stderr: "pipe", env });
|
|
28
|
+
if (result.exitCode === 0) return result.stdout.toString();
|
|
29
|
+
throw new Error(result.stderr.toString().trim() || `tmux ${args.join(" ")} failed`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function tryKillSession(sessionName: string, env: NodeJS.ProcessEnv): void {
|
|
33
|
+
try {
|
|
34
|
+
runTmux(["kill-session", "-t", `=${sessionName}`], env);
|
|
35
|
+
} catch {
|
|
36
|
+
// Best-effort cleanup only; preserve the original create/tag failure.
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseBooleanFlag(value: string | undefined): boolean {
|
|
41
|
+
return value === "1";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseNumber(value: string | undefined): number {
|
|
45
|
+
const parsed = Number.parseInt(value ?? "0", 10);
|
|
46
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseSessionLine(line: string): GjcTmuxSessionStatus | null {
|
|
50
|
+
const [
|
|
51
|
+
name = "",
|
|
52
|
+
windows = "0",
|
|
53
|
+
attached = "0",
|
|
54
|
+
created = "",
|
|
55
|
+
profile = "",
|
|
56
|
+
bindings = "",
|
|
57
|
+
panes = "0",
|
|
58
|
+
branch = "",
|
|
59
|
+
branchSlug = "",
|
|
60
|
+
project = "",
|
|
61
|
+
] = line.split("\t");
|
|
62
|
+
if (!name || profile !== GJC_TMUX_PROFILE_VALUE) return null;
|
|
63
|
+
return {
|
|
64
|
+
name,
|
|
65
|
+
attached: parseBooleanFlag(attached),
|
|
66
|
+
windows: parseNumber(windows),
|
|
67
|
+
panes: parseNumber(panes),
|
|
68
|
+
bindings,
|
|
69
|
+
createdAt: normalizeTmuxCreatedAt(created),
|
|
70
|
+
branch: branch || undefined,
|
|
71
|
+
branchSlug: branchSlug || undefined,
|
|
72
|
+
project: project || undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function listSessionLines(env: NodeJS.ProcessEnv = process.env): string[] {
|
|
77
|
+
let output = "";
|
|
78
|
+
try {
|
|
79
|
+
output = runTmux(
|
|
80
|
+
[
|
|
81
|
+
"list-sessions",
|
|
82
|
+
"-F",
|
|
83
|
+
`#{session_name}\t#{session_windows}\t#{session_attached}\t#{session_created}\t#{${GJC_TMUX_PROFILE_OPTION}}\t#{session_key_table}\t#{session_panes}\t#{${GJC_TMUX_BRANCH_OPTION}}\t#{${GJC_TMUX_BRANCH_SLUG_OPTION}}\t#{${GJC_TMUX_PROJECT_OPTION}}`,
|
|
84
|
+
],
|
|
85
|
+
env,
|
|
86
|
+
);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
89
|
+
if (message.includes("no server running") || message.includes("failed to connect to server")) return [];
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
return output
|
|
93
|
+
.split("\n")
|
|
94
|
+
.map(line => line.trim())
|
|
95
|
+
.filter(Boolean);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function listGjcTmuxSessions(env: NodeJS.ProcessEnv = process.env): GjcTmuxSessionStatus[] {
|
|
99
|
+
return listSessionLines(env)
|
|
100
|
+
.map(parseSessionLine)
|
|
101
|
+
.filter((session): session is GjcTmuxSessionStatus => session != null)
|
|
102
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function findGjcTmuxSessionByBranch(
|
|
106
|
+
branch: string,
|
|
107
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
108
|
+
project?: string | null,
|
|
109
|
+
): GjcTmuxSessionStatus | undefined {
|
|
110
|
+
return listGjcTmuxSessions(env).find(
|
|
111
|
+
session => session.branch === branch && (!project || session.project === project),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function statusGjcTmuxSession(sessionName: string, env: NodeJS.ProcessEnv = process.env): GjcTmuxSessionStatus {
|
|
116
|
+
const session = listGjcTmuxSessions(env).find(candidate => candidate.name === sessionName);
|
|
117
|
+
if (!session) throw new Error(`gjc_tmux_session_not_found:${sessionName}`);
|
|
118
|
+
return session;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function createGjcTmuxSession(env: NodeJS.ProcessEnv = process.env): GjcTmuxSessionStatus {
|
|
122
|
+
const tmuxCommand = resolveGjcTmuxCommand(env);
|
|
123
|
+
const sessionName = buildGjcTmuxSessionName(env);
|
|
124
|
+
const command = "exec env GJC_TMUX_LAUNCHED=1 gjc";
|
|
125
|
+
const created = Bun.spawnSync([tmuxCommand, "new-session", "-d", "-s", sessionName, command], {
|
|
126
|
+
stdout: "pipe",
|
|
127
|
+
stderr: "pipe",
|
|
128
|
+
env,
|
|
129
|
+
});
|
|
130
|
+
if (created.exitCode !== 0) throw new Error(created.stderr.toString().trim() || "gjc_tmux_session_create_failed");
|
|
131
|
+
try {
|
|
132
|
+
for (const profileCommand of buildGjcTmuxProfileCommands(sessionName, env)) {
|
|
133
|
+
runTmux(profileCommand.args, env);
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
tryKillSession(sessionName, env);
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
return statusGjcTmuxSession(sessionName, env);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function readProfileForExactTarget(sessionName: string, env: NodeJS.ProcessEnv): string {
|
|
143
|
+
return runTmux(["show-options", "-qv", "-t", `=${sessionName}`, GJC_TMUX_PROFILE_OPTION], env).trim();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function removeGjcTmuxSession(sessionName: string, env: NodeJS.ProcessEnv = process.env): GjcTmuxSessionStatus {
|
|
147
|
+
const session = statusGjcTmuxSession(sessionName, env);
|
|
148
|
+
if (readProfileForExactTarget(session.name, env) !== GJC_TMUX_PROFILE_VALUE) {
|
|
149
|
+
throw new Error(`gjc_tmux_session_not_managed:${sessionName}`);
|
|
150
|
+
}
|
|
151
|
+
runTmux(["kill-session", "-t", `=${session.name}`], env);
|
|
152
|
+
return session;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function attachGjcTmuxSession(sessionName: string, env: NodeJS.ProcessEnv = process.env): never {
|
|
156
|
+
const session = statusGjcTmuxSession(sessionName, env);
|
|
157
|
+
const tmuxCommand = resolveGjcTmuxCommand(env);
|
|
158
|
+
const result = Bun.spawnSync([tmuxCommand, "attach-session", "-t", `=${session.name}`], {
|
|
159
|
+
stdin: "inherit",
|
|
160
|
+
stdout: "inherit",
|
|
161
|
+
stderr: "inherit",
|
|
162
|
+
env,
|
|
163
|
+
});
|
|
164
|
+
process.exit(result.exitCode ?? 1);
|
|
165
|
+
}
|
|
@@ -171,7 +171,7 @@ export function validateCompletionReceipt(input: {
|
|
|
171
171
|
if (hashStructuredValue(event.gjcGoalJson) !== receipt.gjcGoalSnapshotHash) {
|
|
172
172
|
return {
|
|
173
173
|
state: "active_stale_receipt",
|
|
174
|
-
message: `Ultragoal ${input.goal.id} receipt
|
|
174
|
+
message: `Ultragoal ${input.goal.id} receipt goal({"op":"get"}) snapshot hash does not match ledger.`,
|
|
175
175
|
goalId: input.goal.id,
|
|
176
176
|
};
|
|
177
177
|
}
|
|
@@ -275,7 +275,10 @@ export async function assertCanCompleteCurrentGoal(input: {
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
export function isUltragoalBypassPrompt(prompt: string): boolean {
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
const normalized = prompt.replace(/\\?"/g, '"');
|
|
279
|
+
return (
|
|
280
|
+
/update_goal\s*\(|goal\s+complete|checkpoint[^\n]+--status\s+complete|skip\s+verification|weaken\s+verification|mark\s+.*complete/i.test(
|
|
281
|
+
normalized,
|
|
282
|
+
) || /goal[\s\S]{0,80}complete/i.test(normalized)
|
|
280
283
|
);
|
|
281
284
|
}
|
|
@@ -645,7 +645,7 @@ async function readGjcGoalSnapshot(input: {
|
|
|
645
645
|
}): Promise<unknown> {
|
|
646
646
|
if (!input.value?.trim()) {
|
|
647
647
|
if (!input.required) return undefined;
|
|
648
|
-
throw new Error(`${input.errorPrefix} require --gjc-goal-json from a fresh active
|
|
648
|
+
throw new Error(`${input.errorPrefix} require --gjc-goal-json from a fresh active goal({"op":"get"}) snapshot`);
|
|
649
649
|
}
|
|
650
650
|
const snapshot = await readStructuredValue(input.cwd, input.value);
|
|
651
651
|
const snapshotObject = qualityGateObject(snapshot);
|
|
@@ -653,7 +653,8 @@ async function readGjcGoalSnapshot(input: {
|
|
|
653
653
|
const goalObject = qualityGateObject(snapshotObject?.goal) ?? qualityGateObject(detailsObject?.goal);
|
|
654
654
|
if (!goalObject) throw new Error(`${input.errorPrefix} require --gjc-goal-json with a goal object`);
|
|
655
655
|
const updatedAt = typeof goalObject.updatedAt === "number" ? goalObject.updatedAt : null;
|
|
656
|
-
if (!updatedAt)
|
|
656
|
+
if (!updatedAt)
|
|
657
|
+
throw new Error(`${input.errorPrefix} require --gjc-goal-json goal.updatedAt from goal({"op":"get"})`);
|
|
657
658
|
const nowMilliseconds = Date.now();
|
|
658
659
|
if (updatedAt < nowMilliseconds - GJC_GOAL_SNAPSHOT_MAX_AGE_MILLISECONDS) {
|
|
659
660
|
throw new Error(`${input.errorPrefix} require a fresh --gjc-goal-json snapshot`);
|
|
@@ -817,7 +818,7 @@ export async function recordUltragoalReviewBlockers(input: {
|
|
|
817
818
|
const objective = input.objective.trim();
|
|
818
819
|
if (!objective) throw new Error("record-review-blockers --objective is required");
|
|
819
820
|
if (!input.gjcGoalJson?.trim()) {
|
|
820
|
-
throw new Error(
|
|
821
|
+
throw new Error('record-review-blockers require --gjc-goal-json from a fresh active goal({"op":"get"}) snapshot');
|
|
821
822
|
}
|
|
822
823
|
const plan = await checkpointUltragoalGoal({
|
|
823
824
|
cwd: input.cwd,
|
|
@@ -913,7 +914,7 @@ function renderCompleteHandoff(
|
|
|
913
914
|
`Ultragoal handoff: ${result.goal.id} — ${result.goal.title}`,
|
|
914
915
|
`Objective: ${result.goal.objective}`,
|
|
915
916
|
`GJC objective: ${result.plan.gjcObjective}`,
|
|
916
|
-
|
|
917
|
+
'Call goal({"op":"get"}); call goal({"op":"create","objective":"<printed objective>"}) only if no active GJC goal exists, then complete this GJC story with goal({"op":"complete"}) after verification.',
|
|
917
918
|
"Before checkpointing complete, obtain a passing architectReview (architecture/product/code CLEAR + APPROVE) and executorQa (e2e/red-team passed); record blockers instead of completing on any finding.",
|
|
918
919
|
"",
|
|
919
920
|
].join("\n");
|