@gajae-code/coding-agent 0.8.0 → 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.
Files changed (116) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/types/cli/completion-cli.d.ts +41 -0
  3. package/dist/types/cli/config-cli.d.ts +1 -0
  4. package/dist/types/cli/notify-cli.d.ts +7 -0
  5. package/dist/types/cli.d.ts +124 -1
  6. package/dist/types/commands/completion.d.ts +29 -0
  7. package/dist/types/commands/config.d.ts +3 -0
  8. package/dist/types/commands/deep-interview.d.ts +3 -0
  9. package/dist/types/config/file-lock.d.ts +7 -6
  10. package/dist/types/config/keybindings.d.ts +2 -2
  11. package/dist/types/config/model-profile-activation.d.ts +6 -0
  12. package/dist/types/config/settings.d.ts +8 -0
  13. package/dist/types/extensibility/extensions/runner.d.ts +2 -1
  14. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  15. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +39 -0
  16. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
  17. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -1
  18. package/dist/types/gjc-runtime/deep-interview-state.d.ts +12 -0
  19. package/dist/types/gjc-runtime/team-runtime.d.ts +2 -1
  20. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  21. package/dist/types/main.d.ts +4 -1
  22. package/dist/types/modes/components/model-selector.d.ts +4 -0
  23. package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
  24. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  25. package/dist/types/modes/controllers/input-controller.d.ts +1 -3
  26. package/dist/types/modes/print-mode.d.ts +13 -1
  27. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
  28. package/dist/types/notifications/config.d.ts +8 -1
  29. package/dist/types/notifications/daemon-paths.d.ts +1 -0
  30. package/dist/types/notifications/index.d.ts +6 -3
  31. package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
  32. package/dist/types/notifications/telegram-daemon.d.ts +18 -3
  33. package/dist/types/notifications/telegram-reference.d.ts +0 -5
  34. package/dist/types/runtime-api-key.d.ts +7 -0
  35. package/dist/types/session/agent-session.d.ts +10 -0
  36. package/dist/types/session/agent-storage.d.ts +1 -0
  37. package/dist/types/task/executor.d.ts +2 -0
  38. package/dist/types/tools/computer.d.ts +2 -2
  39. package/dist/types/tools/skill.d.ts +1 -1
  40. package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
  41. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
  42. package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
  43. package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
  44. package/package.json +7 -7
  45. package/src/cli/completion-cli.ts +234 -0
  46. package/src/cli/config-cli.ts +37 -9
  47. package/src/cli/fast-help.ts +1 -0
  48. package/src/cli/notify-cli.ts +91 -7
  49. package/src/cli.ts +3 -1
  50. package/src/commands/completion.ts +84 -0
  51. package/src/commands/config.ts +2 -0
  52. package/src/commands/deep-interview.ts +2 -1
  53. package/src/config/file-lock.ts +73 -34
  54. package/src/config/keybindings.ts +2 -2
  55. package/src/config/model-profile-activation.ts +65 -0
  56. package/src/config/model-registry.ts +14 -2
  57. package/src/config/settings.ts +22 -0
  58. package/src/coordinator-mcp/server.ts +67 -12
  59. package/src/defaults/gjc/skills/deep-interview/SKILL.md +38 -5
  60. package/src/defaults/gjc/skills/ralplan/SKILL.md +15 -11
  61. package/src/defaults/gjc/skills/team/SKILL.md +1 -1
  62. package/src/extensibility/extensions/runner.ts +2 -0
  63. package/src/extensibility/extensions/types.ts +9 -0
  64. package/src/extensibility/skills.ts +30 -2
  65. package/src/gjc-runtime/deep-interview-ambiguity.ts +206 -0
  66. package/src/gjc-runtime/deep-interview-recorder.ts +62 -8
  67. package/src/gjc-runtime/deep-interview-runtime.ts +210 -2
  68. package/src/gjc-runtime/deep-interview-state.ts +13 -0
  69. package/src/gjc-runtime/session-state-sidecar.ts +59 -11
  70. package/src/gjc-runtime/state-runtime.ts +12 -2
  71. package/src/gjc-runtime/team-runtime.ts +23 -0
  72. package/src/gjc-runtime/ultragoal-runtime.ts +73 -19
  73. package/src/hooks/native-skill-hook.ts +29 -0
  74. package/src/internal-urls/docs-index.generated.ts +2 -2
  75. package/src/main.ts +52 -41
  76. package/src/modes/acp/acp-agent.ts +7 -6
  77. package/src/modes/components/model-selector.ts +54 -11
  78. package/src/modes/components/queued-message-selector.ts +92 -0
  79. package/src/modes/components/settings-selector.ts +4 -0
  80. package/src/modes/components/welcome.ts +160 -44
  81. package/src/modes/controllers/command-controller.ts +4 -4
  82. package/src/modes/controllers/extension-ui-controller.ts +4 -10
  83. package/src/modes/controllers/input-controller.ts +186 -25
  84. package/src/modes/controllers/selector-controller.ts +122 -27
  85. package/src/modes/interactive-mode.ts +43 -16
  86. package/src/modes/print-mode.ts +43 -4
  87. package/src/modes/prompt-action-autocomplete.ts +24 -13
  88. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  89. package/src/modes/utils/ui-helpers.ts +2 -2
  90. package/src/notifications/config.ts +9 -1
  91. package/src/notifications/daemon-paths.ts +2 -0
  92. package/src/notifications/index.ts +39 -9
  93. package/src/notifications/lifecycle-commands.ts +37 -7
  94. package/src/notifications/lifecycle-orchestrator.ts +19 -6
  95. package/src/notifications/telegram-daemon-control.ts +3 -1
  96. package/src/notifications/telegram-daemon.ts +231 -43
  97. package/src/notifications/telegram-reference.ts +0 -16
  98. package/src/prompts/system/system-prompt.md +3 -0
  99. package/src/prompts/tools/computer.md +2 -2
  100. package/src/runtime-api-key.ts +16 -0
  101. package/src/sdk.ts +20 -6
  102. package/src/session/agent-session.ts +225 -31
  103. package/src/session/agent-storage.ts +4 -0
  104. package/src/session/session-manager.ts +44 -2
  105. package/src/slash-commands/builtin-registry.ts +153 -71
  106. package/src/task/executor.ts +7 -4
  107. package/src/task/index.ts +9 -3
  108. package/src/task/output-manager.ts +16 -6
  109. package/src/task/receipt.ts +26 -7
  110. package/src/tools/computer.ts +246 -43
  111. package/src/tools/skill.ts +1 -1
  112. package/src/tools/telegram-send.ts +6 -0
  113. package/src/workflow/workflow-intent-diff.ts +29 -6
  114. package/src/workflow/workflow-intent-report-consumer.ts +221 -0
  115. package/src/workflow/workflow-intent-report-schema.ts +129 -0
  116. package/src/workflow/workflow-intent-report.ts +135 -0
@@ -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 : null;
462
- if (!goalId || relevant.has(goalId)) return eventId;
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 settings/registry
1123
- // wiring (`settings-registry`), or computer Rust (`code`). A bare regeneration
1124
- // of the SHARED native binding (`generated-binding`: packages/natives/native/
1125
- // index.{d.ts,js}) is NOT by itself a computer-use change: that file is
1126
- // generated from Rust, so any real computer-use behavior change must also
1127
- // touch one of the categories above and will still trigger the suite. Treating
1128
- // the regenerated aggregate binding as a computer surface forced the suite on
1129
- // unrelated features (e.g. notifications), which the SKILL explicitly warns
1130
- // against, so it is excluded here.
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(isComputerControlSurfaceChangePath);
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, diff, unstaged, staged] = await Promise.all([
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: diff.stdout,
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 [diff, nameStatus] = await Promise.all([
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 { kind: sourceKind, base, branch, diffStat: diff.stdout, nameStatus: nameStatus.stdout };
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(" ");