@blxzer/cursor-trellis 0.3.3 → 0.3.5
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/CHANGELOG.md +6 -0
- package/README.md +0 -13
- package/README.zh-CN.md +2 -1
- package/bin/cstl.js +1 -1
- package/bin/smart-search.js +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +14 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/campaign/canvas-render.d.ts +68 -0
- package/dist/commands/campaign/canvas-render.d.ts.map +1 -0
- package/dist/commands/campaign/canvas-render.js +414 -0
- package/dist/commands/campaign/canvas-render.js.map +1 -0
- package/dist/commands/campaign/compose.d.ts +15 -0
- package/dist/commands/campaign/compose.d.ts.map +1 -0
- package/dist/commands/campaign/compose.js +110 -0
- package/dist/commands/campaign/compose.js.map +1 -0
- package/dist/commands/campaign/index.d.ts +13 -0
- package/dist/commands/campaign/index.d.ts.map +1 -0
- package/dist/commands/campaign/index.js +134 -0
- package/dist/commands/campaign/index.js.map +1 -0
- package/dist/commands/campaign/kind-map.d.ts +6 -0
- package/dist/commands/campaign/kind-map.d.ts.map +1 -0
- package/dist/commands/campaign/kind-map.js +21 -0
- package/dist/commands/campaign/kind-map.js.map +1 -0
- package/dist/commands/campaign/mcp-server.d.ts +6 -0
- package/dist/commands/campaign/mcp-server.d.ts.map +1 -0
- package/dist/commands/campaign/mcp-server.js +167 -0
- package/dist/commands/campaign/mcp-server.js.map +1 -0
- package/dist/commands/campaign/render.d.ts +5 -0
- package/dist/commands/campaign/render.d.ts.map +1 -0
- package/dist/commands/campaign/render.js +103 -0
- package/dist/commands/campaign/render.js.map +1 -0
- package/dist/commands/campaign/trellis-load.d.ts +13 -0
- package/dist/commands/campaign/trellis-load.d.ts.map +1 -0
- package/dist/commands/campaign/trellis-load.js +122 -0
- package/dist/commands/campaign/trellis-load.js.map +1 -0
- package/dist/commands/campaign/types.d.ts +61 -0
- package/dist/commands/campaign/types.d.ts.map +1 -0
- package/dist/commands/campaign/types.js +3 -0
- package/dist/commands/campaign/types.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +11 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/rpc/client.d.ts +51 -0
- package/dist/commands/rpc/client.d.ts.map +1 -0
- package/dist/commands/rpc/client.js +147 -0
- package/dist/commands/rpc/client.js.map +1 -0
- package/dist/commands/rpc/index.d.ts +10 -0
- package/dist/commands/rpc/index.d.ts.map +1 -0
- package/dist/commands/rpc/index.js +157 -0
- package/dist/commands/rpc/index.js.map +1 -0
- package/dist/commands/rpc/serve.d.ts +23 -0
- package/dist/commands/rpc/serve.d.ts.map +1 -0
- package/dist/commands/rpc/serve.js +60 -0
- package/dist/commands/rpc/serve.js.map +1 -0
- package/dist/commands/rpc/smoke.d.ts +16 -0
- package/dist/commands/rpc/smoke.d.ts.map +1 -0
- package/dist/commands/rpc/smoke.js +92 -0
- package/dist/commands/rpc/smoke.js.map +1 -0
- package/dist/commands/sdk/index.d.ts +9 -0
- package/dist/commands/sdk/index.d.ts.map +1 -0
- package/dist/commands/sdk/index.js +58 -0
- package/dist/commands/sdk/index.js.map +1 -0
- package/dist/commands/sdk/run.d.ts +42 -0
- package/dist/commands/sdk/run.d.ts.map +1 -0
- package/dist/commands/sdk/run.js +245 -0
- package/dist/commands/sdk/run.js.map +1 -0
- package/dist/commands/sdk/status.d.ts +14 -0
- package/dist/commands/sdk/status.d.ts.map +1 -0
- package/dist/commands/sdk/status.js +69 -0
- package/dist/commands/sdk/status.js.map +1 -0
- package/dist/migrations/manifests/0.3.2.json +16 -0
- package/dist/migrations/manifests/0.3.3.json +9 -0
- package/dist/migrations/manifests/0.3.4.json +9 -0
- package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/hooks-and-settings.md +2 -1
- package/dist/templates/common/bundled-skills/smart-search-cli/SKILL.md +35 -29
- package/dist/templates/common/bundled-skills/smart-search-cli/references/cli-contract.md +11 -25
- package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.js +5 -0
- package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
- package/dist/templates/cursor/hooks.json +7 -0
- package/dist/templates/cursor/rules/cstl-session-rename.mdc +31 -0
- package/dist/templates/cursor/rules/retrieval-routing.mdc +1 -1
- package/dist/templates/markdown/agents.md +2 -0
- package/dist/templates/markdown/spec/guides/retrieval-daily-guide.md.txt +14 -0
- package/dist/templates/shared-hooks/index.d.ts +1 -1
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +1 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/rename-session-for-task.py +231 -0
- package/dist/templates/trellis/scripts/common/codebase_retrieval_router.py +14 -0
- package/dist/templates/trellis/scripts/common/parent_orchestration.py +341 -2
- package/dist/templates/trellis/scripts/common/task_map.py +638 -614
- package/dist/templates/trellis/scripts/common/task_store.py +47 -1
- package/dist/templates/trellis/scripts/task.py +32 -0
- package/dist/templates/trellis/tasks/templates/release-execution/design.md +60 -60
- package/dist/utils/codebase-retrieval-router.d.ts.map +1 -1
- package/dist/utils/codebase-retrieval-router.js +14 -0
- package/dist/utils/codebase-retrieval-router.js.map +1 -1
- package/dist/utils/cursor-sdk-gate.d.ts +10 -0
- package/dist/utils/cursor-sdk-gate.d.ts.map +1 -0
- package/dist/utils/cursor-sdk-gate.js +32 -0
- package/dist/utils/cursor-sdk-gate.js.map +1 -0
- package/dist/utils/project-capabilities.d.ts +30 -3
- package/dist/utils/project-capabilities.d.ts.map +1 -1
- package/dist/utils/project-capabilities.js +182 -11
- package/dist/utils/project-capabilities.js.map +1 -1
- package/dist/utils/readiness.d.ts.map +1 -1
- package/dist/utils/readiness.js +26 -0
- package/dist/utils/readiness.js.map +1 -1
- package/package.json +4 -3
|
@@ -21,14 +21,13 @@
|
|
|
21
21
|
- `smart-search fetch URL [--format json|markdown|content] [--output PATH]`
|
|
22
22
|
- `smart-search exa-search QUERY [--num-results N] [--search-type neural|keyword|auto] [--include-text] [--include-highlights] [--start-published-date YYYY-MM-DD] [--include-domains DOMAIN...] [--exclude-domains DOMAIN...] [--category NAME] [--format json|markdown|content] [--output PATH]`
|
|
23
23
|
- `smart-search exa-similar URL [--num-results N] [--format json|markdown|content] [--output PATH]`
|
|
24
|
-
- `smart-search zhipu-search QUERY [--count N] [--search-engine NAME] [--search-recency-filter VALUE] [--search-domain-filter DOMAIN] [--content-size medium|high] [--format json|markdown|content] [--output PATH]` — **DEPRECATED**: emits a stderr warning on every invocation; the subcommand, the `research_discovery` zhipu branch, and `providers/zhipu.py` will be removed on the schedule in README § "Deprecation notices".
|
|
25
24
|
- `smart-search context7-library NAME [QUERY] [--format json|markdown|content] [--output PATH]`
|
|
26
25
|
- `smart-search context7-docs LIBRARY_ID QUERY [--format json|markdown|content] [--output PATH]`
|
|
27
26
|
- `smart-search research QUERY [--budget quick|standard|deep] [--locale-scope cn|en|both] [--evidence-dir PATH] [--fallback auto|off] [--dry-run] [--progress] [--format json|markdown|content] [--output PATH]`
|
|
28
27
|
- `smart-search map URL [--instructions TEXT] [--max-depth N] [--max-breadth N] [--limit N] [--timeout SECONDS] [--format json|markdown|content] [--output PATH]`
|
|
29
28
|
- `smart-search doctor [--format json|markdown|content] [--output PATH]`
|
|
30
29
|
- `smart-search diagnose openai-compatible [--timeout SECONDS] [--format json|markdown] [--output PATH]`
|
|
31
|
-
- `smart-search setup [--lang zh|en] [--advanced] [--non-interactive] [--openai-compatible-api-url URL] [--openai-compatible-api-key KEY] [--openai-compatible-model ID] [--openai-compatible-stream true|false] [--validation-level fast|balanced|strict] [--fallback-mode auto|off] [--minimum-profile standard|off] [--exa-key KEY] [--context7-key KEY] [--
|
|
30
|
+
- `smart-search setup [--lang zh|en] [--advanced] [--non-interactive] [--openai-compatible-api-url URL] [--openai-compatible-api-key KEY] [--openai-compatible-model ID] [--openai-compatible-stream true|false] [--validation-level fast|balanced|strict] [--fallback-mode auto|off] [--minimum-profile standard|off] [--exa-key KEY] [--context7-key KEY] [--jina-key KEY] [--jina-reader-api-url URL] [--jina-respond-with MODE] [--jina-timeout SECONDS] [--tavily-api-url URL] [--tavily-key KEY] [--firecrawl-api-url URL] [--firecrawl-key KEY] [--format json|markdown|content] [--output PATH]`
|
|
32
31
|
- `smart-search config path [--format json|markdown|content] [--output PATH]`
|
|
33
32
|
- `smart-search config list [--format json|markdown|content] [--output PATH]`
|
|
34
33
|
- `smart-search config set KEY VALUE [--format json|markdown|content] [--output PATH]`
|
|
@@ -47,7 +46,6 @@ Top-level aliases must normalize to the same service behavior as their full comm
|
|
|
47
46
|
| `map` | `m` |
|
|
48
47
|
| `exa-search` | `exa`, `x` |
|
|
49
48
|
| `exa-similar` | `xs` |
|
|
50
|
-
| `zhipu-search` | `z`, `zp` |
|
|
51
49
|
| `context7-library` | `c7`, `ctx7` |
|
|
52
50
|
| `context7-docs` | `c7d`, `c7docs`, `ctx7-docs` |
|
|
53
51
|
| `research` | `rs` |
|
|
@@ -71,7 +69,7 @@ Successful search output includes `ok`, `query`, `primary_api_mode`, `content`,
|
|
|
71
69
|
|
|
72
70
|
`--format json` is the stable machine-readable contract for agents and scripts. JSON output remains parseable and uses readable non-ASCII text when the terminal encoding supports it.
|
|
73
71
|
|
|
74
|
-
`--format markdown` is the human-readable report format. `doctor --format markdown` must render a detailed diagnostic report with overall status, active/default/legacy config paths, log path resolution, evidence path resolution, file-logging status, masked config values with sources, minimum profile, capability status, main-search provider checks, provider connectivity checks, model metadata, and full long error/message detail instead of falling back to raw JSON. `diagnose openai-compatible --format markdown` must render a short copy-pasteable troubleshooting report with masked config, quick chat check, real search-shape `stream=false` and `stream=true` checks, a plain-language summary, and a next command. Provider list commands such as `exa-search`, `exa-similar`, `
|
|
72
|
+
`--format markdown` is the human-readable report format. `doctor --format markdown` must render a detailed diagnostic report with overall status, active/default/legacy config paths, log path resolution, evidence path resolution, file-logging status, masked config values with sources, minimum profile, capability status, main-search provider checks, provider connectivity checks, model metadata, and full long error/message detail instead of falling back to raw JSON. `diagnose openai-compatible --format markdown` must render a short copy-pasteable troubleshooting report with masked config, quick chat check, real search-shape `stream=false` and `stream=true` checks, a plain-language summary, and a next command. Provider list commands such as `exa-search`, `exa-similar`, `context7-library`, and `map` render result lists or a clear no-results message.
|
|
75
73
|
|
|
76
74
|
`--format content` prints only the `content` field for content-bearing commands such as `search`, `fetch`, `context7-docs`, and `research`. Commands without a `content` field, including `doctor` and `config`, must print a compact non-empty text summary rather than an empty stdout.
|
|
77
75
|
|
|
@@ -90,17 +88,9 @@ Exa domain filters:
|
|
|
90
88
|
|
|
91
89
|
Fetch output includes `ok`, `url`, `provider`, `content`, `provider_attempts`, `fallback_used`, and `elapsed_ms`.
|
|
92
90
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
- `
|
|
96
|
-
- `ZHIPU_SEARCH_ENGINE` defaults to `search_std`.
|
|
97
|
-
- Official Web Search API service values include `search_std`, `search_pro`, `search_pro_sogou`, and `search_pro_quark`.
|
|
98
|
-
- `smart-search setup --zhipu-api-url URL --zhipu-search-engine ENGINE` saves these values in non-interactive mode.
|
|
99
|
-
- Interactive setup no longer recommends or prompts for Zhipu in the default flow. Use `config set` or non-interactive flags only for explicit manual legacy compatibility.
|
|
100
|
-
- `config set ZHIPU_SEARCH_ENGINE VALUE` must remain free-form so newly added official services do not require a CLI release.
|
|
101
|
-
- `zhipu-search` corresponds to Zhipu Web Search API, not Zhipu Chat Completions `tools=[web_search]`, not Search Agent, and not the MCP Server.
|
|
102
|
-
- `zhipu-search` is deprecated and not used by default routing because quota may be unavailable. Default source discovery uses bilingual `search` through Tavily / Firecrawl when configured.
|
|
103
|
-
- `TAVILY_API_URL` only affects Tavily and does not proxy Zhipu.
|
|
91
|
+
Tavily setup notes:
|
|
92
|
+
|
|
93
|
+
- `TAVILY_API_URL` only affects Tavily.
|
|
104
94
|
- `TAVILY_TIMEOUT_SECONDS` controls the Tavily `doctor` connectivity timeout. It defaults to `60` so slower pooled/community endpoints are not incorrectly marked unhealthy by the diagnostic check.
|
|
105
95
|
|
|
106
96
|
Jina Reader setup:
|
|
@@ -124,15 +114,13 @@ Exa HTTP `400` or `422` failures are returned as `ok=false` with `error_type=par
|
|
|
124
114
|
|
|
125
115
|
Exa similar output includes `ok`, `url`, `results`, `total`, and `elapsed_ms` when successful.
|
|
126
116
|
|
|
127
|
-
Zhipu search output includes `ok`, `query`, `provider`, `search_engine`, `results`, `total`, and `elapsed_ms` when successful.
|
|
128
|
-
|
|
129
117
|
Context7 library output includes `ok`, `query`, `provider`, `results`, `total`, and `elapsed_ms` when successful. Context7 docs output includes `ok`, `library_id`, `query`, `provider`, `results`, `total`, `content`, and `elapsed_ms` when successful.
|
|
130
118
|
|
|
131
119
|
Map output includes `ok`, `base_url`, `results`, `response_time`, `url`, and `elapsed_ms` when successful.
|
|
132
120
|
|
|
133
121
|
Research executor output includes `ok`, `mode=deep_research_execution`, `query_mode=research`, `question`, `budget`, `research_plan`, `routing_decision`, `stage_results`, `discovery_sources`, `final_answer`, `content`, `citations`, `evidence_items`, `gap_check`, `provider_attempts`, `providers_used`, `fallback_used`, `degraded`, `route_policy_version`, `evidence_dir`, `minimum_profile_ok`, `capability_status`, and `elapsed_ms`. The embedded `research_plan` carries `intent_signals`, `decomposition`, `capability_plan`, `evidence_policy`, `steps`, and `gap_check`. Citations must come only from fetched/read `evidence_items`; discovery sources are candidates until fetched. If evidence cannot close, `research` returns degraded gaps instead of unsupported claims.
|
|
134
122
|
|
|
135
|
-
Diagnostic output masks keys, reports `config_file` / `config_dir` / `config_dir_source` / `default_config_file` / Windows legacy config metadata / `config_dir_override_value` / `config_dir_override_matches_default` / `log_dir_config_value` / `resolved_log_dir` / `evidence_dir_config_value` / `resolved_evidence_dir` / `file_logging_enabled` / `config_sources` / `primary_api_mode` / `primary_api_mode_source` / provider timeout values / `capability_status` / `minimum_profile_ok`, and includes `main_search_connection_tests` plus connection test objects for Exa, Tavily,
|
|
123
|
+
Diagnostic output masks keys, reports `config_file` / `config_dir` / `config_dir_source` / `default_config_file` / Windows legacy config metadata / `config_dir_override_value` / `config_dir_override_matches_default` / `log_dir_config_value` / `resolved_log_dir` / `evidence_dir_config_value` / `resolved_evidence_dir` / `file_logging_enabled` / `config_sources` / `primary_api_mode` / `primary_api_mode_source` / provider timeout values / `capability_status` / `minimum_profile_ok`, and includes `main_search_connection_tests` plus connection test objects for Exa, Tavily, Context7, and Firecrawl. `primary_connection_test` remains as a backward-compatible alias for the first configured main provider check. OpenAI-compatible provider health must be validated through `/chat/completions`; `/models` is supplementary metadata and must not be the health gate. xAI Responses health is validated through `/responses` (a lightweight probe in `doctor`; `diagnose xai` adds a search-shape probe with server-side tools). Firecrawl currently reports whether `FIRECRAWL_API_KEY` is configured; it is not a live Firecrawl request.
|
|
136
124
|
|
|
137
125
|
When a Windows user reports that different versions seem to use different config paths, diagnose in this order: `config_dir_source`, `config_dir_override_value`, `config_dir_override_matches_default`, then `legacy_windows_config_exists`. A source of `environment` with `config_dir_override_matches_default=true` means the active path is pinned by `SMART_SEARCH_CONFIG_DIR` but is functionally the same as the current default. Do not delete either config file or the user-level override until the upgraded CLI has been verified with `config path` and `doctor` checks.
|
|
138
126
|
|
|
@@ -163,7 +151,6 @@ Each `steps[]` item must include `id`, `subquestion_id`, `tool`, `purpose`, `com
|
|
|
163
151
|
Capability boundaries:
|
|
164
152
|
|
|
165
153
|
- `search`: broad bilingual discovery and synthesis through `main_search`; use returned `routing_decision`, `provider_attempts`, `fallback_used`, and `source_warning` as orchestration signals, not as claim proof.
|
|
166
|
-
- `zhipu-search`: deprecated manual compatibility command. Do not include it in default research plans.
|
|
167
154
|
- `context7-library` and `context7-docs`: library, SDK, API, framework, and documentation intent. Prefer Context7 before Exa for docs/API questions.
|
|
168
155
|
- `exa-search`: low-noise source discovery for official domains, papers, product pages, known domains, and trusted pages. It is not the default second hop for every high-risk or verification task.
|
|
169
156
|
- `exa-similar`: adjacent-source discovery when a known reliable URL is available.
|
|
@@ -258,15 +245,15 @@ Agent timeout handling contract:
|
|
|
258
245
|
|
|
259
246
|
## Provider Routing
|
|
260
247
|
|
|
261
|
-
- `search` builds `main_search` from `OPENAI_COMPATIBLE_API_URL` + `OPENAI_COMPATIBLE_API_KEY
|
|
248
|
+
- `search` builds `main_search` from `XAI_API_KEY` (xAI Responses with server-side `web_search`/`x_search` tools) and/or `OPENAI_COMPATIBLE_API_URL` + `OPENAI_COMPATIBLE_API_KEY` (Chat Completions). One is enough; when both are configured, `SMART_SEARCH_MAIN_SEARCH_ROUTE` (ordered CSV of `xai-responses,openai-compatible`) sets priority, and a single entry disables cross-route fallback.
|
|
262
249
|
- OpenAI-compatible relays/gateways use Chat Completions `/chat/completions` through `OPENAI_COMPATIBLE_*`.
|
|
263
250
|
- `OPENAI_COMPATIBLE_STREAM` and `search --stream/--no-stream` affect only the OpenAI-compatible Chat Completions transport for search/fetch. They do not change provider-internal ranking/URL description tasks.
|
|
264
251
|
- Legacy `SMART_SEARCH_API_URL`, `SMART_SEARCH_API_KEY`, `SMART_SEARCH_API_MODE`, and `SMART_SEARCH_MODEL` are unsupported config keys. `config set` / `config unset` must return a parameter error for them.
|
|
265
252
|
- Standard minimum profile requires `main_search`, `docs_search`, and fetch capability. Missing required capabilities produce a configuration error.
|
|
266
253
|
- Jina satisfies fetch capability only when `JINA_API_KEY` is configured. Anonymous Jina Reader does not satisfy `standard`.
|
|
267
254
|
- Same-capability fallback is allowed; cross-capability fallback is not. Context7 is not used for unrelated broad web queries, and page extraction providers are not used as docs search providers.
|
|
268
|
-
- `main_search`: OpenAI-compatible Chat Completions.
|
|
269
|
-
- `web_search`: `search` runs bilingual web_search source discovery through Tavily / Firecrawl when configured.
|
|
255
|
+
- `main_search`: xAI Responses (`XAI_*`) or OpenAI-compatible Chat Completions (`OPENAI_COMPATIBLE_*`), ordered by `SMART_SEARCH_MAIN_SEARCH_ROUTE` when both are configured (default `xai-responses -> openai-compatible`).
|
|
256
|
+
- `web_search`: `search` runs bilingual web_search source discovery through Tavily / Firecrawl when configured.
|
|
270
257
|
- `docs_search`: explicit keyword-based docs/API/library/framework intent. Context7 is first for library/API/docs intent, then Exa for official-domain, paper, product-page, trusted-site, or low-noise supplemental discovery.
|
|
271
258
|
- Fetch capability: Tavily first, then Jina Reader with `JINA_API_KEY`, then Firecrawl.
|
|
272
259
|
- `search --validation strict` uses the same bilingual web_search policy as balanced mode when source discovery providers are configured. Strict queries without primary, docs, fetch, or explicit source evidence can still fail with `evidence_error`; use `--extra-sources N`, source-first commands such as `exa-search`, or `fetch` when citable evidence is required.
|
|
@@ -278,17 +265,16 @@ Agent timeout handling contract:
|
|
|
278
265
|
- `research` uses capability-first plus provider-advantage routing. Fallback remains same-capability only; low-quality fetches, challenge pages, empty content, auth/rate/timeout/provider errors, and runtime errors are failed attempts that may trigger same-capability fallback.
|
|
279
266
|
- `map` uses Tavily only.
|
|
280
267
|
- `exa-search` and `exa-similar` use Exa only.
|
|
281
|
-
- `zhipu-search` uses Zhipu only and is retained as a deprecated manual compatibility command.
|
|
282
268
|
- `context7-library` and `context7-docs` use Context7 only.
|
|
283
269
|
- Runtime config priority is environment variables first, then local config file, then defaults.
|
|
284
270
|
- `setup` and `config` read/write the local Smart Search config file and do not call providers.
|
|
285
|
-
- Use `config set OPENAI_COMPATIBLE_MODEL ...` to change the main-search model.
|
|
271
|
+
- Use `config set OPENAI_COMPATIBLE_MODEL ...` (or `XAI_MODEL ...` for the xAI route) to change the main-search model; use `config set SMART_SEARCH_MAIN_SEARCH_ROUTE ...` to change route priority.
|
|
286
272
|
|
|
287
273
|
## Routing Heuristics
|
|
288
274
|
|
|
289
275
|
- Use `exa-search --include-domains` when official documentation domains are known.
|
|
290
276
|
- Use `context7-library` / `context7-docs` for explicit docs/API/SDK/library/framework intent when Context7 is configured.
|
|
291
|
-
- Use the bilingual `search` pair for Chinese, domestic, current, or mixed-language source discovery.
|
|
277
|
+
- Use the bilingual `search` pair for Chinese, domestic, current, or mixed-language source discovery.
|
|
292
278
|
- Use `exa-search --start-published-date` for recency-constrained source discovery.
|
|
293
279
|
- Use `exa-similar` when a known good page is available and adjacent sources are needed.
|
|
294
280
|
- Use `search --format content` when a human wants only the generated answer body.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expected-rules.d.ts","sourceRoot":"","sources":["../../../../src/templates/cursor/fixtures/expected-rules.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"expected-rules.d.ts","sourceRoot":"","sources":["../../../../src/templates/cursor/fixtures/expected-rules.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,EAAE,YAAY,EAyBvC,CAAC"}
|
|
@@ -22,5 +22,10 @@ export const expectedRules = [
|
|
|
22
22
|
requiredSections: ["Native", "BYOK", "codegraph", "smart-search"],
|
|
23
23
|
minBytes: 600,
|
|
24
24
|
},
|
|
25
|
+
{
|
|
26
|
+
filename: "cstl-session-rename.mdc",
|
|
27
|
+
requiredSections: ["rename_chat", "select", "start-execution", "skip silently"],
|
|
28
|
+
minBytes: 300,
|
|
29
|
+
},
|
|
25
30
|
];
|
|
26
31
|
//# sourceMappingURL=expected-rules.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expected-rules.js","sourceRoot":"","sources":["../../../../src/templates/cursor/fixtures/expected-rules.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C;QACE,QAAQ,EAAE,iBAAiB;QAC3B,gBAAgB,EAAE;YAChB,eAAe;YACf,qBAAqB;YACrB,cAAc;SACf;QACD,QAAQ,EAAE,GAAG;KACd;IACD;QACE,QAAQ,EAAE,4BAA4B;QACtC,gBAAgB,EAAE,CAAC,SAAS,EAAE,0BAA0B,EAAE,KAAK,CAAC;QAChE,QAAQ,EAAE,GAAG;KACd;IACD;QACE,QAAQ,EAAE,uBAAuB;QACjC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,CAAC;QACjE,QAAQ,EAAE,GAAG;KACd;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"expected-rules.js","sourceRoot":"","sources":["../../../../src/templates/cursor/fixtures/expected-rules.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C;QACE,QAAQ,EAAE,iBAAiB;QAC3B,gBAAgB,EAAE;YAChB,eAAe;YACf,qBAAqB;YACrB,cAAc;SACf;QACD,QAAQ,EAAE,GAAG;KACd;IACD;QACE,QAAQ,EAAE,4BAA4B;QACtC,gBAAgB,EAAE,CAAC,SAAS,EAAE,0BAA0B,EAAE,KAAK,CAAC;QAChE,QAAQ,EAAE,GAAG;KACd;IACD;QACE,QAAQ,EAAE,uBAAuB;QACjC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,CAAC;QACjE,QAAQ,EAAE,GAAG;KACd;IACD;QACE,QAAQ,EAAE,yBAAyB;QACnC,gBAAgB,EAAE,CAAC,aAAa,EAAE,QAAQ,EAAE,iBAAiB,EAAE,eAAe,CAAC;QAC/E,QAAQ,EAAE,GAAG;KACd;CACF,CAAC"}
|
|
@@ -26,6 +26,13 @@
|
|
|
26
26
|
"timeout": 5
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
|
+
"afterShellExecution": [
|
|
30
|
+
{
|
|
31
|
+
"command": "{{PYTHON_CMD}} .cursor/hooks/rename-session-for-task.py",
|
|
32
|
+
"matcher": "task\\.py.*(select|start-execution)",
|
|
33
|
+
"timeout": 5
|
|
34
|
+
}
|
|
35
|
+
],
|
|
29
36
|
"stop": [
|
|
30
37
|
{
|
|
31
38
|
"command": "{{PYTHON_CMD}} .cursor/hooks/research-end-retrieval-pack.py",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Rename Cursor chat to cstl task directory name after select or start-execution (cursor-app-control rename_chat)"
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Trellis session rename (Cursor main session)
|
|
7
|
+
|
|
8
|
+
Encourage **one task per main Agent chat**. Subagent / Task child windows are out of scope.
|
|
9
|
+
|
|
10
|
+
## When to rename
|
|
11
|
+
|
|
12
|
+
After **you** successfully run either command in the **main** session (same turn or immediately after shell output):
|
|
13
|
+
|
|
14
|
+
- `python ./.cstl/scripts/task.py select <task-dir>`
|
|
15
|
+
- `python ./.cstl/scripts/task.py start-execution <task-dir> --approved`
|
|
16
|
+
|
|
17
|
+
Do **not** rename for `task.py create` alone (create does not bind the session).
|
|
18
|
+
|
|
19
|
+
## Title
|
|
20
|
+
|
|
21
|
+
Use the task **directory name** only (e.g. `07-04-cstl-session-rename`), not `task.json` `title`.
|
|
22
|
+
|
|
23
|
+
## How
|
|
24
|
+
|
|
25
|
+
1. If `cursor-app-control` MCP `rename_chat` is available, call it once with `title` = directory name.
|
|
26
|
+
2. If the tool is **not** available (BYOK or other environments), **skip silently** — no user prompt to rename manually.
|
|
27
|
+
3. Do not rename repeatedly for the same task in the same session unless the user switched to a different task directory.
|
|
28
|
+
|
|
29
|
+
## Native vs BYOK
|
|
30
|
+
|
|
31
|
+
Same rule for both. BYOK availability of `rename_chat` is environment-dependent; never fail the task workflow when rename is unavailable.
|
|
@@ -56,4 +56,4 @@ Use codegraph for **call chains**, **cross-package trap disambiguation**, **exte
|
|
|
56
56
|
|
|
57
57
|
## External facts
|
|
58
58
|
|
|
59
|
-
Web/current events: **smart-search** first per `.cstl/spec/guides/retrieval-daily-guide.md
|
|
59
|
+
Web/current events: **smart-search** first per `.cstl/spec/guides/retrieval-daily-guide.md` (see **External-knowledge gate** for search-or-not).
|
|
@@ -23,6 +23,8 @@ Only a handful of Trellis entry points are meant for **manual `/` invocation**.
|
|
|
23
23
|
|
|
24
24
|
For **any external / current / web fact**, run **`python ./.cstl/scripts/run_smart_search.py "<question>" --intent deep-research --json`** first. That script is the **only** Trellis web-research evidence entrypoint (it shells out to the `smart-search` CLI). Do not guess paths under package source trees or sibling repos. Platform built-in web tools (Cursor `WebSearch` / `WebFetch`, or native web tools elsewhere) are **downgrade-only fallbacks**, used solely when smart-search is unavailable (`doctor` not ok, status `not_configured` / `failed`, or search timeout). Do not reach for built-in web search while smart-search is healthy. On Cursor, `smart-search-cli` is an **internal workflow skill name** only (not shipped under `.cursor/skills/`); follow `.cstl/spec/guides/retrieval-daily-guide.md` and `.cursor/rules/retrieval-routing.mdc` for the executable contract.
|
|
25
25
|
|
|
26
|
+
**External-knowledge gate:** If the answer would be wrong because the **world or a third-party API moved** and that matters → use smart-search (cheap `docs` / `broad-search` when enough; `deep-research` when multi-source). If truth lives only in this workspace → do not default to web. When unsure, prefer a cheap probe over guessing. See retrieval-daily-guide § External-knowledge gate.
|
|
27
|
+
|
|
26
28
|
Managed by cursor-trellis. Edits outside this block are preserved; edits inside may be overwritten by a future `cstl update`.
|
|
27
29
|
|
|
28
30
|
<!-- CSTL:END -->
|
|
@@ -6,6 +6,20 @@
|
|
|
6
6
|
|
|
7
7
|
For **external / current / web facts**, **smart-search** (`run_smart_search.py`) is the **mandatory first choice**. Platform built-in web tools (Cursor `WebSearch` / `WebFetch`) are **downgrade-only**: use them solely when smart-search is unavailable (`doctor` not ok, `not_configured` / `failed`, or timeout). Never reach for built-in web search while smart-search is healthy. This rule overrides any general "use available tools" instinct — web retrieval strength is routed inside Trellis, not left to the platform default.
|
|
8
8
|
|
|
9
|
+
## External-knowledge gate (search-or-not)
|
|
10
|
+
|
|
11
|
+
Ask: *If this answer were wrong because the world or a third-party API moved, would that matter?*
|
|
12
|
+
|
|
13
|
+
| Decision | Action |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| **YES** (freshness / third-party surface) | Run `run_smart_search.py` — prefer `docs` / `official-source` / `broad-search` when cheap; use `deep-research` when multi-source or claim-risk is high |
|
|
16
|
+
| **NO** (truth lives in this workspace) | Use rg / codegraph / `.cstl/spec` / task artifacts only — do **not** default to web |
|
|
17
|
+
| **Ambiguous** | Prefer a **cheap** external probe over guessing; do not skip solely to save a call |
|
|
18
|
+
|
|
19
|
+
**YES examples:** library/SDK current API or version; changelog / release notes; CVE / GitHub issue status; pricing / product status; live URLs; industry practice when designing policy.
|
|
20
|
+
**NO examples:** symbol location / call chains; in-repo Trellis contracts; behavior of the code you are editing; pure rename/lint inside known files.
|
|
21
|
+
After search: persist under `{TASK}/research/` with a **provider label**; treat hits as candidates until corroborated (repo, test, or second source).
|
|
22
|
+
|
|
9
23
|
## Quick matrix
|
|
10
24
|
|
|
11
25
|
| Need | Tool | Notes |
|
|
@@ -11,7 +11,7 @@ export interface HookScript {
|
|
|
11
11
|
/** Script content — no placeholders, ready to write directly */
|
|
12
12
|
content: string;
|
|
13
13
|
}
|
|
14
|
-
export type SharedHookName = "session-start.py" | "inject-shell-session-context.py" | "inject-retrieval-plan.py" | "inject-workflow-state.py" | "inject-subagent-context.py" | "research-end-retrieval-pack.py";
|
|
14
|
+
export type SharedHookName = "session-start.py" | "inject-shell-session-context.py" | "rename-session-for-task.py" | "inject-retrieval-plan.py" | "inject-workflow-state.py" | "inject-subagent-context.py" | "research-end-retrieval-pack.py";
|
|
15
15
|
export type SharedHookPlatform = "claude" | "cursor" | "codex" | "gemini" | "qoder" | "copilot" | "codebuddy" | "droid" | "kiro";
|
|
16
16
|
/**
|
|
17
17
|
* Which shared hooks each platform actually invokes. Single source of truth
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,iCAAiC,GACjC,0BAA0B,GAC1B,0BAA0B,GAC1B,4BAA4B,GAC5B,gCAAgC,CAAC;AAErC,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,kBAAkB,EAClB,SAAS,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,iCAAiC,GACjC,4BAA4B,GAC5B,0BAA0B,GAC1B,0BAA0B,GAC1B,4BAA4B,GAC5B,gCAAgC,CAAC;AAErC,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,OAAO,GACP,SAAS,GACT,WAAW,GACX,OAAO,GACP,MAAM,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAC3C,kBAAkB,EAClB,SAAS,cAAc,EAAE,CAiC1B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,UAAU,EAAE,CAWnD;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,kBAAkB,GAC3B,UAAU,EAAE,CAGd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,SAAS,YAAY,CAAC,YAAoB;IACxC,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/shared-hooks/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,SAAS,YAAY,CAAC,YAAoB;IACxC,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;AA6BD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAGjC;IACF,MAAM,EAAE;QACN,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;QAC5B,gCAAgC;KACjC;IACD,MAAM,EAAE;QACN,kBAAkB;QAClB,iCAAiC;QACjC,4BAA4B;QAC5B,0BAA0B;QAC1B,4BAA4B;QAC5B,gCAAgC;KACjC;IACD,KAAK,EAAE,CAAC,0BAA0B,CAAC;IACnC,MAAM,EAAE,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;IACxD,KAAK,EAAE,CAAC,kBAAkB,EAAE,0BAA0B,CAAC;IACvD,OAAO,EAAE,CAAC,0BAA0B,CAAC;IACrC,SAAS,EAAE;QACT,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;QAC5B,gCAAgC;KACjC;IACD,KAAK,EAAE;QACL,kBAAkB;QAClB,0BAA0B;QAC1B,4BAA4B;QAC5B,gCAAgC;KACjC;IACD,IAAI,EAAE,CAAC,4BAA4B,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC;SACjC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAChC,IAAI,EAAE,CAAC;IAEV,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAC7C,QAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpE,OAAO,oBAAoB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Cursor afterShellExecution hook: prompt session rename after task bind/start.
|
|
3
|
+
|
|
4
|
+
When the agent successfully runs `task.py select` or
|
|
5
|
+
`task.py start-execution <task> --approved`, emit an agent_message (best-effort)
|
|
6
|
+
so the main session calls cursor-app-control `rename_chat` with the task
|
|
7
|
+
directory name. MCP unavailable → agent skips silently per cstl-session-rename rule.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import shlex
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
DIR_WORKFLOW = ".cstl"
|
|
21
|
+
DIR_RUNTIME = ".runtime"
|
|
22
|
+
DIR_SESSION_RENAME = "session-rename"
|
|
23
|
+
ANSI_ESCAPE = re.compile(r"\x1b\[[0-9;]*m")
|
|
24
|
+
CONTEXT_IDENTITY_KEYS = (
|
|
25
|
+
"session_id",
|
|
26
|
+
"sessionId",
|
|
27
|
+
"sessionID",
|
|
28
|
+
"conversation_id",
|
|
29
|
+
"conversationId",
|
|
30
|
+
"conversationID",
|
|
31
|
+
"transcript_path",
|
|
32
|
+
"transcriptPath",
|
|
33
|
+
"transcript",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _string_value(value: Any) -> str | None:
|
|
38
|
+
if isinstance(value, str):
|
|
39
|
+
stripped = value.strip()
|
|
40
|
+
return stripped or None
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _strip_ansi(text: str) -> str:
|
|
45
|
+
return ANSI_ESCAPE.sub("", text)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _find_trellis_root(start: Path) -> Path | None:
|
|
49
|
+
current = start.resolve()
|
|
50
|
+
while True:
|
|
51
|
+
if (current / DIR_WORKFLOW).is_dir():
|
|
52
|
+
return current
|
|
53
|
+
if current == current.parent:
|
|
54
|
+
return None
|
|
55
|
+
current = current.parent
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _task_py_token_index(tokens: list[str]) -> int | None:
|
|
59
|
+
for index, token in enumerate(tokens):
|
|
60
|
+
if Path(token.strip("\"'")).name == "task.py":
|
|
61
|
+
return index
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def parse_rename_intent(command: str) -> dict[str, str] | None:
|
|
66
|
+
"""Return subcommand + task_ref when command should trigger rename."""
|
|
67
|
+
try:
|
|
68
|
+
tokens = shlex.split(command, posix=os.name != "nt")
|
|
69
|
+
except ValueError:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
task_index = _task_py_token_index(tokens)
|
|
73
|
+
if task_index is None or task_index + 1 >= len(tokens):
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
subcommand = tokens[task_index + 1]
|
|
77
|
+
if subcommand == "select":
|
|
78
|
+
if task_index + 2 >= len(tokens):
|
|
79
|
+
return None
|
|
80
|
+
return {"subcommand": subcommand, "task_ref": tokens[task_index + 2]}
|
|
81
|
+
if subcommand == "start-execution":
|
|
82
|
+
if "--approved" not in tokens:
|
|
83
|
+
return None
|
|
84
|
+
if task_index + 2 >= len(tokens):
|
|
85
|
+
return None
|
|
86
|
+
task_ref = tokens[task_index + 2]
|
|
87
|
+
if task_ref.startswith("-"):
|
|
88
|
+
return None
|
|
89
|
+
return {"subcommand": subcommand, "task_ref": task_ref}
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def task_directory_name(task_ref: str) -> str:
|
|
94
|
+
"""Use task folder basename as the chat title."""
|
|
95
|
+
cleaned = task_ref.strip().strip("\"'")
|
|
96
|
+
return Path(cleaned.replace("\\", "/")).name
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def shell_output_succeeded(subcommand: str, output: str) -> bool:
|
|
100
|
+
text = _strip_ansi(output or "")
|
|
101
|
+
if "Error:" in text or "error:" in text.lower():
|
|
102
|
+
return False
|
|
103
|
+
if subcommand == "select":
|
|
104
|
+
return "Selected task:" in text or "✓ Selected task" in text
|
|
105
|
+
if subcommand == "start-execution":
|
|
106
|
+
return "Execution approved" in text or "✓ Execution approved" in text
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _safe_context_filename(context_key: str) -> str:
|
|
111
|
+
digest = hashlib.sha256(context_key.encode("utf-8")).hexdigest()[:24]
|
|
112
|
+
return f"{digest}.json"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _rename_state_dir(root: Path) -> Path:
|
|
116
|
+
return root / DIR_WORKFLOW / DIR_RUNTIME / DIR_SESSION_RENAME
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _load_rename_state(path: Path) -> dict[str, Any]:
|
|
120
|
+
if not path.is_file():
|
|
121
|
+
return {}
|
|
122
|
+
try:
|
|
123
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
124
|
+
except (json.JSONDecodeError, OSError):
|
|
125
|
+
return {}
|
|
126
|
+
return data if isinstance(data, dict) else {}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _should_emit_rename(state: dict[str, Any], title: str) -> bool:
|
|
130
|
+
return state.get("last_title") != title
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _write_rename_state(path: Path, context_key: str, title: str) -> None:
|
|
134
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
135
|
+
payload = {
|
|
136
|
+
"context_key": context_key,
|
|
137
|
+
"last_title": title,
|
|
138
|
+
}
|
|
139
|
+
path.write_text(
|
|
140
|
+
json.dumps(payload, indent=2, ensure_ascii=False) + "\n",
|
|
141
|
+
encoding="utf-8",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _agent_rename_message(title: str) -> str:
|
|
146
|
+
return (
|
|
147
|
+
"Trellis session rename: call cursor-app-control MCP `rename_chat` once "
|
|
148
|
+
f'with title "{title}" (task directory name). '
|
|
149
|
+
"If rename_chat is not in your tool list, skip silently — do not ask the user "
|
|
150
|
+
"to rename manually."
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _load_context_key(root: Path, hook_input: dict[str, Any]) -> str | None:
|
|
155
|
+
scripts_dir = root / DIR_WORKFLOW / "scripts"
|
|
156
|
+
if str(scripts_dir) not in sys.path:
|
|
157
|
+
sys.path.insert(0, str(scripts_dir))
|
|
158
|
+
from common.active_task import resolve_context_key # type: ignore[import-not-found]
|
|
159
|
+
|
|
160
|
+
if not any(_string_value(hook_input.get(key)) for key in CONTEXT_IDENTITY_KEYS):
|
|
161
|
+
return None
|
|
162
|
+
return resolve_context_key(hook_input, platform="cursor")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def main() -> int:
|
|
166
|
+
if os.environ.get("TRELLIS_HOOKS") == "0" or os.environ.get("TRELLIS_DISABLE_HOOKS") == "1":
|
|
167
|
+
print("{}")
|
|
168
|
+
return 0
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
hook_input = json.loads(sys.stdin.read())
|
|
172
|
+
except (json.JSONDecodeError, ValueError):
|
|
173
|
+
hook_input = {}
|
|
174
|
+
if not isinstance(hook_input, dict):
|
|
175
|
+
hook_input = {}
|
|
176
|
+
|
|
177
|
+
event = _string_value(hook_input.get("hook_event_name")) or "afterShellExecution"
|
|
178
|
+
if event != "afterShellExecution":
|
|
179
|
+
print("{}")
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
command = _string_value(hook_input.get("command")) or ""
|
|
183
|
+
intent = parse_rename_intent(command)
|
|
184
|
+
if intent is None:
|
|
185
|
+
print("{}")
|
|
186
|
+
return 0
|
|
187
|
+
|
|
188
|
+
output = _string_value(hook_input.get("output")) or ""
|
|
189
|
+
if not shell_output_succeeded(intent["subcommand"], output):
|
|
190
|
+
print("{}")
|
|
191
|
+
return 0
|
|
192
|
+
|
|
193
|
+
title = task_directory_name(intent["task_ref"])
|
|
194
|
+
if not title:
|
|
195
|
+
print("{}")
|
|
196
|
+
return 0
|
|
197
|
+
|
|
198
|
+
cwd = Path(_string_value(hook_input.get("cwd")) or os.getcwd())
|
|
199
|
+
root = _find_trellis_root(cwd)
|
|
200
|
+
if root is None:
|
|
201
|
+
print("{}")
|
|
202
|
+
return 0
|
|
203
|
+
|
|
204
|
+
context_key = _load_context_key(root, hook_input)
|
|
205
|
+
if not context_key:
|
|
206
|
+
print("{}")
|
|
207
|
+
return 0
|
|
208
|
+
|
|
209
|
+
state_path = _rename_state_dir(root) / _safe_context_filename(context_key)
|
|
210
|
+
state = _load_rename_state(state_path)
|
|
211
|
+
if not _should_emit_rename(state, title):
|
|
212
|
+
print("{}")
|
|
213
|
+
return 0
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
_write_rename_state(state_path, context_key, title)
|
|
217
|
+
except OSError:
|
|
218
|
+
print("{}")
|
|
219
|
+
return 0
|
|
220
|
+
|
|
221
|
+
print(
|
|
222
|
+
json.dumps(
|
|
223
|
+
{"agent_message": _agent_rename_message(title)},
|
|
224
|
+
ensure_ascii=False,
|
|
225
|
+
),
|
|
226
|
+
)
|
|
227
|
+
return 0
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == "__main__":
|
|
231
|
+
sys.exit(main())
|
|
@@ -75,6 +75,16 @@ POLICY_PATTERNS = [
|
|
|
75
75
|
re.compile(r"\bresponsibilit(y|ies)\b", re.I),
|
|
76
76
|
re.compile(r"不能"),
|
|
77
77
|
re.compile(r"规则"),
|
|
78
|
+
# Workflow / task-ladder docs (AGENTS.md, workflow.md) — not code-search first
|
|
79
|
+
re.compile(r"\bFull\s+Task\b", re.I),
|
|
80
|
+
re.compile(r"\bLite\s+Task\b", re.I),
|
|
81
|
+
re.compile(r"\bMicro-Grill\b", re.I),
|
|
82
|
+
re.compile(r"\bParent\s+Task\b", re.I),
|
|
83
|
+
re.compile(r"\bChild\s+Task\b", re.I),
|
|
84
|
+
re.compile(r"\bTriage\b"),
|
|
85
|
+
re.compile(r"\btask\s+ladder\b", re.I),
|
|
86
|
+
re.compile(r"任务阶梯"),
|
|
87
|
+
re.compile(r"工作流"),
|
|
78
88
|
]
|
|
79
89
|
|
|
80
90
|
CONCEPTUAL_PATTERNS = [
|
|
@@ -152,6 +162,10 @@ EXACT_PATTERNS = [
|
|
|
152
162
|
re.compile(r"`[^`]+`"),
|
|
153
163
|
re.compile(r"\b[\w.-]+\.(ts|tsx|js|jsx|py|rs|go|swift|kt|md|json|yaml|yml)\b", re.I),
|
|
154
164
|
re.compile(r"\b(?:src|packages|extensions)/[\w./-]+", re.I),
|
|
165
|
+
# Meta / router-implementation queries → lexical exact (not policy docs)
|
|
166
|
+
re.compile(r"检索(意图)?路由"),
|
|
167
|
+
re.compile(r"\bintent\s*rout(?:e|ing)?\b", re.I),
|
|
168
|
+
re.compile(r"codebase[_-]?retrieval[_-]?router", re.I),
|
|
155
169
|
]
|
|
156
170
|
|
|
157
171
|
|