@vitos-pizza/vitos-pizza 0.1.0 → 0.2.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 (259) hide show
  1. package/AGENTS.md +158 -158
  2. package/CHANGELOG.md +42 -24
  3. package/LICENSE +21 -21
  4. package/README.md +237 -234
  5. package/assets/logo.svg +28 -28
  6. package/node_modules/@vitos-pizza/agent-mode/extensions/index.ts +138 -117
  7. package/node_modules/@vitos-pizza/agent-mode/package.json +3 -3
  8. package/node_modules/@vitos-pizza/agent-mode/src/apply-mode.ts +60 -60
  9. package/node_modules/@vitos-pizza/agent-mode/src/mode-events.ts +102 -102
  10. package/node_modules/@vitos-pizza/agent-mode/src/modes.ts +43 -43
  11. package/node_modules/@vitos-pizza/agent-mode/src/plan-instructions.ts +85 -44
  12. package/node_modules/@vitos-pizza/agent-mode/tests/apply-mode.test.ts +36 -36
  13. package/node_modules/@vitos-pizza/agent-mode/tests/mode-events.test.ts +22 -22
  14. package/node_modules/@vitos-pizza/agent-mode/tests/modes.test.ts +38 -38
  15. package/node_modules/@vitos-pizza/agent-mode/tests/plan-instructions.test.ts +68 -45
  16. package/node_modules/@vitos-pizza/agent-mode/tsconfig.json +13 -13
  17. package/node_modules/@vitos-pizza/agent-mode/vitest.config.ts +8 -8
  18. package/node_modules/@vitos-pizza/keybindings/extensions/index.ts +104 -104
  19. package/node_modules/@vitos-pizza/keybindings/package.json +1 -1
  20. package/node_modules/@vitos-pizza/keybindings/presets/shortcuts.json +3 -3
  21. package/node_modules/@vitos-pizza/keybindings/src/bind.ts +57 -57
  22. package/node_modules/@vitos-pizza/keybindings/src/config.ts +84 -84
  23. package/node_modules/@vitos-pizza/keybindings/src/registry.ts +43 -43
  24. package/node_modules/@vitos-pizza/keybindings/src/types.ts +54 -54
  25. package/node_modules/@vitos-pizza/keybindings/tests/config.test.ts +83 -83
  26. package/node_modules/@vitos-pizza/keybindings/tests/registry.test.ts +142 -142
  27. package/node_modules/@vitos-pizza/keybindings/tsconfig.json +13 -13
  28. package/node_modules/@vitos-pizza/keybindings/vitest.config.ts +8 -8
  29. package/node_modules/@vitos-pizza/permission-system/extensions/index.ts +162 -162
  30. package/node_modules/@vitos-pizza/permission-system/package.json +2 -2
  31. package/node_modules/@vitos-pizza/permission-system/src/bash/matcher.ts +54 -51
  32. package/node_modules/@vitos-pizza/permission-system/src/bash/parser.ts +40 -40
  33. package/node_modules/@vitos-pizza/permission-system/src/config-paths.ts +23 -23
  34. package/node_modules/@vitos-pizza/permission-system/src/detect-preset-mode.ts +61 -61
  35. package/node_modules/@vitos-pizza/permission-system/src/expand-home.ts +12 -12
  36. package/node_modules/@vitos-pizza/permission-system/src/forwarding/forwarder.ts +224 -224
  37. package/node_modules/@vitos-pizza/permission-system/src/forwarding/subagent-registry.ts +61 -61
  38. package/node_modules/@vitos-pizza/permission-system/src/gates/path-utils.ts +66 -66
  39. package/node_modules/@vitos-pizza/permission-system/src/gates/pipeline.ts +151 -155
  40. package/node_modules/@vitos-pizza/permission-system/src/handlers/before-agent-start.ts +86 -65
  41. package/node_modules/@vitos-pizza/permission-system/src/handlers/skill-input.ts +1 -1
  42. package/node_modules/@vitos-pizza/permission-system/src/handlers/tool-call-gate.ts +204 -199
  43. package/node_modules/@vitos-pizza/permission-system/src/mode-api.ts +44 -44
  44. package/node_modules/@vitos-pizza/permission-system/src/permission-evaluator.ts +233 -216
  45. package/node_modules/@vitos-pizza/permission-system/src/permission-session.ts +62 -62
  46. package/node_modules/@vitos-pizza/permission-system/src/policy-loader.ts +211 -211
  47. package/node_modules/@vitos-pizza/permission-system/src/sanitizers/skill-prompt.ts +99 -99
  48. package/node_modules/@vitos-pizza/permission-system/src/sanitizers/system-prompt.ts +221 -221
  49. package/node_modules/@vitos-pizza/permission-system/src/service.ts +66 -66
  50. package/node_modules/@vitos-pizza/permission-system/src/session-approvals.ts +4 -1
  51. package/node_modules/@vitos-pizza/permission-system/src/types.ts +80 -80
  52. package/node_modules/@vitos-pizza/permission-system/src/ui/approval-summary.ts +43 -43
  53. package/node_modules/@vitos-pizza/permission-system/src/ui/config-modal.ts +36 -36
  54. package/node_modules/@vitos-pizza/permission-system/src/ui/permission-dialog.ts +52 -52
  55. package/node_modules/@vitos-pizza/permission-system/src/wildcard-matcher.ts +81 -81
  56. package/node_modules/@vitos-pizza/permission-system/src/yaml-frontmatter.ts +84 -84
  57. package/node_modules/@vitos-pizza/permission-system/src/yolo/runtime-api.ts +49 -49
  58. package/node_modules/@vitos-pizza/permission-system/tests/bash-parser.test.ts +32 -32
  59. package/node_modules/@vitos-pizza/permission-system/tests/before-agent-start.test.ts +61 -0
  60. package/node_modules/@vitos-pizza/permission-system/tests/detect-preset-mode.test.ts +68 -68
  61. package/node_modules/@vitos-pizza/permission-system/tests/forwarder.test.ts +55 -55
  62. package/node_modules/@vitos-pizza/permission-system/tests/gates.test.ts +170 -61
  63. package/node_modules/@vitos-pizza/permission-system/tests/path-utils.test.ts +68 -68
  64. package/node_modules/@vitos-pizza/permission-system/tests/permission-evaluator.test.ts +82 -55
  65. package/node_modules/@vitos-pizza/permission-system/tests/policy-loader.test.ts +25 -25
  66. package/node_modules/@vitos-pizza/permission-system/tests/system-prompt-sanitizer.test.ts +22 -22
  67. package/node_modules/@vitos-pizza/permission-system/tests/wildcard-matcher.test.ts +27 -27
  68. package/node_modules/@vitos-pizza/permission-system/tsconfig.json +13 -13
  69. package/node_modules/@vitos-pizza/permission-system/vitest.config.ts +8 -8
  70. package/node_modules/@vitos-pizza/question/package.json +1 -1
  71. package/node_modules/@vitos-pizza/question/src/question-ui.ts +5 -1
  72. package/node_modules/@vitos-pizza/question/src/register-tool.ts +1 -4
  73. package/node_modules/@vitos-pizza/question/tests/question-ui-width-cache.test.ts +45 -0
  74. package/node_modules/@vitos-pizza/question/tests/register-tool-render.test.ts +48 -45
  75. package/node_modules/@vitos-pizza/session-title/extensions/index.ts +137 -137
  76. package/node_modules/@vitos-pizza/session-title/package.json +3 -3
  77. package/node_modules/@vitos-pizza/session-title/src/build-title-prompt.ts +16 -16
  78. package/node_modules/@vitos-pizza/session-title/src/extract-title-context.ts +55 -55
  79. package/node_modules/@vitos-pizza/session-title/src/fast-rules.ts +48 -48
  80. package/node_modules/@vitos-pizza/session-title/src/load-settings.ts +70 -70
  81. package/node_modules/@vitos-pizza/session-title/src/normalize-title.ts +20 -20
  82. package/node_modules/@vitos-pizza/session-title/src/parse-title-response.ts +6 -6
  83. package/node_modules/@vitos-pizza/session-title/src/resolve-title-model.ts +33 -33
  84. package/node_modules/@vitos-pizza/session-title/src/schedule-title-job.ts +53 -53
  85. package/node_modules/@vitos-pizza/session-title/src/should-schedule.ts +30 -30
  86. package/node_modules/@vitos-pizza/session-title/src/title-agent.ts +60 -60
  87. package/node_modules/@vitos-pizza/session-title/src/truncate-input.ts +10 -10
  88. package/node_modules/@vitos-pizza/session-title/src/types.ts +29 -29
  89. package/node_modules/@vitos-pizza/session-title/tests/build-title-prompt.test.ts +34 -34
  90. package/node_modules/@vitos-pizza/session-title/tests/extension.test.ts +198 -198
  91. package/node_modules/@vitos-pizza/session-title/tests/extract-title-context.test.ts +48 -48
  92. package/node_modules/@vitos-pizza/session-title/tests/fast-rules.test.ts +43 -43
  93. package/node_modules/@vitos-pizza/session-title/tests/parse-title-response.test.ts +34 -34
  94. package/node_modules/@vitos-pizza/session-title/tests/should-schedule.test.ts +30 -30
  95. package/node_modules/@vitos-pizza/session-title/tests/title-agent.test.ts +154 -154
  96. package/node_modules/@vitos-pizza/session-title/tests/truncate-input.test.ts +15 -15
  97. package/node_modules/@vitos-pizza/session-title/tsconfig.json +13 -13
  98. package/node_modules/@vitos-pizza/session-title/vitest.config.ts +8 -8
  99. package/node_modules/@vitos-pizza/subagents/agents/planner.md +46 -43
  100. package/node_modules/@vitos-pizza/subagents/agents/scout.md +40 -40
  101. package/node_modules/@vitos-pizza/subagents/agents/title.md +13 -13
  102. package/node_modules/@vitos-pizza/subagents/agents/worker.md +36 -36
  103. package/node_modules/@vitos-pizza/subagents/extensions/index.ts +164 -164
  104. package/node_modules/@vitos-pizza/subagents/package.json +1 -1
  105. package/node_modules/@vitos-pizza/subagents/skills/subagents/SKILL.md +86 -80
  106. package/node_modules/@vitos-pizza/subagents/src/agents.ts +166 -166
  107. package/node_modules/@vitos-pizza/subagents/src/async/job-tracker.ts +71 -71
  108. package/node_modules/@vitos-pizza/subagents/src/async/spawn-async.ts +220 -220
  109. package/node_modules/@vitos-pizza/subagents/src/async/storage.ts +57 -57
  110. package/node_modules/@vitos-pizza/subagents/src/env.ts +4 -4
  111. package/node_modules/@vitos-pizza/subagents/src/execute-subagent.ts +285 -285
  112. package/node_modules/@vitos-pizza/subagents/src/progress.ts +187 -187
  113. package/node_modules/@vitos-pizza/subagents/src/render-animation.ts +40 -40
  114. package/node_modules/@vitos-pizza/subagents/src/render.ts +428 -428
  115. package/node_modules/@vitos-pizza/subagents/src/rpc/channels.ts +21 -21
  116. package/node_modules/@vitos-pizza/subagents/src/rpc/client.ts +144 -144
  117. package/node_modules/@vitos-pizza/subagents/src/rpc/server.ts +11 -11
  118. package/node_modules/@vitos-pizza/subagents/src/run-agent.ts +313 -313
  119. package/node_modules/@vitos-pizza/subagents/src/runtime.ts +102 -102
  120. package/node_modules/@vitos-pizza/subagents/src/schema.ts +62 -62
  121. package/node_modules/@vitos-pizza/subagents/src/spawn.ts +104 -104
  122. package/node_modules/@vitos-pizza/subagents/src/types.ts +92 -92
  123. package/node_modules/@vitos-pizza/subagents/src/utils.ts +100 -100
  124. package/node_modules/@vitos-pizza/subagents/src/wait.ts +159 -159
  125. package/node_modules/@vitos-pizza/subagents/tests/agents.test.ts +19 -19
  126. package/node_modules/@vitos-pizza/subagents/tests/execute-subagent.test.ts +96 -96
  127. package/node_modules/@vitos-pizza/subagents/tests/extension.test.ts +42 -42
  128. package/node_modules/@vitos-pizza/subagents/tests/progress.test.ts +206 -206
  129. package/node_modules/@vitos-pizza/subagents/tests/rpc.test.ts +74 -74
  130. package/node_modules/@vitos-pizza/subagents/tsconfig.json +13 -13
  131. package/node_modules/@vitos-pizza/subagents/vitest.config.ts +8 -8
  132. package/node_modules/@vitos-pizza/ui-enhancements/package.json +1 -1
  133. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/agent-mode-badge.ts +60 -60
  134. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/border-status.ts +14 -1
  135. package/node_modules/@vitos-pizza/ui-enhancements/src/chrome/resize-recovery.ts +63 -0
  136. package/node_modules/@vitos-pizza/ui-enhancements/tests/agent-mode-badge.test.ts +35 -35
  137. package/node_modules/@vitos-pizza/ui-enhancements/tests/resize-recovery.test.ts +135 -0
  138. package/node_modules/@vitos-pizza/websearch/package.json +1 -1
  139. package/node_modules/node-addon-api/tools/conversion.js +0 -0
  140. package/node_modules/node-gyp-build/bin.js +0 -0
  141. package/node_modules/node-gyp-build/build-test.js +0 -0
  142. package/node_modules/node-gyp-build/optional.js +0 -0
  143. package/package.json +11 -9
  144. package/packages/agent-mode/extensions/index.ts +138 -117
  145. package/packages/agent-mode/package.json +3 -3
  146. package/packages/agent-mode/src/apply-mode.ts +60 -60
  147. package/packages/agent-mode/src/mode-events.ts +102 -102
  148. package/packages/agent-mode/src/modes.ts +43 -43
  149. package/packages/agent-mode/src/plan-instructions.ts +85 -44
  150. package/packages/agent-mode/tsconfig.json +13 -13
  151. package/packages/agent-mode/vitest.config.ts +8 -8
  152. package/packages/keybindings/extensions/index.ts +104 -104
  153. package/packages/keybindings/package.json +1 -1
  154. package/packages/keybindings/presets/shortcuts.json +3 -3
  155. package/packages/keybindings/src/bind.ts +57 -57
  156. package/packages/keybindings/src/config.ts +84 -84
  157. package/packages/keybindings/src/registry.ts +43 -43
  158. package/packages/keybindings/src/types.ts +54 -54
  159. package/packages/keybindings/tsconfig.json +13 -13
  160. package/packages/keybindings/vitest.config.ts +8 -8
  161. package/packages/permission-system/extensions/index.ts +162 -162
  162. package/packages/permission-system/node_modules/web-tree-sitter/debug/tree-sitter.wasm +0 -0
  163. package/packages/permission-system/node_modules/web-tree-sitter/tree-sitter.wasm +0 -0
  164. package/packages/permission-system/package.json +2 -2
  165. package/packages/permission-system/src/bash/matcher.ts +54 -51
  166. package/packages/permission-system/src/bash/parser.ts +40 -40
  167. package/packages/permission-system/src/config-paths.ts +23 -23
  168. package/packages/permission-system/src/detect-preset-mode.ts +61 -61
  169. package/packages/permission-system/src/expand-home.ts +12 -12
  170. package/packages/permission-system/src/forwarding/forwarder.ts +224 -224
  171. package/packages/permission-system/src/forwarding/subagent-registry.ts +61 -61
  172. package/packages/permission-system/src/gates/path-utils.ts +66 -66
  173. package/packages/permission-system/src/gates/pipeline.ts +151 -155
  174. package/packages/permission-system/src/handlers/before-agent-start.ts +86 -65
  175. package/packages/permission-system/src/handlers/skill-input.ts +1 -1
  176. package/packages/permission-system/src/handlers/tool-call-gate.ts +204 -199
  177. package/packages/permission-system/src/mode-api.ts +44 -44
  178. package/packages/permission-system/src/permission-evaluator.ts +233 -216
  179. package/packages/permission-system/src/permission-session.ts +62 -62
  180. package/packages/permission-system/src/policy-loader.ts +211 -211
  181. package/packages/permission-system/src/sanitizers/skill-prompt.ts +99 -99
  182. package/packages/permission-system/src/sanitizers/system-prompt.ts +221 -221
  183. package/packages/permission-system/src/service.ts +66 -66
  184. package/packages/permission-system/src/session-approvals.ts +4 -1
  185. package/packages/permission-system/src/types.ts +80 -80
  186. package/packages/permission-system/src/ui/approval-summary.ts +43 -43
  187. package/packages/permission-system/src/ui/config-modal.ts +36 -36
  188. package/packages/permission-system/src/ui/permission-dialog.ts +52 -52
  189. package/packages/permission-system/src/wildcard-matcher.ts +81 -81
  190. package/packages/permission-system/src/yaml-frontmatter.ts +84 -84
  191. package/packages/permission-system/src/yolo/runtime-api.ts +49 -49
  192. package/packages/permission-system/tsconfig.json +13 -13
  193. package/packages/permission-system/vitest.config.ts +8 -8
  194. package/packages/question/package.json +1 -1
  195. package/packages/question/src/question-ui.ts +5 -1
  196. package/packages/question/src/register-tool.ts +1 -4
  197. package/packages/session-title/extensions/index.ts +137 -137
  198. package/packages/session-title/package.json +3 -3
  199. package/packages/session-title/src/build-title-prompt.ts +16 -16
  200. package/packages/session-title/src/extract-title-context.ts +55 -55
  201. package/packages/session-title/src/fast-rules.ts +48 -48
  202. package/packages/session-title/src/load-settings.ts +70 -70
  203. package/packages/session-title/src/normalize-title.ts +20 -20
  204. package/packages/session-title/src/parse-title-response.ts +6 -6
  205. package/packages/session-title/src/resolve-title-model.ts +33 -33
  206. package/packages/session-title/src/schedule-title-job.ts +53 -53
  207. package/packages/session-title/src/should-schedule.ts +30 -30
  208. package/packages/session-title/src/title-agent.ts +60 -60
  209. package/packages/session-title/src/truncate-input.ts +10 -10
  210. package/packages/session-title/src/types.ts +29 -29
  211. package/packages/session-title/tsconfig.json +13 -13
  212. package/packages/session-title/vitest.config.ts +8 -8
  213. package/packages/subagents/agents/planner.md +46 -43
  214. package/packages/subagents/agents/scout.md +40 -40
  215. package/packages/subagents/agents/title.md +13 -13
  216. package/packages/subagents/agents/worker.md +36 -36
  217. package/packages/subagents/extensions/index.ts +164 -164
  218. package/packages/subagents/package.json +1 -1
  219. package/packages/subagents/skills/subagents/SKILL.md +86 -80
  220. package/packages/subagents/src/agents.ts +166 -166
  221. package/packages/subagents/src/async/job-tracker.ts +71 -71
  222. package/packages/subagents/src/async/spawn-async.ts +220 -220
  223. package/packages/subagents/src/async/storage.ts +57 -57
  224. package/packages/subagents/src/env.ts +4 -4
  225. package/packages/subagents/src/execute-subagent.ts +285 -285
  226. package/packages/subagents/src/progress.ts +187 -187
  227. package/packages/subagents/src/render-animation.ts +40 -40
  228. package/packages/subagents/src/render.ts +428 -428
  229. package/packages/subagents/src/rpc/channels.ts +21 -21
  230. package/packages/subagents/src/rpc/client.ts +144 -144
  231. package/packages/subagents/src/rpc/server.ts +11 -11
  232. package/packages/subagents/src/run-agent.ts +313 -313
  233. package/packages/subagents/src/runtime.ts +102 -102
  234. package/packages/subagents/src/schema.ts +62 -62
  235. package/packages/subagents/src/spawn.ts +104 -104
  236. package/packages/subagents/src/types.ts +92 -92
  237. package/packages/subagents/src/utils.ts +100 -100
  238. package/packages/subagents/src/wait.ts +159 -159
  239. package/packages/subagents/tsconfig.json +13 -13
  240. package/packages/subagents/vitest.config.ts +8 -8
  241. package/packages/todoist/extensions/index.ts +163 -0
  242. package/packages/todoist/package.json +34 -0
  243. package/packages/todoist/src/events.ts +7 -0
  244. package/packages/todoist/src/silent-render.ts +8 -0
  245. package/packages/todoist/src/state.ts +147 -0
  246. package/packages/todoist/src/tools.ts +175 -0
  247. package/packages/todoist/src/types.ts +34 -0
  248. package/packages/todoist/src/widget.ts +109 -0
  249. package/packages/todoist/tsconfig.json +13 -0
  250. package/packages/todoist/vitest.config.ts +8 -0
  251. package/packages/ui-enhancements/package.json +1 -1
  252. package/packages/ui-enhancements/src/chrome/agent-mode-badge.ts +60 -60
  253. package/packages/ui-enhancements/src/chrome/border-status.ts +14 -1
  254. package/packages/ui-enhancements/src/chrome/resize-recovery.ts +63 -0
  255. package/packages/websearch/package.json +1 -1
  256. package/scripts/sync-pi-manifest.mjs +1 -1
  257. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.cjs +0 -2988
  258. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.wasm +0 -0
  259. package/packages/permission-system/node_modules/web-tree-sitter/lib/tree-sitter.wasm.map +0 -1
