@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.
@@ -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 { execa } from "execa";
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
- try {
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 args = [...addDevArgs, ...packages];
840
- await execa(pmCommand, args, {
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
- return { success: true };
846
- } catch (error) {
847
- return {
848
- success: false,
849
- error: error instanceof Error ? error.message : "Unknown error"
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
- await copyDirectory(packageSkillsDir, targetSkillsDir, result, targetDir);
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 as execa2 } from "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 execa2("git", ["rev-parse", "--git-dir"], { cwd: targetDir });
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 execa2("gh", ["auth", "status"]);
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 execa2("gh", ["repo", "view", "--json", "owner,name"], {
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 execa3 } from "execa";
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 execa3("gh", args);
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 execa3("gh", args);
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 execa4 } from "execa";
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 execa4(
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 execa4(
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 execa4(
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.6.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
- execa: "^8.0.0",
2747
+ "cross-spawn": "^7.0.6",
2748
+ execa: "^9.6.1",
2679
2749
  picocolors: "^1.0.0"
2680
2750
  }
2681
2751
  };