@gajae-code/coding-agent 0.1.1 → 0.1.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/types/config/model-registry.d.ts +8 -0
  3. package/dist/types/config/model-resolver.d.ts +4 -1
  4. package/dist/types/gjc-runtime/team-runtime.d.ts +5 -0
  5. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +26 -0
  6. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +44 -0
  7. package/dist/types/goals/tools/goal-tool.d.ts +4 -4
  8. package/dist/types/hooks/skill-state.d.ts +3 -0
  9. package/dist/types/modes/components/model-selector.d.ts +5 -7
  10. package/dist/types/modes/interactive-mode.d.ts +1 -0
  11. package/dist/types/sdk.d.ts +2 -4
  12. package/dist/types/session/agent-session.d.ts +3 -9
  13. package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +28 -0
  14. package/package.json +13 -9
  15. package/src/config/model-registry.ts +45 -0
  16. package/src/config/model-resolver.ts +5 -1
  17. package/src/defaults/gjc/skills/deep-interview/SKILL.md +30 -30
  18. package/src/defaults/gjc/skills/team/SKILL.md +1 -0
  19. package/src/defaults/gjc/skills/ultragoal/SKILL.md +51 -21
  20. package/src/gjc-runtime/team-runtime.ts +80 -1
  21. package/src/gjc-runtime/ultragoal-guard.ts +239 -0
  22. package/src/gjc-runtime/ultragoal-runtime.ts +318 -4
  23. package/src/goals/tools/goal-tool.ts +10 -4
  24. package/src/hooks/native-skill-hook.ts +26 -0
  25. package/src/hooks/skill-state.ts +59 -0
  26. package/src/main.ts +2 -17
  27. package/src/modes/components/model-selector.ts +225 -33
  28. package/src/modes/controllers/selector-controller.ts +16 -3
  29. package/src/modes/interactive-mode.ts +34 -22
  30. package/src/modes/prompt-action-autocomplete.ts +40 -15
  31. package/src/sdk.ts +3 -1
  32. package/src/session/agent-session.ts +40 -4
  33. package/src/setup/model-onboarding-guidance.ts +5 -3
  34. package/src/skill-state/deep-interview-mutation-guard.ts +303 -0
  35. package/src/slash-commands/builtin-registry.ts +130 -11
  36. package/src/tools/ask.ts +55 -17
  37. package/src/tools/ast-edit.ts +7 -0
  38. package/src/tools/bash.ts +2 -1
  39. package/src/tools/gh.ts +37 -9
  40. package/src/tools/image-gen.ts +19 -10
  41. package/src/tools/path-utils.ts +1 -0
package/src/tools/gh.ts CHANGED
@@ -106,6 +106,34 @@ const GH_PR_CHECKOUT_FIELDS = [
106
106
  "title",
107
107
  "url",
108
108
  ];
109
+
110
+ const PR_CHECKOUT_BRANCH_CONFIG = {
111
+ headRef: "gjcPrHeadRef",
112
+ url: "gjcPrUrl",
113
+ isCrossRepository: "gjcPrIsCrossRepository",
114
+ maintainerCanModify: "gjcPrMaintainerCanModify",
115
+ } as const;
116
+
117
+ const LEGACY_PR_CHECKOUT_BRANCH_CONFIG = {
118
+ headRef: "ompPrHeadRef",
119
+ url: "ompPrUrl",
120
+ isCrossRepository: "ompPrIsCrossRepository",
121
+ maintainerCanModify: "ompPrMaintainerCanModify",
122
+ } as const;
123
+
124
+ type PrCheckoutBranchConfigKey = keyof typeof PR_CHECKOUT_BRANCH_CONFIG;
125
+
126
+ async function getPrCheckoutBranchConfig(
127
+ repoRoot: string,
128
+ localBranch: string,
129
+ key: PrCheckoutBranchConfigKey,
130
+ signal?: AbortSignal,
131
+ ): Promise<string | undefined> {
132
+ return (
133
+ (await git.config.getBranch(repoRoot, localBranch, PR_CHECKOUT_BRANCH_CONFIG[key], signal)) ??
134
+ (await git.config.getBranch(repoRoot, localBranch, LEGACY_PR_CHECKOUT_BRANCH_CONFIG[key], signal))
135
+ );
136
+ }
109
137
  // /search/<endpoint> API response shapes (subset). Used when projecting raw
