@plaited/development-skills 0.4.1 → 0.6.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 → .plaited}/rules/accuracy.md +3 -10
- package/{.claude → .plaited}/rules/code-review.md +2 -41
- package/.plaited/rules/git-workflow.md +36 -0
- package/.plaited/rules/module-organization.md +92 -0
- package/{.claude → .plaited}/rules/testing.md +81 -1
- package/package.json +3 -2
- package/src/lsp-analyze.ts +2 -2
- package/src/lsp-find.ts +15 -34
- package/src/lsp-hover.ts +2 -2
- package/src/lsp-references.ts +2 -2
- package/src/lsp-symbols.ts +2 -2
- package/src/resolve-file-path.ts +18 -28
- package/src/scaffold-rules.ts +148 -204
- package/src/tests/resolve-file-path.spec.ts +90 -51
- package/src/tests/scaffold-rules.spec.ts +148 -118
- package/.claude/commands/lsp-analyze.md +0 -66
- package/.claude/commands/lsp-find.md +0 -51
- package/.claude/commands/lsp-hover.md +0 -48
- package/.claude/commands/lsp-refs.md +0 -55
- package/.claude/commands/scaffold-rules.md +0 -221
- package/.claude/commands/validate-skill.md +0 -29
- package/.claude/rules/git-workflow.md +0 -66
- package/.claude/skills/code-documentation/SKILL.md +0 -47
- package/.claude/skills/code-documentation/references/internal-templates.md +0 -113
- package/.claude/skills/code-documentation/references/maintenance.md +0 -164
- package/.claude/skills/code-documentation/references/public-api-templates.md +0 -100
- package/.claude/skills/code-documentation/references/type-documentation.md +0 -116
- package/.claude/skills/code-documentation/references/workflow.md +0 -60
- package/.claude/skills/scaffold-rules/SKILL.md +0 -97
- package/.claude/skills/typescript-lsp/SKILL.md +0 -239
- package/.claude/skills/validate-skill/SKILL.md +0 -105
- /package/{.claude → .plaited}/rules/bun-apis.md +0 -0
- /package/{.claude → .plaited}/rules/github.md +0 -0
package/src/scaffold-rules.ts
CHANGED
|
@@ -2,40 +2,42 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Scaffold development rules from templates
|
|
4
4
|
*
|
|
5
|
-
* Reads bundled rule templates, processes template variables
|
|
5
|
+
* Reads bundled rule templates, processes template variables,
|
|
6
6
|
* and outputs JSON for agent consumption.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
* (works with Cursor, Factory, Copilot, Windsurf, Cline, Aider, and 60,000+ others)
|
|
8
|
+
* All agents use `.plaited/rules/` as the unified default location.
|
|
9
|
+
* The output includes marker-wrapped sections for both CLAUDE.md and AGENTS.md,
|
|
10
|
+
* allowing programmatic updates without destroying user content.
|
|
12
11
|
*
|
|
13
12
|
* Options:
|
|
14
|
-
* - --
|
|
15
|
-
* - --rules-dir, -d: Custom rules directory path (overrides default)
|
|
16
|
-
* - --agents-md-path, -m: Custom AGENTS.md file path (default: AGENTS.md)
|
|
13
|
+
* - --rules-dir, -d: Custom rules directory path (overrides default .plaited/rules)
|
|
17
14
|
* - --rules, -r: Filter to specific rules (can be used multiple times)
|
|
15
|
+
* - --list, -l: List available rules without full output
|
|
16
|
+
*
|
|
17
|
+
* Output includes:
|
|
18
|
+
* - claudeMdSection: Marker-wrapped section with @ syntax for CLAUDE.md
|
|
19
|
+
* - agentsMdSection: Marker-wrapped section with markdown links for AGENTS.md
|
|
20
|
+
* - templates: Processed rule content for each selected rule
|
|
18
21
|
*
|
|
19
22
|
* Template syntax:
|
|
20
23
|
* - {{LINK:rule-id}} - Cross-reference to another rule
|
|
21
|
-
* - {{#if development-skills}}...{{/if}} - Conditional block
|
|
22
|
-
* - {{
|
|
23
|
-
* - {{^if condition}}...{{/if}} - Inverse conditional
|
|
24
|
+
* - {{#if development-skills}}...{{/if}} - Conditional block (always true when using CLI)
|
|
25
|
+
* - {{^if development-skills}}...{{/if}} - Inverse conditional
|
|
24
26
|
* - <!-- RULE TEMPLATE ... --> - Template header (removed)
|
|
25
27
|
*
|
|
26
|
-
* Capabilities:
|
|
27
|
-
* - has-sandbox: Agent runs in sandboxed environment (Claude Code only)
|
|
28
|
-
* - supports-slash-commands: Agent has /command syntax (Claude Code only)
|
|
29
|
-
*
|
|
30
28
|
* @example
|
|
31
29
|
* ```bash
|
|
32
|
-
* # Default
|
|
33
|
-
* bunx @plaited/development-skills scaffold-rules
|
|
34
|
-
*
|
|
30
|
+
* # Default: outputs to .plaited/rules/
|
|
31
|
+
* bunx @plaited/development-skills scaffold-rules
|
|
32
|
+
*
|
|
33
|
+
* # Custom rules directory
|
|
34
|
+
* bunx @plaited/development-skills scaffold-rules --rules-dir=.cursor/rules
|
|
35
35
|
*
|
|
36
|
-
* #
|
|
37
|
-
* bunx @plaited/development-skills scaffold-rules --
|
|
38
|
-
*
|
|
36
|
+
* # Filter specific rules
|
|
37
|
+
* bunx @plaited/development-skills scaffold-rules --rules testing --rules bun-apis
|
|
38
|
+
*
|
|
39
|
+
* # List available rules
|
|
40
|
+
* bunx @plaited/development-skills scaffold-rules --list
|
|
39
41
|
* ```
|
|
40
42
|
*/
|
|
41
43
|
|
|
@@ -44,144 +46,101 @@ import { join } from 'node:path'
|
|
|
44
46
|
import { parseArgs } from 'node:util'
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
|
-
*
|
|
49
|
+
* Marker delimiters for programmatic file updates
|
|
48
50
|
*
|
|
49
51
|
* @remarks
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
52
|
+
* These markers allow scaffold-rules to update CLAUDE.md and AGENTS.md
|
|
53
|
+
* without destroying user content outside the marked section.
|
|
54
|
+
*
|
|
55
|
+
* Use these markers to implement idempotent updates:
|
|
56
|
+
* 1. Find existing markers in file content
|
|
57
|
+
* 2. Replace content between markers (inclusive) with new section
|
|
58
|
+
* 3. If no markers exist, append section to end of file
|
|
59
|
+
*
|
|
60
|
+
* @property start - Opening marker to place before rules section
|
|
61
|
+
* @property end - Closing marker to place after rules section
|
|
62
|
+
*
|
|
63
|
+
* @public
|
|
53
64
|
*/
|
|
54
|
-
|
|
65
|
+
export const MARKERS = {
|
|
66
|
+
start: '<!-- PLAITED-RULES-START -->',
|
|
67
|
+
end: '<!-- PLAITED-RULES-END -->',
|
|
68
|
+
} as const
|
|
55
69
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Unified rules directory path
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* All agents use `.plaited/rules/` as the default location.
|
|
75
|
+
* This provides consistency and allows both CLAUDE.md and AGENTS.md
|
|
76
|
+
* to reference the same rule files.
|
|
77
|
+
*/
|
|
78
|
+
const UNIFIED_RULES_PATH = '.plaited/rules' as const
|
|
62
79
|
|
|
63
80
|
type TemplateContext = {
|
|
64
|
-
agent: Agent
|
|
65
|
-
capabilities: AgentCapabilities
|
|
66
|
-
hasDevelopmentSkills: boolean
|
|
67
81
|
rulesPath: string
|
|
68
82
|
}
|
|
69
83
|
|
|
70
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Processed template with filename, content, and description
|
|
86
|
+
*/
|
|
87
|
+
export type ProcessedTemplate = {
|
|
71
88
|
filename: string
|
|
72
89
|
content: string
|
|
73
90
|
description: string
|
|
74
91
|
}
|
|
75
92
|
|
|
76
|
-
type ScaffoldOutput = {
|
|
77
|
-
agent: Agent
|
|
78
|
-
rulesPath: string
|
|
79
|
-
agentsMdPath: string
|
|
80
|
-
format: 'multi-file' | 'agents-md'
|
|
81
|
-
supportsAgentsMd: boolean
|
|
82
|
-
agentsMdContent?: string
|
|
83
|
-
templates: Record<string, ProcessedTemplate>
|
|
84
|
-
}
|
|
85
|
-
|
|
86
93
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* @remarks
|
|
90
|
-
* - hasSandbox: Runs in restricted environment (affects git commands, temp files)
|
|
91
|
-
* - multiFileRules: Supports directory of rule files vs single file
|
|
92
|
-
* - supportsSlashCommands: Has /command syntax for invoking tools
|
|
93
|
-
* - supportsAgentsMd: Reads AGENTS.md format
|
|
94
|
-
*
|
|
95
|
-
* Note: We only support 2 targets now:
|
|
96
|
-
* - claude: Claude Code with unique .claude/ directory system
|
|
97
|
-
* - agents-md: Universal format for all AGENTS.md-compatible agents
|
|
98
|
-
* (Cursor, Factory, Copilot, Windsurf, Cline, Aider, and 60,000+ others)
|
|
94
|
+
* Output from scaffold-rules CLI
|
|
99
95
|
*/
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
'agents-md': {
|
|
108
|
-
hasSandbox: false,
|
|
109
|
-
multiFileRules: true,
|
|
110
|
-
supportsSlashCommands: false,
|
|
111
|
-
supportsAgentsMd: true,
|
|
112
|
-
},
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Evaluate a single condition against context
|
|
117
|
-
*/
|
|
118
|
-
const evaluateCondition = (condition: string, context: TemplateContext): boolean => {
|
|
119
|
-
// Check development-skills
|
|
120
|
-
if (condition === 'development-skills') {
|
|
121
|
-
return context.hasDevelopmentSkills
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Check capability-based conditions
|
|
125
|
-
if (condition === 'has-sandbox') {
|
|
126
|
-
return context.capabilities.hasSandbox
|
|
127
|
-
}
|
|
128
|
-
if (condition === 'multi-file-rules') {
|
|
129
|
-
return context.capabilities.multiFileRules
|
|
130
|
-
}
|
|
131
|
-
if (condition === 'supports-slash-commands') {
|
|
132
|
-
return context.capabilities.supportsSlashCommands
|
|
133
|
-
}
|
|
134
|
-
if (condition === 'supports-agents-md') {
|
|
135
|
-
return context.capabilities.supportsAgentsMd
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Check agent-specific conditions (legacy: agent:name)
|
|
139
|
-
const agentMatch = condition.match(/^agent:(\w+)$/)
|
|
140
|
-
if (agentMatch) {
|
|
141
|
-
return context.agent === agentMatch[1]
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return false
|
|
96
|
+
export type ScaffoldOutput = {
|
|
97
|
+
rulesPath: string
|
|
98
|
+
/** Marker-wrapped section with @ syntax for CLAUDE.md */
|
|
99
|
+
claudeMdSection: string
|
|
100
|
+
/** Marker-wrapped section with markdown links for AGENTS.md */
|
|
101
|
+
agentsMdSection: string
|
|
102
|
+
templates: Record<string, ProcessedTemplate>
|
|
145
103
|
}
|
|
146
104
|
|
|
147
105
|
/**
|
|
148
106
|
* Process template conditionals
|
|
149
107
|
*
|
|
150
108
|
* Handles:
|
|
151
|
-
* - {{#if development-skills}}...{{/if}}
|
|
152
|
-
* - {{
|
|
153
|
-
* - {{#if agent:name}}...{{/if}} (legacy, still supported)
|
|
154
|
-
* - {{^if condition}}...{{/if}} (inverse)
|
|
109
|
+
* - {{#if development-skills}}...{{/if}} - Always true (using our CLI means dev-skills is installed)
|
|
110
|
+
* - {{^if development-skills}}...{{/if}} - Always false
|
|
155
111
|
*
|
|
156
112
|
* Processes iteratively to handle nested conditionals correctly.
|
|
157
113
|
*/
|
|
158
|
-
const processConditionals = (content: string
|
|
114
|
+
const processConditionals = (content: string): string => {
|
|
159
115
|
let result = content
|
|
160
116
|
let previousResult = ''
|
|
117
|
+
const maxIterations = 100
|
|
118
|
+
let iterations = 0
|
|
161
119
|
|
|
162
120
|
// Process iteratively until no more changes (handles nested conditionals)
|
|
163
|
-
while (result !== previousResult) {
|
|
121
|
+
while (result !== previousResult && iterations < maxIterations) {
|
|
164
122
|
previousResult = result
|
|
123
|
+
iterations++
|
|
165
124
|
|
|
166
|
-
// Process positive conditionals {{#if
|
|
167
|
-
//
|
|
168
|
-
// Note: Nested quantifiers are safe here - input is trusted bundled templates, not user content
|
|
125
|
+
// Process positive conditionals {{#if development-skills}}...{{/if}}
|
|
126
|
+
// Always true - include the block content
|
|
169
127
|
result = result.replace(
|
|
170
|
-
/\{\{#if
|
|
171
|
-
(_,
|
|
172
|
-
return evaluateCondition(condition, context) ? block : ''
|
|
173
|
-
},
|
|
128
|
+
/\{\{#if development-skills\}\}((?:(?!\{\{#if )(?!\{\{\^if )(?!\{\{\/if\}\})[\s\S])*?)\{\{\/if\}\}/g,
|
|
129
|
+
(_, block) => block,
|
|
174
130
|
)
|
|
175
131
|
|
|
176
|
-
// Process inverse conditionals {{^if
|
|
132
|
+
// Process inverse conditionals {{^if development-skills}}...{{/if}}
|
|
133
|
+
// Always false - remove the block content
|
|
177
134
|
result = result.replace(
|
|
178
|
-
/\{\{\^if
|
|
179
|
-
(
|
|
180
|
-
return evaluateCondition(condition, context) ? '' : block
|
|
181
|
-
},
|
|
135
|
+
/\{\{\^if development-skills\}\}((?:(?!\{\{#if )(?!\{\{\^if )(?!\{\{\/if\}\})[\s\S])*?)\{\{\/if\}\}/g,
|
|
136
|
+
() => '',
|
|
182
137
|
)
|
|
183
138
|
}
|
|
184
139
|
|
|
140
|
+
if (iterations >= maxIterations) {
|
|
141
|
+
console.warn('Warning: Max iterations reached in template processing. Some conditionals may be unprocessed.')
|
|
142
|
+
}
|
|
143
|
+
|
|
185
144
|
return result
|
|
186
145
|
}
|
|
187
146
|
|
|
@@ -189,39 +148,23 @@ const processConditionals = (content: string, context: TemplateContext): string
|
|
|
189
148
|
* Process template variables
|
|
190
149
|
*
|
|
191
150
|
* Handles:
|
|
192
|
-
* - {{LINK:rule-id}} - Generate cross-reference
|
|
193
|
-
* - {{AGENT_NAME}} - Agent name
|
|
151
|
+
* - {{LINK:rule-id}} - Generate cross-reference path
|
|
194
152
|
* - {{RULES_PATH}} - Rules path
|
|
195
153
|
*/
|
|
196
154
|
const processVariables = (content: string, context: TemplateContext): string => {
|
|
197
155
|
let result = content
|
|
198
156
|
|
|
199
|
-
// Replace {{LINK:rule-id}} with
|
|
157
|
+
// Replace {{LINK:rule-id}} with path reference
|
|
200
158
|
result = result.replace(/\{\{LINK:(\w+)\}\}/g, (_, ruleId) => {
|
|
201
|
-
return
|
|
159
|
+
return `${context.rulesPath}/${ruleId}.md`
|
|
202
160
|
})
|
|
203
161
|
|
|
204
|
-
// Replace {{AGENT_NAME}}
|
|
205
|
-
result = result.replace(/\{\{AGENT_NAME\}\}/g, context.agent)
|
|
206
|
-
|
|
207
162
|
// Replace {{RULES_PATH}}
|
|
208
163
|
result = result.replace(/\{\{RULES_PATH\}\}/g, context.rulesPath)
|
|
209
164
|
|
|
210
165
|
return result
|
|
211
166
|
}
|
|
212
167
|
|
|
213
|
-
/**
|
|
214
|
-
* Generate cross-reference based on agent format
|
|
215
|
-
*/
|
|
216
|
-
const generateCrossReference = (ruleId: string, context: TemplateContext): string => {
|
|
217
|
-
if (context.agent === 'claude') {
|
|
218
|
-
// Claude Code uses @ syntax for file references
|
|
219
|
-
return `@${context.rulesPath}/${ruleId}.md`
|
|
220
|
-
}
|
|
221
|
-
// Use context.rulesPath for cross-references (supports custom --rules-dir)
|
|
222
|
-
return `${context.rulesPath}/${ruleId}.md`
|
|
223
|
-
}
|
|
224
|
-
|
|
225
168
|
/**
|
|
226
169
|
* Remove template headers
|
|
227
170
|
*/
|
|
@@ -238,7 +181,8 @@ const extractDescription = (content: string): string => {
|
|
|
238
181
|
let description = ''
|
|
239
182
|
|
|
240
183
|
for (let i = 1; i < lines.length; i++) {
|
|
241
|
-
|
|
184
|
+
// Non-null assertion safe: loop condition guarantees i < lines.length
|
|
185
|
+
const line = lines[i]!.trim()
|
|
242
186
|
if (line && !line.startsWith('#') && !line.startsWith('**')) {
|
|
243
187
|
description = line
|
|
244
188
|
break
|
|
@@ -258,7 +202,7 @@ const processTemplate = (content: string, context: TemplateContext): string => {
|
|
|
258
202
|
result = removeTemplateHeaders(result)
|
|
259
203
|
|
|
260
204
|
// 2. Process conditionals
|
|
261
|
-
result = processConditionals(result
|
|
205
|
+
result = processConditionals(result)
|
|
262
206
|
|
|
263
207
|
// 3. Process variables
|
|
264
208
|
result = processVariables(result, context)
|
|
@@ -270,27 +214,43 @@ const processTemplate = (content: string, context: TemplateContext): string => {
|
|
|
270
214
|
}
|
|
271
215
|
|
|
272
216
|
/**
|
|
273
|
-
*
|
|
217
|
+
* Generate marker-wrapped section for CLAUDE.md with @ syntax
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* Claude Code uses `@path/to/file.md` syntax to reference rule files.
|
|
221
|
+
* The markers allow this section to be updated without affecting other content.
|
|
274
222
|
*/
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
223
|
+
const generateClaudeMdSection = (templates: Record<string, ProcessedTemplate>, rulesPath: string): string => {
|
|
224
|
+
const lines = [
|
|
225
|
+
MARKERS.start,
|
|
226
|
+
'',
|
|
227
|
+
'## Project Rules',
|
|
228
|
+
'',
|
|
229
|
+
`This project uses modular development rules stored in \`${rulesPath}/\`.`,
|
|
230
|
+
'Each rule file covers a specific topic:',
|
|
231
|
+
'',
|
|
232
|
+
]
|
|
278
233
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
234
|
+
for (const [_ruleId, template] of Object.entries(templates)) {
|
|
235
|
+
lines.push(`- @${rulesPath}/${template.filename} - ${template.description}`)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
lines.push('')
|
|
239
|
+
lines.push(MARKERS.end)
|
|
240
|
+
|
|
241
|
+
return lines.join('\n')
|
|
284
242
|
}
|
|
285
243
|
|
|
286
244
|
/**
|
|
287
|
-
* Generate AGENTS.md
|
|
245
|
+
* Generate marker-wrapped section for AGENTS.md with markdown links
|
|
246
|
+
*
|
|
247
|
+
* @remarks
|
|
248
|
+
* AGENTS.md format uses standard markdown links to reference rule files.
|
|
249
|
+
* The markers allow this section to be updated without affecting other content.
|
|
288
250
|
*/
|
|
289
|
-
const
|
|
251
|
+
const generateAgentsMdSection = (templates: Record<string, ProcessedTemplate>, rulesPath: string): string => {
|
|
290
252
|
const lines = [
|
|
291
|
-
|
|
292
|
-
'',
|
|
293
|
-
'Development rules for AI coding agents.',
|
|
253
|
+
MARKERS.start,
|
|
294
254
|
'',
|
|
295
255
|
'## Rules',
|
|
296
256
|
'',
|
|
@@ -304,10 +264,7 @@ const generateAgentsMd = (templates: Record<string, ProcessedTemplate>, rulesPat
|
|
|
304
264
|
}
|
|
305
265
|
|
|
306
266
|
lines.push('')
|
|
307
|
-
lines.push(
|
|
308
|
-
lines.push('')
|
|
309
|
-
lines.push('For detailed guidance on each topic, see the linked rule files above.')
|
|
310
|
-
lines.push('')
|
|
267
|
+
lines.push(MARKERS.end)
|
|
311
268
|
|
|
312
269
|
return lines.join('\n')
|
|
313
270
|
}
|
|
@@ -319,16 +276,6 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
|
|
|
319
276
|
const { values } = parseArgs({
|
|
320
277
|
args,
|
|
321
278
|
options: {
|
|
322
|
-
agent: {
|
|
323
|
-
type: 'string',
|
|
324
|
-
short: 'a',
|
|
325
|
-
default: 'claude',
|
|
326
|
-
},
|
|
327
|
-
format: {
|
|
328
|
-
type: 'string',
|
|
329
|
-
short: 'f',
|
|
330
|
-
default: 'json',
|
|
331
|
-
},
|
|
332
279
|
rules: {
|
|
333
280
|
type: 'string',
|
|
334
281
|
short: 'r',
|
|
@@ -338,54 +285,55 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
|
|
|
338
285
|
type: 'string',
|
|
339
286
|
short: 'd',
|
|
340
287
|
},
|
|
341
|
-
|
|
342
|
-
type: '
|
|
343
|
-
short: '
|
|
288
|
+
list: {
|
|
289
|
+
type: 'boolean',
|
|
290
|
+
short: 'l',
|
|
344
291
|
},
|
|
345
292
|
},
|
|
346
293
|
allowPositionals: true,
|
|
347
294
|
strict: false,
|
|
348
295
|
})
|
|
349
296
|
|
|
350
|
-
const agent = values.agent as Agent
|
|
351
297
|
const rulesFilter = values.rules as string[] | undefined
|
|
352
298
|
const customRulesDir = values['rules-dir'] as string | undefined
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
// Validate agent
|
|
356
|
-
const validAgents: Agent[] = ['claude', 'agents-md']
|
|
357
|
-
if (!validAgents.includes(agent)) {
|
|
358
|
-
console.error(`Error: Invalid agent "${agent}". Must be one of: ${validAgents.join(', ')}`)
|
|
359
|
-
console.error(
|
|
360
|
-
'Note: Use "agents-md" for Cursor, Factory, Copilot, Windsurf, Cline, Aider, and other AGENTS.md-compatible tools.',
|
|
361
|
-
)
|
|
362
|
-
process.exit(1)
|
|
363
|
-
}
|
|
299
|
+
const listOnly = values.list as boolean | undefined
|
|
364
300
|
|
|
365
301
|
// Get bundled templates directory
|
|
366
|
-
const packageRulesDir = join(import.meta.dir, '../.
|
|
302
|
+
const packageRulesDir = join(import.meta.dir, '../.plaited/rules')
|
|
367
303
|
|
|
368
304
|
// Read template files
|
|
369
305
|
const templateFiles = await readdir(packageRulesDir)
|
|
370
306
|
|
|
371
307
|
// Filter to .md files
|
|
372
308
|
const mdFiles = templateFiles.filter((f) => f.endsWith('.md'))
|
|
309
|
+
const availableRuleIds = mdFiles.map((f) => f.replace('.md', ''))
|
|
310
|
+
|
|
311
|
+
// Validate requested rules exist
|
|
312
|
+
if (rulesFilter) {
|
|
313
|
+
const invalidRules = rulesFilter.filter((r) => !availableRuleIds.includes(r))
|
|
314
|
+
if (invalidRules.length > 0) {
|
|
315
|
+
console.error(`Warning: Unknown rules: ${invalidRules.join(', ')}`)
|
|
316
|
+
console.error(`Available rules: ${availableRuleIds.join(', ')}`)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Handle --list flag: output available rules and exit
|
|
321
|
+
if (listOnly) {
|
|
322
|
+
const listOutput = availableRuleIds.map((id) => ({ id, filename: `${id}.md` }))
|
|
323
|
+
console.log(JSON.stringify(listOutput, null, 2))
|
|
324
|
+
return
|
|
325
|
+
}
|
|
373
326
|
|
|
374
327
|
// Filter if specific rules requested
|
|
375
328
|
const rulesToProcess = rulesFilter ? mdFiles.filter((f) => rulesFilter.includes(f.replace('.md', ''))) : mdFiles
|
|
376
329
|
|
|
377
330
|
// Process each template
|
|
378
331
|
const templates: Record<string, ProcessedTemplate> = {}
|
|
379
|
-
const capabilities = AGENT_CAPABILITIES[agent]
|
|
380
332
|
|
|
381
|
-
// Use custom
|
|
382
|
-
const rulesPath = customRulesDir ??
|
|
383
|
-
const agentsMdPath = customAgentsMdPath ?? 'AGENTS.md'
|
|
333
|
+
// Use custom path or unified default
|
|
334
|
+
const rulesPath = customRulesDir ?? UNIFIED_RULES_PATH
|
|
384
335
|
|
|
385
336
|
const context: TemplateContext = {
|
|
386
|
-
agent,
|
|
387
|
-
capabilities,
|
|
388
|
-
hasDevelopmentSkills: true, // Always true when using our CLI
|
|
389
337
|
rulesPath,
|
|
390
338
|
}
|
|
391
339
|
|
|
@@ -405,27 +353,23 @@ export const scaffoldRules = async (args: string[]): Promise<void> => {
|
|
|
405
353
|
description: extractDescription(processed),
|
|
406
354
|
}
|
|
407
355
|
} catch (error) {
|
|
408
|
-
|
|
409
|
-
console.error(`Error processing template ${file}: ${message}`)
|
|
356
|
+
console.error(`Error processing template ${file}:`, error)
|
|
410
357
|
process.exit(1)
|
|
411
358
|
}
|
|
412
359
|
}
|
|
413
360
|
|
|
361
|
+
// Generate marker-wrapped sections for both CLAUDE.md and AGENTS.md
|
|
362
|
+
const claudeMdSection = generateClaudeMdSection(templates, rulesPath)
|
|
363
|
+
const agentsMdSection = generateAgentsMdSection(templates, rulesPath)
|
|
364
|
+
|
|
414
365
|
// Build output
|
|
415
366
|
const output: ScaffoldOutput = {
|
|
416
|
-
agent,
|
|
417
367
|
rulesPath,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
supportsAgentsMd: capabilities.supportsAgentsMd,
|
|
368
|
+
claudeMdSection,
|
|
369
|
+
agentsMdSection,
|
|
421
370
|
templates,
|
|
422
371
|
}
|
|
423
372
|
|
|
424
|
-
// Generate AGENTS.md content for agents-md format
|
|
425
|
-
if (agent === 'agents-md') {
|
|
426
|
-
output.agentsMdContent = generateAgentsMd(templates, rulesPath)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
373
|
console.log(JSON.stringify(output, null, 2))
|
|
430
374
|
}
|
|
431
375
|
|