@blulotus/trellis 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configurators/workflow.d.ts +0 -15
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +14 -74
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/migrations/manifests/0.7.3.json +9 -0
- package/dist/migrations/manifests/0.7.4.json +9 -0
- package/dist/templates/markdown/spec/backend/index.md.txt +2 -2
- package/dist/templates/markdown/spec/frontend/index.md.txt +1 -1
- package/dist/templates/markdown/spec/frontend/visual-design.md.txt +21 -1
- package/dist/templates/shared-hooks/inject-spec-context.py +125 -0
- package/dist/templates/trellis/config.yaml +19 -16
- package/package.json +2 -2
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
import { type ProjectType, type DetectedPackage } from "../utils/project-detector.js";
|
|
2
|
-
/** How `spec/frontend/visual-design.md` is scaffolded. See config.yaml. */
|
|
3
|
-
export type VisualDesignMode = "auto" | "always";
|
|
4
|
-
/**
|
|
5
|
-
* Read `spec.visual_design` from `.trellis/config.yaml`.
|
|
6
|
-
*
|
|
7
|
-
* `createWorkflowStructure` writes config.yaml before it writes spec, and
|
|
8
|
-
* `writeFile` preserves an existing config, so on a re-init this reads the
|
|
9
|
-
* user's own file. A first init has no config yet and gets `auto`.
|
|
10
|
-
*
|
|
11
|
-
* Hand-parsed for the same reason `loadUpdateSkipPaths` in `commands/update.ts`
|
|
12
|
-
* is: config.yaml is read in several places without a YAML dependency.
|
|
13
|
-
*
|
|
14
|
-
* @internal Exported for testing only
|
|
15
|
-
*/
|
|
16
|
-
export declare function loadVisualDesignMode(cwd: string): VisualDesignMode;
|
|
17
2
|
/**
|
|
18
3
|
* Options for creating workflow structure
|
|
19
4
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/configurators/workflow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/configurators/workflow.ts"],"names":[],"mappings":"AAwCA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AAOtC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,yFAAyF;IACzF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;IAC7B,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,IAAI,CAAC,CA+Df"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { DIR_NAMES, PATHS } from "../constants/paths.js";
|
|
4
3
|
import { copyTrellisDir } from "../templates/extract.js";
|
|
@@ -15,59 +14,6 @@ guidesIndexContent, guidesCrossLayerThinkingGuideContent, guidesCodeReuseThinkin
|
|
|
15
14
|
import { writeFile, ensureDir } from "../utils/file-writer.js";
|
|
16
15
|
import { replacePythonCommandLiterals } from "./shared.js";
|
|
17
16
|
import { sanitizePkgName, } from "../utils/project-detector.js";
|
|
18
|
-
/**
|
|
19
|
-
* Read `spec.visual_design` from `.trellis/config.yaml`.
|
|
20
|
-
*
|
|
21
|
-
* `createWorkflowStructure` writes config.yaml before it writes spec, and
|
|
22
|
-
* `writeFile` preserves an existing config, so on a re-init this reads the
|
|
23
|
-
* user's own file. A first init has no config yet and gets `auto`.
|
|
24
|
-
*
|
|
25
|
-
* Hand-parsed for the same reason `loadUpdateSkipPaths` in `commands/update.ts`
|
|
26
|
-
* is: config.yaml is read in several places without a YAML dependency.
|
|
27
|
-
*
|
|
28
|
-
* @internal Exported for testing only
|
|
29
|
-
*/
|
|
30
|
-
export function loadVisualDesignMode(cwd) {
|
|
31
|
-
const configPath = path.join(cwd, DIR_NAMES.WORKFLOW, "config.yaml");
|
|
32
|
-
if (!fs.existsSync(configPath))
|
|
33
|
-
return "auto";
|
|
34
|
-
let raw = null;
|
|
35
|
-
try {
|
|
36
|
-
const lines = fs.readFileSync(configPath, "utf-8").split("\n");
|
|
37
|
-
let inSpec = false;
|
|
38
|
-
for (const line of lines) {
|
|
39
|
-
if (/^spec:\s*(#.*)?$/.test(line)) {
|
|
40
|
-
inSpec = true;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
// A non-indented, non-comment, non-blank line closes the block.
|
|
44
|
-
if (inSpec && /^\S/.test(line) && !line.startsWith("#")) {
|
|
45
|
-
inSpec = false;
|
|
46
|
-
}
|
|
47
|
-
if (!inSpec)
|
|
48
|
-
continue;
|
|
49
|
-
const match = /^\s+visual_design:\s*([^#]+)/.exec(line);
|
|
50
|
-
if (match) {
|
|
51
|
-
raw = match[1]
|
|
52
|
-
.trim()
|
|
53
|
-
.replace(/^['"]|['"]$/g, "")
|
|
54
|
-
.toLowerCase();
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
console.warn(`Warning: failed to read ${configPath}, using spec.visual_design: auto`);
|
|
61
|
-
return "auto";
|
|
62
|
-
}
|
|
63
|
-
if (raw === null || raw === "auto")
|
|
64
|
-
return "auto";
|
|
65
|
-
if (raw === "always")
|
|
66
|
-
return "always";
|
|
67
|
-
console.warn(`Warning: unknown spec.visual_design value "${raw}" in ${configPath}; ` +
|
|
68
|
-
`expected "auto" or "always". Using "auto".`);
|
|
69
|
-
return "auto";
|
|
70
|
-
}
|
|
71
17
|
/**
|
|
72
18
|
* Create workflow structure based on project type
|
|
73
19
|
*
|
|
@@ -87,10 +33,6 @@ export async function createWorkflowStructure(cwd, options) {
|
|
|
87
33
|
const packages = options?.packages;
|
|
88
34
|
const remoteSpecPackages = options?.remoteSpecPackages;
|
|
89
35
|
const workflowMd = options?.workflowMdOverride ?? workflowMdTemplate;
|
|
90
|
-
// Read before this function writes config.yaml. `--force` overwrites the
|
|
91
|
-
// user's config with the template, so reading it later would silently reset
|
|
92
|
-
// the choice to `auto` on exactly the run that rewrites the files.
|
|
93
|
-
const visualDesignMode = loadVisualDesignMode(cwd);
|
|
94
36
|
// Create base .trellis directory
|
|
95
37
|
ensureDir(path.join(cwd, DIR_NAMES.WORKFLOW));
|
|
96
38
|
// Copy scripts/ directory from templates
|
|
@@ -122,11 +64,11 @@ export async function createWorkflowStructure(cwd, options) {
|
|
|
122
64
|
// These are NOT dogfooded - they are generic templates for new projects
|
|
123
65
|
if (packages && packages.length > 0) {
|
|
124
66
|
// Monorepo mode: create per-package spec directories
|
|
125
|
-
await createSpecTemplates(cwd, projectType,
|
|
67
|
+
await createSpecTemplates(cwd, projectType, packages, remoteSpecPackages);
|
|
126
68
|
}
|
|
127
69
|
else if (!skipSpecTemplates) {
|
|
128
70
|
// Single-repo mode: create global spec (skip if using remote template)
|
|
129
|
-
await createSpecTemplates(cwd, projectType
|
|
71
|
+
await createSpecTemplates(cwd, projectType);
|
|
130
72
|
}
|
|
131
73
|
}
|
|
132
74
|
/**
|
|
@@ -185,7 +127,7 @@ async function writeFrontendDocs(specBase) {
|
|
|
185
127
|
/**
|
|
186
128
|
* Write spec docs for a given project type into a target spec directory.
|
|
187
129
|
*/
|
|
188
|
-
async function writeSpecForType(specBase, projectType
|
|
130
|
+
async function writeSpecForType(specBase, projectType) {
|
|
189
131
|
if (projectType !== "frontend") {
|
|
190
132
|
await writeBackendDocs(specBase);
|
|
191
133
|
}
|
|
@@ -193,18 +135,16 @@ async function writeSpecForType(specBase, projectType, visualDesignMode) {
|
|
|
193
135
|
await writeFrontendDocs(specBase);
|
|
194
136
|
return;
|
|
195
137
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
await writeFile(path.join(frontendDir, "visual-design.md"), frontendVisualDesignContent);
|
|
205
|
-
}
|
|
138
|
+
// Backend-only layer still gets visual-design.md, and only that file: a
|
|
139
|
+
// backend task can span into UI work, and whether the agent is actually
|
|
140
|
+
// handed the rules is decided at runtime by `spec.visual_design` (read by
|
|
141
|
+
// inject-spec-context.py), not by what init chose to write. Every other
|
|
142
|
+
// `frontend/` doc is an unfilled stub a backend layer has no use for.
|
|
143
|
+
const frontendDir = path.join(specBase, "frontend");
|
|
144
|
+
ensureDir(frontendDir);
|
|
145
|
+
await writeFile(path.join(frontendDir, "visual-design.md"), frontendVisualDesignContent);
|
|
206
146
|
}
|
|
207
|
-
async function createSpecTemplates(cwd, projectType,
|
|
147
|
+
async function createSpecTemplates(cwd, projectType, packages, remoteSpecPackages) {
|
|
208
148
|
// Ensure spec directory exists
|
|
209
149
|
ensureDir(path.join(cwd, PATHS.SPEC));
|
|
210
150
|
// Guides - always created regardless of mode
|
|
@@ -233,12 +173,12 @@ async function createSpecTemplates(cwd, projectType, visualDesignMode, packages,
|
|
|
233
173
|
const pkgSpecBase = path.join(cwd, `${PATHS.SPEC}/${dirName}`);
|
|
234
174
|
ensureDir(pkgSpecBase);
|
|
235
175
|
const pkgType = pkg.type === "unknown" ? "fullstack" : pkg.type;
|
|
236
|
-
await writeSpecForType(pkgSpecBase, pkgType
|
|
176
|
+
await writeSpecForType(pkgSpecBase, pkgType);
|
|
237
177
|
}
|
|
238
178
|
}
|
|
239
179
|
else {
|
|
240
180
|
// Single-repo mode
|
|
241
|
-
await writeSpecForType(path.join(cwd, PATHS.SPEC), projectType
|
|
181
|
+
await writeSpecForType(path.join(cwd, PATHS.SPEC), projectType);
|
|
242
182
|
}
|
|
243
183
|
}
|
|
244
184
|
//# sourceMappingURL=workflow.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/configurators/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/configurators/workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.7.3",
|
|
3
|
+
"description": "Patch: spec index gates that skills already pointed at, a config switch for visual-design.md, and de-duplicated render-verification rules.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Bug Fixes:**\n- fix(spec): `trellis-before-dev` and `trellis-check` have long instructed the agent to \"follow the Pre-Development Checklist / Quality Check section\" of `.trellis/spec/<package>/<layer>/index.md`, but the shipped backend and frontend index templates never contained those headings — the instruction dead-ended and the gates were silently skipped. Both sections now ship, with a platform-neutral floor to extend.\n\n**Features:**\n- feat(spec): new `spec.visual_design` key in `.trellis/config.yaml` (`auto` | `always`). `auto` keeps the existing behavior — `spec/frontend/visual-design.md` lands only where a frontend layer was detected. `always` also writes it for a backend-only layer, for projects where backend work regularly reaches a rendered surface. `spec/backend/index.md` gained an existence-conditional pointer so the file is reachable from a backend layer.\n- feat(agents): the Codex `trellis-research` agent gained the source-quality discipline the Claude Code agent already had — follow every claim to the source that owns it, prefer sources openable on this machine, record the version read against.\n\n**Internal:**\n- refactor(spec): `spec/frontend/index.md`'s Quality Check restated `visual-design.md`'s render-verification rules almost verbatim. It now points at them instead, with a test asserting the duplicate cannot grow back. `visual-design.md` gained a scope-and-shelf-life note, a corrected claim about `padding` / `margin` transform equivalents, and a check date on its browser baselines.\n- refactor(skills): the `check` skill's authorization paragraph no longer restates Step 6. It keeps the commit/push boundary Step 6 does not cover, and states what a dispatched `trellis-check` sub-agent should do when there is no user turn to ask in: report upward rather than stop or widen the change.\n- test: coverage for the backend index's new sections; `platforms.integration.test.ts` given headroom over the global 10s timeout, since `spawnSync` on a cold Node start blocks a vitest worker long enough to trip it under full-suite contention.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"notes": "No files are renamed or deleted — `--migrate` is not needed.\n\n`.trellis/spec/` is a protected path that `trellis update` never writes, so the new index sections and `visual-design.md` reach `trellis init` only. An existing project keeps its current spec tree; re-run `trellis init` in it to pick up files that are missing, or copy the two new index sections in by hand."
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.7.4",
|
|
3
|
+
"description": "Patch: spec.visual_design becomes a runtime injection switch, and visual-design.md installs for every project type.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Behavior change:**\n- feat(spec): `spec.visual_design` in `.trellis/config.yaml` is now a **runtime** switch instead of a scaffolding one. 0.7.3 read it during `trellis init` and it only decided whether `visual-design.md` was written to disk — setting it back had no effect, and it could not be turned on for one task. It is now read by `inject-spec-context.py` on every injection event, so an edit takes effect on the next tool call with no `trellis init` and no session restart. Values: `auto` (default) follows the `paths:` globs in `visual-design.md`'s own frontmatter, so a frontend project is covered with no configuration and a backend project is left alone; `true` injects on every touched file, for a task that spans backend and UI; `false` never injects.\n- feat(spec): `spec/frontend/visual-design.md` is installed for **every** project type, backend-only included — a backend task can span into UI work. A backend-only layer gets that one file and none of the other `frontend/` to-fill stubs.\n- feat(spec): `visual-design.md` is the first shipped spec file to carry `paths:` frontmatter, making it a live example of the path-scoped injection contract. Add your own globs if the project keeps UI somewhere they miss (a Go template dir, a Rails view path, a design-token package).\n\n**Bug Fixes:**\n- fix(release): the release preflight's `npm view` timeout was 15s, but the first `npm view` of a session pays DNS plus npm startup and measures around 16s — so the first release attempt of a session failed with an `ETIMEDOUT` that read like broken release tooling. Raised to 60s.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"notes": "No files are renamed or deleted — `--migrate` is not needed.\n\nIf you set `spec.visual_design: always` under 0.7.3, remove it: `always` is no longer a recognized value and now warns to stderr and falls back to `auto`. Its 0.7.3 purpose — getting the file into a backend-only project — is the default from this release on, so no replacement setting is needed. Use `true` only when you want the rules injected regardless of which file the agent touches.\n\n`.trellis/spec/` remains a protected path that `trellis update` never writes, so an existing project does not receive `visual-design.md` from an update. Re-run `trellis init` to have it appear."
|
|
9
|
+
}
|
|
@@ -26,7 +26,7 @@ This directory contains guidelines for backend development. Fill in each file wi
|
|
|
26
26
|
|
|
27
27
|
Read before writing backend code. `trellis-before-dev` follows this section.
|
|
28
28
|
|
|
29
|
-
1. **Read the conventions that apply** — the files above covering the layers this change touches, plus `.trellis/spec/guides/` for the cross-cutting thinking guides. When the change reaches a rendered surface
|
|
29
|
+
1. **Read the conventions that apply** — the files above covering the layers this change touches, plus `.trellis/spec/guides/` for the cross-cutting thinking guides. When the change reaches a rendered surface, read `../frontend/visual-design.md` too — it ships in every project, backend-only included.
|
|
30
30
|
2. **State the change boundary.** The smallest behavior gap between what happens now and what should happen; where that behavior actually lives (not where it is easiest to intercept); which files you expect to change and why each is necessary; what you are explicitly not doing.
|
|
31
31
|
3. **Name the contract you are about to touch.** Any change to a public function signature, HTTP route, event payload, database schema, or config key has callers. Find them before editing, not after.
|
|
32
32
|
4. **Decide where the test will sit** before writing code, on a boundary that survives a refactor of the implementation behind it.
|
|
@@ -45,7 +45,7 @@ Run before reporting a backend change complete. `trellis-check` follows this sec
|
|
|
45
45
|
- [ ] Errors are propagated or handled deliberately, never swallowed. No debug logging left behind.
|
|
46
46
|
- [ ] No credential, token, or personal data in logs, error messages, or committed fixtures.
|
|
47
47
|
- [ ] Migrations run forward on a copy of real-shaped data, and the rollback path is known.
|
|
48
|
-
- [ ] A change that reached a rendered surface was verified against `../frontend/visual-design.md` → **Verification**, which owns the render rules and the no-renderer fallback.
|
|
48
|
+
- [ ] A change that reached a rendered surface was verified against `../frontend/visual-design.md` → **Verification**, which owns the render rules and the no-renderer fallback. For a task that spans backend and UI, `spec.visual_design: true` in `.trellis/config.yaml` makes those rules arrive on every file you touch.
|
|
49
49
|
|
|
50
50
|
Replace and extend these with your project's actual gates — the real verification commands, the real risky areas.
|
|
51
51
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
This directory contains guidelines for frontend development. Fill in each file with your project's specific conventions.
|
|
10
10
|
|
|
11
|
-
One file is the exception: `visual-design.md` ships filled in, because visual craft rules are general knowledge no codebase can state. Read it as-is; override a rule where your project has decided against it.
|
|
11
|
+
One file is the exception: `visual-design.md` ships filled in, because visual craft rules are general knowledge no codebase can state. Read it as-is; override a rule where your project has decided against it. It is also the one spec file that carries `paths:` frontmatter, so it arrives on its own when you touch a UI file — `spec.visual_design` in `.trellis/config.yaml` is the switch.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -1,10 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: visual-design
|
|
3
|
+
description: Typography, color, motion, surfaces, banned patterns, render verification
|
|
4
|
+
paths:
|
|
5
|
+
- "**/*.tsx"
|
|
6
|
+
- "**/*.jsx"
|
|
7
|
+
- "**/*.vue"
|
|
8
|
+
- "**/*.svelte"
|
|
9
|
+
- "**/*.astro"
|
|
10
|
+
- "**/*.css"
|
|
11
|
+
- "**/*.scss"
|
|
12
|
+
- "**/*.sass"
|
|
13
|
+
- "**/*.less"
|
|
14
|
+
- "**/*.styl"
|
|
15
|
+
- "**/*.html"
|
|
16
|
+
- "**/tailwind.config.*"
|
|
17
|
+
---
|
|
18
|
+
|
|
1
19
|
# Visual Design Rules
|
|
2
20
|
|
|
3
21
|
> **This file ships filled in.** Unlike its siblings in this directory, it is not a "document your project's conventions" stub. These rules are general craft knowledge that a codebase cannot tell you — no config file states which typefaces read as unconsidered, and no linter catches a purple-to-blue gradient. Delete or override any rule your project has deliberately decided against, and record the reason where you override it.
|
|
4
22
|
>
|
|
5
23
|
> Project-specific token values (your palette, your type scale, your radius scale) belong in a `DESIGN.md`-style section you add at the bottom, or in `component-guidelines.md`. This file holds the rules that hold regardless of project.
|
|
6
24
|
>
|
|
7
|
-
> **Scope and shelf life.** Written for web UI: the mechanisms named are CSS, and a React Native / Flutter / desktop-toolkit surface should read the intent and ignore the syntax. Two kinds of content here decay — the browser baselines under Motion, and the typeface list under Typography — so treat both as "true when written" and re-check before leaning on them. Rules as of Trellis 0.7.
|
|
25
|
+
> **Scope and shelf life.** Written for web UI: the mechanisms named are CSS, and a React Native / Flutter / desktop-toolkit surface should read the intent and ignore the syntax. Two kinds of content here decay — the browser baselines under Motion, and the typeface list under Typography — so treat both as "true when written" and re-check before leaning on them. Rules as of Trellis 0.7.4 (2026-08).
|
|
26
|
+
>
|
|
27
|
+
> **When you get this file.** The `paths:` globs above are what `spec.visual_design: auto` follows — touch one of those files and these rules arrive on that tool call. Add your own globs if this project keeps UI somewhere they miss (a Go template dir, a Rails view path, a design-token package). `spec.visual_design: true` in `.trellis/config.yaml` ignores the globs and injects on every file, which is the switch for a task that spans backend and UI; `false` turns it off entirely.
|
|
8
28
|
|
|
9
29
|
---
|
|
10
30
|
|
|
@@ -332,6 +332,123 @@ def get_spec_injection_settings(
|
|
|
332
332
|
)
|
|
333
333
|
|
|
334
334
|
|
|
335
|
+
VISUAL_DESIGN_BASENAME = "visual-design.md"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def get_visual_design_mode(root: Path) -> str:
|
|
339
|
+
"""Return the `spec.visual_design` mode: "auto" | "on" | "off".
|
|
340
|
+
|
|
341
|
+
Read here rather than at `trellis init` time, so flipping it takes effect
|
|
342
|
+
on the next tool call — the point of the key is to be turned on for one
|
|
343
|
+
cross-stack task and back off afterwards, and a scaffolding-time read could
|
|
344
|
+
not express that.
|
|
345
|
+
|
|
346
|
+
spec:
|
|
347
|
+
visual_design: auto # follow the file's own `paths:` globs
|
|
348
|
+
# visual_design: true # inject on every touched file
|
|
349
|
+
# visual_design: false # never inject
|
|
350
|
+
|
|
351
|
+
Accepts the same truthy/falsy spellings as `session_auto_commit`. An
|
|
352
|
+
unrecognized value warns and falls back to "auto".
|
|
353
|
+
"""
|
|
354
|
+
config = _read_trellis_config(root)
|
|
355
|
+
section = config.get("spec") if isinstance(config, dict) else None
|
|
356
|
+
if not isinstance(section, dict) or "visual_design" not in section:
|
|
357
|
+
return "auto"
|
|
358
|
+
raw = section["visual_design"]
|
|
359
|
+
if isinstance(raw, bool):
|
|
360
|
+
return "on" if raw else "off"
|
|
361
|
+
text = str(raw).strip().lower()
|
|
362
|
+
if text == "auto":
|
|
363
|
+
return "auto"
|
|
364
|
+
if text in ("true", "yes", "1", "on"):
|
|
365
|
+
return "on"
|
|
366
|
+
if text in ("false", "no", "0", "off"):
|
|
367
|
+
return "off"
|
|
368
|
+
_warn(
|
|
369
|
+
f"invalid spec.visual_design value: {raw!r}; "
|
|
370
|
+
f'expected auto / true / false. Using "auto"'
|
|
371
|
+
)
|
|
372
|
+
return "auto"
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def apply_visual_design_mode(
|
|
376
|
+
root: Path,
|
|
377
|
+
mode: str,
|
|
378
|
+
matches: list,
|
|
379
|
+
match_files: dict[str, str],
|
|
380
|
+
trigger_file: str,
|
|
381
|
+
) -> list:
|
|
382
|
+
"""Apply `spec.visual_design` to a path-matched spec list.
|
|
383
|
+
|
|
384
|
+
"auto" is a no-op: the file's own `paths:` globs already decided, which is
|
|
385
|
+
why a frontend project needs no configuration and a backend project is left
|
|
386
|
+
alone. "off" drops it however it matched. "on" force-appends every
|
|
387
|
+
`visual-design.md` under `.trellis/spec/` that the globs did not already
|
|
388
|
+
pull in, so a task editing only `.go` files still gets the rules.
|
|
389
|
+
|
|
390
|
+
Appended, never prepended: `assemble_payload` spends its budget in list
|
|
391
|
+
order, so a forced entry must not outrank a spec the touched path actually
|
|
392
|
+
matched. In a monorepo with one copy per package the budget degrades the
|
|
393
|
+
extra copies to index lines, which is the intended cheap outcome.
|
|
394
|
+
"""
|
|
395
|
+
if mode == "auto":
|
|
396
|
+
return matches
|
|
397
|
+
if mode == "off":
|
|
398
|
+
return [
|
|
399
|
+
match
|
|
400
|
+
for match in matches
|
|
401
|
+
if not match.rel_path.endswith("/" + VISUAL_DESIGN_BASENAME)
|
|
402
|
+
]
|
|
403
|
+
|
|
404
|
+
try:
|
|
405
|
+
from common.spec_match import ( # type: ignore[import-not-found]
|
|
406
|
+
SpecMatch,
|
|
407
|
+
parse_spec_frontmatter,
|
|
408
|
+
)
|
|
409
|
+
from common.paths import ( # type: ignore[import-not-found]
|
|
410
|
+
DIR_SPEC,
|
|
411
|
+
DIR_WORKFLOW,
|
|
412
|
+
)
|
|
413
|
+
except Exception:
|
|
414
|
+
return matches # matcher unavailable — degrade to path-matched only
|
|
415
|
+
|
|
416
|
+
spec_dir = root / DIR_WORKFLOW / DIR_SPEC
|
|
417
|
+
if not spec_dir.is_dir():
|
|
418
|
+
return matches
|
|
419
|
+
try:
|
|
420
|
+
found = sorted(spec_dir.rglob(VISUAL_DESIGN_BASENAME))
|
|
421
|
+
except OSError as exc:
|
|
422
|
+
_warn(f"cannot scan {spec_dir} for {VISUAL_DESIGN_BASENAME}: {exc}")
|
|
423
|
+
return matches
|
|
424
|
+
|
|
425
|
+
for spec_file in found:
|
|
426
|
+
try:
|
|
427
|
+
rel_path = spec_file.relative_to(root).as_posix()
|
|
428
|
+
except ValueError:
|
|
429
|
+
continue
|
|
430
|
+
if rel_path in match_files:
|
|
431
|
+
continue
|
|
432
|
+
description = None
|
|
433
|
+
try:
|
|
434
|
+
frontmatter = parse_spec_frontmatter(
|
|
435
|
+
spec_file.read_text(encoding="utf-8", errors="replace")
|
|
436
|
+
)
|
|
437
|
+
if frontmatter is not None:
|
|
438
|
+
description = frontmatter.description
|
|
439
|
+
except (OSError, ValueError):
|
|
440
|
+
pass # description is cosmetic; a forced inject still carries the body
|
|
441
|
+
matches.append(
|
|
442
|
+
SpecMatch(
|
|
443
|
+
spec_path=spec_file,
|
|
444
|
+
rel_path=rel_path,
|
|
445
|
+
description=description,
|
|
446
|
+
)
|
|
447
|
+
)
|
|
448
|
+
match_files[rel_path] = trigger_file
|
|
449
|
+
return matches
|
|
450
|
+
|
|
451
|
+
|
|
335
452
|
# =============================================================================
|
|
336
453
|
# Identity ladder
|
|
337
454
|
# =============================================================================
|
|
@@ -730,6 +847,14 @@ def main() -> int:
|
|
|
730
847
|
continue
|
|
731
848
|
matches.append(match)
|
|
732
849
|
match_files[match.rel_path] = file_path
|
|
850
|
+
|
|
851
|
+
matches = apply_visual_design_mode(
|
|
852
|
+
root,
|
|
853
|
+
get_visual_design_mode(root),
|
|
854
|
+
matches,
|
|
855
|
+
match_files,
|
|
856
|
+
file_paths[0],
|
|
857
|
+
)
|
|
733
858
|
if not matches:
|
|
734
859
|
return 0
|
|
735
860
|
|
|
@@ -77,25 +77,28 @@ session_auto_commit: false
|
|
|
77
77
|
# default_package: frontend
|
|
78
78
|
|
|
79
79
|
#-------------------------------------------------------------------------------
|
|
80
|
-
#
|
|
80
|
+
# Visual design rules
|
|
81
81
|
#-------------------------------------------------------------------------------
|
|
82
82
|
# `spec/frontend/visual-design.md` is the one spec file Trellis ships filled in
|
|
83
83
|
# rather than as a "document your conventions" stub, because visual craft rules
|
|
84
|
-
# are general knowledge no codebase can state.
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
84
|
+
# are general knowledge no codebase can state. It is installed for every project
|
|
85
|
+
# type, backend-only included. This key decides whether the agent is actually
|
|
86
|
+
# handed it, and is read at injection time — an edit takes effect on the next
|
|
87
|
+
# tool call, with no `trellis init` and no session restart.
|
|
88
|
+
#
|
|
89
|
+
# auto (default) Follow the `paths:` globs in visual-design.md's own
|
|
90
|
+
# frontmatter: the rules arrive when the agent touches a UI
|
|
91
|
+
# file (.tsx/.vue/.svelte/.css/...) and stay out of the way
|
|
92
|
+
# otherwise. A frontend project is covered without doing
|
|
93
|
+
# anything; a backend project essentially never sees it.
|
|
94
|
+
# true Inject on every touched file, ignoring those globs. Set this
|
|
95
|
+
# for a task that spans backend and UI, where the visible
|
|
96
|
+
# change is driven from files the globs do not cover.
|
|
97
|
+
# false Never inject, whatever the touched file is.
|
|
98
|
+
#
|
|
99
|
+
# Widening which paths count as UI is an edit to that file's `paths:` list, not
|
|
100
|
+
# a setting here. `spec_injection.enabled: false` further down turns off all
|
|
101
|
+
# path-scoped spec injection, this key included.
|
|
99
102
|
#
|
|
100
103
|
# spec:
|
|
101
104
|
# visual_design: auto
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blulotus/trellis",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "AI capabilities grow like ivy — Trellis provides the structure to guide them along a disciplined path",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"inquirer": "^9.3.7",
|
|
35
35
|
"undici": "^6.21.0",
|
|
36
36
|
"zod": "^4.4.2",
|
|
37
|
-
"@blulotus/trellis-core": "0.7.
|
|
37
|
+
"@blulotus/trellis-core": "0.7.4"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@eslint/js": "^9.18.0",
|