@gajae-code/coding-agent 0.8.1 → 0.8.2
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 +34 -0
- package/dist/types/cli/completion-cli.d.ts +41 -0
- package/dist/types/cli/config-cli.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +7 -0
- package/dist/types/cli.d.ts +124 -1
- package/dist/types/commands/completion.d.ts +29 -0
- package/dist/types/commands/config.d.ts +3 -0
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/file-lock.d.ts +7 -6
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-profile-activation.d.ts +6 -0
- package/dist/types/config/settings.d.ts +8 -0
- package/dist/types/extensibility/extensions/runner.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +8 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +39 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +12 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +2 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/main.d.ts +4 -1
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/print-mode.d.ts +13 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
- package/dist/types/notifications/config.d.ts +8 -1
- package/dist/types/notifications/daemon-paths.d.ts +1 -0
- package/dist/types/notifications/index.d.ts +6 -3
- package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
- package/dist/types/notifications/telegram-daemon.d.ts +15 -3
- package/dist/types/notifications/telegram-reference.d.ts +0 -5
- package/dist/types/runtime-api-key.d.ts +7 -0
- package/dist/types/session/agent-session.d.ts +10 -0
- package/dist/types/session/agent-storage.d.ts +1 -0
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/tools/computer.d.ts +2 -2
- package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
- package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
- package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
- package/package.json +7 -7
- package/src/cli/completion-cli.ts +234 -0
- package/src/cli/config-cli.ts +37 -9
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/notify-cli.ts +91 -7
- package/src/cli.ts +3 -1
- package/src/commands/completion.ts +84 -0
- package/src/commands/config.ts +2 -0
- package/src/commands/deep-interview.ts +2 -1
- package/src/config/file-lock.ts +73 -34
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-profile-activation.ts +65 -0
- package/src/config/model-registry.ts +14 -2
- package/src/config/settings.ts +22 -0
- package/src/coordinator-mcp/server.ts +34 -8
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +38 -5
- package/src/defaults/gjc/skills/ralplan/SKILL.md +15 -11
- package/src/defaults/gjc/skills/team/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +2 -0
- package/src/extensibility/extensions/types.ts +9 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +206 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +62 -8
- package/src/gjc-runtime/deep-interview-runtime.ts +210 -2
- package/src/gjc-runtime/deep-interview-state.ts +13 -0
- package/src/gjc-runtime/session-state-sidecar.ts +59 -11
- package/src/gjc-runtime/state-runtime.ts +12 -2
- package/src/gjc-runtime/team-runtime.ts +23 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +73 -19
- package/src/hooks/native-skill-hook.ts +29 -0
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/main.ts +52 -41
- package/src/modes/components/model-selector.ts +54 -11
- package/src/modes/components/queued-message-selector.ts +92 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/components/welcome.ts +160 -44
- package/src/modes/controllers/command-controller.ts +1 -2
- package/src/modes/controllers/extension-ui-controller.ts +4 -10
- package/src/modes/controllers/input-controller.ts +179 -17
- package/src/modes/controllers/selector-controller.ts +122 -27
- package/src/modes/interactive-mode.ts +43 -16
- package/src/modes/print-mode.ts +43 -4
- package/src/modes/prompt-action-autocomplete.ts +21 -20
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +2 -2
- package/src/notifications/config.ts +9 -1
- package/src/notifications/daemon-paths.ts +2 -0
- package/src/notifications/index.ts +39 -9
- package/src/notifications/lifecycle-commands.ts +37 -7
- package/src/notifications/lifecycle-orchestrator.ts +19 -6
- package/src/notifications/telegram-daemon-control.ts +3 -1
- package/src/notifications/telegram-daemon.ts +217 -43
- package/src/notifications/telegram-reference.ts +0 -16
- package/src/prompts/system/system-prompt.md +3 -0
- package/src/prompts/tools/computer.md +2 -2
- package/src/runtime-api-key.ts +16 -0
- package/src/sdk.ts +20 -6
- package/src/session/agent-session.ts +225 -31
- package/src/session/agent-storage.ts +4 -0
- package/src/session/session-manager.ts +44 -2
- package/src/slash-commands/builtin-registry.ts +153 -71
- package/src/task/executor.ts +7 -4
- package/src/task/index.ts +9 -3
- package/src/task/output-manager.ts +16 -6
- package/src/task/receipt.ts +26 -7
- package/src/tools/computer.ts +246 -43
- package/src/tools/telegram-send.ts +6 -0
- package/src/workflow/workflow-intent-diff.ts +29 -6
- package/src/workflow/workflow-intent-report-consumer.ts +221 -0
- package/src/workflow/workflow-intent-report-schema.ts +129 -0
- package/src/workflow/workflow-intent-report.ts +135 -0
|
@@ -1896,16 +1896,39 @@ function readCurrentTmuxLeaderContext(tmuxCommand: string, env: NodeJS.ProcessEn
|
|
|
1896
1896
|
}
|
|
1897
1897
|
return { sessionName, windowIndex, leaderPaneId, target: `${sessionName}:${windowIndex}` };
|
|
1898
1898
|
}
|
|
1899
|
+
type CommandPathResolver = (command: string) => string | null;
|
|
1900
|
+
|
|
1901
|
+
function isBunVirtualPath(candidate: string | undefined): boolean {
|
|
1902
|
+
const normalized = candidate?.trim().replace(/\\/g, "/");
|
|
1903
|
+
return normalized === "/$bunfs" || normalized?.startsWith("/$bunfs/") === true;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
function resolveFallbackGjcWorkerCommand(
|
|
1907
|
+
platform: NodeJS.Platform,
|
|
1908
|
+
execPath: string,
|
|
1909
|
+
which: CommandPathResolver,
|
|
1910
|
+
): string {
|
|
1911
|
+
const pathModule = platform === "win32" ? path.win32 : path;
|
|
1912
|
+
const quote = platform === "win32" ? powershellQuote : shellQuote;
|
|
1913
|
+
const executable = execPath.trim();
|
|
1914
|
+
if (executable && !isBunVirtualPath(executable) && pathModule.isAbsolute(executable)) return quote(executable);
|
|
1915
|
+
const gjcPath = which("gjc")?.trim();
|
|
1916
|
+
if (gjcPath && !isBunVirtualPath(gjcPath)) return quote(gjcPath);
|
|
1917
|
+
return "gjc";
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1899
1920
|
export function resolveGjcWorkerCommand(
|
|
1900
1921
|
cwd = process.cwd(),
|
|
1901
1922
|
env: NodeJS.ProcessEnv = process.env,
|
|
1902
1923
|
platform: NodeJS.Platform = process.platform,
|
|
1903
1924
|
argv: string[] = process.argv,
|
|
1904
1925
|
execPath = process.execPath,
|
|
1926
|
+
which: CommandPathResolver = Bun.which,
|
|
1905
1927
|
): string {
|
|
1906
1928
|
const explicit = env.GJC_TEAM_WORKER_COMMAND?.trim();
|
|
1907
1929
|
if (explicit) return explicit;
|
|
1908
1930
|
const entrypoint = argv[1];
|
|
1931
|
+
if (isBunVirtualPath(entrypoint)) return resolveFallbackGjcWorkerCommand(platform, execPath, which);
|
|
1909
1932
|
if (!entrypoint) return "gjc";
|
|
1910
1933
|
const pathModule = platform === "win32" ? path.win32 : path;
|
|
1911
1934
|
const resolvedEntrypoint = pathModule.isAbsolute(entrypoint) ? entrypoint : pathModule.resolve(cwd, entrypoint);
|
|
@@ -458,8 +458,8 @@ function latestRelevantLedgerEventId(
|
|
|
458
458
|
for (const event of [...ledger].reverse()) {
|
|
459
459
|
const eventId = ledgerEventId(event);
|
|
460
460
|
if (eventId && eventId === excludeEventId) continue;
|
|
461
|
-
const goalId = typeof event.goalId === "string" ? event.goalId :
|
|
462
|
-
if (
|
|
461
|
+
const goalId = typeof event.goalId === "string" ? event.goalId.trim() : "";
|
|
462
|
+
if (goalId && relevant.has(goalId)) return eventId;
|
|
463
463
|
}
|
|
464
464
|
return null;
|
|
465
465
|
}
|
|
@@ -1074,6 +1074,7 @@ interface UltragoalChangeSet extends JsonObject {
|
|
|
1074
1074
|
mergeBase?: string;
|
|
1075
1075
|
paths: UltragoalChangeSetPath[];
|
|
1076
1076
|
rawDiffStat?: string;
|
|
1077
|
+
rawDiff?: string;
|
|
1077
1078
|
trusted: true;
|
|
1078
1079
|
}
|
|
1079
1080
|
|
|
@@ -1086,11 +1087,16 @@ const MANDATORY_COMPUTER_CASE_IDS = [
|
|
|
1086
1087
|
"runaway-loop-halt",
|
|
1087
1088
|
"blast-radius",
|
|
1088
1089
|
] as const;
|
|
1090
|
+
const TOOLS_INDEX_PATH = "packages/coding-agent/src/tools/index.ts";
|
|
1089
1091
|
|
|
1090
1092
|
function normalizeRepoPath(value: string): string {
|
|
1091
1093
|
return value.replaceAll("\\\\", "/").replace(/^\.\//, "");
|
|
1092
1094
|
}
|
|
1093
1095
|
|
|
1096
|
+
function isToolsIndexPath(value: string): boolean {
|
|
1097
|
+
return normalizeRepoPath(value) === TOOLS_INDEX_PATH;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1094
1100
|
function categorizeComputerChangePath(value: string): UltragoalChangeCategory {
|
|
1095
1101
|
const normalized = normalizeRepoPath(value);
|
|
1096
1102
|
if (normalized.startsWith("crates/pi-natives/src/computer/")) return "code";
|
|
@@ -1101,7 +1107,6 @@ function categorizeComputerChangePath(value: string): UltragoalChangeCategory {
|
|
|
1101
1107
|
)
|
|
1102
1108
|
return "tool";
|
|
1103
1109
|
if (
|
|
1104
|
-
normalized === "packages/coding-agent/src/tools/index.ts" ||
|
|
1105
1110
|
normalized === "packages/coding-agent/src/tools/renderers.ts" ||
|
|
1106
1111
|
normalized === "packages/coding-agent/src/config/settings-schema.ts"
|
|
1107
1112
|
)
|
|
@@ -1119,18 +1124,44 @@ function categorizeComputerChangePath(value: string): UltragoalChangeCategory {
|
|
|
1119
1124
|
function isComputerControlSurfaceCategory(category: UltragoalChangeCategory): boolean {
|
|
1120
1125
|
// The computer-use red-team suite is conditional, not universal (see the
|
|
1121
1126
|
// ultragoal SKILL): require it only when the change actually touches
|
|
1122
|
-
// computer-control source — the computer tool (`tool`), its
|
|
1123
|
-
// wiring (`settings-registry`), or computer Rust (`code`).
|
|
1124
|
-
// of the SHARED native binding (`generated-binding`:
|
|
1125
|
-
// index.{d.ts,js}) is NOT by itself a computer-use
|
|
1126
|
-
// generated from Rust, so any real computer-use behavior
|
|
1127
|
-
// touch one of the categories above and will still trigger
|
|
1128
|
-
// the
|
|
1129
|
-
//
|
|
1130
|
-
// against, so
|
|
1127
|
+
// computer-control source — the computer tool (`tool`), its behavior-bearing
|
|
1128
|
+
// settings/renderer wiring (`settings-registry`), or computer Rust (`code`).
|
|
1129
|
+
// A bare regeneration of the SHARED native binding (`generated-binding`:
|
|
1130
|
+
// packages/natives/native/index.{d.ts,js}) is NOT by itself a computer-use
|
|
1131
|
+
// change: that file is generated from Rust, so any real computer-use behavior
|
|
1132
|
+
// change must also touch one of the categories above and will still trigger
|
|
1133
|
+
// the suite. Treating aggregate binding or registration files as a computer
|
|
1134
|
+
// surface forced the suite on unrelated changes, which the SKILL explicitly
|
|
1135
|
+
// warns against, so they are excluded here.
|
|
1131
1136
|
return category === "code" || category === "tool" || category === "settings-registry";
|
|
1132
1137
|
}
|
|
1133
1138
|
|
|
1139
|
+
function isComputerSpecificToolsIndexDiff(diff: string | undefined, targetPath: string): boolean {
|
|
1140
|
+
if (!diff || !isToolsIndexPath(targetPath)) return false;
|
|
1141
|
+
let inTargetFile = false;
|
|
1142
|
+
for (const line of diff.split("\n")) {
|
|
1143
|
+
if (line.startsWith("diff --git ")) {
|
|
1144
|
+
const match = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
|
|
1145
|
+
inTargetFile = !!match && (isToolsIndexPath(match[1]!) || isToolsIndexPath(match[2]!));
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (!inTargetFile || line.startsWith("+++") || line.startsWith("---")) continue;
|
|
1149
|
+
if (!line.startsWith("+") && !line.startsWith("-")) continue;
|
|
1150
|
+
const changedLine = line.slice(1);
|
|
1151
|
+
if (
|
|
1152
|
+
/\bComputerTool\b/.test(changedLine) ||
|
|
1153
|
+
/\bisComputerCallable\b/.test(changedLine) ||
|
|
1154
|
+
/\bisComputerLoadablePlatform\b/.test(changedLine) ||
|
|
1155
|
+
/["']computer["']/.test(changedLine) ||
|
|
1156
|
+
/["']\.\/computer["']/.test(changedLine) ||
|
|
1157
|
+
/\bcomputer\s*:/.test(changedLine)
|
|
1158
|
+
) {
|
|
1159
|
+
return true;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1134
1165
|
function isComputerControlSurfaceChangePath(row: UltragoalChangeSetPath): boolean {
|
|
1135
1166
|
const category = row.category ?? categorizeComputerChangePath(row.path);
|
|
1136
1167
|
const oldCategory = row.oldPath ? categorizeComputerChangePath(row.oldPath) : category;
|
|
@@ -1139,7 +1170,13 @@ function isComputerControlSurfaceChangePath(row: UltragoalChangeSetPath): boolea
|
|
|
1139
1170
|
|
|
1140
1171
|
function trustedChangeSetRequiresComputerSuite(changeSet: UltragoalChangeSet | undefined): boolean {
|
|
1141
1172
|
if (!changeSet?.trusted) return false;
|
|
1142
|
-
return changeSet.paths.some(
|
|
1173
|
+
return changeSet.paths.some(row => {
|
|
1174
|
+
if (isComputerControlSurfaceChangePath(row)) return true;
|
|
1175
|
+
return (
|
|
1176
|
+
isComputerSpecificToolsIndexDiff(changeSet.rawDiff, row.path) ||
|
|
1177
|
+
(row.oldPath ? isComputerSpecificToolsIndexDiff(changeSet.rawDiff, row.oldPath) : false)
|
|
1178
|
+
);
|
|
1179
|
+
});
|
|
1143
1180
|
}
|
|
1144
1181
|
|
|
1145
1182
|
function requiresComputerRedTeamSuite(executorQa: JsonObject, changeSet: UltragoalChangeSet | undefined): boolean {
|
|
@@ -3184,15 +3221,17 @@ function mergeChangeSetPaths(groups: UltragoalChangeSetPath[][]): UltragoalChang
|
|
|
3184
3221
|
async function computeCheckpointChangeSet(cwd: string): Promise<UltragoalChangeSet | undefined> {
|
|
3185
3222
|
const inGit = await spawnText(["git", "rev-parse", "--is-inside-work-tree"], { cwd, timeoutMs: 3000 });
|
|
3186
3223
|
if (!inGit.ok || inGit.stdout.trim() !== "true") return undefined;
|
|
3187
|
-
if (!(await Bun.file(path.join(cwd, ".git")).exists())) return undefined;
|
|
3188
3224
|
const baseRef = await resolveGitBase(cwd);
|
|
3189
3225
|
const base = baseRef;
|
|
3190
3226
|
const mergeBase = await spawnText(["git", "merge-base", "HEAD", baseRef], { cwd, timeoutMs: 3000 });
|
|
3191
|
-
const [committed, unstaged, staged, stat] = await Promise.all([
|
|
3227
|
+
const [committed, unstaged, staged, stat, committedDiff, unstagedDiff, stagedDiff] = await Promise.all([
|
|
3192
3228
|
spawnText(["git", "diff", "--name-status", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
3193
3229
|
spawnText(["git", "diff", "--name-status"], { cwd, timeoutMs: 5000 }),
|
|
3194
3230
|
spawnText(["git", "diff", "--cached", "--name-status"], { cwd, timeoutMs: 5000 }),
|
|
3195
3231
|
spawnText(["git", "diff", "--stat", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
3232
|
+
spawnText(["git", "diff", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
3233
|
+
spawnText(["git", "diff"], { cwd, timeoutMs: 5000 }),
|
|
3234
|
+
spawnText(["git", "diff", "--cached"], { cwd, timeoutMs: 5000 }),
|
|
3196
3235
|
]);
|
|
3197
3236
|
if (!committed.ok && !unstaged.ok && !staged.ok) return undefined;
|
|
3198
3237
|
return {
|
|
@@ -3206,6 +3245,7 @@ async function computeCheckpointChangeSet(cwd: string): Promise<UltragoalChangeS
|
|
|
3206
3245
|
parseGitNameStatus(staged.stdout),
|
|
3207
3246
|
]),
|
|
3208
3247
|
rawDiffStat: stat.stdout,
|
|
3248
|
+
rawDiff: [committedDiff.stdout, unstagedDiff.stdout, stagedDiff.stdout].filter(Boolean).join("\n"),
|
|
3209
3249
|
trusted: true,
|
|
3210
3250
|
};
|
|
3211
3251
|
}
|
|
@@ -3236,6 +3276,7 @@ function changeSetFromReviewSource(source: JsonObject): UltragoalChangeSet | und
|
|
|
3236
3276
|
source: "review-pr",
|
|
3237
3277
|
paths: parseUnifiedDiffPaths(source.diff),
|
|
3238
3278
|
rawDiffStat: source.diff,
|
|
3279
|
+
rawDiff: source.diff,
|
|
3239
3280
|
trusted: true,
|
|
3240
3281
|
};
|
|
3241
3282
|
const local = qualityGateObject(source.local);
|
|
@@ -3245,6 +3286,7 @@ function changeSetFromReviewSource(source: JsonObject): UltragoalChangeSet | und
|
|
|
3245
3286
|
source: "review-worktree",
|
|
3246
3287
|
paths: parseGitNameStatus(String(source.nameStatus ?? source.status ?? "")),
|
|
3247
3288
|
rawDiffStat: String(source.diffStat ?? ""),
|
|
3289
|
+
rawDiff: String(source.diff ?? ""),
|
|
3248
3290
|
trusted: true,
|
|
3249
3291
|
};
|
|
3250
3292
|
if (kind === "branch" || kind === "pr-fallback")
|
|
@@ -3254,6 +3296,7 @@ function changeSetFromReviewSource(source: JsonObject): UltragoalChangeSet | und
|
|
|
3254
3296
|
headRef: "HEAD",
|
|
3255
3297
|
paths: parseGitNameStatus(String(source.nameStatus ?? "")),
|
|
3256
3298
|
rawDiffStat: String(source.diffStat ?? ""),
|
|
3299
|
+
rawDiff: String(source.diff ?? ""),
|
|
3257
3300
|
trusted: true,
|
|
3258
3301
|
};
|
|
3259
3302
|
return undefined;
|
|
@@ -3261,25 +3304,36 @@ function changeSetFromReviewSource(source: JsonObject): UltragoalChangeSet | und
|
|
|
3261
3304
|
|
|
3262
3305
|
async function localDiffSource(cwd: string, sourceKind: string, branch?: string): Promise<JsonObject> {
|
|
3263
3306
|
if (sourceKind === "worktree") {
|
|
3264
|
-
const [status,
|
|
3307
|
+
const [status, diffStat, unstaged, staged, unstagedDiff, stagedDiff] = await Promise.all([
|
|
3265
3308
|
spawnText(["git", "status", "--short"], { cwd, timeoutMs: 5000 }),
|
|
3266
3309
|
spawnText(["git", "diff", "--stat"], { cwd, timeoutMs: 5000 }),
|
|
3267
3310
|
spawnText(["git", "diff", "--name-status"], { cwd, timeoutMs: 5000 }),
|
|
3268
3311
|
spawnText(["git", "diff", "--cached", "--name-status"], { cwd, timeoutMs: 5000 }),
|
|
3312
|
+
spawnText(["git", "diff"], { cwd, timeoutMs: 5000 }),
|
|
3313
|
+
spawnText(["git", "diff", "--cached"], { cwd, timeoutMs: 5000 }),
|
|
3269
3314
|
]);
|
|
3270
3315
|
return {
|
|
3271
3316
|
kind: "worktree",
|
|
3272
3317
|
status: status.stdout,
|
|
3273
|
-
diffStat:
|
|
3318
|
+
diffStat: diffStat.stdout,
|
|
3319
|
+
diff: [unstagedDiff.stdout, stagedDiff.stdout].filter(Boolean).join("\n"),
|
|
3274
3320
|
nameStatus: `${unstaged.stdout}\n${staged.stdout}`,
|
|
3275
3321
|
};
|
|
3276
3322
|
}
|
|
3277
3323
|
const base = await resolveGitBase(cwd, branch);
|
|
3278
|
-
const [
|
|
3324
|
+
const [diffStat, nameStatus, diff] = await Promise.all([
|
|
3279
3325
|
spawnText(["git", "diff", "--stat", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
3280
3326
|
spawnText(["git", "diff", "--name-status", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
3327
|
+
spawnText(["git", "diff", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
3281
3328
|
]);
|
|
3282
|
-
return {
|
|
3329
|
+
return {
|
|
3330
|
+
kind: sourceKind,
|
|
3331
|
+
base,
|
|
3332
|
+
branch,
|
|
3333
|
+
diffStat: diffStat.stdout,
|
|
3334
|
+
diff: diff.stdout,
|
|
3335
|
+
nameStatus: nameStatus.stdout,
|
|
3336
|
+
};
|
|
3283
3337
|
}
|
|
3284
3338
|
|
|
3285
3339
|
async function resolveReviewSource(
|
|
@@ -143,6 +143,34 @@ function readPromptText(payload: HookPayload): string {
|
|
|
143
143
|
return safeString(payload.prompt ?? payload.user_prompt ?? payload.userPrompt).trim();
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
const QUESTION_ONLY_ADVISORY_CONTEXT =
|
|
147
|
+
"Question-only prompt advisory: Treat bare '?' and unambiguous informational questions as answer-only/read-only; do not modify files, run commands, or execute workflow changes unless the user explicitly asks for action.";
|
|
148
|
+
|
|
149
|
+
const QUESTION_EXPLICIT_ACTION_PATTERN =
|
|
150
|
+
/\b(add|apply|build|change|commit|create|delete|edit|execute|fix|implement|install|merge|modify|move|patch|refactor|remove|rename|replace|run|ship|start|stop|test|update|write)\b/i;
|
|
151
|
+
const QUESTION_START_PATTERN =
|
|
152
|
+
/^(what|why|how|when|where|who|which|does|do|did|is|are|was|were|can|could|should|would)\b/i;
|
|
153
|
+
|
|
154
|
+
function classifyQuestionOnlyPrompt(prompt: string): string | null {
|
|
155
|
+
const normalized = prompt.trim().replace(/\s+/g, " ");
|
|
156
|
+
if (!normalized) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
if (normalized === "?") {
|
|
160
|
+
return QUESTION_ONLY_ADVISORY_CONTEXT;
|
|
161
|
+
}
|
|
162
|
+
if (!normalized.endsWith("?")) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
if (QUESTION_EXPLICIT_ACTION_PATTERN.test(normalized)) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
if (!QUESTION_START_PATTERN.test(normalized)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return QUESTION_ONLY_ADVISORY_CONTEXT;
|
|
172
|
+
}
|
|
173
|
+
|
|
146
174
|
function readSessionId(payload: HookPayload): string | undefined {
|
|
147
175
|
return safeString(payload.session_id ?? payload.sessionId).trim() || undefined;
|
|
148
176
|
}
|
|
@@ -221,6 +249,7 @@ export async function dispatchGjcNativeSkillHook(
|
|
|
221
249
|
const additionalContext = [
|
|
222
250
|
skillState ? buildSkillActivationAdditionalContext(skillState, effectiveSkillConfig) : activeUltragoalContext,
|
|
223
251
|
recoveryContext,
|
|
252
|
+
classifyQuestionOnlyPrompt(prompt),
|
|
224
253
|
]
|
|
225
254
|
.filter((value): value is string => Boolean(value))
|
|
226
255
|
.join(" ");
|