@gajae-code/coding-agent 0.7.0 → 0.7.1
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 +9 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +3 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +2 -0
- package/package.json +7 -7
- package/src/cli.ts +1 -3
- package/src/edit/modes/replace.ts +1 -1
- package/src/gjc-runtime/launch-tmux.ts +10 -2
- package/src/gjc-runtime/tmux-common.ts +8 -0
- package/src/gjc-runtime/tmux-sessions.ts +8 -1
- package/src/hashline/hash.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +5 -4
- package/src/notifications/index.ts +44 -7
- package/src/tools/ask.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.7.1] - 2026-06-23
|
|
6
|
+
### Fixed
|
|
7
|
+
|
|
8
|
+
- Fixed packaged source installs (`gajae-code` wrapper) failing `gjc --smoke-test` because native smoke/fallback imports used monorepo-relative paths instead of the `@gajae-code/natives` package export.
|
|
9
|
+
- Fixed Telegram/notification turn ordering around pending asks: the assistant's lead-in text is now emitted before the ask prompt, and only the assistant `message_end` is captured as the pre-ask turn text, so remote prompts show the correct context instead of stale or duplicated output (#1006, #1007).
|
|
10
|
+
|
|
5
11
|
## [0.7.0] - 2026-06-22
|
|
6
12
|
|
|
7
13
|
### Added
|
|
@@ -23,6 +29,9 @@
|
|
|
23
29
|
- Free-text answers resolve pending asks and ask choices remain unredacted (#998, #1001).
|
|
24
30
|
- Recover in-flight sessions after a connection drop and connect new sessions during the `getUpdates` long-poll (#988, #990).
|
|
25
31
|
- Daemon hardening: deliver ask buttons at invocation, fix the topic-reuse race, write daemon logs to file with resilient frame handling, and de-duplicate idle output (#985, #991, and related).
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Avoided automatically reusing stale GJC-managed tmux sessions from older GJC versions after an upgrade; scoped `gjc --tmux` reuse now only auto-attaches sessions tagged with the current version.
|
|
26
35
|
|
|
27
36
|
## [0.6.5] - 2026-06-21
|
|
28
37
|
|
|
@@ -69,6 +69,7 @@ export interface GjcTmuxProfileContext {
|
|
|
69
69
|
project?: string | null;
|
|
70
70
|
sessionId?: string | null;
|
|
71
71
|
sessionStateFile?: string | null;
|
|
72
|
+
version?: string | null;
|
|
72
73
|
}
|
|
73
74
|
export declare function applyGjcTmuxProfile(context: GjcTmuxProfileContext): GjcTmuxProfileResult;
|
|
74
75
|
export declare function buildGjcTmuxWindowTitle(cwd: string, branch: string | null | undefined): string;
|
|
@@ -10,6 +10,7 @@ export declare const GJC_TMUX_BRANCH_SLUG_OPTION = "@gjc-branch-slug";
|
|
|
10
10
|
export declare const GJC_TMUX_PROJECT_OPTION = "@gjc-project";
|
|
11
11
|
export declare const GJC_TMUX_SESSION_ID_OPTION = "@gjc-session-id";
|
|
12
12
|
export declare const GJC_TMUX_SESSION_STATE_FILE_OPTION = "@gjc-session-state-file";
|
|
13
|
+
export declare const GJC_TMUX_VERSION_OPTION = "@gjc-version";
|
|
13
14
|
export interface GjcTmuxProfileCommand {
|
|
14
15
|
description: string;
|
|
15
16
|
args: string[];
|
|
@@ -50,6 +51,7 @@ export declare function buildGjcTmuxRequiredProfileCommands(target: string, meta
|
|
|
50
51
|
project?: string | null;
|
|
51
52
|
sessionId?: string | null;
|
|
52
53
|
sessionStateFile?: string | null;
|
|
54
|
+
version?: string | null;
|
|
53
55
|
}): GjcTmuxProfileCommand[];
|
|
54
56
|
export declare function buildGjcTmuxProfileCommands(target: string, env?: NodeJS.ProcessEnv, metadata?: {
|
|
55
57
|
branch?: string | null;
|
|
@@ -57,5 +59,6 @@ export declare function buildGjcTmuxProfileCommands(target: string, env?: NodeJS
|
|
|
57
59
|
project?: string | null;
|
|
58
60
|
sessionId?: string | null;
|
|
59
61
|
sessionStateFile?: string | null;
|
|
62
|
+
version?: string | null;
|
|
60
63
|
}): GjcTmuxProfileCommand[];
|
|
61
64
|
export declare function normalizeTmuxCreatedAt(raw: string): string;
|
|
@@ -10,6 +10,7 @@ export interface GjcTmuxSessionStatus {
|
|
|
10
10
|
project?: string;
|
|
11
11
|
sessionId?: string;
|
|
12
12
|
sessionStateFile?: string;
|
|
13
|
+
version?: string;
|
|
13
14
|
panePids: number[];
|
|
14
15
|
profile?: string;
|
|
15
16
|
}
|
|
@@ -20,6 +21,7 @@ export interface GjcTmuxSessionTagsForGc {
|
|
|
20
21
|
branchSlug?: string;
|
|
21
22
|
sessionId?: string;
|
|
22
23
|
sessionStateFile?: string;
|
|
24
|
+
version?: string;
|
|
23
25
|
createdAt?: string;
|
|
24
26
|
attached?: boolean;
|
|
25
27
|
panePids?: number[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.1",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gaebal-gajae.dev",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
52
52
|
"@babel/parser": "^7.29.3",
|
|
53
53
|
"@mozilla/readability": "^0.6.0",
|
|
54
|
-
"@gajae-code/stats": "0.7.
|
|
55
|
-
"@gajae-code/agent-core": "0.7.
|
|
56
|
-
"@gajae-code/ai": "0.7.
|
|
57
|
-
"@gajae-code/natives": "0.7.
|
|
58
|
-
"@gajae-code/tui": "0.7.
|
|
59
|
-
"@gajae-code/utils": "0.7.
|
|
54
|
+
"@gajae-code/stats": "0.7.1",
|
|
55
|
+
"@gajae-code/agent-core": "0.7.1",
|
|
56
|
+
"@gajae-code/ai": "0.7.1",
|
|
57
|
+
"@gajae-code/natives": "0.7.1",
|
|
58
|
+
"@gajae-code/tui": "0.7.1",
|
|
59
|
+
"@gajae-code/utils": "0.7.1",
|
|
60
60
|
"@puppeteer/browsers": "^2.13.0",
|
|
61
61
|
"@types/turndown": "5.0.6",
|
|
62
62
|
"@xterm/headless": "^6.0.0",
|
package/src/cli.ts
CHANGED
|
@@ -176,9 +176,7 @@ async function runSmokeTest(): Promise<void> {
|
|
|
176
176
|
// the COMPILED single binary (dev runs only load the on-disk .node). Loading the
|
|
177
177
|
// natives module triggers loadNative()/embedded extraction; calling each new
|
|
178
178
|
// export confirms the symbols are present in the shipped binary.
|
|
179
|
-
const { h06FormatHashLines, h02ScoreSequenceFuzzy, h01FindBestFuzzyMatch } = await import(
|
|
180
|
-
"../../natives/native/index.js"
|
|
181
|
-
);
|
|
179
|
+
const { h06FormatHashLines, h02ScoreSequenceFuzzy, h01FindBestFuzzyMatch } = await import("@gajae-code/natives");
|
|
182
180
|
const hashed = h06FormatHashLines("a\nb", 1);
|
|
183
181
|
if (hashed.split("\n").length !== 2) {
|
|
184
182
|
throw new Error(`smoke-test: h06FormatHashLines returned unexpected output: ${JSON.stringify(hashed)}`);
|
|
@@ -42,7 +42,7 @@ let scoreSequenceFuzzyNative:
|
|
|
42
42
|
let findBestFuzzyMatchNative:
|
|
43
43
|
| ((content: string, target: string, threshold: number) => NativeBestFuzzyMatchResult)
|
|
44
44
|
| undefined;
|
|
45
|
-
void import("
|
|
45
|
+
void import("@gajae-code/natives")
|
|
46
46
|
.then(mod => {
|
|
47
47
|
if (typeof mod.h02ScoreSequenceFuzzy === "function") {
|
|
48
48
|
scoreSequenceFuzzyNative = mod.h02ScoreSequenceFuzzy;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { safeStderrWrite } from "@gajae-code/utils";
|
|
4
|
+
import { VERSION } from "@gajae-code/utils/dirs";
|
|
4
5
|
import type { Args } from "../cli/args";
|
|
5
6
|
import { tmuxRuntimeSessionPath } from "./session-layout";
|
|
6
7
|
import { GJC_COORDINATOR_SESSION_ID_ENV, GJC_COORDINATOR_SESSION_STATE_FILE_ENV } from "./session-state-sidecar";
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
type GjcTmuxProfileCommand,
|
|
17
18
|
resolveGjcTmuxCommand,
|
|
18
19
|
} from "./tmux-common";
|
|
19
|
-
import { findGjcTmuxSessionByName, findGjcTmuxSessionByScope } from "./tmux-sessions";
|
|
20
|
+
import { findGjcTmuxSessionByName, findGjcTmuxSessionByScope, type GjcTmuxSessionStatus } from "./tmux-sessions";
|
|
20
21
|
|
|
21
22
|
export {
|
|
22
23
|
buildGjcTmuxProfileCommands,
|
|
@@ -88,6 +89,9 @@ export interface TmuxLaunchPlan {
|
|
|
88
89
|
function explicitTmuxSessionName(env: NodeJS.ProcessEnv): string | undefined {
|
|
89
90
|
return env.GJC_TMUX_SESSION?.trim() || undefined;
|
|
90
91
|
}
|
|
92
|
+
function hasCurrentGjcVersion(session: GjcTmuxSessionStatus | undefined): boolean {
|
|
93
|
+
return session?.version === VERSION;
|
|
94
|
+
}
|
|
91
95
|
|
|
92
96
|
function findExistingSessionForLaunch(context: {
|
|
93
97
|
env: NodeJS.ProcessEnv;
|
|
@@ -96,7 +100,8 @@ function findExistingSessionForLaunch(context: {
|
|
|
96
100
|
}): string | undefined {
|
|
97
101
|
const explicit = explicitTmuxSessionName(context.env);
|
|
98
102
|
if (explicit) return findGjcTmuxSessionByName(explicit, context.env)?.name;
|
|
99
|
-
|
|
103
|
+
const scoped = findGjcTmuxSessionByScope(context.project, context.branch, context.env);
|
|
104
|
+
return hasCurrentGjcVersion(scoped) ? scoped?.name : undefined;
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
export interface GjcTmuxProfileResult {
|
|
@@ -116,6 +121,7 @@ export interface GjcTmuxProfileContext {
|
|
|
116
121
|
project?: string | null;
|
|
117
122
|
sessionId?: string | null;
|
|
118
123
|
sessionStateFile?: string | null;
|
|
124
|
+
version?: string | null;
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
interface CommandResolutionContext {
|
|
@@ -195,6 +201,7 @@ export function applyGjcTmuxProfile(context: GjcTmuxProfileContext): GjcTmuxProf
|
|
|
195
201
|
project: context.project ?? null,
|
|
196
202
|
sessionId: context.sessionId ?? env[GJC_COORDINATOR_SESSION_ID_ENV] ?? null,
|
|
197
203
|
sessionStateFile: context.sessionStateFile ?? env[GJC_COORDINATOR_SESSION_STATE_FILE_ENV] ?? null,
|
|
204
|
+
version: context.version ?? null,
|
|
198
205
|
});
|
|
199
206
|
if (commands.length === 0) return { skipped: true, commands: [], failures: [] };
|
|
200
207
|
const spawnSync = context.spawnSync ?? defaultSpawnSync;
|
|
@@ -457,6 +464,7 @@ export function launchDefaultTmuxIfNeeded(context: TmuxLaunchContext): boolean {
|
|
|
457
464
|
project: plan.project,
|
|
458
465
|
sessionId: plan.sessionId ?? null,
|
|
459
466
|
sessionStateFile: plan.sessionStateFile ?? null,
|
|
467
|
+
version: VERSION,
|
|
460
468
|
});
|
|
461
469
|
const ownershipFailure = profile.failures.find(item => item.command.args.includes("@gjc-profile"));
|
|
462
470
|
if (ownershipFailure) {
|
|
@@ -10,6 +10,7 @@ export const GJC_TMUX_BRANCH_SLUG_OPTION = "@gjc-branch-slug";
|
|
|
10
10
|
export const GJC_TMUX_PROJECT_OPTION = "@gjc-project";
|
|
11
11
|
export const GJC_TMUX_SESSION_ID_OPTION = "@gjc-session-id";
|
|
12
12
|
export const GJC_TMUX_SESSION_STATE_FILE_OPTION = "@gjc-session-state-file";
|
|
13
|
+
export const GJC_TMUX_VERSION_OPTION = "@gjc-version";
|
|
13
14
|
|
|
14
15
|
export interface GjcTmuxProfileCommand {
|
|
15
16
|
description: string;
|
|
@@ -101,6 +102,7 @@ export function buildGjcTmuxRequiredProfileCommands(
|
|
|
101
102
|
project?: string | null;
|
|
102
103
|
sessionId?: string | null;
|
|
103
104
|
sessionStateFile?: string | null;
|
|
105
|
+
version?: string | null;
|
|
104
106
|
} = {},
|
|
105
107
|
): GjcTmuxProfileCommand[] {
|
|
106
108
|
const commands: GjcTmuxProfileCommand[] = [
|
|
@@ -134,6 +136,11 @@ export function buildGjcTmuxRequiredProfileCommands(
|
|
|
134
136
|
description: "record GJC session state marker",
|
|
135
137
|
args: ["set-option", "-t", target, GJC_TMUX_SESSION_STATE_FILE_OPTION, metadata.sessionStateFile],
|
|
136
138
|
});
|
|
139
|
+
if (metadata.version)
|
|
140
|
+
commands.push({
|
|
141
|
+
description: "record GJC version identity",
|
|
142
|
+
args: ["set-option", "-t", target, GJC_TMUX_VERSION_OPTION, metadata.version],
|
|
143
|
+
});
|
|
137
144
|
return commands;
|
|
138
145
|
}
|
|
139
146
|
|
|
@@ -146,6 +153,7 @@ export function buildGjcTmuxProfileCommands(
|
|
|
146
153
|
project?: string | null;
|
|
147
154
|
sessionId?: string | null;
|
|
148
155
|
sessionStateFile?: string | null;
|
|
156
|
+
version?: string | null;
|
|
149
157
|
} = {},
|
|
150
158
|
): GjcTmuxProfileCommand[] {
|
|
151
159
|
const commands = buildGjcTmuxRequiredProfileCommands(target, metadata);
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
GJC_TMUX_PROJECT_OPTION,
|
|
11
11
|
GJC_TMUX_SESSION_ID_OPTION,
|
|
12
12
|
GJC_TMUX_SESSION_STATE_FILE_OPTION,
|
|
13
|
+
GJC_TMUX_VERSION_OPTION,
|
|
13
14
|
normalizeTmuxCreatedAt,
|
|
14
15
|
resolveGjcTmuxCommand,
|
|
15
16
|
} from "./tmux-common";
|
|
@@ -26,6 +27,7 @@ export interface GjcTmuxSessionStatus {
|
|
|
26
27
|
project?: string;
|
|
27
28
|
sessionId?: string;
|
|
28
29
|
sessionStateFile?: string;
|
|
30
|
+
version?: string;
|
|
29
31
|
panePids: number[];
|
|
30
32
|
profile?: string;
|
|
31
33
|
}
|
|
@@ -37,6 +39,7 @@ export interface GjcTmuxSessionTagsForGc {
|
|
|
37
39
|
branchSlug?: string;
|
|
38
40
|
sessionId?: string;
|
|
39
41
|
sessionStateFile?: string;
|
|
42
|
+
version?: string;
|
|
40
43
|
createdAt?: string;
|
|
41
44
|
attached?: boolean;
|
|
42
45
|
panePids?: number[];
|
|
@@ -86,6 +89,7 @@ function parseSessionLine(line: string): GjcTmuxSessionStatus | null {
|
|
|
86
89
|
project = "",
|
|
87
90
|
sessionId = "",
|
|
88
91
|
sessionStateFile = "",
|
|
92
|
+
version = "",
|
|
89
93
|
] = line.split("\t");
|
|
90
94
|
if (!name) return null;
|
|
91
95
|
return {
|
|
@@ -105,6 +109,7 @@ function parseSessionLine(line: string): GjcTmuxSessionStatus | null {
|
|
|
105
109
|
profile: profile || undefined,
|
|
106
110
|
sessionId: sessionId || undefined,
|
|
107
111
|
sessionStateFile: sessionStateFile || undefined,
|
|
112
|
+
version: version || undefined,
|
|
108
113
|
};
|
|
109
114
|
}
|
|
110
115
|
|
|
@@ -131,7 +136,7 @@ function runListSessions(format: string, env: NodeJS.ProcessEnv = process.env):
|
|
|
131
136
|
|
|
132
137
|
function listSessionLines(env: NodeJS.ProcessEnv = process.env): string[] {
|
|
133
138
|
return runListSessions(
|
|
134
|
-
`#{session_name}\t#{session_windows}\t#{session_attached}\t#{session_created}\t#{${GJC_TMUX_PROFILE_OPTION}}\t#{session_key_table}\t#{session_panes}\t#{pane_pid}\t#{${GJC_TMUX_BRANCH_OPTION}}\t#{${GJC_TMUX_BRANCH_SLUG_OPTION}}\t#{${GJC_TMUX_PROJECT_OPTION}}\t#{${GJC_TMUX_SESSION_ID_OPTION}}\t#{${GJC_TMUX_SESSION_STATE_FILE_OPTION}}`,
|
|
139
|
+
`#{session_name}\t#{session_windows}\t#{session_attached}\t#{session_created}\t#{${GJC_TMUX_PROFILE_OPTION}}\t#{session_key_table}\t#{session_panes}\t#{pane_pid}\t#{${GJC_TMUX_BRANCH_OPTION}}\t#{${GJC_TMUX_BRANCH_SLUG_OPTION}}\t#{${GJC_TMUX_PROJECT_OPTION}}\t#{${GJC_TMUX_SESSION_ID_OPTION}}\t#{${GJC_TMUX_SESSION_STATE_FILE_OPTION}}\t#{${GJC_TMUX_VERSION_OPTION}}`,
|
|
135
140
|
env,
|
|
136
141
|
);
|
|
137
142
|
}
|
|
@@ -265,6 +270,7 @@ function hydrateSessionFromExactOptions(session: GjcTmuxSessionStatus, env: Node
|
|
|
265
270
|
sessionId: session.sessionId ?? readExactOptionForGc(session.name, GJC_TMUX_SESSION_ID_OPTION, env),
|
|
266
271
|
sessionStateFile:
|
|
267
272
|
session.sessionStateFile ?? readExactOptionForGc(session.name, GJC_TMUX_SESSION_STATE_FILE_OPTION, env),
|
|
273
|
+
version: session.version ?? readExactOptionForGc(session.name, GJC_TMUX_VERSION_OPTION, env),
|
|
268
274
|
};
|
|
269
275
|
}
|
|
270
276
|
|
|
@@ -281,6 +287,7 @@ export function readTmuxSessionTagsForGc(
|
|
|
281
287
|
branchSlug: readExactOptionForGc(sessionName, GJC_TMUX_BRANCH_SLUG_OPTION, env),
|
|
282
288
|
sessionId: readExactOptionForGc(sessionName, GJC_TMUX_SESSION_ID_OPTION, env),
|
|
283
289
|
sessionStateFile: readExactOptionForGc(sessionName, GJC_TMUX_SESSION_STATE_FILE_OPTION, env),
|
|
290
|
+
version: readExactOptionForGc(sessionName, GJC_TMUX_VERSION_OPTION, env),
|
|
284
291
|
createdAt: session?.createdAt,
|
|
285
292
|
attached: session?.attached,
|
|
286
293
|
panePids: session?.panePids,
|
package/src/hashline/hash.ts
CHANGED
|
@@ -9,7 +9,7 @@ import bigrams from "./bigrams.json" with { type: "json" };
|
|
|
9
9
|
// module evaluation so this core module (and its re-exported helpers) stays
|
|
10
10
|
// usable and falls back to the TS loop if the native addon is unavailable.
|
|
11
11
|
let formatHashLinesNative: ((text: string, startLine?: number) => string) | undefined;
|
|
12
|
-
void import("
|
|
12
|
+
void import("@gajae-code/natives")
|
|
13
13
|
.then(mod => {
|
|
14
14
|
if (typeof mod.h06FormatHashLines === "function") {
|
|
15
15
|
formatHashLinesNative = mod.h06FormatHashLines;
|