@@ -1,211 +1,211 @@
1
- import {
2
- existsSync,
3
- mkdirSync,
4
- readFileSync,
5
- statSync,
6
- writeFileSync,
7
- } from "node:fs";
8
- import { join } from "node:path";
9
- import {
10
- getGlobalAgentsDir,
11
- getGlobalConfigPath,
12
- getProjectAgentsDir,
13
- getProjectConfigPath,
14
- } from "./config-paths.ts";
15
- import type {
16
- ExtensionConfig,
17
- FlatPermissionConfig,
18
- PatternMap,
19
- PatternValue,
20
- } from "./types.ts";
21
- import { isDenyWithReason, isPermissionState } from "./types.ts";
22
- import { parseAgentFrontmatterPermission } from "./yaml-frontmatter.ts";
23
-
24
- function stripJsonComments(text: string): string {
25
- return text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
26
- }
27
-
28
- function toRecord(value: unknown): Record<string, unknown> {
29
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
30
- return {};
31
- }
32
- return value as Record<string, unknown>;
33
- }
34
-
35
- function isPatternMap(value: PatternValue): value is PatternMap {
36
- return typeof value === "object" && value !== null && !("action" in value);
37
- }
38
-
39
- function normalizePermissionValue(value: unknown): PatternValue | undefined {
40
- if (isPermissionState(value)) return value;
41
- if (isDenyWithReason(value)) return value;
42
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
43
- const nested: PatternMap = {};
44
- for (const [key, nestedValue] of Object.entries(value)) {
45
- const normalized = normalizePermissionValue(nestedValue);
46
- if (normalized !== undefined) nested[key] = normalized;
47
- }
48
- return Object.keys(nested).length > 0 ? nested : undefined;
49
- }
50
- return undefined;
51
- }
52
-
53
- function normalizePermissionConfig(raw: unknown): FlatPermissionConfig {
54
- const record = toRecord(raw);
55
- const result: FlatPermissionConfig = {};
56
- for (const [key, value] of Object.entries(record)) {
57
- const normalized = normalizePermissionValue(value);
58
- if (normalized !== undefined) result[key] = normalized;
59
- }
60
- return result;
61
- }
62
-
63
- export function loadConfigFile(path: string): ExtensionConfig {
64
- try {
65
- const raw = readFileSync(path, "utf8");
66
- const parsed = JSON.parse(stripJsonComments(raw)) as unknown;
67
- const record = toRecord(parsed);
68
- const agentMode = record.agentMode;
69
- const normalizedAgentMode =
70
- agentMode === "agent" ||
71
- agentMode === "plan" ||
72
- agentMode === "execute"
73
- ? agentMode
74
- : undefined;
75
- return {
76
- yoloMode: record.yoloMode === true,
77
- debug: record.debug === true,
78
- agentMode: normalizedAgentMode,
79
- permission: normalizePermissionConfig(record.permission),
80
- };
81
- } catch {
82
- return { permission: { "*": "ask" } };
83
- }
84
- }
85
-
86
- export function saveConfigFile(path: string, config: ExtensionConfig): void {
87
- mkdirSync(join(path, ".."), { recursive: true });
88
- writeFileSync(path, `${JSON.stringify(config, null, "\t")}\n`, "utf8");
89
- }
90
-
91
- function getFileStamp(path: string): string {
92
- try {
93
- return existsSync(path) ? String(statSync(path).mtimeMs) : "missing";
94
- } catch {
95
- return "missing";
96
- }
97
- }
98
-
99
- function findAgentFile(dir: string, agentName: string): string | null {
100
- const candidates = [`${agentName}.md`, `${agentName}.agent.md`];
101
- for (const name of candidates) {
102
- const path = join(dir, name);
103
- if (existsSync(path)) return path;
104
- }
105
- return null;
106
- }
107
-
108
- export interface LoadedPolicy {
109
- config: ExtensionConfig;
110
- agentPermission?: FlatPermissionConfig;
111
- }
112
-
113
- export class PolicyLoader {
114
- private readonly agentDir: string;
115
- private cwd: string | null = null;
116
- private cacheStamp = "";
117
- private cached: LoadedPolicy | null = null;
118
-
119
- constructor(agentDir: string) {
120
- this.agentDir = agentDir;
121
- }
122
-
123
- setCwd(cwd: string): void {
124
- this.cwd = cwd;
125
- this.cached = null;
126
- }
127
-
128
- load(agentName?: string): LoadedPolicy {
129
- const stamp = [
130
- getFileStamp(getGlobalConfigPath(this.agentDir)),
131
- this.cwd ? getFileStamp(getProjectConfigPath(this.cwd)) : "no-cwd",
132
- agentName ?? "no-agent",
133
- ].join("|");
134
- if (this.cached && this.cacheStamp === stamp) {
135
- return this.cached;
136
- }
137
-
138
- const global = loadConfigFile(getGlobalConfigPath(this.agentDir));
139
- const project =
140
- this.cwd && existsSync(getProjectConfigPath(this.cwd))
141
- ? loadConfigFile(getProjectConfigPath(this.cwd))
142
- : { permission: {} };
143
-
144
- const mergedPermission = mergePermissionMaps(
145
- global.permission ?? {},
146
- project.permission ?? {},
147
- );
148
-
149
- let agentPermission: FlatPermissionConfig | undefined;
150
- if (agentName) {
151
- const globalAgentDir = getGlobalAgentsDir(this.agentDir);
152
- const projectAgentDir = this.cwd ? getProjectAgentsDir(this.cwd) : null;
153
- const globalAgentPath = findAgentFile(globalAgentDir, agentName);
154
- const projectAgentPath = projectAgentDir
155
- ? findAgentFile(projectAgentDir, agentName)
156
- : null;
157
- const globalAgentPerm = globalAgentPath
158
- ? loadAgentPermission(globalAgentPath)
159
- : {};
160
- const projectAgentPerm = projectAgentPath
161
- ? loadAgentPermission(projectAgentPath)
162
- : {};
163
- agentPermission = mergePermissionMaps(globalAgentPerm, projectAgentPerm);
164
- Object.assign(
165
- mergedPermission,
166
- mergePermissionMaps(mergedPermission, agentPermission),
167
- );
168
- }
169
-
170
- const config: ExtensionConfig = {
171
- yoloMode: project.yoloMode ?? global.yoloMode ?? false,
172
- debug: project.debug ?? global.debug ?? false,
173
- agentMode: project.agentMode ?? global.agentMode,
174
- permission: mergedPermission,
175
- };
176
-
177
- this.cacheStamp = stamp;
178
- this.cached = { config, agentPermission };
179
- return this.cached;
180
- }
181
- }
182
-
183
- function loadAgentPermission(path: string): FlatPermissionConfig {
184
- try {
185
- const content = readFileSync(path, "utf8");
186
- const permission = parseAgentFrontmatterPermission(content);
187
- return permission ? normalizePermissionConfig(permission) : {};
188
- } catch {
189
- return {};
190
- }
191
- }
192
-
193
- export function mergePermissionMaps(
194
- base: FlatPermissionConfig,
195
- override: FlatPermissionConfig,
196
- ): FlatPermissionConfig {
197
- const result: FlatPermissionConfig = { ...base };
198
- for (const [key, value] of Object.entries(override)) {
199
- const existing = result[key];
200
- if (
201
- existing !== undefined &&
202
- isPatternMap(existing) &&
203
- isPatternMap(value)
204
- ) {
205
- result[key] = mergePermissionMaps(existing, value);
206
- } else {
207
- result[key] = value;
208
- }
209
- }
210
- return result;
211
- }
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ statSync,
6
+ writeFileSync,
7
+ } from "node:fs";
8
+ import { join } from "node:path";
9
+ import {
10
+ getGlobalAgentsDir,
11
+ getGlobalConfigPath,
12
+ getProjectAgentsDir,
13
+ getProjectConfigPath,
14
+ } from "./config-paths.ts";
15
+ import type {
16
+ ExtensionConfig,
17
+ FlatPermissionConfig,
18
+ PatternMap,
19
+ PatternValue,
20
+ } from "./types.ts";
21
+ import { isDenyWithReason, isPermissionState } from "./types.ts";
22
+ import { parseAgentFrontmatterPermission } from "./yaml-frontmatter.ts";
23
+
24
+ function stripJsonComments(text: string): string {
25
+ return text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
26
+ }
27
+
28
+ function toRecord(value: unknown): Record<string, unknown> {
29
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
30
+ return {};
31
+ }
32
+ return value as Record<string, unknown>;
33
+ }
34
+
35
+ function isPatternMap(value: PatternValue): value is PatternMap {
36
+ return typeof value === "object" && value !== null && !("action" in value);
37
+ }
38
+
39
+ function normalizePermissionValue(value: unknown): PatternValue | undefined {
40
+ if (isPermissionState(value)) return value;
41
+ if (isDenyWithReason(value)) return value;
42
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
43
+ const nested: PatternMap = {};
44
+ for (const [key, nestedValue] of Object.entries(value)) {
45
+ const normalized = normalizePermissionValue(nestedValue);
46
+ if (normalized !== undefined) nested[key] = normalized;
47
+ }
48
+ return Object.keys(nested).length > 0 ? nested : undefined;
49
+ }
50
+ return undefined;
51
+ }
52
+
53
+ function normalizePermissionConfig(raw: unknown): FlatPermissionConfig {
54
+ const record = toRecord(raw);
55
+ const result: FlatPermissionConfig = {};
56
+ for (const [key, value] of Object.entries(record)) {
57
+ const normalized = normalizePermissionValue(value);
58
+ if (normalized !== undefined) result[key] = normalized;
59
+ }
60
+ return result;
61
+ }
62
+
63
+ export function loadConfigFile(path: string): ExtensionConfig {
64
+ try {
65
+ const raw = readFileSync(path, "utf8");
66
+ const parsed = JSON.parse(stripJsonComments(raw)) as unknown;
67
+ const record = toRecord(parsed);
68
+ const agentMode = record.agentMode;
69
+ const normalizedAgentMode =
70
+ agentMode === "agent" ||
71
+ agentMode === "plan" ||
72
+ agentMode === "execute"
73
+ ? agentMode
74
+ : undefined;
75
+ return {
76
+ yoloMode: record.yoloMode === true,
77
+ debug: record.debug === true,
78
+ agentMode: normalizedAgentMode,
79
+ permission: normalizePermissionConfig(record.permission),
80
+ };
81
+ } catch {
82
+ return { permission: { "*": "ask" } };
83
+ }
84
+ }
85
+
86
+ export function saveConfigFile(path: string, config: ExtensionConfig): void {
87
+ mkdirSync(join(path, ".."), { recursive: true });
88
+ writeFileSync(path, `${JSON.stringify(config, null, "\t")}\n`, "utf8");
89
+ }
90
+
91
+ function getFileStamp(path: string): string {
92
+ try {
93
+ return existsSync(path) ? String(statSync(path).mtimeMs) : "missing";
94
+ } catch {
95
+ return "missing";
96
+ }
97
+ }
98
+
99
+ function findAgentFile(dir: string, agentName: string): string | null {
100
+ const candidates = [`${agentName}.md`, `${agentName}.agent.md`];
101
+ for (const name of candidates) {
102
+ const path = join(dir, name);
103
+ if (existsSync(path)) return path;
104
+ }
105
+ return null;
106
+ }
107
+
108
+ export interface LoadedPolicy {
109
+ config: ExtensionConfig;
110
+ agentPermission?: FlatPermissionConfig;
111
+ }
112
+
113
+ export class PolicyLoader {
114
+ private readonly agentDir: string;
115
+ private cwd: string | null = null;
116
+ private cacheStamp = "";
117
+ private cached: LoadedPolicy | null = null;
118
+
119
+ constructor(agentDir: string) {
120
+ this.agentDir = agentDir;
121
+ }
122
+
123
+ setCwd(cwd: string): void {
124
+ this.cwd = cwd;
125
+ this.cached = null;
126
+ }
127
+
128
+ load(agentName?: string): LoadedPolicy {
129
+ const stamp = [
130
+ getFileStamp(getGlobalConfigPath(this.agentDir)),
131
+ this.cwd ? getFileStamp(getProjectConfigPath(this.cwd)) : "no-cwd",
132
+ agentName ?? "no-agent",
133
+ ].join("|");
134
+ if (this.cached && this.cacheStamp === stamp) {
135
+ return this.cached;
136
+ }
137
+
138
+ const global = loadConfigFile(getGlobalConfigPath(this.agentDir));
139
+ const project =
140
+ this.cwd && existsSync(getProjectConfigPath(this.cwd))
141
+ ? loadConfigFile(getProjectConfigPath(this.cwd))
142
+ : { permission: {} };
143
+
144
+ const mergedPermission = mergePermissionMaps(
145
+ global.permission ?? {},
146
+ project.permission ?? {},
147
+ );
148
+
149
+ let agentPermission: FlatPermissionConfig | undefined;
150
+ if (agentName) {
151
+ const globalAgentDir = getGlobalAgentsDir(this.agentDir);
152
+ const projectAgentDir = this.cwd ? getProjectAgentsDir(this.cwd) : null;
153
+ const globalAgentPath = findAgentFile(globalAgentDir, agentName);
154
+ const projectAgentPath = projectAgentDir
155
+ ? findAgentFile(projectAgentDir, agentName)
156
+ : null;
157
+ const globalAgentPerm = globalAgentPath
158
+ ? loadAgentPermission(globalAgentPath)
159
+ : {};
160
+ const projectAgentPerm = projectAgentPath
161
+ ? loadAgentPermission(projectAgentPath)
162
+ : {};
163
+ agentPermission = mergePermissionMaps(globalAgentPerm, projectAgentPerm);
164
+ Object.assign(
165
+ mergedPermission,
166
+ mergePermissionMaps(mergedPermission, agentPermission),
167
+ );
168
+ }
169
+
170
+ const config: ExtensionConfig = {
171
+ yoloMode: project.yoloMode ?? global.yoloMode ?? false,
172
+ debug: project.debug ?? global.debug ?? false,
173
+ agentMode: project.agentMode ?? global.agentMode,
174
+ permission: mergedPermission,
175
+ };
176
+
177
+ this.cacheStamp = stamp;
178
+ this.cached = { config, agentPermission };
179
+ return this.cached;
180
+ }
181
+ }
182
+
183
+ function loadAgentPermission(path: string): FlatPermissionConfig {
184
+ try {
185
+ const content = readFileSync(path, "utf8");
186
+ const permission = parseAgentFrontmatterPermission(content);
187
+ return permission ? normalizePermissionConfig(permission) : {};
188
+ } catch {
189
+ return {};
190
+ }
191
+ }
192
+
193
+ export function mergePermissionMaps(
194
+ base: FlatPermissionConfig,
195
+ override: FlatPermissionConfig,
196
+ ): FlatPermissionConfig {
197
+ const result: FlatPermissionConfig = { ...base };
198
+ for (const [key, value] of Object.entries(override)) {
199
+ const existing = result[key];
200
+ if (
201
+ existing !== undefined &&
202
+ isPatternMap(existing) &&
203
+ isPatternMap(value)
204
+ ) {
205
+ result[key] = mergePermissionMaps(existing, value);
206
+ } else {
207
+ result[key] = value;
208
+ }
209
+ }
210
+ return result;
211
+ }
@@ -1,99 +1,99 @@
1
- import type { PermissionEvaluator } from "../permission-evaluator.ts";
2
- import type { FlatPermissionConfig } from "../types.ts";
3
-
4
- export interface SkillPromptEntry {
5
- name: string;
6
- start: number;
7
- end: number;
8
- }
9
-
10
- export function findSkillBlocks(prompt: string): SkillPromptEntry[] {
11
- const lines = prompt.replace(/\r\n/g, "\n").split("\n");
12
- const entries: SkillPromptEntry[] = [];
13
- let current: SkillPromptEntry | null = null;
14
-
15
- for (let index = 0; index < lines.length; index += 1) {
16
- const line = lines[index];
17
- const match = /^## Skill:\s*(.+)$/.exec(line.trim());
18
- if (match) {
19
- if (current) {
20
- current.end = index;
21
- entries.push(current);
22
- }
23
- current = { name: match[1].trim(), start: index, end: lines.length };
24
- continue;
25
- }
26
- if (/^## /.test(line.trim()) && current) {
27
- current.end = index;
28
- entries.push(current);
29
- current = null;
30
- }
31
- }
32
-
33
- if (current) entries.push(current);
34
- return entries;
35
- }
36
-
37
- export function sanitizeSkillPromptBlocks(
38
- prompt: string,
39
- permission: FlatPermissionConfig,
40
- evaluator: PermissionEvaluator,
41
- options: { yoloMode?: boolean; agentName?: string },
42
- ): { prompt: string; entries: SkillPromptEntry[] } {
43
- const blocks = findSkillBlocks(prompt);
44
- if (blocks.length === 0) return { prompt, entries: [] };
45
-
46
- const lines = prompt.replace(/\r\n/g, "\n").split("\n");
47
- const deniedRanges: Array<{ start: number; end: number }> = [];
48
- const allowedEntries: SkillPromptEntry[] = [];
49
-
50
- for (const block of blocks) {
51
- const result = evaluator.evaluateSurface(
52
- permission,
53
- "skill",
54
- block.name,
55
- "project",
56
- { yoloMode: options.yoloMode },
57
- );
58
- if (result.state === "deny") {
59
- deniedRanges.push({ start: block.start, end: block.end });
60
- } else {
61
- allowedEntries.push(block);
62
- }
63
- }
64
-
65
- if (deniedRanges.length === 0) {
66
- return { prompt, entries: allowedEntries };
67
- }
68
-
69
- const keepLine = new Array(lines.length).fill(true);
70
- for (const range of deniedRanges) {
71
- for (let index = range.start; index < range.end; index += 1) {
72
- keepLine[index] = false;
73
- }
74
- }
75
-
76
- const filtered = lines.filter((_line, index) => keepLine[index]);
77
- return {
78
- prompt: filtered
79
- .join("\n")
80
- .replace(/\n{3,}/g, "\n\n")
81
- .trimEnd(),
82
- entries: allowedEntries,
83
- };
84
- }
85
-
86
- export function inferSkillNameFromPath(
87
- filePath: string,
88
- skillDirs: readonly string[],
89
- ): string | null {
90
- const normalized = filePath.replace(/\\/g, "/");
91
- for (const dir of skillDirs) {
92
- const normalizedDir = dir.replace(/\\/g, "/").replace(/\/$/, "");
93
- if (!normalized.startsWith(`${normalizedDir}/`)) continue;
94
- const remainder = normalized.slice(normalizedDir.length + 1);
95
- const skillName = remainder.split("/")[0];
96
- return skillName || null;
97
- }
98
- return null;
99
- }
1
+ import type { PermissionEvaluator } from "../permission-evaluator.ts";
2
+ import type { FlatPermissionConfig } from "../types.ts";
3
+
4
+ export interface SkillPromptEntry {
5
+ name: string;
6
+ start: number;
7
+ end: number;
8
+ }
9
+
10
+ export function findSkillBlocks(prompt: string): SkillPromptEntry[] {
11
+ const lines = prompt.replace(/\r\n/g, "\n").split("\n");
12
+ const entries: SkillPromptEntry[] = [];
13
+ let current: SkillPromptEntry | null = null;
14
+
15
+ for (let index = 0; index < lines.length; index += 1) {
16
+ const line = lines[index];
17
+ const match = /^## Skill:\s*(.+)$/.exec(line.trim());
18
+ if (match) {
19
+ if (current) {
20
+ current.end = index;
21
+ entries.push(current);
22
+ }
23
+ current = { name: match[1].trim(), start: index, end: lines.length };
24
+ continue;
25
+ }
26
+ if (/^## /.test(line.trim()) && current) {
27
+ current.end = index;
28
+ entries.push(current);
29
+ current = null;
30
+ }
31
+ }
32
+
33
+ if (current) entries.push(current);
34
+ return entries;
35
+ }
36
+
37
+ export function sanitizeSkillPromptBlocks(
38
+ prompt: string,
39
+ permission: FlatPermissionConfig,
40
+ evaluator: PermissionEvaluator,
41
+ options: { yoloMode?: boolean; agentName?: string },
42
+ ): { prompt: string; entries: SkillPromptEntry[] } {
43
+ const blocks = findSkillBlocks(prompt);
44
+ if (blocks.length === 0) return { prompt, entries: [] };
45
+
46
+ const lines = prompt.replace(/\r\n/g, "\n").split("\n");
47
+ const deniedRanges: Array<{ start: number; end: number }> = [];
48
+ const allowedEntries: SkillPromptEntry[] = [];
49
+
50
+ for (const block of blocks) {
51
+ const result = evaluator.evaluateSurface(
52
+ permission,
53
+ "skill",
54
+ block.name,
55
+ "project",
56
+ { yoloMode: options.yoloMode },
57
+ );
58
+ if (result.state === "deny") {
59
+ deniedRanges.push({ start: block.start, end: block.end });
60
+ } else {
61
+ allowedEntries.push(block);
62
+ }
63
+ }
64
+
65
+ if (deniedRanges.length === 0) {
66
+ return { prompt, entries: allowedEntries };
67
+ }
68
+
69
+ const keepLine = new Array(lines.length).fill(true);
70
+ for (const range of deniedRanges) {
71
+ for (let index = range.start; index < range.end; index += 1) {
72
+ keepLine[index] = false;
73
+ }
74
+ }
75
+
76
+ const filtered = lines.filter((_line, index) => keepLine[index]);
77
+ return {
78
+ prompt: filtered
79
+ .join("\n")
80
+ .replace(/\n{3,}/g, "\n\n")
81
+ .trimEnd(),
82
+ entries: allowedEntries,
83
+ };
84
+ }
85
+
86
+ export function inferSkillNameFromPath(
87
+ filePath: string,
88
+ skillDirs: readonly string[],
89
+ ): string | null {
90
+ const normalized = filePath.replace(/\\/g, "/");
91
+ for (const dir of skillDirs) {
92
+ const normalizedDir = dir.replace(/\\/g, "/").replace(/\/$/, "");
93
+ if (!normalized.startsWith(`${normalizedDir}/`)) continue;
94
+ const remainder = normalized.slice(normalizedDir.length + 1);
95
+ const skillName = remainder.split("/")[0];
96
+ return skillName || null;
97
+ }
98
+ return null;
99
+ }