pi-crew 0.5.24 → 0.6.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/CHANGELOG.md CHANGED
@@ -1401,3 +1401,37 @@ correctness+error-handling, and performance+architecture audits across 77 source
1401
1401
 
1402
1402
  ### CI
1403
1403
  - `.github/workflows/ci.yml`: typecheck step re-enabled (was disabled since v0.3.x)
1404
+
1405
+ ## [0.6.0] — Source Tour Patterns Implementation (2026-06-04)
1406
+
1407
+ ### Highlights
1408
+ - **15 patterns** implemented from 63-repo source tour (2,267 LOC)
1409
+ - All patterns pass TypeScript strict mode with 0 errors
1410
+ - 37 skills (including new council skill)
1411
+
1412
+ ### Tier 1 — Quick Wins
1413
+ - **Council skill** (Pattern 5): 3 adversarial roles for critical decisions
1414
+ - **6 lifecycle hooks** (Pattern 12): after_run_complete, after_task_complete, session hooks
1415
+ - **3-tier convention** (Pattern 13): Command→Agent→Skill documentation + effort field
1416
+ - **Pre-step scripts** (Pattern 2): Deterministic scripts before LLM dispatch
1417
+ - **Chain DSL parser** (Pattern 8): step1 -> parallel(step2, step3) -> step4
1418
+
1419
+ ### Tier 2 — Medium-Term
1420
+ - **DAG enhancements** (Pattern 7): findBlockedTasks, getBlockingTasks, topologicalSort
1421
+ - **Drift detection** (Pattern 10): 5 detectors, 2-pass reconciliation
1422
+ - **Hash-based task IDs** (Pattern 11): Base36 + adaptive length + hierarchical
1423
+ - **Iterative retrieval** (Pattern 6): Score → converge → refine loop
1424
+ - **Intercom bridge** (Pattern 9): Worker→orchestrator escalation queue
1425
+ - **Plan templates** (Pattern 15): Built-in standard-review and full-implementation
1426
+
1427
+ ### Tier 3 — Long-Term
1428
+ - **Phase-gated intermediates** (Pattern 1): Disk-persistent step outputs
1429
+ - **Incremental fingerprinting** (Pattern 3): Content hash + structural signature
1430
+ - **4-tier memory** (Pattern 4): Working→Episodic→Semantic→Procedural with Ebbinghaus decay
1431
+ - **Observation system** (Pattern 14): Capture→compress→re-inject with privacy tags
1432
+
1433
+ ### Stats
1434
+ - Test suite: 2698 pass + 1 skip, 0 fail
1435
+ - TypeScript: 0 errors
1436
+ - Skills: 37/37 PASS
1437
+ - New modules: 11 files, 2,267 LOC
@@ -0,0 +1,71 @@
1
+ # Command → Agent → Skill: 3-Tier Pattern
2
+
3
+ > **Origin**: `source/claude-code-best-practice/CLAUDE.md`
4
+ > **Applicable to**: pi-crew v0.5.25+
5
+
6
+ ## The 3 Tiers
7
+
8
+ ```
9
+ User invokes → [Command] → [Agent] → [Skill]
10
+ entry worker reusable
11
+ point + tools capability
12
+ ```
13
+
14
+ ### Tier 1: Command (Entry Point)
15
+ - Maps user intent to an agent
16
+ - Defined in workflow `.md` files as a step
17
+ - Example: `team action='run' team='review'`
18
+
19
+ ### Tier 2: Agent (Specialized Worker)
20
+ - Has a system prompt, model, tools, skills, effort level
21
+ - Defined as `.md` file with YAML frontmatter in `agents/` directory
22
+ - Example:
23
+
24
+ ```markdown
25
+ ---
26
+ name: security-reviewer
27
+ description: Chief Security Officer who finds OWASP Top 10 threats
28
+ tools: read, bash, edit
29
+ model: claude-sonnet-4-20250514
30
+ effort: high
31
+ skills: safe-bash, security-review
32
+ maxTurns: 30
33
+ contextMode: fresh
34
+ ---
35
+
36
+ You are a Chief Security Officer...
37
+ ```
38
+
39
+ ### Tier 3: Skill (Reusable Capability)
40
+ - A `SKILL.md` file with instructions + operating rules
41
+ - Injected into agent context at dispatch time
42
+ - Example: `skills/safe-bash/SKILL.md`
43
+
44
+ ## How to Compose
45
+
46
+ 1. **Define the skill** — Create `skills/my-skill/SKILL.md`
47
+ 2. **Define the agent** — Create `agents/my-agent.md` referencing the skill
48
+ 3. **Define the workflow** — Create `workflows/my-workflow.workflow.md` referencing the agent as a step
49
+
50
+ ```yaml
51
+ # workflows/my-workflow.workflow.md
52
+ steps:
53
+ - name: analyze
54
+ agent: my-agent
55
+ # The agent loads my-skill automatically
56
+ ```
57
+
58
+ ## Agent YAML Frontmatter Reference
59
+
60
+ | Field | Type | Description |
61
+ |-------|------|-------------|
62
+ | `name` | string | Agent identifier |
63
+ | `description` | string | One-line description for routing |
64
+ | `tools` | csv | Tools the agent can use |
65
+ | `model` | string | Model override |
66
+ | `skills` | csv | Skills to inject |
67
+ | `effort` | `low`/`medium`/`high` | Work effort level |
68
+ | `maxTurns` | number | Maximum conversation turns |
69
+ | `contextMode` | `fresh`/`fork` | Context inheritance |
70
+ | `loadMode` | `essential`/`lean` | Tool loading strategy |
71
+ | `thinking` | string | Thinking level override |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-crew",
3
- "version": "0.5.24",
3
+ "version": "0.6.0",
4
4
  "description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
