@ulpi/cli 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -214
- package/dist/{chunk-KYYI23AQ.js → chunk-BV5UYMYQ.js} +1 -1
- package/dist/{chunk-V2H5D6Y3.js → chunk-PO4NUZUU.js} +2 -1
- package/dist/{chunk-VXH5Y4FO.js → chunk-WVOZE25N.js} +6 -10
- package/dist/{chunk-VVEDXI7E.js → chunk-XKF4DPUM.js} +6 -6
- package/dist/{ci-X3U2W4HC.js → ci-ZKXPTYOS.js} +2 -2
- package/dist/{doctor-SI4LLLDZ.js → doctor-OHAU2ZOF.js} +1 -1
- package/dist/{history-5NE46ZAH.js → history-INYAXMBQ.js} +1 -1
- package/dist/{hooks-installer-UN5JZLDQ.js → hooks-installer-YEYTYA6Q.js} +1 -1
- package/dist/index.js +16 -16
- package/dist/{init-5FK3VKRT.js → init-TJYW5ROZ.js} +4 -4
- package/dist/{launchd-6AWT54HR.js → launchd-U3MSWBRH.js} +1 -1
- package/dist/{review-integration-5WHEJU2A.js → review-integration-RQE4KMAV.js} +1 -1
- package/dist/{server-KKSETHDV-XSSLEENT.js → server-U7PQ6FTS-MG4MJPTS.js} +1 -1
- package/dist/skills/ulpi-generate-guards/SKILL.md +750 -0
- package/dist/skills/ulpi-generate-guards/references/framework-rules.md +849 -0
- package/dist/skills/ulpi-generate-guards/references/language-rules.md +591 -0
- package/dist/{start-JYOEL7AJ.js → start-SQRNELKC.js} +3 -3
- package/dist/{uninstall-ICUV6DDV.js → uninstall-BX6FOV77.js} +2 -2
- package/dist/{update-7ZMAYRBH.js → update-V4LET4UD.js} +2 -2
- package/dist/{version-checker-4ZFMZA7Y.js → version-checker-AUAHP4P2.js} +1 -1
- package/package.json +39 -38
- package/LICENSE +0 -21
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ulpi-generate-guards
|
|
3
|
+
description: Use when the user asks to generate ULPI configuration for a project. Detects language, framework, package manager, and tooling to create optimized guards.yml with preconditions, permissions, postconditions, and pipelines. Invoke via /ulpi-generate-guards or when user says "generate guards", "create guards.yml", "setup ulpi", "configure guardian".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<EXTREMELY-IMPORTANT>
|
|
7
|
+
Before generating ANY guards.yml configuration, you **ABSOLUTELY MUST**:
|
|
8
|
+
|
|
9
|
+
1. Verify the target directory exists
|
|
10
|
+
2. Check for existing `.ulpi/guards.yml` (ask before overwriting)
|
|
11
|
+
3. Detect at least one technology signal (language, framework, or package manager)
|
|
12
|
+
|
|
13
|
+
**Generating without verification = wrong rules, overwritten configs, broken guards**
|
|
14
|
+
|
|
15
|
+
This is not optional. Every generation requires disciplined verification.
|
|
16
|
+
</EXTREMELY-IMPORTANT>
|
|
17
|
+
|
|
18
|
+
# Generate ULPI Configuration
|
|
19
|
+
|
|
20
|
+
## MANDATORY FIRST RESPONSE PROTOCOL
|
|
21
|
+
|
|
22
|
+
Before generating ANY configuration, you **MUST** complete this checklist:
|
|
23
|
+
|
|
24
|
+
1. ☐ Verify target directory exists
|
|
25
|
+
2. ☐ Check for existing guards.yml
|
|
26
|
+
3. ☐ Detect language (tsconfig.json, pyproject.toml, go.mod, etc.)
|
|
27
|
+
4. ☐ Detect framework (next.config.*, artisan, manage.py, etc.)
|
|
28
|
+
5. ☐ Detect package manager (pnpm-lock.yaml, yarn.lock, etc.)
|
|
29
|
+
6. ☐ Announce: "Generating ULPI configuration for [language]/[framework]/[package_manager]"
|
|
30
|
+
|
|
31
|
+
Do NOT ask "Proceed?" — the user invoked this skill explicitly. Show the detected stack as an informational summary, then generate immediately.
|
|
32
|
+
|
|
33
|
+
**Generating WITHOUT completing this checklist = wrong or harmful rules.**
|
|
34
|
+
|
|
35
|
+
## Purpose
|
|
36
|
+
|
|
37
|
+
This skill generates configuration for **ULPI**, a tool that:
|
|
38
|
+
- Auto-approves safe operations (reads, package manager commands)
|
|
39
|
+
- Blocks dangerous commands (force push, database wipes, env file edits)
|
|
40
|
+
- Enforces best practices (read-before-write)
|
|
41
|
+
- Runs postconditions (lint, test, generate) after file changes
|
|
42
|
+
|
|
43
|
+
**Output:** `.ulpi/guards.yml` configuration file
|
|
44
|
+
|
|
45
|
+
**Does NOT:** Install ULPI, run the generated rules, or modify existing configurations without confirmation.
|
|
46
|
+
|
|
47
|
+
## Overview
|
|
48
|
+
|
|
49
|
+
Analyze a project directory, detect the technology stack, and generate a complete `guards.yml` configuration for ULPI. Creates rules that auto-approve safe operations, block dangerous commands, and enforce best practices.
|
|
50
|
+
|
|
51
|
+
## When to Use
|
|
52
|
+
|
|
53
|
+
- User says "generate guards", "create guards.yml", "/ulpi-generate-guards"
|
|
54
|
+
- User says "setup ulpi", "configure guardian for this project"
|
|
55
|
+
- $ARGUMENTS contains a path (e.g., `/ulpi-generate-guards /path/to/project`)
|
|
56
|
+
|
|
57
|
+
**Never generate unprompted.** Only when explicitly requested.
|
|
58
|
+
|
|
59
|
+
## Step 1: Determine Target Directory
|
|
60
|
+
|
|
61
|
+
**Gate: Valid directory confirmed before proceeding to Step 2.**
|
|
62
|
+
|
|
63
|
+
If $ARGUMENTS has a path, use it. Otherwise use current working directory.
|
|
64
|
+
|
|
65
|
+
Verify the directory exists before proceeding. If not found, stop and inform the user.
|
|
66
|
+
|
|
67
|
+
Check for existing `.ulpi/guards.yml`:
|
|
68
|
+
- If found, ask user: merge, overwrite, or abort?
|
|
69
|
+
- Never overwrite without explicit confirmation
|
|
70
|
+
|
|
71
|
+
## Step 2: Detect Technology Stack
|
|
72
|
+
|
|
73
|
+
**Gate: Stack detected before proceeding to Step 3.**
|
|
74
|
+
|
|
75
|
+
Scan for indicator files in priority order:
|
|
76
|
+
|
|
77
|
+
### Language Detection
|
|
78
|
+
|
|
79
|
+
| Signal | Language |
|
|
80
|
+
|--------|----------|
|
|
81
|
+
| `tsconfig.json` | TypeScript |
|
|
82
|
+
| `package.json` (no tsconfig) | JavaScript |
|
|
83
|
+
| `pyproject.toml`, `requirements.txt` | Python |
|
|
84
|
+
| `go.mod` | Go |
|
|
85
|
+
| `Cargo.toml` | Rust |
|
|
86
|
+
| `composer.json` | PHP |
|
|
87
|
+
| `Gemfile` | Ruby |
|
|
88
|
+
| `pom.xml`, `build.gradle` | Java |
|
|
89
|
+
| `*.csproj`, `*.sln` | C# |
|
|
90
|
+
| `mix.exs` | Elixir |
|
|
91
|
+
|
|
92
|
+
### Framework Detection
|
|
93
|
+
|
|
94
|
+
| Signal | Framework |
|
|
95
|
+
|--------|-----------|
|
|
96
|
+
| `next.config.*` | Next.js |
|
|
97
|
+
| `nuxt.config.*` | Nuxt |
|
|
98
|
+
| `angular.json` | Angular |
|
|
99
|
+
| `svelte.config.*` | SvelteKit |
|
|
100
|
+
| `nest-cli.json` | NestJS |
|
|
101
|
+
| `artisan` | Laravel |
|
|
102
|
+
| `manage.py` + django | Django |
|
|
103
|
+
| `fastapi` in deps | FastAPI |
|
|
104
|
+
| `actix-web` in Cargo.toml | Actix |
|
|
105
|
+
| `gin` in go.mod | Gin |
|
|
106
|
+
|
|
107
|
+
### Package Manager Detection
|
|
108
|
+
|
|
109
|
+
| Signal | Package Manager |
|
|
110
|
+
|--------|-----------------|
|
|
111
|
+
| `pnpm-lock.yaml` | pnpm |
|
|
112
|
+
| `yarn.lock` | yarn |
|
|
113
|
+
| `package-lock.json` | npm |
|
|
114
|
+
| `bun.lockb` | bun |
|
|
115
|
+
| `poetry.lock` | poetry |
|
|
116
|
+
| `uv.lock` | uv |
|
|
117
|
+
| `Cargo.lock` | cargo |
|
|
118
|
+
| `composer.lock` | composer |
|
|
119
|
+
| `Gemfile.lock` | bundler |
|
|
120
|
+
|
|
121
|
+
### Tooling Detection
|
|
122
|
+
|
|
123
|
+
| Signal | Tool | Type |
|
|
124
|
+
|--------|------|------|
|
|
125
|
+
| `vitest.config.*` | Vitest | test |
|
|
126
|
+
| `jest.config.*` | Jest | test |
|
|
127
|
+
| `pytest.ini` | pytest | test |
|
|
128
|
+
| `.eslintrc*` | ESLint | lint |
|
|
129
|
+
| `.prettierrc*` | Prettier | format |
|
|
130
|
+
| `biome.json` | Biome | lint+format |
|
|
131
|
+
| `prisma/schema.prisma` | Prisma | ORM |
|
|
132
|
+
| `drizzle.config.*` | Drizzle | ORM |
|
|
133
|
+
|
|
134
|
+
### Monorepo Detection
|
|
135
|
+
|
|
136
|
+
| Signal | Structure |
|
|
137
|
+
|--------|-----------|
|
|
138
|
+
| `turbo.json` | Turborepo |
|
|
139
|
+
| `lerna.json` | Lerna |
|
|
140
|
+
| `pnpm-workspace.yaml` | pnpm workspaces |
|
|
141
|
+
| `"workspaces"` in package.json | Yarn/npm workspaces |
|
|
142
|
+
|
|
143
|
+
**If a monorepo is detected, you MUST proceed to Step 2b (Map Monorepo Topology) before continuing.**
|
|
144
|
+
|
|
145
|
+
## Step 2b: Map Monorepo Topology (Monorepos Only)
|
|
146
|
+
|
|
147
|
+
**Gate: Full workspace topology mapped before proceeding to Step 3.**
|
|
148
|
+
|
|
149
|
+
When a monorepo is detected, do NOT treat it as a flat project. You must:
|
|
150
|
+
|
|
151
|
+
1. **Read the workspace config** — `pnpm-workspace.yaml`, `lerna.json`, or `package.json` workspaces field to find all workspace globs
|
|
152
|
+
2. **List all packages** — Resolve workspace globs to actual directories (e.g., `apps/*`, `packages/*`)
|
|
153
|
+
3. **Read each package's `package.json`** — Identify name, dependencies, devDependencies, scripts
|
|
154
|
+
4. **Map the dependency graph** — Which packages depend on which? Identify the direction of dependency flow
|
|
155
|
+
5. **Read the task runner config** — `turbo.json` task definitions, dependency chains (`^` prefix = depends on upstream)
|
|
156
|
+
6. **Identify build characteristics** — Does the package have a build step? Is it consumed as source? Does it have its own tsconfig?
|
|
157
|
+
|
|
158
|
+
### Topology Output Format
|
|
159
|
+
|
|
160
|
+
Record the topology as a YAML comment in the header:
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
# Workspace Topology:
|
|
164
|
+
# {package-a} → {package-b} (one-way dependency)
|
|
165
|
+
# {package-c} (standalone)
|
|
166
|
+
#
|
|
167
|
+
# Task Graph (turbo.json):
|
|
168
|
+
# build: depends on ^build
|
|
169
|
+
# type-check: depends on ^type-check
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### What Monorepo Topology Enables
|
|
173
|
+
|
|
174
|
+
- **Package boundary enforcement** — Prevent imports in the wrong direction
|
|
175
|
+
- **Per-package postconditions** — Type-check only the affected package using `turbo --filter=`
|
|
176
|
+
- **Turbo-first commands** — Use `turbo run` instead of direct package manager for tasks defined in turbo.json
|
|
177
|
+
- **Critical file cautions** — Barrel exports, shared configs, workspace manifests
|
|
178
|
+
|
|
179
|
+
See `references/framework-rules.md` → Turborepo section for complete rule templates.
|
|
180
|
+
|
|
181
|
+
## Step 3: Extract Commands from Config
|
|
182
|
+
|
|
183
|
+
**Gate: Commands extracted before proceeding to Step 4.**
|
|
184
|
+
|
|
185
|
+
For Node.js projects, read package.json scripts to identify:
|
|
186
|
+
- `test_command` (from "test" script)
|
|
187
|
+
- `build_command` (from "build" script)
|
|
188
|
+
- `lint_command` (from "lint" script)
|
|
189
|
+
- `format_command` (from "format" script)
|
|
190
|
+
|
|
191
|
+
**For monorepos:** Read BOTH the root `package.json` AND `turbo.json`:
|
|
192
|
+
- If root scripts delegate to turbo (e.g., `"build": "turbo run build"`), the real commands are turbo tasks
|
|
193
|
+
- Read each workspace's `package.json` scripts to understand what turbo invokes per-package
|
|
194
|
+
- Postconditions and pipelines should use `turbo run <task> --filter=<package>`, NOT direct `pnpm <script>`
|
|
195
|
+
|
|
196
|
+
For Python projects, check pyproject.toml for tool configurations.
|
|
197
|
+
|
|
198
|
+
For Rust projects, use standard cargo commands.
|
|
199
|
+
|
|
200
|
+
## Step 4: Generate guards.yml
|
|
201
|
+
|
|
202
|
+
**Gate: Complete rules generated before proceeding to Step 5.**
|
|
203
|
+
|
|
204
|
+
Create `.ulpi/guards.yml` with these sections:
|
|
205
|
+
|
|
206
|
+
### Header
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
# ULPI — Generated Configuration
|
|
210
|
+
# Stack: {language} / {framework} / {package_manager}
|
|
211
|
+
# Generated: {timestamp}
|
|
212
|
+
|
|
213
|
+
project:
|
|
214
|
+
name: "{project_name}"
|
|
215
|
+
runtime: "{runtime}"
|
|
216
|
+
package_manager: "{package_manager}"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Universal Preconditions (Always Include)
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
preconditions:
|
|
223
|
+
read-before-write:
|
|
224
|
+
enabled: true
|
|
225
|
+
trigger: PreToolUse
|
|
226
|
+
matcher: "Write|Edit|MultiEdit"
|
|
227
|
+
requires_read: true
|
|
228
|
+
message: "Read {file_path} before editing it."
|
|
229
|
+
locked: true
|
|
230
|
+
priority: 10
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Universal Permissions (Always Include)
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
permissions:
|
|
237
|
+
# --- Auto-approvals ---
|
|
238
|
+
auto-approve-reads:
|
|
239
|
+
enabled: true
|
|
240
|
+
trigger: PermissionRequest
|
|
241
|
+
matcher: "Read|LS|Glob|Grep"
|
|
242
|
+
decision: allow
|
|
243
|
+
priority: 100
|
|
244
|
+
|
|
245
|
+
auto-approve-git-readonly:
|
|
246
|
+
enabled: true
|
|
247
|
+
trigger: PermissionRequest
|
|
248
|
+
matcher: Bash
|
|
249
|
+
command_pattern: "git (status|log|diff|branch|show|stash list|remote -v|rev-parse)"
|
|
250
|
+
decision: allow
|
|
251
|
+
priority: 90
|
|
252
|
+
|
|
253
|
+
auto-approve-gh-cli:
|
|
254
|
+
enabled: true
|
|
255
|
+
trigger: PermissionRequest
|
|
256
|
+
matcher: Bash
|
|
257
|
+
command_pattern: "gh"
|
|
258
|
+
decision: allow
|
|
259
|
+
priority: 80
|
|
260
|
+
|
|
261
|
+
auto-approve-prettier:
|
|
262
|
+
enabled: true
|
|
263
|
+
trigger: PermissionRequest
|
|
264
|
+
matcher: Bash
|
|
265
|
+
command_pattern: "prettier"
|
|
266
|
+
decision: allow
|
|
267
|
+
priority: 80
|
|
268
|
+
|
|
269
|
+
auto-approve-shell-inspection:
|
|
270
|
+
enabled: true
|
|
271
|
+
trigger: PermissionRequest
|
|
272
|
+
matcher: Bash
|
|
273
|
+
command_pattern: "(ls|cat|head|tail|wc|file|which|echo|pwd|whoami)"
|
|
274
|
+
decision: allow
|
|
275
|
+
priority: 80
|
|
276
|
+
|
|
277
|
+
# --- Git destructive blocks (comprehensive) ---
|
|
278
|
+
no-force-push:
|
|
279
|
+
enabled: true
|
|
280
|
+
trigger: PreToolUse
|
|
281
|
+
matcher: Bash
|
|
282
|
+
command_pattern: "git push.*(--force|-f)"
|
|
283
|
+
decision: deny
|
|
284
|
+
message: "Force push blocked. Use --force-with-lease."
|
|
285
|
+
locked: true
|
|
286
|
+
priority: 1
|
|
287
|
+
|
|
288
|
+
no-git-checkout-dot:
|
|
289
|
+
enabled: true
|
|
290
|
+
trigger: PreToolUse
|
|
291
|
+
matcher: Bash
|
|
292
|
+
command_pattern: "git checkout \\."
|
|
293
|
+
decision: deny
|
|
294
|
+
message: "git checkout . discards all unstaged changes."
|
|
295
|
+
locked: true
|
|
296
|
+
priority: 1
|
|
297
|
+
|
|
298
|
+
no-git-restore-dot:
|
|
299
|
+
enabled: true
|
|
300
|
+
trigger: PreToolUse
|
|
301
|
+
matcher: Bash
|
|
302
|
+
command_pattern: "git restore \\."
|
|
303
|
+
decision: deny
|
|
304
|
+
message: "git restore . discards all unstaged changes."
|
|
305
|
+
locked: true
|
|
306
|
+
priority: 1
|
|
307
|
+
|
|
308
|
+
no-git-reset-hard:
|
|
309
|
+
enabled: true
|
|
310
|
+
trigger: PreToolUse
|
|
311
|
+
matcher: Bash
|
|
312
|
+
command_pattern: "git reset --hard"
|
|
313
|
+
decision: deny
|
|
314
|
+
message: "git reset --hard destroys uncommitted work."
|
|
315
|
+
locked: true
|
|
316
|
+
priority: 1
|
|
317
|
+
|
|
318
|
+
no-git-clean-force:
|
|
319
|
+
enabled: true
|
|
320
|
+
trigger: PreToolUse
|
|
321
|
+
matcher: Bash
|
|
322
|
+
command_pattern: "git clean.* -f"
|
|
323
|
+
decision: deny
|
|
324
|
+
message: "git clean -f permanently deletes untracked files."
|
|
325
|
+
locked: true
|
|
326
|
+
priority: 1
|
|
327
|
+
|
|
328
|
+
no-git-branch-delete-force:
|
|
329
|
+
enabled: true
|
|
330
|
+
trigger: PreToolUse
|
|
331
|
+
matcher: Bash
|
|
332
|
+
command_pattern: "git branch -D"
|
|
333
|
+
decision: deny
|
|
334
|
+
message: "git branch -D force-deletes branches. Use -d for safe delete."
|
|
335
|
+
locked: true
|
|
336
|
+
priority: 1
|
|
337
|
+
|
|
338
|
+
no-skip-hooks:
|
|
339
|
+
enabled: true
|
|
340
|
+
trigger: PreToolUse
|
|
341
|
+
matcher: Bash
|
|
342
|
+
command_pattern: "--no-verify"
|
|
343
|
+
decision: deny
|
|
344
|
+
message: "Do not skip git hooks."
|
|
345
|
+
locked: true
|
|
346
|
+
priority: 1
|
|
347
|
+
|
|
348
|
+
no-rm-rf:
|
|
349
|
+
enabled: true
|
|
350
|
+
trigger: PreToolUse
|
|
351
|
+
matcher: Bash
|
|
352
|
+
command_pattern: "rm -rf"
|
|
353
|
+
decision: deny
|
|
354
|
+
message: "rm -rf is destructive. Remove specific files instead."
|
|
355
|
+
locked: true
|
|
356
|
+
priority: 1
|
|
357
|
+
|
|
358
|
+
# --- File protection ---
|
|
359
|
+
block-env-files:
|
|
360
|
+
enabled: true
|
|
361
|
+
trigger: PreToolUse
|
|
362
|
+
matcher: "Write|Edit"
|
|
363
|
+
file_pattern: ".env*"
|
|
364
|
+
decision: deny
|
|
365
|
+
message: "Cannot edit .env files directly."
|
|
366
|
+
priority: 50
|
|
367
|
+
|
|
368
|
+
block-node-modules:
|
|
369
|
+
enabled: true
|
|
370
|
+
trigger: PreToolUse
|
|
371
|
+
matcher: "Write|Edit"
|
|
372
|
+
file_pattern: "node_modules/**"
|
|
373
|
+
decision: deny
|
|
374
|
+
locked: true
|
|
375
|
+
priority: 1
|
|
376
|
+
|
|
377
|
+
block-dist:
|
|
378
|
+
enabled: true
|
|
379
|
+
trigger: PreToolUse
|
|
380
|
+
matcher: "Write|Edit"
|
|
381
|
+
file_pattern: "**/dist/**"
|
|
382
|
+
decision: deny
|
|
383
|
+
locked: true
|
|
384
|
+
priority: 1
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Important:** `git push` is intentionally NOT auto-approved. Pushes affect shared state and should always require confirmation.
|
|
388
|
+
|
|
389
|
+
### Language-Specific Rules
|
|
390
|
+
|
|
391
|
+
Add based on detected language. See `references/language-rules.md`.
|
|
392
|
+
|
|
393
|
+
### Framework-Specific Rules
|
|
394
|
+
|
|
395
|
+
Add based on detected framework. See `references/framework-rules.md`.
|
|
396
|
+
|
|
397
|
+
### Pipelines (Required Fields)
|
|
398
|
+
|
|
399
|
+
Every pipeline MUST include `on_failure`. It is a **required field** — omitting it causes a validation error.
|
|
400
|
+
|
|
401
|
+
```yaml
|
|
402
|
+
pipelines:
|
|
403
|
+
pre-commit-checks:
|
|
404
|
+
enabled: true
|
|
405
|
+
trigger: "PreToolUse"
|
|
406
|
+
matcher: "Bash"
|
|
407
|
+
command_pattern: "git commit"
|
|
408
|
+
steps:
|
|
409
|
+
- name: "type-check"
|
|
410
|
+
run: "{type_check_command}"
|
|
411
|
+
timeout: 60000
|
|
412
|
+
- name: "lint"
|
|
413
|
+
run: "{lint_command}"
|
|
414
|
+
timeout: 60000
|
|
415
|
+
on_failure: "block" # REQUIRED — "block" or "warn"
|
|
416
|
+
message: "Pre-commit checks failed."
|
|
417
|
+
locked: true
|
|
418
|
+
priority: 5
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Valid `on_failure` values: `"block"` (prevent the action) or `"warn"` (show warning but allow).
|
|
422
|
+
|
|
423
|
+
## Step 5: Write Configuration
|
|
424
|
+
|
|
425
|
+
**Gate: File written before proceeding to Step 6.**
|
|
426
|
+
|
|
427
|
+
Create the `.ulpi/` directory if it doesn't exist.
|
|
428
|
+
|
|
429
|
+
Write the generated YAML to `guards.yml`.
|
|
430
|
+
|
|
431
|
+
Verify the file was written successfully.
|
|
432
|
+
|
|
433
|
+
## Step 6: Report Results
|
|
434
|
+
|
|
435
|
+
**Gate: Results reported before marking complete.**
|
|
436
|
+
|
|
437
|
+
Report to the user:
|
|
438
|
+
- Stack detected
|
|
439
|
+
- Rules created (counts)
|
|
440
|
+
- File location
|
|
441
|
+
- Suggested next steps
|
|
442
|
+
|
|
443
|
+
## Pre-Generation Checklist
|
|
444
|
+
|
|
445
|
+
Before generating, verify:
|
|
446
|
+
- [ ] Target directory exists and is accessible
|
|
447
|
+
- [ ] No existing guards.yml OR user approved overwrite
|
|
448
|
+
- [ ] At least one technology detected
|
|
449
|
+
|
|
450
|
+
## Error Handling
|
|
451
|
+
|
|
452
|
+
| Situation | Action |
|
|
453
|
+
|-----------|--------|
|
|
454
|
+
| Directory not found | Stop and inform user |
|
|
455
|
+
| No tech stack detected | Generate minimal universal rules only |
|
|
456
|
+
| Multiple frameworks | Ask user which is primary |
|
|
457
|
+
| Existing guards.yml | Ask: merge, overwrite, or abort |
|
|
458
|
+
| Conflicting signals | Prefer more specific (framework > language) |
|
|
459
|
+
|
|
460
|
+
## About Postconditions
|
|
461
|
+
|
|
462
|
+
Postconditions are **disabled by default** because they run automatically after file changes and may:
|
|
463
|
+
- Slow down workflows
|
|
464
|
+
- Produce unexpected side effects
|
|
465
|
+
- Conflict with user's preferred workflow
|
|
466
|
+
|
|
467
|
+
**To enable:** User should manually set `enabled: true` for desired postconditions after reviewing them.
|
|
468
|
+
|
|
469
|
+
## About Pipelines
|
|
470
|
+
|
|
471
|
+
Pipelines define multi-step checks (e.g., pre-commit). Every pipeline **MUST** include the `on_failure` field — it is required. Valid values: `"block"` (stop the action) or `"warn"` (show warning but proceed).
|
|
472
|
+
|
|
473
|
+
```yaml
|
|
474
|
+
pipelines:
|
|
475
|
+
pre-commit-checks:
|
|
476
|
+
enabled: true
|
|
477
|
+
trigger: "PreToolUse"
|
|
478
|
+
matcher: "Bash"
|
|
479
|
+
command_pattern: "git commit"
|
|
480
|
+
steps:
|
|
481
|
+
- name: "type-check"
|
|
482
|
+
run: "{type_check_command}"
|
|
483
|
+
timeout: 60000
|
|
484
|
+
- name: "lint"
|
|
485
|
+
run: "{lint_command}"
|
|
486
|
+
timeout: 60000
|
|
487
|
+
on_failure: "block" # REQUIRED — omitting this causes validation error
|
|
488
|
+
message: "Pre-commit checks."
|
|
489
|
+
locked: true
|
|
490
|
+
priority: 5
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Safety Rules
|
|
494
|
+
|
|
495
|
+
| Rule | Reason |
|
|
496
|
+
|------|--------|
|
|
497
|
+
| Always include read-before-write | Prevents editing files without reading first |
|
|
498
|
+
| Always block force push (--force AND -f) | Prevents history destruction |
|
|
499
|
+
| Always block all git destructive ops | checkout ., restore ., reset --hard, clean -f, branch -D |
|
|
500
|
+
| Always block --no-verify | Never skip git hooks |
|
|
501
|
+
| Always block rm -rf | Prevents catastrophic deletions |
|
|
502
|
+
| Always block .env edits | Protects secrets |
|
|
503
|
+
| Always block node_modules/dist | Build artifacts should not be edited |
|
|
504
|
+
| Auto-approve reads | Safe operations should not prompt |
|
|
505
|
+
| Auto-approve ONLY the detected package manager | Do not auto-approve all package managers |
|
|
506
|
+
| Never auto-approve git push | Pushes affect shared state, always confirm |
|
|
507
|
+
| Never generate state-tracking rules | ULPI has no state; rules like "track last N" are non-functional |
|
|
508
|
+
| Never generate redundant rules | If a rule duplicates another, remove the weaker one |
|
|
509
|
+
| Always include `on_failure` in pipelines | Required field — omitting causes validation error |
|
|
510
|
+
| Always map monorepo topology when detected | Flat treatment of monorepos produces wrong rules |
|
|
511
|
+
| Never overwrite without asking | Preserves existing configuration |
|
|
512
|
+
| Always verify directory exists | Prevents errors |
|
|
513
|
+
|
|
514
|
+
## Quick Reference: Command Detection
|
|
515
|
+
|
|
516
|
+
```
|
|
517
|
+
package.json scripts:
|
|
518
|
+
"test" → test_command
|
|
519
|
+
"build" → build_command
|
|
520
|
+
"lint" → lint_command
|
|
521
|
+
"format" → format_command
|
|
522
|
+
"dev" → auto-approve permission
|
|
523
|
+
|
|
524
|
+
pyproject.toml:
|
|
525
|
+
[tool.pytest] → pytest
|
|
526
|
+
[tool.ruff] → ruff
|
|
527
|
+
[tool.black] → black
|
|
528
|
+
|
|
529
|
+
Cargo.toml:
|
|
530
|
+
cargo test → test_command
|
|
531
|
+
cargo build → build_command
|
|
532
|
+
cargo clippy → lint_command
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Quality Checklist (Must Score 8/10)
|
|
538
|
+
|
|
539
|
+
Score yourself honestly before marking generation complete:
|
|
540
|
+
|
|
541
|
+
### Detection Accuracy (0-2 points)
|
|
542
|
+
- **0 points:** Guessed technology without file verification
|
|
543
|
+
- **1 point:** Detected some signals but missed others
|
|
544
|
+
- **2 points:** Verified all signals, detection matches reality
|
|
545
|
+
|
|
546
|
+
### Stack Announcement (0-2 points)
|
|
547
|
+
- **0 points:** Generated without showing detected stack
|
|
548
|
+
- **1 point:** Showed partial detection
|
|
549
|
+
- **2 points:** Full detection shown before generation
|
|
550
|
+
|
|
551
|
+
### Rule Coverage (0-2 points)
|
|
552
|
+
- **0 points:** Missing universal rules (read-before-write, block-env)
|
|
553
|
+
- **1 point:** Universal rules present but missing language/framework rules
|
|
554
|
+
- **2 points:** Complete coverage: universal + language + framework + tooling
|
|
555
|
+
|
|
556
|
+
### Safety Rules (0-2 points)
|
|
557
|
+
- **0 points:** Missing critical blocks (force-push, env files)
|
|
558
|
+
- **1 point:** Some safety rules but incomplete
|
|
559
|
+
- **2 points:** All dangerous operations blocked
|
|
560
|
+
|
|
561
|
+
### Output Quality (0-2 points)
|
|
562
|
+
- **0 points:** Invalid YAML or missing required fields
|
|
563
|
+
- **1 point:** Valid but poorly organized
|
|
564
|
+
- **2 points:** Clean, well-commented, properly structured YAML
|
|
565
|
+
|
|
566
|
+
**Minimum passing score: 8/10**
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Common Rationalizations (All Wrong)
|
|
571
|
+
|
|
572
|
+
These are excuses. Don't fall for them:
|
|
573
|
+
|
|
574
|
+
- **"The directory is obvious"** → STILL verify it exists
|
|
575
|
+
- **"I know this is a Node.js project"** → STILL detect from config files
|
|
576
|
+
- **"There's no existing guards.yml"** → STILL check before generating
|
|
577
|
+
- **"The user wants it fast"** → STILL show detected stack first
|
|
578
|
+
- **"These are standard rules"** → STILL customize for detected stack
|
|
579
|
+
- **"Postconditions are disabled anyway"** → STILL generate them correctly
|
|
580
|
+
- **"It's a monorepo but I'll just use global commands"** → Map the topology and use per-package `--filter`
|
|
581
|
+
- **"git push is just like other git commands"** → Push affects shared state, NEVER auto-approve it
|
|
582
|
+
- **"I'll add a rule to track recent actions"** → ULPI has no state between invocations, this won't work
|
|
583
|
+
- **"All Node.js package managers should be auto-approved"** → Only approve the DETECTED one
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Failure Modes
|
|
588
|
+
|
|
589
|
+
### Failure Mode 1: Wrong Technology Detection
|
|
590
|
+
|
|
591
|
+
**Symptom:** Generated Python rules for a TypeScript project
|
|
592
|
+
**Fix:** Always verify with config files, not assumptions
|
|
593
|
+
|
|
594
|
+
### Failure Mode 2: Overwritten Existing Config
|
|
595
|
+
|
|
596
|
+
**Symptom:** User's custom guards.yml was replaced without warning
|
|
597
|
+
**Fix:** Always check for existing file, ask before overwriting
|
|
598
|
+
|
|
599
|
+
### Failure Mode 3: Missing Critical Safety Rules
|
|
600
|
+
|
|
601
|
+
**Symptom:** Agent force-pushed after generation (rule wasn't blocked)
|
|
602
|
+
**Fix:** Always include universal safety rules regardless of stack
|
|
603
|
+
|
|
604
|
+
### Failure Mode 4: Invalid YAML Generated
|
|
605
|
+
|
|
606
|
+
**Symptom:** ULPI fails to parse guards.yml
|
|
607
|
+
**Fix:** Validate YAML structure before writing
|
|
608
|
+
|
|
609
|
+
### Failure Mode 5: Flat Treatment of Monorepo
|
|
610
|
+
|
|
611
|
+
**Symptom:** Postconditions run `tsc --noEmit` globally instead of `turbo --filter=<pkg>`, package boundaries not enforced, no topology in header
|
|
612
|
+
**Fix:** Always run Step 2b when monorepo is detected. Map the full workspace dependency graph. Generate per-package postconditions with `--filter`.
|
|
613
|
+
|
|
614
|
+
### Failure Mode 6: Non-Functional State-Tracking Rules
|
|
615
|
+
|
|
616
|
+
**Symptom:** Rules reference "last N actions" or "track recent commands" — ULPI has no state between invocations
|
|
617
|
+
**Fix:** Only generate rules that use ULPI's actual capabilities: matchers, patterns, triggers. No stateful tracking.
|
|
618
|
+
|
|
619
|
+
### Failure Mode 7: Redundant Rules
|
|
620
|
+
|
|
621
|
+
**Symptom:** Multiple rules block the same thing (e.g., separate `block-dist` and `block-build-output` when only one output dir exists)
|
|
622
|
+
**Fix:** Audit generated rules for overlap. Each rule should block a distinct concern.
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
## Quick Workflow Summary
|
|
627
|
+
|
|
628
|
+
```
|
|
629
|
+
STEP 1: DETERMINE TARGET
|
|
630
|
+
├── Parse $ARGUMENTS for path
|
|
631
|
+
├── Default to current directory
|
|
632
|
+
├── Verify directory exists
|
|
633
|
+
├── Check for existing guards.yml
|
|
634
|
+
└── Gate: Valid directory confirmed
|
|
635
|
+
|
|
636
|
+
STEP 2: DETECT TECHNOLOGY
|
|
637
|
+
├── Scan for language signals
|
|
638
|
+
├── Scan for framework signals
|
|
639
|
+
├── Scan for package manager signals
|
|
640
|
+
├── Scan for tooling (test, lint, ORM)
|
|
641
|
+
├── Check for monorepo structure
|
|
642
|
+
├── Announce detected stack (informational, no question)
|
|
643
|
+
└── Gate: Stack detected
|
|
644
|
+
|
|
645
|
+
STEP 2b: MAP MONOREPO TOPOLOGY (if monorepo detected)
|
|
646
|
+
├── Read workspace config (pnpm-workspace.yaml, etc.)
|
|
647
|
+
├── List all packages in workspace
|
|
648
|
+
├── Read each package's package.json
|
|
649
|
+
├── Map dependency graph (who depends on whom)
|
|
650
|
+
├── Read turbo.json / lerna.json task definitions
|
|
651
|
+
├── Identify build characteristics per package
|
|
652
|
+
└── Gate: Full topology mapped
|
|
653
|
+
|
|
654
|
+
STEP 3: EXTRACT COMMANDS
|
|
655
|
+
├── Read package.json scripts (root + per-package for monorepos)
|
|
656
|
+
├── Read turbo.json tasks (monorepos)
|
|
657
|
+
├── Read pyproject.toml tools
|
|
658
|
+
├── Identify test/build/lint commands
|
|
659
|
+
├── For monorepos: note turbo --filter commands per package
|
|
660
|
+
└── Gate: Commands extracted
|
|
661
|
+
|
|
662
|
+
STEP 4: GENERATE RULES
|
|
663
|
+
├── Start with universal rules (comprehensive git blocks, no push auto-approve)
|
|
664
|
+
├── Add language-specific rules (only detected pkg manager)
|
|
665
|
+
├── Add framework-specific rules
|
|
666
|
+
├── Add monorepo rules (boundaries, per-pkg postconditions, prefer-turbo)
|
|
667
|
+
├── Add project-specific critical file cautions
|
|
668
|
+
├── Add tooling rules
|
|
669
|
+
├── Audit for redundancy (no duplicate rules)
|
|
670
|
+
└── Gate: Complete rules generated
|
|
671
|
+
|
|
672
|
+
STEP 5: WRITE CONFIGURATION
|
|
673
|
+
├── Create .ulpi/ directory
|
|
674
|
+
├── Write guards.yml
|
|
675
|
+
├── Verify file written
|
|
676
|
+
└── Gate: File written
|
|
677
|
+
|
|
678
|
+
STEP 6: REPORT RESULTS
|
|
679
|
+
├── Show stack summary
|
|
680
|
+
├── Show rule counts
|
|
681
|
+
├── Show file location
|
|
682
|
+
├── Suggest next steps
|
|
683
|
+
└── Gate: Complete
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
---
|
|
687
|
+
|
|
688
|
+
## Completion Announcement
|
|
689
|
+
|
|
690
|
+
When generation is complete, announce:
|
|
691
|
+
|
|
692
|
+
```
|
|
693
|
+
ULPI configuration generated.
|
|
694
|
+
|
|
695
|
+
**Quality Score: X/10**
|
|
696
|
+
- Detection Accuracy: X/2
|
|
697
|
+
- Stack Announcement: X/2
|
|
698
|
+
- Rule Coverage: X/2
|
|
699
|
+
- Safety Rules: X/2
|
|
700
|
+
- Output Quality: X/2
|
|
701
|
+
|
|
702
|
+
**Stack Detected:**
|
|
703
|
+
- Language: [language]
|
|
704
|
+
- Framework: [framework]
|
|
705
|
+
- Package Manager: [package_manager]
|
|
706
|
+
- Test Runner: [test_runner]
|
|
707
|
+
- Linter: [linter]
|
|
708
|
+
|
|
709
|
+
**Rules Generated:**
|
|
710
|
+
- Preconditions: [count]
|
|
711
|
+
- Permissions: [count]
|
|
712
|
+
- Postconditions: [count]
|
|
713
|
+
|
|
714
|
+
**Output:** .ulpi/guards.yml
|
|
715
|
+
|
|
716
|
+
**Next steps:**
|
|
717
|
+
Run `ulpi rules validate` to verify configuration.
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
## Integration with Other Skills
|
|
723
|
+
|
|
724
|
+
The `ulpi-generate-guards` skill integrates with:
|
|
725
|
+
|
|
726
|
+
- **`start`** — Detects ULPI configuration needs during project setup
|
|
727
|
+
- **`commit`** — Generated rules can auto-approve git operations
|
|
728
|
+
- **`create-pr`** — Generated rules can auto-approve PR creation commands
|
|
729
|
+
|
|
730
|
+
**Workflow Chain:**
|
|
731
|
+
|
|
732
|
+
```
|
|
733
|
+
New project or directory
|
|
734
|
+
│
|
|
735
|
+
▼
|
|
736
|
+
ulpi-generate-guards skill (this skill)
|
|
737
|
+
│
|
|
738
|
+
▼
|
|
739
|
+
guards.yml created
|
|
740
|
+
│
|
|
741
|
+
▼
|
|
742
|
+
ULPI uses rules during development
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
---
|
|
746
|
+
|
|
747
|
+
## Resources
|
|
748
|
+
|
|
749
|
+
See `references/language-rules.md` for language-specific rule templates.
|
|
750
|
+
See `references/framework-rules.md` for framework-specific rule templates.
|