Skip to content

Releases: KenM76/scriptree

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.

v0.8.0a13 — Run buttons no longer pushed off the form dock

28 May 13:12

Choose a tag to compare

Bug fix

v0.8.0a12 left the configuration line and Run / Stop button row sharing visual space with the parameters scroll area. When the form dock wasn't tall enough to show everything, the bottom rows got pushed below the dock's bottom edge instead of the params scroll area shrinking to fit.

Root cause: form_scroll was added with stretch=1 but its minimumHeight defaulted to its content's sizeHint. Qt's QVBoxLayout reserved that much space for it even when the dock was constrained, squeezing the bottom rows first.

Fix: form_scroll.setMinimumHeight(0) + QSizePolicy.Expanding. The scroll area can now compress all the way to a tiny strip; the bottom band (cfg / extras / cmd / Run row / status) keeps its natural height and stays visible.

Tests

New regression test test_run_button_visible_when_dock_is_tight forces a 16-param tool into a 600×280 runner and asserts the Run button stays mapped within the runner's own height. Suite: 1728 passing, 5 skipped.

Download

ScripTree-v0.8.0a13.zip — portable Windows build with combridge bundled.

v0.8.0a12 — arrow-collapse sections + flatter form layout

28 May 12:55

Choose a tag to compare

What's new

Arrow-collapse sections

The three checkboxes in the tool-runner form panel (description, Extra arguments, Command line) read as "enable / disable the feature" because Qt's QGroupBox(setCheckable=True) puts a literal checkbox in the title bar — but they were actually just show/hide affordances. Replaced with a new ArrowSection widget that uses a ▶ / ▼ arrow so the chrome matches the semantics.

  • Click the arrow (or press Space / Enter when focused) to collapse / expand.
  • The header bar stays visible at all times so the toggle is always findable when the section is collapsed.
  • API mirrors QGroupBox's isChecked / setChecked / toggled / setTitle so external callers keep compiling.

Flatter form layout — controls always visible

Dropped the QSplitter that used to separate the form from the extras + command-line panes. The drag handle was rarely used and let the bottom controls disappear off-screen when accidentally pulled too high. New layout:

[Header arrow + description]    ← collapsible
─────────────────────────────
[Parameters scroll area]         ← takes available space
─────────────────────────────
[Configurations bar]             ← always visible
[Extras arrow + edit]            ← collapsible
[Command line arrow + edit]      ← collapsible
[Run / Stop / etc row]
[Action buttons row]
[Status line]

The parameters scroll area is the only scrollable region; everything from the configurations bar downward stays at its natural height so the run controls never get pushed out of view.

Extras hidden by default in standalone mode

ToolRunnerView.set_standalone_mode(True) now also collapses the Extra arguments section. In standalone (direct-launch) mode the form is the canonical input surface and extras are almost never used; when run inside the main editor, extras still starts expanded so power users can see and edit at a glance.

Tests

The existing test_tool_runner_collapsibles.py was retargeted to the new ArrowSection-based contract. Added new tests for standalone-mode auto-collapse and for layout order. Full suite: 1727 passing, 5 skipped. No net change in count — the rework was a clean swap, not an addition.

Download

ScripTree-v0.8.0a12.zip — portable Windows build with combridge bundled.