codexmate 0.1.21 → 0.1.23
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/README.md +0 -10
- package/README.vi.md +0 -2
- package/README.zh.md +0 -10
- package/cli.js +1 -0
- package/package.json +1 -1
- package/web-ui/app.js +34 -0
- package/web-ui/index.html +3 -0
- package/web-ui/logic.markdown-editor.mjs +77 -0
- package/web-ui/logic.prompts-change-axis.mjs +187 -0
- package/web-ui/modules/app.computed.session.mjs +33 -13
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.prompts-editor.mjs +326 -0
- package/web-ui/modules/app.methods.session-actions.mjs +37 -4
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +4 -0
- package/web-ui/modules/i18n/locales/en.mjs +11 -0
- package/web-ui/modules/i18n/locales/ja.mjs +11 -0
- package/web-ui/modules/i18n/locales/vi.mjs +11 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +11 -0
- package/web-ui/modules/i18n/locales/zh.mjs +11 -0
- package/web-ui/partials/index/panel-prompts.html +95 -25
- package/web-ui/res/highlight.min.js +1244 -0
- package/web-ui/res/marked.min.js +6 -0
- package/web-ui/res/purify.min.js +3 -0
- package/web-ui/res/web-ui-render.precompiled.js +315 -41
- package/web-ui/styles/prompts-editor.css +422 -0
- package/web-ui/styles.css +1 -0
package/README.md
CHANGED
|
@@ -26,16 +26,6 @@
|
|
|
26
26
|
|
|
27
27
|
<br />
|
|
28
28
|
|
|
29
|
-
<p>
|
|
30
|
-
<img src="site/.vitepress/public/images/readme/config-codex.png" alt="Codex Mate Codex provider configuration" width="100%" />
|
|
31
|
-
</p>
|
|
32
|
-
<p>
|
|
33
|
-
<img src="site/.vitepress/public/images/readme/config-opencode.png" alt="Codex Mate OpenCode provider configuration" width="100%" />
|
|
34
|
-
</p>
|
|
35
|
-
<p>
|
|
36
|
-
<img src="site/.vitepress/public/images/readme/sessions.png" alt="Codex Mate unified session browser" width="100%" />
|
|
37
|
-
</p>
|
|
38
|
-
|
|
39
29
|
</div>
|
|
40
30
|
|
|
41
31
|
---
|
package/README.vi.md
CHANGED
package/README.zh.md
CHANGED
|
@@ -25,16 +25,6 @@
|
|
|
25
25
|
|
|
26
26
|
<br />
|
|
27
27
|
|
|
28
|
-
<p>
|
|
29
|
-
<img src="site/.vitepress/public/images/readme/config-codex.png" alt="Codex Mate Codex Provider 配置" width="100%" />
|
|
30
|
-
</p>
|
|
31
|
-
<p>
|
|
32
|
-
<img src="site/.vitepress/public/images/readme/config-opencode.png" alt="Codex Mate OpenCode Provider 配置" width="100%" />
|
|
33
|
-
</p>
|
|
34
|
-
<p>
|
|
35
|
-
<img src="site/.vitepress/public/images/readme/sessions.png" alt="Codex Mate 统一会话浏览器" width="100%" />
|
|
36
|
-
</p>
|
|
37
|
-
|
|
38
28
|
</div>
|
|
39
29
|
|
|
40
30
|
---
|
package/cli.js
CHANGED
|
@@ -1076,6 +1076,7 @@ function normalizeWebUiPreferences(value = {}) {
|
|
|
1076
1076
|
configTemplateDiffConfirmEnabled: normalizeBooleanPreference(source.configTemplateDiffConfirmEnabled, true),
|
|
1077
1077
|
sessionsUsageTimeRange: normalizeUsageTimeRangePreference(source.sessionsUsageTimeRange),
|
|
1078
1078
|
promptsSubTab: normalizePromptsSubTabPreference(source.promptsSubTab),
|
|
1079
|
+
promptsPreviewCollapsed: normalizeBooleanPreference(source.promptsPreviewCollapsed, true),
|
|
1079
1080
|
sysPromptScope: normalizeSysPromptScopePreference(source.sysPromptScope),
|
|
1080
1081
|
sysPromptMode: normalizeSysPromptModePreference(source.sysPromptMode),
|
|
1081
1082
|
projectClaudeMdPath: typeof source.projectClaudeMdPath === 'string' ? source.projectClaudeMdPath : '',
|
package/package.json
CHANGED
package/web-ui/app.js
CHANGED
|
@@ -102,6 +102,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
102
102
|
showConfigTemplateModal: false,
|
|
103
103
|
showAgentsModal: false,
|
|
104
104
|
promptsSubTab: 'codex',
|
|
105
|
+
promptsPreviewEnabled: true,
|
|
106
|
+
promptsPreviewCollapsed: true,
|
|
107
|
+
agentsChangeAxis: { mode: 'edit', ticks: [], truncated: false, totalLines: 0, added: 0, removed: 0 },
|
|
108
|
+
sysChangeAxis: { mode: 'edit', ticks: [], truncated: false, totalLines: 0, added: 0, removed: 0 },
|
|
109
|
+
promptsMobileView: 'edit',
|
|
110
|
+
promptsPreviewLibsMissing: false,
|
|
111
|
+
agentsHighlightHtml: '',
|
|
112
|
+
sysPromptHighlightHtml: '',
|
|
113
|
+
agentsPreviewHtml: '',
|
|
114
|
+
sysPromptPreviewHtml: '',
|
|
105
115
|
projectClaudeMdPath: '',
|
|
106
116
|
promptHistoryVisible: false,
|
|
107
117
|
promptHistoryLoading: false,
|
|
@@ -812,6 +822,30 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
812
822
|
if (typeof this.loadPromptsTabContent === 'function') this.loadPromptsTabContent();
|
|
813
823
|
}
|
|
814
824
|
},
|
|
825
|
+
agentsContent() {
|
|
826
|
+
if (typeof this.schedulePromptsEditorRefresh === 'function') this.schedulePromptsEditorRefresh('agents');
|
|
827
|
+
},
|
|
828
|
+
sysPromptContent() {
|
|
829
|
+
if (typeof this.schedulePromptsEditorRefresh === 'function') this.schedulePromptsEditorRefresh('sys');
|
|
830
|
+
},
|
|
831
|
+
agentsOriginalContent() {
|
|
832
|
+
if (typeof this.schedulePromptsChangeAxis === 'function') this.schedulePromptsChangeAxis('agents');
|
|
833
|
+
},
|
|
834
|
+
sysPromptOriginalContent() {
|
|
835
|
+
if (typeof this.schedulePromptsChangeAxis === 'function') this.schedulePromptsChangeAxis('sys');
|
|
836
|
+
},
|
|
837
|
+
agentsDiffVisible() {
|
|
838
|
+
if (typeof this.refreshPromptsChangeAxis === 'function') this.refreshPromptsChangeAxis('agents');
|
|
839
|
+
},
|
|
840
|
+
sysPromptDiffVisible() {
|
|
841
|
+
if (typeof this.refreshPromptsChangeAxis === 'function') this.refreshPromptsChangeAxis('sys');
|
|
842
|
+
},
|
|
843
|
+
agentsDiffLines() {
|
|
844
|
+
if (this.agentsDiffVisible && typeof this.refreshPromptsChangeAxis === 'function') this.refreshPromptsChangeAxis('agents');
|
|
845
|
+
},
|
|
846
|
+
sysPromptDiffLines() {
|
|
847
|
+
if (this.sysPromptDiffVisible && typeof this.refreshPromptsChangeAxis === 'function') this.refreshPromptsChangeAxis('sys');
|
|
848
|
+
},
|
|
815
849
|
projectClaudeMdPath(newPath) {
|
|
816
850
|
if (typeof this.persistWebUiPreferences === 'function') {
|
|
817
851
|
this.persistWebUiPreferences({ projectClaudeMdPath: newPath || '' });
|
package/web-ui/index.html
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
<link rel="apple-touch-icon" href="/res/logo-pack.webp">
|
|
9
9
|
<script src="/res/vue.runtime.global.prod.js"></script>
|
|
10
10
|
<script src="/res/json5.min.js"></script>
|
|
11
|
+
<script src="/res/highlight.min.js"></script>
|
|
12
|
+
<script src="/res/marked.min.js"></script>
|
|
13
|
+
<script src="/res/purify.min.js"></script>
|
|
11
14
|
<link rel="stylesheet" href="/web-ui/styles.css">
|
|
12
15
|
</head>
|
|
13
16
|
<body>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure logic for the prompts-panel Markdown editor.
|
|
3
|
+
* No DOM access in this module so every function stays unit-testable.
|
|
4
|
+
*
|
|
5
|
+
* Syntax highlighting delegates to the vendored highlight.js UMD build
|
|
6
|
+
* (web-ui/res/highlight.min.js, "markdown" grammar is included in the
|
|
7
|
+
* common build) and the rendered highlight markup is layered behind a
|
|
8
|
+
* transparent textarea. Preview rendering delegates to the vendored
|
|
9
|
+
* marked + DOMPurify pair; raw markdown may embed arbitrary HTML, so
|
|
10
|
+
* sanitize is mandatory before the result reaches v-html.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const HIGHLIGHT_MAX_CHARS = 200000;
|
|
14
|
+
const HTML_ESCAPE_MAP = {
|
|
15
|
+
'&': '&',
|
|
16
|
+
'<': '<',
|
|
17
|
+
'>': '>',
|
|
18
|
+
'"': '"',
|
|
19
|
+
"'": '''
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
let hasWarnedHighlightFallback = false;
|
|
24
|
+
|
|
25
|
+
export function escapeHtmlText(text) {
|
|
26
|
+
return String(text === null || text === undefined ? '' : text)
|
|
27
|
+
.replace(/[&<>"']/g, (ch) => HTML_ESCAPE_MAP[ch]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function plainHighlightFallback(text) {
|
|
31
|
+
if (!hasWarnedHighlightFallback) {
|
|
32
|
+
hasWarnedHighlightFallback = true;
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.warn('prompts editor: highlight.js unavailable, editor falls back to plain text rendering');
|
|
35
|
+
}
|
|
36
|
+
return escapeHtmlText(text) + '\n';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Produce the highlighted HTML layer for a markdown source string.
|
|
41
|
+
* highlight.js escapes HTML entities itself; the trailing newline keeps
|
|
42
|
+
* the <pre> backdrop visually aligned with the textarea's final line.
|
|
43
|
+
* Returns escaped plain text when hljs is missing or the input exceeds
|
|
44
|
+
* the highlight budget (observable degradation, never a fake success).
|
|
45
|
+
*/
|
|
46
|
+
export function highlightMarkdownText(text, hljs) {
|
|
47
|
+
const source = typeof text === 'string' ? text : '';
|
|
48
|
+
if (!hljs || typeof hljs.highlight !== 'function' || typeof hljs.getLanguage !== 'function' || !hljs.getLanguage('markdown')) {
|
|
49
|
+
return plainHighlightFallback(source);
|
|
50
|
+
}
|
|
51
|
+
if (source.length > HIGHLIGHT_MAX_CHARS) {
|
|
52
|
+
return plainHighlightFallback(source);
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return hljs.highlight(source, { language: 'markdown', ignoreUnescapedHTML: true }).value + '\n';
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return plainHighlightFallback(source);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Render a markdown preview. Returns a state object instead of raw HTML so
|
|
64
|
+
* the UI can surface "libs missing" and "empty input" explicitly instead
|
|
65
|
+
* of faking a successful render.
|
|
66
|
+
*/
|
|
67
|
+
export function buildMarkdownPreviewHtml(text, marked, purify) {
|
|
68
|
+
const source = typeof text === 'string' ? text : '';
|
|
69
|
+
if (!source.trim()) {
|
|
70
|
+
return { empty: true, html: '' };
|
|
71
|
+
}
|
|
72
|
+
if (!marked || typeof marked.parse !== 'function' || !purify || typeof purify.sanitize !== 'function') {
|
|
73
|
+
return { unavailable: true, html: '' };
|
|
74
|
+
}
|
|
75
|
+
const rawHtml = marked.parse(source, { gfm: true, breaks: false });
|
|
76
|
+
return { html: purify.sanitize(String(rawHtml)) };
|
|
77
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts-panel change axis: aggregates the line-level diff between the
|
|
3
|
+
* saved original content and the current editor content (or between the
|
|
4
|
+
* rendered diff rows) into clickable tick positions. Pure functions only.
|
|
5
|
+
*
|
|
6
|
+
* The diff engine itself is reused from logic.agents-diff.mjs (LCS for
|
|
7
|
+
* small inputs, window-sync for large ones) so hunk line numbers stay
|
|
8
|
+
* consistent with the red/green preview flow.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { buildLineDiff } from './logic.agents-diff.mjs';
|
|
12
|
+
|
|
13
|
+
const AXIS_MAX_TICKS = 200;
|
|
14
|
+
const AXIS_MIN_TICK_HEIGHT_PERCENT = 1.2;
|
|
15
|
+
|
|
16
|
+
function isChangedDiffType(type) {
|
|
17
|
+
return type === 'add' || type === 'del';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function clampAxisPercent(value) {
|
|
21
|
+
if (!Number.isFinite(value) || value < 0) return 0;
|
|
22
|
+
if (value > 100) return 100;
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function tickKindForRun(added, removed) {
|
|
27
|
+
if (added && removed) return 'mixed';
|
|
28
|
+
return added ? 'add' : 'del';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function buildTickFromRun(run, positioning) {
|
|
32
|
+
const height = clampAxisPercent(Math.max(
|
|
33
|
+
AXIS_MIN_TICK_HEIGHT_PERCENT,
|
|
34
|
+
(Math.max(run.added, run.removed) / Math.max(1, positioning.total)) * 100
|
|
35
|
+
));
|
|
36
|
+
const top = Math.min(clampAxisPercent((run.anchor / Math.max(1, positioning.total)) * 100), 100 - height);
|
|
37
|
+
return {
|
|
38
|
+
kind: tickKindForRun(run.added, run.removed),
|
|
39
|
+
startLine: run.startLine,
|
|
40
|
+
endLine: run.startLine + Math.max(run.added, run.removed) - 1,
|
|
41
|
+
added: run.added,
|
|
42
|
+
removed: run.removed,
|
|
43
|
+
top,
|
|
44
|
+
height,
|
|
45
|
+
rowIndex: typeof run.rowIndex === 'number' ? run.rowIndex : null
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function capAxisTicks(runs, positioning) {
|
|
50
|
+
const truncated = runs.length > AXIS_MAX_TICKS;
|
|
51
|
+
const visibleRuns = truncated ? runs.slice(0, AXIS_MAX_TICKS) : runs;
|
|
52
|
+
return {
|
|
53
|
+
ticks: visibleRuns.map((run) => buildTickFromRun(run, positioning)),
|
|
54
|
+
truncated
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Build change-axis ticks for the editor view.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} baseText saved original content
|
|
62
|
+
* @param {string} currentText current editor content
|
|
63
|
+
* @returns {{ ticks: Array<object>, totalLines: number, truncated: boolean,
|
|
64
|
+
* stats: { added: number, removed: number, unchanged: number } }}
|
|
65
|
+
*/
|
|
66
|
+
export function buildPromptsChangeAxis(baseText, currentText) {
|
|
67
|
+
const diff = buildLineDiff(baseText, currentText);
|
|
68
|
+
const totalLines = Math.max(1, diff.newLineCount);
|
|
69
|
+
const runs = [];
|
|
70
|
+
let run = null;
|
|
71
|
+
let newCursor = 0;
|
|
72
|
+
const flushRun = () => {
|
|
73
|
+
if (run) {
|
|
74
|
+
runs.push(run);
|
|
75
|
+
run = null;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
for (const line of diff.lines) {
|
|
80
|
+
if (!line || !isChangedDiffType(line.type)) {
|
|
81
|
+
flushRun();
|
|
82
|
+
if (line && line.type === 'context' && typeof line.newNumber === 'number') {
|
|
83
|
+
newCursor = line.newNumber;
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!run) {
|
|
88
|
+
run = {
|
|
89
|
+
anchor: line.type === 'add' && typeof line.newNumber === 'number'
|
|
90
|
+
? line.newNumber - 1
|
|
91
|
+
: newCursor,
|
|
92
|
+
startLine: line.type === 'add' && typeof line.newNumber === 'number'
|
|
93
|
+
? line.newNumber
|
|
94
|
+
: newCursor + 1,
|
|
95
|
+
added: 0,
|
|
96
|
+
removed: 0
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (line.type === 'add') {
|
|
100
|
+
run.added += 1;
|
|
101
|
+
} else {
|
|
102
|
+
run.removed += 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
flushRun();
|
|
106
|
+
|
|
107
|
+
const { ticks, truncated } = capAxisTicks(runs, { total: totalLines });
|
|
108
|
+
return {
|
|
109
|
+
ticks,
|
|
110
|
+
totalLines,
|
|
111
|
+
truncated,
|
|
112
|
+
stats: diff.stats
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Build change-axis ticks for the diff preview view. Tick positions map to
|
|
118
|
+
* rendered row indexes so clicks scroll the diff list to the exact row.
|
|
119
|
+
*
|
|
120
|
+
* @param {Array<object>} diffLines rendered diff rows ({type, value, oldNumber, newNumber})
|
|
121
|
+
* @returns {{ ticks: Array<object>, rowCount: number, truncated: boolean }}
|
|
122
|
+
*/
|
|
123
|
+
export function buildPromptsDiffAxis(diffLines) {
|
|
124
|
+
const rows = Array.isArray(diffLines) ? diffLines.filter((line) => line && line.type) : [];
|
|
125
|
+
const rowCount = rows.length;
|
|
126
|
+
const runs = [];
|
|
127
|
+
let run = null;
|
|
128
|
+
const flushRun = () => {
|
|
129
|
+
if (run) {
|
|
130
|
+
runs.push(run);
|
|
131
|
+
run = null;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
rows.forEach((line, index) => {
|
|
136
|
+
if (!isChangedDiffType(line.type)) {
|
|
137
|
+
flushRun();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!run) {
|
|
141
|
+
run = {
|
|
142
|
+
anchor: index,
|
|
143
|
+
rowIndex: index,
|
|
144
|
+
startLine: line.type === 'add' && typeof line.newNumber === 'number'
|
|
145
|
+
? line.newNumber
|
|
146
|
+
: (typeof line.oldNumber === 'number' ? line.oldNumber : index + 1),
|
|
147
|
+
added: 0,
|
|
148
|
+
removed: 0
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (line.type === 'add') {
|
|
152
|
+
run.added += 1;
|
|
153
|
+
} else {
|
|
154
|
+
run.removed += 1;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
flushRun();
|
|
158
|
+
|
|
159
|
+
const { ticks, truncated } = capAxisTicks(runs, { total: Math.max(1, rowCount) });
|
|
160
|
+
return {
|
|
161
|
+
ticks,
|
|
162
|
+
rowCount,
|
|
163
|
+
truncated
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Proportional scroll position for jumping to a line inside the editor
|
|
169
|
+
* textarea. The textarea scrolls internally (min-height contract in
|
|
170
|
+
* prompts-editor.css), so scrollTop maps linearly onto the line space.
|
|
171
|
+
*
|
|
172
|
+
* @param {number} scrollHeight textarea scrollHeight
|
|
173
|
+
* @param {number} clientHeight textarea clientHeight
|
|
174
|
+
* @param {number} lineIndex 1-based target line in the current content
|
|
175
|
+
* @param {number} totalLines total line count of the current content
|
|
176
|
+
* @returns {number} clamped scrollTop value
|
|
177
|
+
*/
|
|
178
|
+
export function computePromptsAxisScrollTop(scrollHeight, clientHeight, lineIndex, totalLines) {
|
|
179
|
+
const maxScroll = Math.max(0, (Number(scrollHeight) || 0) - (Number(clientHeight) || 0));
|
|
180
|
+
if (maxScroll <= 0) {
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
const safeTotal = Math.max(1, Number(totalLines) || 1);
|
|
184
|
+
const safeLine = Math.max(1, Number(lineIndex) || 1);
|
|
185
|
+
const ratio = Math.min(1, Math.max(0, (safeLine - 1) / Math.max(1, safeTotal - 1)));
|
|
186
|
+
return Math.min(maxScroll, Math.round(maxScroll * ratio));
|
|
187
|
+
}
|
|
@@ -112,30 +112,39 @@ function extractWorkspaceArtifacts(line, target) {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
function buildWorkspaceBriefText(summary, labels = {}) {
|
|
115
|
+
function buildWorkspaceBriefText(summary, labels = {}, metricLabels = {}) {
|
|
116
116
|
if (!summary || !summary.available) return '';
|
|
117
117
|
const label = (key, fallback) => (labels && labels[key]) || fallback;
|
|
118
|
+
const metricLabel = (key, fallback) => (metricLabels && metricLabels[key]) || fallback;
|
|
118
119
|
const lines = [
|
|
119
120
|
`# ${label('title', 'Session workspace brief')}`,
|
|
120
121
|
'',
|
|
121
122
|
`${label('source', 'Source')}: ${summary.sourceLabel || summary.source || '-'}`,
|
|
122
123
|
`${label('messages', 'Messages')}: ${summary.messageCount}`,
|
|
124
|
+
`${metricLabel('userLabel', 'User')}: ${summary.roleCounts.user}`,
|
|
125
|
+
`${metricLabel('assistantLabel', 'Assistant')}: ${summary.roleCounts.assistant}`,
|
|
126
|
+
`${metricLabel('commandsLabel', 'Commands')}: ${summary.commands.length}`,
|
|
127
|
+
`${metricLabel('artifactsLabel', 'Artifacts')}: ${summary.files.length + summary.links.length}`,
|
|
128
|
+
`${metricLabel('risksLabel', 'Risks')}: ${summary.risks.length + summary.nextSteps.length}`,
|
|
123
129
|
`${label('path', 'Path')}: ${summary.cwd || '-'}`,
|
|
124
130
|
''
|
|
125
131
|
];
|
|
126
132
|
const sections = [
|
|
127
|
-
['signals', label('signals', 'Signals'), summary.signals],
|
|
128
|
-
['commands', label('commands', 'Reusable commands'), summary.commands],
|
|
129
|
-
['files', label('files', 'Files'), summary.files],
|
|
130
|
-
['links', label('links', 'Links'), summary.links],
|
|
131
|
-
['risks', label('risks', 'Risks / blockers'), summary.risks],
|
|
132
|
-
['nextSteps', label('nextSteps', 'Next steps'), summary.nextSteps]
|
|
133
|
+
['signals', label('signals', 'Signals'), summary.signals, label('noneSignals', 'No strong signals yet')],
|
|
134
|
+
['commands', label('commands', 'Reusable commands'), summary.commands, label('noneCommands', 'No command signals')],
|
|
135
|
+
['files', label('files', 'Files'), summary.files, label('noneFiles', 'No file signals')],
|
|
136
|
+
['links', label('links', 'Links'), summary.links, label('noneLinks', 'No link signals')],
|
|
137
|
+
['risks', label('risks', 'Risks / blockers'), summary.risks, label('noneRisks', 'No risks or todos detected')],
|
|
138
|
+
['nextSteps', label('nextSteps', 'Next steps'), summary.nextSteps, label('noneRisks', 'No risks or todos detected')]
|
|
133
139
|
];
|
|
134
|
-
for (const [, sectionTitle, items] of sections) {
|
|
135
|
-
if (!Array.isArray(items) || !items.length) continue;
|
|
140
|
+
for (const [, sectionTitle, items, emptyText] of sections) {
|
|
136
141
|
lines.push(`## ${sectionTitle}`);
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
if (Array.isArray(items) && items.length) {
|
|
143
|
+
for (const item of items) {
|
|
144
|
+
lines.push(`- ${item}`);
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
lines.push(`- ${emptyText}`);
|
|
139
148
|
}
|
|
140
149
|
lines.push('');
|
|
141
150
|
}
|
|
@@ -231,7 +240,13 @@ export function buildSessionWorkspaceSummary(session, messages, options = {}) {
|
|
|
231
240
|
risks,
|
|
232
241
|
nextSteps
|
|
233
242
|
};
|
|
234
|
-
summary.briefText = buildWorkspaceBriefText(summary, options.briefLabels || {}
|
|
243
|
+
summary.briefText = buildWorkspaceBriefText(summary, options.briefLabels || {}, {
|
|
244
|
+
userLabel: options.userLabel,
|
|
245
|
+
assistantLabel: options.assistantLabel,
|
|
246
|
+
commandsLabel: options.commandsLabel,
|
|
247
|
+
artifactsLabel: options.artifactsLabel,
|
|
248
|
+
risksLabel: options.risksLabel
|
|
249
|
+
});
|
|
235
250
|
return summary;
|
|
236
251
|
}
|
|
237
252
|
|
|
@@ -474,7 +489,12 @@ export function createSessionComputed() {
|
|
|
474
489
|
files: t ? t('sessions.workspace.files') : 'Files',
|
|
475
490
|
links: t ? t('sessions.workspace.links') : 'Links',
|
|
476
491
|
risks: t ? t('sessions.workspace.risks') : 'Risks / blockers',
|
|
477
|
-
nextSteps: t ? t('sessions.workspace.nextSteps') : 'Next steps'
|
|
492
|
+
nextSteps: t ? t('sessions.workspace.nextSteps') : 'Next steps',
|
|
493
|
+
noneSignals: t ? t('sessions.workspace.empty') : 'No strong signals yet',
|
|
494
|
+
noneCommands: t ? t('sessions.workspace.noneCommands') : 'No command signals',
|
|
495
|
+
noneFiles: t ? t('sessions.workspace.noneFiles') : 'No file signals',
|
|
496
|
+
noneLinks: t ? t('sessions.workspace.noneLinks') : 'No link signals',
|
|
497
|
+
noneRisks: t ? t('sessions.workspace.noneRisks') : 'No risks or todos detected'
|
|
478
498
|
}
|
|
479
499
|
});
|
|
480
500
|
},
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from './app.constants.mjs';
|
|
14
14
|
import { createAgentsMethods } from './app.methods.agents.mjs';
|
|
15
15
|
import { createSystemPromptMethods } from './app.methods.system-prompt.mjs';
|
|
16
|
+
import { createPromptsEditorMethods } from './app.methods.prompts-editor.mjs';
|
|
16
17
|
import { createClaudeConfigMethods } from './app.methods.claude-config.mjs';
|
|
17
18
|
import { createCodexConfigMethods } from './app.methods.codex-config.mjs';
|
|
18
19
|
import { createInstallMethods } from './app.methods.install.mjs';
|
|
@@ -51,6 +52,7 @@ import {
|
|
|
51
52
|
export function createAppMethods() {
|
|
52
53
|
return {
|
|
53
54
|
...createI18nMethods(),
|
|
55
|
+
...createPromptsEditorMethods(),
|
|
54
56
|
...createWebhookMethods(),
|
|
55
57
|
...createStartupClaudeMethods({
|
|
56
58
|
api,
|