@hanzlaa/rcode 4.1.2 → 4.3.0
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/cli/install.js +176 -13
- package/cli/lib/config.cjs +4 -2
- package/cli/lib/fsutil.cjs +13 -2
- package/cli/lib/homedir.cjs +21 -0
- package/cli/lib/schemas.cjs +6 -1
- package/cli/nuke.js +13 -8
- package/cli/postinstall.js +14 -4
- package/cli/rcode-slash-router.cjs +118 -0
- package/cli/uninstall.js +59 -1
- package/cli/update.js +10 -5
- package/dist/rcode.js +234 -230
- package/package.json +1 -1
- package/server/dashboard.js +26 -7
- package/server/lib/api.js +62 -4
- package/server/lib/html/client/agents-data.js +22 -18
- package/server/lib/html/client/app.js +3 -0
- package/server/lib/html/client/components/AgentCard.js +127 -0
- package/server/lib/html/client/components/App.js +104 -39
- package/server/lib/html/client/components/CommandPalette.js +133 -0
- package/server/lib/html/client/components/FileReader.js +116 -0
- package/server/lib/html/client/components/FilterChips.js +94 -0
- package/server/lib/html/client/components/NotifyCenter.js +117 -0
- package/server/lib/html/client/components/OrchPanel.js +80 -52
- package/server/lib/html/client/components/PhaseGraph.js +300 -0
- package/server/lib/html/client/components/RejectDialog.js +78 -0
- package/server/lib/html/client/components/RunnerPicker.js +190 -0
- package/server/lib/html/client/components/Sidebar.js +106 -61
- package/server/lib/html/client/components/StatusSummaryBar.js +76 -0
- package/server/lib/html/client/components/TaskPipeline.js +83 -0
- package/server/lib/html/client/components/Topbar.js +86 -39
- package/server/lib/html/client/components/dashboard/Blockers.js +57 -0
- package/server/lib/html/client/components/dashboard/CompletedTasks.js +47 -0
- package/server/lib/html/client/components/dashboard/CurrentPhase.js +107 -0
- package/server/lib/html/client/components/dashboard/InProgress.js +72 -0
- package/server/lib/html/client/components/dashboard/ProgressDonut.js +101 -0
- package/server/lib/html/client/components/dashboard/ProgressTimeline.js +101 -0
- package/server/lib/html/client/components/dashboard/ProjectHealth.js +80 -0
- package/server/lib/html/client/components/dashboard/RecentDecisions.js +57 -0
- package/server/lib/html/client/components/dashboard/Timeline.js +143 -0
- package/server/lib/html/client/components/shared.js +47 -11
- package/server/lib/html/client/filter-state.js +72 -0
- package/server/lib/html/client/icons-client.js +7 -0
- package/server/lib/html/client/notify.js +75 -0
- package/server/lib/html/client/orchestrator.js +168 -41
- package/server/lib/html/client/preact.js +13 -8
- package/server/lib/html/client/store.js +70 -6
- package/server/lib/html/client/util.js +78 -0
- package/server/lib/html/client/vendor/htm.js +1 -0
- package/server/lib/html/client/vendor/preact-hooks.js +2 -0
- package/server/lib/html/client/vendor/preact.js +2 -0
- package/server/lib/html/client/views/AgentsView.js +144 -51
- package/server/lib/html/client/views/FilesView.js +20 -103
- package/server/lib/html/client/views/KanbanView.js +40 -21
- package/server/lib/html/client/views/MemoryView.js +26 -9
- package/server/lib/html/client/views/MilestonesView.js +4 -4
- package/server/lib/html/client/views/OrchestrationView.js +154 -19
- package/server/lib/html/client/views/OverviewView.js +47 -239
- package/server/lib/html/client/views/PhasesView.js +50 -6
- package/server/lib/html/client/views/RoadmapView.js +6 -3
- package/server/lib/html/client/views/SprintsView.js +50 -6
- package/server/lib/html/client/views/TasksView.js +4 -3
- package/server/lib/html/client.js +21 -4
- package/server/lib/html/css.js +2761 -8
- package/server/lib/html/icons.js +7 -0
- package/server/lib/html/shell.js +10 -3
- package/server/lib/scanner.js +376 -39
- package/server/orchestrator.js +329 -5
|
@@ -14,9 +14,19 @@ import { useState, useEffect } from './preact.js';
|
|
|
14
14
|
const _seed = (typeof window !== 'undefined' && window.__S__) || {};
|
|
15
15
|
|
|
16
16
|
let _state = {
|
|
17
|
+
// First-run signal — false only when the server scanned and found no .rcode.
|
|
18
|
+
initialized: _seed.initialized !== false,
|
|
19
|
+
// Redesign dashboard contract slices (DATA-CONTRACT.md) — read by the Overview
|
|
20
|
+
// slot components. Derived server-side by scanner.buildDashboard.
|
|
21
|
+
project: _seed.project || null,
|
|
22
|
+
progress: _seed.progress || null,
|
|
23
|
+
timeline: _seed.timeline || null,
|
|
24
|
+
tasks: _seed.tasks || null,
|
|
25
|
+
health: _seed.health || null,
|
|
17
26
|
// Fields injected by client.js / window.__S__
|
|
18
27
|
phases: _seed.phases || [],
|
|
19
28
|
milestone: _seed.milestone || '',
|
|
29
|
+
// currentPhase is now the contract object { name, status, milestones[] }.
|
|
20
30
|
currentPhase: _seed.currentPhase || null,
|
|
21
31
|
currentSprint: _seed.currentSprint || null,
|
|
22
32
|
decisions: _seed.decisions || [],
|
|
@@ -37,9 +47,26 @@ let _state = {
|
|
|
37
47
|
refreshing: false,
|
|
38
48
|
offline: false,
|
|
39
49
|
lastRefresh: null,
|
|
50
|
+
// state.json parse failure message (or null). Surfaced as a dismissible
|
|
51
|
+
// banner in App.js; parseErrorDismissed is session-local UI state.
|
|
52
|
+
rawParseError: _seed.rawParseError || null,
|
|
53
|
+
parseErrorDismissed: false,
|
|
40
54
|
// Live orchestrator sessions (populated by startSessionsPoll in orchestrator.js)
|
|
41
55
|
activeSessions: [],
|
|
42
|
-
//
|
|
56
|
+
// Derived join map: storyId → running session. Recomputed automatically by
|
|
57
|
+
// setState whenever activeSessions is written, so views can join tasks to
|
|
58
|
+
// live runs without scanning the array (TasksView, Kanban, Overview).
|
|
59
|
+
runningByStory: {},
|
|
60
|
+
// Persisted past runs (populated by startSessionsPoll → fetchHistory)
|
|
61
|
+
history: [],
|
|
62
|
+
// Orchestrator reachability: null = unknown (before first poll),
|
|
63
|
+
// true = reachable, false = unreachable. Written by the 4s session poll.
|
|
64
|
+
orchOnline: null,
|
|
65
|
+
// Persistent blocked-session alerts (written by notify.js trackBlocked).
|
|
66
|
+
// [{ storyId, cmd }] — rendered as clickable toasts by NotifyCenter.js.
|
|
67
|
+
blockedAlerts: [],
|
|
68
|
+
// File jump bridge: the agent drawer's "View file in Files" sets this to a
|
|
69
|
+
// project-relative .md path; FilesView opens it on arrival and clears it.
|
|
43
70
|
requestedFile: null,
|
|
44
71
|
// xterm terminal panel state (driven by orchestrator.js / XtermPanel.js)
|
|
45
72
|
// { open, storyId, title, minimized, fullscreen }
|
|
@@ -47,6 +74,9 @@ let _state = {
|
|
|
47
74
|
// Orchestrator side-panel state (driven by orchestrator.js / OrchPanel.js)
|
|
48
75
|
// { open, storyId }
|
|
49
76
|
orchPanel: null,
|
|
77
|
+
// Runner-picker popover state (driven by components/RunnerPicker.js)
|
|
78
|
+
// { open, x, y, run: { kind: 'session'|'command', storyId?, cmd, title? } }
|
|
79
|
+
runnerPicker: null,
|
|
50
80
|
};
|
|
51
81
|
|
|
52
82
|
/** Registered subscriber functions. */
|
|
@@ -57,11 +87,25 @@ export function getState() {
|
|
|
57
87
|
return { ..._state };
|
|
58
88
|
}
|
|
59
89
|
|
|
90
|
+
/** Build the storyId → session map for LIVE sessions. A 'blocked' session is
|
|
91
|
+
* a live PTY waiting for input, so it counts as live alongside 'running'. */
|
|
92
|
+
function deriveRunningByStory(sessions) {
|
|
93
|
+
const map = {};
|
|
94
|
+
for (const s of sessions || []) {
|
|
95
|
+
if (s && s.storyId && (s.status === 'running' || s.status === 'blocked')) map[s.storyId] = s;
|
|
96
|
+
}
|
|
97
|
+
return map;
|
|
98
|
+
}
|
|
99
|
+
|
|
60
100
|
/**
|
|
61
101
|
* Shallow-merge `patch` into state, then notify all subscribers.
|
|
62
102
|
* Only notifies if at least one key actually changed value.
|
|
103
|
+
* Writing activeSessions also refreshes the derived runningByStory map.
|
|
63
104
|
*/
|
|
64
105
|
export function setState(patch) {
|
|
106
|
+
if ('activeSessions' in patch) {
|
|
107
|
+
patch = { ...patch, runningByStory: deriveRunningByStory(patch.activeSessions) };
|
|
108
|
+
}
|
|
65
109
|
let changed = false;
|
|
66
110
|
for (const key of Object.keys(patch)) {
|
|
67
111
|
if (_state[key] !== patch[key]) {
|
|
@@ -107,14 +151,34 @@ export function refresh() {
|
|
|
107
151
|
|
|
108
152
|
/**
|
|
109
153
|
* Preact hook. Subscribes the calling component to the store and
|
|
110
|
-
* returns the current state
|
|
154
|
+
* returns the current state (or the selected slice).
|
|
155
|
+
*
|
|
156
|
+
* Without a selector the component re-renders on every setState().
|
|
157
|
+
* With a selector it re-renders only when the selected value changes
|
|
158
|
+
* (Object.is), so slice subscribers skip unrelated store traffic:
|
|
159
|
+
*
|
|
160
|
+
* const project = useStore(s => s.project);
|
|
161
|
+
*
|
|
162
|
+
* The selector must be pure and is captured on mount — pass a stable
|
|
163
|
+
* function (module-level or inline reading fixed keys), not one that
|
|
164
|
+
* closes over changing props.
|
|
111
165
|
*/
|
|
112
|
-
export function useStore() {
|
|
113
|
-
const [state, setLocalState] = useState(
|
|
166
|
+
export function useStore(selector) {
|
|
167
|
+
const [state, setLocalState] = useState(
|
|
168
|
+
() => (selector ? selector(_state) : getState())
|
|
169
|
+
);
|
|
114
170
|
useEffect(() => {
|
|
171
|
+
const update = (newState) => {
|
|
172
|
+
if (selector) {
|
|
173
|
+
const next = selector(newState);
|
|
174
|
+
setLocalState(prev => (Object.is(prev, next) ? prev : next));
|
|
175
|
+
} else {
|
|
176
|
+
setLocalState({ ...newState });
|
|
177
|
+
}
|
|
178
|
+
};
|
|
115
179
|
// Resync on mount in case setState was called before mount.
|
|
116
|
-
|
|
117
|
-
const unsub = subscribe(
|
|
180
|
+
update(_state);
|
|
181
|
+
const unsub = subscribe(update);
|
|
118
182
|
return unsub;
|
|
119
183
|
}, []);
|
|
120
184
|
return state;
|
|
@@ -33,6 +33,29 @@ export function humanDate(s) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Display name for the store's `currentPhase`, which may be the contract
|
|
38
|
+
* object { id, name, status, milestones[] }, a legacy plain string/number
|
|
39
|
+
* (raw state.json current_phase), or null. Returns '' when absent —
|
|
40
|
+
* never "[object Object]".
|
|
41
|
+
*/
|
|
42
|
+
export function currentPhaseName(cp) {
|
|
43
|
+
if (cp == null) return '';
|
|
44
|
+
if (typeof cp === 'object') return cp.name || (cp.id != null ? String(cp.id) : '');
|
|
45
|
+
return String(cp);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Identifier for the store's `currentPhase` (same shapes as currentPhaseName).
|
|
50
|
+
* Prefers the phase id (what commands and phase-card comparisons use);
|
|
51
|
+
* falls back to the name. Returns '' when absent.
|
|
52
|
+
*/
|
|
53
|
+
export function currentPhaseId(cp) {
|
|
54
|
+
if (cp == null) return '';
|
|
55
|
+
if (typeof cp === 'object') return cp.id != null ? String(cp.id) : (cp.name || '');
|
|
56
|
+
return String(cp);
|
|
57
|
+
}
|
|
58
|
+
|
|
36
59
|
/**
|
|
37
60
|
* Flatten all sprints across phases.
|
|
38
61
|
* @param {Array} phases — the phases array from the store.
|
|
@@ -75,6 +98,27 @@ export function chip(status) {
|
|
|
75
98
|
return { cls, label: status };
|
|
76
99
|
}
|
|
77
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Props for a clickable card row that navigates to a hash route.
|
|
103
|
+
* Spread onto a list row (`<li ...${rowLink('tasks')}>`) to make it act like
|
|
104
|
+
* a link: pointer + keyboard activation and an accessible role. Pair with the
|
|
105
|
+
* `ovr-link` class for the hover/focus affordance.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} hash — target hash route without the leading '#'.
|
|
108
|
+
* @returns {object} Preact props (role, tabindex, onClick, onKeyDown).
|
|
109
|
+
*/
|
|
110
|
+
export function rowLink(hash) {
|
|
111
|
+
const go = () => { location.hash = hash; };
|
|
112
|
+
return {
|
|
113
|
+
role: 'link',
|
|
114
|
+
tabindex: 0,
|
|
115
|
+
onClick: go,
|
|
116
|
+
onKeyDown: (e) => {
|
|
117
|
+
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); go(); }
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
78
122
|
/**
|
|
79
123
|
* Human-readable elapsed time since an ISO timestamp.
|
|
80
124
|
* Ported from _orchElapsed() in client-main.js.
|
|
@@ -176,3 +220,37 @@ export function phaseHints(p) {
|
|
|
176
220
|
];
|
|
177
221
|
}
|
|
178
222
|
}
|
|
223
|
+
|
|
224
|
+
// ---- Markdown helpers (moved from FilesView so AgentsView can share) ----
|
|
225
|
+
|
|
226
|
+
/** Strip a leading YAML frontmatter block from a markdown string. */
|
|
227
|
+
export function stripFrontmatter(md) {
|
|
228
|
+
if (!md.startsWith('---')) return md;
|
|
229
|
+
const end = md.indexOf('\n---', 3);
|
|
230
|
+
return end === -1 ? md : md.slice(end + 4).trimStart();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Minimal HTML sanitizer for rendered markdown. No DOMPurify dependency on
|
|
235
|
+
* the client, so we strip the dangerous primitives via regex after marked
|
|
236
|
+
* emits HTML: script/iframe/object/embed tags, inline event handlers, and
|
|
237
|
+
* javascript:/data: URLs in href/src. Markdown content comes from the project
|
|
238
|
+
* dir (semi-trusted) but may include attacker-controlled text checked into a
|
|
239
|
+
* repo, so we cannot trust raw HTML passthrough.
|
|
240
|
+
*/
|
|
241
|
+
export function sanitizeHtml(html) {
|
|
242
|
+
return String(html)
|
|
243
|
+
.replace(/<\s*(script|iframe|object|embed|link|meta|style)\b[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/gi, '')
|
|
244
|
+
.replace(/<\s*(script|iframe|object|embed|link|meta|style)\b[^>]*\/?>/gi, '')
|
|
245
|
+
.replace(/\son[a-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, '')
|
|
246
|
+
.replace(/(href|src|xlink:href)\s*=\s*(["'])\s*(?:javascript|data|vbscript):[^"']*\2/gi, '$1=$2#blocked$2');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Render markdown to sanitized HTML via the global `marked` CDN lib. */
|
|
250
|
+
export function renderMd(md) {
|
|
251
|
+
const clean = stripFrontmatter(md);
|
|
252
|
+
if (typeof marked === 'undefined') {
|
|
253
|
+
return '<pre>' + clean.replace(/</g, '<') + '</pre>';
|
|
254
|
+
}
|
|
255
|
+
return sanitizeHtml(marked.parse(clean));
|
|
256
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var n=function(t,s,r,e){var u;s[0]=0;for(var h=1;h<s.length;h++){var p=s[h++],a=s[h]?(s[0]|=p?1:2,r[s[h++]]):s[++h];3===p?e[0]=a:4===p?e[1]=Object.assign(e[1]||{},a):5===p?(e[1]=e[1]||{})[s[++h]]=a:6===p?e[1][s[++h]]+=a+"":p?(u=t.apply(a,n(t,a,r,["",null])),e.push(u),a[0]?s[0]|=2:(s[h-2]=0,s[h]=u)):e.push(a)}return e},t=new Map;export default function(s){var r=t.get(this);return r||(r=new Map,t.set(this,r)),(r=n(this,r.get(s)||(r.set(s,r=function(n){for(var t,s,r=1,e="",u="",h=[0],p=function(n){1===r&&(n||(e=e.replace(/^\s*\n\s*|\s*\n\s*$/g,"")))?h.push(0,n,e):3===r&&(n||e)?(h.push(3,n,e),r=2):2===r&&"..."===e&&n?h.push(4,n,0):2===r&&e&&!n?h.push(5,0,!0,e):r>=5&&((e||!n&&5===r)&&(h.push(r,0,e,s),r=6),n&&(h.push(r,n,0,s),r=6)),e=""},a=0;a<n.length;a++){a&&(1===r&&p(),p(a));for(var l=0;l<n[a].length;l++)t=n[a][l],1===r?"<"===t?(p(),h=[h],r=3):e+=t:4===r?"--"===e&&">"===t?(r=1,e=""):e=t+e[0]:u?t===u?u="":e+=t:'"'===t||"'"===t?u=t:">"===t?(p(),r=1):r&&("="===t?(r=5,s=e,e=""):"/"===t&&(r<5||">"===n[a][l+1])?(p(),3===r&&(h=h[0]),r=h,(h=h[0]).push(2,0,r),r=0):" "===t||"\t"===t||"\n"===t||"\r"===t?(p(),r=2):e+=t),3===r&&"!--"===e&&(r=4,h=h[0])}return p(),h}(s)),r),arguments,[])).length>1?r:r[0]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{options as n}from"./preact.js";var t,r,u,i,o=0,f=[],c=n,e=c.__b,a=c.__r,v=c.diffed,l=c.__c,m=c.unmount,s=c.__;function d(n,t){c.__h&&c.__h(r,n,o||t),o=0;var u=r.__H||(r.__H={__:[],__h:[]});return n>=u.__.length&&u.__.push({}),u.__[n]}function h(n){return o=1,p(D,n)}function p(n,u,i){var o=d(t++,2);if(o.t=n,!o.__c&&(o.__=[i?i(u):D(void 0,u),function(n){var t=o.__N?o.__N[0]:o.__[0],r=o.t(t,n);t!==r&&(o.__N=[r,o.__[1]],o.__c.setState({}))}],o.__c=r,!r.u)){var f=function(n,t,r){if(!o.__c.__H)return!0;var u=o.__c.__H.__.filter(function(n){return!!n.__c});if(u.every(function(n){return!n.__N}))return!c||c.call(this,n,t,r);var i=!1;return u.forEach(function(n){if(n.__N){var t=n.__[0];n.__=n.__N,n.__N=void 0,t!==n.__[0]&&(i=!0)}}),!(!i&&o.__c.props===n)&&(!c||c.call(this,n,t,r))};r.u=!0;var c=r.shouldComponentUpdate,e=r.componentWillUpdate;r.componentWillUpdate=function(n,t,r){if(this.__e){var u=c;c=void 0,f(n,t,r),c=u}e&&e.call(this,n,t,r)},r.shouldComponentUpdate=f}return o.__N||o.__}function y(n,u){var i=d(t++,3);!c.__s&&C(i.__H,u)&&(i.__=n,i.i=u,r.__H.__h.push(i))}function _(n,u){var i=d(t++,4);!c.__s&&C(i.__H,u)&&(i.__=n,i.i=u,r.__h.push(i))}function A(n){return o=5,T(function(){return{current:n}},[])}function F(n,t,r){o=6,_(function(){return"function"==typeof n?(n(t()),function(){return n(null)}):n?(n.current=t(),function(){return n.current=null}):void 0},null==r?r:r.concat(n))}function T(n,r){var u=d(t++,7);return C(u.__H,r)&&(u.__=n(),u.__H=r,u.__h=n),u.__}function q(n,t){return o=8,T(function(){return n},t)}function x(n){var u=r.context[n.__c],i=d(t++,9);return i.c=n,u?(null==i.__&&(i.__=!0,u.sub(r)),u.props.value):n.__}function P(n,t){c.useDebugValue&&c.useDebugValue(t?t(n):n)}function b(n){var u=d(t++,10),i=h();return u.__=n,r.componentDidCatch||(r.componentDidCatch=function(n,t){u.__&&u.__(n,t),i[1](n)}),[i[0],function(){i[1](void 0)}]}function g(){var n=d(t++,11);if(!n.__){for(var u=r.__v;null!==u&&!u.__m&&null!==u.__;)u=u.__;var i=u.__m||(u.__m=[0,0]);n.__="P"+i[0]+"-"+i[1]++}return n.__}function j(){for(var n;n=f.shift();)if(n.__P&&n.__H)try{n.__H.__h.forEach(z),n.__H.__h.forEach(B),n.__H.__h=[]}catch(t){n.__H.__h=[],c.__e(t,n.__v)}}c.__b=function(n){r=null,e&&e(n)},c.__=function(n,t){n&&t.__k&&t.__k.__m&&(n.__m=t.__k.__m),s&&s(n,t)},c.__r=function(n){a&&a(n),t=0;var i=(r=n.__c).__H;i&&(u===r?(i.__h=[],r.__h=[],i.__.forEach(function(n){n.__N&&(n.__=n.__N),n.i=n.__N=void 0})):(i.__h.forEach(z),i.__h.forEach(B),i.__h=[],t=0)),u=r},c.diffed=function(n){v&&v(n);var t=n.__c;t&&t.__H&&(t.__H.__h.length&&(1!==f.push(t)&&i===c.requestAnimationFrame||((i=c.requestAnimationFrame)||w)(j)),t.__H.__.forEach(function(n){n.i&&(n.__H=n.i),n.i=void 0})),u=r=null},c.__c=function(n,t){t.some(function(n){try{n.__h.forEach(z),n.__h=n.__h.filter(function(n){return!n.__||B(n)})}catch(r){t.some(function(n){n.__h&&(n.__h=[])}),t=[],c.__e(r,n.__v)}}),l&&l(n,t)},c.unmount=function(n){m&&m(n);var t,r=n.__c;r&&r.__H&&(r.__H.__.forEach(function(n){try{z(n)}catch(n){t=n}}),r.__H=void 0,t&&c.__e(t,r.__v))};var k="function"==typeof requestAnimationFrame;function w(n){var t,r=function(){clearTimeout(u),k&&cancelAnimationFrame(t),setTimeout(n)},u=setTimeout(r,100);k&&(t=requestAnimationFrame(r))}function z(n){var t=r,u=n.__c;"function"==typeof u&&(n.__c=void 0,u()),r=t}function B(n){var t=r;n.__c=n.__(),r=t}function C(n,t){return!n||n.length!==t.length||t.some(function(t,r){return t!==n[r]})}function D(n,t){return"function"==typeof t?t(n):t}export{q as useCallback,x as useContext,P as useDebugValue,y as useEffect,b as useErrorBoundary,g as useId,F as useImperativeHandle,_ as useLayoutEffect,T as useMemo,p as useReducer,A as useRef,h as useState};
|
|
2
|
+
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var n,l,u,t,i,o,r,f,e,c,s,a,h={},v=[],p=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,y=Array.isArray;function d(n,l){for(var u in l)n[u]=l[u];return n}function w(n){n&&n.parentNode&&n.parentNode.removeChild(n)}function _(l,u,t){var i,o,r,f={};for(r in u)"key"==r?i=u[r]:"ref"==r?o=u[r]:f[r]=u[r];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):t),"function"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===f[r]&&(f[r]=l.defaultProps[r]);return g(l,f,i,o,null)}function g(n,t,i,o,r){var f={type:n,props:t,key:i,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,constructor:void 0,__v:null==r?++u:r,__i:-1,__u:0};return null==r&&null!=l.vnode&&l.vnode(f),f}function m(){return{current:null}}function b(n){return n.children}function k(n,l){this.props=n,this.context=l}function x(n,l){if(null==l)return n.__?x(n.__,n.__i+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return"function"==typeof n.type?x(n):null}function C(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return C(n)}}function S(n){(!n.__d&&(n.__d=!0)&&i.push(n)&&!M.__r++||o!==l.debounceRendering)&&((o=l.debounceRendering)||r)(M)}function M(){var n,u,t,o,r,e,c,s;for(i.sort(f);n=i.shift();)n.__d&&(u=i.length,o=void 0,e=(r=(t=n).__v).__e,c=[],s=[],t.__P&&((o=d({},r)).__v=r.__v+1,l.vnode&&l.vnode(o),O(t.__P,o,r,t.__n,t.__P.namespaceURI,32&r.__u?[e]:null,c,null==e?x(r):e,!!(32&r.__u),s),o.__v=r.__v,o.__.__k[o.__i]=o,j(c,o,s),o.__e!=e&&C(o)),i.length>u&&i.sort(f));M.__r=0}function P(n,l,u,t,i,o,r,f,e,c,s){var a,p,y,d,w,_=t&&t.__k||v,g=l.length;for(u.__d=e,$(u,l,_),e=u.__d,a=0;a<g;a++)null!=(y=u.__k[a])&&(p=-1===y.__i?h:_[y.__i]||h,y.__i=a,O(n,y,p,i,o,r,f,e,c,s),d=y.__e,y.ref&&p.ref!=y.ref&&(p.ref&&N(p.ref,null,y),s.push(y.ref,y.__c||d,y)),null==w&&null!=d&&(w=d),65536&y.__u||p.__k===y.__k?e=I(y,e,n):"function"==typeof y.type&&void 0!==y.__d?e=y.__d:d&&(e=d.nextSibling),y.__d=void 0,y.__u&=-196609);u.__d=e,u.__e=w}function $(n,l,u){var t,i,o,r,f,e=l.length,c=u.length,s=c,a=0;for(n.__k=[],t=0;t<e;t++)null!=(i=l[t])&&"boolean"!=typeof i&&"function"!=typeof i?(r=t+a,(i=n.__k[t]="string"==typeof i||"number"==typeof i||"bigint"==typeof i||i.constructor==String?g(null,i,null,null,null):y(i)?g(b,{children:i},null,null,null):void 0===i.constructor&&i.__b>0?g(i.type,i.props,i.key,i.ref?i.ref:null,i.__v):i).__=n,i.__b=n.__b+1,o=null,-1!==(f=i.__i=L(i,u,r,s))&&(s--,(o=u[f])&&(o.__u|=131072)),null==o||null===o.__v?(-1==f&&a--,"function"!=typeof i.type&&(i.__u|=65536)):f!==r&&(f==r-1?a--:f==r+1?a++:(f>r?a--:a++,i.__u|=65536))):i=n.__k[t]=null;if(s)for(t=0;t<c;t++)null!=(o=u[t])&&0==(131072&o.__u)&&(o.__e==n.__d&&(n.__d=x(o)),V(o,o))}function I(n,l,u){var t,i;if("function"==typeof n.type){for(t=n.__k,i=0;t&&i<t.length;i++)t[i]&&(t[i].__=n,l=I(t[i],l,u));return l}n.__e!=l&&(l&&n.type&&!u.contains(l)&&(l=x(n)),u.insertBefore(n.__e,l||null),l=n.__e);do{l=l&&l.nextSibling}while(null!=l&&8===l.nodeType);return l}function H(n,l){return l=l||[],null==n||"boolean"==typeof n||(y(n)?n.some(function(n){H(n,l)}):l.push(n)),l}function L(n,l,u,t){var i=n.key,o=n.type,r=u-1,f=u+1,e=l[u];if(null===e||e&&i==e.key&&o===e.type&&0==(131072&e.__u))return u;if(t>(null!=e&&0==(131072&e.__u)?1:0))for(;r>=0||f<l.length;){if(r>=0){if((e=l[r])&&0==(131072&e.__u)&&i==e.key&&o===e.type)return r;r--}if(f<l.length){if((e=l[f])&&0==(131072&e.__u)&&i==e.key&&o===e.type)return f;f++}}return-1}function T(n,l,u){"-"===l[0]?n.setProperty(l,null==u?"":u):n[l]=null==u?"":"number"!=typeof u||p.test(l)?u:u+"px"}function A(n,l,u,t,i){var o;n:if("style"===l)if("string"==typeof u)n.style.cssText=u;else{if("string"==typeof t&&(n.style.cssText=t=""),t)for(l in t)u&&l in u||T(n.style,l,"");if(u)for(l in u)t&&u[l]===t[l]||T(n.style,l,u[l])}else if("o"===l[0]&&"n"===l[1])o=l!==(l=l.replace(/(PointerCapture)$|Capture$/i,"$1")),l=l.toLowerCase()in n||"onFocusOut"===l||"onFocusIn"===l?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+o]=u,u?t?u.u=t.u:(u.u=e,n.addEventListener(l,o?s:c,o)):n.removeEventListener(l,o?s:c,o);else{if("http://www.w3.org/2000/svg"==i)l=l.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!=l&&"height"!=l&&"href"!=l&&"list"!=l&&"form"!=l&&"tabIndex"!=l&&"download"!=l&&"rowSpan"!=l&&"colSpan"!=l&&"role"!=l&&"popover"!=l&&l in n)try{n[l]=null==u?"":u;break n}catch(n){}"function"==typeof u||(null==u||!1===u&&"-"!==l[4]?n.removeAttribute(l):n.setAttribute(l,"popover"==l&&1==u?"":u))}}function F(n){return function(u){if(this.l){var t=this.l[u.type+n];if(null==u.t)u.t=e++;else if(u.t<t.u)return;return t(l.event?l.event(u):u)}}}function O(n,u,t,i,o,r,f,e,c,s){var a,h,v,p,w,_,g,m,x,C,S,M,$,I,H,L,T=u.type;if(void 0!==u.constructor)return null;128&t.__u&&(c=!!(32&t.__u),r=[e=u.__e=t.__e]),(a=l.__b)&&a(u);n:if("function"==typeof T)try{if(m=u.props,x="prototype"in T&&T.prototype.render,C=(a=T.contextType)&&i[a.__c],S=a?C?C.props.value:a.__:i,t.__c?g=(h=u.__c=t.__c).__=h.__E:(x?u.__c=h=new T(m,S):(u.__c=h=new k(m,S),h.constructor=T,h.render=q),C&&C.sub(h),h.props=m,h.state||(h.state={}),h.context=S,h.__n=i,v=h.__d=!0,h.__h=[],h._sb=[]),x&&null==h.__s&&(h.__s=h.state),x&&null!=T.getDerivedStateFromProps&&(h.__s==h.state&&(h.__s=d({},h.__s)),d(h.__s,T.getDerivedStateFromProps(m,h.__s))),p=h.props,w=h.state,h.__v=u,v)x&&null==T.getDerivedStateFromProps&&null!=h.componentWillMount&&h.componentWillMount(),x&&null!=h.componentDidMount&&h.__h.push(h.componentDidMount);else{if(x&&null==T.getDerivedStateFromProps&&m!==p&&null!=h.componentWillReceiveProps&&h.componentWillReceiveProps(m,S),!h.__e&&(null!=h.shouldComponentUpdate&&!1===h.shouldComponentUpdate(m,h.__s,S)||u.__v===t.__v)){for(u.__v!==t.__v&&(h.props=m,h.state=h.__s,h.__d=!1),u.__e=t.__e,u.__k=t.__k,u.__k.some(function(n){n&&(n.__=u)}),M=0;M<h._sb.length;M++)h.__h.push(h._sb[M]);h._sb=[],h.__h.length&&f.push(h);break n}null!=h.componentWillUpdate&&h.componentWillUpdate(m,h.__s,S),x&&null!=h.componentDidUpdate&&h.__h.push(function(){h.componentDidUpdate(p,w,_)})}if(h.context=S,h.props=m,h.__P=n,h.__e=!1,$=l.__r,I=0,x){for(h.state=h.__s,h.__d=!1,$&&$(u),a=h.render(h.props,h.state,h.context),H=0;H<h._sb.length;H++)h.__h.push(h._sb[H]);h._sb=[]}else do{h.__d=!1,$&&$(u),a=h.render(h.props,h.state,h.context),h.state=h.__s}while(h.__d&&++I<25);h.state=h.__s,null!=h.getChildContext&&(i=d(d({},i),h.getChildContext())),x&&!v&&null!=h.getSnapshotBeforeUpdate&&(_=h.getSnapshotBeforeUpdate(p,w)),P(n,y(L=null!=a&&a.type===b&&null==a.key?a.props.children:a)?L:[L],u,t,i,o,r,f,e,c,s),h.base=u.__e,u.__u&=-161,h.__h.length&&f.push(h),g&&(h.__E=h.__=null)}catch(n){if(u.__v=null,c||null!=r){for(u.__u|=c?160:128;e&&8===e.nodeType&&e.nextSibling;)e=e.nextSibling;r[r.indexOf(e)]=null,u.__e=e}else u.__e=t.__e,u.__k=t.__k;l.__e(n,u,t)}else null==r&&u.__v===t.__v?(u.__k=t.__k,u.__e=t.__e):u.__e=z(t.__e,u,t,i,o,r,f,c,s);(a=l.diffed)&&a(u)}function j(n,u,t){u.__d=void 0;for(var i=0;i<t.length;i++)N(t[i],t[++i],t[++i]);l.__c&&l.__c(u,n),n.some(function(u){try{n=u.__h,u.__h=[],n.some(function(n){n.call(u)})}catch(n){l.__e(n,u.__v)}})}function z(u,t,i,o,r,f,e,c,s){var a,v,p,d,_,g,m,b=i.props,k=t.props,C=t.type;if("svg"===C?r="http://www.w3.org/2000/svg":"math"===C?r="http://www.w3.org/1998/Math/MathML":r||(r="http://www.w3.org/1999/xhtml"),null!=f)for(a=0;a<f.length;a++)if((_=f[a])&&"setAttribute"in _==!!C&&(C?_.localName===C:3===_.nodeType)){u=_,f[a]=null;break}if(null==u){if(null===C)return document.createTextNode(k);u=document.createElementNS(r,C,k.is&&k),c&&(l.__m&&l.__m(t,f),c=!1),f=null}if(null===C)b===k||c&&u.data===k||(u.data=k);else{if(f=f&&n.call(u.childNodes),b=i.props||h,!c&&null!=f)for(b={},a=0;a<u.attributes.length;a++)b[(_=u.attributes[a]).name]=_.value;for(a in b)if(_=b[a],"children"==a);else if("dangerouslySetInnerHTML"==a)p=_;else if(!(a in k)){if("value"==a&&"defaultValue"in k||"checked"==a&&"defaultChecked"in k)continue;A(u,a,null,_,r)}for(a in k)_=k[a],"children"==a?d=_:"dangerouslySetInnerHTML"==a?v=_:"value"==a?g=_:"checked"==a?m=_:c&&"function"!=typeof _||b[a]===_||A(u,a,_,b[a],r);if(v)c||p&&(v.__html===p.__html||v.__html===u.innerHTML)||(u.innerHTML=v.__html),t.__k=[];else if(p&&(u.innerHTML=""),P(u,y(d)?d:[d],t,i,o,"foreignObject"===C?"http://www.w3.org/1999/xhtml":r,f,e,f?f[0]:i.__k&&x(i,0),c,s),null!=f)for(a=f.length;a--;)w(f[a]);c||(a="value","progress"===C&&null==g?u.removeAttribute("value"):void 0!==g&&(g!==u[a]||"progress"===C&&!g||"option"===C&&g!==b[a])&&A(u,a,g,b[a],r),a="checked",void 0!==m&&m!==u[a]&&A(u,a,m,b[a],r))}return u}function N(n,u,t){try{if("function"==typeof n){var i="function"==typeof n.__u;i&&n.__u(),i&&null==u||(n.__u=n(u))}else n.current=u}catch(n){l.__e(n,t)}}function V(n,u,t){var i,o;if(l.unmount&&l.unmount(n),(i=n.ref)&&(i.current&&i.current!==n.__e||N(i,null,u)),null!=(i=n.__c)){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(n){l.__e(n,u)}i.base=i.__P=null}if(i=n.__k)for(o=0;o<i.length;o++)i[o]&&V(i[o],u,t||"function"!=typeof n.type);t||w(n.__e),n.__c=n.__=n.__e=n.__d=void 0}function q(n,l,u){return this.constructor(n,u)}function B(u,t,i){var o,r,f,e;l.__&&l.__(u,t),r=(o="function"==typeof i)?null:i&&i.__k||t.__k,f=[],e=[],O(t,u=(!o&&i||t).__k=_(b,null,[u]),r||h,h,t.namespaceURI,!o&&i?[i]:r?null:t.firstChild?n.call(t.childNodes):null,f,!o&&i?i:r?r.__e:t.firstChild,o,e),j(f,u,e)}function D(n,l){B(n,l,D)}function E(l,u,t){var i,o,r,f,e=d({},l.props);for(r in l.type&&l.type.defaultProps&&(f=l.type.defaultProps),u)"key"==r?i=u[r]:"ref"==r?o=u[r]:e[r]=void 0===u[r]&&void 0!==f?f[r]:u[r];return arguments.length>2&&(e.children=arguments.length>3?n.call(arguments,2):t),g(l.type,e,i||l.key,o||l.ref,null)}function G(n,l){var u={__c:l="__cC"+a++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,t;return this.getChildContext||(u=new Set,(t={})[l]=this,this.getChildContext=function(){return t},this.componentWillUnmount=function(){u=null},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.forEach(function(n){n.__e=!0,S(n)})},this.sub=function(n){u.add(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u&&u.delete(n),l&&l.call(n)}}),n.children}};return u.Provider.__=u.Consumer.contextType=u}n=v.slice,l={__e:function(n,l,u,t){for(var i,o,r;l=l.__;)if((i=l.__c)&&!i.__)try{if((o=i.constructor)&&null!=o.getDerivedStateFromError&&(i.setState(o.getDerivedStateFromError(n)),r=i.__d),null!=i.componentDidCatch&&(i.componentDidCatch(n,t||{}),r=i.__d),r)return i.__E=i}catch(l){n=l}throw n}},u=0,t=function(n){return null!=n&&null==n.constructor},k.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=d({},this.state),"function"==typeof n&&(n=n(d({},u),this.props)),n&&d(u,n),null!=n&&this.__v&&(l&&this._sb.push(l),S(this))},k.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),S(this))},k.prototype.render=b,i=[],r="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,f=function(n,l){return n.__v.__b-l.__v.__b},M.__r=0,e=0,c=F(!1),s=F(!0),a=0;export{k as Component,b as Fragment,E as cloneElement,G as createContext,_ as createElement,m as createRef,_ as h,D as hydrate,t as isValidElement,l as options,B as render,H as toChildArray};
|
|
2
|
+
|
|
@@ -1,83 +1,176 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AgentsView —
|
|
2
|
+
* AgentsView — Team roster: category sections of rich agent cards plus an
|
|
3
|
+
* agent detail drawer (components/AgentCard.js).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* that
|
|
5
|
+
* Cards render from the client-side AGENTS roster (agents-data.js) plus a
|
|
6
|
+
* one-shot /api/agents call that returns frontmatter metadata (description,
|
|
7
|
+
* model, tools) per agent definition — small payload, so cards can show
|
|
8
|
+
* summaries and chips without touching prompt bodies.
|
|
6
9
|
*
|
|
7
|
-
* Clicking
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* Clicking a card opens the drawer with the agent's FULL prompt: the actual
|
|
11
|
+
* rcode/agents/<file> body fetched lazily through the existing /api/file
|
|
12
|
+
* handler and rendered as markdown. Prompts are fetched one at a time on
|
|
13
|
+
* click (never all at once) and cached per file for the session.
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
|
-
import { html, useState } from '../preact.js';
|
|
13
|
-
import {
|
|
16
|
+
import { html, useState, useEffect, useCallback, useMemo } from '../preact.js';
|
|
17
|
+
import { AgentCard, AgentDrawer } from '../components/AgentCard.js';
|
|
14
18
|
import { AGENTS } from '../agents-data.js';
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
// Category sections in display order. Roster `type` values not listed here
|
|
21
|
+
// fall into Specialists so a future type can't silently drop agents.
|
|
22
|
+
const SECTIONS = [
|
|
23
|
+
{ label: 'Leadership', types: ['leadership'] },
|
|
24
|
+
{ label: 'Engineering', types: ['engineering'] },
|
|
25
|
+
{ label: 'Product', types: ['product'] },
|
|
26
|
+
{ label: 'Design', types: ['design'] },
|
|
27
|
+
{ label: 'Quality', types: ['quality'] },
|
|
28
|
+
{ label: 'Specialists', types: ['support', 'system'] },
|
|
29
|
+
];
|
|
30
|
+
const KNOWN_TYPES = SECTIONS.flatMap(s => s.types);
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// FilesView watches requestedFile in the store and responds via useEffect.
|
|
26
|
-
setState({ requestedFile: skillSlug });
|
|
27
|
-
window.location.hash = 'files';
|
|
28
|
-
}
|
|
32
|
+
function agentsForSection(section) {
|
|
33
|
+
return AGENTS.filter(a =>
|
|
34
|
+
section.types.includes(a.type) ||
|
|
35
|
+
(section.label === 'Specialists' && !KNOWN_TYPES.includes(a.type))
|
|
36
|
+
);
|
|
37
|
+
}
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
${agent.name}
|
|
38
|
-
${agent.real ? html` <span class="real-badge">real</span>` : null}
|
|
39
|
-
${' '}<span class="type-badge">${agent.type}</span>
|
|
40
|
-
</div>
|
|
41
|
-
<div class="arabic">${agent.arabic}</div>
|
|
42
|
-
<div class="role">${agent.role}</div>
|
|
43
|
-
</div>
|
|
44
|
-
`;
|
|
39
|
+
function matchesFilter(agent, meta, filter) {
|
|
40
|
+
if (!filter) return true;
|
|
41
|
+
const haystack = [
|
|
42
|
+
agent.name, agent.role, agent.arabic, agent.type,
|
|
43
|
+
meta && meta.model, meta && meta.description, meta && (meta.tools || []).join(' '),
|
|
44
|
+
].filter(Boolean).join(' ');
|
|
45
|
+
return haystack.toLowerCase().includes(filter.toLowerCase());
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (!visible.length) return null;
|
|
48
|
+
// Session-local prompt cache: file name -> raw markdown. Re-opening a card
|
|
49
|
+
// renders from here instead of refetching.
|
|
50
|
+
const promptCache = new Map();
|
|
51
|
+
|
|
52
|
+
// ---- Section: sticky header + card grid ----
|
|
53
|
+
function AgentSection({ section, agents, metaByFile, onOpen }) {
|
|
54
|
+
if (!agents.length) return null;
|
|
55
55
|
return html`
|
|
56
|
-
<div class="
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
<div class="agent-section">
|
|
57
|
+
<div class="agent-section-head">
|
|
58
|
+
${section.label}
|
|
59
|
+
<span class="agent-section-count">${agents.length}</span>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="agent-grid">
|
|
62
|
+
${agents.map(a => html`
|
|
63
|
+
<${AgentCard}
|
|
64
|
+
key=${a.name}
|
|
65
|
+
agent=${a}
|
|
66
|
+
meta=${a.file ? metaByFile[a.file] : null}
|
|
67
|
+
onOpen=${onOpen}
|
|
68
|
+
/>
|
|
69
|
+
`)}
|
|
70
|
+
</div>
|
|
59
71
|
</div>
|
|
60
72
|
`;
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
// ---- Root AgentsView ----
|
|
64
76
|
export function AgentsView() {
|
|
65
|
-
const [filter, setFilter]
|
|
77
|
+
const [filter, setFilter] = useState('');
|
|
78
|
+
const [metaByFile, setMetaByFile] = useState({});
|
|
79
|
+
const [selected, setSelected] = useState(null);
|
|
80
|
+
const [prompt, setPrompt] = useState({ loading: false, error: null, text: null });
|
|
81
|
+
|
|
82
|
+
// One-shot roster metadata fetch — frontmatter summaries only, no bodies.
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
fetch('/api/agents')
|
|
85
|
+
.then(r => r.json())
|
|
86
|
+
.then(list => {
|
|
87
|
+
const map = {};
|
|
88
|
+
for (const a of (Array.isArray(list) ? list : [])) map[a.file] = a;
|
|
89
|
+
setMetaByFile(map);
|
|
90
|
+
})
|
|
91
|
+
.catch(() => { /* chips/summaries are progressive enhancement — cards still render */ });
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
const openAgent = useCallback(async (agent) => {
|
|
95
|
+
setSelected(agent);
|
|
96
|
+
if (!agent.file) {
|
|
97
|
+
setPrompt({ loading: false, error: null, text: null });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (promptCache.has(agent.file)) {
|
|
101
|
+
setPrompt({ loading: false, error: null, text: promptCache.get(agent.file) });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
setPrompt({ loading: true, error: null, text: null });
|
|
105
|
+
try {
|
|
106
|
+
const resp = await fetch('/api/file?path=' + encodeURIComponent('rcode/agents/' + agent.file));
|
|
107
|
+
if (!resp.ok) {
|
|
108
|
+
const msg = resp.status === 404
|
|
109
|
+
? 'Prompt file not found: rcode/agents/' + agent.file
|
|
110
|
+
: 'Failed to load prompt (HTTP ' + resp.status + ').';
|
|
111
|
+
setPrompt({ loading: false, error: msg, text: null });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const text = await resp.text();
|
|
115
|
+
promptCache.set(agent.file, text);
|
|
116
|
+
setPrompt({ loading: false, error: null, text });
|
|
117
|
+
} catch {
|
|
118
|
+
setPrompt({ loading: false, error: 'Network error.', text: null });
|
|
119
|
+
}
|
|
120
|
+
}, []);
|
|
121
|
+
|
|
122
|
+
const closeDrawer = useCallback(() => setSelected(null), []);
|
|
123
|
+
|
|
124
|
+
// Escape closes the drawer while it is open.
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (!selected) return;
|
|
127
|
+
const onKey = (e) => { if (e.key === 'Escape') closeDrawer(); };
|
|
128
|
+
document.addEventListener('keydown', onKey);
|
|
129
|
+
return () => document.removeEventListener('keydown', onKey);
|
|
130
|
+
}, [selected, closeDrawer]);
|
|
131
|
+
|
|
132
|
+
const sections = useMemo(() =>
|
|
133
|
+
SECTIONS.map(s => ({
|
|
134
|
+
section: s,
|
|
135
|
+
agents: agentsForSection(s).filter(a =>
|
|
136
|
+
matchesFilter(a, a.file ? metaByFile[a.file] : null, filter)),
|
|
137
|
+
})),
|
|
138
|
+
[filter, metaByFile]);
|
|
139
|
+
|
|
140
|
+
const visibleCount = sections.reduce((n, s) => n + s.agents.length, 0);
|
|
66
141
|
|
|
67
142
|
return html`
|
|
68
143
|
<div class="view active" id="view-agents">
|
|
69
144
|
<div class="view-title">Team</div>
|
|
70
|
-
<div class="filter-bar">
|
|
145
|
+
<div class="filter-bar agent-filter-bar">
|
|
71
146
|
<input
|
|
72
147
|
class="filter-input"
|
|
73
148
|
type="text"
|
|
74
|
-
placeholder="
|
|
149
|
+
placeholder="Search agents by name, role, model, or tool…"
|
|
75
150
|
value=${filter}
|
|
76
151
|
onInput=${e => setFilter(e.target.value)}
|
|
77
152
|
/>
|
|
153
|
+
<span class="agent-count">${visibleCount} agent${visibleCount === 1 ? '' : 's'}</span>
|
|
78
154
|
</div>
|
|
79
|
-
|
|
80
|
-
|
|
155
|
+
${visibleCount === 0
|
|
156
|
+
? html`<div class="empty">No agents match “${filter}”.</div>`
|
|
157
|
+
: sections.map(({ section, agents }) => html`
|
|
158
|
+
<${AgentSection}
|
|
159
|
+
key=${section.label}
|
|
160
|
+
section=${section}
|
|
161
|
+
agents=${agents}
|
|
162
|
+
metaByFile=${metaByFile}
|
|
163
|
+
onOpen=${openAgent}
|
|
164
|
+
/>
|
|
165
|
+
`)}
|
|
166
|
+
${selected ? html`
|
|
167
|
+
<${AgentDrawer}
|
|
168
|
+
agent=${selected}
|
|
169
|
+
meta=${selected.file ? metaByFile[selected.file] : null}
|
|
170
|
+
prompt=${prompt}
|
|
171
|
+
onClose=${closeDrawer}
|
|
172
|
+
/>
|
|
173
|
+
` : null}
|
|
81
174
|
</div>
|
|
82
175
|
`;
|
|
83
176
|
}
|