@sentry/warden 0.0.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 (199) hide show
  1. package/.agents/skills/find-bugs/SKILL.md +75 -0
  2. package/.agents/skills/vercel-react-best-practices/AGENTS.md +2934 -0
  3. package/.agents/skills/vercel-react-best-practices/SKILL.md +136 -0
  4. package/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  5. package/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
  6. package/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
  7. package/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  8. package/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
  9. package/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
  10. package/.agents/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  11. package/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  12. package/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
  13. package/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  14. package/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  15. package/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  16. package/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  17. package/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  18. package/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
  19. package/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
  20. package/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  21. package/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
  22. package/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  23. package/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  24. package/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  25. package/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  26. package/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  27. package/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  28. package/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  29. package/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  30. package/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  31. package/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  32. package/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  33. package/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  34. package/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  35. package/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  36. package/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  37. package/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  38. package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  39. package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  40. package/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  41. package/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  42. package/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  43. package/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  44. package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  45. package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  46. package/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  47. package/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  48. package/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  49. package/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  50. package/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  51. package/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  52. package/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  53. package/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  54. package/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  55. package/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
  56. package/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  57. package/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
  58. package/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
  59. package/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
  60. package/.agents/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  61. package/.claude/settings.json +57 -0
  62. package/.claude/settings.local.json +88 -0
  63. package/.claude/skills/agent-prompt/SKILL.md +54 -0
  64. package/.claude/skills/agent-prompt/references/agentic-patterns.md +94 -0
  65. package/.claude/skills/agent-prompt/references/anti-patterns.md +140 -0
  66. package/.claude/skills/agent-prompt/references/context-design.md +124 -0
  67. package/.claude/skills/agent-prompt/references/core-principles.md +75 -0
  68. package/.claude/skills/agent-prompt/references/model-guidance.md +118 -0
  69. package/.claude/skills/agent-prompt/references/output-formats.md +98 -0
  70. package/.claude/skills/agent-prompt/references/skill-structure.md +115 -0
  71. package/.claude/skills/agent-prompt/references/system-prompts.md +115 -0
  72. package/.claude/skills/notseer/SKILL.md +131 -0
  73. package/.claude/skills/skill-writer/SKILL.md +140 -0
  74. package/.claude/skills/testing-guidelines/SKILL.md +132 -0
  75. package/.claude/skills/warden-skill/SKILL.md +250 -0
  76. package/.claude/skills/warden-skill/references/config-schema.md +133 -0
  77. package/.dex/config.toml +2 -0
  78. package/.github/workflows/ci.yml +33 -0
  79. package/.github/workflows/release.yml +54 -0
  80. package/.github/workflows/warden.yml +40 -0
  81. package/AGENTS.md +89 -0
  82. package/CONTRIBUTING.md +60 -0
  83. package/LICENSE +105 -0
  84. package/README.md +43 -0
  85. package/SPEC.md +263 -0
  86. package/action.yml +87 -0
  87. package/assets/favicon.png +0 -0
  88. package/assets/warden-icon-bw.svg +5 -0
  89. package/assets/warden-icon-purple.png +0 -0
  90. package/assets/warden-icon-purple.svg +5 -0
  91. package/docs/.claude/settings.local.json +11 -0
  92. package/docs/astro.config.mjs +43 -0
  93. package/docs/package.json +19 -0
  94. package/docs/pnpm-lock.yaml +4000 -0
  95. package/docs/public/favicon.svg +5 -0
  96. package/docs/src/components/Code.astro +141 -0
  97. package/docs/src/components/PackageManagerTabs.astro +183 -0
  98. package/docs/src/components/Terminal.astro +212 -0
  99. package/docs/src/layouts/Base.astro +380 -0
  100. package/docs/src/pages/cli.astro +167 -0
  101. package/docs/src/pages/config.astro +394 -0
  102. package/docs/src/pages/guide.astro +449 -0
  103. package/docs/src/pages/index.astro +490 -0
  104. package/docs/src/styles/global.css +551 -0
  105. package/docs/tsconfig.json +3 -0
  106. package/docs/vercel.json +5 -0
  107. package/eslint.config.js +33 -0
  108. package/package.json +73 -0
  109. package/src/action/index.ts +1 -0
  110. package/src/action/main.ts +868 -0
  111. package/src/cli/args.test.ts +477 -0
  112. package/src/cli/args.ts +415 -0
  113. package/src/cli/commands/add.ts +447 -0
  114. package/src/cli/commands/init.test.ts +136 -0
  115. package/src/cli/commands/init.ts +132 -0
  116. package/src/cli/commands/setup-app/browser.ts +38 -0
  117. package/src/cli/commands/setup-app/credentials.ts +45 -0
  118. package/src/cli/commands/setup-app/manifest.ts +48 -0
  119. package/src/cli/commands/setup-app/server.ts +172 -0
  120. package/src/cli/commands/setup-app.ts +156 -0
  121. package/src/cli/commands/sync.ts +114 -0
  122. package/src/cli/context.ts +131 -0
  123. package/src/cli/files.test.ts +155 -0
  124. package/src/cli/files.ts +89 -0
  125. package/src/cli/fix.test.ts +310 -0
  126. package/src/cli/fix.ts +387 -0
  127. package/src/cli/git.test.ts +119 -0
  128. package/src/cli/git.ts +318 -0
  129. package/src/cli/index.ts +14 -0
  130. package/src/cli/main.ts +672 -0
  131. package/src/cli/output/box.ts +235 -0
  132. package/src/cli/output/formatters.test.ts +187 -0
  133. package/src/cli/output/formatters.ts +269 -0
  134. package/src/cli/output/icons.ts +13 -0
  135. package/src/cli/output/index.ts +44 -0
  136. package/src/cli/output/ink-runner.tsx +337 -0
  137. package/src/cli/output/jsonl.test.ts +347 -0
  138. package/src/cli/output/jsonl.ts +126 -0
  139. package/src/cli/output/reporter.ts +435 -0
  140. package/src/cli/output/tasks.ts +374 -0
  141. package/src/cli/output/tty.test.ts +117 -0
  142. package/src/cli/output/tty.ts +60 -0
  143. package/src/cli/output/verbosity.test.ts +40 -0
  144. package/src/cli/output/verbosity.ts +31 -0
  145. package/src/cli/terminal.test.ts +148 -0
  146. package/src/cli/terminal.ts +301 -0
  147. package/src/config/index.ts +3 -0
  148. package/src/config/loader.test.ts +313 -0
  149. package/src/config/loader.ts +103 -0
  150. package/src/config/schema.ts +168 -0
  151. package/src/config/writer.test.ts +119 -0
  152. package/src/config/writer.ts +84 -0
  153. package/src/diff/classify.test.ts +162 -0
  154. package/src/diff/classify.ts +92 -0
  155. package/src/diff/coalesce.test.ts +208 -0
  156. package/src/diff/coalesce.ts +133 -0
  157. package/src/diff/context.test.ts +226 -0
  158. package/src/diff/context.ts +201 -0
  159. package/src/diff/index.ts +4 -0
  160. package/src/diff/parser.test.ts +212 -0
  161. package/src/diff/parser.ts +149 -0
  162. package/src/event/context.ts +132 -0
  163. package/src/event/index.ts +2 -0
  164. package/src/event/schedule-context.ts +101 -0
  165. package/src/examples/examples.integration.test.ts +66 -0
  166. package/src/examples/index.test.ts +101 -0
  167. package/src/examples/index.ts +122 -0
  168. package/src/examples/setup.ts +25 -0
  169. package/src/index.ts +115 -0
  170. package/src/output/dedup.test.ts +419 -0
  171. package/src/output/dedup.ts +607 -0
  172. package/src/output/github-checks.test.ts +300 -0
  173. package/src/output/github-checks.ts +476 -0
  174. package/src/output/github-issues.ts +329 -0
  175. package/src/output/index.ts +5 -0
  176. package/src/output/issue-renderer.ts +197 -0
  177. package/src/output/renderer.test.ts +727 -0
  178. package/src/output/renderer.ts +217 -0
  179. package/src/output/stale.test.ts +375 -0
  180. package/src/output/stale.ts +155 -0
  181. package/src/output/types.ts +34 -0
  182. package/src/sdk/index.ts +1 -0
  183. package/src/sdk/runner.test.ts +806 -0
  184. package/src/sdk/runner.ts +1232 -0
  185. package/src/skills/index.ts +36 -0
  186. package/src/skills/loader.test.ts +300 -0
  187. package/src/skills/loader.ts +423 -0
  188. package/src/skills/remote.test.ts +704 -0
  189. package/src/skills/remote.ts +604 -0
  190. package/src/triggers/matcher.test.ts +277 -0
  191. package/src/triggers/matcher.ts +152 -0
  192. package/src/types/index.ts +194 -0
  193. package/src/utils/async.ts +18 -0
  194. package/src/utils/index.test.ts +84 -0
  195. package/src/utils/index.ts +50 -0
  196. package/tsconfig.json +25 -0
  197. package/vitest.config.ts +8 -0
  198. package/vitest.integration.config.ts +11 -0
  199. package/warden.toml +19 -0
