Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## [1.72.1.0] - 2026-08-29

**Slim runtime installs now carry every asset their commands load.**

Setup now keeps `bin/`, `lib/`, and `scripts/` together in Agents sidecars and
the Codex, Factory, OpenCode, Cursor, and Kiro runtime roots. Commands such as
`gstack-question-preference`, `gstack-developer-profile`, and
`gstack-brain-cache` no longer fail because classifiers, registries, profile
data, or cache schema files were left behind.

Regression coverage exercises real commands through Unix symlink installs and
Windows copy installs, including Cursor's repository sidecar and negative
controls for both missing `lib/` and missing `scripts/` layouts. The declarative
host defaults and host-authoring documentation now describe the same runtime
asset contract.

## [1.72.0.0] - 2026-08-28

**"Go register an API key" now drives your real browser.**
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.72.0.0
1.72.1.0
5 changes: 3 additions & 2 deletions bin/gstack-update-check
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ fi
# ─── Migration: fix stale Codex descriptions (one-time) ───────
# Existing installs may have .agents/skills/gstack/SKILL.md with oversized
# descriptions (>1024 chars) that Codex rejects. We can't regenerate from
# the runtime root (no bun/scripts), so delete oversized files — the next
# ./setup or /gstack-upgrade will regenerate them properly.
# the slim runtime root because it omits the package manifest, dependencies,
# and template sources, so delete oversized files — the next ./setup or
# /gstack-upgrade will regenerate them properly.
# Marker file ensures this runs at most once per install.
if [ ! -f "$STATE_DIR/.codex-desc-healed" ]; then
for _AGENTS_SKILL in "$GSTACK_DIR"/.agents/skills/*/SKILL.md; do
Expand Down
22 changes: 14 additions & 8 deletions docs/ADDING_A_HOST.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ gstack uses a declarative host config system. Each supported AI coding agent
(Claude, Codex, Factory, Kiro, OpenCode, Slate, Cursor, OpenClaw, Hermes,
GBrain) is defined as a typed TypeScript config object built by the
`defineHost()` factory. Adding a new host means creating one file and
re-exporting it. Zero code changes to the generator, setup, or tooling.
re-exporting it for generation and shared tooling. `setup` still has explicit
per-host dispatch and runtime-root implementations, so a host that needs a
slim runtime root must add or update its corresponding setup branch as well.

## How it works

Expand All @@ -25,16 +27,20 @@ hosts/
```

Each config file calls `defineHost()` and exports the resulting `HostConfig`
object, which tells the generator:
object, which centralizes:
- Where to put generated skills (paths)
- How to transform frontmatter (allowlist/denylist fields)
- What Claude-specific references to rewrite (paths, tool names)
- What binary to detect for auto-install
- What CLI binaries the inspection/export tooling can detect
- What resolver sections to suppress
- What assets to symlink at install time
- What assets a slim runtime root is expected to contain

The generator, setup script, platform-detect, uninstall, health checks, worktree
copy, and tests all read from these configs. None of them have per-host code.
The generator, template resolvers, skill checks, worktree copy, config
inspection/export CLI, and tests read from these configs. The setup and
uninstall scripts do not yet consume every corresponding field; in particular,
host configs and setup's explicit runtime-root implementations are maintained
separately. Keep both layers in sync until setup becomes fully table-driven
(tracked in `TODOS.md`).

## Step-by-step: add a new host

Expand Down Expand Up @@ -69,8 +75,8 @@ That expands to the full `HostConfig` with these defaults:
(`~/.claude/skills/gstack` → `~/{globalRoot}`, `.claude/skills/gstack` →
`{localSkillRoot}`, `.claude/skills` → `{hostSubdir}/skills`)
- `suppressedResolvers`: the GBrain pair (`GBRAIN_CONTEXT_LOAD`, `GBRAIN_SAVE_RESULTS`)
- `runtimeRoot`: the shared asset list (`bin`, `browse/dist`, `browse/bin`,
`gstack-upgrade`, `ETHOS.md` + review checklist files)
- `runtimeRoot`: the shared asset list (`bin`, `lib`, `scripts`, `browse/dist`,
`browse/bin`, `gstack-upgrade`, `ETHOS.md` + review checklist files)
- `install`: `{ linkingStrategy: 'symlink-generated' }`
- `learningsMode: 'basic'`

Expand Down
5 changes: 3 additions & 2 deletions docs/PROJECT_STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ gstack/
│ ├── opencode.ts, slate.ts, cursor.ts, openclaw.ts # IDE hosts
│ ├── hermes.ts, gbrain.ts # Agent runtime hosts
│ └── index.ts # Registry: exports all, derives Host type
├── scripts/ # Build + DX tooling
├── scripts/ # Runtime data/modules + build and DX tooling
│ ├── one-way-doors.ts, archetypes.ts, brain-cache-spec.ts # Runtime dependencies
│ ├── gen-skill-docs.ts # Template → SKILL.md generator (config-driven)
│ ├── host-config.ts # HostConfig interface + validator
│ ├── host-config-export.ts # Shell bridge for setup script
│ ├── host-config-export.ts # Host config inspection/export CLI
│ ├── resolvers/ # Template resolver modules (preamble, design, review, gbrain, etc.)
│ ├── skill-check.ts # Health dashboard
│ ├── test-paid-shards.ts # Sharded paid-tier runner (one Bun process per shard)
Expand Down
5 changes: 3 additions & 2 deletions hosts/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ const codex = defineHost({

// generateMetadata emits agents/openai.yaml (the format is hardcoded in
// gen-skill-docs.ts). Codex also gets a repo-local sidecar at
// .agents/skills/gstack (symlinked runtime assets: bin, browse, review, qa,
// ETHOS.md) — that behavior lives in setup's create_agents_sidecar, not here.
// .agents/skills/gstack (symlinked runtime assets: bin, lib, scripts, browse,
// review, qa, ETHOS.md) — that behavior lives in setup's
// create_agents_sidecar, not here.
generation: {
generateMetadata: true,
skipSkills: ['codex'], // Codex skill is a Claude wrapper around codex exec
Expand Down
2 changes: 1 addition & 1 deletion hosts/define-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function defineHost<const N extends string>(overrides: HostOverrides<N>):
toolRewrites,
suppressedResolvers = [...GBRAIN_RESOLVERS],
runtimeRoot = {
globalSymlinks: ['bin', 'browse/dist', 'browse/bin', 'gstack-upgrade', 'ETHOS.md'],
globalSymlinks: ['bin', 'lib', 'scripts', 'browse/dist', 'browse/bin', 'gstack-upgrade', 'ETHOS.md'],
globalFiles: {
'review': ['checklist.md', 'TODOS-format.md'],
},
Expand Down
2 changes: 1 addition & 1 deletion hosts/opencode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const opencode = defineHost({
// OpenCode links a wider runtime asset set than the shared default
// (design binary, review specialists, qa templates/references, DX hall of fame).
runtimeRoot: {
globalSymlinks: ['bin', 'browse/dist', 'browse/bin', 'design/dist', 'gstack-upgrade', 'ETHOS.md', 'review/specialists', 'qa/templates', 'qa/references', 'plan-devex-review/dx-hall-of-fame.md'],
globalSymlinks: ['bin', 'lib', 'scripts', 'browse/dist', 'browse/bin', 'design/dist', 'gstack-upgrade', 'ETHOS.md', 'review/specialists', 'qa/templates', 'qa/references', 'plan-devex-review/dx-hall-of-fame.md'],
globalFiles: {
'review': ['checklist.md', 'design-checklist.md', 'greptile-triage.md', 'TODOS-format.md'],
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gstack",
"version": "1.72.0",
"version": "1.72.1",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
Expand Down
6 changes: 3 additions & 3 deletions scripts/host-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
* hosts/*.ts → hosts/index.ts → host-config.ts (this file)
* │ │
* └── typed configs ──────────────────→ consumed by gen-skill-docs.ts,
* setup (via host-config-export.ts),
* skill-check.ts, worktree.ts,
* platform-detect, uninstall
* template resolvers,
* host-config-export.ts,
* skill-check.ts, worktree.ts
*/

