@colin4k1024/tsp 2.5.3 → 2.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/goal.md +7 -1
- package/commands/heartbeat.md +34 -1
- package/commands/loop-start.md +34 -6
- package/commands/triage.md +8 -1
- package/hooks/README.md +2 -2
- package/hooks/harness-statusline.js +9 -30
- package/hooks/strategic-compact/README.md +11 -12
- package/manifests/install-components.json +40 -0
- package/manifests/install-modules.json +43 -0
- package/manifests/install-profiles.json +2 -0
- package/package.json +1 -1
- package/schemas/loop-spec.schema.json +124 -0
- package/scripts/hooks/pre-compact.js +39 -8
- package/scripts/hooks/session-start-goal-resume.js +3 -20
- package/scripts/hooks/suggest-compact.js +9 -115
- package/scripts/lib/completion-oracle.js +4 -27
- package/scripts/lib/context-window-state.js +129 -0
- package/scripts/lib/context-window.js +294 -0
- package/scripts/lib/heartbeat-scheduler.js +40 -25
- package/scripts/lib/install-targets/registry.js +1 -1
- package/scripts/lib/loop-oracle.js +5 -0
- package/scripts/lib/loop-spec.js +168 -0
- package/scripts/lib/loop-state-store.js +221 -0
- package/scripts/lib/transcript-usage.js +11 -1
- package/skills/goframe-v2/examples/practices/quick-demo/manifest/config/config.yaml +14 -14
- package/skills/repo-scan/SKILL.md +63 -63
- package/skills/strategic-compact/SKILL.md +11 -2
- package/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/__pycache__/build_platform_artifacts.cpython-311.pyc +0 -0
- package/scripts/__pycache__/install_platform.cpython-311.pyc +0 -0
- package/scripts/__pycache__/langfuse_trace.cpython-311.pyc +0 -0
- package/scripts/__pycache__/query_audit_logs.cpython-311.pyc +0 -0
- package/scripts/__pycache__/scan_leaked_keys.cpython-311.pyc +0 -0
- package/scripts/__pycache__/team_skills_platform.cpython-311.pyc +0 -0
- package/scripts/__pycache__/team_skills_platform.cpython-313.pyc +0 -0
- package/scripts/__pycache__/validate_library.cpython-311.pyc +0 -0
- package/scripts/__pycache__/validate_workflow_state.cpython-311.pyc +0 -0
- package/scripts/evolution/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/evolution/__pycache__/store.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/mcp_health_check.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/observe.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/session_end.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/session_start.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/audit_logger.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/audit_query.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/hook_contract.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/memory_store.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/utils.cpython-311.pyc +0 -0
package/commands/goal.md
CHANGED
|
@@ -114,7 +114,13 @@ When ANY budget limit is hit, the goal escalates rather than continuing blindly.
|
|
|
114
114
|
|
|
115
115
|
## State Persistence
|
|
116
116
|
|
|
117
|
-
Goals persist
|
|
117
|
+
Goals persist through the shared loop state store:
|
|
118
|
+
|
|
119
|
+
- `TSP_LOOP_STATE_DIR/goals/{goalId}.json` when `TSP_LOOP_STATE_DIR` is set
|
|
120
|
+
- `.tsp/loops/goals/{goalId}.json` for project-local loop state
|
|
121
|
+
- target defaults such as `~/.claude/loops/goals/{goalId}.json` or `~/.codex/loops/goals/{goalId}.json`
|
|
122
|
+
|
|
123
|
+
Legacy `~/.claude/goals/{goalId}.json` remains readable during migration.
|
|
118
124
|
|
|
119
125
|
- Active goals survive session restarts
|
|
120
126
|
- SessionStart hook displays active goal reminder
|
package/commands/heartbeat.md
CHANGED
|
@@ -51,7 +51,39 @@ Execute all configured scans immediately (one-shot, no scheduling).
|
|
|
51
51
|
|
|
52
52
|
## Configuration
|
|
53
53
|
|
|
54
|
-
Heartbeat reads
|
|
54
|
+
Heartbeat reads configuration in this order:
|
|
55
|
+
|
|
56
|
+
1. `.tsp/loop.yaml` — preferred Loop Engineering spec. Gates become discovery scans.
|
|
57
|
+
2. `.tsp/heartbeat.yaml` — heartbeat-only config.
|
|
58
|
+
3. `.claude/heartbeat.yaml` — legacy compatibility path.
|
|
59
|
+
|
|
60
|
+
Preferred `.tsp/loop.yaml`:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
loop:
|
|
64
|
+
id: ci-triage
|
|
65
|
+
description: Keep CI failures triaged and fix machine-checkable failures.
|
|
66
|
+
cadence: 30m
|
|
67
|
+
skill: loop-ci-triage
|
|
68
|
+
stateFile: .tsp/loops/state/ci-triage.md
|
|
69
|
+
gates:
|
|
70
|
+
- name: library-validation
|
|
71
|
+
command: node scripts/validate-library.js
|
|
72
|
+
- name: tests
|
|
73
|
+
command: npm test
|
|
74
|
+
maker:
|
|
75
|
+
role: backend-engineer
|
|
76
|
+
writeAccess: true
|
|
77
|
+
checker:
|
|
78
|
+
role: qa-engineer
|
|
79
|
+
writeAccess: false
|
|
80
|
+
budget:
|
|
81
|
+
maxIterations: 10
|
|
82
|
+
maxDuration: 2h
|
|
83
|
+
maxDollars: 5
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Heartbeat-only config remains supported:
|
|
55
87
|
|
|
56
88
|
```yaml
|
|
57
89
|
heartbeat:
|
|
@@ -119,6 +151,7 @@ Each scan result is classified into an action:
|
|
|
119
151
|
- **`/triage`**: Heartbeat populates triage inbox from `triage` scan failures
|
|
120
152
|
- **Budget**: Integrates with cost tracking; pauses if hourly budget exceeded
|
|
121
153
|
- **Hooks**: Uses CronCreate/CronDelete for scheduling (7-day auto-expiry applies)
|
|
154
|
+
- **State**: Writes via the shared loop state store under `TSP_LOOP_STATE_DIR`, `.tsp/loops/`, or a target default
|
|
122
155
|
|
|
123
156
|
## Arguments
|
|
124
157
|
|
package/commands/loop-start.md
CHANGED
|
@@ -4,29 +4,57 @@ Start a managed autonomous loop pattern with safety defaults.
|
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
|
-
`/loop-start [pattern] [--mode safe|fast]`
|
|
7
|
+
`/loop-start [pattern] [--mode safe|fast] [--spec .tsp/loop.yaml]`
|
|
8
8
|
|
|
9
9
|
- `pattern`: `sequential`, `continuous-pr`, `rfc-dag`, `infinite`
|
|
10
10
|
- `--mode`:
|
|
11
11
|
- `safe` (default): strict quality gates and checkpoints
|
|
12
12
|
- `fast`: reduced gates for speed
|
|
13
|
+
- `--spec`: loop spec path, default `.tsp/loop.yaml`
|
|
14
|
+
|
|
15
|
+
## Loop Engineering Intake
|
|
16
|
+
|
|
17
|
+
Do not start a scheduled loop until all four conditions hold:
|
|
18
|
+
|
|
19
|
+
1. The task repeats often enough to amortize loop setup.
|
|
20
|
+
2. Verification is automated with at least one hard gate.
|
|
21
|
+
3. Budget is explicit: iterations, wall-clock time, and cost.
|
|
22
|
+
4. Tool access is sufficient and bounded for the intended work.
|
|
23
|
+
|
|
24
|
+
If any condition fails, use `/quick`, `/verify`, or a one-shot script instead of
|
|
25
|
+
creating a loop.
|
|
13
26
|
|
|
14
27
|
## Flow
|
|
15
28
|
|
|
16
29
|
1. Confirm repository state and branch strategy.
|
|
17
|
-
2.
|
|
18
|
-
3.
|
|
19
|
-
4.
|
|
20
|
-
5.
|
|
30
|
+
2. Validate `.tsp/loop.yaml` against `schemas/loop-spec.schema.json`.
|
|
31
|
+
3. Initialize loop state under `TSP_LOOP_STATE_DIR`, `.tsp/loops/`, or the target adapter default.
|
|
32
|
+
4. Select loop pattern and model tier strategy.
|
|
33
|
+
5. Enable required hooks/profile for the chosen mode.
|
|
34
|
+
6. Print commands to start and monitor the loop.
|
|
21
35
|
|
|
22
36
|
## Required Safety Checks
|
|
23
37
|
|
|
24
38
|
- Verify tests pass before first loop iteration.
|
|
25
39
|
- Ensure `ECC_HOOK_PROFILE` is not disabled globally.
|
|
26
|
-
- Ensure loop has
|
|
40
|
+
- Ensure loop has at least one objective gate.
|
|
41
|
+
- Ensure loop has explicit stop conditions and budget limits.
|
|
42
|
+
- Ensure maker and checker are separate roles or agents.
|
|
43
|
+
- Ensure loop writes go through a branch or worktree, not protected main.
|
|
44
|
+
|
|
45
|
+
## State
|
|
46
|
+
|
|
47
|
+
Loop runtime state is target-neutral:
|
|
48
|
+
|
|
49
|
+
- `TSP_LOOP_STATE_DIR` when set
|
|
50
|
+
- project-local `.tsp/loops/`
|
|
51
|
+
- target default, such as `~/.claude/loops/` or `~/.codex/loops/`
|
|
52
|
+
|
|
53
|
+
Legacy Claude goal and triage paths remain readable for migration.
|
|
27
54
|
|
|
28
55
|
## Arguments
|
|
29
56
|
|
|
30
57
|
$ARGUMENTS:
|
|
31
58
|
- `<pattern>` optional (`sequential|continuous-pr|rfc-dag|infinite`)
|
|
32
59
|
- `--mode safe|fast` optional
|
|
60
|
+
- `--spec <path>` optional
|
package/commands/triage.md
CHANGED
|
@@ -86,7 +86,14 @@ Items enter triage from:
|
|
|
86
86
|
|
|
87
87
|
## Storage
|
|
88
88
|
|
|
89
|
-
Inbox is stored as newline-delimited JSON
|
|
89
|
+
Inbox is stored as newline-delimited JSON through the shared loop state store:
|
|
90
|
+
|
|
91
|
+
1. `TSP_LOOP_STATE_DIR/triage/inbox.jsonl` when `TSP_LOOP_STATE_DIR` is set
|
|
92
|
+
2. `.tsp/loops/triage/inbox.jsonl` for project-local state
|
|
93
|
+
3. target defaults such as `~/.claude/loops/triage/inbox.jsonl` or `~/.codex/loops/triage/inbox.jsonl`
|
|
94
|
+
|
|
95
|
+
Legacy `~/.claude/triage/inbox.jsonl` remains a migration source for older Claude installs.
|
|
96
|
+
|
|
90
97
|
This format is:
|
|
91
98
|
- Append-only (safe for concurrent writes)
|
|
92
99
|
- Human-readable (one JSON object per line)
|
package/hooks/README.md
CHANGED
|
@@ -26,7 +26,7 @@ User request → Claude picks a tool → PreToolUse hook runs → Tool executes
|
|
|
26
26
|
| **Git push reminder** | `Bash` | Reminds to review changes before `git push` | 0 (warns) |
|
|
27
27
|
| **Pre-commit quality check** | `Bash` | Runs quality checks before `git commit`: lints staged files, validates commit message format when provided via `-m/--message`, detects console.log/debugger/secrets | 2 (blocks critical) / 0 (warns) |
|
|
28
28
|
| **Doc file warning** | `Write` | Warns about non-standard `.md`/`.txt` files (allows README, CLAUDE, CONTRIBUTING, CHANGELOG, LICENSE, SKILL, docs/, skills/); cross-platform path handling | 0 (warns) |
|
|
29
|
-
| **Strategic compact** | `*` | Suggests `/compact` when context usage crosses 70/85/95% thresholds, using
|
|
29
|
+
| **Strategic compact** | `*` | Suggests `/compact` when context usage crosses 65/70/85/95% thresholds, using CCometixLine-compatible remaining context first | 0 (warns) |
|
|
30
30
|
| **InsAIts security monitor (opt-in)** | `Bash\|Write\|Edit\|MultiEdit` | Optional security scan for high-signal tool inputs. Disabled unless `ECC_ENABLE_INSAITS=1`. Blocks on critical findings, warns on non-critical, and writes audit log to `.insaits_audit_session.jsonl`. JS wrapper is the canonical hook entry; the Python monitor is an explicit third-party SDK exception. Requires `pip install insa-its`. [Details](../scripts/hooks/insaits-security-monitor.py) | 2 (blocks critical) / 0 (warns) |
|
|
31
31
|
|
|
32
32
|
### PostToolUse Hooks
|
|
@@ -48,7 +48,7 @@ User request → Claude picks a tool → PreToolUse hook runs → Tool executes
|
|
|
48
48
|
|------|-------|-------------|
|
|
49
49
|
| **Session start** | `SessionStart` | Loads previous context and detects package manager |
|
|
50
50
|
| **PUA always-on restore** | `SessionStart` | Restores flavor, level, and failure count when `~/.claude/pua/config.json` enables always-on |
|
|
51
|
-
| **Pre-compact** | `PreCompact` | Saves state before context compaction |
|
|
51
|
+
| **Pre-compact** | `PreCompact` | Saves state and increments project/session compact count before context compaction |
|
|
52
52
|
| **PUA state snapshot** | `PreCompact` | Writes flavor, level, and failure count to the PUA builder journal |
|
|
53
53
|
| **Console.log audit** | `Stop` | Checks all modified files for `console.log` after each response |
|
|
54
54
|
| **Session summary** | `Stop` | Persists session state when transcript path is available |
|
|
@@ -15,9 +15,7 @@
|
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const os = require('os');
|
|
18
|
-
|
|
19
|
-
// Autocompact buffer ~16.5% — GSD pattern, normalize to usable context
|
|
20
|
-
const AUTO_COMPACT_BUFFER_PCT = 16.5;
|
|
18
|
+
const { resolveContextMetrics } = require('../scripts/lib/context-window');
|
|
21
19
|
|
|
22
20
|
let input = '';
|
|
23
21
|
// Guard: if stdin stalls (Windows/pipe issues), exit silently
|
|
@@ -31,8 +29,6 @@ process.stdin.on('end', () => {
|
|
|
31
29
|
const session = data.session_id || '';
|
|
32
30
|
const model = data.model?.display_name || 'Claude';
|
|
33
31
|
const dir = data.cwd || data.workspace?.current_dir || process.cwd();
|
|
34
|
-
const remaining = data.context_window?.remaining_percentage;
|
|
35
|
-
|
|
36
32
|
// --- Resolve active role from harness state file ---
|
|
37
33
|
const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
38
34
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || dir;
|
|
@@ -49,30 +45,9 @@ process.stdin.on('end', () => {
|
|
|
49
45
|
// --- Context window metrics ---
|
|
50
46
|
let ctxBar = '';
|
|
51
47
|
let usedPct = null;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
usedPct =
|
|
55
|
-
} else if (data.transcript_path) {
|
|
56
|
-
// Parse actual token usage from transcript JSONL (CCometixLine approach)
|
|
57
|
-
try {
|
|
58
|
-
const { resolveTranscriptMetrics } = require('../scripts/lib/transcript-usage');
|
|
59
|
-
const modelId = (data.model && data.model.id) || process.env.CLAUDE_MODEL || null;
|
|
60
|
-
const metrics = resolveTranscriptMetrics(data.transcript_path, modelId);
|
|
61
|
-
if (metrics) {
|
|
62
|
-
usedPct = Math.max(0, Math.min(100, Math.round(metrics.usagePct)));
|
|
63
|
-
}
|
|
64
|
-
} catch (_) { /* ignore */ }
|
|
65
|
-
|
|
66
|
-
// Final fallback: file size estimate
|
|
67
|
-
if (usedPct == null) {
|
|
68
|
-
try {
|
|
69
|
-
const stat = fs.statSync(data.transcript_path);
|
|
70
|
-
const estimatedTokens = Math.round(stat.size * 0.25);
|
|
71
|
-
if (estimatedTokens > 0) {
|
|
72
|
-
usedPct = Math.max(0, Math.min(100, Math.round((estimatedTokens / 200000) * 100)));
|
|
73
|
-
}
|
|
74
|
-
} catch (_) { /* ignore */ }
|
|
75
|
-
}
|
|
48
|
+
const metrics = resolveContextMetrics(data);
|
|
49
|
+
if (metrics) {
|
|
50
|
+
usedPct = metrics.usagePct;
|
|
76
51
|
}
|
|
77
52
|
|
|
78
53
|
// Write bridge file for downstream hooks (suggest-compact, context-monitor)
|
|
@@ -81,8 +56,12 @@ process.stdin.on('end', () => {
|
|
|
81
56
|
const bridgePath = path.join(os.tmpdir(), `harness-ctx-${session}.json`);
|
|
82
57
|
fs.writeFileSync(bridgePath, JSON.stringify({
|
|
83
58
|
session_id: session,
|
|
84
|
-
remaining_percentage:
|
|
59
|
+
remaining_percentage: metrics?.remainingPct ?? null,
|
|
60
|
+
remaining_tokens: metrics?.remainingTokens ?? null,
|
|
85
61
|
used_pct: usedPct,
|
|
62
|
+
context_limit: metrics?.contextLimit ?? null,
|
|
63
|
+
compact_count: metrics?.compactCount ?? null,
|
|
64
|
+
context_source: metrics?.source ?? null,
|
|
86
65
|
active_role: activeRole,
|
|
87
66
|
timestamp: Math.floor(Date.now() / 1000),
|
|
88
67
|
}));
|
|
@@ -13,12 +13,9 @@
|
|
|
13
13
|
**触发时机**: 上下文压缩前自动调用
|
|
14
14
|
|
|
15
15
|
**功能**:
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
- `summarize`: 中等价值内容(长输出、对话历史)
|
|
20
|
-
- `discard`: 低价值内容(工具结果、搜索结果、重复信息)
|
|
21
|
-
- 生成摘要指令
|
|
16
|
+
- 保存 compaction 事件
|
|
17
|
+
- 递增 `.tsp/context/compact-state.json` 中的 session compact count 与 total compact count
|
|
18
|
+
- 把 compact 轮次写入 session tmp 文件,供后续恢复和动态上下文策略使用
|
|
22
19
|
|
|
23
20
|
**优先级规则**:
|
|
24
21
|
- 总是保留的类型:`decision`, `conclusion`, `pending_item`, `task_output`, `verification_result`, `error_fix`
|
|
@@ -27,13 +24,14 @@
|
|
|
27
24
|
|
|
28
25
|
### suggest-compact.js
|
|
29
26
|
|
|
30
|
-
**触发时机**: `hooks/hooks.json` 的 `pre:all:strategic-compact` 在工具调用前运行;当真实上下文使用超过
|
|
27
|
+
**触发时机**: `hooks/hooks.json` 的 `pre:all:strategic-compact` 在工具调用前运行;当真实上下文使用超过 65% 时注入 `/compact` 建议。
|
|
31
28
|
|
|
32
29
|
**功能**:
|
|
33
|
-
- 优先读取
|
|
34
|
-
- 其次读取 `
|
|
35
|
-
-
|
|
36
|
-
- 评估紧迫度:`low` (< 70%) | `medium` (70-85%) | `high` (85-95%) | `critical` (> 95%)
|
|
30
|
+
- 优先读取 CCometixLine-compatible remaining context:`TSP_CONTEXT_WINDOW_JSON` / `CCOMETIXLINE_CONTEXT_JSON`、`TSP_CONTEXT_WINDOW_FILE` / `CCOMETIXLINE_CONTEXT_FILE`、hook 输入的 `ccometixline.context_window` / `ccometixline_context_window`
|
|
31
|
+
- 其次读取 Claude hook 输入里的 `context_window.used_percentage` / `context_window.remaining_percentage`
|
|
32
|
+
- 再退回 transcript JSONL usage、`CLAUDE_CONTEXT_SIZE` / `CLAUDE_CONTEXT_LIMIT`、`harness-statusline.js` 写入的 `/tmp/harness-ctx-{session_id}.json`
|
|
33
|
+
- 评估紧迫度:`low` (< 65%) | `advisory` (65-70%) | `medium` (70-85%) | `high` (85-95%) | `critical` (> 95%)
|
|
34
|
+
- 输出 `compact_count`,让动态上下文策略知道当前已经经历过几轮 compact
|
|
37
35
|
- 估算可节省的 token
|
|
38
36
|
- 提供具体压缩建议:
|
|
39
37
|
- 压缩早期对话历史
|
|
@@ -59,7 +57,8 @@
|
|
|
59
57
|
|
|
60
58
|
| 使用率 | 紧迫度 | 建议操作 |
|
|
61
59
|
|--------|--------|---------|
|
|
62
|
-
| <
|
|
60
|
+
| < 65% | low | 无需操作 |
|
|
61
|
+
| 65-70% | advisory | 控制上下文增长 |
|
|
63
62
|
| 70-85% | medium | 建议压缩,可选择性执行 |
|
|
64
63
|
| 85-95% | high | 强烈建议压缩 |
|
|
65
64
|
| > 95% | critical | 必须立即压缩 |
|
|
@@ -193,6 +193,14 @@
|
|
|
193
193
|
"orchestration"
|
|
194
194
|
]
|
|
195
195
|
},
|
|
196
|
+
{
|
|
197
|
+
"id": "capability:loop-engineering",
|
|
198
|
+
"family": "capability",
|
|
199
|
+
"description": "Loop Engineering runtime: automation, skills, target-neutral state, hard gates, and maker/checker loop primitives.",
|
|
200
|
+
"modules": [
|
|
201
|
+
"loop-engineering"
|
|
202
|
+
]
|
|
203
|
+
},
|
|
196
204
|
{
|
|
197
205
|
"id": "capability:workflow-engine",
|
|
198
206
|
"family": "capability",
|
|
@@ -396,6 +404,38 @@
|
|
|
396
404
|
"workflow-quality"
|
|
397
405
|
]
|
|
398
406
|
},
|
|
407
|
+
{
|
|
408
|
+
"id": "skill:loop-heartbeat",
|
|
409
|
+
"family": "skill",
|
|
410
|
+
"description": "Scheduled discovery automation that routes findings to goals or triage.",
|
|
411
|
+
"modules": [
|
|
412
|
+
"loop-engineering"
|
|
413
|
+
]
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
"id": "skill:goal-convergence",
|
|
417
|
+
"family": "skill",
|
|
418
|
+
"description": "Goal convergence patterns for bounded autonomous loops.",
|
|
419
|
+
"modules": [
|
|
420
|
+
"loop-engineering"
|
|
421
|
+
]
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"id": "skill:rework-loop",
|
|
425
|
+
"family": "skill",
|
|
426
|
+
"description": "Rework loop patterns for checker-driven iteration.",
|
|
427
|
+
"modules": [
|
|
428
|
+
"loop-engineering"
|
|
429
|
+
]
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"id": "skill:autonomous-loops",
|
|
433
|
+
"family": "skill",
|
|
434
|
+
"description": "Autonomous loop design and safety guidance.",
|
|
435
|
+
"modules": [
|
|
436
|
+
"loop-engineering"
|
|
437
|
+
]
|
|
438
|
+
},
|
|
399
439
|
{
|
|
400
440
|
"id": "skill:strategic-compact",
|
|
401
441
|
"family": "skill",
|
|
@@ -528,6 +528,49 @@
|
|
|
528
528
|
"cost": "medium",
|
|
529
529
|
"stability": "beta"
|
|
530
530
|
},
|
|
531
|
+
{
|
|
532
|
+
"id": "loop-engineering",
|
|
533
|
+
"kind": "orchestration",
|
|
534
|
+
"description": "Loop Engineering command surface, target-neutral state runtime, loop spec schema, heartbeat discovery, and maker/checker loop skills.",
|
|
535
|
+
"paths": [
|
|
536
|
+
".tsp/loop.example.yaml",
|
|
537
|
+
"commands/goal.md",
|
|
538
|
+
"commands/heartbeat.md",
|
|
539
|
+
"commands/triage.md",
|
|
540
|
+
"commands/loop-start.md",
|
|
541
|
+
"commands/loop-status.md",
|
|
542
|
+
"docs/runbooks/loop-engineering-usage.md",
|
|
543
|
+
"schemas/loop-spec.schema.json",
|
|
544
|
+
"scripts/lib/loop-state-store.js",
|
|
545
|
+
"scripts/lib/loop-spec.js",
|
|
546
|
+
"scripts/lib/loop-oracle.js",
|
|
547
|
+
"scripts/lib/completion-oracle.js",
|
|
548
|
+
"scripts/lib/heartbeat-scheduler.js",
|
|
549
|
+
"scripts/hooks/session-start-goal-resume.js",
|
|
550
|
+
"skills/loop-heartbeat",
|
|
551
|
+
"skills/goal-convergence",
|
|
552
|
+
"skills/rework-loop",
|
|
553
|
+
"skills/context-engineering",
|
|
554
|
+
"skills/context-lifecycle",
|
|
555
|
+
"templates/context-docs"
|
|
556
|
+
],
|
|
557
|
+
"targets": [
|
|
558
|
+
"claude",
|
|
559
|
+
"cursor",
|
|
560
|
+
"codex",
|
|
561
|
+
"opencode",
|
|
562
|
+
"cangming",
|
|
563
|
+
"codebuddy",
|
|
564
|
+
"codewhale"
|
|
565
|
+
],
|
|
566
|
+
"dependencies": [
|
|
567
|
+
"commands-core",
|
|
568
|
+
"platform-configs"
|
|
569
|
+
],
|
|
570
|
+
"defaultInstall": false,
|
|
571
|
+
"cost": "medium",
|
|
572
|
+
"stability": "beta"
|
|
573
|
+
},
|
|
531
574
|
{
|
|
532
575
|
"id": "workflow-engine",
|
|
533
576
|
"kind": "workflow",
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"workflow-engine",
|
|
79
79
|
"workflow-defaults",
|
|
80
80
|
"orchestration",
|
|
81
|
+
"loop-engineering",
|
|
81
82
|
"swift-apple",
|
|
82
83
|
"agentic-patterns",
|
|
83
84
|
"devops-infra",
|
|
@@ -105,6 +106,7 @@
|
|
|
105
106
|
"knowledge-graph",
|
|
106
107
|
"workflow-engine",
|
|
107
108
|
"workflow-defaults",
|
|
109
|
+
"loop-engineering",
|
|
108
110
|
"design-prototyping",
|
|
109
111
|
"enhanced-workflows",
|
|
110
112
|
"rtk-optimization"
|
package/package.json
CHANGED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://colin4k1024.github.io/tsp/schemas/loop-spec.schema.json",
|
|
4
|
+
"title": "TSP Loop Engineering Spec",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["loop"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"loop": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"required": [
|
|
12
|
+
"id",
|
|
13
|
+
"description",
|
|
14
|
+
"cadence",
|
|
15
|
+
"skill",
|
|
16
|
+
"stateFile",
|
|
17
|
+
"gates",
|
|
18
|
+
"maker",
|
|
19
|
+
"checker",
|
|
20
|
+
"budget"
|
|
21
|
+
],
|
|
22
|
+
"additionalProperties": false,
|
|
23
|
+
"properties": {
|
|
24
|
+
"id": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"minLength": 1
|
|
27
|
+
},
|
|
28
|
+
"description": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"minLength": 1
|
|
31
|
+
},
|
|
32
|
+
"cadence": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"pattern": "^[0-9]+[mhd]$"
|
|
35
|
+
},
|
|
36
|
+
"skill": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"minLength": 1
|
|
39
|
+
},
|
|
40
|
+
"stateFile": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"minLength": 1
|
|
43
|
+
},
|
|
44
|
+
"gates": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"minItems": 1,
|
|
47
|
+
"items": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"required": ["name", "command"],
|
|
50
|
+
"additionalProperties": false,
|
|
51
|
+
"properties": {
|
|
52
|
+
"name": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 1
|
|
55
|
+
},
|
|
56
|
+
"command": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1
|
|
59
|
+
},
|
|
60
|
+
"description": {
|
|
61
|
+
"type": "string"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"maker": {
|
|
67
|
+
"$ref": "#/$defs/actor"
|
|
68
|
+
},
|
|
69
|
+
"checker": {
|
|
70
|
+
"$ref": "#/$defs/actor"
|
|
71
|
+
},
|
|
72
|
+
"budget": {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"required": ["maxIterations", "maxDuration", "maxDollars"],
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"properties": {
|
|
77
|
+
"maxIterations": {
|
|
78
|
+
"type": "number",
|
|
79
|
+
"exclusiveMinimum": 0
|
|
80
|
+
},
|
|
81
|
+
"maxDuration": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"pattern": "^[0-9]+[mh]$"
|
|
84
|
+
},
|
|
85
|
+
"maxDollars": {
|
|
86
|
+
"type": "number",
|
|
87
|
+
"exclusiveMinimum": 0
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"escalation": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"additionalProperties": false,
|
|
94
|
+
"properties": {
|
|
95
|
+
"onBudgetExhausted": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"enum": ["triage", "human", "stop"]
|
|
98
|
+
},
|
|
99
|
+
"onSecurityFinding": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"enum": ["human", "triage", "stop"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"$defs": {
|
|
109
|
+
"actor": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"required": ["role", "writeAccess"],
|
|
112
|
+
"additionalProperties": false,
|
|
113
|
+
"properties": {
|
|
114
|
+
"role": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"minLength": 1
|
|
117
|
+
},
|
|
118
|
+
"writeAccess": {
|
|
119
|
+
"type": "boolean"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -18,8 +18,16 @@ const {
|
|
|
18
18
|
appendFile,
|
|
19
19
|
log
|
|
20
20
|
} = require('../lib/utils');
|
|
21
|
+
const { recordCompactEvent } = require('../lib/context-window-state');
|
|
22
|
+
|
|
23
|
+
async function main(rawInput = '{}') {
|
|
24
|
+
let input = {};
|
|
25
|
+
try {
|
|
26
|
+
input = JSON.parse(rawInput || '{}');
|
|
27
|
+
} catch (_) {
|
|
28
|
+
input = {};
|
|
29
|
+
}
|
|
21
30
|
|
|
22
|
-
async function main() {
|
|
23
31
|
const sessionsDir = getSessionsDir();
|
|
24
32
|
const compactionLog = path.join(sessionsDir, 'compaction-log.txt');
|
|
25
33
|
|
|
@@ -27,7 +35,18 @@ async function main() {
|
|
|
27
35
|
|
|
28
36
|
// Log compaction event with timestamp
|
|
29
37
|
const timestamp = getDateTimeString();
|
|
30
|
-
|
|
38
|
+
let compactEvent = null;
|
|
39
|
+
try {
|
|
40
|
+
compactEvent = recordCompactEvent(input, {
|
|
41
|
+
cwd: input.cwd || input.workspace?.current_dir || process.cwd(),
|
|
42
|
+
});
|
|
43
|
+
} catch (_) {
|
|
44
|
+
compactEvent = null;
|
|
45
|
+
}
|
|
46
|
+
const compactCountSuffix = compactEvent
|
|
47
|
+
? ` (session compact #${compactEvent.sessionCompactCount}, total #${compactEvent.totalCompactCount})`
|
|
48
|
+
: '';
|
|
49
|
+
appendFile(compactionLog, `[${timestamp}] Context compaction triggered${compactCountSuffix}\n`);
|
|
31
50
|
|
|
32
51
|
// If there's an active session file, note the compaction
|
|
33
52
|
const sessions = findFiles(sessionsDir, '*-session.tmp');
|
|
@@ -35,14 +54,26 @@ async function main() {
|
|
|
35
54
|
if (sessions.length > 0) {
|
|
36
55
|
const activeSession = sessions[0].path;
|
|
37
56
|
const timeStr = getTimeString();
|
|
38
|
-
|
|
57
|
+
const countNote = compactEvent ? ` (compact #${compactEvent.sessionCompactCount})` : '';
|
|
58
|
+
appendFile(activeSession, `\n---\n**[Compaction occurred at ${timeStr}${countNote}]** - Context was summarized\n`);
|
|
39
59
|
}
|
|
40
60
|
|
|
41
|
-
log(
|
|
61
|
+
log(`[PreCompact] State saved before compaction${compactCountSuffix}`);
|
|
42
62
|
process.exit(0);
|
|
43
63
|
}
|
|
44
64
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
if (require.main === module) {
|
|
66
|
+
let input = '';
|
|
67
|
+
const stdinTimeout = setTimeout(() => main(input), 5000);
|
|
68
|
+
process.stdin.setEncoding('utf8');
|
|
69
|
+
process.stdin.on('data', chunk => { input += chunk; });
|
|
70
|
+
process.stdin.on('end', () => {
|
|
71
|
+
clearTimeout(stdinTimeout);
|
|
72
|
+
main(input).catch(err => {
|
|
73
|
+
console.error('[PreCompact] Error:', err.message);
|
|
74
|
+
process.exit(0);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { main };
|
|
@@ -11,34 +11,17 @@
|
|
|
11
11
|
* session start output so the user knows they can resume with `/goal resume`.
|
|
12
12
|
*
|
|
13
13
|
* Behavior:
|
|
14
|
-
* 1. Scans
|
|
14
|
+
* 1. Scans the TSP loop state goals store for active or paused goal files
|
|
15
15
|
* 2. If found, appends a summary to the hook output
|
|
16
16
|
* 3. Non-blocking: failures are silently ignored
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
const fs = require('fs');
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
function getGoalsDir() {
|
|
23
|
-
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
24
|
-
return path.join(home, '.claude', 'goals');
|
|
25
|
-
}
|
|
20
|
+
const { listGoals } = require('../lib/loop-state-store');
|
|
26
21
|
|
|
27
22
|
function scanActiveGoals() {
|
|
28
|
-
const dir = getGoalsDir();
|
|
29
|
-
if (!fs.existsSync(dir)) return [];
|
|
30
|
-
|
|
31
23
|
try {
|
|
32
|
-
return
|
|
33
|
-
.filter(f => f.endsWith('.json'))
|
|
34
|
-
.map(f => {
|
|
35
|
-
try {
|
|
36
|
-
return JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));
|
|
37
|
-
} catch {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
.filter(g => g && (g.state === 'active' || g.state === 'paused'))
|
|
24
|
+
return [...listGoals('active'), ...listGoals('paused')]
|
|
42
25
|
.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
|
|
43
26
|
} catch {
|
|
44
27
|
return [];
|