5
5
  "author": "baphuongna",
6
6
  "license": "MIT",
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: council
3
+ description: >
4
+ Spawn 3 adversarial subagents (Skeptic, Pragmatist, Critic) to evaluate a decision,
5
+ architecture choice, or plan. Anti-anchoring: each role receives ONLY the question,
6
+ not conversation history. Aggregates votes into consensus recommendation with dissent tracking.
7
+ Use when facing critical decisions, architecture choices, security tradeoffs, or plan reviews
8
+ where single-perspective analysis is insufficient.
9
+ origin: ECC/skills/council
10
+ ---
11
+
12
+ # Council Pattern — Adversarial Multi-Perspective Decision Making
13
+
14
+ ## When to Use
15
+
16
+ - Evaluating architecture decisions with significant tradeoffs
17
+ - Reviewing security-sensitive design choices
18
+ - Validating implementation plans before execution
19
+ - Resolving ambiguity where multiple valid approaches exist
20
+ - Deciding whether to build, buy, or extend
21
+
22
+ ## Prerequisites
23
+
24
+ - A clearly formulated question or decision to evaluate
25
+ - Sufficient context about the system for meaningful analysis
26
+
27
+ ## Operating Rules
28
+
29
+ 1. You MUST spawn exactly 3 subagents with isolated context (fresh, not forked)
30
+ 2. Each subagent receives ONLY the question — NO conversation history (anti-anchoring)
31
+ 3. You MUST NOT influence any subagent's analysis direction
32
+ 4. You MUST record all 3 votes before forming consensus
33
+ 5. You MUST include dissent in the final recommendation
34
+
35
+ ## Workflow
36
+
37
+ ### Step 1: Formulate the Question
38
+
39
+ Write a clear, neutral question that includes:
40
+ - The decision to be made
41
+ - Relevant constraints (performance, security, timeline)
42
+ - Available options (if known)
43
+ - What "success" looks like
44
+
45
+ DO NOT bias the question toward any particular answer.
46
+
47
+ ### Step 2: Spawn 3 Council Members
48
+
49
+ Launch 3 parallel subagents with these EXACT roles:
50
+
51
+ **Skeptic** (Goal: Find flaws):
52
+ ```
53
+ You are the Skeptic on a council evaluating: [QUESTION]
54
+
55
+ Your role: Find every possible flaw, risk, and failure mode.
56
+ - Challenge assumptions
57
+ - Identify edge cases that break the proposed approach
58
+ - Focus on what could go WRONG
59
+ - Rate your confidence (0.0-1.0) and give a PRO/CON/ABSTAIN position
60
+ - Provide your top 3 risks
61
+
62
+ Output format:
63
+ Position: PRO | CON | ABSTAIN
64
+ Confidence: 0.0-1.0
65
+ Reasoning: [your analysis]
66
+ Top 3 Risks: [list]
67
+ ```
68
+
69
+ **Pragmatist** (Goal: Evaluate tradeoffs):
70
+ ```
71
+ You are the Pragmatist on a council evaluating: [QUESTION]
72
+
73
+ Your role: Weigh practical tradeoffs objectively.
74
+ - Consider implementation cost, maintenance burden, team impact
75
+ - Evaluate time-to-value and opportunity cost
76
+ - Compare against realistic alternatives
77
+ - Rate your confidence (0.0-1.0) and give a PRO/CON/ABSTAIN position
78
+
79
+ Output format:
80
+ Position: PRO | CON | ABSTAIN
81
+ Confidence: 0.0-1.0
82
+ Reasoning: [your analysis]
83
+ Alternatives Considered: [list]
84
+ ```
85
+
86
+ **Critic** (Goal: Stress-test reasoning):
87
+ ```
88
+ You are the Critic on a council evaluating: [QUESTION]
89
+
90
+ Your role: Stress-test the logical foundations of each possible answer.
91
+ - Identify logical fallacies in common arguments for/against
92
+ - Check if the question itself contains hidden assumptions
93
+ - Evaluate whether the stated constraints are real or assumed
94
+ - Rate your confidence (0.0-1.0) and give a PRO/CON/ABSTAIN position
95
+
96
+ Output format:
97
+ Position: PRO | CON | ABSTAIN
98
+ Confidence: 0.0-1.0
99
+ Reasoning: [your analysis]
100
+ Hidden Assumptions: [list]
101
+ ```
102
+
103
+ ### Step 3: Aggregate Votes
104
+
105
+ Collect all 3 responses. Compute consensus:
106
+
107
+ | Vote Pattern | Consensus Level | Action |
108
+ |---|---|---|
109
+ | 3 PRO | **Strong accept** | Proceed with high confidence |
110
+ | 2 PRO, 1 CON | **Weak accept** | Proceed, but address CON dissent |
111
+ | 2 PRO, 1 ABSTAIN | **Accept with uncertainty** | Proceed, investigate ABSTAIN concerns |
112
+ | 1 PRO, 1 CON, 1 ABSTAIN | **No consensus** | Reformulate question or gather more data |
113
+ | 2 CON, 1 PRO | **Weak reject** | Do not proceed; explore alternatives |
114
+ | 3 CON | **Strong reject** | Reject; fundamentally rethink approach |
115
+
116
+ ### Step 4: Output Recommendation
117
+
118
+ ```markdown
119
+ ## Council Decision: [Question Summary]
120
+
121
+ ### Votes
122
+ | Role | Position | Confidence |
123
+ |------|----------|------------|
124
+ | Skeptic | PRO/CON/ABSTAIN | 0.X |
125
+ | Pragmatist | PRO/CON/ABSTAIN | 0.X |
126
+ | Critic | PRO/CON/ABSTAIN | 0.X |
127
+
128
+ ### Consensus: [STRONG ACCEPT | WEAK ACCEPT | NO CONSENSUS | WEAK REJECT | STRONG REJECT]
129
+
130
+ ### Recommendation
131
+ [One-paragraph synthesis]
132
+
133
+ ### Key Insights
134
+ - [Best point from Skeptic]
135
+ - [Best point from Pragmatist]
136
+ - [Best point from Critic]
137
+
138
+ ### Dissent
139
+ [Summary of any dissenting opinions and why they were overruled or remain unresolved]
140
+
141
+ ### Action Items
142
+ - [ ] [Specific next step 1]
143
+ - [ ] [Specific next step 2]
144
+ ```
145
+
146
+ ## Anti-Patterns
147
+
148
+ - DO NOT spawn fewer than 3 roles
149
+ - DO NOT share one subagent's analysis with another (contamination)
150
+ - DO NOT phrase the question to favor a specific outcome
151
+ - DO NOT override the council's consensus without documented justification
152
+ - DO NOT use council for trivial decisions (wastes resources)
153
+
154
+ ## Enforcement — Council Gate
155
+
156
+ Before finalizing a council result, verify:
157
+
158
+ - [ ] All 3 roles spawned with isolated (fresh) context
159
+ - [ ] Each role received ONLY the question, no prior conversation
160
+ - [ ] All 3 votes recorded with confidence scores
161
+ - [ ] Consensus level computed from vote pattern
162
+ - [ ] Dissent explicitly documented (not hidden)
163
+ - [ ] Recommendation includes actionable next steps
@@ -36,6 +36,8 @@ export interface AgentConfig {
36
36
  contextMode?: "fresh" | "fork";
37
37
  /** Maximum turns for this agent. Overrides runtime config if set. */
38
38
  maxTurns?: number;
39
+ /** Effort level for this agent. Controls how much work the agent puts in. */
40
+ effort?: "low" | "medium" | "high";
39
41
  /** Tools to explicitly forbid for this agent. Takes precedence over allowedTools. */
40
42
  disallowedTools?: string[];
41
43
  disabled?: boolean;
@@ -376,6 +376,7 @@ function parseAgentFile(filePath: string, source: ResourceSource): AgentConfig |
376
376
  defaultTools: frontmatter.defaultTools !== undefined ? parseCsv(frontmatter.defaultTools) ?? null : undefined,
377
377
  contextMode: parseContextMode(frontmatter.contextMode),
378
378
  maxTurns: (() => { const n = Number.parseInt(frontmatter.maxTurns, 10); return Number.isFinite(n) && n > 0 ? n : undefined; })(),
379
+ effort: frontmatter.effort === "low" || frontmatter.effort === "medium" || frontmatter.effort === "high" ? frontmatter.effort : undefined,
379
380
  disabled: frontmatter.disabled === "true" || frontmatter.enabled === "false",
380
381
  routing: triggers || useWhen || avoidWhen || cost || category ? { triggers, useWhen, avoidWhen, cost, category } : undefined,
381
382
  };
