@blxzer/cursor-trellis 0.4.2 → 0.4.3
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 +24 -0
- package/README.md +23 -14
- package/README.zh-CN.md +14 -10
- package/bin/smart-search.js +10 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +36 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +42 -153
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts +10 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +38 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +137 -40
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/cursor.d.ts +2 -1
- package/dist/configurators/cursor.d.ts.map +1 -1
- package/dist/configurators/cursor.js +4 -2
- package/dist/configurators/cursor.js.map +1 -1
- package/dist/configurators/index.js +4 -4
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/shared.d.ts +8 -0
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +27 -1
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/workflow.d.ts +0 -9
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +2 -4
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/constants/paths.d.ts +9 -0
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +13 -0
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.3.0.json +1 -1
- package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/skills-and-commands.md +2 -4
- package/dist/templates/common/bundled-skills/cstl-skill-creator/SKILL.md +10 -7
- package/dist/templates/common/bundled-skills/cstl-skill-creator/references/authoring-rules.md +93 -19
- package/dist/templates/common/bundled-skills/cstl-skill-creator/references/general-authoring.md +189 -0
- package/dist/templates/common/bundled-skills/cstl-skill-creator/references/review-checklist.md +10 -5
- package/dist/templates/common/commands/continue.md +5 -4
- package/dist/templates/common/index.d.ts +20 -0
- package/dist/templates/common/index.d.ts.map +1 -1
- package/dist/templates/common/index.js +21 -2
- package/dist/templates/common/index.js.map +1 -1
- package/dist/templates/common/optional-skills/chrome-cdp/SKILL.md +179 -0
- package/dist/templates/common/optional-skills/chrome-cdp/examples/fetch-hook-api-capture.md +149 -0
- package/dist/templates/common/optional-skills/chrome-cdp/scripts/cdp.mjs +903 -0
- package/dist/templates/cursor/agents/cstl-research.md +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.d.ts +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.js +7 -24
- package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
- package/dist/templates/cursor/hooks.json +4 -0
- package/dist/templates/cursor/index.d.ts +1 -1
- package/dist/templates/cursor/index.js +1 -1
- package/dist/templates/cursor/rules/cstl-bootstrap.mdc +17 -0
- package/dist/templates/markdown/agents.md +15 -10
- package/dist/templates/markdown/framework/cursor-native-modes-guide.md.txt +12 -9
- package/dist/templates/markdown/framework/cursor-subagent-policy.md.txt +62 -126
- package/dist/templates/markdown/framework/dogfood-only-surfaces.md.txt +4 -6
- package/dist/templates/markdown/framework/execution-strategy.md.txt +2 -0
- package/dist/templates/markdown/framework/index.md.txt +4 -0
- package/dist/templates/markdown/framework/internal-skills-cursor-reachability.md.txt +4 -3
- package/dist/templates/markdown/framework/middleware-protocol.md.txt +83 -0
- package/dist/templates/markdown/framework/parallel-first-execution.md.txt +78 -0
- package/dist/templates/markdown/framework/release-boundary.md.txt +30 -0
- package/dist/templates/markdown/framework/retrieval-daily-guide.md.txt +1 -1
- package/dist/templates/markdown/framework/upgrade.md.txt +15 -0
- package/dist/templates/markdown/index.d.ts +4 -0
- package/dist/templates/markdown/index.d.ts.map +1 -1
- package/dist/templates/markdown/index.js +17 -0
- package/dist/templates/markdown/index.js.map +1 -1
- package/dist/templates/markdown/spec/guides/index.md.txt +0 -6
- package/dist/templates/shared-hooks/event-bridge.py +60 -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/session-start.py +18 -1
- package/dist/templates/trellis/CONTEXT.md +63 -3
- package/dist/templates/trellis/gitignore.txt +39 -39
- package/dist/templates/trellis/index.d.ts +12 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +73 -15
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/pool/README.md +12 -3
- package/dist/templates/trellis/pool/plan.md +24 -5
- package/dist/templates/trellis/scripts/common/adapter_middleware.py +158 -0
- package/dist/templates/trellis/scripts/common/artifact_locale.py +12 -3
- package/dist/templates/trellis/scripts/common/cursor_retrieval_env.py +5 -2
- package/dist/templates/trellis/scripts/common/full_quality.py +237 -0
- package/dist/templates/trellis/scripts/common/git.py +6 -0
- package/dist/templates/trellis/scripts/common/git_context.py +17 -5
- package/dist/templates/trellis/scripts/common/kernel_command.py +367 -0
- package/dist/templates/trellis/scripts/common/lite_context.py +280 -0
- package/dist/templates/trellis/scripts/common/ondemand_topology.py +225 -0
- package/dist/templates/trellis/scripts/common/parent_orchestration.py +1 -1
- package/dist/templates/trellis/scripts/common/pool_store.py +16 -4
- package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +2 -2
- package/dist/templates/trellis/scripts/common/session_context.py +28 -2
- package/dist/templates/trellis/scripts/common/task_gates.py +73 -12
- package/dist/templates/trellis/scripts/common/task_map.py +42 -9
- package/dist/templates/trellis/scripts/common/task_store.py +286 -56
- package/dist/templates/trellis/scripts/common/test_adapter_middleware.py +56 -0
- package/dist/templates/trellis/scripts/common/test_depends_mode_block.py +9 -0
- package/dist/templates/trellis/scripts/common/test_full_quality.py +197 -0
- package/dist/templates/trellis/scripts/common/test_kernel_command.py +326 -0
- package/dist/templates/trellis/scripts/common/test_lite_path.py +190 -0
- package/dist/templates/trellis/scripts/common/test_ondemand_topology.py +122 -0
- package/dist/templates/trellis/scripts/common/test_pool_store.py +17 -0
- package/dist/templates/trellis/scripts/common/test_task_store_kernel_patch.py +163 -0
- package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -0
- package/dist/templates/trellis/scripts/get_context.py +1 -0
- package/dist/templates/trellis/scripts/pool.py +5 -0
- package/dist/templates/trellis/scripts/task.py +83 -7
- package/dist/templates/trellis/workflow.md +100 -18
- package/dist/utils/cursor-retrieval-env.d.ts +2 -1
- package/dist/utils/cursor-retrieval-env.d.ts.map +1 -1
- package/dist/utils/cursor-retrieval-env.js +3 -2
- package/dist/utils/cursor-retrieval-env.js.map +1 -1
- package/dist/utils/cursor2plus-residue-cleanup.d.ts +38 -0
- package/dist/utils/cursor2plus-residue-cleanup.d.ts.map +1 -0
- package/dist/utils/cursor2plus-residue-cleanup.js +145 -0
- package/dist/utils/cursor2plus-residue-cleanup.js.map +1 -0
- package/dist/utils/manifest-prune.d.ts.map +1 -1
- package/dist/utils/manifest-prune.js +7 -2
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/p36-upgrade.d.ts +62 -0
- package/dist/utils/p36-upgrade.d.ts.map +1 -0
- package/dist/utils/p36-upgrade.js +160 -0
- package/dist/utils/p36-upgrade.js.map +1 -0
- package/dist/utils/project-capabilities.d.ts +1 -12
- package/dist/utils/project-capabilities.d.ts.map +1 -1
- package/dist/utils/project-capabilities.js +3 -100
- package/dist/utils/project-capabilities.js.map +1 -1
- package/dist/utils/readiness.d.ts +10 -0
- package/dist/utils/readiness.d.ts.map +1 -1
- package/dist/utils/readiness.js +58 -26
- package/dist/utils/readiness.js.map +1 -1
- package/dist/utils/retrieval-agent-instructions.js +1 -1
- package/dist/utils/retrieval-agent-instructions.js.map +1 -1
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +11 -1
- package/dist/utils/template-hash.js.map +1 -1
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
- package/dist/utils/uninstall-scrubbers.js +52 -14
- package/dist/utils/uninstall-scrubbers.js.map +1 -1
- package/dist/utils/update-rollout-report.d.ts +14 -0
- package/dist/utils/update-rollout-report.d.ts.map +1 -1
- package/dist/utils/update-rollout-report.js +1 -0
- package/dist/utils/update-rollout-report.js.map +1 -1
- package/dist/utils/workflow-ownership.d.ts +1 -1
- package/dist/utils/workflow-ownership.js +4 -4
- package/dist/utils/workflow-ownership.js.map +1 -1
- package/package.json +5 -4
- package/scripts/postinstall.js +6 -7
- package/dist/commands/campaign/canvas-render.d.ts +0 -68
- package/dist/commands/campaign/canvas-render.d.ts.map +0 -1
- package/dist/commands/campaign/canvas-render.js +0 -414
- package/dist/commands/campaign/canvas-render.js.map +0 -1
- package/dist/commands/campaign/compose.d.ts +0 -15
- package/dist/commands/campaign/compose.d.ts.map +0 -1
- package/dist/commands/campaign/compose.js +0 -110
- package/dist/commands/campaign/compose.js.map +0 -1
- package/dist/commands/campaign/index.d.ts +0 -13
- package/dist/commands/campaign/index.d.ts.map +0 -1
- package/dist/commands/campaign/index.js +0 -134
- package/dist/commands/campaign/index.js.map +0 -1
- package/dist/commands/campaign/kind-map.d.ts +0 -6
- package/dist/commands/campaign/kind-map.d.ts.map +0 -1
- package/dist/commands/campaign/kind-map.js +0 -21
- package/dist/commands/campaign/kind-map.js.map +0 -1
- package/dist/commands/campaign/mcp-server.d.ts +0 -6
- package/dist/commands/campaign/mcp-server.d.ts.map +0 -1
- package/dist/commands/campaign/mcp-server.js +0 -167
- package/dist/commands/campaign/mcp-server.js.map +0 -1
- package/dist/commands/campaign/render.d.ts +0 -5
- package/dist/commands/campaign/render.d.ts.map +0 -1
- package/dist/commands/campaign/render.js +0 -103
- package/dist/commands/campaign/render.js.map +0 -1
- package/dist/commands/campaign/trellis-load.d.ts +0 -13
- package/dist/commands/campaign/trellis-load.d.ts.map +0 -1
- package/dist/commands/campaign/trellis-load.js +0 -122
- package/dist/commands/campaign/trellis-load.js.map +0 -1
- package/dist/commands/campaign/types.d.ts +0 -61
- package/dist/commands/campaign/types.d.ts.map +0 -1
- package/dist/commands/campaign/types.js +0 -3
- package/dist/commands/campaign/types.js.map +0 -1
- package/dist/commands/goal/index.d.ts +0 -3
- package/dist/commands/goal/index.d.ts.map +0 -1
- package/dist/commands/goal/index.js +0 -159
- package/dist/commands/goal/index.js.map +0 -1
- package/dist/commands/goal/review.d.ts +0 -6
- package/dist/commands/goal/review.d.ts.map +0 -1
- package/dist/commands/goal/review.js +0 -28
- package/dist/commands/goal/review.js.map +0 -1
- package/dist/commands/rpc/client.d.ts +0 -51
- package/dist/commands/rpc/client.d.ts.map +0 -1
- package/dist/commands/rpc/client.js +0 -147
- package/dist/commands/rpc/client.js.map +0 -1
- package/dist/commands/rpc/index.d.ts +0 -10
- package/dist/commands/rpc/index.d.ts.map +0 -1
- package/dist/commands/rpc/index.js +0 -157
- package/dist/commands/rpc/index.js.map +0 -1
- package/dist/commands/rpc/serve.d.ts +0 -23
- package/dist/commands/rpc/serve.d.ts.map +0 -1
- package/dist/commands/rpc/serve.js +0 -60
- package/dist/commands/rpc/serve.js.map +0 -1
- package/dist/commands/rpc/smoke.d.ts +0 -16
- package/dist/commands/rpc/smoke.d.ts.map +0 -1
- package/dist/commands/rpc/smoke.js +0 -92
- package/dist/commands/rpc/smoke.js.map +0 -1
- package/dist/commands/sdk/index.d.ts +0 -9
- package/dist/commands/sdk/index.d.ts.map +0 -1
- package/dist/commands/sdk/index.js +0 -58
- package/dist/commands/sdk/index.js.map +0 -1
- package/dist/commands/sdk/run.d.ts +0 -42
- package/dist/commands/sdk/run.d.ts.map +0 -1
- package/dist/commands/sdk/run.js +0 -220
- package/dist/commands/sdk/run.js.map +0 -1
- package/dist/commands/sdk/status.d.ts +0 -14
- package/dist/commands/sdk/status.d.ts.map +0 -1
- package/dist/commands/sdk/status.js +0 -69
- package/dist/commands/sdk/status.js.map +0 -1
- package/dist/configurators/cursor2plus-local.d.ts +0 -6
- package/dist/configurators/cursor2plus-local.d.ts.map +0 -1
- package/dist/configurators/cursor2plus-local.js +0 -21
- package/dist/configurators/cursor2plus-local.js.map +0 -1
- package/dist/goal/action-packet.d.ts +0 -89
- package/dist/goal/action-packet.d.ts.map +0 -1
- package/dist/goal/action-packet.js +0 -113
- package/dist/goal/action-packet.js.map +0 -1
- package/dist/goal/audit.d.ts +0 -14
- package/dist/goal/audit.d.ts.map +0 -1
- package/dist/goal/audit.js +0 -12
- package/dist/goal/audit.js.map +0 -1
- package/dist/goal/constants.d.ts +0 -26
- package/dist/goal/constants.d.ts.map +0 -1
- package/dist/goal/constants.js +0 -24
- package/dist/goal/constants.js.map +0 -1
- package/dist/goal/paths.d.ts +0 -8
- package/dist/goal/paths.d.ts.map +0 -1
- package/dist/goal/paths.js +0 -36
- package/dist/goal/paths.js.map +0 -1
- package/dist/goal/preflight.d.ts +0 -21
- package/dist/goal/preflight.d.ts.map +0 -1
- package/dist/goal/preflight.js +0 -97
- package/dist/goal/preflight.js.map +0 -1
- package/dist/goal/review-seam.d.ts +0 -44
- package/dist/goal/review-seam.d.ts.map +0 -1
- package/dist/goal/review-seam.js +0 -153
- package/dist/goal/review-seam.js.map +0 -1
- package/dist/goal/reviewer-rules.d.ts +0 -13
- package/dist/goal/reviewer-rules.d.ts.map +0 -1
- package/dist/goal/reviewer-rules.js +0 -153
- package/dist/goal/reviewer-rules.js.map +0 -1
- package/dist/goal/reviewer.d.ts +0 -9
- package/dist/goal/reviewer.d.ts.map +0 -1
- package/dist/goal/reviewer.js +0 -54
- package/dist/goal/reviewer.js.map +0 -1
- package/dist/goal/runtime.d.ts +0 -32
- package/dist/goal/runtime.d.ts.map +0 -1
- package/dist/goal/runtime.js +0 -242
- package/dist/goal/runtime.js.map +0 -1
- package/dist/goal/sdk-client.d.ts +0 -10
- package/dist/goal/sdk-client.d.ts.map +0 -1
- package/dist/goal/sdk-client.js +0 -34
- package/dist/goal/sdk-client.js.map +0 -1
- package/dist/goal/state.d.ts +0 -40
- package/dist/goal/state.d.ts.map +0 -1
- package/dist/goal/state.js +0 -37
- package/dist/goal/state.js.map +0 -1
- package/dist/goal/task-triggers.d.ts +0 -15
- package/dist/goal/task-triggers.d.ts.map +0 -1
- package/dist/goal/task-triggers.js +0 -23
- package/dist/goal/task-triggers.js.map +0 -1
- package/dist/goal/walls.d.ts +0 -12
- package/dist/goal/walls.d.ts.map +0 -1
- package/dist/goal/walls.js +0 -65
- package/dist/goal/walls.js.map +0 -1
- package/dist/goal/worker-prompt.d.ts +0 -13
- package/dist/goal/worker-prompt.d.ts.map +0 -1
- package/dist/goal/worker-prompt.js +0 -41
- package/dist/goal/worker-prompt.js.map +0 -1
- package/dist/goal/worker.d.ts +0 -42
- package/dist/goal/worker.d.ts.map +0 -1
- package/dist/goal/worker.js +0 -180
- package/dist/goal/worker.js.map +0 -1
- package/dist/templates/common/bundled-skills/cstl-cursor2plus-setup/SKILL.md +0 -63
- package/dist/templates/cursor/commands/cursor2plus-setup.md +0 -82
- package/dist/templates/cursor/rules/cstl-cursor-modes.mdc +0 -32
- package/dist/templates/cursor/rules/cstl-session-rename.mdc +0 -31
- package/dist/templates/cursor/rules/cstl-subagent-dispatch.mdc +0 -34
- package/dist/templates/cursor/rules/cstl-triage.mdc +0 -36
- package/dist/templates/cursor/rules/retrieval-routing.mdc +0 -78
- package/dist/templates/trellis/local/README.md +0 -85
- package/dist/templates/trellis/local/config.local.json.example +0 -6
- package/dist/templates/trellis/local/index.d.ts +0 -8
- package/dist/templates/trellis/local/index.d.ts.map +0 -1
- package/dist/templates/trellis/local/index.js +0 -15
- package/dist/templates/trellis/local/index.js.map +0 -1
- package/dist/templates/trellis/local/patch_wpelc8.py +0 -421
- package/dist/templates/trellis/local/smoke.py +0 -94
- package/dist/templates/trellis/local/subagent-models.json.example +0 -4
- package/dist/templates/trellis/local/trellis-task-models.json.example +0 -10
- package/dist/templates/trellis/local/trellis-task-models.json5.example +0 -45
- package/dist/templates/trellis/local/trellis_task_models_config.py +0 -200
- package/dist/utils/cursor-sdk-gate.d.ts +0 -10
- package/dist/utils/cursor-sdk-gate.d.ts.map +0 -1
- package/dist/utils/cursor-sdk-gate.js +0 -32
- package/dist/utils/cursor-sdk-gate.js.map +0 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Personal Lite context pack (P29 context-progressive / P30 Stage 3).
|
|
3
|
+
|
|
4
|
+
Phase-assembled Definition / Evidence pack with a hard budget. Unactivated
|
|
5
|
+
On-demand modules (Parent / VCS / retrieval-extended) and a full workflow.md
|
|
6
|
+
dump are rejected instead of silently stuffed into the pack.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
LITE_PACK_VERSION = 1
|
|
14
|
+
LITE_PACK_SOURCE = "personal-lite-context-pack"
|
|
15
|
+
|
|
16
|
+
LITE_BASELINE_MODULES = (
|
|
17
|
+
"intake-basic",
|
|
18
|
+
"define-basic",
|
|
19
|
+
"approval-personal",
|
|
20
|
+
"execute-agent",
|
|
21
|
+
"verify-basic",
|
|
22
|
+
"close-basic",
|
|
23
|
+
"context-progressive",
|
|
24
|
+
"observability-local",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
LITE_BLOCKED_ON_DEMAND_MODULES = (
|
|
28
|
+
"parent-child",
|
|
29
|
+
"vcs-integration",
|
|
30
|
+
"personal-memory",
|
|
31
|
+
"retention-storage",
|
|
32
|
+
"retrieval-extended",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
LITE_RETRIEVAL_INTENTS = ("exact", "semantic", "structural", "external")
|
|
36
|
+
|
|
37
|
+
LITE_DEFAULT_MAX_ITEMS = 8
|
|
38
|
+
LITE_DEFAULT_MAX_ESTIMATED_TOKENS = 4000
|
|
39
|
+
LITE_CHARS_PER_TOKEN = 4
|
|
40
|
+
LITE_MIN_ITEM_TOKENS = 40
|
|
41
|
+
|
|
42
|
+
PHASE_ROLES: dict[str, tuple[str, ...]] = {
|
|
43
|
+
"open": ("triage", "retrieval-router"),
|
|
44
|
+
"define": ("triage", "definition", "retrieval-router"),
|
|
45
|
+
"approve": ("triage", "definition", "approval", "retrieval-router"),
|
|
46
|
+
"execute": ("definition", "approval", "retrieval-router"),
|
|
47
|
+
"verify": ("definition", "evidence", "retrieval-router"),
|
|
48
|
+
"integrate": ("definition", "evidence", "retrieval-router"),
|
|
49
|
+
"close": ("definition", "evidence", "outcome", "retrieval-router"),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ROLE_MODULE = {
|
|
53
|
+
"triage": "intake-basic",
|
|
54
|
+
"definition": "define-basic",
|
|
55
|
+
"approval": "approval-personal",
|
|
56
|
+
"evidence": "verify-basic",
|
|
57
|
+
"outcome": "close-basic",
|
|
58
|
+
"retrieval-router": "context-progressive",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
STATUS_TO_PHASE = {
|
|
62
|
+
"planning": "define",
|
|
63
|
+
"in_progress": "execute",
|
|
64
|
+
"review": "verify",
|
|
65
|
+
"completed": "close",
|
|
66
|
+
"done": "close",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class LiteContextPackError(RuntimeError):
|
|
71
|
+
"""Lite pack refused to assemble (budget, On-demand, or workflow dump)."""
|
|
72
|
+
|
|
73
|
+
def __init__(self, code: str, message: str) -> None:
|
|
74
|
+
super().__init__(message)
|
|
75
|
+
self.code = code
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def kernel_phase_from_legacy_status(status: str | None) -> str:
|
|
79
|
+
"""Project user-visible task.json.status onto a Kernel phase name."""
|
|
80
|
+
if not status:
|
|
81
|
+
return "open"
|
|
82
|
+
return STATUS_TO_PHASE.get(status, "open")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def estimate_lite_tokens(text: str) -> int:
|
|
86
|
+
if not text:
|
|
87
|
+
return LITE_MIN_ITEM_TOKENS
|
|
88
|
+
return max(LITE_MIN_ITEM_TOKENS, len(text) // LITE_CHARS_PER_TOKEN + 20)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _normalize_path(value: object) -> str:
|
|
92
|
+
if not isinstance(value, str) or not value.strip():
|
|
93
|
+
return ""
|
|
94
|
+
return value.replace("\\", "/")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _is_workflow_dump(role: str, path: str) -> bool:
|
|
98
|
+
if role == "workflow-full":
|
|
99
|
+
return True
|
|
100
|
+
base = path.rsplit("/", 1)[-1]
|
|
101
|
+
return base == "workflow.md"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _is_blocked(name: str) -> bool:
|
|
105
|
+
return name in LITE_BLOCKED_ON_DEMAND_MODULES
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def build_lite_context_pack(
|
|
109
|
+
*,
|
|
110
|
+
phase: str,
|
|
111
|
+
artifacts: list[dict[str, Any]] | None = None,
|
|
112
|
+
activated_modules: list[str] | None = None,
|
|
113
|
+
include_full_workflow: bool = False,
|
|
114
|
+
max_items: int | None = None,
|
|
115
|
+
max_estimated_tokens: int | None = None,
|
|
116
|
+
) -> dict[str, Any]:
|
|
117
|
+
"""Assemble a bounded Lite pack. Raises LiteContextPackError on refuse."""
|
|
118
|
+
if phase not in PHASE_ROLES:
|
|
119
|
+
raise LiteContextPackError("WORKFLOW_DUMP", f"invalid Lite pack phase: {phase}")
|
|
120
|
+
if include_full_workflow:
|
|
121
|
+
raise LiteContextPackError(
|
|
122
|
+
"WORKFLOW_DUMP",
|
|
123
|
+
"Lite context pack refuses a full workflow.md dump",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
activated = [
|
|
127
|
+
name for name in (activated_modules or []) if isinstance(name, str) and name.strip()
|
|
128
|
+
]
|
|
129
|
+
for name in activated:
|
|
130
|
+
if _is_blocked(name):
|
|
131
|
+
raise LiteContextPackError(
|
|
132
|
+
"ON_DEMAND_INACTIVE",
|
|
133
|
+
f"Lite pack excludes unactivated On-demand module: {name}",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
item_cap = LITE_DEFAULT_MAX_ITEMS if max_items is None else max_items
|
|
137
|
+
token_cap = (
|
|
138
|
+
LITE_DEFAULT_MAX_ESTIMATED_TOKENS
|
|
139
|
+
if max_estimated_tokens is None
|
|
140
|
+
else max_estimated_tokens
|
|
141
|
+
)
|
|
142
|
+
allowed_roles = set(PHASE_ROLES[phase])
|
|
143
|
+
|
|
144
|
+
candidates: list[dict[str, Any]] = [
|
|
145
|
+
{
|
|
146
|
+
"role": "retrieval-router",
|
|
147
|
+
"module": "context-progressive",
|
|
148
|
+
"path": "capability://retrieval-router",
|
|
149
|
+
"estimatedTokens": LITE_MIN_ITEM_TOKENS,
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
for artifact in artifacts or []:
|
|
154
|
+
if not isinstance(artifact, dict):
|
|
155
|
+
continue
|
|
156
|
+
role = str(artifact.get("role") or "")
|
|
157
|
+
path = _normalize_path(artifact.get("path"))
|
|
158
|
+
if _is_workflow_dump(role, path):
|
|
159
|
+
raise LiteContextPackError(
|
|
160
|
+
"WORKFLOW_DUMP",
|
|
161
|
+
"Lite context pack refuses a full workflow.md dump",
|
|
162
|
+
)
|
|
163
|
+
module_name = str(artifact.get("module") or ROLE_MODULE.get(role) or "")
|
|
164
|
+
if _is_blocked(module_name) or _is_blocked(role):
|
|
165
|
+
raise LiteContextPackError(
|
|
166
|
+
"ON_DEMAND_INACTIVE",
|
|
167
|
+
f"Lite pack excludes unactivated On-demand module: {module_name or role}",
|
|
168
|
+
)
|
|
169
|
+
if role not in allowed_roles:
|
|
170
|
+
continue
|
|
171
|
+
candidates.append(
|
|
172
|
+
{
|
|
173
|
+
"role": role,
|
|
174
|
+
"module": module_name or "define-basic",
|
|
175
|
+
"path": path,
|
|
176
|
+
"estimatedTokens": estimate_lite_tokens(str(artifact.get("content") or "")),
|
|
177
|
+
}
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
selected: list[dict[str, Any]] = []
|
|
181
|
+
omitted: list[dict[str, str]] = []
|
|
182
|
+
warnings: list[str] = []
|
|
183
|
+
estimated_tokens = 0
|
|
184
|
+
budget_exceeded = False
|
|
185
|
+
|
|
186
|
+
for item in candidates:
|
|
187
|
+
would_exceed_items = len(selected) >= item_cap
|
|
188
|
+
next_tokens = int(item["estimatedTokens"])
|
|
189
|
+
would_exceed_tokens = estimated_tokens + next_tokens > token_cap
|
|
190
|
+
if would_exceed_items or would_exceed_tokens:
|
|
191
|
+
budget_exceeded = True
|
|
192
|
+
reason = (
|
|
193
|
+
"outside token budget after higher-ranked Lite surfaces"
|
|
194
|
+
if would_exceed_tokens
|
|
195
|
+
else "outside item budget after higher-ranked Lite surfaces"
|
|
196
|
+
)
|
|
197
|
+
omitted.append({"role": item["role"], "path": item["path"], "reason": reason})
|
|
198
|
+
continue
|
|
199
|
+
selected.append(item)
|
|
200
|
+
estimated_tokens += next_tokens
|
|
201
|
+
|
|
202
|
+
if budget_exceeded:
|
|
203
|
+
if any(
|
|
204
|
+
row["path"].endswith("workflow.md") or row["role"] == "workflow-full"
|
|
205
|
+
for row in omitted
|
|
206
|
+
):
|
|
207
|
+
raise LiteContextPackError(
|
|
208
|
+
"BUDGET_EXCEEDED",
|
|
209
|
+
"Lite context pack over budget; refusing silent full workflow dump",
|
|
210
|
+
)
|
|
211
|
+
warnings.append("budget limits caused Lite pack omission")
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
"version": LITE_PACK_VERSION,
|
|
215
|
+
"source": LITE_PACK_SOURCE,
|
|
216
|
+
"phase": phase,
|
|
217
|
+
"budget": {
|
|
218
|
+
"maxItems": item_cap,
|
|
219
|
+
"maxEstimatedTokens": token_cap,
|
|
220
|
+
"estimatedTokens": estimated_tokens,
|
|
221
|
+
"itemsUsed": len(selected),
|
|
222
|
+
},
|
|
223
|
+
"selected": selected,
|
|
224
|
+
"omitted": omitted,
|
|
225
|
+
"modules": {
|
|
226
|
+
"baseline": list(LITE_BASELINE_MODULES),
|
|
227
|
+
"activatedOnDemand": activated,
|
|
228
|
+
},
|
|
229
|
+
"retrievalIntents": list(LITE_RETRIEVAL_INTENTS),
|
|
230
|
+
"warnings": warnings,
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def collect_lite_artifacts(task_dir) -> list[dict[str, Any]]:
|
|
235
|
+
"""Read Definition / Evidence surfaces when present (capped per file)."""
|
|
236
|
+
from pathlib import Path
|
|
237
|
+
|
|
238
|
+
root = Path(task_dir)
|
|
239
|
+
artifacts: list[dict[str, Any]] = []
|
|
240
|
+
for role, name in (("definition", "prd.md"), ("evidence", "verify.md")):
|
|
241
|
+
path = root / name
|
|
242
|
+
if not path.is_file():
|
|
243
|
+
continue
|
|
244
|
+
try:
|
|
245
|
+
content = path.read_text(encoding="utf-8")
|
|
246
|
+
except OSError:
|
|
247
|
+
continue
|
|
248
|
+
artifacts.append(
|
|
249
|
+
{
|
|
250
|
+
"role": role,
|
|
251
|
+
"path": name,
|
|
252
|
+
"content": content[:12_000],
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
return artifacts
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def get_lite_context_json(repo_root=None) -> dict[str, Any]:
|
|
259
|
+
"""Build a Lite pack for the selected task, or an Open-phase empty pack."""
|
|
260
|
+
from .paths import get_repo_root, get_selected_task
|
|
261
|
+
from .tasks import load_task
|
|
262
|
+
from .task_utils import resolve_task_dir
|
|
263
|
+
|
|
264
|
+
root = repo_root or get_repo_root()
|
|
265
|
+
selected = get_selected_task(root)
|
|
266
|
+
phase = "open"
|
|
267
|
+
artifacts: list[dict[str, Any]] = []
|
|
268
|
+
if selected:
|
|
269
|
+
task_dir = resolve_task_dir(selected, root)
|
|
270
|
+
info = load_task(task_dir) if task_dir.is_dir() else None
|
|
271
|
+
status = getattr(info, "status", None) if info is not None else None
|
|
272
|
+
if status is None and task_dir.is_dir():
|
|
273
|
+
from .io import read_json
|
|
274
|
+
from .paths import FILE_TASK_JSON
|
|
275
|
+
|
|
276
|
+
data = read_json(task_dir / FILE_TASK_JSON) or {}
|
|
277
|
+
status = data.get("status") if isinstance(data, dict) else None
|
|
278
|
+
phase = kernel_phase_from_legacy_status(status if isinstance(status, str) else None)
|
|
279
|
+
artifacts = collect_lite_artifacts(task_dir)
|
|
280
|
+
return build_lite_context_pack(phase=phase, artifacts=artifacts)
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Stage 5 Topology + typed graph + On-demand (Python projection).
|
|
2
|
+
|
|
3
|
+
Kernel extras remain authoritative. This module renders and validates the
|
|
4
|
+
same contract; it does not schedule workers or write a second graph.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
STAGE5_SOURCE = "stage5-ondemand-topology"
|
|
12
|
+
STAGE5_SCHEMA_VERSION = 1
|
|
13
|
+
|
|
14
|
+
STAGE5_ONDEMAND_MODULES = (
|
|
15
|
+
"candidate-pool",
|
|
16
|
+
"define-extended",
|
|
17
|
+
"worker-orchestration",
|
|
18
|
+
"parent-child",
|
|
19
|
+
"debug-recovery",
|
|
20
|
+
"integration-handoff",
|
|
21
|
+
"session-transfer",
|
|
22
|
+
"spec-learning",
|
|
23
|
+
"vcs-integration",
|
|
24
|
+
"personal-memory",
|
|
25
|
+
"retention-storage",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
ONDEMAND_OWNERS = {
|
|
29
|
+
"integration-handoff": "parent-child",
|
|
30
|
+
"session-transfer": "session-transfer",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
LITE_DORMANT_ONDEMAND = (
|
|
34
|
+
"parent-child",
|
|
35
|
+
"vcs-integration",
|
|
36
|
+
"personal-memory",
|
|
37
|
+
"retention-storage",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TopologyError(RuntimeError):
|
|
42
|
+
"""Topology / On-demand contract rejected a request."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def capture_candidate(summary: str) -> dict[str, Any]:
|
|
46
|
+
return {
|
|
47
|
+
"kind": "candidate",
|
|
48
|
+
"summary": (summary or "").strip(),
|
|
49
|
+
"task_created": False,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def propose_decompose(parent_id: str, slices: list[str]) -> dict[str, Any]:
|
|
54
|
+
return {
|
|
55
|
+
"parent_id": parent_id,
|
|
56
|
+
"slices": _unique(slices),
|
|
57
|
+
"confirmed": False,
|
|
58
|
+
"children_created": [],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def confirm_decompose(proposal: dict[str, Any]) -> dict[str, Any]:
|
|
63
|
+
slices = _unique(proposal.get("slices") or [])
|
|
64
|
+
return {
|
|
65
|
+
**proposal,
|
|
66
|
+
"confirmed": True,
|
|
67
|
+
"children_created": slices,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def apply_retention(outcome: str) -> str:
|
|
72
|
+
return outcome
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def default_topology(
|
|
76
|
+
parent: str | None = None,
|
|
77
|
+
children: list[str] | None = None,
|
|
78
|
+
) -> dict[str, Any]:
|
|
79
|
+
"""Seed Topology. ``parent_id`` + empty children stays ``single``."""
|
|
80
|
+
kids = _unique(children or [])
|
|
81
|
+
parent_id = (parent or "").strip() or None
|
|
82
|
+
return {
|
|
83
|
+
"schema_version": STAGE5_SCHEMA_VERSION,
|
|
84
|
+
"kind": "parent-child" if kids else "single",
|
|
85
|
+
"parent_id": parent_id,
|
|
86
|
+
"children": kids,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def assign_parent(topology: dict[str, Any], parent_id: str) -> dict[str, Any]:
|
|
91
|
+
current = topology.get("parent_id")
|
|
92
|
+
next_parent = (parent_id or "").strip()
|
|
93
|
+
if not next_parent:
|
|
94
|
+
raise TopologyError("parent_id must be a non-empty string")
|
|
95
|
+
if current and current != next_parent:
|
|
96
|
+
raise TopologyError(
|
|
97
|
+
f"single-parent tree: second parent rejected ({current} vs {next_parent})"
|
|
98
|
+
)
|
|
99
|
+
children = list(topology.get("children") or [])
|
|
100
|
+
return {
|
|
101
|
+
"schema_version": STAGE5_SCHEMA_VERSION,
|
|
102
|
+
"kind": "parent-child" if children else "single",
|
|
103
|
+
"parent_id": next_parent,
|
|
104
|
+
"children": children,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def assert_integration_authority(actor_role: str) -> None:
|
|
109
|
+
if actor_role == "parent":
|
|
110
|
+
return
|
|
111
|
+
raise TopologyError(
|
|
112
|
+
"Integration authority stays with Parent; Child cannot integrate-child"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def expand_depends_on_to_requires(
|
|
117
|
+
graph: dict[str, Any],
|
|
118
|
+
from_id: str,
|
|
119
|
+
depends_on: list[str],
|
|
120
|
+
) -> dict[str, Any]:
|
|
121
|
+
edges = list(graph.get("edges") or [])
|
|
122
|
+
for dep in _unique(depends_on):
|
|
123
|
+
if any(
|
|
124
|
+
edge.get("from") == from_id
|
|
125
|
+
and edge.get("to") == dep
|
|
126
|
+
and edge.get("type") == "requires"
|
|
127
|
+
for edge in edges
|
|
128
|
+
if isinstance(edge, dict)
|
|
129
|
+
):
|
|
130
|
+
continue
|
|
131
|
+
edges.append({"from": from_id, "to": dep, "type": "requires"})
|
|
132
|
+
return {"schema_version": STAGE5_SCHEMA_VERSION, "edges": edges}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def unmet_requires(
|
|
136
|
+
graph: dict[str, Any],
|
|
137
|
+
satisfied: list[str],
|
|
138
|
+
from_id: str | None = None,
|
|
139
|
+
) -> list[str]:
|
|
140
|
+
done = set(satisfied)
|
|
141
|
+
missing: list[str] = []
|
|
142
|
+
for edge in graph.get("edges") or []:
|
|
143
|
+
if not isinstance(edge, dict) or edge.get("type") != "requires":
|
|
144
|
+
continue
|
|
145
|
+
if from_id and edge.get("from") != from_id:
|
|
146
|
+
continue
|
|
147
|
+
target = edge.get("to")
|
|
148
|
+
if isinstance(target, str) and target not in done and target not in missing:
|
|
149
|
+
missing.append(target)
|
|
150
|
+
return missing
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def project_task_map_graph(
|
|
154
|
+
topology: dict[str, Any],
|
|
155
|
+
graph: dict[str, Any],
|
|
156
|
+
) -> dict[str, Any]:
|
|
157
|
+
children = []
|
|
158
|
+
for child_id in topology.get("children") or []:
|
|
159
|
+
if not isinstance(child_id, str):
|
|
160
|
+
continue
|
|
161
|
+
depends = [
|
|
162
|
+
edge.get("to")
|
|
163
|
+
for edge in graph.get("edges") or []
|
|
164
|
+
if isinstance(edge, dict)
|
|
165
|
+
and edge.get("type") == "requires"
|
|
166
|
+
and edge.get("from") == child_id
|
|
167
|
+
]
|
|
168
|
+
children.append({"id": child_id, "depends_on": depends})
|
|
169
|
+
return {
|
|
170
|
+
"graph_authority": "kernel-extras",
|
|
171
|
+
"topology_kind": topology.get("kind") or "single",
|
|
172
|
+
"parent_id": topology.get("parent_id"),
|
|
173
|
+
"children": children,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def apply_kernel_graph_projection(data: dict[str, Any], extras: dict[str, Any]) -> None:
|
|
178
|
+
"""Stamp Parent task-map frontmatter from Kernel extras. Projection only."""
|
|
179
|
+
topology = extras.get("topology")
|
|
180
|
+
graph = extras.get("dependency_graph")
|
|
181
|
+
if not isinstance(topology, dict) and not isinstance(graph, dict):
|
|
182
|
+
return
|
|
183
|
+
projection = project_task_map_graph(
|
|
184
|
+
topology if isinstance(topology, dict) else {},
|
|
185
|
+
graph if isinstance(graph, dict) else {"edges": []},
|
|
186
|
+
)
|
|
187
|
+
data["graph_authority"] = "kernel-extras"
|
|
188
|
+
data["topology_kind"] = projection["topology_kind"]
|
|
189
|
+
if projection.get("parent_id"):
|
|
190
|
+
data["parent_id"] = projection["parent_id"]
|
|
191
|
+
existing = {
|
|
192
|
+
child.get("id"): child
|
|
193
|
+
for child in data.get("children") or []
|
|
194
|
+
if isinstance(child, dict)
|
|
195
|
+
}
|
|
196
|
+
children = data.setdefault("children", [])
|
|
197
|
+
if not isinstance(children, list):
|
|
198
|
+
data["children"] = []
|
|
199
|
+
children = data["children"]
|
|
200
|
+
for child in projection["children"]:
|
|
201
|
+
current = existing.get(child["id"])
|
|
202
|
+
if current is None:
|
|
203
|
+
current = {"id": child["id"], "state": "open", "depends_on": []}
|
|
204
|
+
children.append(current)
|
|
205
|
+
existing[child["id"]] = current
|
|
206
|
+
current["depends_on"] = child["depends_on"]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def resident_on_demand_modules(extras: dict[str, Any]) -> list[str]:
|
|
210
|
+
modules = extras.get("ondemand_modules")
|
|
211
|
+
if not isinstance(modules, dict):
|
|
212
|
+
return []
|
|
213
|
+
active = modules.get("active") or []
|
|
214
|
+
return [name for name in active if isinstance(name, str)]
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _unique(values: list[Any]) -> list[str]:
|
|
218
|
+
out: list[str] = []
|
|
219
|
+
for value in values:
|
|
220
|
+
if not isinstance(value, str):
|
|
221
|
+
continue
|
|
222
|
+
item = value.strip()
|
|
223
|
+
if item and item not in out:
|
|
224
|
+
out.append(item)
|
|
225
|
+
return out
|
|
@@ -271,7 +271,7 @@ def build_child_prompt(
|
|
|
271
271
|
f"Selected task: {child_rel}",
|
|
272
272
|
"",
|
|
273
273
|
"Delivery mode: subagent (Cursor)",
|
|
274
|
-
"- **Default:** Parent session dispatches **Task** with `subagent_type=cstl-implement` and this prompt as the task description (writable sub-agent).
|
|
274
|
+
"- **Default:** Parent session dispatches **Task** with `subagent_type=cstl-implement` and this prompt as the task description (writable sub-agent). Native Cursor `model:` frontmatter still applies. Cursor++ / cstl-byok are not Native install surfaces.",
|
|
275
275
|
"- **Exception:** If parent `child-prompts.md` or the user names this child for a **separate writable Agent chat**, open a new Agent session, pick the model manually, paste this prompt — do not use Task from Parent.",
|
|
276
276
|
"- Parent retains `review-child` / `integrate-child`. Child must not nest further `cstl-research` / `cstl-implement` / `cstl-check` Task dispatches.",
|
|
277
277
|
f"- Parent orchestration: `execution_topology={topo}`, `merge_limit={merge_lim}`.",
|
|
@@ -12,7 +12,7 @@ Frontmatter writes splice a re-serialized header between the raw `---`
|
|
|
12
12
|
fences so the body is preserved byte-for-byte (including the blank line
|
|
13
13
|
after the closing fence and the final newline), and key order stays stable:
|
|
14
14
|
known keys first (id, title, status, type, locale, created, approved,
|
|
15
|
-
linked_tasks), unknown keys keep their original relative order.
|
|
15
|
+
priority, linked_tasks), unknown keys keep their original relative order.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
@@ -22,7 +22,8 @@ from dataclasses import dataclass, field
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
|
|
24
24
|
from .artifact_search import parse_frontmatter, split_frontmatter
|
|
25
|
-
from .io import read_json
|
|
25
|
+
from .io import read_json
|
|
26
|
+
from .kernel_command import kernel_expected_revision, kernel_patch, kernel_projection_extras
|
|
26
27
|
from .paths import get_repo_root, get_tasks_dir
|
|
27
28
|
|
|
28
29
|
POOL_STATUSES = frozenset({"inbox", "review", "accepted", "rejected", "rework"})
|
|
@@ -39,6 +40,7 @@ KNOWN_FRONTMATTER_KEY_ORDER = (
|
|
|
39
40
|
"locale",
|
|
40
41
|
"created",
|
|
41
42
|
"approved",
|
|
43
|
+
"priority",
|
|
42
44
|
LINKED_TASKS_KEY,
|
|
43
45
|
)
|
|
44
46
|
|
|
@@ -328,7 +330,7 @@ def _iter_task_dirs(tasks_dir: Path):
|
|
|
328
330
|
|
|
329
331
|
|
|
330
332
|
def _task_meta_with_pool_items(task_dir: Path, item_id: str, *, remove: bool) -> bool:
|
|
331
|
-
"""Add/remove item_id in task.json meta.pool_items
|
|
333
|
+
"""Add/remove item_id in task.json meta.pool_items via Kernel patch."""
|
|
332
334
|
data = read_json(task_dir / "task.json")
|
|
333
335
|
if not isinstance(data, dict):
|
|
334
336
|
return False
|
|
@@ -346,7 +348,17 @@ def _task_meta_with_pool_items(task_dir: Path, item_id: str, *, remove: bool) ->
|
|
|
346
348
|
pool_items.append(item_id)
|
|
347
349
|
meta[POOL_ITEMS_KEY] = pool_items
|
|
348
350
|
data[META_KEY] = meta
|
|
349
|
-
|
|
351
|
+
action = "unlink" if remove else "link"
|
|
352
|
+
kernel_patch(
|
|
353
|
+
task_dir,
|
|
354
|
+
data,
|
|
355
|
+
kernel_projection_extras(data),
|
|
356
|
+
expected_revision=kernel_expected_revision(task_dir),
|
|
357
|
+
actor="pool.py link" if not remove else "pool.py unlink",
|
|
358
|
+
idempotency_key=f"patch:pool-{action}:{task_dir.name}:{item_id}",
|
|
359
|
+
evidence="pool_store._task_meta_with_pool_items",
|
|
360
|
+
)
|
|
361
|
+
return True
|
|
350
362
|
|
|
351
363
|
|
|
352
364
|
def link_item_task(
|
|
@@ -312,11 +312,11 @@ def render_agent_instructions(
|
|
|
312
312
|
label = (
|
|
313
313
|
"**语义合规(cursorEnv 未知,保守 BYOK):**"
|
|
314
314
|
if cursor_env == ENV_UNKNOWN
|
|
315
|
-
else "**语义合规(
|
|
315
|
+
else "**语义合规(BYOK env):**"
|
|
316
316
|
)
|
|
317
317
|
if locale != "zh":
|
|
318
318
|
lines.append(
|
|
319
|
-
"**Semantic compliance (
|
|
319
|
+
"**Semantic compliance (BYOK env):**"
|
|
320
320
|
if cursor_env != ENV_UNKNOWN
|
|
321
321
|
else "**Semantic compliance (cursorEnv unknown, conservative BYOK):**"
|
|
322
322
|
)
|
|
@@ -22,7 +22,6 @@ from pathlib import Path
|
|
|
22
22
|
from .active_task import resolve_context_key
|
|
23
23
|
from .artifact_locale import artifact_locale_summary, resolve_artifact_locale
|
|
24
24
|
from .config import get_git_packages, get_journal_snippet_enabled
|
|
25
|
-
from .session_memory import get_latest_journal_summary
|
|
26
25
|
from .git import run_git
|
|
27
26
|
from .packages_context import get_packages_section
|
|
28
27
|
from .tasks import iter_active_tasks, load_task, get_all_statuses, children_progress
|
|
@@ -48,7 +47,30 @@ from .paths import (
|
|
|
48
47
|
# =============================================================================
|
|
49
48
|
|
|
50
49
|
_PACKAGE_NAME = "@blxzer/cursor-trellis"
|
|
51
|
-
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _resolve_python_command() -> str:
|
|
53
|
+
"""Pick the platform-appropriate python command.
|
|
54
|
+
|
|
55
|
+
Mirrors test resolvePython() (win32 prefers python, POSIX prefers
|
|
56
|
+
python3) so rendered guidance matches what integration tests assert.
|
|
57
|
+
"""
|
|
58
|
+
candidates = ["python", "python3"] if os.name == "nt" else ["python3", "python"]
|
|
59
|
+
for exe in candidates:
|
|
60
|
+
try:
|
|
61
|
+
subprocess.run(
|
|
62
|
+
[exe, "--version"],
|
|
63
|
+
capture_output=True,
|
|
64
|
+
timeout=2,
|
|
65
|
+
check=True,
|
|
66
|
+
)
|
|
67
|
+
return exe
|
|
68
|
+
except (OSError, subprocess.SubprocessError):
|
|
69
|
+
continue
|
|
70
|
+
return "python"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
_PYTHON_CMD = _resolve_python_command()
|
|
52
74
|
_UPDATE_CHECK_TIMEOUT_SECONDS = 1.0
|
|
53
75
|
_VERSION_RE = re.compile(
|
|
54
76
|
r"^\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?\s*$"
|
|
@@ -842,6 +864,10 @@ def _resolve_journal_snippet(
|
|
|
842
864
|
) -> str:
|
|
843
865
|
if not journal_file or not developer:
|
|
844
866
|
return ""
|
|
867
|
+
try:
|
|
868
|
+
from .session_memory import get_latest_journal_summary
|
|
869
|
+
except ImportError:
|
|
870
|
+
return ""
|
|
845
871
|
return get_latest_journal_summary(journal_file, repo_root, developer)
|
|
846
872
|
|
|
847
873
|
|