@chankov/agent-skills 0.2.0 → 0.3.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.
Files changed (178) hide show
  1. package/.claude/commands/{doctor.md → doctor-agent-skills.md} +1 -1
  2. package/.pi/extensions/agent-skills-update-check/README.md +4 -4
  3. package/.pi/prompts/{doctor.md → doctor-agent-skills.md} +1 -1
  4. package/.versions/0.2.0/.claude/commands/{doctor.md → doctor-agent-skills.md} +1 -1
  5. package/.versions/0.2.0/.pi/extensions/agent-skills-update-check/README.md +4 -4
  6. package/.versions/0.2.0/.pi/prompts/{doctor.md → doctor-agent-skills.md} +1 -1
  7. package/.versions/0.2.0/skills/guided-workspace-setup/SKILL.md +40 -2
  8. package/.versions/0.3.0/.claude/commands/build.md +18 -0
  9. package/.versions/0.3.0/.claude/commands/code-simplify.md +22 -0
  10. package/.versions/0.3.0/.claude/commands/design-agent.md +14 -0
  11. package/.versions/0.3.0/.claude/commands/doctor-agent-skills.md +13 -0
  12. package/.versions/0.3.0/.claude/commands/plan.md +16 -0
  13. package/.versions/0.3.0/.claude/commands/prime.md +22 -0
  14. package/.versions/0.3.0/.claude/commands/review.md +16 -0
  15. package/.versions/0.3.0/.claude/commands/setup-agent-skills.md +19 -0
  16. package/.versions/0.3.0/.claude/commands/ship.md +17 -0
  17. package/.versions/0.3.0/.claude/commands/spec.md +15 -0
  18. package/.versions/0.3.0/.claude/commands/test.md +19 -0
  19. package/.versions/0.3.0/.opencode/commands/as-build.md +17 -0
  20. package/.versions/0.3.0/.opencode/commands/as-code-simplify.md +16 -0
  21. package/.versions/0.3.0/.opencode/commands/as-design-agent.md +15 -0
  22. package/.versions/0.3.0/.opencode/commands/as-doctor-agent-skills.md +11 -0
  23. package/.versions/0.3.0/.opencode/commands/as-plan.md +16 -0
  24. package/.versions/0.3.0/.opencode/commands/as-prime.md +22 -0
  25. package/.versions/0.3.0/.opencode/commands/as-review.md +15 -0
  26. package/.versions/0.3.0/.opencode/commands/as-setup-agent-skills.md +11 -0
  27. package/.versions/0.3.0/.opencode/commands/as-ship.md +16 -0
  28. package/.versions/0.3.0/.opencode/commands/as-spec.md +16 -0
  29. package/.versions/0.3.0/.opencode/commands/as-test.md +21 -0
  30. package/.versions/0.3.0/.pi/agents/agent-chain.yaml +49 -0
  31. package/.versions/0.3.0/.pi/agents/bowser.md +19 -0
  32. package/.versions/0.3.0/.pi/agents/pi-pi/agent-expert.md +98 -0
  33. package/.versions/0.3.0/.pi/agents/pi-pi/cli-expert.md +41 -0
  34. package/.versions/0.3.0/.pi/agents/pi-pi/config-expert.md +63 -0
  35. package/.versions/0.3.0/.pi/agents/pi-pi/ext-expert.md +43 -0
  36. package/.versions/0.3.0/.pi/agents/pi-pi/keybinding-expert.md +134 -0
  37. package/.versions/0.3.0/.pi/agents/pi-pi/pi-orchestrator.md +57 -0
  38. package/.versions/0.3.0/.pi/agents/pi-pi/prompt-expert.md +70 -0
  39. package/.versions/0.3.0/.pi/agents/pi-pi/skill-expert.md +42 -0
  40. package/.versions/0.3.0/.pi/agents/pi-pi/theme-expert.md +40 -0
  41. package/.versions/0.3.0/.pi/agents/pi-pi/tui-expert.md +85 -0
  42. package/.versions/0.3.0/.pi/agents/teams.yaml +31 -0
  43. package/.versions/0.3.0/.pi/damage-control-rules.yaml +278 -0
  44. package/.versions/0.3.0/.pi/extensions/agent-skills-update-check/README.md +58 -0
  45. package/.versions/0.3.0/.pi/extensions/agent-skills-update-check/index.ts +161 -0
  46. package/.versions/0.3.0/.pi/extensions/agent-skills-update-check/package.json +6 -0
  47. package/.versions/0.3.0/.pi/extensions/chrome-devtools-mcp/README.md +39 -0
  48. package/.versions/0.3.0/.pi/extensions/chrome-devtools-mcp/index.ts +61 -0
  49. package/.versions/0.3.0/.pi/extensions/chrome-devtools-mcp/package.json +6 -0
  50. package/.versions/0.3.0/.pi/extensions/compact-and-continue/README.md +42 -0
  51. package/.versions/0.3.0/.pi/extensions/compact-and-continue/index.ts +120 -0
  52. package/.versions/0.3.0/.pi/extensions/compact-and-continue/package.json +6 -0
  53. package/.versions/0.3.0/.pi/extensions/mcp-bridge/README.md +46 -0
  54. package/.versions/0.3.0/.pi/extensions/mcp-bridge/index.ts +206 -0
  55. package/.versions/0.3.0/.pi/extensions/mcp-bridge/package.json +6 -0
  56. package/.versions/0.3.0/.pi/extensions/package-lock.json +1143 -0
  57. package/.versions/0.3.0/.pi/extensions/package.json +9 -0
  58. package/.versions/0.3.0/.pi/harnesses/agent-chain/README.md +37 -0
  59. package/.versions/0.3.0/.pi/harnesses/agent-chain/index.ts +795 -0
  60. package/.versions/0.3.0/.pi/harnesses/agent-chain/package.json +6 -0
  61. package/.versions/0.3.0/.pi/harnesses/agent-team/README.md +38 -0
  62. package/.versions/0.3.0/.pi/harnesses/agent-team/index.ts +732 -0
  63. package/.versions/0.3.0/.pi/harnesses/agent-team/package.json +6 -0
  64. package/.versions/0.3.0/.pi/harnesses/coms/README.md +36 -0
  65. package/.versions/0.3.0/.pi/harnesses/coms/index.ts +1595 -0
  66. package/.versions/0.3.0/.pi/harnesses/coms/package.json +6 -0
  67. package/.versions/0.3.0/.pi/harnesses/coms-net/README.md +46 -0
  68. package/.versions/0.3.0/.pi/harnesses/coms-net/index.ts +1637 -0
  69. package/.versions/0.3.0/.pi/harnesses/coms-net/package.json +6 -0
  70. package/.versions/0.3.0/.pi/harnesses/damage-control/README.md +38 -0
  71. package/.versions/0.3.0/.pi/harnesses/damage-control/index.ts +207 -0
  72. package/.versions/0.3.0/.pi/harnesses/damage-control/package.json +6 -0
  73. package/.versions/0.3.0/.pi/harnesses/damage-control-continue/README.md +37 -0
  74. package/.versions/0.3.0/.pi/harnesses/damage-control-continue/index.ts +234 -0
  75. package/.versions/0.3.0/.pi/harnesses/damage-control-continue/package.json +6 -0
  76. package/.versions/0.3.0/.pi/harnesses/minimal/README.md +27 -0
  77. package/.versions/0.3.0/.pi/harnesses/minimal/index.ts +32 -0
  78. package/.versions/0.3.0/.pi/harnesses/minimal/package.json +6 -0
  79. package/.versions/0.3.0/.pi/harnesses/package-lock.json +35 -0
  80. package/.versions/0.3.0/.pi/harnesses/package.json +9 -0
  81. package/.versions/0.3.0/.pi/harnesses/pi-pi/README.md +39 -0
  82. package/.versions/0.3.0/.pi/harnesses/pi-pi/index.ts +631 -0
  83. package/.versions/0.3.0/.pi/harnesses/pi-pi/package.json +6 -0
  84. package/.versions/0.3.0/.pi/harnesses/purpose-gate/README.md +27 -0
  85. package/.versions/0.3.0/.pi/harnesses/purpose-gate/index.ts +82 -0
  86. package/.versions/0.3.0/.pi/harnesses/purpose-gate/package.json +6 -0
  87. package/.versions/0.3.0/.pi/harnesses/session-replay/README.md +28 -0
  88. package/.versions/0.3.0/.pi/harnesses/session-replay/index.ts +214 -0
  89. package/.versions/0.3.0/.pi/harnesses/session-replay/package.json +6 -0
  90. package/.versions/0.3.0/.pi/harnesses/subagent-widget/README.md +36 -0
  91. package/.versions/0.3.0/.pi/harnesses/subagent-widget/index.ts +479 -0
  92. package/.versions/0.3.0/.pi/harnesses/subagent-widget/package.json +6 -0
  93. package/.versions/0.3.0/.pi/harnesses/system-select/README.md +39 -0
  94. package/.versions/0.3.0/.pi/harnesses/system-select/index.ts +165 -0
  95. package/.versions/0.3.0/.pi/harnesses/system-select/package.json +6 -0
  96. package/.versions/0.3.0/.pi/harnesses/tilldone/README.md +35 -0
  97. package/.versions/0.3.0/.pi/harnesses/tilldone/index.ts +724 -0
  98. package/.versions/0.3.0/.pi/harnesses/tilldone/package.json +6 -0
  99. package/.versions/0.3.0/.pi/harnesses/tool-counter/README.md +31 -0
  100. package/.versions/0.3.0/.pi/harnesses/tool-counter/index.ts +100 -0
  101. package/.versions/0.3.0/.pi/harnesses/tool-counter/package.json +6 -0
  102. package/.versions/0.3.0/.pi/harnesses/tool-counter-widget/README.md +27 -0
  103. package/.versions/0.3.0/.pi/harnesses/tool-counter-widget/index.ts +66 -0
  104. package/.versions/0.3.0/.pi/harnesses/tool-counter-widget/package.json +6 -0
  105. package/.versions/0.3.0/.pi/prompts/build.md +24 -0
  106. package/.versions/0.3.0/.pi/prompts/code-simplify.md +22 -0
  107. package/.versions/0.3.0/.pi/prompts/doctor-agent-skills.md +13 -0
  108. package/.versions/0.3.0/.pi/prompts/plan.md +16 -0
  109. package/.versions/0.3.0/.pi/prompts/review.md +16 -0
  110. package/.versions/0.3.0/.pi/prompts/setup-agent-skills.md +19 -0
  111. package/.versions/0.3.0/.pi/prompts/ship.md +17 -0
  112. package/.versions/0.3.0/.pi/prompts/spec.md +15 -0
  113. package/.versions/0.3.0/.pi/prompts/test.md +19 -0
  114. package/.versions/0.3.0/.pi/skills/bowser/SKILL.md +114 -0
  115. package/.versions/0.3.0/.version +1 -0
  116. package/.versions/0.3.0/agents/builder.md +6 -0
  117. package/.versions/0.3.0/agents/code-reviewer.md +93 -0
  118. package/.versions/0.3.0/agents/documenter.md +6 -0
  119. package/.versions/0.3.0/agents/plan-reviewer.md +22 -0
  120. package/.versions/0.3.0/agents/planner.md +6 -0
  121. package/.versions/0.3.0/agents/scout.md +6 -0
  122. package/.versions/0.3.0/agents/security-auditor.md +97 -0
  123. package/.versions/0.3.0/agents/test-engineer.md +89 -0
  124. package/.versions/0.3.0/hooks/SIMPLIFY-IGNORE.md +90 -0
  125. package/.versions/0.3.0/hooks/hooks.json +14 -0
  126. package/.versions/0.3.0/hooks/session-start.sh +74 -0
  127. package/.versions/0.3.0/hooks/simplify-ignore-test.sh +247 -0
  128. package/.versions/0.3.0/hooks/simplify-ignore.sh +302 -0
  129. package/.versions/0.3.0/references/accessibility-checklist.md +159 -0
  130. package/.versions/0.3.0/references/performance-checklist.md +121 -0
  131. package/.versions/0.3.0/references/prompting-patterns.md +380 -0
  132. package/.versions/0.3.0/references/security-checklist.md +134 -0
  133. package/.versions/0.3.0/references/testing-patterns.md +236 -0
  134. package/.versions/0.3.0/skills/api-and-interface-design/SKILL.md +294 -0
  135. package/.versions/0.3.0/skills/browser-testing-with-devtools/SKILL.md +335 -0
  136. package/.versions/0.3.0/skills/ci-cd-and-automation/SKILL.md +390 -0
  137. package/.versions/0.3.0/skills/code-review-and-quality/SKILL.md +347 -0
  138. package/.versions/0.3.0/skills/code-simplification/SKILL.md +331 -0
  139. package/.versions/0.3.0/skills/context-engineering/SKILL.md +291 -0
  140. package/.versions/0.3.0/skills/debugging-and-error-recovery/SKILL.md +300 -0
  141. package/.versions/0.3.0/skills/deprecation-and-migration/SKILL.md +206 -0
  142. package/.versions/0.3.0/skills/designing-agents/SKILL.md +394 -0
  143. package/.versions/0.3.0/skills/designing-agents/pi-harness-authoring.md +213 -0
  144. package/.versions/0.3.0/skills/documentation-and-adrs/SKILL.md +278 -0
  145. package/.versions/0.3.0/skills/frontend-ui-engineering/SKILL.md +322 -0
  146. package/.versions/0.3.0/skills/git-workflow-and-versioning/SKILL.md +316 -0
  147. package/.versions/0.3.0/skills/guided-workspace-setup/SKILL.md +331 -0
  148. package/.versions/0.3.0/skills/idea-refine/SKILL.md +178 -0
  149. package/.versions/0.3.0/skills/idea-refine/examples.md +238 -0
  150. package/.versions/0.3.0/skills/idea-refine/frameworks.md +99 -0
  151. package/.versions/0.3.0/skills/idea-refine/refinement-criteria.md +113 -0
  152. package/.versions/0.3.0/skills/idea-refine/scripts/idea-refine.sh +15 -0
  153. package/.versions/0.3.0/skills/incremental-implementation/SKILL.md +279 -0
  154. package/.versions/0.3.0/skills/performance-optimization/SKILL.md +350 -0
  155. package/.versions/0.3.0/skills/planning-and-task-breakdown/SKILL.md +237 -0
  156. package/.versions/0.3.0/skills/security-and-hardening/SKILL.md +349 -0
  157. package/.versions/0.3.0/skills/shipping-and-launch/SKILL.md +309 -0
  158. package/.versions/0.3.0/skills/source-driven-development/SKILL.md +194 -0
  159. package/.versions/0.3.0/skills/spec-driven-development/SKILL.md +237 -0
  160. package/.versions/0.3.0/skills/test-driven-development/SKILL.md +379 -0
  161. package/.versions/0.3.0/skills/using-agent-skills/SKILL.md +176 -0
  162. package/CHANGELOG.md +72 -0
  163. package/README.md +5 -5
  164. package/bin/cli.js +100 -24
  165. package/bin/lib/bootstrap.js +254 -0
  166. package/bin/lib/doctor.js +1 -1
  167. package/docs/getting-started.md +2 -2
  168. package/docs/npm-install.md +34 -11
  169. package/package.json +1 -1
  170. package/skills/guided-workspace-setup/SKILL.md +40 -2
  171. /package/.claude/commands/{setup.md → setup-agent-skills.md} +0 -0
  172. /package/.opencode/commands/{as-doctor.md → as-doctor-agent-skills.md} +0 -0
  173. /package/.opencode/commands/{as-setup.md → as-setup-agent-skills.md} +0 -0
  174. /package/.pi/prompts/{setup.md → setup-agent-skills.md} +0 -0
  175. /package/.versions/0.2.0/.claude/commands/{setup.md → setup-agent-skills.md} +0 -0
  176. /package/.versions/0.2.0/.opencode/commands/{as-doctor.md → as-doctor-agent-skills.md} +0 -0
  177. /package/.versions/0.2.0/.opencode/commands/{as-setup.md → as-setup-agent-skills.md} +0 -0
  178. /package/.versions/0.2.0/.pi/prompts/{setup.md → setup-agent-skills.md} +0 -0
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "agent-skills-pi-coms-net",
3
+ "private": true,
4
+ "type": "module",
5
+ "main": "index.ts"
6
+ }
@@ -0,0 +1,38 @@
1
+ # damage-control
2
+
3
+ Safety auditing — blocks destructive tool calls.
4
+
5
+ > Ported from [`pi-vs-claude-code`](https://github.com/disler/pi-vs-claude-code) by [disler](https://github.com/disler) (MIT). See the [extension catalog](../../../docs/pi-extensions.md).
6
+
7
+ ## What it does
8
+
9
+ Intercepts every tool call and checks it against rules in `.pi/damage-control-rules.yaml`:
10
+
11
+ - `bashToolPatterns` — destructive shell commands (`rm -rf`, `git reset --hard`,
12
+ `DROP TABLE`, cloud-resource deletes, …); some are hard-blocked, some marked `ask`
13
+ - `zeroAccessPaths` — secrets and credentials that must never be read (`.env`, `*.pem`, …)
14
+ - `readOnlyPaths` — paths that may be read but not written (lockfiles, build output, …)
15
+ - `noDeletePaths` — paths that may be edited but not deleted (`README`, `.git/`, …)
16
+
17
+ On a match the tool result is replaced with a block message and the agent's turn is
18
+ aborted (`ctx.abort()`). For a variant that lets the agent keep working with corrective
19
+ feedback instead of aborting, use [`damage-control-continue`](../damage-control-continue/README.md).
20
+
21
+ ## Commands & tools
22
+
23
+ None — it runs passively on the `tool_call` event.
24
+
25
+ ## Requires
26
+
27
+ - `.pi/damage-control-rules.yaml` — the rule set (shipped in this repo)
28
+
29
+ ## Usage
30
+
31
+ ```bash
32
+ pi -e .pi/harnesses/damage-control/index.ts
33
+ ```
34
+
35
+ ## Upstream changes
36
+
37
+ - Theme integration removed — the `themeMap.ts` import and the `applyExtensionDefaults()`
38
+ call were stripped (this repo does not ship pi themes).
@@ -0,0 +1,207 @@
1
+ import type { ExtensionAPI, ToolCallEvent } from "@mariozechner/pi-coding-agent";
2
+ import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
3
+ import { parse as yamlParse } from "yaml";
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+ import * as os from "os";
7
+
8
+ interface Rule {
9
+ pattern: string;
10
+ reason: string;
11
+ ask?: boolean;
12
+ }
13
+
14
+ interface Rules {
15
+ bashToolPatterns: Rule[];
16
+ zeroAccessPaths: string[];
17
+ readOnlyPaths: string[];
18
+ noDeletePaths: string[];
19
+ }
20
+
21
+ export default function (pi: ExtensionAPI) {
22
+ let rules: Rules = {
23
+ bashToolPatterns: [],
24
+ zeroAccessPaths: [],
25
+ readOnlyPaths: [],
26
+ noDeletePaths: [],
27
+ };
28
+
29
+ function resolvePath(p: string, cwd: string): string {
30
+ if (p.startsWith("~")) {
31
+ p = path.join(os.homedir(), p.slice(1));
32
+ }
33
+ return path.resolve(cwd, p);
34
+ }
35
+
36
+ function isPathMatch(targetPath: string, pattern: string, cwd: string): boolean {
37
+ // Simple glob-to-regex or substring match
38
+ // Expand tilde in pattern if present
39
+ const resolvedPattern = pattern.startsWith("~") ? path.join(os.homedir(), pattern.slice(1)) : pattern;
40
+
41
+ // If pattern ends with /, it's a directory match
42
+ if (resolvedPattern.endsWith("/")) {
43
+ const absolutePattern = path.isAbsolute(resolvedPattern) ? resolvedPattern : path.resolve(cwd, resolvedPattern);
44
+ return targetPath.startsWith(absolutePattern);
45
+ }
46
+
47
+ // Handle basic wildcards *
48
+ const regexPattern = resolvedPattern
49
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&") // escape regex chars
50
+ .replace(/\*/g, ".*"); // convert * to .*
51
+
52
+ const regex = new RegExp(`^${regexPattern}$|^${regexPattern}/|/${regexPattern}$|/${regexPattern}/`);
53
+
54
+ // Match against absolute path and relative-to-cwd path
55
+ const relativePath = path.relative(cwd, targetPath);
56
+
57
+ return regex.test(targetPath) || regex.test(relativePath) || targetPath.includes(resolvedPattern) || relativePath.includes(resolvedPattern);
58
+ }
59
+
60
+ pi.on("session_start", async (_event, ctx) => {
61
+ const projectRulesPath = path.join(ctx.cwd, ".pi", "damage-control-rules.yaml");
62
+ const globalRulesPath = path.join(os.homedir(), ".pi", "damage-control-rules.yaml");
63
+ const rulesPath = fs.existsSync(projectRulesPath) ? projectRulesPath : fs.existsSync(globalRulesPath) ? globalRulesPath : null;
64
+ try {
65
+ if (rulesPath) {
66
+ const content = fs.readFileSync(rulesPath, "utf8");
67
+ const loaded = yamlParse(content) as Partial<Rules>;
68
+ rules = {
69
+ bashToolPatterns: loaded.bashToolPatterns || [],
70
+ zeroAccessPaths: loaded.zeroAccessPaths || [],
71
+ readOnlyPaths: loaded.readOnlyPaths || [],
72
+ noDeletePaths: loaded.noDeletePaths || [],
73
+ };
74
+ const source = rulesPath === projectRulesPath ? "project" : "global";
75
+ ctx.ui.notify(`🛡️ Damage-Control: Loaded ${rules.bashToolPatterns.length + rules.zeroAccessPaths.length + rules.readOnlyPaths.length + rules.noDeletePaths.length} rules (${source}).`);
76
+ } else {
77
+ ctx.ui.notify("🛡️ Damage-Control: No rules found at .pi/damage-control-rules.yaml (project or global)");
78
+ }
79
+ } catch (err) {
80
+ ctx.ui.notify(`🛡️ Damage-Control: Failed to load rules: ${err instanceof Error ? err.message : String(err)}`);
81
+ }
82
+
83
+ ctx.ui.setStatus(`🛡️ Damage-Control Active: ${rules.bashToolPatterns.length + rules.zeroAccessPaths.length + rules.readOnlyPaths.length + rules.noDeletePaths.length} Rules`);
84
+ });
85
+
86
+ pi.on("tool_call", async (event, ctx) => {
87
+ let violationReason: string | null = null;
88
+ let shouldAsk = false;
89
+
90
+ // 1. Check Zero Access Paths for all tools that use path or glob
91
+ const checkPaths = (pathsToCheck: string[]) => {
92
+ for (const p of pathsToCheck) {
93
+ const resolved = resolvePath(p, ctx.cwd);
94
+ for (const zap of rules.zeroAccessPaths) {
95
+ if (isPathMatch(resolved, zap, ctx.cwd)) {
96
+ return `Access to zero-access path restricted: ${zap}`;
97
+ }
98
+ }
99
+ }
100
+ return null;
101
+ };
102
+
103
+ // Extract paths from tool input
104
+ const inputPaths: string[] = [];
105
+ if (isToolCallEventType("read", event) || isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
106
+ inputPaths.push(event.input.path);
107
+ } else if (isToolCallEventType("grep", event) || isToolCallEventType("find", event) || isToolCallEventType("ls", event)) {
108
+ inputPaths.push(event.input.path || ".");
109
+ }
110
+
111
+ if (isToolCallEventType("grep", event) && event.input.glob) {
112
+ // Check glob field as well
113
+ for (const zap of rules.zeroAccessPaths) {
114
+ if (event.input.glob.includes(zap) || isPathMatch(event.input.glob, zap, ctx.cwd)) {
115
+ violationReason = `Glob matches zero-access path: ${zap}`;
116
+ break;
117
+ }
118
+ }
119
+ }
120
+
121
+ if (!violationReason) {
122
+ violationReason = checkPaths(inputPaths);
123
+ }
124
+
125
+ // 2. Tool-specific logic
126
+ if (!violationReason) {
127
+ if (isToolCallEventType("bash", event)) {
128
+ const command = event.input.command;
129
+
130
+ // Check bashToolPatterns
131
+ for (const rule of rules.bashToolPatterns) {
132
+ const regex = new RegExp(rule.pattern);
133
+ if (regex.test(command)) {
134
+ violationReason = rule.reason;
135
+ shouldAsk = !!rule.ask;
136
+ break;
137
+ }
138
+ }
139
+
140
+ // Check if bash command interacts with restricted paths
141
+ if (!violationReason) {
142
+ for (const zap of rules.zeroAccessPaths) {
143
+ if (command.includes(zap)) {
144
+ violationReason = `Bash command references zero-access path: ${zap}`;
145
+ break;
146
+ }
147
+ }
148
+ }
149
+
150
+ if (!violationReason) {
151
+ for (const rop of rules.readOnlyPaths) {
152
+ // Heuristic: check if command might modify a read-only path
153
+ // Redirects, sed -i, rm, mv to, etc.
154
+ if (command.includes(rop) && (/[\s>|]/.test(command) || command.includes("rm") || command.includes("mv") || command.includes("sed"))) {
155
+ violationReason = `Bash command may modify read-only path: ${rop}`;
156
+ break;
157
+ }
158
+ }
159
+ }
160
+
161
+ if (!violationReason) {
162
+ for (const ndp of rules.noDeletePaths) {
163
+ if (command.includes(ndp) && (command.includes("rm") || command.includes("mv"))) {
164
+ violationReason = `Bash command attempts to delete/move protected path: ${ndp}`;
165
+ break;
166
+ }
167
+ }
168
+ }
169
+ } else if (isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
170
+ // Check Read-Only paths
171
+ for (const p of inputPaths) {
172
+ const resolved = resolvePath(p, ctx.cwd);
173
+ for (const rop of rules.readOnlyPaths) {
174
+ if (isPathMatch(resolved, rop, ctx.cwd)) {
175
+ violationReason = `Modification of read-only path restricted: ${rop}`;
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ if (violationReason) {
184
+ if (shouldAsk) {
185
+ const confirmed = await ctx.ui.confirm("🛡️ Damage-Control Confirmation", `Dangerous command detected: ${violationReason}\n\nCommand: ${isToolCallEventType("bash", event) ? event.input.command : JSON.stringify(event.input)}\n\nDo you want to proceed?`, { timeout: 30000 });
186
+
187
+ if (!confirmed) {
188
+ ctx.ui.setStatus(`⚠️ Last Violation Blocked: ${violationReason.slice(0, 30)}...`);
189
+ pi.appendEntry("damage-control-log", { tool: event.toolName, input: event.input, rule: violationReason, action: "blocked_by_user" });
190
+ ctx.abort();
191
+ return { block: true, reason: `🛑 BLOCKED by Damage-Control: ${violationReason} (User denied)\n\nDO NOT attempt to work around this restriction. DO NOT retry with alternative commands, paths, or approaches that achieve the same result. Report this block to the user exactly as stated and ask how they would like to proceed.` };
192
+ } else {
193
+ pi.appendEntry("damage-control-log", { tool: event.toolName, input: event.input, rule: violationReason, action: "confirmed_by_user" });
194
+ return { block: false };
195
+ }
196
+ } else {
197
+ ctx.ui.notify(`🛑 Damage-Control: Blocked ${event.toolName} due to ${violationReason}`);
198
+ ctx.ui.setStatus(`⚠️ Last Violation: ${violationReason.slice(0, 30)}...`);
199
+ pi.appendEntry("damage-control-log", { tool: event.toolName, input: event.input, rule: violationReason, action: "blocked" });
200
+ ctx.abort();
201
+ return { block: true, reason: `🛑 BLOCKED by Damage-Control: ${violationReason}\n\nDO NOT attempt to work around this restriction. DO NOT retry with alternative commands, paths, or approaches that achieve the same result. Report this block to the user exactly as stated and ask how they would like to proceed.` };
202
+ }
203
+ }
204
+
205
+ return { block: false };
206
+ });
207
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "agent-skills-pi-damage-control",
3
+ "private": true,
4
+ "type": "module",
5
+ "main": "index.ts"
6
+ }
@@ -0,0 +1,37 @@
1
+ # damage-control-continue
2
+
3
+ Safety auditing that lets the agent recover instead of aborting.
4
+
5
+ > Ported from [`pi-vs-claude-code`](https://github.com/disler/pi-vs-claude-code) by [disler](https://github.com/disler) (MIT). See the [extension catalog](../../../docs/pi-extensions.md).
6
+
7
+ ## What it does
8
+
9
+ Same rule engine as [`damage-control`](../damage-control/README.md) — it reads the same
10
+ `.pi/damage-control-rules.yaml` — but it differs in how a blocked call is handled:
11
+
12
+ - the blocked tool result is replaced with **actionable feedback** that distinguishes
13
+ destructive intent from merely non-destructive intent and tells the agent how to adapt;
14
+ - it does **not** call `ctx.abort()`, so the agent's turn continues and it can try an
15
+ alternate path (e.g. assume a `.env` key exists rather than reading the file to verify).
16
+
17
+ Use this when you want guard rails without killing the turn; use plain `damage-control`
18
+ when a hard stop is preferred.
19
+
20
+ ## Commands & tools
21
+
22
+ None — it runs passively on the `tool_call` event.
23
+
24
+ ## Requires
25
+
26
+ - `.pi/damage-control-rules.yaml` — the rule set (shipped in this repo)
27
+
28
+ ## Usage
29
+
30
+ ```bash
31
+ pi -e .pi/harnesses/damage-control-continue/index.ts
32
+ ```
33
+
34
+ ## Upstream changes
35
+
36
+ - Theme integration removed — the `themeMap.ts` import and the `applyExtensionDefaults()`
37
+ call were stripped (this repo does not ship pi themes).
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Damage-Control (continue) — same rules, but the agent keeps working
3
+ *
4
+ * Difference from damage-control.ts:
5
+ * - The blocked tool result is replaced with actionable feedback that
6
+ * distinguishes destructive vs non-destructive intent and tells the
7
+ * agent how to adapt.
8
+ * - We do NOT call ctx.abort(), so the agent's turn continues and can
9
+ * try an alternate path (e.g. assume a .env key exists instead of
10
+ * reading it to verify).
11
+ *
12
+ * Usage: pi -e extensions/damage-control-continue.ts
13
+ */
14
+
15
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
16
+ import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
17
+ import { parse as yamlParse } from "yaml";
18
+ import * as fs from "fs";
19
+ import * as path from "path";
20
+ import * as os from "os";
21
+
22
+ interface Rule {
23
+ pattern: string;
24
+ reason: string;
25
+ ask?: boolean;
26
+ }
27
+
28
+ interface Rules {
29
+ bashToolPatterns: Rule[];
30
+ zeroAccessPaths: string[];
31
+ readOnlyPaths: string[];
32
+ noDeletePaths: string[];
33
+ }
34
+
35
+ function continueFeedback(toolName: string, violationReason: string, invocation: string): string {
36
+ return [
37
+ `🛡️ Damage-Control: ${toolName} blocked — ${violationReason}`,
38
+ ``,
39
+ `Attempted: ${invocation}`,
40
+ ``,
41
+ `Don't call ${toolName} directly like this. Decide which case you're in and continue:`,
42
+ ``,
43
+ `→ NON-DESTRUCTIVE (e.g. reading .env to verify a key, listing a protected dir, peeking at config):`,
44
+ ` Assume the data is present and correct. Skip the verification step and move on with the task.`,
45
+ ` Example: if you were reading .env to confirm a key exists, just assume it does — the user has`,
46
+ ` configured their environment. If you actually need a value, ask the user for it explicitly.`,
47
+ ``,
48
+ `→ DESTRUCTIVE (delete, overwrite, force-push, drop, rm, truncate, sudo, kill, etc.):`,
49
+ ` STOP. Tell the user exactly what you need to ship this task and ask how they want to proceed.`,
50
+ ` Do not invent a workaround that achieves the same destructive effect.`,
51
+ ``,
52
+ `Pick the right path above and continue working. Do not retry this exact call.`,
53
+ ].join("\n");
54
+ }
55
+
56
+ export default function (pi: ExtensionAPI) {
57
+ let rules: Rules = {
58
+ bashToolPatterns: [],
59
+ zeroAccessPaths: [],
60
+ readOnlyPaths: [],
61
+ noDeletePaths: [],
62
+ };
63
+
64
+ function resolvePath(p: string, cwd: string): string {
65
+ if (p.startsWith("~")) {
66
+ p = path.join(os.homedir(), p.slice(1));
67
+ }
68
+ return path.resolve(cwd, p);
69
+ }
70
+
71
+ function isPathMatch(targetPath: string, pattern: string, cwd: string): boolean {
72
+ const resolvedPattern = pattern.startsWith("~") ? path.join(os.homedir(), pattern.slice(1)) : pattern;
73
+
74
+ if (resolvedPattern.endsWith("/")) {
75
+ const absolutePattern = path.isAbsolute(resolvedPattern) ? resolvedPattern : path.resolve(cwd, resolvedPattern);
76
+ return targetPath.startsWith(absolutePattern);
77
+ }
78
+
79
+ const regexPattern = resolvedPattern
80
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
81
+ .replace(/\*/g, ".*");
82
+
83
+ const regex = new RegExp(`^${regexPattern}$|^${regexPattern}/|/${regexPattern}$|/${regexPattern}/`);
84
+
85
+ const relativePath = path.relative(cwd, targetPath);
86
+
87
+ return regex.test(targetPath) || regex.test(relativePath) || targetPath.includes(resolvedPattern) || relativePath.includes(resolvedPattern);
88
+ }
89
+
90
+ pi.on("session_start", async (_event, ctx) => {
91
+ const projectRulesPath = path.join(ctx.cwd, ".pi", "damage-control-rules.yaml");
92
+ const globalRulesPath = path.join(os.homedir(), ".pi", "damage-control-rules.yaml");
93
+ const rulesPath = fs.existsSync(projectRulesPath) ? projectRulesPath : fs.existsSync(globalRulesPath) ? globalRulesPath : null;
94
+ try {
95
+ if (rulesPath) {
96
+ const content = fs.readFileSync(rulesPath, "utf8");
97
+ const loaded = yamlParse(content) as Partial<Rules>;
98
+ rules = {
99
+ bashToolPatterns: loaded.bashToolPatterns || [],
100
+ zeroAccessPaths: loaded.zeroAccessPaths || [],
101
+ readOnlyPaths: loaded.readOnlyPaths || [],
102
+ noDeletePaths: loaded.noDeletePaths || [],
103
+ };
104
+ const source = rulesPath === projectRulesPath ? "project" : "global";
105
+ const total = rules.bashToolPatterns.length + rules.zeroAccessPaths.length + rules.readOnlyPaths.length + rules.noDeletePaths.length;
106
+ ctx.ui.notify(`🛡️ Damage-Control (continue): Loaded ${total} rules (${source}). Blocks deliver feedback so the agent can adapt and keep working.`);
107
+ } else {
108
+ ctx.ui.notify("🛡️ Damage-Control (continue): No rules found at .pi/damage-control-rules.yaml (project or global)");
109
+ }
110
+ } catch (err) {
111
+ ctx.ui.notify(`🛡️ Damage-Control (continue): Failed to load rules: ${err instanceof Error ? err.message : String(err)}`);
112
+ }
113
+
114
+ const total = rules.bashToolPatterns.length + rules.zeroAccessPaths.length + rules.readOnlyPaths.length + rules.noDeletePaths.length;
115
+ ctx.ui.setStatus(`🛡️ Damage-Control (continue): ${total} Rules`);
116
+ });
117
+
118
+ pi.on("tool_call", async (event, ctx) => {
119
+ let violationReason: string | null = null;
120
+ let shouldAsk = false;
121
+
122
+ const checkPaths = (pathsToCheck: string[]) => {
123
+ for (const p of pathsToCheck) {
124
+ const resolved = resolvePath(p, ctx.cwd);
125
+ for (const zap of rules.zeroAccessPaths) {
126
+ if (isPathMatch(resolved, zap, ctx.cwd)) {
127
+ return `Access to zero-access path restricted: ${zap}`;
128
+ }
129
+ }
130
+ }
131
+ return null;
132
+ };
133
+
134
+ const inputPaths: string[] = [];
135
+ if (isToolCallEventType("read", event) || isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
136
+ inputPaths.push(event.input.path);
137
+ } else if (isToolCallEventType("grep", event) || isToolCallEventType("find", event) || isToolCallEventType("ls", event)) {
138
+ inputPaths.push(event.input.path || ".");
139
+ }
140
+
141
+ if (isToolCallEventType("grep", event) && event.input.glob) {
142
+ for (const zap of rules.zeroAccessPaths) {
143
+ if (event.input.glob.includes(zap) || isPathMatch(event.input.glob, zap, ctx.cwd)) {
144
+ violationReason = `Glob matches zero-access path: ${zap}`;
145
+ break;
146
+ }
147
+ }
148
+ }
149
+
150
+ if (!violationReason) {
151
+ violationReason = checkPaths(inputPaths);
152
+ }
153
+
154
+ if (!violationReason) {
155
+ if (isToolCallEventType("bash", event)) {
156
+ const command = event.input.command;
157
+
158
+ for (const rule of rules.bashToolPatterns) {
159
+ const regex = new RegExp(rule.pattern);
160
+ if (regex.test(command)) {
161
+ violationReason = rule.reason;
162
+ shouldAsk = !!rule.ask;
163
+ break;
164
+ }
165
+ }
166
+
167
+ if (!violationReason) {
168
+ for (const zap of rules.zeroAccessPaths) {
169
+ if (command.includes(zap)) {
170
+ violationReason = `Bash command references zero-access path: ${zap}`;
171
+ break;
172
+ }
173
+ }
174
+ }
175
+
176
+ if (!violationReason) {
177
+ for (const rop of rules.readOnlyPaths) {
178
+ if (command.includes(rop) && (/[\s>|]/.test(command) || command.includes("rm") || command.includes("mv") || command.includes("sed"))) {
179
+ violationReason = `Bash command may modify read-only path: ${rop}`;
180
+ break;
181
+ }
182
+ }
183
+ }
184
+
185
+ if (!violationReason) {
186
+ for (const ndp of rules.noDeletePaths) {
187
+ if (command.includes(ndp) && (command.includes("rm") || command.includes("mv"))) {
188
+ violationReason = `Bash command attempts to delete/move protected path: ${ndp}`;
189
+ break;
190
+ }
191
+ }
192
+ }
193
+ } else if (isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
194
+ for (const p of inputPaths) {
195
+ const resolved = resolvePath(p, ctx.cwd);
196
+ for (const rop of rules.readOnlyPaths) {
197
+ if (isPathMatch(resolved, rop, ctx.cwd)) {
198
+ violationReason = `Modification of read-only path restricted: ${rop}`;
199
+ break;
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ if (violationReason) {
207
+ const invocation = isToolCallEventType("bash", event) ? event.input.command : JSON.stringify(event.input);
208
+
209
+ if (shouldAsk) {
210
+ const confirmed = await ctx.ui.confirm(
211
+ "🛡️ Damage-Control Confirmation",
212
+ `Dangerous command detected: ${violationReason}\n\nCommand: ${invocation}\n\nDo you want to proceed?`,
213
+ { timeout: 30000 },
214
+ );
215
+
216
+ if (!confirmed) {
217
+ ctx.ui.setStatus(`⚠️ Last Violation Blocked: ${violationReason.slice(0, 30)}...`);
218
+ pi.appendEntry("damage-control-log", { tool: event.toolName, input: event.input, rule: violationReason, action: "blocked_by_user" });
219
+ return { block: true, reason: continueFeedback(event.toolName, `${violationReason} (user denied)`, invocation) };
220
+ } else {
221
+ pi.appendEntry("damage-control-log", { tool: event.toolName, input: event.input, rule: violationReason, action: "confirmed_by_user" });
222
+ return { block: false };
223
+ }
224
+ } else {
225
+ ctx.ui.notify(`🛑 Damage-Control: Blocked ${event.toolName} (${violationReason}) — agent will adapt and continue.`);
226
+ ctx.ui.setStatus(`⚠️ Last Violation: ${violationReason.slice(0, 30)}...`);
227
+ pi.appendEntry("damage-control-log", { tool: event.toolName, input: event.input, rule: violationReason, action: "blocked" });
228
+ return { block: true, reason: continueFeedback(event.toolName, violationReason, invocation) };
229
+ }
230
+ }
231
+
232
+ return { block: false };
233
+ });
234
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "agent-skills-pi-damage-control-continue",
3
+ "private": true,
4
+ "type": "module",
5
+ "main": "index.ts"
6
+ }
@@ -0,0 +1,27 @@
1
+ # minimal
2
+
3
+ Model name + context meter in a compact footer.
4
+
5
+ > Ported from [`pi-vs-claude-code`](https://github.com/disler/pi-vs-claude-code) by [disler](https://github.com/disler) (MIT). See the [extension catalog](../../../docs/pi-extensions.md).
6
+
7
+ ## What it does
8
+
9
+ Replaces the pi footer with a single compact line showing the active model ID and a
10
+ 10-block context usage meter — e.g. `claude-opus-4-7 [###-------] 30%`. Useful on its
11
+ own or stacked behind another extension as a lightweight status surface.
12
+
13
+ ## Commands & tools
14
+
15
+ None — footer only.
16
+
17
+ ## Usage
18
+
19
+ ```bash
20
+ pi -e .pi/harnesses/minimal/index.ts
21
+ ```
22
+
23
+ ## Upstream changes
24
+
25
+ - Theme integration removed — the `themeMap.ts` import and the `applyExtensionDefaults()`
26
+ call in `session_start` were stripped (this repo does not ship pi themes). The footer
27
+ now renders against pi's active theme.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Minimal — Model name + context meter in a compact footer
3
+ *
4
+ * Shows model ID and a 10-block context usage bar: [###-------] 30%
5
+ *
6
+ * Usage: pi -e extensions/minimal.ts
7
+ */
8
+
9
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
+ import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
11
+
12
+ export default function (pi: ExtensionAPI) {
13
+ pi.on("session_start", async (_event, ctx) => {
14
+ ctx.ui.setFooter((_tui, theme, _footerData) => ({
15
+ dispose: () => {},
16
+ invalidate() {},
17
+ render(width: number): string[] {
18
+ const model = ctx.model?.id || "no-model";
19
+ const usage = ctx.getContextUsage();
20
+ const pct = (usage && usage.percent !== null) ? usage.percent : 0;
21
+ const filled = Math.round(pct / 10);
22
+ const bar = "#".repeat(filled) + "-".repeat(10 - filled);
23
+
24
+ const left = theme.fg("dim", ` ${model}`);
25
+ const right = theme.fg("dim", `[${bar}] ${Math.round(pct)}% `);
26
+ const pad = " ".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
27
+
28
+ return [truncateToWidth(left + pad + right, width)];
29
+ },
30
+ }));
31
+ });
32
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "agent-skills-pi-minimal",
3
+ "private": true,
4
+ "type": "module",
5
+ "main": "index.ts"
6
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "agent-skills-pi-harnesses-runtime-deps",
3
+ "lockfileVersion": 3,
4
+ "requires": true,
5
+ "packages": {
6
+ "": {
7
+ "name": "agent-skills-pi-harnesses-runtime-deps",
8
+ "dependencies": {
9
+ "@sinclair/typebox": "^0.34.0",
10
+ "yaml": "^2.8.0"
11
+ }
12
+ },
13
+ "node_modules/@sinclair/typebox": {
14
+ "version": "0.34.49",
15
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz",
16
+ "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==",
17
+ "license": "MIT"
18
+ },
19
+ "node_modules/yaml": {
20
+ "version": "2.9.0",
21
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
22
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
23
+ "license": "ISC",
24
+ "bin": {
25
+ "yaml": "bin.mjs"
26
+ },
27
+ "engines": {
28
+ "node": ">= 14.6"
29
+ },
30
+ "funding": {
31
+ "url": "https://github.com/sponsors/eemeli"
32
+ }
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "agent-skills-pi-harnesses-runtime-deps",
3
+ "private": true,
4
+ "type": "module",
5
+ "dependencies": {
6
+ "@sinclair/typebox": "^0.34.0",
7
+ "yaml": "^2.8.0"
8
+ }
9
+ }