@relipa/ai-flow-kit 0.0.8-beta.1 → 0.0.9-beta.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/bin/ak.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
-
3
- process.env.AIFLOW_IS_AK = '1';
4
- require('./aiflow.js');
2
+
3
+ process.env.AIFLOW_IS_AK = '1';
4
+ require('./aiflow.js');
@@ -53,6 +53,72 @@ aiflow use BACKLOG-456
53
53
  # Loads task from your Backlog instance
54
54
  ```
55
55
 
56
+ ### Figma (`figma.json`)
57
+
58
+ Connect to Figma via REST API to read design files and generate components.
59
+
60
+ **When to use:** Default choice. Works anywhere — no Desktop app required.
61
+
62
+ **Setup:**
63
+ ```bash
64
+ aiflow init --adapter figma
65
+ ```
66
+
67
+ **Required Environment Variables:**
68
+ - `FIGMA_API_TOKEN` — Your Figma Personal Access Token
69
+
70
+ **Get API Token:**
71
+ 1. Go to https://www.figma.com/settings → Security → Personal access tokens
72
+ 2. Create a new token with File content (read) scope
73
+ 3. Copy the token
74
+
75
+ **Tools provided by `figma-developer-mcp`:**
76
+ - `get_figma_data(fileKey, nodeId, depth)` — fetch node layout, styles, components
77
+ - `download_figma_images(fileKey, nodes, localPath)` — export images
78
+
79
+ **Usage:**
80
+ ```
81
+ Generate component from Figma:
82
+ https://www.figma.com/design/XXXXX/App?node-id=123-456
83
+ ```
84
+
85
+ See: [Figma workflow guide](../../docs/common/workflows/figma.md)
86
+
87
+ ---
88
+
89
+ ### Figma Desktop (`figma-desktop.json`)
90
+
91
+ Connect via the official Figma MCP server. Requires Figma Desktop app open.
92
+
93
+ **When to use:** When your team prefers the official Figma tooling and has Desktop app licenses.
94
+ **When NOT to use:** CI environments, headless servers, team members without Desktop licenses.
95
+
96
+ **Setup:**
97
+ ```bash
98
+ aiflow init --adapter figma-desktop
99
+ ```
100
+
101
+ **Requirements:**
102
+ - Figma Desktop app installed
103
+ - A Figma file open in Desktop when invoking the skill
104
+ - No API token needed — uses Desktop session
105
+
106
+ **Tools provided by `@figma/mcp-server`:**
107
+ - `get_figma_data(fileKey, nodeId)` — fetch design data for open file
108
+ - `get_node_children(nodeId)` — traverse component tree
109
+ - `get_local_components()` — list components in the open file
110
+
111
+ | | `figma` (REST API) | `figma-desktop` (Official) |
112
+ |---|---|---|
113
+ | Needs Desktop app | No | Yes (must be open) |
114
+ | Auth | API Token | Desktop session |
115
+ | Works in CI | Yes | No |
116
+ | Access scope | Full REST API | Currently open file |
117
+
118
+ See: [Figma workflow guide](../../docs/common/workflows/figma.md)
119
+
120
+ ---
121
+
56
122
  ### Google Sheets (`google-sheets.json`)
57
123
 
58
124
  Connect to Google Sheets for loading task context.
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "figma": {
4
+ "command": "npx",
5
+ "args": ["-y", "@figma/mcp-server"],
6
+ "env": {}
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,51 @@
1
+ # Project Conventions (Override Skill Defaults)
2
+
3
+ > **MANDATORY:** These rules override ANY upstream skill default (including `writing-plans`, `generate-spec`, and all superpowers skills).
4
+ > Read this file before writing any output file during the Gate Workflow.
5
+
6
+ ---
7
+
8
+ ## Plan Output Paths
9
+
10
+ | Output | Path | Note |
11
+ |--------|------|------|
12
+ | Requirement doc | `plan/[ticket-id]/requirement.md` | Gate 1 output |
13
+ | Implementation plan | `plan/[ticket-id]/plan.md` | Gate 2 output — **NOT** `docs/superpowers/plans/` |
14
+ | Summary | `plan/[ticket-id]/summary.md` | Gate 4 output |
15
+
16
+ > **Override:** The `writing-plans` skill defaults to `docs/superpowers/plans/<filename>.md`.
17
+ > In this project, **always save plans to `plan/[ticket-id]/plan.md` instead.**
18
+ >
19
+ > The `[ticket-id]` comes from `.aiflow/context/current.json`.
20
+ > If no ticket context exists, use a descriptive slug: `plan/<feature-name>/plan.md`.
21
+
22
+ ---
23
+
24
+ ## Execution Handoff Message
25
+
26
+ When `writing-plans` skill says to announce the saved path, use the **actual path**:
27
+
28
+ ```
29
+ Plan complete and saved to `plan/[ticket-id]/plan.md`. Two execution options: ...
30
+ ```
31
+
32
+ Do NOT copy the hardcoded path from the skill template (`docs/superpowers/plans/`).
33
+
34
+ ---
35
+
36
+ ## Checklist Before Writing Any File
37
+
38
+ Before saving any plan, requirement, or summary file, verify:
39
+
40
+ - [ ] Output path follows the table above — NOT the skill's default path
41
+ - [ ] `[ticket-id]` is read from `.aiflow/context/current.json`
42
+ - [ ] Directory `plan/[ticket-id]/` exists or will be created
43
+ - [ ] Announced path in the handoff message matches the actual saved path
44
+
45
+ ---
46
+
47
+ ## Other Conventions
48
+
49
+ - **Review checklist:** `custom/rules/review-checklist.md`
50
+ - **Code style:** `custom/rules/code-style.md`
51
+ - **Naming:** `custom/rules/naming.md`
@@ -1,79 +1,79 @@
1
- # Python AI System Prompt
2
-
3
- You are an expert Python developer. Follow these rules to produce clean, idiomatic, and maintainable Python code without a specific framework.
4
-
5
- ---
6
-
7
- ## Project Structure
8
-
9
- ```
10
- project/
11
- ├── main.py # Entry point
12
- ├── src/
13
- │ ├── service/ # Business logic
14
- │ ├── repository/ # Data access layer
15
- │ ├── model/ # Data classes / domain models
16
- │ └── util/ # Pure helper functions
17
- ├── tests/ # Pytest suite
18
- ├── requirements.txt
19
- └── pyproject.toml
20
- ```
21
-
22
- ---
23
-
24
- ## Python Rules
25
-
26
- - Use **type hints** on all function signatures and return types.
27
- - Follow **PEP 8** and keep functions small and single-purpose.
28
- - Use **dataclasses** or **NamedTuple** for value objects; avoid plain dicts for structured data.
29
- - Prefer **pathlib** over `os.path`; prefer `with` statements for file/resource handling.
30
- - Raise specific exceptions — never `raise Exception("message")`.
31
-
32
- ```python
33
- # ✅ Good
34
- from dataclasses import dataclass
35
-
36
- @dataclass
37
- class UserRequest:
38
- email: str
39
- full_name: str
40
-
41
- def create_user(request: UserRequest) -> UserResponse:
42
- if not request.email:
43
- raise ValueError("email is required")
44
- ...
45
- ```
46
-
47
- ---
48
-
49
- ## Testing Rules
50
-
51
- - Use **Pytest** for all tests.
52
- - Name test functions `test_<what>_<expected_outcome>`.
53
- - Use `pytest.raises` to assert exceptions.
54
-
55
- ```python
56
- def test_create_user_raises_when_email_is_empty():
57
- with pytest.raises(ValueError, match="email is required"):
58
- create_user(UserRequest(email="", full_name="Test"))
59
- ```
60
-
61
- ---
62
-
63
- ## Naming Conventions
64
-
65
- | Element | Convention | Example |
66
- |---------|-----------|---------|
67
- | Module/package | snake_case | `user_service.py` |
68
- | Class | PascalCase | `UserService` |
69
- | Function/variable | snake_case | `find_by_id`, `user_id` |
70
- | Constant | UPPER_SNAKE_CASE | `MAX_RETRY` |
71
-
72
- ---
73
-
74
- ## Common Anti-Patterns to Avoid
75
-
76
- - ❌ Bare `except:` — always catch a specific exception type
77
- - ❌ Mutable default arguments (`def f(x=[])`) — use `None` sentinel instead
78
- - ❌ Global state — pass dependencies explicitly
79
- - ❌ Returning `None` implicitly on error paths — raise or return a typed result
1
+ # Python AI System Prompt
2
+
3
+ You are an expert Python developer. Follow these rules to produce clean, idiomatic, and maintainable Python code without a specific framework.
4
+
5
+ ---
6
+
7
+ ## Project Structure
8
+
9
+ ```
10
+ project/
11
+ ├── main.py # Entry point
12
+ ├── src/
13
+ │ ├── service/ # Business logic
14
+ │ ├── repository/ # Data access layer
15
+ │ ├── model/ # Data classes / domain models
16
+ │ └── util/ # Pure helper functions
17
+ ├── tests/ # Pytest suite
18
+ ├── requirements.txt
19
+ └── pyproject.toml
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Python Rules
25
+
26
+ - Use **type hints** on all function signatures and return types.
27
+ - Follow **PEP 8** and keep functions small and single-purpose.
28
+ - Use **dataclasses** or **NamedTuple** for value objects; avoid plain dicts for structured data.
29
+ - Prefer **pathlib** over `os.path`; prefer `with` statements for file/resource handling.
30
+ - Raise specific exceptions — never `raise Exception("message")`.
31
+
32
+ ```python
33
+ # ✅ Good
34
+ from dataclasses import dataclass
35
+
36
+ @dataclass
37
+ class UserRequest:
38
+ email: str
39
+ full_name: str
40
+
41
+ def create_user(request: UserRequest) -> UserResponse:
42
+ if not request.email:
43
+ raise ValueError("email is required")
44
+ ...
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Testing Rules
50
+
51
+ - Use **Pytest** for all tests.
52
+ - Name test functions `test_<what>_<expected_outcome>`.
53
+ - Use `pytest.raises` to assert exceptions.
54
+
55
+ ```python
56
+ def test_create_user_raises_when_email_is_empty():
57
+ with pytest.raises(ValueError, match="email is required"):
58
+ create_user(UserRequest(email="", full_name="Test"))
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Naming Conventions
64
+
65
+ | Element | Convention | Example |
66
+ |---------|-----------|---------|
67
+ | Module/package | snake_case | `user_service.py` |
68
+ | Class | PascalCase | `UserService` |
69
+ | Function/variable | snake_case | `find_by_id`, `user_id` |
70
+ | Constant | UPPER_SNAKE_CASE | `MAX_RETRY` |
71
+
72
+ ---
73
+
74
+ ## Common Anti-Patterns to Avoid
75
+
76
+ - ❌ Bare `except:` — always catch a specific exception type
77
+ - ❌ Mutable default arguments (`def f(x=[])`) — use `None` sentinel instead
78
+ - ❌ Global state — pass dependencies explicitly
79
+ - ❌ Returning `None` implicitly on error paths — raise or return a typed result
@@ -7,6 +7,7 @@ You have superpowers. When a ticket context exists in `.aiflow/context/current.j
7
7
  - **Consult the "AI Skill Registry"** below to find instructions for each skill (`SKILL.md`).
8
8
  - **AUTO-START Gate 1 immediately** — do NOT wait for the developer to ask.
9
9
  - Read instructions and follow the gate sequence below — NO EXCEPTIONS.
10
+ - **PROJECT CONVENTIONS:** Before writing any output file (plan, requirement, summary), read `custom/rules/project-conventions.md`. These rules override upstream skill defaults.
10
11
 
11
12
  ---
12
13
 
@@ -38,6 +39,7 @@ DO NOT just check format — **understand the content and propose solutions**.
38
39
  **INVOKE:** `generate-spec` skill, then `superpowers:writing-plans`
39
40
 
40
41
  - Create a detailed TDD implementation plan based on the approved requirement.
42
+ - Output `plan/[ticket-id]/plan.md` with the detailed TDD implementation plan.
41
43
  - Display: "GATE 2 PAUSED: type APPROVED to start coding".
42
44
  - CODE WILL NOT BE GENERATED until "APPROVED" is received.
43
45
 
@@ -7,6 +7,24 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
+ ## [0.0.9] - 2026-05-21
11
+
12
+ ### Added
13
+
14
+ - **`figma-desktop` adapter** — New MCP preset using the official `@figma/mcp-server` package (Figma Inc.). Requires Figma Desktop app installed and open. No API token needed — authenticates via Desktop session. Run `ak init -a figma-desktop` to set up. See [`custom/mcp-presets/figma-desktop.json`](../../custom/mcp-presets/figma-desktop.json).
15
+ - **Next.js App Router support in `figma-to-component` skill** — Detects App Router projects (via `nextjs-app-router` in `CLAUDE.md` or presence of `app/` directory). Defaults to Server Component (`export default async function`); adds `'use client'` directive only when the design has interactive states (hover, click handlers, form inputs).
16
+ - **Angular support in `figma-to-component` skill** — Detects Angular projects (via `angular` in `CLAUDE.md` or presence of `angular.json`). Generates standalone `@Component` class with `@Input() className` and computed `hostClasses` getter. Uses Tailwind if `tailwind.config.*` exists, otherwise CSS-in-component.
17
+ - **4-tier framework detection in `figma-to-component` skill** — Detection order: (1) `CLAUDE.md` identifier → (2) project file scan (`app/` dir, `angular.json`, `nuxt.config.*`) → (3) fallback to Next.js/React. Previously only supported React and Vue.
18
+ - **Figma workflow guide** — New developer guide at [`docs/common/workflows/figma.md`](figma.md) covering prerequisites (REST API vs Desktop), how to get node URLs, trigger commands, expected output format, review checklist, and troubleshooting table.
19
+ - **MCP presets documentation** — Added full Figma REST API and Figma Desktop sections to [`custom/mcp-presets/README.md`](../../custom/mcp-presets/README.md), including a side-by-side comparison table.
20
+
21
+ ### Fixed
22
+
23
+ - **Critical: `figma-to-component` skill used non-existent tool names** — The skill referenced `figma_get_file_nodes`, `figma_get_file_styles`, `figma_get_file_components`, and `figma_get_image` — none of which exist in `figma-developer-mcp` or any other MCP package, causing the skill to silently fail on every run. Replaced with the correct tools: `get_figma_data(fileKey, nodeId, depth=2)` and `download_figma_images(fileKey, nodes, localPath)`.
24
+ - **Figma API token verification using wrong auth header** — `verifyFigma()` in `scripts/init.js` was sending `Authorization: Bearer <token>`, which is only valid for OAuth tokens. Figma Personal Access Tokens (`figd_...`) require `X-Figma-Token: <token>`. This caused every PAT to be rejected as invalid during `ak init -a figma` even when the token was correct.
25
+
26
+ ---
27
+
10
28
  ## [0.0.8-beta.1] - 2026-05-14
11
29
 
12
30
  ### Fixed
@@ -0,0 +1,105 @@
1
+ # Figma → Component Workflow
2
+
3
+ Generate UI components directly from Figma designs using the `figma-to-component` skill.
4
+
5
+ ---
6
+
7
+ ## Prerequisites
8
+
9
+ Choose one adapter based on your setup:
10
+
11
+ ### Option A — REST API (recommended, no Desktop required)
12
+
13
+ ```bash
14
+ ak init -a figma
15
+ # Prompts for FIGMA_API_TOKEN
16
+ ```
17
+
18
+ Get your token: [figma.com/settings](https://www.figma.com/settings) → Security → Personal access tokens → Create new token (File content: read).
19
+
20
+ ### Option B — Official Figma MCP (requires Figma Desktop app)
21
+
22
+ ```bash
23
+ ak init -a figma-desktop
24
+ # No token needed — uses Desktop app session
25
+ ```
26
+
27
+ Requirements: Figma Desktop app installed and the target file open before invoking the skill.
28
+
29
+ ---
30
+
31
+ ## Getting a Figma Node URL
32
+
33
+ 1. Open your file in Figma (browser or Desktop)
34
+ 2. Right-click on a frame or component → **Copy link to selection**
35
+ 3. The URL looks like:
36
+ ```
37
+ https://www.figma.com/design/FILEKEY/FileName?node-id=123-456
38
+ ```
39
+ - `FILEKEY` — the file identifier (between `/design/` and the next `/`)
40
+ - `node-id=123-456` — the specific frame or component to generate
41
+
42
+ You can also link to the entire file (no `node-id`) to let the skill list available frames.
43
+
44
+ ---
45
+
46
+ ## Trigger the Skill
47
+
48
+ Paste the Figma URL into Claude Code with a generation request:
49
+
50
+ ```
51
+ Generate component from this Figma frame:
52
+ https://www.figma.com/design/XXXXX/App?node-id=123-456
53
+ ```
54
+
55
+ ```
56
+ Implement the UserCard UI from Figma:
57
+ https://www.figma.com/design/XXXXX/App?node-id=78-910
58
+ ```
59
+
60
+ ```
61
+ Read Figma and generate component following my project conventions:
62
+ https://www.figma.com/design/XXXXX/App?node-id=42-0
63
+ ```
64
+
65
+ ---
66
+
67
+ ## What to Expect
68
+
69
+ The skill outputs in this order:
70
+
71
+ 1. **Design Summary** — layout structure, color palette, typography scale used in the design
72
+ 2. **Component file** — complete code for your detected framework (React, Next.js App Router, Vue 3, or Angular)
73
+ 3. **Usage example** — how to import and use the component
74
+ 4. **Notes** — anything that couldn't be mapped 1:1 from Figma (custom fonts, unsupported effects, etc.)
75
+
76
+ **Framework auto-detection order:**
77
+ 1. Read `CLAUDE.md` for framework identifier (`nextjs-app-router`, `reactjs`, `vue-nuxt`, `angular`)
78
+ 2. Scan project files (`app/` dir → App Router, `angular.json` → Angular, `nuxt.config.*` → Vue)
79
+ 3. Fallback → Next.js / React
80
+
81
+ ---
82
+
83
+ ## Review Checklist
84
+
85
+ After the component is generated, verify:
86
+
87
+ - [ ] Layout matches Figma (flex direction, alignment, gap)
88
+ - [ ] Colors are correct (Tailwind class or arbitrary value)
89
+ - [ ] Typography matches (size, weight, line-height)
90
+ - [ ] Spacing correct (padding, margin, gap)
91
+ - [ ] Interactive states handled (`'use client'` in App Router, variants in React/Vue/Angular)
92
+ - [ ] Images use correct component (`next/image` for Next.js, `<img>` for others)
93
+ - [ ] `className` / `class` prop exposed for external overrides
94
+
95
+ ---
96
+
97
+ ## Troubleshooting
98
+
99
+ | Problem | Fix |
100
+ |---|---|
101
+ | `get_figma_data` tool not found | Run `ak init -a figma` to configure the MCP |
102
+ | "Invalid API token" | Token expired — create a new one at figma.com/settings |
103
+ | Wrong node generated | Check the `node-id` in the URL matches your selection in Figma |
104
+ | Figma Desktop MCP not responding | Ensure Figma Desktop app is open with the file loaded |
105
+ | Component uses wrong framework | Add framework to `CLAUDE.md` (e.g. `nextjs-app-router`) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relipa/ai-flow-kit",
3
- "version": "0.0.8-beta.1",
3
+ "version": "0.0.9-beta.0",
4
4
  "description": "All-in-one AI Flow Kit for team development with Claude AI - skills, templates, and MCP adapters",
5
5
  "author": "Example Team",
6
6
  "publishConfig": {
@@ -1,52 +1,52 @@
1
- #!/usr/bin/env node
2
- /**
3
- * PreToolUse hook for ai-flow-kit
4
- *
5
- * Hard-blocks AI from running git-write operations (commit/add/push/tag) via the
6
- * Bash tool. The developer always controls commits manually in their own terminal.
7
- *
8
- * Stdin payload (from Claude Code):
9
- * { tool_name: "Bash", tool_input: { command: "..." }, ... }
10
- *
11
- * Exit codes:
12
- * 0 — allow (stdout empty)
13
- * 2 — block (stderr message is fed back to Claude as feedback)
14
- *
15
- * Override (for the kit's own maintenance scripts):
16
- * export AIFLOW_ALLOW_GIT_WRITE=1
17
- */
18
-
19
- let raw = '';
20
- process.stdin.setEncoding('utf-8');
21
- process.stdin.on('data', (chunk) => { raw += chunk; });
22
- process.stdin.on('end', () => {
23
- try {
24
- if (process.env.AIFLOW_ALLOW_GIT_WRITE === '1') process.exit(0);
25
-
26
- const payload = raw ? JSON.parse(raw) : {};
27
- if (payload.tool_name !== 'Bash') process.exit(0);
28
-
29
- const command = (payload.tool_input && payload.tool_input.command) || '';
30
- if (!command) process.exit(0);
31
-
32
- // Match `git <subcommand>` where subcommand is a write operation.
33
- // Tolerates leading args (e.g. `git -C path commit`, `git --no-pager add`).
34
- // Captures occurrences inside compound shells: `&&`, `||`, `;`, `|`, `$(...)`, backticks, newline.
35
- const BLOCK_RE = /(^|[\s;&|`(){}])git(\s+-[^\s]+)*\s+(commit|add|push|tag|reset|rebase|revert|cherry-pick|am|merge)\b/i;
36
- const match = command.match(BLOCK_RE);
37
- if (!match) process.exit(0);
38
-
39
- const verb = match[3].toLowerCase();
40
- process.stderr.write(
41
- `[aiflow] BLOCKED: \`git ${verb}\` is not allowed for AI.\n` +
42
- `Reason: ai-flow-kit forbids AI-driven commits/pushes. The developer manages git state manually.\n` +
43
- `If this is a legitimate maintenance task (running the kit's own CI), set AIFLOW_ALLOW_GIT_WRITE=1 in the environment.\n` +
44
- `Otherwise, finish the task without committing; the developer will run \`git ${verb}\` themselves at Gate 5.\n`
45
- );
46
- process.exit(2);
47
- } catch (err) {
48
- // On parsing error, fail open (don't block) but log to stderr for visibility.
49
- process.stderr.write(`[aiflow] block-git-write hook error: ${err.message}\n`);
50
- process.exit(0);
51
- }
52
- });
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PreToolUse hook for ai-flow-kit
4
+ *
5
+ * Hard-blocks AI from running git-write operations (commit/add/push/tag) via the
6
+ * Bash tool. The developer always controls commits manually in their own terminal.
7
+ *
8
+ * Stdin payload (from Claude Code):
9
+ * { tool_name: "Bash", tool_input: { command: "..." }, ... }
10
+ *
11
+ * Exit codes:
12
+ * 0 — allow (stdout empty)
13
+ * 2 — block (stderr message is fed back to Claude as feedback)
14
+ *
15
+ * Override (for the kit's own maintenance scripts):
16
+ * export AIFLOW_ALLOW_GIT_WRITE=1
17
+ */
18
+
19
+ let raw = '';
20
+ process.stdin.setEncoding('utf-8');
21
+ process.stdin.on('data', (chunk) => { raw += chunk; });
22
+ process.stdin.on('end', () => {
23
+ try {
24
+ if (process.env.AIFLOW_ALLOW_GIT_WRITE === '1') process.exit(0);
25
+
26
+ const payload = raw ? JSON.parse(raw) : {};
27
+ if (payload.tool_name !== 'Bash') process.exit(0);
28
+
29
+ const command = (payload.tool_input && payload.tool_input.command) || '';
30
+ if (!command) process.exit(0);
31
+
32
+ // Match `git <subcommand>` where subcommand is a write operation.
33
+ // Tolerates leading args (e.g. `git -C path commit`, `git --no-pager add`).
34
+ // Captures occurrences inside compound shells: `&&`, `||`, `;`, `|`, `$(...)`, backticks, newline.
35
+ const BLOCK_RE = /(^|[\s;&|`(){}])git(\s+-[^\s]+)*\s+(commit|add|push|tag|reset|rebase|revert|cherry-pick|am|merge)\b/i;
36
+ const match = command.match(BLOCK_RE);
37
+ if (!match) process.exit(0);
38
+
39
+ const verb = match[3].toLowerCase();
40
+ process.stderr.write(
41
+ `[aiflow] BLOCKED: \`git ${verb}\` is not allowed for AI.\n` +
42
+ `Reason: ai-flow-kit forbids AI-driven commits/pushes. The developer manages git state manually.\n` +
43
+ `If this is a legitimate maintenance task (running the kit's own CI), set AIFLOW_ALLOW_GIT_WRITE=1 in the environment.\n` +
44
+ `Otherwise, finish the task without committing; the developer will run \`git ${verb}\` themselves at Gate 5.\n`
45
+ );
46
+ process.exit(2);
47
+ } catch (err) {
48
+ // On parsing error, fail open (don't block) but log to stderr for visibility.
49
+ process.stderr.write(`[aiflow] block-git-write hook error: ${err.message}\n`);
50
+ process.exit(0);
51
+ }
52
+ });