Releases: KenM76/scriptree
v0.8.0a45 — single-tool cell + action-panel polish
Single-tool cells now behave correctly with the developer-editor gesture, plus a one-click "open in editor" affordance on every tool's right-click action panel. Continues the v0.8.0a-series alpha.
Highlights
Single-tool cell double-click → developer editor
Double-left-click on a cell bound to a single .scriptree (or a tree with one leaf) used to launch V1's standalone runner. It now opens the developer editor — uniform with multi-tool cells. Two interacting causes were both fixed:
v1_launcher.show_tree_for(mode=\"lock-open\")no longer branches by extension. Both.scriptreeand.scriptreetreeroute tolaunch_editor_with_tree(V1 opens the editor when the path is positional without-standalone).cell_window.pydouble-click handler: the v0.8.0a25 "single-tool shortcut" (which calledlaunch_tooldirectly) is removed. Single-tool cells now follow the standard lock-open path.
Edit icon next to the X in the action panel
Right-clicking a tool in the cell menu opens the per-item action panel. The header now carries an "Open in developer editor" button (📄 file glyph) between the tool name and the X. One click opens the catalog in the developer editor; the snapshot overlay and dialog dismiss cleanly.
Action panel: snapshot overlay (carried from a44)
The tool menu stays visually "locked in place" while the action panel is open (it's a pixmap overlay, not the real menu — Qt's popup-grab forbids both at once). Dismiss matrix:
- X button → real tool menu reopens
- Action picked / outside click → menu and panel both go away
Build artifact
ScripTree-v0.8.0a45.zip is the portable, ready-to-run end-user build. Unzip and run run_scriptreering.bat (the cell shell) or run_scriptree.bat (the developer editor) — no install required. Includes vendored Python and combridge runtime.
Behaviour unchanged
Single-click is still governed by the catalog's cell.click_action setting — switch a cell from "Run" to "Menu" via the cell Settings dialog if you want single-click to show a popup instead of running directly.
Commits since v0.8.0a44
251bd6cv0.8.0a45: single-tool cells double-click → editor, + edit-icon in action panel
v0.8.0a25
Highlights since v0.8.0a23
Category taxonomy + auto-organise (new)
- New
categoryfield onToolDef/TreeDef(slash-delimited path likeMSOffice/Word). - Forest's discovery pass groups tools sharing a top-level category into a synthesised
.scriptreetree, written into<personal-apps>/_groups/. Multiple Word/Excel tools collapse into a single MSOffice cell. Forest ▸ Re-organisemenu item to re-run the pass on demand.- Authoring contract:
docs/LLM/category_authoring.md.
SCRIPTREE_HOME canonical env-var contract (new)
- The runner now injects
SCRIPTREE_HOME,SCRIPTREE_LIB,SCRIPTREE_TOOL_DIRon every spawned tool's environment AND prepends<install>/lib/combridgetoPATH. - Tools can call
combridge.exeby bare name — no discovery code needed. - Authoring contract:
docs/LLM/scriptree_home_env_var.md. - The 21 Office tools shipped in
ScripTreeAppProjectsare simplified — ~20 lines of upward-walk boilerplate removed per tool.
Validator lint for unsectioned forms (new)
scriptree validatenow emits[WARN]lines on forms with>4params and no sections, with a stronger recommendation for tab-mode at≥10params.- Non-blocking by default;
--strictpromotes to non-zero exit for CI.
UX polish
- Settings dialog: tab renamed Label → Label/Icon; live preview thumbnail of the actually-rendered icon; OK / Cancel / Reset semantics across every settings-style dialog; Reset reloads catalog-authored state instead of wiping to factory defaults.
- Standalone window: when
UIVisibilitysuppresses the Run-controls or Output sections, they're now removed from the layout entirely (previously left empty containers behind). - Per-tool taskbar identity: launched tools get their own taskbar slot and icon based on the catalog's embedded glyph, instead of all stacking under the generic ScripTree forest icon.
- Single-tool double-click shortcut: double-left-click on a cell whose catalog contains exactly one tool launches it directly, skipping the menu.
- Forest persistence: closing a cell now removes it from
forest.itemsand adds the path toexcludedso saving the forest reflects the user's actual layout; closed cells don't resurrect on reload. - Forest auto-discovery: the personal-apps directory (
%LOCALAPPDATA%/ScripTree/Appson Windows; OS-appropriate elsewhere) is now in the default scan-folders list of every new forest.
Drop-install (shipped a23, polished a25)
- All five phases complete: location dialog, conflict dialog, settings INI rows, post-install forest refresh, multi-drop support.
Choose image file…andLibrary…buttons in the Settings → Label/Icon tab embed icons directly into the catalog as base64.
No-bytecode policy (new)
- Every launcher
.batsetsPYTHONDONTWRITEBYTECODE=1; every entry-point.pyandscriptree/__init__.pysetsys.dont_write_bytecode = True. CI guard intests/test_no_bytecode_guard.pypins it. - Prevents
__pycache__/*.pycwrites that paralyse cloud-sync clients (Dropbox/OneDrive/Google Drive) on every launch. - Policy doc:
docs/LLM/no_bytecode_policy.md.
Screenshooter
- Default
formandeditorcaptures now show only the parameter section — extras / command-line / output hidden unless--show-extras,--show-command-line,--show-output, or--full. - Form panel auto-grows to fit all parameter rows.
Tooling for catalog authors
scripts/bulk_categorize.py— one-shot stampcategoryfields onto every.scriptreein a tree.scripts/bulk_update_find_combridge.py/bulk_simplify_find_combridge.py— bulk-update Office tool helpers across multiple deployment locations.
Cross-platform category doc + LLM authoring contracts
- New:
docs/LLM/category_authoring.md,docs/LLM/scriptree_home_env_var.md,docs/LLM/no_bytecode_policy.md.
Download
The portable zip below is fully self-contained (Python + PySide6 + combridge bundled). Extract and double-click run_scriptreeforest.bat.
🤖 Generated with Claude Code
v0.8.0a22 — cross-platform overrides (one .scriptree, three OSes)
Cross-platform overrides
A single .scriptree file can now declare per-OS variants of the executable, argument template, PATH-prepend, env vars, and action-button argv. The same tool runs on Windows / macOS / Linux without forking the catalog into three parallel files.
Why
Most CLI tools have OS-dependent runtimes:
- Python:
py.exe -3 ./tool.pyon Windows,python3 ./tool.pyon macOS / Linux - Microsoft Office automation:
combridge.exedriving COM on Windows,osascriptdriving AppleScript on macOS (completely different argv shape) - Native binaries living under different filesystem roots per OS
Before v0.8.0a22 an author had to ship one .scriptree per OS and the user saw three near-identical entries in their menu. Now it's one file, one menu entry, the right binary fires per host.
What landed
Schema (scriptree/core/model.py):
{
"executable": "combridge.exe",
"argument_template": ["word", "active-document.text"],
"platforms": {
"macos": {
"executable": "/usr/bin/osascript",
"argument_template": ["-e", "tell application \"Microsoft Word\" to return content of active document as string"]
}
}
}Top-level fields are the default; platforms.<os> overrides per-field. Per-field replace (no deep merge). Empty per-OS entry {} = "supported, identical to default". Missing entry = "no explicit support claim; inherit defaults at run time anyway."
Runtime (scriptree/core/runner.py):
build_full_argv and resolve_action call resolve_for_host(tool) at the top so the resolved argv reflects the host OS before any spawn. Original ToolDef is never mutated — the editor keeps the full cross-platform view.
Editor UX (scriptree/ui/tool_editor.py + new platform_overrides_widget.py):
A new "Per-OS overrides" group sits between the Tool section and the Parameters splitter. Three tabs (Windows / macOS / Linux); each has an "Override for this OS" checkbox + editable fields for executable, argument_template, path_prepend. When the checkbox is off, the fields show a read-only "Inherited from default" preview. A "Preview command line as:" dropdown at the bottom flips the preview to any OS independent of which tab is being edited.
Docs:
docs/LLM/scriptree_format.md— full schema section with worked examples (Python on three OSes, combridge-vs-osascript)- Per-field semantics, OS-id mapping, fall-back rules, and the editor-support pointer all spelled out
OS detection
scriptree.core.platform.host_os() returns "windows" / "macos" / "linux". Maps Python's platform.system() ("Windows" / "Darwin" / "Linux"). Unknown platforms fall back to "linux" (safest POSIX shape). Cached per process.
Tests
+69 new tests across:
tests/test_platforms_io.py— 13 (round-trip, byte-identical legacy, malformed-tolerance)tests/test_platforms_resolve.py— 16 (host detection, per-field replace, original-not-mutated)tests/test_platforms_runtime.py— 7 (build_full_argv + resolve_action pick the right OS variant)tests/test_platform_overrides_widget.py— 13 (editor widget load / apply / refresh / round-trip)tests/test_doc_examples.py— 4 new JSON examples now parse against the loader
Existing runner + editor suites stay green (no regressions).
Internal
0.8.0a21 → 0.8.0a22. Build date 2026-05-30 09:31 EDT.
🤖 Generated with Claude Code
v0.8.0a21 — tree auto-discovery (parity with forest)
Tree auto-discovery
A .scriptreetree-bound cell can now scan its own folder for new .scriptree files and offer to add them. Parity with the existing .scriptreeforest auto-discovery feature, scoped to a single tree.
What landed
Right-click → Tree submenu (on any .scriptreetree-bound cell):
- Refresh from sources — run the walker against the tree's
rootsand react per the persistedupdate_mode. - Auto-add from this folder now — force a one-shot prompt regardless of mode.
- Tree settings… — edit the auto-discover config (enabled / roots / sibling-tree toggle / update_mode).
- Excluded items… — manage paths the user has explicitly removed.
First-load chooser — on the very first open of a .scriptreetree that has no auto_discover block, a small dialog asks the user to pick a mode (Prompt / Auto / Off). Choice persists; never asks again for that tree.
Diff dialog — three sections (Add / Remove / Previously excluded) with checkable rows. Default checked for Add and Remove, default unchecked for Previously excluded.
MainWindow File menu — new Scan tree for new tools… entry, enabled when a tree is loaded with a backing file. Editor-side equivalent of the cell shell's Refresh.
Walker rule
For each directory the walker reaches:
- If the directory contains another
.scriptreetree, stop descending (that subtree is owned by the other file); optionally surface the boundary file as a candidate sub-tree leaf viainclude_sibling_trees. - Otherwise emit every
.scriptreeand recurse.
.scriptreetree schema additions
Two new optional top-level fields documented in docs/LLM/scriptreetree_format.md:
auto_discover—TreeAutoDiscoverConfig(enabled/roots/include_sibling_trees/update_mode).excluded— paths the user has removed; routed by the diff to a separatePreviously excludedsection so they can be re-included.
A non-None auto_discover (even {}) signals "user has been asked"; the chooser fires only when the key is absent or null. Legacy files round-trip byte-identical when never touched by the feature.
What changed under the hood
| Module | Role |
|---|---|
scriptree/core/discovery.py |
Shared TreeAutoDiscoverConfig + UpdateMode literal |
scriptree/core/tree_discover.py |
Pure-Python walker (no Qt) |
scriptree/core/tree_diff.py |
Diff + apply (no Qt) |
scriptree/ui/tree_dialogs.py |
TreeUpdateDiffDialog, TreeSettingsDialog, ChooseUpdateModeDialog |
scriptree/ui/discovery_widgets.py |
Shared widget library (RootsEditor, UpdateModeChoice, IncludeKindsChecklist) used by both forest and tree settings dialogs |
scriptree/shell/tree_controller.py |
Per-cell orchestration; installs _tree_menu_extension |
scriptree/shell/cell_window.py |
Hook invocation + auto-attach on every catalog-mutation site (Load, drop, Save as, Clear) |
scripts/set_default_auto_discover.py |
One-shot batch updater that stamps auto_discover: {} on every shipped tree to suppress the first-load chooser on existing catalogs |
Tests
+95 new tests across these files:
tests/test_tree_auto_discover_io.py(17)tests/test_tree_discover.py(21)tests/test_tree_diff.py(23)tests/test_tree_dialogs.py(15)tests/test_tree_controller.py(20, includes the 5-test catalog-rebind suite)tests/test_main_window_scan_tree.py(7)
Existing forest + dialog suites stay green after the shared-widget refactor.
Other v0.8.0a21 notes
- Bumped version 0.8.0a20 → 0.8.0a21.
- Build date 2026-05-29 21:51 EDT.
- Every
.scriptreetreethis repo ships now carries"auto_discover": {}so the chooser doesn't fire on upgrade. cell_window.py's_attach_tree_controller_if_applicableis wired into every runtime_catalog_pathmutation site so loading / dropping / saving-as a new tree on an existing cell correctly swaps the controller.
🤖 Generated with Claude Code
v0.8.0a20 — rename help/ to docs/, drop internal artifacts
Repository structure cleanup
Adopts the universal open-source convention for documentation: docs/ is where user-facing documentation lives, not help/.
Why
Before this release the public repo had two confusingly-named folders:
docs/held internal artifacts — a competitive analysis Word doc, the script that produced it, and three beta-test reports from Claude sessions. None of that has any business in a public-facing repo.help/held the actual user-facing documentation — README, getting_started, quickstart, configurations, security, theLLM/authoring contract, the parser pages, etc.
That's backwards from convention. docs/ is the de-facto standard everywhere — Sphinx, mkdocs, Read the Docs, PyPI's "Documentation" link, GitHub Pages. help/ is uncommon and usually refers to in-app help systems (HTML help files bundled into desktop apps), not project documentation.
What changed
- The previous
docs/content (ScripTree_Competitive_Analysis.docx,competitive_analysis.py,beta-reports/) is no longer in the repo. It's preserved privately outside version control. - The previous
help/is nowdocs/. Same content, conventional name. scriptree/ui/help_dialog.py::help_root()resolves<pkg parent>/docsfirst, then falls back to<pkg parent>/helpfor backwards compatibility with end-user installs that haven't been refreshed yet.- Every in-repo cross-reference updated: README,
scriptree.schema.json, every doc-to-doc link in the renamed markdown, and the docstring/comment references inscriptree/core/{io,model,providers,runner}.py,scriptree/shell/{cell_window,icon_assets}.py,scriptree/resources/make_icon.py,scripts/gen_facet_icons.py, and the affected tests.
Tests
Full suite green: 1757 passed, 5 skipped.
Internal version
0.8.0a19 → 0.8.0a20, build date 2026-05-29 09:54 EDT.
🤖 Generated with Claude Code
v0.8.0a19 — form panel layout overhaul + cross-platform settings
Form panel layout overhaul
The user kept reporting across v0.8.0a14 / a16 / a17 / a18 that the Run / Stop buttons were getting scrolled off the bottom of the form dock in standalone mode, while developer mode worked fine. v0.8.0a19 lands the actual fix plus the two follow-on issues that surfaced once the bottom band was visible again.
Fixed
-
Run row no longer scrolls out of sight in standalone mode. Three load-bearing pieces:
_FormPanelContaineris now a realQWidgetsubclass with C++ virtualminimumSizeHint/sizeHintoverrides. The previous instance-attribute assignment was silently bypassed by Qt's C++ vtable, so QtAds saw 0×0 and freely shrank the form dock below the bottom band.form_dock.setMinimumSizeHintMode(MinimumSizeHintFromContent)so QtAds queries the contained widget'sminimumSizeHintinstead of the dock widget's own (always 0×0). Standalone now mirrors what MainWindow already did for its tools dock.- New
_FormScrollAreasubclass caps the params scroll area'ssizeHintat a small fixed value so it doesn't inflateform_panel.layout().sizeHint()past the dock viewport — which would force QtAds to wrap the whole form in a second scroll area and push the Run row off the bottom.
-
Beige gap between the params view and the Configuration row is gone.
_populate_form_rowsnow assigns the trailing layout stretch to the LAST inserted widget (QTabWidget / collapsible section / flat form) so it fillsform_groupvertically. Tab-section tools like Find Missing Refs (Source / Matching / Apply) now show their white params area all the way down to the Configuration row. -
Shrinking a resizable param widget tracks live.
ResizableContainer._on_draggedno longer clamps the row sizeHint atmax(current, new)— the row tracks the container's height in BOTH directions. Dragging back up to shrink a multi-line field now shrinks the row from the bottom edge and the next row slides up with the drag instead of waiting for a window resize.
Changed
-
Settings storage moved from Windows registry to portable INI.
standalone_window.pyandaction_result_dialog.pynow route throughcore.app_settings.get_settings(), writing toscriptree.ininext to the install instead ofHKCU\Software\ScripTree. Cross-platform; layout state travels with the install. -
_LAYOUT_SCHEMAbumped v3 → v4 so any saved standalone dock layout from a broken intermediate build is invalidated on first launch. -
StandaloneWindowno longer wrapsform_panelin aQStackedWidgetbefore installing in the QtAds dock. The wrapper empirically diverged from its child'ssizeHint()override when the child had a complex layout, re-inflating the dock past the viewport. Direct install + the C++ vtable override is enough.
Tests
- New
tests/test_form_panel_dock_sizing.pywith 10 regression tests covering the C++ vtable reachability, QtAdsMinimumSizeHintFromContentmode, Run-button visibility inside form_dock (not just inside form_panel), and tab-section content filling form_group. - New
test_shrink_drag_shrinks_row_sizehintintests/test_resizable_container.pypins the bidirectional row-sizeHint tracking. - Full suite: 1757 passed, 5 skipped.
Internal version
0.8.0a18→0.8.0a19, build date2026-05-28 11:25 EDT.
🤖 Generated with Claude Code
v0.8.0a17 — pin form panel minimum + propagate resize up
Two more layout fixes
1. Standalone run controls still got scrolled away
Even with the v0.8.0a14 bottom-band + a16 QStackedWidget wrapper, the form panel's minimumSizeHint was effectively 0 (because form_scroll.setMinimumHeight(0) from v0.8.0a13 contributes nothing and the bottom band's minimumSizeHint defaulted to 0 too). The MainWindow's QStackedWidget masked the issue; the standalone path didn't.
Fix: explicit minimum heights — bottom_band.setMinimumHeight(200) (covers cfg row + extras + cmd + Run/Stop row + status with room to spare) and container.setMinimumHeight(310) (header + small form floor + bottom band). Both modes now floor the form dock at a sane height; the run controls always have a place to sit.
2. Resizing a multi-line / list widget didn't grow the surrounding section
The v0.8.0a16 fix updated the QListWidgetItem's sizeHint but the QListWidget's own sizeHint wasn't re-asked, so the tab section's QScrollArea kept its original size and the resized widget overflowed visually behind the next section.
Fix: after rewriting the row item sizeHint, call form_list.updateGeometry() to invalidate the QListWidget's own size, then walk up the parent chain calling layout().invalidate() + updateGeometry() on every intermediate scroll area / group box. The entire chain re-asks for sizeHints and the section grows with the widget.
Tests
Suite: 1738 passing, 5 skipped (no test count change).
Download
ScripTree-v0.8.0a17.zip — portable Windows build with combridge bundled.
v0.8.0a16 — resize grows the row, standalone form wrap
Two fixes following v0.8.0a15
1. Resizing the box now grows the section it sits in
In v0.8.0a15 the resize handle grew the inner widget but the surrounding QListWidgetItem (the param row) kept its original sizeHint. The widget ended up overflowing behind the next param row — the failure mode you described.
The param row's cached sizeHint is now updated directly when the drag happens: walk up to the owning QListWidget, find the row's QListWidgetItem, and set its sizeHint to include the dragged-to height plus a small padding. Intermediate layouts are invalidated on the way so future sizeHint calls see the new value. The container's sizeHint is also overridden to report the desired (not natural) height so QPlainTextEdit.sizeHint doesn't reassert its fontmetric-derived hint.
2. Standalone mode now holds the bottom band in place
v0.8.0a14's bottom-band fix made the configurations bar + Run/Stop row hold their space in the developer-mode editor — which empirically depends on the form panel being wrapped in a QStackedWidget before QtAds receives it (MainWindow's editor uses the stack to switch between loaded tools; it also makes QtAds honour the inner Fixed sizePolicy reliably).
The standalone window installed the form panel directly into the QtAds dock without that wrapper, so QtAds's geometry negotiation pushed the bottom controls out of view when the dock was tight.
Fix: mirror MainWindow's pattern by wrapping form_panel in a QStackedWidget before the standalone dock receives it. No behavioural change otherwise — the stack has one page (the form panel) and that's the current widget.
Tests
+1 new test test_dragging_inside_param_form_grows_the_row verifies the row's sizeHint grows when the inner widget is dragged. Updated the existing standalone-window visibility test to reach through the stack via stack.currentWidget(). Suite: 1738 passing, 5 skipped.
Download
ScripTree-v0.8.0a16.zip — portable Windows build with combridge bundled.
v0.8.0a15 — drag-handle resize for multi-line + list widgets
What's new
The multi-line text area, checkbox list, and folder/file list param widgets were all capped at a fixed pixel height (80 px for text, 160 px for lists) via setMaximumHeight. Long content had to scroll inside the widget even when there was plenty of vertical room on the form.
Fix: a new ResizableContainer wrapper widget puts a thin grip handle (5 px high, three small dots in the middle) below the wrapped widget. Click-and-drag the handle to grow / shrink it vertically. No upper cap — the form's outer scroll area already handles overgrown rows by scrolling. The drag clamps at a lower bound (32 px for text, 48 px for lists) so the widget can't be dragged into a useless zero-pixel strip.
Applied to:
TextAreaWidget(thetextwidget forstringparams) — was capped at 80 pxCheckboxListWidget(thecheckbox_listwidget formultiselectparams) — was capped at 160 pxFolderListWidget/FileListWidget(thefolder_list/file_listwidgets formultiselectpaths) — were capped at 160 px
The value API is unchanged — get_value / set_value still round-trip through the original child widget reference, so no functional regression.
Tests
New test_resizable_container.py covers container basics (initial height, drag grows, min-height clamp, no upper cap, handle emits signal with delta) and per-widget wiring (each candidate widget builds with a ResizableContainer around its child). Suite: 1737 passing, 5 skipped (+9 net).
Download
ScripTree-v0.8.0a15.zip — portable Windows build with combridge bundled.
v0.8.0a14 — bottom-band fix for standalone mode
Bug fix (continuation of v0.8.0a13)
The v0.8.0a13 form_scroll.setMinimumHeight(0) fix kept the Run / Stop buttons visible in the developer-mode editor but not in the StandaloneWindow — the editor wraps the form panel in a QStackedWidget, while standalone puts the form panel directly inside a QtAds dock, and QtAds's dock geometry negotiation doesn't honour the inner scroll area's "I can shrink" promise reliably enough.
Fix: wrap the configurations bar + extras + command line + Run row + action-button row + status into a single bottom_band widget with QSizePolicy(Preferred, Fixed). Qt's QVBoxLayout reads Fixed as "must get sizeHint exactly," so the band claims its natural height first; form_scroll absorbs whatever is left over (or compresses to nothing). Result: the bottom controls always stay visible across both MainWindow and StandaloneWindow regardless of QtAds's quirks.
What stays the same
The visual shape is unchanged — same arrow-collapse sections, same vertical order. Only the underlying widget hierarchy changed (a single band widget instead of widgets sitting directly in the outer layout).
Tests
The existing test_form_panel_layout_order test was re-pointed at the band's own layout. Suite: 1728 passing, 5 skipped (no net change).
Download
ScripTree-v0.8.0a14.zip — portable Windows build with combridge bundled.