110
138
  // REST results into the normalized `GhSearch*Result` shapes the formatters
111
139
  // consume. We talk to the API directly because `gh search prs`/`issues`
@@ -1023,21 +1051,21 @@ async function resolvePrBranchPushTarget(
1023
1051
  maintainerCanModify?: boolean;
1024
1052
  isCrossRepository: boolean;
1025
1053
  }> {
1026
- const headRef = await git.config.getBranch(repoRoot, localBranch, "ompPrHeadRef", signal);
1054
+ const headRef = await getPrCheckoutBranchConfig(repoRoot, localBranch, "headRef", signal);
1027
1055
  if (!headRef) {
1028
1056
  throw new ToolError(`branch ${localBranch} has no PR push metadata; check it out via op: pr_checkout first`);
1029
1057
  }
1030
1058
 
1031
1059
  const pushRemote = await git.config.getBranch(repoRoot, localBranch, "pushRemote", signal);
1032
1060
  const remote = await git.config.getBranch(repoRoot, localBranch, "remote", signal);
1033
- const prUrl = await git.config.getBranch(repoRoot, localBranch, "ompPrUrl", signal);
1034
- const maintainerCanModifyValue = await git.config.getBranch(
1061
+ const prUrl = await getPrCheckoutBranchConfig(repoRoot, localBranch, "url", signal);
1062
+ const maintainerCanModifyValue = await getPrCheckoutBranchConfig(
1035
1063
  repoRoot,
1036
1064
  localBranch,
1037
- "ompPrMaintainerCanModify",
1065
+ "maintainerCanModify",
1038
1066
  signal,
1039
1067
  );
1040
- const isCrossRepositoryValue = await git.config.getBranch(repoRoot, localBranch, "ompPrIsCrossRepository", signal);
1068
+ const isCrossRepositoryValue = await getPrCheckoutBranchConfig(repoRoot, localBranch, "isCrossRepository", signal);
1041
1069
 
1042
1070
  const remoteName = pushRemote ?? remote;
1043
1071
  if (!remoteName) {
@@ -2989,19 +3017,19 @@ async function checkoutPullRequest(
2989
3017
  await git.config.setBranch(repoRoot, localBranch, "remote", remote.name, signal);
2990
3018
  await git.config.setBranch(repoRoot, localBranch, "merge", `refs/heads/${headRefName}`, signal);
2991
3019
  await git.config.setBranch(repoRoot, localBranch, "pushRemote", remote.name, signal);
2992
- await git.config.setBranch(repoRoot, localBranch, "ompPrHeadRef", headRefName, signal);
2993
- await git.config.setBranch(repoRoot, localBranch, "ompPrUrl", data.url ?? "", signal);
3020
+ await git.config.setBranch(repoRoot, localBranch, PR_CHECKOUT_BRANCH_CONFIG.headRef, headRefName, signal);
3021
+ await git.config.setBranch(repoRoot, localBranch, PR_CHECKOUT_BRANCH_CONFIG.url, data.url ?? "", signal);
2994
3022
  await git.config.setBranch(
2995
3023
  repoRoot,
2996
3024
  localBranch,
2997
- "ompPrIsCrossRepository",
3025
+ PR_CHECKOUT_BRANCH_CONFIG.isCrossRepository,
2998
3026
  String(Boolean(data.isCrossRepository)),
2999
3027
  signal,
3000
3028
  );
3001
3029
  await git.config.setBranch(
3002
3030
  repoRoot,
3003
3031
  localBranch,
3004
- "ompPrMaintainerCanModify",
3032
+ PR_CHECKOUT_BRANCH_CONFIG.maintainerCanModify,
3005
3033
  String(Boolean(data.maintainerCanModify)),
3006
3034
  signal,
3007
3035
  );
@@ -44,6 +44,7 @@ interface ImageApiKey {
44
44
  apiKey: string;
45
45
  projectId?: string;
46
46
  model?: Model;
47
+ authCredentialType?: "api_key" | "oauth";
47
48
  }
48
49
 
49
50
  const responseModalitySchema = z.enum(["IMAGE", "TEXT"] as const);
@@ -441,6 +442,7 @@ async function findOpenAIHostedImageCredentials(
441
442
  provider: getOpenAIHostedImageProvider(activeModel),
442
443
  apiKey,
443
444
  model: activeModel,
445
+ authCredentialType: modelRegistry.getSessionCredentialType?.(activeModel.provider, sessionId),
444
446
  };
445
447
  }
446
448
 
@@ -700,16 +702,21 @@ function getOpenAIResponseErrorMessage(rawText: string): string {
700
702
  }
701
703
  }
702
704
 
703
- function getOpenAIBaseUrl(model: Model): string {
704
- const fallback =
705
- model.api === "openai-codex-responses" || model.provider === "openai-codex"
706
- ? CODEX_BASE_URL
707
- : DEFAULT_OPENAI_BASE_URL;
708
- return (model.baseUrl || fallback).replace(/\/+$/, "");
705
+ function getOpenAIBaseUrl(model: Model, authCredentialType?: "api_key" | "oauth"): string {
706
+ if (model.api === "openai-codex-responses" || model.provider === "openai-codex") {
707
+ return (model.baseUrl || CODEX_BASE_URL).replace(/\/+$/, "");
708
+ }
709
+ if (authCredentialType === "oauth") return DEFAULT_OPENAI_BASE_URL;
710
+ const envBaseUrl = $env.OPENAI_BASE_URL?.trim();
711
+ const configuredBaseUrl = model.baseUrl?.trim();
712
+ if (envBaseUrl && (!configuredBaseUrl || configuredBaseUrl.toLowerCase().includes("api.openai.com"))) {
713
+ return envBaseUrl.replace(/\/+$/, "");
714
+ }
715
+ return (configuredBaseUrl || envBaseUrl || DEFAULT_OPENAI_BASE_URL).replace(/\/+$/, "");
709
716
  }
710
717
 
711
- function getOpenAIResponsesUrl(model: Model): string {
712
- const baseUrl = getOpenAIBaseUrl(model);
718
+ function getOpenAIResponsesUrl(model: Model, authCredentialType?: "api_key" | "oauth"): string {
719
+ const baseUrl = getOpenAIBaseUrl(model, authCredentialType);
713
720
  if (model.api !== "openai-codex-responses" && model.provider !== "openai-codex") {
714
721
  return `${baseUrl}/responses`;
715
722
  }
@@ -782,11 +789,12 @@ async function generateOpenAIHostedImage(
782
789
  inputImages: InlineImageData[],
783
790
  signal: AbortSignal | undefined,
784
791
  sessionId: string | undefined,
792
+ options?: { authCredentialType?: "api_key" | "oauth" },
785
793
  ): Promise<OpenAIHostedImageResult> {
786
794
  const promptText = assemblePrompt(params);
787
795
  const stream = model.api === "openai-codex-responses" || model.provider === "openai-codex";
788
796
  const requestBody = buildOpenAIHostedImageRequest(model, promptText, params, inputImages, stream);
789
- const response = await fetch(getOpenAIResponsesUrl(model), {
797
+ const response = await fetch(getOpenAIResponsesUrl(model, options?.authCredentialType), {
790
798
  method: "POST",
791
799
  headers: buildOpenAIImageHeaders(model, apiKey, sessionId),
792
800
  body: JSON.stringify(requestBody),
@@ -946,6 +954,7 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
946
954
  resolvedImages,
947
955
  requestSignal,
948
956
  sessionId,
957
+ { authCredentialType: apiKey.authCredentialType },
949
958
  );
950
959
 
951
960
  if (parsed.images.length === 0) {
@@ -1075,7 +1084,7 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
1075
1084
  headers: {
1076
1085
  "Content-Type": "application/json",
1077
1086
  Authorization: `Bearer ${apiKey.apiKey}`,
1078
- "HTTP-Referer": "https://gajae-code.dev/",
1087
+ "HTTP-Referer": "https://gaebal-gajae.dev/",
1079
1088
  "X-OpenRouter-Title": "Gajae Code",
1080
1089
  "X-OpenRouter-Categories": "cli-agent",
1081
1090
  },
@@ -26,6 +26,7 @@ const INTERNAL_SCHEMES_WITH_SELECTORS: Record<string, true> = {
26
26
  gjc: true,
27
27
  pr: true,
28
28
  rule: true,
29
+ skill: true,
29
30
  };
30
31
  const INTERNAL_URL_SCHEME_RE = /^([a-z][a-z0-9+.-]*):\/\//i;
31
32
  const NARROW_NO_BREAK_SPACE = "\u202F";