@@ -1,6 +1,8 @@
1
1
  export type HookName =
2
2
  | "before_run_start"
3
+ | "after_run_complete"
3
4
  | "before_task_start"
5
+ | "after_task_complete"
4
6
  | "task_result"
5
7
  | "before_cancel"
6
8
  | "before_retry"
@@ -8,8 +10,20 @@ export type HookName =
8
10
  | "before_cleanup"
9
11
  | "before_publish"
10
12
  | "session_before_switch"
13
+ | "session_after_connect"
14
+ | "session_after_disconnect"
11
15
  | "run_recovery";
12
16
 
17
+ /**
18
+ * Hook exit codes inspired by claude-mem's lifecycle architecture:
19
+ * - 0 = allow (success)
20
+ * - 1 = warn (non-blocking error, continue)
21
+ * - 2 = block (blocking error, stop)
22
+ */
23
+ export const HOOK_EXIT_SUCCESS = 0 as const;
24
+ export const HOOK_EXIT_WARN = 1 as const;
25
+ export const HOOK_EXIT_BLOCK = 2 as const;
26
+
13
27
  export type HookMode = "blocking" | "non_blocking";
14
28
  export type HookOutcome = "allow" | "block" | "modify" | "diagnostic";
15
29
 
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Chain/Parallel DSL Parser — parses workflow chain expressions.
3
+ *
4
+ * Syntax:
5
+ * step1 -> step2 -> parallel(step3, step4) -> step5
6
+ * step1:3 -> step2 --with-context -> step3
7
+ * parallel(a, b, parallel(c, d)) -> e
8
+ *
9
+ * Pattern origin: pi-prompt-template-model chain-parser.ts
10
+ */
11
+
12
+ export interface ChainStep {
13
+ /** Step name (maps to agent or workflow step ID) */
14
+ name: string;
15
+ /** Nested parallel group */
16
+ parallel?: ChainStep[];
17
+ /** Loop count (default: 1) */
18
+ loopCount?: number;
19
+ /** Pass predecessor output as context */
20
+ withContext?: boolean;
21
+ /** Positional arguments */
22
+ args?: string[];
23
+ }
24
+
25
+ /**
26
+ * Parse a chain DSL string into an AST.
27
+ *
28
+ * @throws {Error} on syntax errors (unclosed parens, empty names, etc.)
29
+ */
30
+ export function parseChainDSL(input: string): ChainStep[] {
31
+ const tokens = tokenize(input);
32
+ const parser = new ChainParser(tokens);
33
+ return parser.parse();
34
+ }
35
+
36
+ // ── Tokenizer ────────────────────────────────────────────────────────────
37
+
38
+ type TokenType = "NAME" | "ARROW" | "LPAREN" | "RPAREN" | "COMMA" | "COLON" | "NUMBER" | "FLAG" | "QUOTED";
39
+
40
+ interface Token {
41
+ type: TokenType;
42
+ value: string;
43
+ }
44
+
45
+ function tokenize(input: string): Token[] {
46
+ const tokens: Token[] = [];
47
+ let i = 0;
48
+
49
+ while (i < input.length) {
50
+ // Skip whitespace
51
+ if (/\s/.test(input[i]!)) { i++; continue; }
52
+
53
+ // Arrow ->
54
+ if (input[i] === "-" && input[i + 1] === ">") {
55
+ tokens.push({ type: "ARROW", value: "->" });
56
+ i += 2; continue;
57
+ }
58
+
59
+ // Punctuation
60
+ if (input[i] === "(") { tokens.push({ type: "LPAREN", value: "(" }); i++; continue; }
61
+ if (input[i] === ")") { tokens.push({ type: "RPAREN", value: ")" }); i++; continue; }
62
+ if (input[i] === ",") { tokens.push({ type: "COMMA", value: "," }); i++; continue; }
63
+ if (input[i] === ":") { tokens.push({ type: "COLON", value: ":" }); i++; continue; }
64
+
65
+ // Quoted argument
66
+ if (input[i] === '"' || input[i] === "'") {
67
+ const quote = input[i];
68
+ let str = "";
69
+ i++; // skip opening quote
70
+ while (i < input.length && input[i] !== quote) {
71
+ if (input[i] === "\\" && i + 1 < input.length) { i++; str += input[i]; }
72
+ else { str += input[i]!; }
73
+ i++;
74
+ }
75
+ if (i >= input.length) throw new Error("Unclosed quoted string in chain DSL");
76
+ i++; // skip closing quote
77
+ tokens.push({ type: "QUOTED", value: str });
78
+ continue;
79
+ }
80
+
81
+ // Flag --with-context
82
+ if (input[i] === "-" && input[i + 1] === "-") {
83
+ let flag = "";
84
+ i += 2;
85
+ while (i < input.length && /[a-zA-Z0-9_-]/.test(input[i]!)) { flag += input[i]; i++; }
86
+ tokens.push({ type: "FLAG", value: flag });
87
+ continue;
88
+ }
89
+
90
+ // Number
91
+ if (/[0-9]/.test(input[i]!)) {
92
+ let num = "";
93
+ while (i < input.length && /[0-9]/.test(input[i]!)) { num += input[i]; i++; }
94
+ tokens.push({ type: "NUMBER", value: num });
95
+ continue;
96
+ }
97
+
98
+ // Name
99
+ if (/[a-zA-Z_]/.test(input[i]!)) {
100
+ let name = "";
101
+ while (i < input.length && /[a-zA-Z0-9_.-]/.test(input[i]!)) { name += input[i]; i++; }
102
+ tokens.push({ type: "NAME", value: name });
103
+ continue;
104
+ }
105
+
106
+ throw new Error(`Unexpected character '${input[i]}' at position ${i} in chain DSL`);
107
+ }
108
+
109
+ return tokens;
110
+ }
111
+
112
+ // ── Recursive Descent Parser ─────────────────────────────────────────────
113
+
114
+ class ChainParser {
115
+ private pos = 0;
116
+
117
+ constructor(private tokens: Token[]) {}
118
+
119
+ parse(): ChainStep[] {
120
+ const steps: ChainStep[] = [];
121
+ steps.push(this.parseStep());
122
+ while (this.peek("ARROW")) {
123
+ this.consume("ARROW");
124
+ steps.push(this.parseStep());
125
+ }
126
+ if (this.pos < this.tokens.length) {
127
+ throw new Error(`Unexpected token '${this.tokens[this.pos]?.value}' at position ${this.pos}`);
128
+ }
129
+ return steps;
130
+ }
131
+
132
+ private parseStep(): ChainStep {
133
+ // Check for parallel(...) construct
134
+ if (this.peek("NAME", "parallel")) {
135
+ this.consume("NAME"); // eat "parallel"
136
+ this.consume("LPAREN");
137
+ const parallel: ChainStep[] = [];
138
+ parallel.push(this.parseStep());
139
+ while (this.peek("COMMA")) {
140
+ this.consume("COMMA");
141
+ parallel.push(this.parseStep());
142
+ }
143
+ this.consume("RPAREN");
144
+ const step: ChainStep = { name: "parallel", parallel };
145
+ this.parseModifiers(step);
146
+ return step;
147
+ }
148
+
149
+ // Normal step name
150
+ const name = this.consume("NAME").value;
151
+ const step: ChainStep = { name };
152
+
153
+ // Parse modifiers
154
+ this.parseModifiers(step);
155
+ return step;
156
+ }
157
+
158
+ private parseModifiers(step: ChainStep): void {
159
+ while (this.pos < this.tokens.length) {
160
+ if (this.peek("COLON")) {
161
+ this.consume("COLON");
162
+ const num = this.consume("NUMBER");
163
+ step.loopCount = Number.parseInt(num.value, 10);
164
+ } else if (this.peek("FLAG", "with-context")) {
165
+ this.consume("FLAG");
166
+ step.withContext = true;
167
+ } else if (this.peek("QUOTED")) {
168
+ const arg = this.consume("QUOTED");
169
+ step.args = step.args ?? [];
170
+ step.args.push(arg.value);
171
+ } else {
172
+ break;
173
+ }
174
+ }
175
+ }
176
+
177
+ private peek(type: TokenType, value?: string): boolean {
178
+ const tok = this.tokens[this.pos];
179
+ if (!tok) return false;
180
+ if (tok.type !== type) return false;
181
+ if (value !== undefined && tok.value !== value) return false;
182
+ return true;
183
+ }
184
+
185
+ private consume(type: TokenType): Token {
186
+ const tok = this.tokens[this.pos];
187
+ if (!tok) throw new Error(`Expected ${type} but reached end of chain DSL`);
188
+ if (tok.type !== type) throw new Error(`Expected ${type} but got ${tok.type}('${tok.value}')`);
189
+ this.pos++;
190
+ return tok;
191
+ }
192
+ }