@raftlabs/raftstack 1.6.1 → 1.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/.claude/skills/asana/SKILL.md +378 -0
- package/dist/cli.js +101 -31
- package/dist/cli.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: asana
|
|
3
|
+
description: Use when working with Asana - creating tasks, updating tasks, adding comments, posting stories, searching tasks, managing projects, or any Asana MCP tool interaction. Essential for proper formatting and understanding MCP limitations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Asana MCP Guide
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
This skill guides effective use of Asana MCP tools. **Critical limitation:** The Asana MCP server has restricted rich text support compared to the full Asana API.
|
|
11
|
+
|
|
12
|
+
## When to Use This Skill
|
|
13
|
+
|
|
14
|
+
Trigger on ANY Asana-related request:
|
|
15
|
+
- "Create a task in Asana"
|
|
16
|
+
- "Update the Asana task"
|
|
17
|
+
- "Add a comment to the task"
|
|
18
|
+
- "Post an update on Asana"
|
|
19
|
+
- "Leave a note on the Asana task"
|
|
20
|
+
- "Search for tasks in Asana"
|
|
21
|
+
- "Check my Asana tasks"
|
|
22
|
+
- "What's in my Asana project?"
|
|
23
|
+
- Any mention of Asana + task/project/comment/story
|
|
24
|
+
|
|
25
|
+
## ⚠️ Critical: MCP Rich Text Limitations
|
|
26
|
+
|
|
27
|
+
The Asana MCP server does **NOT** fully support rich text formatting:
|
|
28
|
+
|
|
29
|
+
| Tool | Rich Text Field | Plain Text Field | Rich Text Works? |
|
|
30
|
+
|------|-----------------|------------------|------------------|
|
|
31
|
+
| `asana_create_task` | `html_notes` ✅ | `notes` | **YES** |
|
|
32
|
+
| `asana_update_task` | ❌ None | `notes` | **NO** |
|
|
33
|
+
| `asana_create_task_story` | ❌ None | `text` | **NO** |
|
|
34
|
+
|
|
35
|
+
### What This Means
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// ✅ WORKS: Creating a NEW task with rich text
|
|
39
|
+
asana_create_task({
|
|
40
|
+
name: "New feature",
|
|
41
|
+
project_id: "123",
|
|
42
|
+
html_notes: "<body><strong>Bold text</strong> works here!</body>"
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// ❌ DOES NOT WORK: Updating existing task with rich text
|
|
46
|
+
// The MCP only exposes `notes` field (plain text)
|
|
47
|
+
asana_update_task({
|
|
48
|
+
task_id: "456",
|
|
49
|
+
notes: "Plain text only. <strong>Tags</strong> show as literal text."
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// ❌ DOES NOT WORK: Comments with rich text
|
|
53
|
+
// The MCP only exposes `text` field (plain text)
|
|
54
|
+
asana_create_task_story({
|
|
55
|
+
task_id: "456",
|
|
56
|
+
text: "Plain text only. No formatting supported."
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## The Iron Rules
|
|
61
|
+
|
|
62
|
+
### 1. Use `html_notes` ONLY for `asana_create_task`
|
|
63
|
+
|
|
64
|
+
Rich text formatting **only works** when creating new tasks.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// ✅ CORRECT: Use html_notes for new tasks
|
|
68
|
+
asana_create_task({
|
|
69
|
+
name: "Implementation task",
|
|
70
|
+
project_id: "123456",
|
|
71
|
+
html_notes: `<body>
|
|
72
|
+
<h2>Requirements</h2>
|
|
73
|
+
<ul>
|
|
74
|
+
<li>Feature A</li>
|
|
75
|
+
<li>Feature B</li>
|
|
76
|
+
</ul>
|
|
77
|
+
<strong>Owner:</strong> <a data-asana-gid="789012"/>
|
|
78
|
+
</body>`
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 2. Accept Plain Text for Updates and Comments
|
|
83
|
+
|
|
84
|
+
For `asana_update_task` and `asana_create_task_story`, write naturally like you're messaging a colleague:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// ✅ CORRECT: Plain text for updates (only option available)
|
|
88
|
+
asana_update_task({
|
|
89
|
+
task_id: "456",
|
|
90
|
+
notes: `Quick update - finished features A and B. Waiting on John for review, should be good to merge after that.`
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// ✅ CORRECT: Plain text for comments (only option available)
|
|
94
|
+
asana_create_task_story({
|
|
95
|
+
task_id: "456",
|
|
96
|
+
text: `Found the bug! It was in utils/parser.ts line 42 - missing a trim() call. Added .map(s => s.trim()) to fix it.
|
|
97
|
+
|
|
98
|
+
PR is up: https://github.com/org/repo/pull/123`
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 3. Wrap `html_notes` in `<body>` Tags
|
|
103
|
+
|
|
104
|
+
When using `html_notes` (only in `asana_create_task`), always wrap content in `<body>` tags:
|
|
105
|
+
|
|
106
|
+
```xml
|
|
107
|
+
<!-- ❌ BAD: No body wrapper -->
|
|
108
|
+
<strong>Important</strong> task details
|
|
109
|
+
|
|
110
|
+
<!-- ✅ GOOD: Properly wrapped -->
|
|
111
|
+
<body><strong>Important</strong> task details</body>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 4. Use `data-asana-gid` for @-mentions (Task Creation Only)
|
|
115
|
+
|
|
116
|
+
In `html_notes` for new tasks, reference users/tasks using `data-asana-gid`:
|
|
117
|
+
|
|
118
|
+
```xml
|
|
119
|
+
<body>
|
|
120
|
+
Assigned to <a data-asana-gid="USER_GID"/>. Please review.
|
|
121
|
+
See related: <a data-asana-gid="TASK_GID"/>
|
|
122
|
+
</body>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Writing Natural Comments
|
|
126
|
+
|
|
127
|
+
Since most operations only support plain text, write like you're messaging a colleague - natural, simple, conversational. But also format so Asana renders it cleanly.
|
|
128
|
+
|
|
129
|
+
### The Goal
|
|
130
|
+
|
|
131
|
+
Sound like a human, not a report generator. Skip the `===` underlines and formal structure.
|
|
132
|
+
|
|
133
|
+
### Do This
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
Hey, finished the code review!
|
|
137
|
+
|
|
138
|
+
Found a few things:
|
|
139
|
+
• auth.ts line 42 needs error handling around the API call
|
|
140
|
+
• types.ts line 15 has an 'any' that should be a proper interface
|
|
141
|
+
• query.ts could use some caching for performance
|
|
142
|
+
|
|
143
|
+
Otherwise looks good, just minor fixes needed. Let me know if you have questions!
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Not This
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Code Review Complete
|
|
150
|
+
--------------------
|
|
151
|
+
|
|
152
|
+
Found 3 issues:
|
|
153
|
+
|
|
154
|
+
1. Missing error handling in auth.ts:42
|
|
155
|
+
- Add try/catch around API call
|
|
156
|
+
|
|
157
|
+
2. Type safety issue in types.ts:15
|
|
158
|
+
- Change 'any' to proper interface
|
|
159
|
+
|
|
160
|
+
Overall: Good implementation, minor fixes needed.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Natural Writing Tips
|
|
164
|
+
|
|
165
|
+
- **Write conversationally** - "Hey, quick update" or "Found the issue!" is fine
|
|
166
|
+
- **Keep it brief** - say what matters, then stop
|
|
167
|
+
- **Links work fine** - just paste URLs directly: https://github.com/org/repo/pull/123
|
|
168
|
+
- **Don't over-structure** - skip the headers and category labels
|
|
169
|
+
|
|
170
|
+
### Asana Plain Text Formatting
|
|
171
|
+
|
|
172
|
+
Asana's plain text renderer has quirks. Use these patterns for clean rendering:
|
|
173
|
+
|
|
174
|
+
| Use | Not | Why |
|
|
175
|
+
|-----|-----|-----|
|
|
176
|
+
| `•` (bullet character) | `-` (dash) | Bullets render as proper list items |
|
|
177
|
+
| Flat lists | Nested/indented lists | Indentation doesn't preserve well |
|
|
178
|
+
| One blank line between sections | Multiple blank lines | Keeps spacing consistent |
|
|
179
|
+
| Inline items (no indent) | Indented sub-items | Sub-indentation gets flattened |
|
|
180
|
+
|
|
181
|
+
**Good list format:**
|
|
182
|
+
```
|
|
183
|
+
Found a few things:
|
|
184
|
+
• First item here
|
|
185
|
+
• Second item here
|
|
186
|
+
• Third item here
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Bad list format (indentation lost):**
|
|
190
|
+
```
|
|
191
|
+
Key Features:
|
|
192
|
+
|
|
193
|
+
- First item
|
|
194
|
+
- Sub-item (will flatten)
|
|
195
|
+
- Second item
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Anti-Patterns to Avoid
|
|
199
|
+
|
|
200
|
+
| Don't Do This | Why |
|
|
201
|
+
|---------------|-----|
|
|
202
|
+
| `===` or `---` underlines | ASCII art formatting looks robotic |
|
|
203
|
+
| ALL CAPS FOR EMPHASIS | Comes across as shouting |
|
|
204
|
+
| Category headers everywhere | "Completed:", "Blocked:", "Next:" feels like a form |
|
|
205
|
+
| Nested indentation | Asana flattens it, looks broken |
|
|
206
|
+
| Report-style formatting | You're messaging a colleague, not filing a TPS report |
|
|
207
|
+
|
|
208
|
+
## HTML Tags Reference (for `html_notes` only)
|
|
209
|
+
|
|
210
|
+
| Markdown | Asana XML |
|
|
211
|
+
|----------|-----------|
|
|
212
|
+
| `**bold**` | `<strong>bold</strong>` |
|
|
213
|
+
| `*italic*` | `<em>italic</em>` |
|
|
214
|
+
| `~~strike~~` | `<s>strike</s>` |
|
|
215
|
+
| `__underline__` | `<u>underline</u>` |
|
|
216
|
+
| `` `code` `` | `<code>code</code>` |
|
|
217
|
+
| `- item` | `<ul><li>item</li></ul>` |
|
|
218
|
+
| `1. item` | `<ol><li>item</li></ol>` |
|
|
219
|
+
| `> quote` | `<blockquote>quote</blockquote>` |
|
|
220
|
+
| ` ```block``` ` | `<pre>block</pre>` |
|
|
221
|
+
| `# H1` | `<h1>H1</h1>` |
|
|
222
|
+
| `## H2` | `<h2>H2</h2>` |
|
|
223
|
+
| `[text](url)` | `<a href="url">text</a>` |
|
|
224
|
+
| `@mention` | `<a data-asana-gid="GID"/>` |
|
|
225
|
+
|
|
226
|
+
## Complete Examples
|
|
227
|
+
|
|
228
|
+
### Example 1: Create Task with Rich Formatting
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
asana_create_task({
|
|
232
|
+
name: "Implement user authentication",
|
|
233
|
+
project_id: "111222333",
|
|
234
|
+
html_notes: `<body>
|
|
235
|
+
<h1>User Authentication Feature</h1>
|
|
236
|
+
|
|
237
|
+
<h2>Requirements</h2>
|
|
238
|
+
<ul>
|
|
239
|
+
<li>OAuth 2.0 with Google</li>
|
|
240
|
+
<li>Session management</li>
|
|
241
|
+
<li>Password reset flow</li>
|
|
242
|
+
</ul>
|
|
243
|
+
|
|
244
|
+
<h2>Technical Notes</h2>
|
|
245
|
+
<blockquote>Must comply with security policy SEC-2024-001</blockquote>
|
|
246
|
+
|
|
247
|
+
<strong>Owner:</strong> <a data-asana-gid="12345678901234"/>
|
|
248
|
+
</body>`,
|
|
249
|
+
due_on: "2024-03-15"
|
|
250
|
+
})
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Example 2: Update Task (Plain Text Only)
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
asana_update_task({
|
|
257
|
+
task_id: "1234567890",
|
|
258
|
+
notes: `Quick update on this - API endpoints are done and tests are passing (95% coverage). Documentation draft is ready too.
|
|
259
|
+
|
|
260
|
+
Currently working on integration testing and addressing review feedback.
|
|
261
|
+
|
|
262
|
+
One blocker: still waiting on design approval for the UI changes. Pinged Sarah about it yesterday.
|
|
263
|
+
|
|
264
|
+
Once that's sorted, just need to finish integration tests and we can deploy to staging.`
|
|
265
|
+
})
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Example 3: Add Comment (Plain Text Only)
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
asana_create_task_story({
|
|
272
|
+
task_id: "1234567890",
|
|
273
|
+
text: `Hey, finished the code review!
|
|
274
|
+
|
|
275
|
+
Found a few things:
|
|
276
|
+
• auth.ts line 42 needs error handling around the API call
|
|
277
|
+
• types.ts line 15 has an 'any' that should be a proper interface
|
|
278
|
+
• query.ts line 88 could use some caching for performance
|
|
279
|
+
|
|
280
|
+
Otherwise looks good - solid implementation, just minor fixes. Let me know if you have questions!
|
|
281
|
+
|
|
282
|
+
PR: https://github.com/org/repo/pull/456`
|
|
283
|
+
})
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Available Asana MCP Tools
|
|
287
|
+
|
|
288
|
+
### Task Operations
|
|
289
|
+
| Tool | Purpose | Key Parameters |
|
|
290
|
+
|------|---------|----------------|
|
|
291
|
+
| `asana_create_task` | Create new task | `name`, `project_id`, `html_notes`, `assignee`, `due_on` |
|
|
292
|
+
| `asana_update_task` | Update existing task | `task_id`, `notes` (plain text), `completed`, `assignee` |
|
|
293
|
+
| `asana_get_task` | Get task details | `task_id`, `opt_fields` |
|
|
294
|
+
| `asana_delete_task` | Delete a task | `task_id` |
|
|
295
|
+
| `asana_search_tasks` | Search tasks | `workspace`, `text`, `assignee_any`, `completed` |
|
|
296
|
+
|
|
297
|
+
### Comments/Stories
|
|
298
|
+
| Tool | Purpose | Key Parameters |
|
|
299
|
+
|------|---------|----------------|
|
|
300
|
+
| `asana_create_task_story` | Add comment | `task_id`, `text` (plain text only) |
|
|
301
|
+
| `asana_get_stories_for_task` | Get task comments | `task_id` |
|
|
302
|
+
|
|
303
|
+
### Project Operations
|
|
304
|
+
| Tool | Purpose | Key Parameters |
|
|
305
|
+
|------|---------|----------------|
|
|
306
|
+
| `asana_get_project` | Get project details | `project_id` |
|
|
307
|
+
| `asana_get_projects` | List projects | `workspace` |
|
|
308
|
+
| `asana_get_project_sections` | Get sections | `project_id` |
|
|
309
|
+
| `asana_create_project` | Create project | `name`, `workspace`, `team` |
|
|
310
|
+
|
|
311
|
+
### Search & Discovery
|
|
312
|
+
| Tool | Purpose | Key Parameters |
|
|
313
|
+
|------|---------|----------------|
|
|
314
|
+
| `asana_typeahead_search` | Quick search | `workspace_gid`, `resource_type`, `query` |
|
|
315
|
+
| `asana_list_workspaces` | Get workspaces | (none required) |
|
|
316
|
+
| `asana_get_user` | Get user info | `user_id` (default: "me") |
|
|
317
|
+
|
|
318
|
+
## Common Workflows
|
|
319
|
+
|
|
320
|
+
### Find and Update a Task
|
|
321
|
+
```typescript
|
|
322
|
+
// 1. Search for the task
|
|
323
|
+
asana_typeahead_search({
|
|
324
|
+
workspace_gid: "WORKSPACE_ID",
|
|
325
|
+
resource_type: "task",
|
|
326
|
+
query: "authentication feature"
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
// 2. Get task details
|
|
330
|
+
asana_get_task({
|
|
331
|
+
task_id: "FOUND_TASK_ID",
|
|
332
|
+
opt_fields: "name,notes,assignee,due_on,completed"
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
// 3. Update the task (plain text only)
|
|
336
|
+
asana_update_task({
|
|
337
|
+
task_id: "FOUND_TASK_ID",
|
|
338
|
+
notes: "Updated description here (plain text)"
|
|
339
|
+
})
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Add Status Update Comment
|
|
343
|
+
```typescript
|
|
344
|
+
// Get current user first (for context)
|
|
345
|
+
asana_get_user({}) // Returns current user info
|
|
346
|
+
|
|
347
|
+
// Add comment to task
|
|
348
|
+
asana_create_task_story({
|
|
349
|
+
task_id: "TASK_ID",
|
|
350
|
+
text: `Making good progress here! Got the core functionality working, moving on to testing and docs now. No blockers.`
|
|
351
|
+
})
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Common Mistakes
|
|
355
|
+
|
|
356
|
+
| Mistake | Impact | Fix |
|
|
357
|
+
|---------|--------|-----|
|
|
358
|
+
| Using `html_notes` in `asana_update_task` | Parameter doesn't exist, ignored | Use `notes` with plain text |
|
|
359
|
+
| Using `html_text` in `asana_create_task_story` | Parameter doesn't exist, ignored | Use `text` with plain text |
|
|
360
|
+
| Expecting markdown to render | Shows as literal `**text**` | Write naturally in plain text |
|
|
361
|
+
| Missing `<body>` tags in `html_notes` | May fail or render incorrectly | Always wrap in `<body>` tags |
|
|
362
|
+
| Not closing XML tags | Invalid XML error | Close all tags: `<li></li>` |
|
|
363
|
+
|
|
364
|
+
## Red Flags - STOP and Check
|
|
365
|
+
|
|
366
|
+
| Thought | Reality |
|
|
367
|
+
|---------|---------|
|
|
368
|
+
| "I'll use html_notes to update the task" | `asana_update_task` doesn't have `html_notes`. Use `notes`. |
|
|
369
|
+
| "I'll format the comment with HTML" | `asana_create_task_story` only has `text`. Plain text only. |
|
|
370
|
+
| "Markdown will render in Asana" | No. Write naturally in plain text, or use `html_notes` for new tasks only. |
|
|
371
|
+
| "I need the workspace ID" | Call `asana_list_workspaces` first to get it. |
|
|
372
|
+
| "I'll @mention with @username" | Use `<a data-asana-gid="GID"/>` in `html_notes` only. |
|
|
373
|
+
|
|
374
|
+
## References
|
|
375
|
+
|
|
376
|
+
- [Asana Rich Text Documentation](https://developers.asana.com/docs/rich-text) - Full API rich text (note: MCP has limited support)
|
|
377
|
+
- Asana MCP exposes subset of Asana API functionality
|
|
378
|
+
- Rich text via `html_notes` only available in `asana_create_task`
|
package/dist/cli.js
CHANGED
|
@@ -772,7 +772,7 @@ function getLintStagedConfig(usesEslint, usesPrettier, usesTypeScript) {
|
|
|
772
772
|
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
773
773
|
import { existsSync as existsSync4 } from "fs";
|
|
774
774
|
import { join as join7 } from "path";
|
|
775
|
-
import
|
|
775
|
+
import spawn from "cross-spawn";
|
|
776
776
|
async function readPackageJson(targetDir = process.cwd()) {
|
|
777
777
|
const pkgPath = join7(targetDir, "package.json");
|
|
778
778
|
if (!existsSync4(pkgPath)) {
|
|
@@ -829,26 +829,45 @@ var REACT_ESLINT_PACKAGES = [
|
|
|
829
829
|
"eslint-plugin-react",
|
|
830
830
|
"eslint-plugin-react-hooks"
|
|
831
831
|
];
|
|
832
|
+
function isPnpmWorkspace(targetDir) {
|
|
833
|
+
return existsSync4(join7(targetDir, "pnpm-workspace.yaml"));
|
|
834
|
+
}
|
|
832
835
|
async function installPackages(pm, packages, targetDir) {
|
|
833
836
|
if (packages.length === 0) {
|
|
834
837
|
return { success: true };
|
|
835
838
|
}
|
|
836
|
-
|
|
837
|
-
const addDevArgs = pm.addDev.split(" ");
|
|
839
|
+
return new Promise((resolve) => {
|
|
838
840
|
const pmCommand = pm.name === "npm" ? "npm" : pm.name.replace("-berry", "");
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
+
const parts = [pmCommand, ...pm.addDev.split(" ")];
|
|
842
|
+
if (pm.name === "pnpm" && isPnpmWorkspace(targetDir)) {
|
|
843
|
+
parts.push("-w");
|
|
844
|
+
}
|
|
845
|
+
parts.push(...packages);
|
|
846
|
+
const fullCommand = parts.join(" ");
|
|
847
|
+
const child = spawn(fullCommand, [], {
|
|
841
848
|
cwd: targetDir,
|
|
842
|
-
stdio: "inherit"
|
|
849
|
+
stdio: "inherit",
|
|
843
850
|
// Show install progress to user
|
|
851
|
+
shell: true
|
|
852
|
+
// Key: use shell to resolve PATH in sandboxed envs
|
|
844
853
|
});
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
854
|
+
child.on("close", (code) => {
|
|
855
|
+
if (code === 0) {
|
|
856
|
+
resolve({ success: true });
|
|
857
|
+
} else {
|
|
858
|
+
resolve({
|
|
859
|
+
success: false,
|
|
860
|
+
error: `Installation failed with exit code ${code}`
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
child.on("error", (err) => {
|
|
865
|
+
resolve({
|
|
866
|
+
success: false,
|
|
867
|
+
error: err.message
|
|
868
|
+
});
|
|
869
|
+
});
|
|
870
|
+
});
|
|
852
871
|
}
|
|
853
872
|
|
|
854
873
|
// src/generators/branch-validation.ts
|
|
@@ -1579,15 +1598,18 @@ function getPackageSkillsDir() {
|
|
|
1579
1598
|
const packageRoot = join15(dirname2(currentFilePath), "..");
|
|
1580
1599
|
return join15(packageRoot, ".claude", "skills");
|
|
1581
1600
|
}
|
|
1582
|
-
async function copyDirectory(srcDir, destDir, result, baseDir) {
|
|
1601
|
+
async function copyDirectory(srcDir, destDir, result, baseDir, skipDirs) {
|
|
1583
1602
|
await ensureDir(destDir);
|
|
1584
1603
|
const entries = await readdir(srcDir, { withFileTypes: true });
|
|
1585
1604
|
for (const entry of entries) {
|
|
1605
|
+
if (skipDirs && entry.isDirectory() && skipDirs.includes(entry.name)) {
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1586
1608
|
const srcPath = join15(srcDir, entry.name);
|
|
1587
1609
|
const destPath = join15(destDir, entry.name);
|
|
1588
1610
|
const relativePath = destPath.replace(baseDir + "/", "");
|
|
1589
1611
|
if (entry.isDirectory()) {
|
|
1590
|
-
await copyDirectory(srcPath, destPath, result, baseDir);
|
|
1612
|
+
await copyDirectory(srcPath, destPath, result, baseDir, skipDirs);
|
|
1591
1613
|
} else {
|
|
1592
1614
|
if (existsSync5(destPath)) {
|
|
1593
1615
|
const backupPath = await backupFile(destPath);
|
|
@@ -1600,7 +1622,7 @@ async function copyDirectory(srcDir, destDir, result, baseDir) {
|
|
|
1600
1622
|
}
|
|
1601
1623
|
}
|
|
1602
1624
|
}
|
|
1603
|
-
async function generateClaudeSkills(targetDir) {
|
|
1625
|
+
async function generateClaudeSkills(targetDir, options) {
|
|
1604
1626
|
const result = {
|
|
1605
1627
|
created: [],
|
|
1606
1628
|
modified: [],
|
|
@@ -1616,7 +1638,11 @@ async function generateClaudeSkills(targetDir) {
|
|
|
1616
1638
|
return result;
|
|
1617
1639
|
}
|
|
1618
1640
|
await ensureDir(join15(targetDir, ".claude"));
|
|
1619
|
-
|
|
1641
|
+
const skipDirs = [];
|
|
1642
|
+
if (!options?.includeAsana) {
|
|
1643
|
+
skipDirs.push("asana");
|
|
1644
|
+
}
|
|
1645
|
+
await copyDirectory(packageSkillsDir, targetSkillsDir, result, targetDir, skipDirs);
|
|
1620
1646
|
return result;
|
|
1621
1647
|
}
|
|
1622
1648
|
|
|
@@ -1694,6 +1720,16 @@ export default tseslint.config(
|
|
|
1694
1720
|
},
|
|
1695
1721
|
},
|
|
1696
1722
|
},
|
|
1723
|
+
{
|
|
1724
|
+
// CommonJS config files (cz.config.js, commitlint.config.js, etc.)
|
|
1725
|
+
files: ["*.config.js", "*.config.cjs"],
|
|
1726
|
+
languageOptions: {
|
|
1727
|
+
globals: {
|
|
1728
|
+
...globals.node,
|
|
1729
|
+
},
|
|
1730
|
+
sourceType: "commonjs",
|
|
1731
|
+
},
|
|
1732
|
+
},
|
|
1697
1733
|
{
|
|
1698
1734
|
ignores: ["node_modules/", "dist/", "build/", ".next/", "coverage/", ".turbo/"],
|
|
1699
1735
|
}
|
|
@@ -1732,6 +1768,16 @@ export default tseslint.config(
|
|
|
1732
1768
|
"no-console": ["warn", { allow: ["warn", "error"] }],
|
|
1733
1769
|
},
|
|
1734
1770
|
},
|
|
1771
|
+
{
|
|
1772
|
+
// CommonJS config files (cz.config.js, commitlint.config.js, etc.)
|
|
1773
|
+
files: ["*.config.js", "*.config.cjs"],
|
|
1774
|
+
languageOptions: {
|
|
1775
|
+
globals: {
|
|
1776
|
+
...globals.node,
|
|
1777
|
+
},
|
|
1778
|
+
sourceType: "commonjs",
|
|
1779
|
+
},
|
|
1780
|
+
},
|
|
1735
1781
|
{
|
|
1736
1782
|
ignores: ["node_modules/", "dist/", "build/", "coverage/", ".turbo/"],
|
|
1737
1783
|
}
|
|
@@ -1785,6 +1831,16 @@ export default [
|
|
|
1785
1831
|
},
|
|
1786
1832
|
},
|
|
1787
1833
|
},
|
|
1834
|
+
{
|
|
1835
|
+
// CommonJS config files (cz.config.js, commitlint.config.js, etc.)
|
|
1836
|
+
files: ["*.config.js", "*.config.cjs"],
|
|
1837
|
+
languageOptions: {
|
|
1838
|
+
globals: {
|
|
1839
|
+
...globals.node,
|
|
1840
|
+
},
|
|
1841
|
+
sourceType: "commonjs",
|
|
1842
|
+
},
|
|
1843
|
+
},
|
|
1788
1844
|
{
|
|
1789
1845
|
ignores: ["node_modules/", "dist/", "build/", ".next/", "coverage/", ".turbo/"],
|
|
1790
1846
|
},
|
|
@@ -1813,6 +1869,16 @@ export default [
|
|
|
1813
1869
|
"no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
|
|
1814
1870
|
},
|
|
1815
1871
|
},
|
|
1872
|
+
{
|
|
1873
|
+
// CommonJS config files (cz.config.js, commitlint.config.js, etc.)
|
|
1874
|
+
files: ["*.config.js", "*.config.cjs"],
|
|
1875
|
+
languageOptions: {
|
|
1876
|
+
globals: {
|
|
1877
|
+
...globals.node,
|
|
1878
|
+
},
|
|
1879
|
+
sourceType: "commonjs",
|
|
1880
|
+
},
|
|
1881
|
+
},
|
|
1816
1882
|
{
|
|
1817
1883
|
ignores: ["node_modules/", "dist/", "build/", "coverage/", ".turbo/"],
|
|
1818
1884
|
},
|
|
@@ -1987,7 +2053,7 @@ ${pm.run} test
|
|
|
1987
2053
|
}
|
|
1988
2054
|
|
|
1989
2055
|
// src/utils/git.ts
|
|
1990
|
-
import { execa
|
|
2056
|
+
import { execa } from "execa";
|
|
1991
2057
|
import { existsSync as existsSync7 } from "fs";
|
|
1992
2058
|
import { join as join18 } from "path";
|
|
1993
2059
|
async function isGitRepo(targetDir = process.cwd()) {
|
|
@@ -1995,7 +2061,7 @@ async function isGitRepo(targetDir = process.cwd()) {
|
|
|
1995
2061
|
return true;
|
|
1996
2062
|
}
|
|
1997
2063
|
try {
|
|
1998
|
-
await
|
|
2064
|
+
await execa("git", ["rev-parse", "--git-dir"], { cwd: targetDir });
|
|
1999
2065
|
return true;
|
|
2000
2066
|
} catch {
|
|
2001
2067
|
return false;
|
|
@@ -2003,7 +2069,7 @@ async function isGitRepo(targetDir = process.cwd()) {
|
|
|
2003
2069
|
}
|
|
2004
2070
|
async function isGhCliAvailable() {
|
|
2005
2071
|
try {
|
|
2006
|
-
await
|
|
2072
|
+
await execa("gh", ["auth", "status"]);
|
|
2007
2073
|
return true;
|
|
2008
2074
|
} catch {
|
|
2009
2075
|
return false;
|
|
@@ -2011,7 +2077,7 @@ async function isGhCliAvailable() {
|
|
|
2011
2077
|
}
|
|
2012
2078
|
async function getGitHubRepoInfo(targetDir = process.cwd()) {
|
|
2013
2079
|
try {
|
|
2014
|
-
const { stdout } = await
|
|
2080
|
+
const { stdout } = await execa("gh", ["repo", "view", "--json", "owner,name"], {
|
|
2015
2081
|
cwd: targetDir
|
|
2016
2082
|
});
|
|
2017
2083
|
const data = JSON.parse(stdout);
|
|
@@ -2142,7 +2208,9 @@ async function runInit(targetDir = process.cwd()) {
|
|
|
2142
2208
|
await generateContributing(targetDir, !!config.asanaBaseUrl, config.packageManager)
|
|
2143
2209
|
);
|
|
2144
2210
|
results.push(await generateQuickReference(targetDir, config.packageManager));
|
|
2145
|
-
results.push(await generateClaudeSkills(targetDir
|
|
2211
|
+
results.push(await generateClaudeSkills(targetDir, {
|
|
2212
|
+
includeAsana: !!config.asanaBaseUrl
|
|
2213
|
+
}));
|
|
2146
2214
|
results.push(await updateProjectPackageJson(targetDir, config));
|
|
2147
2215
|
spinner4.stop("Configuration files generated!");
|
|
2148
2216
|
} catch (error) {
|
|
@@ -2201,7 +2269,7 @@ async function runInit(targetDir = process.cwd()) {
|
|
|
2201
2269
|
// src/commands/setup-protection.ts
|
|
2202
2270
|
import * as p3 from "@clack/prompts";
|
|
2203
2271
|
import pc3 from "picocolors";
|
|
2204
|
-
import { execa as
|
|
2272
|
+
import { execa as execa2 } from "execa";
|
|
2205
2273
|
function getDefaultSettings(branch) {
|
|
2206
2274
|
return {
|
|
2207
2275
|
branch,
|
|
@@ -2272,7 +2340,7 @@ async function applyBranchProtection(owner, repo, settings) {
|
|
|
2272
2340
|
args.push("-F", "required_status_checks=null");
|
|
2273
2341
|
}
|
|
2274
2342
|
args.push("-F", "restrictions=null");
|
|
2275
|
-
await
|
|
2343
|
+
await execa2("gh", args);
|
|
2276
2344
|
}
|
|
2277
2345
|
async function applyMergeStrategy(owner, repo, settings) {
|
|
2278
2346
|
const args = [
|
|
@@ -2289,7 +2357,7 @@ async function applyMergeStrategy(owner, repo, settings) {
|
|
|
2289
2357
|
"-f",
|
|
2290
2358
|
`delete_branch_on_merge=${settings.deleteBranchOnMerge}`
|
|
2291
2359
|
];
|
|
2292
|
-
await
|
|
2360
|
+
await execa2("gh", args);
|
|
2293
2361
|
}
|
|
2294
2362
|
async function runSetupProtection(targetDir = process.cwd()) {
|
|
2295
2363
|
console.log();
|
|
@@ -2466,12 +2534,12 @@ async function runSetupProtection(targetDir = process.cwd()) {
|
|
|
2466
2534
|
}
|
|
2467
2535
|
|
|
2468
2536
|
// src/commands/metrics.ts
|
|
2469
|
-
import { execa as
|
|
2537
|
+
import { execa as execa3 } from "execa";
|
|
2470
2538
|
import * as p4 from "@clack/prompts";
|
|
2471
2539
|
import pc4 from "picocolors";
|
|
2472
2540
|
async function getRecentCommits(targetDir, days) {
|
|
2473
2541
|
try {
|
|
2474
|
-
const { stdout } = await
|
|
2542
|
+
const { stdout } = await execa3(
|
|
2475
2543
|
"git",
|
|
2476
2544
|
["log", `--since=${days} days ago`, "--oneline", "--no-merges"],
|
|
2477
2545
|
{ cwd: targetDir }
|
|
@@ -2483,7 +2551,7 @@ async function getRecentCommits(targetDir, days) {
|
|
|
2483
2551
|
}
|
|
2484
2552
|
async function getCommitMessages(targetDir, days) {
|
|
2485
2553
|
try {
|
|
2486
|
-
const { stdout } = await
|
|
2554
|
+
const { stdout } = await execa3(
|
|
2487
2555
|
"git",
|
|
2488
2556
|
[
|
|
2489
2557
|
"log",
|
|
@@ -2500,7 +2568,7 @@ async function getCommitMessages(targetDir, days) {
|
|
|
2500
2568
|
}
|
|
2501
2569
|
async function getBranchNames(targetDir) {
|
|
2502
2570
|
try {
|
|
2503
|
-
const { stdout } = await
|
|
2571
|
+
const { stdout } = await execa3(
|
|
2504
2572
|
"git",
|
|
2505
2573
|
["branch", "-a", "--format=%(refname:short)"],
|
|
2506
2574
|
{ cwd: targetDir }
|
|
@@ -2603,7 +2671,7 @@ ${pc4.bold("Branches")}
|
|
|
2603
2671
|
// package.json
|
|
2604
2672
|
var package_default = {
|
|
2605
2673
|
name: "@raftlabs/raftstack",
|
|
2606
|
-
version: "1.
|
|
2674
|
+
version: "1.7.0",
|
|
2607
2675
|
description: "CLI tool for setting up Git hooks, commit conventions, and GitHub integration",
|
|
2608
2676
|
type: "module",
|
|
2609
2677
|
main: "./dist/index.js",
|
|
@@ -2666,6 +2734,7 @@ var package_default = {
|
|
|
2666
2734
|
node: ">=18"
|
|
2667
2735
|
},
|
|
2668
2736
|
devDependencies: {
|
|
2737
|
+
"@types/cross-spawn": "^6.0.6",
|
|
2669
2738
|
"@types/node": "^20.10.0",
|
|
2670
2739
|
"standard-version": "^9.5.0",
|
|
2671
2740
|
tsup: "^8.0.0",
|
|
@@ -2675,7 +2744,8 @@ var package_default = {
|
|
|
2675
2744
|
dependencies: {
|
|
2676
2745
|
"@clack/prompts": "^0.7.0",
|
|
2677
2746
|
commander: "^12.0.0",
|
|
2678
|
-
|
|
2747
|
+
"cross-spawn": "^7.0.6",
|
|
2748
|
+
execa: "^9.6.1",
|
|
2679
2749
|
picocolors: "^1.0.0"
|
|
2680
2750
|
}
|
|
2681
2751
|
};
|