@kenkaiiii/ggcoder 4.3.193 → 4.3.194
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/dist/cli.js +17 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session.d.ts +24 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +101 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/prompt-commands.js +3 -3
- package/dist/core/prompt-commands.test.js +4 -0
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/core/repomap-context.d.ts +11 -0
- package/dist/core/repomap-context.d.ts.map +1 -0
- package/dist/core/repomap-context.js +68 -0
- package/dist/core/repomap-context.js.map +1 -0
- package/dist/core/repomap-context.test.d.ts +2 -0
- package/dist/core/repomap-context.test.d.ts.map +1 -0
- package/dist/core/repomap-context.test.js +47 -0
- package/dist/core/repomap-context.test.js.map +1 -0
- package/dist/core/repomap.d.ts +74 -0
- package/dist/core/repomap.d.ts.map +1 -0
- package/dist/core/repomap.js +897 -0
- package/dist/core/repomap.js.map +1 -0
- package/dist/core/repomap.test.d.ts +2 -0
- package/dist/core/repomap.test.d.ts.map +1 -0
- package/dist/core/repomap.test.js +444 -0
- package/dist/core/repomap.test.js.map +1 -0
- package/dist/core/slash-commands.d.ts +2 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +15 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/tools/edit.d.ts +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +2 -1
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/edit.test.js +20 -0
- package/dist/tools/edit.test.js.map +1 -1
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +2 -1
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/write.d.ts +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +2 -1
- package/dist/tools/write.js.map +1 -1
- package/dist/tools/write.test.js +19 -0
- package/dist/tools/write.test.js.map +1 -1
- package/dist/ui/App.d.ts +6 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +129 -29
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/render.d.ts +6 -0
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +2 -0
- package/dist/ui/render.js.map +1 -1
- package/package.json +7 -5
package/dist/ui/App.js
CHANGED
|
@@ -52,6 +52,8 @@ import { loadCustomCommands } from "../core/custom-commands.js";
|
|
|
52
52
|
import { buildSystemPrompt } from "../system-prompt.js";
|
|
53
53
|
import { detectLanguages, LANGUAGE_DISPLAY_NAMES, } from "../core/language-detector.js";
|
|
54
54
|
import { detectVerifyCommands } from "../core/verify-commands.js";
|
|
55
|
+
import { FOCUSED_REPO_MAP_MAX_CHARS, FIRST_TURN_REPO_MAP_MAX_CHARS, buildRepoMap, createRepoMapCache, } from "../core/repomap.js";
|
|
56
|
+
import { getLatestUserText, injectRepoMapContextMessages, stripRepoMapContextMessages, } from "../core/repomap-context.js";
|
|
55
57
|
import { extractPlanSteps, findCompletedMarkers, markStepsCompleted, segmentDisplayText, stripDoneMarkers, } from "../utils/plan-steps.js";
|
|
56
58
|
import { getMCPServers } from "../core/mcp/index.js";
|
|
57
59
|
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd, flushOverflow, } from "./live-item-flush.js";
|
|
@@ -337,6 +339,12 @@ export function App(props) {
|
|
|
337
339
|
const currentToolsRef = useRef(props.tools);
|
|
338
340
|
const [thinkingEnabled, setThinkingEnabled] = useState(!!props.thinking);
|
|
339
341
|
const messagesRef = useRef(props.sessionStore?.messages ?? props.messages);
|
|
342
|
+
const repoMapInjectionEnabledRef = useRef(true);
|
|
343
|
+
const repoMapDirtyRef = useRef(true);
|
|
344
|
+
const repoMapMarkdownRef = useRef("");
|
|
345
|
+
const repoMapSnapshotRef = useRef(undefined);
|
|
346
|
+
const repoMapChangedCountRef = useRef(0);
|
|
347
|
+
const repoMapCacheRef = useRef(createRepoMapCache());
|
|
340
348
|
const [planAutoExpand, setPlanAutoExpand] = useState(props.sessionStore?.planAutoExpand ?? false);
|
|
341
349
|
const approvedPlanPathRef = useRef(props.sessionStore?.approvedPlanPath);
|
|
342
350
|
const planStepsRef = useRef(props.sessionStore?.planSteps ?? []);
|
|
@@ -740,59 +748,92 @@ export function App(props) {
|
|
|
740
748
|
return messages; // Return unchanged on failure
|
|
741
749
|
}
|
|
742
750
|
}, [currentModel, currentProvider, activeApiKey]);
|
|
751
|
+
const getRepoMapSignalCount = useCallback(() => {
|
|
752
|
+
return ((props.repoMapChangedFilesRef?.current.size ?? 0) +
|
|
753
|
+
(props.repoMapReadFilesRef?.current.size ?? 0));
|
|
754
|
+
}, [props.repoMapChangedFilesRef, props.repoMapReadFilesRef]);
|
|
755
|
+
const getRepoMapBudget = useCallback(() => {
|
|
756
|
+
const userTurns = messagesRef.current.filter((message) => message.role === "user").length;
|
|
757
|
+
const readCount = props.repoMapReadFilesRef?.current.size ?? 0;
|
|
758
|
+
if (userTurns <= 1 && readCount === 0)
|
|
759
|
+
return FIRST_TURN_REPO_MAP_MAX_CHARS;
|
|
760
|
+
if (readCount > 0)
|
|
761
|
+
return FOCUSED_REPO_MAP_MAX_CHARS;
|
|
762
|
+
return FOCUSED_REPO_MAP_MAX_CHARS + 1000;
|
|
763
|
+
}, [props.repoMapReadFilesRef]);
|
|
764
|
+
const refreshRepoMap = useCallback(async (latestUserPrompt) => {
|
|
765
|
+
const rendered = await buildRepoMap({
|
|
766
|
+
cwd: cwdRef.current,
|
|
767
|
+
maxChars: getRepoMapBudget(),
|
|
768
|
+
changedFiles: [...(props.repoMapChangedFilesRef?.current ?? new Set())],
|
|
769
|
+
readFiles: [...(props.repoMapReadFilesRef?.current ?? new Set())],
|
|
770
|
+
focusTerms: latestUserPrompt ? [latestUserPrompt] : [],
|
|
771
|
+
cache: repoMapCacheRef.current,
|
|
772
|
+
});
|
|
773
|
+
repoMapMarkdownRef.current = rendered.markdown;
|
|
774
|
+
repoMapSnapshotRef.current = rendered.snapshot;
|
|
775
|
+
repoMapChangedCountRef.current = getRepoMapSignalCount();
|
|
776
|
+
repoMapDirtyRef.current = false;
|
|
777
|
+
return rendered.markdown;
|
|
778
|
+
}, [
|
|
779
|
+
getRepoMapBudget,
|
|
780
|
+
getRepoMapSignalCount,
|
|
781
|
+
props.repoMapChangedFilesRef,
|
|
782
|
+
props.repoMapReadFilesRef,
|
|
783
|
+
]);
|
|
784
|
+
const stripRepoMapMessages = useCallback((messages) => {
|
|
785
|
+
return stripRepoMapContextMessages(messages);
|
|
786
|
+
}, []);
|
|
787
|
+
const injectRepoMapContext = useCallback(async (messages) => {
|
|
788
|
+
if (!repoMapInjectionEnabledRef.current)
|
|
789
|
+
return stripRepoMapMessages(messages);
|
|
790
|
+
const stripped = stripRepoMapMessages(messages);
|
|
791
|
+
const latestUserPrompt = getLatestUserText(stripped);
|
|
792
|
+
const signalCount = getRepoMapSignalCount();
|
|
793
|
+
if (signalCount !== repoMapChangedCountRef.current)
|
|
794
|
+
repoMapDirtyRef.current = true;
|
|
795
|
+
if (repoMapDirtyRef.current || !repoMapMarkdownRef.current) {
|
|
796
|
+
await refreshRepoMap(latestUserPrompt);
|
|
797
|
+
}
|
|
798
|
+
if (!repoMapMarkdownRef.current)
|
|
799
|
+
return stripped;
|
|
800
|
+
return injectRepoMapContextMessages(stripped, repoMapMarkdownRef.current);
|
|
801
|
+
}, [props.repoMapChangedFilesRef, props.repoMapReadFilesRef, refreshRepoMap, stripRepoMapMessages]);
|
|
743
802
|
/**
|
|
744
803
|
* transformContext callback for the agent loop.
|
|
745
804
|
* Called before each LLM call and on context overflow.
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
* Uses actual API-reported token counts (from previous turn_end) when
|
|
749
|
-
* available, falling back to the character-based estimate. A 30-second
|
|
750
|
-
* cooldown prevents repeated compaction — matching the pattern used by
|
|
751
|
-
* Mysti, openclaw, and other real-world agent frameworks.
|
|
805
|
+
* Compacts persistent chat only, then injects the dynamic repo map transiently.
|
|
752
806
|
*/
|
|
753
807
|
const transformContext = useCallback(async (messages, options) => {
|
|
808
|
+
const stripped = stripRepoMapMessages(messages);
|
|
754
809
|
const settings = settingsRef.current;
|
|
755
810
|
const autoCompact = settings?.get("autoCompact") ?? true;
|
|
756
811
|
const threshold = settings?.get("compactThreshold") ?? 0.8;
|
|
757
812
|
// Force-compact on context overflow regardless of settings
|
|
758
813
|
if (options?.force) {
|
|
759
|
-
const result = await compactConversation(
|
|
814
|
+
const result = await compactConversation(stripped);
|
|
760
815
|
lastCompactionTimeRef.current = Date.now();
|
|
761
|
-
return result;
|
|
816
|
+
return injectRepoMapContext(result);
|
|
762
817
|
}
|
|
763
818
|
if (!autoCompact)
|
|
764
|
-
return
|
|
819
|
+
return injectRepoMapContext(stripped);
|
|
765
820
|
// Time-based cooldown: skip if compaction ran within the last 30 seconds
|
|
766
821
|
if (Date.now() - lastCompactionTimeRef.current < 30_000) {
|
|
767
822
|
log("INFO", "compaction", `Skipping compaction — cooldown active`);
|
|
768
|
-
return
|
|
823
|
+
return injectRepoMapContext(stripped);
|
|
769
824
|
}
|
|
770
825
|
const contextWindow = getContextWindow(currentModel);
|
|
771
|
-
// Reserve = max output budget + ~5K headroom for system prompt + tool
|
|
772
|
-
// schemas. Otherwise the API rejects requests where the prompt fits the
|
|
773
|
-
// window but leaves no room for the response (e.g. Codex Mini at 200K
|
|
774
|
-
// ctx / 100K out — pre-turn estimate may say 160K but a 100K reasoning
|
|
775
|
-
// response then overflows).
|
|
776
826
|
const modelInfo = getModel(currentModel);
|
|
777
827
|
const reserveTokens = (modelInfo?.maxOutputTokens ?? 0) + 5_000;
|
|
778
|
-
// Prefer actual API-reported tokens over char-based estimate, but only
|
|
779
|
-
// when the token count was recorded AFTER the most recent compaction.
|
|
780
|
-
// A count from before compaction is stale — it reflects the old context
|
|
781
|
-
// size and would trigger compaction again immediately for no reason.
|
|
782
828
|
const tokensFresh = lastActualTokensTimestampRef.current > lastCompactionTimeRef.current;
|
|
783
829
|
const actualTokens = lastActualTokensRef.current > 0 && tokensFresh ? lastActualTokensRef.current : undefined;
|
|
784
|
-
if (shouldCompact(
|
|
785
|
-
const
|
|
786
|
-
const result = await compactConversation(messages);
|
|
830
|
+
if (shouldCompact(stripped, contextWindow, threshold, actualTokens, reserveTokens)) {
|
|
831
|
+
const result = await compactConversation(stripped);
|
|
787
832
|
lastCompactionTimeRef.current = Date.now();
|
|
788
|
-
|
|
789
|
-
// return the original reference so the agent loop doesn't replace messages.
|
|
790
|
-
if (result.length === before)
|
|
791
|
-
return messages;
|
|
792
|
-
return result;
|
|
833
|
+
return injectRepoMapContext(result);
|
|
793
834
|
}
|
|
794
|
-
return
|
|
795
|
-
}, [currentModel, compactConversation]);
|
|
835
|
+
return injectRepoMapContext(stripped);
|
|
836
|
+
}, [currentModel, compactConversation, injectRepoMapContext, stripRepoMapMessages]);
|
|
796
837
|
// ── Background task bar state (external store) ──────────
|
|
797
838
|
const { bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, } = useTaskBarStore();
|
|
798
839
|
useTaskBarPolling(props.processManager);
|
|
@@ -828,6 +869,7 @@ export function App(props) {
|
|
|
828
869
|
transformContext,
|
|
829
870
|
}, {
|
|
830
871
|
onComplete: useCallback(() => {
|
|
872
|
+
messagesRef.current = stripRepoMapMessages(messagesRef.current);
|
|
831
873
|
persistNewMessages();
|
|
832
874
|
// Auto-clear plan progress and approved plan when all steps are completed
|
|
833
875
|
const steps = planStepsRef.current;
|
|
@@ -880,6 +922,7 @@ export function App(props) {
|
|
|
880
922
|
}
|
|
881
923
|
}, [
|
|
882
924
|
persistNewMessages,
|
|
925
|
+
stripRepoMapMessages,
|
|
883
926
|
planMode,
|
|
884
927
|
props.cwd,
|
|
885
928
|
props.skills,
|
|
@@ -1706,6 +1749,47 @@ export function App(props) {
|
|
|
1706
1749
|
setLiveItems([{ kind: "plan_event", event: "dismissed", id: getId() }]);
|
|
1707
1750
|
return;
|
|
1708
1751
|
}
|
|
1752
|
+
// Handle /map — show, refresh, or toggle dynamic repo map injection
|
|
1753
|
+
if (trimmed === "/map" ||
|
|
1754
|
+
trimmed === "/map refresh" ||
|
|
1755
|
+
trimmed === "/map on" ||
|
|
1756
|
+
trimmed === "/map off") {
|
|
1757
|
+
const action = trimmed.slice("/map".length).trim();
|
|
1758
|
+
if (action === "on") {
|
|
1759
|
+
repoMapInjectionEnabledRef.current = true;
|
|
1760
|
+
repoMapDirtyRef.current = true;
|
|
1761
|
+
setLiveItems((prev) => [
|
|
1762
|
+
...prev,
|
|
1763
|
+
{ kind: "info", text: "Dynamic repo map injection is on.", id: getId() },
|
|
1764
|
+
]);
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
if (action === "off") {
|
|
1768
|
+
repoMapInjectionEnabledRef.current = false;
|
|
1769
|
+
messagesRef.current = stripRepoMapMessages(messagesRef.current);
|
|
1770
|
+
setLiveItems((prev) => [
|
|
1771
|
+
...prev,
|
|
1772
|
+
{
|
|
1773
|
+
kind: "info",
|
|
1774
|
+
text: "Dynamic repo map injection is off for this session.",
|
|
1775
|
+
id: getId(),
|
|
1776
|
+
},
|
|
1777
|
+
]);
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
if (action === "refresh")
|
|
1781
|
+
repoMapDirtyRef.current = true;
|
|
1782
|
+
const markdown = await refreshRepoMap(getLatestUserText(messagesRef.current));
|
|
1783
|
+
setLiveItems((prev) => [
|
|
1784
|
+
...prev,
|
|
1785
|
+
{
|
|
1786
|
+
kind: "info",
|
|
1787
|
+
text: formatRepoMapCommandOutput(repoMapInjectionEnabledRef.current, markdown, action === "refresh"),
|
|
1788
|
+
id: getId(),
|
|
1789
|
+
},
|
|
1790
|
+
]);
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1709
1793
|
// Handle /plans — open plan pane
|
|
1710
1794
|
if (trimmed === "/plans") {
|
|
1711
1795
|
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
@@ -1895,6 +1979,8 @@ export function App(props) {
|
|
|
1895
1979
|
compactConversation,
|
|
1896
1980
|
rebuildSystemPrompt,
|
|
1897
1981
|
replaceSystemPrompt,
|
|
1982
|
+
refreshRepoMap,
|
|
1983
|
+
stripRepoMapMessages,
|
|
1898
1984
|
]);
|
|
1899
1985
|
const handleDoubleExit = useDoublePress(setExitPending, () => process.exit(0));
|
|
1900
1986
|
const handleAbort = useCallback(() => {
|
|
@@ -2280,6 +2366,13 @@ export function App(props) {
|
|
|
2280
2366
|
log("WARN", "pixel", `chdir failed: ${err.message}`);
|
|
2281
2367
|
}
|
|
2282
2368
|
cwdRef.current = prep.projectPath;
|
|
2369
|
+
repoMapDirtyRef.current = true;
|
|
2370
|
+
repoMapMarkdownRef.current = "";
|
|
2371
|
+
repoMapSnapshotRef.current = undefined;
|
|
2372
|
+
repoMapChangedCountRef.current = 0;
|
|
2373
|
+
repoMapCacheRef.current = createRepoMapCache();
|
|
2374
|
+
props.repoMapChangedFilesRef?.current.clear();
|
|
2375
|
+
props.repoMapReadFilesRef?.current.clear();
|
|
2283
2376
|
setDisplayedCwd(prep.projectPath);
|
|
2284
2377
|
let toolsForPixelFix = currentToolsRef.current;
|
|
2285
2378
|
if (props.rebuildToolsForCwd) {
|
|
@@ -2618,4 +2711,11 @@ export function App(props) {
|
|
|
2618
2711
|
]);
|
|
2619
2712
|
}, cwd: props.cwd, commands: allCommands, eyesCount: eyesCount }), overlay === "model" ? (_jsx(ModelSelector, { onSelect: handleModelSelect, onCancel: () => setOverlay(null), loggedInProviders: props.loggedInProviders ?? [currentProvider], currentModel: currentModel, currentProvider: currentProvider })) : overlay === "theme" ? (_jsx(ThemeSelector, { onSelect: handleThemeSelect, onCancel: () => setOverlay(null), currentTheme: theme.name })) : (_jsx(Footer, { model: currentModel, tokensIn: agentLoop.contextUsed, cwd: displayedCwd, gitBranch: gitBranch, thinkingLevel: thinkingEnabled ? getMaxThinkingLevel(currentModel) : undefined, planMode: planMode, exitPending: exitPending })), (bgTasks.length > 0 || (eyesCount !== undefined && eyesCount > 0) || updatePending) && (_jsxs(Box, { children: [bgTasks.length > 0 && (_jsx(BackgroundTasksBar, { tasks: bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, onExpand: handleTaskBarExpand, onCollapse: handleTaskBarCollapse, onKill: handleTaskKill, onExit: handleTaskBarExit, onNavigate: handleTaskNavigate })), eyesCount !== undefined && eyesCount > 0 && (_jsx(Box, { paddingLeft: bgTasks.length > 0 ? 2 : 1, paddingRight: 1, children: _jsx(Text, { color: theme.accent, bold: true, children: `${eyesCount} eyes signal${eyesCount === 1 ? "" : "s"} · Run /eyes-improve to enhance GG Coder` }) })), updatePending && (_jsx(Box, { paddingLeft: bgTasks.length > 0 || (eyesCount !== undefined && eyesCount > 0) ? 2 : 1, paddingRight: 1, children: _jsx(Text, { color: theme.success, bold: true, children: "\u2728 Update ready \u00B7 restart to apply" }) }))] }))] }))] }));
|
|
2620
2713
|
}
|
|
2714
|
+
function formatRepoMapCommandOutput(enabled, markdown, refreshed) {
|
|
2715
|
+
const status = enabled ? "on" : "off";
|
|
2716
|
+
const prefix = refreshed
|
|
2717
|
+
? `Dynamic repo map refreshed · injection: ${status}`
|
|
2718
|
+
: `Dynamic repo map · injection: ${status}`;
|
|
2719
|
+
return `${prefix}\n\n${markdown}`;
|
|
2720
|
+
}
|
|
2621
2721
|
//# sourceMappingURL=App.js.map
|