Skip to content

Releases: KenM76/scriptree

v0.8.0a45 — single-tool cell + action-panel polish

05 Jun 16:31

Choose a tag to compare

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 .scriptree and .scriptreetree route to launch_editor_with_tree (V1 opens the editor when the path is positional without -standalone).
  • cell_window.py double-click handler: the v0.8.0a25 "single-tool shortcut" (which called launch_tool directly) 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

  • 251bd6c v0.8.0a45: single-tool cells double-click → editor, + edit-icon in action panel

v0.8.0a25

02 Jun 19:49

Choose a tag to compare

Highlights since v0.8.0a23

Category taxonomy + auto-organise (new)

  • New category field on ToolDef / TreeDef (slash-delimited path like MSOffice/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-organise menu 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_DIR on every spawned tool's environment AND prepends <install>/lib/combridge to PATH.
  • Tools can call combridge.exe by bare name — no discovery code needed.
  • Authoring contract: docs/LLM/scriptree_home_env_var.md.
  • The 21 Office tools shipped in ScripTreeAppProjects are simplified — ~20 lines of upward-walk boilerplate removed per tool.

Validator lint for unsectioned forms (new)

  • scriptree validate now emits [WARN] lines on forms with >4 params and no sections, with a stronger recommendation for tab-mode at ≥10 params.
  • Non-blocking by default; --strict promotes 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 UIVisibility suppresses 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.items and adds the path to excluded so 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/Apps on 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… and Library… buttons in the Settings → Label/Icon tab embed icons directly into the catalog as base64.

No-bytecode policy (new)

  • Every launcher .bat sets PYTHONDONTWRITEBYTECODE=1; every entry-point .py and scriptree/__init__.py set sys.dont_write_bytecode = True. CI guard in tests/test_no_bytecode_guard.py pins it.
  • Prevents __pycache__/*.pyc writes that paralyse cloud-sync clients (Dropbox/OneDrive/Google Drive) on every launch.
  • Policy doc: docs/LLM/no_bytecode_policy.md.

Screenshooter

  • Default form and editor captures 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 stamp category fields onto every .scriptree in 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)

31 May 00:14

Choose a tag to compare

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.py on Windows, python3 ./tool.py on macOS / Linux
  • Microsoft Office automation: combridge.exe driving COM on Windows, osascript driving 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.0a210.8.0a22. Build date 2026-05-30 09:31 EDT.

🤖 Generated with Claude Code

v0.8.0a21 — tree auto-discovery (parity with forest)

30 May 01:53

Choose a tag to compare

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 roots and react per the persisted update_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 via include_sibling_trees.
  • Otherwise emit every .scriptree and recurse.

.scriptreetree schema additions

Two new optional top-level fields documented in docs/LLM/scriptreetree_format.md:

  • auto_discoverTreeAutoDiscoverConfig (enabled / roots / include_sibling_trees / update_mode).
  • excluded — paths the user has removed; routed by the diff to a separate Previously excluded section 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 .scriptreetree this repo ships now carries "auto_discover": {} so the chooser doesn't fire on upgrade.
  • cell_window.py's _attach_tree_controller_if_applicable is wired into every runtime _catalog_path mutation 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

29 May 13:57

Choose a tag to compare

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, the LLM/ 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 now docs/. Same content, conventional name.
  • scriptree/ui/help_dialog.py::help_root() resolves <pkg parent>/docs first, then falls back to <pkg parent>/help for 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 in scriptree/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.0a190.8.0a20, build date 2026-05-29 09:54 EDT.

🤖 Generated with Claude Code

v0.8.0a19 — form panel layout overhaul + cross-platform settings

28 May 20:35

Choose a tag to compare

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:

    1. _FormPanelContainer is now a real QWidget subclass with C++ virtual minimumSizeHint/sizeHint overrides. 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.
    2. form_dock.setMinimumSizeHintMode(MinimumSizeHintFromContent) so QtAds queries the contained widget's minimumSizeHint instead of the dock widget's own (always 0×0). Standalone now mirrors what MainWindow already did for its tools dock.
    3. New _FormScrollArea subclass caps the params scroll area's sizeHint at a small fixed value so it doesn't inflate form_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_rows now assigns the trailing layout stretch to the LAST inserted widget (QTabWidget / collapsible section / flat form) so it fills form_group vertically. 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_dragged no longer clamps the row sizeHint at max(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.py and action_result_dialog.py now route through core.app_settings.get_settings(), writing to scriptree.ini next to the install instead of HKCU\Software\ScripTree. Cross-platform; layout state travels with the install.

  • _LAYOUT_SCHEMA bumped v3 → v4 so any saved standalone dock layout from a broken intermediate build is invalidated on first launch.

  • StandaloneWindow no longer wraps form_panel in a QStackedWidget before installing in the QtAds dock. The wrapper empirically diverged from its child's sizeHint() 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.py with 10 regression tests covering the C++ vtable reachability, QtAds MinimumSizeHintFromContent mode, Run-button visibility inside form_dock (not just inside form_panel), and tab-section content filling form_group.
  • New test_shrink_drag_shrinks_row_sizehint in tests/test_resizable_container.py pins the bidirectional row-sizeHint tracking.
  • Full suite: 1757 passed, 5 skipped.

Internal version

  • 0.8.0a180.8.0a19, build date 2026-05-28 11:25 EDT.

🤖 Generated with Claude Code

v0.8.0a17 — pin form panel minimum + propagate resize up

28 May 14:25

Choose a tag to compare

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

28 May 13:57

Choose a tag to compare

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

28 May 13:37

Choose a tag to compare

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 (the text widget for string params) — was capped at 80 px
  • CheckboxListWidget (the checkbox_list widget for multiselect params) — was capped at 160 px
  • FolderListWidget / FileListWidget (the folder_list / file_list widgets for multiselect paths) — 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

28 May 13:23

Choose a tag to compare

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.