@@ -0,0 +1,415 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { parseArgs } from 'node:util';
3
+ import { z } from 'zod';
4
+ import { SeverityThresholdSchema } from '../types/index.js';
5
+ import type { SeverityThreshold } from '../types/index.js';
6
+
7
+ export const CLIOptionsSchema = z.object({
8
+ targets: z.array(z.string()).optional(),
9
+ skill: z.string().optional(),
10
+ config: z.string().optional(),
11
+ json: z.boolean().default(false),
12
+ /** Write full run output to a JSONL file */
13
+ output: z.string().optional(),
14
+ failOn: SeverityThresholdSchema.optional(),
15
+ /** Only show findings at or above this severity in output */
16
+ commentOn: SeverityThresholdSchema.optional(),
17
+ help: z.boolean().default(false),
18
+ /** Max concurrent trigger/skill executions (default: 4) */
19
+ parallel: z.number().int().positive().optional(),
20
+ /** Model to use for analysis (fallback when not set in config) */
21
+ model: z.string().optional(),
22
+ // Verbosity options
23
+ quiet: z.boolean().default(false),
24
+ verbose: z.number().default(0),
25
+ color: z.boolean().optional(),
26
+ /** Automatically apply all suggested fixes */
27
+ fix: z.boolean().default(false),
28
+ /** Overwrite existing files (for init command) */
29
+ force: z.boolean().default(false),
30
+ /** List available skills (for add command) */
31
+ list: z.boolean().default(false),
32
+ /** Force interpretation of ambiguous targets as git refs */
33
+ git: z.boolean().default(false),
34
+ /** Remote repository reference for skills (e.g., "owner/repo" or "owner/repo@sha") */
35
+ remote: z.string().optional(),
36
+ /** Skip network operations - only use cached remote skills */
37
+ offline: z.boolean().default(false),
38
+ });
39
+
40
+ export type CLIOptions = z.infer<typeof CLIOptionsSchema>;
41
+
42
+ export interface SetupAppOptions {
43
+ org?: string;
44
+ port: number;
45
+ timeout: number;
46
+ name?: string;
47
+ open: boolean;
48
+ }
49
+
50
+ export interface ParsedArgs {
51
+ command: 'run' | 'help' | 'init' | 'add' | 'version' | 'setup-app' | 'sync';
52
+ options: CLIOptions;
53
+ setupAppOptions?: SetupAppOptions;
54
+ }
55
+
56
+ const VERSION = '0.1.0';
57
+
58
+ export function showVersion(): void {
59
+ console.log(`warden ${VERSION}`);
60
+ }
61
+
62
+ const HELP_TEXT = `
63
+ Usage: warden [command] [targets...] [options]
64
+
65
+ Analyze code for security issues and code quality.
66
+
67
+ Commands:
68
+ init Initialize warden.toml and GitHub workflow
69
+ add [skill] Add a skill trigger to warden.toml
70
+ sync [remote] Update cached remote skills to latest
71
+ setup-app Create a GitHub App for Warden via manifest flow
72
+ (default) Run analysis on targets or using warden.toml triggers
73
+
74
+ Targets:
75
+ <files> Analyze specific files (e.g., src/auth.ts)
76
+ <glob> Analyze files matching pattern (e.g., "src/**/*.ts")
77
+ <git-ref> Analyze changes from git ref (e.g., HEAD~3, main..feature)
78
+ (none) Analyze uncommitted changes using warden.toml triggers
79
+
80
+ Options:
81
+ --skill <name> Run only this skill (default: run all built-in skills)
82
+ --config <path> Path to warden.toml (default: ./warden.toml)
83
+ -m, --model <model> Model to use (fallback when not set in config)
84
+ --json Output results as JSON
85
+ -o, --output <path> Write full run output to a JSONL file
86
+ --fail-on <severity> Exit with code 1 if findings >= severity
87
+ (off, critical, high, medium, low, info)
88
+ --comment-on <sev> Only show findings >= severity in output
89
+ (off, critical, high, medium, low, info)
90
+ --fix Automatically apply all suggested fixes
91
+ --parallel <n> Max concurrent trigger/skill executions (default: 4)
92
+ --git Force ambiguous targets to be treated as git refs
93
+ --quiet Errors and final summary only
94
+ -v, --verbose Show real-time findings and hunk details
95
+ -vv Show debug info (token counts, latencies)
96
+ --color / --no-color Override color detection
97
+ --help, -h Show this help message
98
+ --version, -V Show version number
99
+
100
+ Init Options:
101
+ -f, --force Overwrite existing files
102
+
103
+ Add Options:
104
+ --list List available skills
105
+ --remote <ref> Remote repository (owner/repo, URL, or with @sha)
106
+
107
+ Run Options:
108
+ --offline Use cached remote skills without network access
109
+
110
+ Setup-app Options:
111
+ --org <name> Create under organization (default: personal)
112
+ --port <number> Local server port (default: 3000)
113
+ --timeout <sec> Callback timeout in seconds (default: 300)
114
+ --name <string> Custom app name (default: Warden)
115
+ --no-open Print URL instead of opening browser
116
+
117
+ Examples:
118
+ warden init # Initialize warden configuration
119
+ warden add # Interactive skill selection
120
+ warden add security-review # Add specific skill trigger
121
+ warden add --list # List available skills
122
+ warden add --remote getsentry/skills --skill security-review
123
+ # Add remote skill trigger
124
+ warden add --remote https://github.com/getsentry/skills --skill security-review
125
+ # Add remote skill via URL
126
+ warden add --remote getsentry/skills@abc123 --skill security-review
127
+ # Add pinned remote skill
128
+ warden # Run triggers from warden.toml
129
+ warden src/auth.ts # Run all skills on file
130
+ warden src/auth.ts --skill security-review
131
+ # Run specific skill on file
132
+ warden "src/**/*.ts" # Run all skills on glob pattern
133
+ warden HEAD~3 # Run all skills on git changes
134
+ warden HEAD~3 --skill security-review # Run specific skill on git changes
135
+ warden --json # Output as JSON
136
+ warden --fail-on high # Fail if high+ severity findings
137
+ warden --offline # Use cached skills only
138
+ warden sync # Update all unpinned remote skills
139
+ warden setup-app # Create GitHub App interactively
140
+ warden setup-app --org myorg # Create app under organization
141
+ `;
142
+
143
+ export function showHelp(): void {
144
+ console.log(HELP_TEXT.trim());
145
+ }
146
+
147
+ export interface DetectTargetTypeOptions {
148
+ /** Current working directory for filesystem checks */
149
+ cwd?: string;
150
+ /** Force git ref interpretation for ambiguous targets */
151
+ forceGit?: boolean;
152
+ }
153
+
154
+ /**
155
+ * Detect if a target looks like a git ref vs a file path.
156
+ * Returns 'git' for git refs, 'file' for file paths.
157
+ *
158
+ * For ambiguous targets (no path separators, no extension), checks
159
+ * if a file/directory exists at that path before defaulting to git ref.
160
+ */
161
+ export function detectTargetType(target: string, options: DetectTargetTypeOptions = {}): 'git' | 'file' {
162
+ const { cwd = process.cwd(), forceGit = false } = options;
163
+
164
+ // Git range syntax (e.g., main..feature, HEAD~3..HEAD)
165
+ if (target.includes('..')) {
166
+ return 'git';
167
+ }
168
+
169
+ // Relative ref syntax (e.g., HEAD~3, main^2)
170
+ if (/[~^]\d*$/.test(target)) {
171
+ return 'git';
172
+ }
173
+
174
+ // Common git refs
175
+ if (/^(HEAD|FETCH_HEAD|ORIG_HEAD|MERGE_HEAD)$/i.test(target)) {
176
+ return 'git';
177
+ }
178
+
179
+ // Contains path separators or glob characters → file
180
+ if (target.includes('/') || target.includes('*') || target.includes('?')) {
181
+ return 'file';
182
+ }
183
+
184
+ // Has a file extension → file
185
+ if (/\.\w+$/.test(target)) {
186
+ return 'file';
187
+ }
188
+
189
+ // Ambiguous target (no path separators, no extension)
190
+ // If --git flag is set, force git ref interpretation
191
+ if (forceGit) {
192
+ return 'git';
193
+ }
194
+
195
+ // Check if file/directory exists at this path
196
+ const fullPath = `${cwd}/${target}`;
197
+ if (existsSync(fullPath)) {
198
+ return 'file';
199
+ }
200
+
201
+ // Default to git ref (will be validated later)
202
+ return 'git';
203
+ }
204
+
205
+ /**
206
+ * Classify targets into git refs and file patterns.
207
+ */
208
+ export function classifyTargets(targets: string[], options: DetectTargetTypeOptions = {}): { gitRefs: string[]; filePatterns: string[] } {
209
+ const gitRefs: string[] = [];
210
+ const filePatterns: string[] = [];
211
+
212
+ for (const target of targets) {
213
+ if (detectTargetType(target, options) === 'git') {
214
+ gitRefs.push(target);
215
+ } else {
216
+ filePatterns.push(target);
217
+ }
218
+ }
219
+
220
+ return { gitRefs, filePatterns };
221
+ }
222
+
223
+ /**
224
+ * Resolve color option from --color / --no-color flags.
225
+ * Returns undefined for auto-detect, true for forced color, false for no color.
226
+ */
227
+ function resolveColorOption(values: { color?: boolean; 'no-color'?: boolean }): boolean | undefined {
228
+ if (values['no-color']) {
229
+ return false;
230
+ }
231
+ if (values.color) {
232
+ return true;
233
+ }
234
+ return undefined;
235
+ }
236
+
237
+ export function parseCliArgs(argv: string[] = process.argv.slice(2)): ParsedArgs {
238
+ // Count -v flags before parsing (parseArgs doesn't handle multiple -v well)
239
+ let verboseCount = 0;
240
+ const filteredArgv = argv.filter((arg) => {
241
+ if (arg === '-v' || arg === '--verbose') {
242
+ verboseCount++;
243
+ return false;
244
+ }
245
+ if (arg === '-vv') {
246
+ verboseCount += 2;
247
+ return false;
248
+ }
249
+ return true;
250
+ });
251
+
252
+ const { values, positionals } = parseArgs({
253
+ args: filteredArgv,
254
+ options: {
255
+ skill: { type: 'string' },
256
+ config: { type: 'string' },
257
+ model: { type: 'string', short: 'm' },
258
+ json: { type: 'boolean', default: false },
259
+ output: { type: 'string', short: 'o' },
260
+ 'fail-on': { type: 'string' },
261
+ 'comment-on': { type: 'string' },
262
+ fix: { type: 'boolean', default: false },
263
+ force: { type: 'boolean', short: 'f', default: false },
264
+ list: { type: 'boolean', short: 'l', default: false },
265
+ remote: { type: 'string' },
266
+ offline: { type: 'boolean', default: false },
267
+ parallel: { type: 'string' },
268
+ git: { type: 'boolean', default: false },
269
+ help: { type: 'boolean', short: 'h', default: false },
270
+ version: { type: 'boolean', short: 'V', default: false },
271
+ quiet: { type: 'boolean', default: false },
272
+ color: { type: 'boolean' },
273
+ 'no-color': { type: 'boolean' },
274
+ // setup-app options
275
+ org: { type: 'string' },
276
+ port: { type: 'string' },
277
+ timeout: { type: 'string' },
278
+ name: { type: 'string' },
279
+ open: { type: 'boolean', default: true },
280
+ 'no-open': { type: 'boolean' },
281
+ },
282
+ allowPositionals: true,
283
+ });
284
+
285
+ if (values.version) {
286
+ return {
287
+ command: 'version',
288
+ options: CLIOptionsSchema.parse({}),
289
+ };
290
+ }
291
+
292
+ if (values.help) {
293
+ return {
294
+ command: 'help',
295
+ options: CLIOptionsSchema.parse({ help: true }),
296
+ };
297
+ }
298
+
299
+ // Filter out known commands from positionals
300
+ const commands = ['run', 'help', 'init', 'add', 'version', 'setup-app', 'sync'];
301
+ const targets = positionals.filter((p) => !commands.includes(p));
302
+
303
+ // Handle explicit help command
304
+ if (positionals.includes('help')) {
305
+ return {
306
+ command: 'help',
307
+ options: CLIOptionsSchema.parse({ help: true }),
308
+ };
309
+ }
310
+
311
+ // Handle explicit version command
312
+ if (positionals.includes('version')) {
313
+ return {
314
+ command: 'version',
315
+ options: CLIOptionsSchema.parse({}),
316
+ };
317
+ }
318
+
319
+ // Handle init command
320
+ if (positionals.includes('init')) {
321
+ return {
322
+ command: 'init',
323
+ options: CLIOptionsSchema.parse({
324
+ force: values.force,
325
+ quiet: values.quiet,
326
+ color: resolveColorOption(values),
327
+ }),
328
+ };
329
+ }
330
+
331
+ // Handle add command
332
+ if (positionals.includes('add')) {
333
+ // First positional after 'add' is the skill name
334
+ const addIndex = positionals.indexOf('add');
335
+ const skillArg = positionals[addIndex + 1];
336
+
337
+ return {
338
+ command: 'add',
339
+ options: CLIOptionsSchema.parse({
340
+ skill: values.skill ?? skillArg,
341
+ list: values.list,
342
+ remote: values.remote,
343
+ quiet: values.quiet,
344
+ color: resolveColorOption(values),
345
+ }),
346
+ };
347
+ }
348
+
349
+ // Handle sync command
350
+ if (positionals.includes('sync')) {
351
+ // First positional after 'sync' is the remote to sync, --remote flag takes precedence
352
+ const syncIndex = positionals.indexOf('sync');
353
+ const remoteArg = values.remote ?? positionals[syncIndex + 1];
354
+
355
+ return {
356
+ command: 'sync',
357
+ options: CLIOptionsSchema.parse({
358
+ remote: remoteArg,
359
+ quiet: values.quiet,
360
+ color: resolveColorOption(values),
361
+ }),
362
+ };
363
+ }
364
+
365
+ // Handle setup-app command
366
+ if (positionals.includes('setup-app')) {
367
+ return {
368
+ command: 'setup-app',
369
+ options: CLIOptionsSchema.parse({
370
+ quiet: values.quiet,
371
+ color: resolveColorOption(values),
372
+ }),
373
+ setupAppOptions: {
374
+ org: values.org as string | undefined,
375
+ port: values.port ? parseInt(values.port as string, 10) : 3000,
376
+ timeout: values.timeout ? parseInt(values.timeout as string, 10) : 300,
377
+ name: values.name as string | undefined,
378
+ open: !values['no-open'],
379
+ },
380
+ };
381
+ }
382
+
383
+ const rawOptions = {
384
+ targets: targets.length > 0 ? targets : undefined,
385
+ skill: values.skill,
386
+ config: values.config,
387
+ model: values.model,
388
+ json: values.json,
389
+ output: values.output,
390
+ failOn: values['fail-on'] as SeverityThreshold | undefined,
391
+ commentOn: values['comment-on'] as SeverityThreshold | undefined,
392
+ fix: values.fix,
393
+ force: values.force,
394
+ parallel: values.parallel ? parseInt(values.parallel, 10) : undefined,
395
+ git: values.git,
396
+ offline: values.offline,
397
+ help: values.help,
398
+ quiet: values.quiet,
399
+ verbose: verboseCount,
400
+ color: resolveColorOption(values),
401
+ };
402
+
403
+ const result = CLIOptionsSchema.safeParse(rawOptions);
404
+ if (!result.success) {
405
+ const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`);
406
+ console.error('Invalid options:');
407
+ console.error(issues.join('\n'));
408
+ process.exit(1);
409
+ }
410
+
411
+ return {
412
+ command: 'run',
413
+ options: result.data,
414
+ };
415
+ }