import type { Model } from './models';
Expand Down
9 changes: 9 additions & 0 deletions scripts/test-free-shards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,15 @@ export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }>
// pattern hit is a false positive — the point of these files is Windows
// coverage, so auto-excluding them defeats the regression tests they carry.
const KNOWN_WINDOWS_SAFE: Array<{ file: string; reason: string }> = [
{
file: 'test/setup-runtime-lib-command.test.ts',
// Trips the "spawns bin/ shebang script" pattern via installed fixture
// paths, but shell commands are invoked through spawnSync('bash', ...), and
// the TypeScript command through Bun. The Unix symlink cells self-skip on
// win32; the IS_WINDOWS=1 cells must run on windows-latest to cover copied
// lib/ and scripts/ runtime assets.
reason: 'installed bin/ fixture paths are launched explicitly with bash or Bun; Windows copy-install coverage must run on windows-latest',
},
{
file: 'test/setup-windows-rerun-refresh.test.ts',
// Trips the "spawns bin/ shebang script" pattern via path.join(..., 'bin',
Expand Down
26 changes: 23 additions & 3 deletions setup
Original file line number Diff line number Diff line change
Expand Up @@ -1180,8 +1180,9 @@ create_agents_sidecar() {
mkdir -p "$agents_gstack"

# Sidecar directories that skills reference at runtime. bin scripts import
# shared modules via ../lib, so bin and lib must always travel together.
for asset in bin lib browse review qa; do
# shared modules via ../lib and load runtime classifiers/profile data from
# ../scripts, so bin, lib, and scripts must always travel together.
for asset in bin lib scripts browse review qa; do
local src="$SOURCE_GSTACK_DIR/$asset"
local dst="$agents_gstack/$asset"
if [ -d "$src" ] || [ -f "$src" ]; then
Expand Down Expand Up @@ -1242,6 +1243,9 @@ create_codex_runtime_root() {
if [ -d "$gstack_dir/lib" ]; then
_link_or_copy "$gstack_dir/lib" "$codex_gstack/lib"
fi
if [ -d "$gstack_dir/scripts" ]; then
_link_or_copy "$gstack_dir/scripts" "$codex_gstack/scripts"
fi
if [ -d "$gstack_dir/browse/dist" ]; then
_link_or_copy "$gstack_dir/browse/dist" "$codex_gstack/browse/dist"
fi
Expand Down Expand Up @@ -1297,6 +1301,9 @@ create_factory_runtime_root() {
if [ -d "$gstack_dir/lib" ]; then
_link_or_copy "$gstack_dir/lib" "$factory_gstack/lib"
fi
if [ -d "$gstack_dir/scripts" ]; then
_link_or_copy "$gstack_dir/scripts" "$factory_gstack/scripts"
fi
if [ -d "$gstack_dir/browse/dist" ]; then
_link_or_copy "$gstack_dir/browse/dist" "$factory_gstack/browse/dist"
fi
Expand Down Expand Up @@ -1350,6 +1357,9 @@ create_opencode_runtime_root() {
if [ -d "$gstack_dir/lib" ]; then
_link_or_copy "$gstack_dir/lib" "$opencode_gstack/lib"
fi
if [ -d "$gstack_dir/scripts" ]; then
_link_or_copy "$gstack_dir/scripts" "$opencode_gstack/scripts"
fi
if [ -d "$gstack_dir/browse/dist" ]; then
_link_or_copy "$gstack_dir/browse/dist" "$opencode_gstack/browse/dist"
fi
Expand Down Expand Up @@ -1506,13 +1516,17 @@ create_cursor_runtime_root() {
if [ -f "$cursor_dir/gstack/SKILL.md" ]; then
_link_or_copy "$cursor_dir/gstack/SKILL.md" "$cursor_gstack/SKILL.md"
fi
# bin scripts import shared modules via ../lib — bin and lib travel together.
# bin scripts import shared modules via ../lib and load runtime data from
# ../scripts — bin, lib, and scripts travel together.
if [ -d "$gstack_dir/bin" ]; then
_link_or_copy "$gstack_dir/bin" "$cursor_gstack/bin"
fi
if [ -d "$gstack_dir/lib" ]; then
_link_or_copy "$gstack_dir/lib" "$cursor_gstack/lib"
fi
if [ -d "$gstack_dir/scripts" ]; then
_link_or_copy "$gstack_dir/scripts" "$cursor_gstack/scripts"
fi
if [ -d "$gstack_dir/browse/dist" ]; then
_link_or_copy "$gstack_dir/browse/dist" "$cursor_gstack/browse/dist"
fi
Expand Down Expand Up @@ -1566,6 +1580,11 @@ create_cursor_sidecar() {
_link_or_copy "$repo_root/lib" "$cursor_gstack/lib"
fi
fi
if [ -d "$repo_root/scripts" ]; then
if [ "$IS_WINDOWS" -eq 1 ] || [ -L "$cursor_gstack/scripts" ] || [ ! -e "$cursor_gstack/scripts" ]; then
_link_or_copy "$repo_root/scripts" "$cursor_gstack/scripts"
fi
fi
if [ -d "$repo_root/browse/dist" ]; then
if [ "$IS_WINDOWS" -eq 1 ] || [ -L "$cursor_gstack/browse/dist" ] || [ ! -e "$cursor_gstack/browse/dist" ]; then
_link_or_copy "$repo_root/browse/dist" "$cursor_gstack/browse/dist"
Expand Down Expand Up @@ -1803,6 +1822,7 @@ if [ "$INSTALL_KIRO" -eq 1 ]; then
mkdir -p "$KIRO_GSTACK" "$KIRO_GSTACK/browse" "$KIRO_GSTACK/gstack-upgrade" "$KIRO_GSTACK/review"
_link_or_copy "$SOURCE_GSTACK_DIR/bin" "$KIRO_GSTACK/bin"
_link_or_copy "$SOURCE_GSTACK_DIR/lib" "$KIRO_GSTACK/lib"
_link_or_copy "$SOURCE_GSTACK_DIR/scripts" "$KIRO_GSTACK/scripts"
_link_or_copy "$SOURCE_GSTACK_DIR/browse/dist" "$KIRO_GSTACK/browse/dist"
_link_or_copy "$SOURCE_GSTACK_DIR/browse/bin" "$KIRO_GSTACK/browse/bin"
# ETHOS.md — referenced by "Search Before Building" in all skill preambles
Expand Down
2 changes: 2 additions & 0 deletions test/host-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,8 @@ describe('host config correctness', () => {
for (const config of ALL_HOST_CONFIGS) {
expect(config.runtimeRoot.globalSymlinks.length).toBeGreaterThan(0);
expect(config.runtimeRoot.globalSymlinks).toContain('bin');
expect(config.runtimeRoot.globalSymlinks).toContain('lib');
expect(config.runtimeRoot.globalSymlinks).toContain('scripts');
expect(config.runtimeRoot.globalSymlinks).toContain('ETHOS.md');
}
});
Expand Down
Loading
Loading