@bookedsolid/reagent 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/ai-platforms/ai-deepseek-specialist.md +83 -0
- package/agents/ai-platforms/ai-elevenlabs-specialist.md +75 -0
- package/agents/ai-platforms/ai-grok-specialist.md +71 -0
- package/agents/ai-platforms/ai-local-llm-specialist.md +95 -0
- package/agents/ai-platforms/ai-video-ai-specialist.md +103 -0
- package/agents/engineering/cto-advisory.md +44 -0
- package/agents/engineering/qa-engineer-automation.md +77 -0
- package/agents/engineering/qa-engineer-manual.md +48 -0
- package/agents/engineering/qa-lead.md +124 -0
- package/agents/engineering/security-engineer-appsec.md +47 -0
- package/agents/engineering/security-engineer-compliance.md +47 -0
- package/dist/cli/commands/catalyze/gap-detector.d.ts +6 -0
- package/dist/cli/commands/catalyze/gap-detector.d.ts.map +1 -0
- package/dist/cli/commands/catalyze/gap-detector.js +359 -0
- package/dist/cli/commands/catalyze/gap-detector.js.map +1 -0
- package/dist/cli/commands/catalyze/index.d.ts +15 -0
- package/dist/cli/commands/catalyze/index.d.ts.map +1 -0
- package/dist/cli/commands/catalyze/index.js +149 -0
- package/dist/cli/commands/catalyze/index.js.map +1 -0
- package/dist/cli/commands/catalyze/report-generator.d.ts +17 -0
- package/dist/cli/commands/catalyze/report-generator.d.ts.map +1 -0
- package/dist/cli/commands/catalyze/report-generator.js +290 -0
- package/dist/cli/commands/catalyze/report-generator.js.map +1 -0
- package/dist/cli/commands/catalyze/stack-analyzer.d.ts +6 -0
- package/dist/cli/commands/catalyze/stack-analyzer.d.ts.map +1 -0
- package/dist/cli/commands/catalyze/stack-analyzer.js +267 -0
- package/dist/cli/commands/catalyze/stack-analyzer.js.map +1 -0
- package/dist/cli/commands/catalyze/types.d.ts +40 -0
- package/dist/cli/commands/catalyze/types.d.ts.map +1 -0
- package/dist/cli/commands/catalyze/types.js +2 -0
- package/dist/cli/commands/catalyze/types.js.map +1 -0
- package/dist/cli/commands/init/agents.d.ts.map +1 -1
- package/dist/cli/commands/init/agents.js +9 -0
- package/dist/cli/commands/init/agents.js.map +1 -1
- package/dist/cli/commands/init/claude-hooks.d.ts.map +1 -1
- package/dist/cli/commands/init/claude-hooks.js +27 -0
- package/dist/cli/commands/init/claude-hooks.js.map +1 -1
- package/dist/cli/commands/init/commands.d.ts.map +1 -1
- package/dist/cli/commands/init/commands.js +9 -0
- package/dist/cli/commands/init/commands.js.map +1 -1
- package/dist/cli/commands/init/discord.d.ts +21 -0
- package/dist/cli/commands/init/discord.d.ts.map +1 -0
- package/dist/cli/commands/init/discord.js +87 -0
- package/dist/cli/commands/init/discord.js.map +1 -0
- package/dist/cli/commands/init/index.d.ts.map +1 -1
- package/dist/cli/commands/init/index.js +61 -17
- package/dist/cli/commands/init/index.js.map +1 -1
- package/dist/cli/commands/init/profiles.d.ts +39 -0
- package/dist/cli/commands/init/profiles.d.ts.map +1 -0
- package/dist/cli/commands/init/profiles.js +132 -0
- package/dist/cli/commands/init/profiles.js.map +1 -0
- package/dist/cli/index.js +27 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/gateway/native-tools.d.ts.map +1 -1
- package/dist/gateway/native-tools.js +25 -0
- package/dist/gateway/native-tools.js.map +1 -1
- package/dist/pm/discord-notifier.d.ts +52 -0
- package/dist/pm/discord-notifier.d.ts.map +1 -0
- package/dist/pm/discord-notifier.js +122 -0
- package/dist/pm/discord-notifier.js.map +1 -0
- package/package.json +1 -1
- package/profiles/astro/README.md +44 -0
- package/profiles/astro/agents.txt +3 -0
- package/profiles/astro/gates.yaml +15 -0
- package/profiles/astro/hooks/astro-ssr-guard.sh +73 -0
- package/profiles/drupal/README.md +53 -0
- package/profiles/drupal/agents.txt +4 -0
- package/profiles/drupal/gates.yaml +15 -0
- package/profiles/drupal/hooks/drupal-coding-standards.sh +70 -0
- package/profiles/drupal/hooks/hook-update-guard.sh +65 -0
- package/profiles/lit-wc/README.md +48 -0
- package/profiles/lit-wc/agents.txt +4 -0
- package/profiles/lit-wc/gates.yaml +15 -0
- package/profiles/lit-wc/hooks/cem-integrity-gate.sh +48 -0
- package/profiles/lit-wc/hooks/shadow-dom-guard.sh +76 -0
- package/profiles/nextjs/README.md +44 -0
- package/profiles/nextjs/agents.txt +4 -0
- package/profiles/nextjs/gates.yaml +15 -0
- package/profiles/nextjs/hooks/server-component-drift.sh +73 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { parse as parseYaml } from 'yaml';
|
|
5
|
+
/**
|
|
6
|
+
* Load discord_ops config from .reagent/gateway.yaml.
|
|
7
|
+
* Returns null if not configured or disabled.
|
|
8
|
+
*/
|
|
9
|
+
export function loadDiscordConfig(baseDir) {
|
|
10
|
+
const gatewayPath = path.join(baseDir, '.reagent', 'gateway.yaml');
|
|
11
|
+
if (!fs.existsSync(gatewayPath))
|
|
12
|
+
return null;
|
|
13
|
+
try {
|
|
14
|
+
const raw = fs.readFileSync(gatewayPath, 'utf8');
|
|
15
|
+
const parsed = parseYaml(raw);
|
|
16
|
+
const discordOps = parsed?.discord_ops;
|
|
17
|
+
if (!discordOps?.enabled)
|
|
18
|
+
return null;
|
|
19
|
+
return discordOps;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Send a message to a Discord channel via the discord-ops CLI.
|
|
27
|
+
* Fails silently — Discord notification failures must never block workflows.
|
|
28
|
+
*/
|
|
29
|
+
function sendDiscordMessage(config, channelKey, content, title) {
|
|
30
|
+
const channelId = config.channels[channelKey];
|
|
31
|
+
if (!channelId || !config.guild_id)
|
|
32
|
+
return;
|
|
33
|
+
const token = process.env['DISCORD_BOT_TOKEN'];
|
|
34
|
+
if (!token)
|
|
35
|
+
return;
|
|
36
|
+
try {
|
|
37
|
+
const args = [
|
|
38
|
+
'-y',
|
|
39
|
+
'@bookedsolid/discord-ops@latest',
|
|
40
|
+
'send_message',
|
|
41
|
+
'--channel-id',
|
|
42
|
+
channelId,
|
|
43
|
+
'--guild-id',
|
|
44
|
+
config.guild_id,
|
|
45
|
+
'--content',
|
|
46
|
+
title ? `**${title}**\n${content}` : content,
|
|
47
|
+
];
|
|
48
|
+
execFileSync('npx', args, {
|
|
49
|
+
encoding: 'utf8',
|
|
50
|
+
timeout: 10_000,
|
|
51
|
+
stdio: 'pipe',
|
|
52
|
+
env: { ...process.env, DISCORD_BOT_TOKEN: token },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Fail silently — Discord notification failure must never block anything
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Discord notifier — sends structured notifications to configured channels.
|
|
61
|
+
* All methods fail silently when Discord is not configured or unavailable.
|
|
62
|
+
*/
|
|
63
|
+
export class DiscordNotifier {
|
|
64
|
+
config;
|
|
65
|
+
constructor(baseDir) {
|
|
66
|
+
this.config = loadDiscordConfig(baseDir);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check whether Discord notifications are enabled.
|
|
70
|
+
*/
|
|
71
|
+
isEnabled() {
|
|
72
|
+
return this.config !== null && this.config.enabled;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Notify Discord that a task was created.
|
|
76
|
+
*/
|
|
77
|
+
async notifyTaskCreated(task) {
|
|
78
|
+
if (!this.config)
|
|
79
|
+
return;
|
|
80
|
+
const content = `Task created: **${task.id}** — ${task.title}` +
|
|
81
|
+
(task.urgency !== 'normal' ? ` [${task.urgency}]` : '') +
|
|
82
|
+
(task.assignee ? ` (assigned to ${task.assignee})` : '');
|
|
83
|
+
sendDiscordMessage(this.config, 'tasks', content, 'Task Created');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Notify Discord that a task was completed.
|
|
87
|
+
*/
|
|
88
|
+
async notifyTaskCompleted(task) {
|
|
89
|
+
if (!this.config)
|
|
90
|
+
return;
|
|
91
|
+
const content = `Task completed: **${task.id}** — ${task.title}`;
|
|
92
|
+
sendDiscordMessage(this.config, 'tasks', content, 'Task Completed');
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Notify Discord that a hook blocked an action.
|
|
96
|
+
*/
|
|
97
|
+
async notifyHookBlocked(hookName, tool, reason) {
|
|
98
|
+
if (!this.config)
|
|
99
|
+
return;
|
|
100
|
+
const content = `Hook **${hookName}** blocked tool \`${tool}\`\nReason: ${reason}`;
|
|
101
|
+
sendDiscordMessage(this.config, 'alerts', content, 'Hook Block');
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Notify Discord of a release.
|
|
105
|
+
*/
|
|
106
|
+
async notifyRelease(version, changelog) {
|
|
107
|
+
if (!this.config)
|
|
108
|
+
return;
|
|
109
|
+
const truncated = changelog.length > 500 ? changelog.slice(0, 497) + '...' : changelog;
|
|
110
|
+
const content = `Version **${version}** released\n\n${truncated}`;
|
|
111
|
+
sendDiscordMessage(this.config, 'releases', content, 'Release');
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Notify Discord of a security/audit alert.
|
|
115
|
+
*/
|
|
116
|
+
async notifyAuditAlert(message) {
|
|
117
|
+
if (!this.config)
|
|
118
|
+
return;
|
|
119
|
+
sendDiscordMessage(this.config, 'alerts', message, 'Audit Alert');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=discord-notifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discord-notifier.js","sourceRoot":"","sources":["../../src/pm/discord-notifier.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bookedsolid/reagent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Zero-trust MCP gateway — policy enforcement, secret redaction, and audit logging for AI-assisted projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Booked Solid Technology <oss@bookedsolid.tech> (https://bookedsolid.tech)",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# astro profile
|
|
2
|
+
|
|
3
|
+
Reagent profile for projects built with [Astro](https://astro.build/) (v3+, including v5).
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Install this profile when your project:
|
|
8
|
+
|
|
9
|
+
- Is built with Astro (any rendering mode: SSG, SSR, hybrid)
|
|
10
|
+
- Uses Astro components (`.astro` files)
|
|
11
|
+
- Has island-architecture client components (React, Vue, Svelte, Lit, etc.)
|
|
12
|
+
- Uses `astro check` and `astro build` as quality gates
|
|
13
|
+
|
|
14
|
+
## What this profile installs
|
|
15
|
+
|
|
16
|
+
### Hooks
|
|
17
|
+
|
|
18
|
+
- **astro-ssr-guard.sh** — PostToolUse/Write: warns on React hooks used in `.astro` frontmatter without a `client:*` directive, and `document`/`window` access in SSR context (frontmatter runs server-side).
|
|
19
|
+
|
|
20
|
+
### Quality gates (gates.yaml)
|
|
21
|
+
|
|
22
|
+
| Gate | Command | On failure |
|
|
23
|
+
| ---------------- | ------------------ | ---------- |
|
|
24
|
+
| astro-check | `npx astro check` | block |
|
|
25
|
+
| astro-build | `npx astro build` | block |
|
|
26
|
+
| astro-type-check | `npx tsc --noEmit` | block |
|
|
27
|
+
|
|
28
|
+
### Agents
|
|
29
|
+
|
|
30
|
+
- `astro-specialist` — Astro architecture, islands, content collections
|
|
31
|
+
- `frontend-specialist` — general frontend and component patterns
|
|
32
|
+
- `performance-engineer` — Core Web Vitals, Astro partial hydration strategies
|
|
33
|
+
|
|
34
|
+
## Recommended additions
|
|
35
|
+
|
|
36
|
+
- Playwright e2e tests for rendered pages
|
|
37
|
+
- `@astrojs/check` for enhanced type checking
|
|
38
|
+
- Lighthouse CI for CWV regression on static output
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
reagent init --profile astro
|
|
44
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
gates:
|
|
2
|
+
- name: astro-check
|
|
3
|
+
command: npx astro check
|
|
4
|
+
description: Astro type checking must pass (catches SSR/component errors)
|
|
5
|
+
on_failure: block
|
|
6
|
+
|
|
7
|
+
- name: astro-build
|
|
8
|
+
command: npx astro build
|
|
9
|
+
description: Astro production build must succeed
|
|
10
|
+
on_failure: block
|
|
11
|
+
|
|
12
|
+
- name: astro-type-check
|
|
13
|
+
command: npx tsc --noEmit
|
|
14
|
+
description: TypeScript must compile without errors
|
|
15
|
+
on_failure: block
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# profiles/astro/hooks/astro-ssr-guard.sh
|
|
3
|
+
# PostToolUse hook for Write — warns on Astro SSR anti-patterns in .astro files.
|
|
4
|
+
# Checks for: React hooks in non-client context, document/window in frontmatter.
|
|
5
|
+
# Advisory only — exits 0 after printing warnings.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
LIB="$HOOK_DIR/../../../hooks/_lib/common.sh"
|
|
11
|
+
if [[ -f "$LIB" ]]; then
|
|
12
|
+
source "$LIB"
|
|
13
|
+
check_halt
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "")
|
|
18
|
+
FILE_PATH=$(printf '%s' "$INPUT" | grep -o '"path":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "")
|
|
19
|
+
|
|
20
|
+
if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Only target .astro files
|
|
25
|
+
case "$FILE_PATH" in
|
|
26
|
+
*.astro) ;;
|
|
27
|
+
*) exit 0 ;;
|
|
28
|
+
esac
|
|
29
|
+
|
|
30
|
+
if [[ ! -f "$FILE_PATH" ]]; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
CONTENT=$(cat "$FILE_PATH" 2>/dev/null || echo "")
|
|
35
|
+
WARNINGS=()
|
|
36
|
+
|
|
37
|
+
# Extract frontmatter (between --- delimiters)
|
|
38
|
+
FRONTMATTER=""
|
|
39
|
+
if printf '%s' "$CONTENT" | grep -q '^---'; then
|
|
40
|
+
FRONTMATTER=$(printf '%s' "$CONTENT" | awk '/^---/{if(f)exit; f=1; next} f{print}')
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Check 1: React hooks in .astro files — only valid in client:* components
|
|
44
|
+
if printf '%s' "$CONTENT" | grep -qE '\b(useState|useEffect|useContext|useReducer|useCallback|useMemo|useRef)\s*[(<(]'; then
|
|
45
|
+
# Check if it's inside a component tag with client: directive
|
|
46
|
+
if ! printf '%s' "$CONTENT" | grep -qE 'client:(load|idle|visible|media|only)'; then
|
|
47
|
+
WARNINGS+=("ASTRO SSR: React hook (useState/useEffect/etc.) found but no 'client:*' directive detected in this file. React hooks only run in client-rendered components. Add 'client:load' (or other hydration strategy) to the component tag, or move hooks to a .tsx component used with 'client:load'.")
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Check 2: document or window in frontmatter (SSR context — not available server-side)
|
|
52
|
+
if [[ -n "$FRONTMATTER" ]]; then
|
|
53
|
+
if printf '%s' "$FRONTMATTER" | grep -qE '\b(document|window)\b'; then
|
|
54
|
+
WARNINGS+=("ASTRO SSR: 'document' or 'window' accessed in Astro frontmatter (the --- block). Frontmatter runs at build time / server-side where these globals are not available. Move browser API calls into a <script> tag or a client:* component.")
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Check 3: import of useState/useEffect at top of frontmatter without client usage
|
|
59
|
+
if printf '%s' "$FRONTMATTER" | grep -qE "import.*\{.*(useState|useEffect).*\}.*from\s+['\"]react['\"]"; then
|
|
60
|
+
if ! printf '%s' "$CONTENT" | grep -qE 'client:(load|idle|visible|media|only)'; then
|
|
61
|
+
WARNINGS+=("ASTRO SSR: React state/effect hooks imported in frontmatter without a 'client:*' component. This import will likely cause an error at runtime in SSR mode.")
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
if [[ ${#WARNINGS[@]} -gt 0 ]]; then
|
|
66
|
+
printf '\n[astro-ssr-guard] Astro SSR advisory warnings for %s:\n' "$FILE_PATH" >&2
|
|
67
|
+
for warning in "${WARNINGS[@]}"; do
|
|
68
|
+
printf ' WARN: %s\n' "$warning" >&2
|
|
69
|
+
done
|
|
70
|
+
printf '\n' >&2
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
exit 0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# drupal profile
|
|
2
|
+
|
|
3
|
+
Reagent profile for Drupal CMS projects (Drupal 9/10/11).
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Install this profile when your project:
|
|
8
|
+
|
|
9
|
+
- Is built on Drupal (any modern version)
|
|
10
|
+
- Uses custom modules or themes
|
|
11
|
+
- Has update hooks (`.install` files)
|
|
12
|
+
- Requires Drupal coding standards enforcement
|
|
13
|
+
|
|
14
|
+
## What this profile installs
|
|
15
|
+
|
|
16
|
+
### Hooks
|
|
17
|
+
|
|
18
|
+
- **drupal-coding-standards.sh** — PostToolUse/Write: warns on raw superglobals (`$_GET`, `$_POST`), hardcoded entity IDs, `hook_update_N` without schema management, and `t()` string concatenation.
|
|
19
|
+
- **hook-update-guard.sh** — PostToolUse/Write: guards `.install` files for destructive schema operations and update hook numbering gaps.
|
|
20
|
+
|
|
21
|
+
### Quality gates (gates.yaml)
|
|
22
|
+
|
|
23
|
+
| Gate | Command | On failure |
|
|
24
|
+
| -------------------- | ------------------------------------- | ---------- |
|
|
25
|
+
| phpcs-drupal | `vendor/bin/phpcs --standard=Drupal` | block |
|
|
26
|
+
| drupal-cache-rebuild | `vendor/bin/drush cr` | warn |
|
|
27
|
+
| phpunit-drupal | `vendor/bin/phpunit --testsuite=unit` | block |
|
|
28
|
+
|
|
29
|
+
### Agents
|
|
30
|
+
|
|
31
|
+
- `drupal-specialist` — Drupal architecture and module development
|
|
32
|
+
- `drupal-integration-specialist` — third-party integrations (APIs, payment, CRM)
|
|
33
|
+
- `backend-engineer-payments` — commerce and payment flows
|
|
34
|
+
- `security-engineer` — Drupal security review
|
|
35
|
+
|
|
36
|
+
## Recommended additions
|
|
37
|
+
|
|
38
|
+
- Behat behavioral tests for content workflows
|
|
39
|
+
- Playwright e2e for rendered frontend
|
|
40
|
+
- `phpstan` with Drupal extension for static analysis
|
|
41
|
+
|
|
42
|
+
## Prerequisites
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
composer require --dev drupal/coder
|
|
46
|
+
./vendor/bin/phpcs --config-set installed_paths vendor/drupal/coder/coder_sniffer
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
reagent init --profile drupal
|
|
53
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
gates:
|
|
2
|
+
- name: phpcs-drupal
|
|
3
|
+
command: vendor/bin/phpcs --standard=Drupal --extensions=php,module,inc,install,theme .
|
|
4
|
+
description: Drupal coding standards (PHPCS) must pass
|
|
5
|
+
on_failure: block
|
|
6
|
+
|
|
7
|
+
- name: drupal-cache-rebuild
|
|
8
|
+
command: vendor/bin/drush cr
|
|
9
|
+
description: Drush cache rebuild must succeed (validates module/theme structure)
|
|
10
|
+
on_failure: warn
|
|
11
|
+
|
|
12
|
+
- name: phpunit-drupal
|
|
13
|
+
command: vendor/bin/phpunit --testsuite=unit
|
|
14
|
+
description: Drupal PHPUnit unit tests must pass
|
|
15
|
+
on_failure: block
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# profiles/drupal/hooks/drupal-coding-standards.sh
|
|
3
|
+
# PostToolUse hook for Write — warns on Drupal anti-patterns in PHP/module/theme files.
|
|
4
|
+
# Checks for: raw superglobals, hardcoded entity IDs, hook_update_N without schema bump.
|
|
5
|
+
# Advisory only — exits 0 after printing warnings.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
LIB="$HOOK_DIR/../../../hooks/_lib/common.sh"
|
|
11
|
+
if [[ -f "$LIB" ]]; then
|
|
12
|
+
source "$LIB"
|
|
13
|
+
check_halt
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "")
|
|
18
|
+
FILE_PATH=$(printf '%s' "$INPUT" | grep -o '"path":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "")
|
|
19
|
+
|
|
20
|
+
if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Only target Drupal PHP files
|
|
25
|
+
if [[ -z "$FILE_PATH" ]]; then
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
case "$FILE_PATH" in
|
|
29
|
+
*.php|*.module|*.theme|*.install) ;;
|
|
30
|
+
*) exit 0 ;;
|
|
31
|
+
esac
|
|
32
|
+
|
|
33
|
+
if [[ ! -f "$FILE_PATH" ]]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
CONTENT=$(cat "$FILE_PATH" 2>/dev/null || echo "")
|
|
38
|
+
WARNINGS=()
|
|
39
|
+
|
|
40
|
+
# Check 1: Raw superglobal access — should use \Drupal::request()
|
|
41
|
+
if printf '%s' "$CONTENT" | grep -qE '\$_(GET|POST|REQUEST|SERVER)\['; then
|
|
42
|
+
WARNINGS+=("DRUPAL: Direct superglobal access (\$_GET, \$_POST, \$_REQUEST, \$_SERVER) detected. Use \\Drupal::request()->query->get() or \\Drupal::request()->request->get() for proper sanitization and testability.")
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Check 2: Hardcoded numeric node/entity IDs in templates or module files
|
|
46
|
+
if printf '%s' "$CONTENT" | grep -qE "(Node::load|\\\\Drupal::entityTypeManager\(\)->getStorage\('node'\)->load\(|nid\s*=\s*[0-9]+|->load\([0-9]+\))"; then
|
|
47
|
+
WARNINGS+=("DRUPAL: Hardcoded numeric entity/node ID detected. Use configuration, a content reference field, or \Drupal::config() instead of load(42) — hardcoded IDs break between environments.")
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Check 3: hook_update_N without schema version bump check
|
|
51
|
+
if printf '%s' "$CONTENT" | grep -qE 'function [a-z_]+_update_[0-9]+'; then
|
|
52
|
+
if ! printf '%s' "$CONTENT" | grep -qE 'schema_version|hook_update_dependencies|db_change_table|Schema::'; then
|
|
53
|
+
WARNINGS+=("DRUPAL: hook_update_N() found without apparent schema version management. Ensure \$sandbox['#finished'] logic is correct for batched updates, and verify schema version alignment via hook_update_dependencies() if needed.")
|
|
54
|
+
fi
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Check 4: t() with concatenated strings (breaks localization)
|
|
58
|
+
if printf '%s' "$CONTENT" | grep -qE "t\(['\"].*\\\$[a-zA-Z_]|t\(['\"].*'\s*\.\s*\\\$"; then
|
|
59
|
+
WARNINGS+=("DRUPAL: String concatenation inside t() detected. Use placeholders ('@var', '%var', ':url') instead: t('Hello @name', ['@name' => \$name]). Concatenation breaks translation extraction.")
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if [[ ${#WARNINGS[@]} -gt 0 ]]; then
|
|
63
|
+
printf '\n[drupal-coding-standards] Advisory warnings for %s:\n' "$FILE_PATH" >&2
|
|
64
|
+
for warning in "${WARNINGS[@]}"; do
|
|
65
|
+
printf ' WARN: %s\n' "$warning" >&2
|
|
66
|
+
done
|
|
67
|
+
printf '\n' >&2
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
exit 0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# profiles/drupal/hooks/hook-update-guard.sh
|
|
3
|
+
# PostToolUse hook for Write — additional guard for hook_update_N patterns.
|
|
4
|
+
# Specifically watches .install files for update hooks that modify critical schema.
|
|
5
|
+
# Advisory only — exits 0 after printing warnings.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
LIB="$HOOK_DIR/../../../hooks/_lib/common.sh"
|
|
11
|
+
if [[ -f "$LIB" ]]; then
|
|
12
|
+
source "$LIB"
|
|
13
|
+
check_halt
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "")
|
|
18
|
+
FILE_PATH=$(printf '%s' "$INPUT" | grep -o '"path":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "")
|
|
19
|
+
|
|
20
|
+
if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Only target .install files
|
|
25
|
+
case "$FILE_PATH" in
|
|
26
|
+
*.install) ;;
|
|
27
|
+
*) exit 0 ;;
|
|
28
|
+
esac
|
|
29
|
+
|
|
30
|
+
if [[ ! -f "$FILE_PATH" ]]; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
CONTENT=$(cat "$FILE_PATH" 2>/dev/null || echo "")
|
|
35
|
+
WARNINGS=()
|
|
36
|
+
|
|
37
|
+
# Check: db_drop_table / db_drop_field without backup advisory
|
|
38
|
+
if printf '%s' "$CONTENT" | grep -qE 'db_drop_(table|field)\s*\('; then
|
|
39
|
+
WARNINGS+=("DRUPAL SCHEMA: Destructive schema operation (db_drop_table/db_drop_field) detected in update hook. Ensure data migration or backup is handled before dropping. Consider using db_rename_table first if data needs preservation.")
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Check: Multiple update hooks in single file (numbering gaps)
|
|
43
|
+
UPDATE_HOOKS=$(printf '%s' "$CONTENT" | grep -oE 'function [a-z_]+_update_([0-9]+)' | grep -oE '[0-9]+$' | sort -n || echo "")
|
|
44
|
+
if [[ -n "$UPDATE_HOOKS" ]]; then
|
|
45
|
+
PREV=""
|
|
46
|
+
while IFS= read -r num; do
|
|
47
|
+
if [[ -n "$PREV" ]]; then
|
|
48
|
+
EXPECTED=$((PREV + 1))
|
|
49
|
+
if [[ "$num" -gt "$EXPECTED" ]]; then
|
|
50
|
+
WARNINGS+=("DRUPAL SCHEMA: Gap detected in update hook numbering (after $PREV, next is $num). Verify this is intentional — gaps can confuse module update ordering.")
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
PREV="$num"
|
|
54
|
+
done <<< "$UPDATE_HOOKS"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
if [[ ${#WARNINGS[@]} -gt 0 ]]; then
|
|
58
|
+
printf '\n[hook-update-guard] Advisory warnings for %s:\n' "$FILE_PATH" >&2
|
|
59
|
+
for warning in "${WARNINGS[@]}"; do
|
|
60
|
+
printf ' WARN: %s\n' "$warning" >&2
|
|
61
|
+
done
|
|
62
|
+
printf '\n' >&2
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
exit 0
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# lit-wc profile
|
|
2
|
+
|
|
3
|
+
Reagent profile for projects using [Lit](https://lit.dev/) and native Web Components (Custom Elements v1, Shadow DOM).
|
|
4
|
+
|
|
5
|
+
## When to use
|
|
6
|
+
|
|
7
|
+
Install this profile when your project:
|
|
8
|
+
|
|
9
|
+
- Uses `LitElement`, `ReactiveElement`, or plain `HTMLElement` subclasses
|
|
10
|
+
- Registers custom elements via `customElements.define()`
|
|
11
|
+
- Uses Shadow DOM for style encapsulation
|
|
12
|
+
- Uses the Custom Elements Manifest (`cem analyze`) for component documentation
|
|
13
|
+
|
|
14
|
+
## What this profile installs
|
|
15
|
+
|
|
16
|
+
### Hooks
|
|
17
|
+
|
|
18
|
+
- **shadow-dom-guard.sh** — PostToolUse/Write: warns on `document.querySelector` inside web components, missing `:host` CSS scoping, and `customElements.define()` without a guard check.
|
|
19
|
+
- **cem-integrity-gate.sh** — PostToolUse/Write: advisory reminder to regenerate `custom-elements.json` after component source changes.
|
|
20
|
+
|
|
21
|
+
### Quality gates (gates.yaml)
|
|
22
|
+
|
|
23
|
+
| Gate | Command | On failure |
|
|
24
|
+
| --------------- | -------------------- | ---------- |
|
|
25
|
+
| cem-analyze | `npx cem analyze` | block |
|
|
26
|
+
| web-test-runner | `npx wtr --coverage` | warn |
|
|
27
|
+
| lit-ts-check | `npx tsc --noEmit` | block |
|
|
28
|
+
|
|
29
|
+
### Agents
|
|
30
|
+
|
|
31
|
+
- `lit-specialist` — deep Lit/Web Components expertise
|
|
32
|
+
- `accessibility-engineer` — a11y for custom elements and ARIA
|
|
33
|
+
- `frontend-specialist` — general frontend patterns
|
|
34
|
+
- `design-system-developer` — design token and component API patterns
|
|
35
|
+
|
|
36
|
+
## Recommended additions
|
|
37
|
+
|
|
38
|
+
- Visual regression tests (Playwright snapshots or Storybook visual tests)
|
|
39
|
+
- `@axe-core/playwright` for accessibility audits per component
|
|
40
|
+
- `@web/test-runner-playwright` for real browser test execution
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
reagent init --profile lit-wc
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or during initial setup when `reagent catalyze` detects Lit/Web Components usage.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
gates:
|
|
2
|
+
- name: cem-analyze
|
|
3
|
+
command: npx cem analyze
|
|
4
|
+
description: Custom Elements Manifest must be valid and up-to-date
|
|
5
|
+
on_failure: block
|
|
6
|
+
|
|
7
|
+
- name: web-test-runner
|
|
8
|
+
command: npx wtr --coverage
|
|
9
|
+
description: Web Test Runner component tests must pass
|
|
10
|
+
on_failure: warn
|
|
11
|
+
|
|
12
|
+
- name: lit-ts-check
|
|
13
|
+
command: npx tsc --noEmit
|
|
14
|
+
description: TypeScript must compile without errors
|
|
15
|
+
on_failure: block
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# profiles/lit-wc/hooks/cem-integrity-gate.sh
|
|
3
|
+
# PostToolUse hook for Write — verifies custom elements manifest stays parseable.
|
|
4
|
+
# If cem analyze is available and a custom-elements.json exists, validates it.
|
|
5
|
+
# Advisory only — exits 0 after printing warnings.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
LIB="$HOOK_DIR/../../../hooks/_lib/common.sh"
|
|
11
|
+
if [[ -f "$LIB" ]]; then
|
|
12
|
+
source "$LIB"
|
|
13
|
+
check_halt
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "")
|
|
18
|
+
FILE_PATH=$(printf '%s' "$INPUT" | grep -o '"path":"[^"]*"' | head -1 | cut -d'"' -f4 2>/dev/null || echo "")
|
|
19
|
+
|
|
20
|
+
if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Only care about .ts/.js files (component source changes)
|
|
25
|
+
case "$FILE_PATH" in
|
|
26
|
+
*.ts|*.js) ;;
|
|
27
|
+
*) exit 0 ;;
|
|
28
|
+
esac
|
|
29
|
+
|
|
30
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
31
|
+
CEM_JSON="$PROJECT_DIR/custom-elements.json"
|
|
32
|
+
|
|
33
|
+
# Only run if a custom-elements.json exists
|
|
34
|
+
if [[ ! -f "$CEM_JSON" ]]; then
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Validate the existing manifest is valid JSON
|
|
39
|
+
if ! python3 -c "import sys,json; json.load(open('$CEM_JSON'))" 2>/dev/null && \
|
|
40
|
+
! node -e "require('$CEM_JSON')" 2>/dev/null; then
|
|
41
|
+
printf '[cem-integrity-gate] WARN: custom-elements.json exists but failed JSON validation. Run: npx cem analyze\n' >&2
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Advisory: remind to regenerate after component changes
|
|
46
|
+
printf '[cem-integrity-gate] Advisory: Component source changed. Remember to run "npx cem analyze" to keep custom-elements.json in sync.\n' >&2
|
|
47
|
+
|
|
48
|
+
exit 0
|