feat(#2316): add Scripts subtab to Tasks panel with backend list/raw endpoints#3935
feat(#2316): add Scripts subtab to Tasks panel with backend list/raw endpoints#3935rodboev wants to merge 4 commits into
Conversation
|
| Filename | Overview |
|---|---|
| api/routes.py | Adds scripts list/raw endpoints with safe_resolve guard and extension filter; _parse_script_docstring has a blank-line handling bug that silently drops descriptions for scripts with a blank line between the shebang and the first comment. |
| static/panels.js | Adds switchTasksSubtab, loadScripts, and _renderScriptsList; subtab switching logic and lazy load are correct; duplicate click-handler pattern is unusual but works because inline onclick fires before addEventListener. |
| static/index.html | Adds subtab buttons and pane wrappers; correctly mirrors existing cron layout with no structural issues. |
| static/i18n.js | Adds five new i18n keys across all 13 locales; translations appear appropriate. |
| static/style.css | Adds subtab and script card CSS rules; no conflicts with existing selectors. |
| tests/test_2316_scripts_panel.py | Good coverage of list, raw, path traversal, and sort order; the blank-line-between-shebang-and-description case is not tested, leaving the parser bug uncaught. |
Sequence Diagram
sequenceDiagram
participant U as User
participant UI as panels.js
participant BE as routes.py (backend)
participant FS as ~/.hermes/scripts/
U->>UI: click "Scripts" subtab
UI->>UI: switchTasksSubtab('scripts')
UI->>BE: GET /api/scripts/list
BE->>FS: iterdir() + filter .py/.sh/.bash/.zsh
FS-->>BE: file paths
BE->>BE: _parse_script_docstring(p) per file
BE-->>UI: "{scripts: [{name, description}, ...]}"
UI->>UI: _renderScriptsList() render cards
U->>UI: click script card to expand
UI->>UI: classList.toggle('expanded')
alt source not yet loaded
UI->>BE: "GET /api/scripts/raw?path=name"
BE->>BE: safe_resolve(scripts_dir, name)
BE->>BE: extension allowlist check
BE->>FS: read_text()
FS-->>BE: source content
BE-->>UI: "{name, source}"
UI->>UI: set code.textContent + Prism.highlightElement()
end
UI->>UI: sourceEl.style.display visible
U->>UI: click card to collapse
UI->>UI: sourceEl.style.display hidden
Reviews (4): Last reviewed commit: "fix(#2316): harden raw endpoint, catch V..." | Re-trigger Greptile
…ist/raw endpoints
76fbc32 to
da4cb2a
Compare
Thinking Path
switchPanel → loadCrons()), but the scripts in~/.hermes/scripts/that those jobs reference are invisible from the UI — users must leave the browser to inspect them.switchSettingsSection+ pane toggling) adapted to a simpler two-tab case.~/.hermes/scripts/path used by the cron hint text (cron_script_path_hintin i18n.js) and applies the samesafe_resolve()guard already used by the skills content endpoint (/api/skills/content) for traversal protection.What Changed
static/index.html: replace#panelTaskspanel header with subtab buttons (#tasksSubtabJobs,#tasksSubtabScripts), wrap the cron list in#tasksJobsPane, add#tasksScriptsPanewith#scriptsListstatic/panels.js: addswitchTasksSubtab(),loadScripts(),_renderScriptsList()functions; updateswitchPaneltasks branch to respect active subtabstatic/i18n.js: addtab_tasks_jobs,tab_tasks_scripts,scripts_no_scripts,scripts_load_error,scripts_path_labelkeysapi/routes.py: addGET /api/scripts/listandGET /api/scripts/rawrouting entries; add_hermes_scripts_dir(),_parse_script_docstring(),_handle_scripts_list(),_handle_scripts_raw()handler functionsstatic/style.css: add.tasks-subtabs,.tasks-subtab,.scripts-list,.script-item,.script-header,.script-desc,.script-sourcerulesWhy It Matters
Users can now browse, read descriptions for, and inspect the source of any script in
~/.hermes/scripts/directly from the Tasks panel without needing server access, making script-backed cron jobs understandable at a glance.Verification
Manual: open Tasks panel, click Scripts subtab, verify list populates; click a script card to expand source; attempt
?path=../../etc/passwdin devtools — expect 400.Risks / Follow-ups
~/.hermes/profiles/<profile>/scripts/discovery deferred to a follow-up (issue comments call out global scope for first cut).rb) are not listed; can be extended by adding to the suffix allowlist in_handle_scripts_listModel Used
Claude Opus 4.6 via Claude Code CLI