@blxzer/cursor-trellis 0.4.1 → 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 +49 -0
- package/README.md +25 -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 +144 -41
- 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 +12 -45
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/constants/paths.d.ts +13 -0
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +17 -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-check/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/hooks-and-settings.md +2 -2
- package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/overview.md +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 +6 -5
- 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/common/skills/brainstorm.md +2 -2
- package/dist/templates/cursor/agents/cstl-check.md +2 -2
- package/dist/templates/cursor/agents/cstl-implement.md +1 -1
- package/dist/templates/cursor/agents/cstl-research.md +2 -2
- 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 +36 -3
- package/dist/templates/markdown/{spec/guides → framework}/cursor-native-modes-guide.md.txt +13 -10
- package/dist/templates/markdown/{spec/guides → framework}/cursor-subagent-policy.md.txt +63 -127
- package/dist/templates/markdown/framework/dogfood-only-surfaces.md.txt +29 -0
- package/dist/templates/markdown/{spec/guides → framework}/execution-strategy.md.txt +2 -0
- package/dist/templates/markdown/framework/index.md.txt +31 -0
- package/dist/templates/markdown/framework/internal-skills-cursor-reachability.md.txt +45 -0
- 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/prd-grill-frontier.md.txt +83 -0
- package/dist/templates/markdown/framework/release-boundary.md.txt +30 -0
- package/dist/templates/markdown/{spec/guides → framework}/retrieval-daily-guide.md.txt +1 -1
- package/dist/templates/markdown/framework/upgrade.md.txt +15 -0
- package/dist/templates/markdown/{spec/guides → framework}/verification-strength-guide.md.txt +2 -2
- package/dist/templates/markdown/index.d.ts +26 -10
- package/dist/templates/markdown/index.d.ts.map +1 -1
- package/dist/templates/markdown/index.js +87 -11
- package/dist/templates/markdown/index.js.map +1 -1
- package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +1 -1
- package/dist/templates/markdown/spec/guides/index.md.txt +5 -19
- package/dist/templates/markdown/spec/guides/prototype-guide.md.txt +10 -10
- package/dist/templates/markdown/spec/guides/test-discipline-guide.md.txt +1 -1
- 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 +19 -2
- package/dist/templates/trellis/CONTEXT.md +63 -3
- package/dist/templates/trellis/config.yaml +1 -1
- 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 +293 -61
- 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_observable_defaults.py +10 -4
- 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/cursor_retrieval_probe_prompt.md +4 -5
- 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 +109 -23
- 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/retrieval-execution-telemetry.js +1 -1
- package/dist/utils/retrieval-execution-telemetry.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/markdown/spec/guides/cursor-trellis-release-coexistence-guide.md.txt +0 -166
- package/dist/templates/markdown/spec/guides/goal-release-regression-runbook.md.txt +0 -132
- 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
- /package/dist/templates/markdown/{spec/guides → framework}/artifact-locale-guide.md.txt +0 -0
- /package/dist/templates/markdown/{spec/guides → framework}/cursor-context-injection-guide.md.txt +0 -0
- /package/dist/templates/markdown/{spec/guides → framework}/cursor-semantic-compliance.md.txt +0 -0
- /package/dist/templates/markdown/{spec/guides → framework}/injection-budget-guide.md.txt +0 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"""JSON CLI bridge to the TypeScript Kernel (P30 Stage 2 strangler).
|
|
2
|
+
|
|
3
|
+
Python create / start-execution / record-gate / archive / patch submit a
|
|
4
|
+
Kernel Command. `task.json` status, meta, and gate extras are a same-command
|
|
5
|
+
projection, not a second core-state writer.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from .log import Colors, colored
|
|
18
|
+
|
|
19
|
+
TASK_RECORD_FIELDS = (
|
|
20
|
+
"id",
|
|
21
|
+
"name",
|
|
22
|
+
"title",
|
|
23
|
+
"description",
|
|
24
|
+
"status",
|
|
25
|
+
"dev_type",
|
|
26
|
+
"scope",
|
|
27
|
+
"package",
|
|
28
|
+
"priority",
|
|
29
|
+
"creator",
|
|
30
|
+
"assignee",
|
|
31
|
+
"createdAt",
|
|
32
|
+
"completedAt",
|
|
33
|
+
"branch",
|
|
34
|
+
"base_branch",
|
|
35
|
+
"worktree_path",
|
|
36
|
+
"commit",
|
|
37
|
+
"pr_url",
|
|
38
|
+
"subtasks",
|
|
39
|
+
"children",
|
|
40
|
+
"parent",
|
|
41
|
+
"relatedFiles",
|
|
42
|
+
"notes",
|
|
43
|
+
"meta",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
_NULLABLE_STRING = {
|
|
47
|
+
"dev_type",
|
|
48
|
+
"scope",
|
|
49
|
+
"package",
|
|
50
|
+
"completedAt",
|
|
51
|
+
"branch",
|
|
52
|
+
"base_branch",
|
|
53
|
+
"worktree_path",
|
|
54
|
+
"commit",
|
|
55
|
+
"pr_url",
|
|
56
|
+
"parent",
|
|
57
|
+
}
|
|
58
|
+
_STRING_ARRAYS = {"subtasks", "children", "relatedFiles"}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class KernelCliNotFound(RuntimeError):
|
|
62
|
+
"""`cstl kernel --json` (or TRELLIS_KERNEL_CLI) is not available."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class KernelCommandError(RuntimeError):
|
|
66
|
+
"""Kernel JSON CLI returned ok:false or could not be parsed."""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
message: str,
|
|
71
|
+
*,
|
|
72
|
+
result: dict | None = None,
|
|
73
|
+
returncode: int | None = None,
|
|
74
|
+
) -> None:
|
|
75
|
+
super().__init__(message)
|
|
76
|
+
self.result = result
|
|
77
|
+
self.returncode = returncode
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def to_kernel_record(task_data: dict) -> dict:
|
|
81
|
+
"""Project a Python task dict onto the canonical 24-field Kernel record."""
|
|
82
|
+
record: dict = {}
|
|
83
|
+
for field in TASK_RECORD_FIELDS:
|
|
84
|
+
if field in task_data:
|
|
85
|
+
record[field] = task_data[field]
|
|
86
|
+
continue
|
|
87
|
+
if field in _NULLABLE_STRING:
|
|
88
|
+
record[field] = None
|
|
89
|
+
elif field in _STRING_ARRAYS:
|
|
90
|
+
record[field] = []
|
|
91
|
+
elif field == "meta":
|
|
92
|
+
record[field] = {}
|
|
93
|
+
elif field == "priority":
|
|
94
|
+
record[field] = "P2"
|
|
95
|
+
elif field == "status":
|
|
96
|
+
record[field] = "planning"
|
|
97
|
+
elif field in ("description", "notes", "id", "name", "title", "creator", "assignee", "createdAt"):
|
|
98
|
+
record[field] = ""
|
|
99
|
+
else:
|
|
100
|
+
record[field] = None
|
|
101
|
+
if not isinstance(record["meta"], dict):
|
|
102
|
+
record["meta"] = {}
|
|
103
|
+
else:
|
|
104
|
+
record["meta"] = {
|
|
105
|
+
key: value
|
|
106
|
+
for key, value in record["meta"].items()
|
|
107
|
+
if key not in {"classification", "task_kind", "task_type", "mode"}
|
|
108
|
+
}
|
|
109
|
+
for array_field in _STRING_ARRAYS:
|
|
110
|
+
value = record[array_field]
|
|
111
|
+
if not isinstance(value, list):
|
|
112
|
+
record[array_field] = []
|
|
113
|
+
return record
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def kernel_cli_argv() -> list[str]:
|
|
117
|
+
override = os.environ.get("TRELLIS_KERNEL_CLI", "").strip()
|
|
118
|
+
if override:
|
|
119
|
+
if sys.platform == "win32":
|
|
120
|
+
import shlex
|
|
121
|
+
|
|
122
|
+
return shlex.split(override, posix=False)
|
|
123
|
+
import shlex
|
|
124
|
+
|
|
125
|
+
return shlex.split(override)
|
|
126
|
+
cstl = shutil.which("cstl")
|
|
127
|
+
if cstl:
|
|
128
|
+
return [cstl, "kernel", "--json"]
|
|
129
|
+
raise KernelCliNotFound(
|
|
130
|
+
"Kernel CLI not found. Set TRELLIS_KERNEL_CLI or put `cstl` on PATH. "
|
|
131
|
+
"Stage 2 create/start-execution/record-gate/archive/patch require `cstl kernel --json`."
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def run_kernel_command(payload: dict, *, cwd: Path | None = None) -> dict:
|
|
136
|
+
"""Send one Kernel Command on stdin and parse one JSON object from stdout."""
|
|
137
|
+
argv = kernel_cli_argv()
|
|
138
|
+
encoded = json.dumps(payload, ensure_ascii=False)
|
|
139
|
+
try:
|
|
140
|
+
proc = subprocess.run(
|
|
141
|
+
argv,
|
|
142
|
+
input=encoded,
|
|
143
|
+
capture_output=True,
|
|
144
|
+
text=True,
|
|
145
|
+
encoding="utf-8",
|
|
146
|
+
cwd=str(cwd) if cwd is not None else None,
|
|
147
|
+
check=False,
|
|
148
|
+
)
|
|
149
|
+
except OSError as err:
|
|
150
|
+
raise KernelCommandError(f"failed to invoke Kernel CLI {argv!r}: {err}") from err
|
|
151
|
+
|
|
152
|
+
raw = (proc.stdout or "").strip()
|
|
153
|
+
parsed: dict | None = None
|
|
154
|
+
if raw:
|
|
155
|
+
try:
|
|
156
|
+
parsed = json.loads(raw)
|
|
157
|
+
except json.JSONDecodeError:
|
|
158
|
+
last_line = raw.splitlines()[-1] if raw.splitlines() else raw
|
|
159
|
+
try:
|
|
160
|
+
parsed = json.loads(last_line)
|
|
161
|
+
except json.JSONDecodeError:
|
|
162
|
+
parsed = None
|
|
163
|
+
|
|
164
|
+
if not isinstance(parsed, dict):
|
|
165
|
+
err_tail = (proc.stderr or "").strip()[-800:]
|
|
166
|
+
raise KernelCommandError(
|
|
167
|
+
f"Kernel CLI did not return JSON (exit {proc.returncode}). {err_tail}",
|
|
168
|
+
returncode=proc.returncode,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if not parsed.get("ok"):
|
|
172
|
+
error = parsed.get("error") if isinstance(parsed.get("error"), dict) else {}
|
|
173
|
+
code = error.get("code", "UNKNOWN")
|
|
174
|
+
message = error.get("message", "kernel command failed")
|
|
175
|
+
raise KernelCommandError(
|
|
176
|
+
f"Kernel {payload.get('op', 'command')} failed ({code}): {message}",
|
|
177
|
+
result=parsed,
|
|
178
|
+
returncode=proc.returncode,
|
|
179
|
+
)
|
|
180
|
+
return parsed
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def kernel_read(task_dir: Path) -> dict:
|
|
184
|
+
return run_kernel_command({"op": "read", "taskDir": str(task_dir)})
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def kernel_expected_revision(task_dir: Path) -> int:
|
|
188
|
+
result = kernel_read(task_dir)
|
|
189
|
+
kernel = result.get("kernel")
|
|
190
|
+
if not isinstance(kernel, dict):
|
|
191
|
+
raise KernelCommandError("Kernel read did not include kernel.revision", result=result)
|
|
192
|
+
revision = kernel.get("revision")
|
|
193
|
+
if not isinstance(revision, int) or revision < 0:
|
|
194
|
+
raise KernelCommandError(
|
|
195
|
+
f"Kernel revision is invalid: {revision!r}",
|
|
196
|
+
result=result,
|
|
197
|
+
)
|
|
198
|
+
return revision
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def kernel_create(
|
|
202
|
+
task_dir: Path,
|
|
203
|
+
record: dict,
|
|
204
|
+
*,
|
|
205
|
+
actor: str,
|
|
206
|
+
idempotency_key: str,
|
|
207
|
+
evidence: str | None = None,
|
|
208
|
+
extras: dict | None = None,
|
|
209
|
+
) -> dict:
|
|
210
|
+
payload: dict = {
|
|
211
|
+
"op": "create",
|
|
212
|
+
"taskDir": str(task_dir),
|
|
213
|
+
"actor": actor,
|
|
214
|
+
"idempotencyKey": idempotency_key,
|
|
215
|
+
"record": to_kernel_record(record),
|
|
216
|
+
}
|
|
217
|
+
if extras:
|
|
218
|
+
payload["extras"] = extras
|
|
219
|
+
if evidence:
|
|
220
|
+
payload["evidence"] = evidence
|
|
221
|
+
return run_kernel_command(payload)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def kernel_start(
|
|
225
|
+
task_dir: Path,
|
|
226
|
+
record: dict,
|
|
227
|
+
extras: dict,
|
|
228
|
+
*,
|
|
229
|
+
expected_revision: int,
|
|
230
|
+
actor: str,
|
|
231
|
+
idempotency_key: str,
|
|
232
|
+
evidence: str | None = None,
|
|
233
|
+
) -> dict:
|
|
234
|
+
payload: dict = {
|
|
235
|
+
"op": "start",
|
|
236
|
+
"taskDir": str(task_dir),
|
|
237
|
+
"expectedRevision": expected_revision,
|
|
238
|
+
"actor": actor,
|
|
239
|
+
"idempotencyKey": idempotency_key,
|
|
240
|
+
"record": to_kernel_record(record),
|
|
241
|
+
"extras": extras,
|
|
242
|
+
}
|
|
243
|
+
if evidence:
|
|
244
|
+
payload["evidence"] = evidence
|
|
245
|
+
return run_kernel_command(payload)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def kernel_record_gate(
|
|
249
|
+
task_dir: Path,
|
|
250
|
+
*,
|
|
251
|
+
expected_revision: int,
|
|
252
|
+
actor: str,
|
|
253
|
+
idempotency_key: str,
|
|
254
|
+
transition: str,
|
|
255
|
+
gate: str,
|
|
256
|
+
record: dict,
|
|
257
|
+
extras: dict,
|
|
258
|
+
evidence: str | None = None,
|
|
259
|
+
) -> dict:
|
|
260
|
+
payload: dict = {
|
|
261
|
+
"op": "record-gate",
|
|
262
|
+
"taskDir": str(task_dir),
|
|
263
|
+
"expectedRevision": expected_revision,
|
|
264
|
+
"actor": actor,
|
|
265
|
+
"idempotencyKey": idempotency_key,
|
|
266
|
+
"transition": transition,
|
|
267
|
+
"gate": gate,
|
|
268
|
+
"record": record,
|
|
269
|
+
"extras": extras,
|
|
270
|
+
}
|
|
271
|
+
if evidence:
|
|
272
|
+
payload["evidence"] = evidence
|
|
273
|
+
return run_kernel_command(payload)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
KERNEL_PROJECTION_EXTRA_KEYS = (
|
|
277
|
+
"quality_gate_results",
|
|
278
|
+
"execution_approval",
|
|
279
|
+
"required_controls",
|
|
280
|
+
"ac_evidence_ledger",
|
|
281
|
+
"independent_check",
|
|
282
|
+
"topology",
|
|
283
|
+
"dependency_graph",
|
|
284
|
+
"ondemand_modules",
|
|
285
|
+
"depends_on",
|
|
286
|
+
"dependency_satisfied",
|
|
287
|
+
"decompose_proposal",
|
|
288
|
+
"profile_health",
|
|
289
|
+
"event_bridge",
|
|
290
|
+
"middleware_providers",
|
|
291
|
+
"capability_router",
|
|
292
|
+
"required_capabilities",
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def kernel_projection_extras(task_data: dict) -> dict:
|
|
297
|
+
"""Non-canonical task.json fields owned by the same Kernel Command."""
|
|
298
|
+
extras: dict = {}
|
|
299
|
+
for key in KERNEL_PROJECTION_EXTRA_KEYS:
|
|
300
|
+
if key in task_data:
|
|
301
|
+
extras[key] = task_data[key]
|
|
302
|
+
return extras
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def kernel_archive(
|
|
306
|
+
task_dir: Path,
|
|
307
|
+
record: dict,
|
|
308
|
+
extras: dict,
|
|
309
|
+
*,
|
|
310
|
+
expected_revision: int,
|
|
311
|
+
actor: str,
|
|
312
|
+
idempotency_key: str,
|
|
313
|
+
evidence: str | None = None,
|
|
314
|
+
) -> dict:
|
|
315
|
+
payload: dict = {
|
|
316
|
+
"op": "archive",
|
|
317
|
+
"taskDir": str(task_dir),
|
|
318
|
+
"expectedRevision": expected_revision,
|
|
319
|
+
"actor": actor,
|
|
320
|
+
"idempotencyKey": idempotency_key,
|
|
321
|
+
"record": to_kernel_record(record),
|
|
322
|
+
"extras": extras,
|
|
323
|
+
}
|
|
324
|
+
if evidence:
|
|
325
|
+
payload["evidence"] = evidence
|
|
326
|
+
return run_kernel_command(payload)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def kernel_patch(
|
|
330
|
+
task_dir: Path,
|
|
331
|
+
record: dict | None = None,
|
|
332
|
+
extras: dict | None = None,
|
|
333
|
+
*,
|
|
334
|
+
expected_revision: int,
|
|
335
|
+
actor: str,
|
|
336
|
+
idempotency_key: str,
|
|
337
|
+
evidence: str | None = None,
|
|
338
|
+
) -> dict:
|
|
339
|
+
payload: dict = {
|
|
340
|
+
"op": "patch",
|
|
341
|
+
"taskDir": str(task_dir),
|
|
342
|
+
"expectedRevision": expected_revision,
|
|
343
|
+
"actor": actor,
|
|
344
|
+
"idempotencyKey": idempotency_key,
|
|
345
|
+
}
|
|
346
|
+
if record is not None:
|
|
347
|
+
payload["record"] = to_kernel_record(record)
|
|
348
|
+
if extras is not None:
|
|
349
|
+
payload["extras"] = extras
|
|
350
|
+
if evidence:
|
|
351
|
+
payload["evidence"] = evidence
|
|
352
|
+
return run_kernel_command(payload)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def print_kernel_error(err: Exception) -> None:
|
|
356
|
+
print(colored(f"Error: {err}", Colors.RED), file=sys.stderr)
|
|
357
|
+
if isinstance(err, KernelCommandError) and isinstance(err.result, dict):
|
|
358
|
+
half = err.result.get("halfConversion")
|
|
359
|
+
if isinstance(half, dict) and half.get("kernelPersisted"):
|
|
360
|
+
print(
|
|
361
|
+
colored(
|
|
362
|
+
"Kernel.json was committed but the task.json projection did not finish "
|
|
363
|
+
"(half-conversion). Re-run the same command to recover; do not treat this as success.",
|
|
364
|
+
Colors.YELLOW,
|
|
365
|
+
),
|
|
366
|
+
file=sys.stderr,
|
|
367
|
+
)
|
|
@@ -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)
|