@leing2021/super-pi 0.22.1 → 0.23.0
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/README.md +253 -211
- package/extensions/ce-core/index.ts +190 -7
- package/extensions/ce-core/tools/parallel-subagent.ts +3 -3
- package/extensions/ce-core/tools/subagent.ts +2 -2
- package/package.json +4 -20
- package/rules/common/naming.md +154 -0
- package/skills/02-plan/SKILL.md +3 -2
- package/skills/03-work/SKILL.md +9 -6
- package/skills/04-review/SKILL.md +3 -2
- package/skills/06-next/SKILL.md +38 -10
- package/skills/06-next/references/recommendation-logic.md +2 -2
- package/skills/07-worktree/SKILL.md +9 -8
- package/skills/{09-help → 08-help}/SKILL.md +3 -3
- package/skills/{09-help → 08-help}/references/workflow-sequence.md +2 -1
- package/skills/references/language-detection.md +44 -0
- package/LICENSE +0 -21
- package/THIRD-PARTY-NOTICES.md +0 -35
- package/extensions/subagent/__tests__/async-job-tracker.test.ts +0 -21
- package/extensions/subagent/__tests__/execution-activity.test.ts +0 -15
- package/extensions/subagent/__tests__/parallel-render-stress.test.ts +0 -82
- package/extensions/subagent/__tests__/render-dedup.test.ts +0 -98
- package/extensions/subagent/__tests__/render-widget.test.ts +0 -28
- package/extensions/subagent/__tests__/throttle.test.ts +0 -90
- package/extensions/subagent/agent-management.ts +0 -596
- package/extensions/subagent/agent-manager-chain-detail.ts +0 -163
- package/extensions/subagent/agent-manager-detail.ts +0 -232
- package/extensions/subagent/agent-manager-edit.ts +0 -391
- package/extensions/subagent/agent-manager-list.ts +0 -279
- package/extensions/subagent/agent-manager-parallel.ts +0 -305
- package/extensions/subagent/agent-manager.ts +0 -706
- package/extensions/subagent/agent-scope.ts +0 -9
- package/extensions/subagent/agent-selection.ts +0 -26
- package/extensions/subagent/agent-serializer.ts +0 -124
- package/extensions/subagent/agent-templates.ts +0 -63
- package/extensions/subagent/agents/context-builder.md +0 -37
- package/extensions/subagent/agents/delegate.md +0 -9
- package/extensions/subagent/agents/oracle.md +0 -73
- package/extensions/subagent/agents/planner.md +0 -52
- package/extensions/subagent/agents/researcher.md +0 -50
- package/extensions/subagent/agents/reviewer.md +0 -38
- package/extensions/subagent/agents/scout.md +0 -48
- package/extensions/subagent/agents/worker.md +0 -52
- package/extensions/subagent/agents.ts +0 -762
- package/extensions/subagent/artifacts.ts +0 -101
- package/extensions/subagent/async-execution.ts +0 -521
- package/extensions/subagent/async-job-tracker.ts +0 -240
- package/extensions/subagent/async-status.ts +0 -242
- package/extensions/subagent/chain-clarify.ts +0 -1365
- package/extensions/subagent/chain-execution.ts +0 -854
- package/extensions/subagent/chain-serializer.ts +0 -127
- package/extensions/subagent/completion-dedupe.ts +0 -66
- package/extensions/subagent/doctor.ts +0 -201
- package/extensions/subagent/execution.ts +0 -748
- package/extensions/subagent/file-coalescer.ts +0 -43
- package/extensions/subagent/fork-context.ts +0 -64
- package/extensions/subagent/formatters.ts +0 -123
- package/extensions/subagent/frontmatter.ts +0 -32
- package/extensions/subagent/index.ts +0 -586
- package/extensions/subagent/intercom-bridge.ts +0 -241
- package/extensions/subagent/jsonl-writer.ts +0 -84
- package/extensions/subagent/model-fallback.ts +0 -109
- package/extensions/subagent/notify.ts +0 -111
- package/extensions/subagent/parallel-utils.ts +0 -109
- package/extensions/subagent/pi-args.ts +0 -139
- package/extensions/subagent/pi-spawn.ts +0 -101
- package/extensions/subagent/post-exit-stdio-guard.ts +0 -88
- package/extensions/subagent/prompt-template-bridge.ts +0 -400
- package/extensions/subagent/prompts/gather-context-and-clarify.md +0 -13
- package/extensions/subagent/prompts/parallel-cleanup.md +0 -42
- package/extensions/subagent/prompts/parallel-research.md +0 -50
- package/extensions/subagent/prompts/parallel-review.md +0 -40
- package/extensions/subagent/render-helpers.ts +0 -83
- package/extensions/subagent/render.ts +0 -908
- package/extensions/subagent/result-intercom.ts +0 -238
- package/extensions/subagent/result-watcher.ts +0 -172
- package/extensions/subagent/run-history.ts +0 -58
- package/extensions/subagent/run-status.ts +0 -137
- package/extensions/subagent/schemas.ts +0 -165
- package/extensions/subagent/session-tokens.ts +0 -51
- package/extensions/subagent/settings.ts +0 -368
- package/extensions/subagent/single-output.ts +0 -98
- package/extensions/subagent/skills.ts +0 -627
- package/extensions/subagent/slash-bridge.ts +0 -177
- package/extensions/subagent/slash-commands.ts +0 -304
- package/extensions/subagent/slash-live-state.ts +0 -295
- package/extensions/subagent/subagent-control.ts +0 -151
- package/extensions/subagent/subagent-executor.ts +0 -1915
- package/extensions/subagent/subagent-prompt-runtime.ts +0 -76
- package/extensions/subagent/subagent-runner.ts +0 -1471
- package/extensions/subagent/subagents-status.ts +0 -473
- package/extensions/subagent/text-editor.ts +0 -273
- package/extensions/subagent/throttle.ts +0 -77
- package/extensions/subagent/top-level-async.ts +0 -16
- package/extensions/subagent/types.ts +0 -624
- package/extensions/subagent/utils.ts +0 -457
- package/extensions/subagent/worktree.ts +0 -580
- package/extensions/super-pi-extension/agents/ce-oracle.md +0 -109
- package/extensions/super-pi-extension/agents/ce-planner.md +0 -116
- package/extensions/super-pi-extension/agents/ce-reviewer.md +0 -99
- package/extensions/super-pi-extension/agents/ce-scout.md +0 -80
- package/extensions/super-pi-extension/agents/ce-worker.md +0 -78
- package/extensions/super-pi-extension/chains/ce-parallel-review.chain.md +0 -34
- package/extensions/super-pi-extension/chains/ce-review-only.chain.md +0 -12
- package/extensions/super-pi-extension/chains/ce-standard.chain.md +0 -28
- package/extensions/super-pi-extension/index.ts +0 -129
- package/extensions/super-pi-extension/model-sync.ts +0 -204
- package/skills/08-status/SKILL.md +0 -41
- package/skills/08-status/references/artifact-locations.md +0 -10
- package/skills/10-rules/SKILL.md +0 -76
- package/skills/pi-subagents/SKILL.md +0 -566
package/skills/06-next/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: 06-next
|
|
3
|
-
description: Inspect workflow state and recommend the single best next Compound Engineering skill.
|
|
3
|
+
description: "Inspect workflow state and recommend the single best next Compound Engineering skill. Use --verbose for a full status report."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Next
|
|
7
7
|
|
|
8
|
-
Use this skill when the user wants to know what to run next in the Compound Engineering workflow.
|
|
8
|
+
Use this skill when the user wants to know what to run next in the Compound Engineering workflow, or wants a full status report of the current project state.
|
|
9
9
|
|
|
10
10
|
## Core rules
|
|
11
11
|
|
|
@@ -15,18 +15,43 @@ Use this skill when the user wants to know what to run next in the Compound Engi
|
|
|
15
15
|
- Do not execute the recommended skill — only suggest it.
|
|
16
16
|
- If the workflow state suggests multiple valid paths, pick the one closest to completing a full loop.
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Two modes
|
|
19
|
+
|
|
20
|
+
### Default mode: "what's next?"
|
|
21
|
+
|
|
22
|
+
When the user asks "what should I do next?", "continue", or runs `/skill:06-next`:
|
|
23
|
+
|
|
24
|
+
1. Call `workflow_state` with the repo root.
|
|
25
|
+
2. Apply the recommendation logic from `references/recommendation-logic.md`.
|
|
26
|
+
3. Return:
|
|
27
|
+
- **Recommended skill name**
|
|
28
|
+
- **Why** this is the next step
|
|
29
|
+
- **Brief** summary of current workflow state (1-2 lines)
|
|
30
|
+
|
|
31
|
+
### Verbose mode: "full status"
|
|
32
|
+
|
|
33
|
+
When the user asks "show status", "what's the current state", or explicitly requests a detailed report:
|
|
19
34
|
|
|
20
35
|
1. Call `workflow_state` with the repo root.
|
|
21
|
-
2.
|
|
22
|
-
3.
|
|
23
|
-
|
|
36
|
+
2. Call `session_history` with `latest` operation to check recent executions.
|
|
37
|
+
3. Return:
|
|
38
|
+
- Latest relevant brainstorm (path + brief summary)
|
|
39
|
+
- Latest relevant plan (path + brief summary)
|
|
40
|
+
- Latest relevant solution (path + brief summary)
|
|
41
|
+
- Latest runtime artifacts under `.context/compound-engineering/`
|
|
42
|
+
- Recommended next step with reason
|
|
43
|
+
- Overall workflow phase summary
|
|
24
44
|
|
|
25
|
-
##
|
|
45
|
+
## Artifact locations
|
|
26
46
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
| Artifact | Path |
|
|
48
|
+
|---|---|
|
|
49
|
+
| Brainstorm | `docs/brainstorms/` |
|
|
50
|
+
| Plan | `docs/plans/` |
|
|
51
|
+
| Solution | `docs/solutions/` |
|
|
52
|
+
| Runtime | `.context/compound-engineering/` |
|
|
53
|
+
|
|
54
|
+
If `workflow_state` is not available, fall back to `bash` with `ls` and `find` to check which directories and recent files exist, then use `read` on the most relevant recent artifact.
|
|
30
55
|
|
|
31
56
|
## Available skills
|
|
32
57
|
|
|
@@ -35,3 +60,6 @@ Use this skill when the user wants to know what to run next in the Compound Engi
|
|
|
35
60
|
- `03-work` — execute the plan
|
|
36
61
|
- `04-review` — review changes with structured findings
|
|
37
62
|
- `05-learn` — capture learnings as solution artifacts
|
|
63
|
+
- `07-worktree` — isolated git worktree development
|
|
64
|
+
|
|
65
|
+
Before finishing this skill, apply the completion checklist in [shared pipeline instructions](../references/pipeline-config.md).
|
|
@@ -42,5 +42,5 @@ If brainstorm, plan, and solution all exist:
|
|
|
42
42
|
## Fallback
|
|
43
43
|
|
|
44
44
|
If no rule matches cleanly:
|
|
45
|
-
-
|
|
46
|
-
-
|
|
45
|
+
- Summarize the ambiguous state
|
|
46
|
+
- Ask the user what they want to focus on
|
|
@@ -3,25 +3,26 @@ name: 07-worktree
|
|
|
3
3
|
description: Create and manage git worktrees for isolated feature development.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Worktree
|
|
7
7
|
|
|
8
|
-
Use this skill when you need
|
|
8
|
+
Use this skill when you need optional isolated git worktree development for large, risky, or parallel feature work, then merge changes back or clean up after completion.
|
|
9
9
|
|
|
10
10
|
## Core rules
|
|
11
11
|
|
|
12
12
|
- Use the `worktree_manager` tool for all git worktree operations.
|
|
13
|
-
-
|
|
13
|
+
- Create a worktree only when the user explicitly asks for isolation, or when the task is large/risky enough and the user confirms.
|
|
14
|
+
- Use a descriptive branch name derived from the plan or task.
|
|
14
15
|
- After creation, report the worktree path so that `03-work` can execute inside it.
|
|
15
|
-
- On completion, merge back and clean up.
|
|
16
|
+
- On completion, offer to merge back and clean up; ask for confirmation before changing the main worktree or deleting branches.
|
|
16
17
|
- Never force operations — if a worktree already exists, report its status instead of creating a duplicate.
|
|
17
18
|
|
|
18
19
|
## Workflow
|
|
19
20
|
|
|
20
|
-
1. **
|
|
21
|
-
2. **
|
|
21
|
+
1. **Detect**: Use `worktree_manager` with operation `detect` to check if already inside a worktree.
|
|
22
|
+
2. **Create**: When confirmed, use `worktree_manager` with operation `create` to spin up a new worktree with a feature branch.
|
|
22
23
|
3. **Execute**: Run `03-work` inside the worktree directory.
|
|
23
|
-
4. **Merge**:
|
|
24
|
-
5. **Cleanup**:
|
|
24
|
+
4. **Merge**: After user confirmation, use `worktree_manager` with operation `merge` to merge the feature branch back to main.
|
|
25
|
+
5. **Cleanup**: After user confirmation, use `worktree_manager` with operation `cleanup` to remove the worktree and delete the branch.
|
|
25
26
|
|
|
26
27
|
## Output
|
|
27
28
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: 08-help
|
|
3
3
|
description: Explain when to use each Compound Engineering Phase 1 skill and how they connect.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -14,7 +14,7 @@ Use this skill when the user asks how to use the package, which workflow step co
|
|
|
14
14
|
- `03-work` — use when there is a plan or a tightly scoped task ready for controlled execution.
|
|
15
15
|
- `04-review` — use after code changes to produce structured findings. After code review, user can optionally extend to browser-based QA testing (find visual/functional bugs with agent-browser) and regression test generation.
|
|
16
16
|
- `05-learn` — use after solving a problem so the repo gains a durable learning in `docs/solutions/`.
|
|
17
|
-
- `
|
|
17
|
+
- `06-next` — use when the user wants to know what to run next, or wants a full status report of the current project state. Default mode recommends one next step; verbose mode (`--verbose` or ask "show status") shows full artifact details.
|
|
18
18
|
|
|
19
19
|
## Recommended flow
|
|
20
20
|
|
|
@@ -30,7 +30,7 @@ Use this skill when the user asks how to use the package, which workflow step co
|
|
|
30
30
|
- Optionally extend to **Browser QA** for visual and functional bug finding.
|
|
31
31
|
- Or **Browser QA + Regression Tests** to also generate automated test coverage.
|
|
32
32
|
5. Capture key learnings with `05-learn`.
|
|
33
|
-
6. Use `
|
|
33
|
+
6. Use `06-next` at any point to check what to do next, or ask for a full status report.
|
|
34
34
|
|
|
35
35
|
## Output
|
|
36
36
|
|
|
@@ -6,4 +6,5 @@
|
|
|
6
6
|
4. `04-review` → inspect the changes (optionally + Browser QA / QA + Regression Tests)
|
|
7
7
|
5. `05-learn` → save key learnings
|
|
8
8
|
|
|
9
|
-
Use `
|
|
9
|
+
Use `06-next` at any point to see where you are and what to do next.
|
|
10
|
+
Use `07-worktree` for isolated feature development on a separate branch.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Language Detection
|
|
2
|
+
|
|
3
|
+
Detect the project's primary language by checking for these files in the repo root:
|
|
4
|
+
|
|
5
|
+
| File(s) | Language | Rules directory |
|
|
6
|
+
|---------|----------|----------------|
|
|
7
|
+
| `tsconfig.json` | TypeScript | `rules/typescript/` |
|
|
8
|
+
| `package.json` (without `tsconfig.json`) | JavaScript | `rules/typescript/` |
|
|
9
|
+
| `Cargo.toml` | Rust | `rules/rust/` |
|
|
10
|
+
| `go.mod` | Go | `rules/golang/` |
|
|
11
|
+
| `pubspec.yaml` | Dart | `rules/dart/` |
|
|
12
|
+
| `pom.xml` / `build.gradle` | Java | `rules/java/` |
|
|
13
|
+
| `*.sln` / `*.csproj` | C# | `rules/csharp/` |
|
|
14
|
+
| `Package.swift` | Swift | `rules/swift/` |
|
|
15
|
+
| `requirements.txt` / `pyproject.toml` / `setup.py` | Python | `rules/python/` |
|
|
16
|
+
| `composer.json` | PHP | `rules/php/` |
|
|
17
|
+
| `Makefile.PL` / `cpanfile` | Perl | `rules/perl/` |
|
|
18
|
+
| `build.gradle.kts` | Kotlin | `rules/kotlin/` |
|
|
19
|
+
|
|
20
|
+
## Rules loading strategy
|
|
21
|
+
|
|
22
|
+
Rules are loaded from two locations with priority:
|
|
23
|
+
|
|
24
|
+
1. **Project-level** `{repo-root}/rules/` — takes priority, survives `pi update`.
|
|
25
|
+
2. **Package-level** `rules/` in the super-pi package — built-in defaults.
|
|
26
|
+
|
|
27
|
+
Check project-level first. If a file exists there for the topic, use it. Otherwise fall back to package-level.
|
|
28
|
+
|
|
29
|
+
## Rule precedence
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
language-specific > web > common
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Override mapping by topic:
|
|
36
|
+
- `common/testing.md` ← `web/testing.md` ← `<lang>/testing.md`
|
|
37
|
+
- `common/coding-style.md` ← `web/coding-style.md` ← `<lang>/coding-style.md`
|
|
38
|
+
- `common/patterns.md` ← `web/patterns.md` ← `<lang>/patterns.md`
|
|
39
|
+
- `common/security.md` ← `web/security.md` ← `<lang>/security.md`
|
|
40
|
+
- `common/hooks.md` ← `web/hooks.md` ← `<lang>/hooks.md`
|
|
41
|
+
|
|
42
|
+
## Do not load all rules at once
|
|
43
|
+
|
|
44
|
+
Only load what the current task needs. See each skill's SKILL.md for phase-specific loading instructions.
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025-2026 leing2021
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/THIRD-PARTY-NOTICES.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Third-Party Notices
|
|
2
|
-
|
|
3
|
-
## pi-subagents
|
|
4
|
-
|
|
5
|
-
This software includes source code from the **pi-subagents** project.
|
|
6
|
-
|
|
7
|
-
- **Author**: Nico Bailon
|
|
8
|
-
- **Repository**: https://github.com/nicobailon/pi-subagents
|
|
9
|
-
- **License**: MIT
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
MIT License
|
|
13
|
-
|
|
14
|
-
Copyright (c) 2025 Nico Bailon
|
|
15
|
-
|
|
16
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
17
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
18
|
-
in the Software without restriction, including without limitation the rights
|
|
19
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
20
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
21
|
-
furnished to do so, subject to the following conditions:
|
|
22
|
-
|
|
23
|
-
The above copyright notice and this permission notice shall be included in all
|
|
24
|
-
copies or substantial portions of the Software.
|
|
25
|
-
|
|
26
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
27
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
28
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
29
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
32
|
-
SOFTWARE.
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The pi-subagents source code is located in `extensions/subagent/`.
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { resolvePollInterval } from "../async-job-tracker.ts";
|
|
3
|
-
|
|
4
|
-
describe("resolvePollInterval", () => {
|
|
5
|
-
it("returns 1000ms as default (no running jobs)", () => {
|
|
6
|
-
expect(resolvePollInterval(0)).toBe(1000);
|
|
7
|
-
expect(resolvePollInterval(1)).toBe(1000);
|
|
8
|
-
expect(resolvePollInterval(2)).toBe(1000);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it("returns 1500ms for 3-4 running jobs", () => {
|
|
12
|
-
expect(resolvePollInterval(3)).toBe(1500);
|
|
13
|
-
expect(resolvePollInterval(4)).toBe(1500);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("returns 2000ms for 5+ running jobs", () => {
|
|
17
|
-
expect(resolvePollInterval(5)).toBe(2000);
|
|
18
|
-
expect(resolvePollInterval(8)).toBe(2000);
|
|
19
|
-
expect(resolvePollInterval(16)).toBe(2000);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { resolveActivityTimerInterval } from "../execution.ts";
|
|
3
|
-
|
|
4
|
-
describe("resolveActivityTimerInterval", () => {
|
|
5
|
-
it("returns 2000ms as default (single subprocess)", () => {
|
|
6
|
-
expect(resolveActivityTimerInterval()).toBe(2000);
|
|
7
|
-
expect(resolveActivityTimerInterval(1)).toBe(2000);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("returns 3000ms for parallel scenarios (2+ concurrent)", () => {
|
|
11
|
-
expect(resolveActivityTimerInterval(2)).toBe(3000);
|
|
12
|
-
expect(resolveActivityTimerInterval(4)).toBe(3000);
|
|
13
|
-
expect(resolveActivityTimerInterval(8)).toBe(3000);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import { createThrottle, resolveThrottleInterval } from "../throttle.ts";
|
|
3
|
-
import { resolveWidgetInterval } from "../render.ts";
|
|
4
|
-
import { resolveActivityTimerInterval } from "../execution.ts";
|
|
5
|
-
import { resolvePollInterval } from "../async-job-tracker.ts";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Integration stress test: simulates 8 parallel subagents firing events
|
|
9
|
-
* and validates that total render frequency stays within bounds.
|
|
10
|
-
*/
|
|
11
|
-
describe("parallel render stress", () => {
|
|
12
|
-
beforeEach(() => { vi.useFakeTimers(); });
|
|
13
|
-
afterEach(() => { vi.useRealTimers(); });
|
|
14
|
-
|
|
15
|
-
it("limits onUpdate to ≤5 triggers/s with 8 parallel tasks", () => {
|
|
16
|
-
const interval = resolveThrottleInterval(8);
|
|
17
|
-
expect(interval).toBe(500);
|
|
18
|
-
|
|
19
|
-
const fn = vi.fn();
|
|
20
|
-
const throttled = createThrottle(fn, interval);
|
|
21
|
-
|
|
22
|
-
// Simulate 100 rapid fireUpdate calls in 1ms intervals (like 8 processes sending events)
|
|
23
|
-
for (let i = 0; i < 100; i++) {
|
|
24
|
-
throttled({ index: i });
|
|
25
|
-
vi.advanceTimersByTime(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// First call executes immediately, then at most 1 call per 500ms
|
|
29
|
-
// Over 100ms simulated time: 1 (immediate) + ~0 (no 500ms passed yet) = 1 call
|
|
30
|
-
expect(fn.mock.calls.length).toBeGreaterThanOrEqual(1);
|
|
31
|
-
expect(fn.mock.calls.length).toBeLessThanOrEqual(2);
|
|
32
|
-
|
|
33
|
-
throttled.dispose();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("allows at most 2 calls in 500ms for 8 tasks (500ms throttle)", () => {
|
|
37
|
-
const interval = resolveThrottleInterval(8);
|
|
38
|
-
const fn = vi.fn();
|
|
39
|
-
const throttled = createThrottle(fn, interval);
|
|
40
|
-
|
|
41
|
-
// Immediate first call
|
|
42
|
-
throttled("a");
|
|
43
|
-
expect(fn).toHaveBeenCalledTimes(1);
|
|
44
|
-
|
|
45
|
-
// Fire many more
|
|
46
|
-
for (let i = 0; i < 50; i++) throttled(`b-${i}`);
|
|
47
|
-
|
|
48
|
-
// Advance 500ms — trailing fires
|
|
49
|
-
vi.advanceTimersByTime(500);
|
|
50
|
-
expect(fn).toHaveBeenCalledTimes(2); // immediate + 1 trailing
|
|
51
|
-
|
|
52
|
-
throttled.dispose();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("widget animation interval adapts to 1000ms at 7+ running jobs", () => {
|
|
56
|
-
expect(resolveWidgetInterval(7)).toBe(1000);
|
|
57
|
-
expect(resolveWidgetInterval(8)).toBe(1000);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("activity timer adapts to 3000ms for parallel scenarios", () => {
|
|
61
|
-
expect(resolveActivityTimerInterval(8)).toBe(3000);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("poll interval adapts to 2000ms at 5+ running jobs", () => {
|
|
65
|
-
expect(resolvePollInterval(5)).toBe(2000);
|
|
66
|
-
expect(resolvePollInterval(8)).toBe(2000);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("total theoretical render rate with 8 parallel subagents is ≤ 8/s", () => {
|
|
70
|
-
const widgetRate = 1000 / resolveWidgetInterval(8); // renders/s
|
|
71
|
-
const pollRate = 1000 / resolvePollInterval(8); // polls/s
|
|
72
|
-
const throttleRate = 1000 / resolveThrottleInterval(8); // onUpdate/s
|
|
73
|
-
const activityRate = 1000 / resolveActivityTimerInterval(8); // activity checks/s
|
|
74
|
-
|
|
75
|
-
// Widget animation + poll + onUpdate throttle + activity are the main render sources
|
|
76
|
-
// Note: not all fire requestRender every time
|
|
77
|
-
const totalMaxRate = widgetRate + pollRate + throttleRate + activityRate;
|
|
78
|
-
|
|
79
|
-
// With all optimizations, should be well under 8/s total
|
|
80
|
-
expect(totalMaxRate).toBeLessThanOrEqual(8);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "bun:test";
|
|
2
|
-
|
|
3
|
-
describe("render dedup", () => {
|
|
4
|
-
beforeEach(async () => {
|
|
5
|
-
const { _testResetWidgetState } = await import("../render.ts");
|
|
6
|
-
_testResetWidgetState();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it("should not call requestRender when widget data is identical", async () => {
|
|
10
|
-
const { renderWidget, stopWidgetAnimation } = await import("../render.ts");
|
|
11
|
-
|
|
12
|
-
const setWidget = vi.fn();
|
|
13
|
-
const requestRender = vi.fn();
|
|
14
|
-
const theme = {
|
|
15
|
-
fg: (_color: string, text: string) => text,
|
|
16
|
-
bold: (text: string) => text,
|
|
17
|
-
};
|
|
18
|
-
const ctx = {
|
|
19
|
-
hasUI: true,
|
|
20
|
-
ui: { setWidget, requestRender, theme },
|
|
21
|
-
} as any;
|
|
22
|
-
|
|
23
|
-
const jobs = [
|
|
24
|
-
{ asyncId: "test-1", asyncDir: "/tmp/test", status: "complete" as const, updatedAt: 1000 },
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
// First render — should call requestRender (new content)
|
|
28
|
-
renderWidget(ctx, jobs);
|
|
29
|
-
expect(requestRender).toHaveBeenCalledTimes(1);
|
|
30
|
-
|
|
31
|
-
// Second render with EXACTLY same data — should NOT call requestRender
|
|
32
|
-
renderWidget(ctx, jobs);
|
|
33
|
-
expect(requestRender).toHaveBeenCalledTimes(1); // Still 1
|
|
34
|
-
|
|
35
|
-
stopWidgetAnimation();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("should call requestRender when widget data changes", async () => {
|
|
39
|
-
const { renderWidget, stopWidgetAnimation } = await import("../render.ts");
|
|
40
|
-
|
|
41
|
-
const setWidget = vi.fn();
|
|
42
|
-
const requestRender = vi.fn();
|
|
43
|
-
const theme = {
|
|
44
|
-
fg: (_color: string, text: string) => text,
|
|
45
|
-
bold: (text: string) => text,
|
|
46
|
-
};
|
|
47
|
-
const ctx = {
|
|
48
|
-
hasUI: true,
|
|
49
|
-
ui: { setWidget, requestRender, theme },
|
|
50
|
-
} as any;
|
|
51
|
-
|
|
52
|
-
// First render
|
|
53
|
-
const jobs1 = [
|
|
54
|
-
{ asyncId: "test-1", asyncDir: "/tmp/test", status: "running" as const, updatedAt: 1000 },
|
|
55
|
-
];
|
|
56
|
-
renderWidget(ctx, jobs1);
|
|
57
|
-
expect(requestRender).toHaveBeenCalledTimes(1);
|
|
58
|
-
|
|
59
|
-
// Second render with changed updatedAt — should call requestRender
|
|
60
|
-
const jobs2 = [
|
|
61
|
-
{ asyncId: "test-1", asyncDir: "/tmp/test", status: "running" as const, updatedAt: 2000 },
|
|
62
|
-
];
|
|
63
|
-
renderWidget(ctx, jobs2);
|
|
64
|
-
expect(requestRender).toHaveBeenCalledTimes(2);
|
|
65
|
-
|
|
66
|
-
stopWidgetAnimation();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("should call requestRender on status transitions", async () => {
|
|
70
|
-
const { renderWidget, stopWidgetAnimation } = await import("../render.ts");
|
|
71
|
-
|
|
72
|
-
const setWidget = vi.fn();
|
|
73
|
-
const requestRender = vi.fn();
|
|
74
|
-
const theme = {
|
|
75
|
-
fg: (_color: string, text: string) => text,
|
|
76
|
-
bold: (text: string) => text,
|
|
77
|
-
};
|
|
78
|
-
const ctx = {
|
|
79
|
-
hasUI: true,
|
|
80
|
-
ui: { setWidget, requestRender, theme },
|
|
81
|
-
} as any;
|
|
82
|
-
|
|
83
|
-
const jobs1 = [
|
|
84
|
-
{ asyncId: "test-1", asyncDir: "/tmp/test", status: "running" as const, updatedAt: 1000 },
|
|
85
|
-
];
|
|
86
|
-
renderWidget(ctx, jobs1);
|
|
87
|
-
expect(requestRender).toHaveBeenCalledTimes(1);
|
|
88
|
-
|
|
89
|
-
// Status changes to complete — should trigger requestRender
|
|
90
|
-
const jobs2 = [
|
|
91
|
-
{ asyncId: "test-1", asyncDir: "/tmp/test", status: "complete" as const, updatedAt: 2000 },
|
|
92
|
-
];
|
|
93
|
-
renderWidget(ctx, jobs2);
|
|
94
|
-
expect(requestRender).toHaveBeenCalledTimes(2);
|
|
95
|
-
|
|
96
|
-
stopWidgetAnimation();
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
resolveWidgetInterval,
|
|
4
|
-
} from "../render.ts";
|
|
5
|
-
|
|
6
|
-
describe("resolveWidgetInterval", () => {
|
|
7
|
-
it("returns 250ms for 1-2 running jobs", () => {
|
|
8
|
-
expect(resolveWidgetInterval(0)).toBe(250);
|
|
9
|
-
expect(resolveWidgetInterval(1)).toBe(250);
|
|
10
|
-
expect(resolveWidgetInterval(2)).toBe(250);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("returns 400ms for 3-4 running jobs", () => {
|
|
14
|
-
expect(resolveWidgetInterval(3)).toBe(400);
|
|
15
|
-
expect(resolveWidgetInterval(4)).toBe(400);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("returns 600ms for 5-6 running jobs", () => {
|
|
19
|
-
expect(resolveWidgetInterval(5)).toBe(600);
|
|
20
|
-
expect(resolveWidgetInterval(6)).toBe(600);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("returns 1000ms for 7+ running jobs", () => {
|
|
24
|
-
expect(resolveWidgetInterval(7)).toBe(1000);
|
|
25
|
-
expect(resolveWidgetInterval(8)).toBe(1000);
|
|
26
|
-
expect(resolveWidgetInterval(16)).toBe(1000);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import { createThrottle, resolveThrottleInterval } from "../throttle.ts";
|
|
3
|
-
|
|
4
|
-
describe("createThrottle", () => {
|
|
5
|
-
beforeEach(() => { vi.useFakeTimers(); });
|
|
6
|
-
afterEach(() => { vi.useRealTimers(); });
|
|
7
|
-
|
|
8
|
-
it("executes the first call immediately", () => {
|
|
9
|
-
const fn = vi.fn();
|
|
10
|
-
const throttled = createThrottle(fn, 200);
|
|
11
|
-
throttled("a");
|
|
12
|
-
expect(fn).toHaveBeenCalledTimes(1);
|
|
13
|
-
expect(fn).toHaveBeenCalledWith("a");
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("drops calls within the interval, keeping only the latest args", () => {
|
|
17
|
-
const fn = vi.fn();
|
|
18
|
-
const throttled = createThrottle(fn, 200);
|
|
19
|
-
throttled("a");
|
|
20
|
-
throttled("b");
|
|
21
|
-
throttled("c");
|
|
22
|
-
expect(fn).toHaveBeenCalledTimes(1);
|
|
23
|
-
expect(fn).toHaveBeenCalledWith("a");
|
|
24
|
-
|
|
25
|
-
vi.advanceTimersByTime(200);
|
|
26
|
-
expect(fn).toHaveBeenCalledTimes(2);
|
|
27
|
-
expect(fn).toHaveBeenCalledWith("c");
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("does not fire trailing call if no pending args", () => {
|
|
31
|
-
const fn = vi.fn();
|
|
32
|
-
const throttled = createThrottle(fn, 200);
|
|
33
|
-
throttled("a");
|
|
34
|
-
expect(fn).toHaveBeenCalledTimes(1);
|
|
35
|
-
|
|
36
|
-
vi.advanceTimersByTime(200);
|
|
37
|
-
// No new call after "a", so no trailing fire
|
|
38
|
-
expect(fn).toHaveBeenCalledTimes(1);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("flush() immediately executes the latest pending args", () => {
|
|
42
|
-
const fn = vi.fn();
|
|
43
|
-
const throttled = createThrottle(fn, 200);
|
|
44
|
-
throttled("a");
|
|
45
|
-
throttled("b");
|
|
46
|
-
throttled.flush();
|
|
47
|
-
expect(fn).toHaveBeenCalledTimes(2);
|
|
48
|
-
expect(fn).toHaveBeenCalledWith("b");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("flush() does nothing if no pending call", () => {
|
|
52
|
-
const fn = vi.fn();
|
|
53
|
-
const throttled = createThrottle(fn, 200);
|
|
54
|
-
throttled.flush();
|
|
55
|
-
expect(fn).toHaveBeenCalledTimes(0);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("dispose() clears timers and prevents future trailing calls", () => {
|
|
59
|
-
const fn = vi.fn();
|
|
60
|
-
const throttled = createThrottle(fn, 200);
|
|
61
|
-
throttled("a");
|
|
62
|
-
throttled("b");
|
|
63
|
-
throttled.dispose();
|
|
64
|
-
vi.advanceTimersByTime(500);
|
|
65
|
-
expect(fn).toHaveBeenCalledTimes(1);
|
|
66
|
-
expect(fn).toHaveBeenCalledWith("a");
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe("resolveThrottleInterval", () => {
|
|
71
|
-
it("returns 0 for 1 task (no throttle)", () => {
|
|
72
|
-
expect(resolveThrottleInterval(1)).toBe(0);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("returns 200ms for 2-4 tasks", () => {
|
|
76
|
-
expect(resolveThrottleInterval(2)).toBe(200);
|
|
77
|
-
expect(resolveThrottleInterval(3)).toBe(200);
|
|
78
|
-
expect(resolveThrottleInterval(4)).toBe(200);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("returns 500ms for 5-8 tasks", () => {
|
|
82
|
-
expect(resolveThrottleInterval(5)).toBe(500);
|
|
83
|
-
expect(resolveThrottleInterval(8)).toBe(500);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("returns 1000ms for 9+ tasks", () => {
|
|
87
|
-
expect(resolveThrottleInterval(9)).toBe(1000);
|
|
88
|
-
expect(resolveThrottleInterval(16)).toBe(1000);
|
|
89
|
-
});
|
|
90
|
-
});
|