@codedrifters/configulator 0.0.155 → 0.0.157

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/lib/index.mjs CHANGED
@@ -173,7 +173,7 @@ var require_lib = __commonJS({
173
173
  });
174
174
 
175
175
  // src/agent/agent-config.ts
176
- import { Component as Component7 } from "projen";
176
+ import { Component as Component8 } from "projen";
177
177
 
178
178
  // src/agent/types.ts
179
179
  var AGENT_RULE_SCOPE = {
@@ -284,10 +284,109 @@ var awsCdkBundle = {
284
284
  };
285
285
 
286
286
  // src/agent/bundles/base.ts
287
+ var createRuleSkill = {
288
+ name: "create-rule",
289
+ description: "Guide for creating new agent rules in this project using configulator",
290
+ disableModelInvocation: true,
291
+ instructions: [
292
+ "# Create Agent Rule",
293
+ "",
294
+ "Create a new agent rule for the **{{repository.owner}}/{{repository.name}}** project.",
295
+ "",
296
+ "## Steps",
297
+ "",
298
+ "1. **Identify the rule purpose** \u2014 ask what behavior the rule should enforce or guide",
299
+ "2. **Choose a scope:**",
300
+ " - `AGENT_RULE_SCOPE.ALWAYS` \u2014 active in all contexts",
301
+ " - `AGENT_RULE_SCOPE.FILE_PATTERN` \u2014 active only when working on matching files (requires `filePatterns`)",
302
+ "3. **Pick a name** \u2014 lowercase kebab-case (e.g., `react-conventions`, `api-error-handling`)",
303
+ "4. **Write the content** \u2014 clear, actionable markdown instructions",
304
+ "5. **Add to the project config** \u2014 add the rule to `AgentConfigOptions.rules` in the Projen config file",
305
+ "",
306
+ "## Rule Template",
307
+ "",
308
+ "```typescript",
309
+ "import { AGENT_RULE_SCOPE } from '@codedrifters/configulator';",
310
+ "",
311
+ "// In your AgentConfig options:",
312
+ "{",
313
+ " rules: [",
314
+ " {",
315
+ " name: 'my-new-rule',",
316
+ " description: 'What this rule does \u2014 used by AI for rule selection',",
317
+ " scope: AGENT_RULE_SCOPE.ALWAYS, // or FILE_PATTERN",
318
+ " // filePatterns: ['src/**/*.ts'], // required for FILE_PATTERN scope",
319
+ " content: [",
320
+ " '# Rule Title',",
321
+ " '',",
322
+ " '## Guidelines',",
323
+ " '',",
324
+ " '- Guideline 1',",
325
+ " '- Guideline 2',",
326
+ " ].join('\\n'),",
327
+ " tags: ['coding'], // optional: for ordering",
328
+ " },",
329
+ " ],",
330
+ "}",
331
+ "```",
332
+ "",
333
+ "## Best Practices",
334
+ "",
335
+ "- Keep rules **focused** \u2014 one concern per rule",
336
+ '- Use **imperative tone** \u2014 "Use X" not "You should use X"',
337
+ "- Include **examples** for complex patterns",
338
+ "- Use `ruleExtensions` to add project-specific content to existing bundle rules instead of replacing them"
339
+ ].join("\n")
340
+ };
341
+ var reviewPrSkill = {
342
+ name: "review-pr",
343
+ description: "Review a pull request for code quality, conventions, and correctness",
344
+ instructions: [
345
+ "# Review Pull Request",
346
+ "",
347
+ "Review the current pull request in **{{repository.owner}}/{{repository.name}}**.",
348
+ "",
349
+ "## Review Checklist",
350
+ "",
351
+ "1. **Read the PR description** \u2014 understand the intent and linked issue",
352
+ "2. **Review the diff** \u2014 check all changed files for:",
353
+ " - Correctness and logic errors",
354
+ " - Adherence to project conventions",
355
+ " - Test coverage for new/changed behavior",
356
+ " - No unintended side effects",
357
+ "3. **Check PR conventions:**",
358
+ " - Title uses conventional commit prefix (`feat:`, `fix:`, `docs:`, etc.)",
359
+ " - Body includes a closing keyword (`Closes #<issue>`)",
360
+ " - Summary of changes is present",
361
+ "4. **Verify build and tests pass**",
362
+ "",
363
+ "## Commands",
364
+ "",
365
+ "```bash",
366
+ "# View the PR details",
367
+ "gh pr view",
368
+ "",
369
+ "# View the diff",
370
+ "gh pr diff",
371
+ "",
372
+ "# Check CI status",
373
+ "gh pr checks",
374
+ "```",
375
+ "",
376
+ "## Output Format",
377
+ "",
378
+ "Provide a structured review with:",
379
+ "- **Summary**: One-line verdict (approve, request changes, or comment)",
380
+ "- **Findings**: Bulleted list of issues or suggestions, grouped by severity",
381
+ "- **Positive notes**: What was done well"
382
+ ].join("\n"),
383
+ allowedTools: ["Read", "Glob", "Grep", "Bash(gh *)"]
384
+ };
287
385
  var baseBundle = {
288
386
  name: "base",
289
387
  description: "Core rules: project overview, interaction style, and general coding conventions",
290
388
  appliesWhen: () => true,
389
+ skills: [createRuleSkill, reviewPrSkill],
291
390
  rules: [
292
391
  {
293
392
  name: "project-overview",
@@ -296,6 +395,9 @@ var baseBundle = {
296
395
  content: [
297
396
  "# Project Overview",
298
397
  "",
398
+ "**Repository:** {{repository.owner}}/{{repository.name}}",
399
+ "**Default branch:** {{repository.defaultBranch}}",
400
+ "",
299
401
  "## Important Notes",
300
402
  "",
301
403
  "- **Never edit generated files** \u2014 they are marked with `// ~~ Generated by projen`",
@@ -1256,6 +1358,76 @@ var BUILT_IN_BUNDLES = [
1256
1358
  projenBundle
1257
1359
  ];
1258
1360
 
1361
+ // src/projects/project-metadata.ts
1362
+ import { Component as Component5 } from "projen";
1363
+ import { NodeProject as NodeProject2 } from "projen/lib/javascript";
1364
+ var GITHUB_HTTPS_RE = /(?:https?:\/\/|git\+https:\/\/)github\.com\/([^/]+)\/([^/.]+)(?:\.git)?/;
1365
+ var GITHUB_SSH_RE = /git@github\.com:([^/]+)\/([^/.]+)(?:\.git)?/;
1366
+ function parseGitHubUrl(url) {
1367
+ const httpsMatch = url.match(GITHUB_HTTPS_RE);
1368
+ if (httpsMatch) {
1369
+ return { owner: httpsMatch[1], name: httpsMatch[2] };
1370
+ }
1371
+ const sshMatch = url.match(GITHUB_SSH_RE);
1372
+ if (sshMatch) {
1373
+ return { owner: sshMatch[1], name: sshMatch[2] };
1374
+ }
1375
+ return { owner: void 0, name: void 0 };
1376
+ }
1377
+ var ProjectMetadata = class _ProjectMetadata extends Component5 {
1378
+ /**
1379
+ * Returns the ProjectMetadata instance for a project, or undefined
1380
+ * if the component has not been added.
1381
+ */
1382
+ static of(project) {
1383
+ const isProjectMetadata = (c) => c instanceof _ProjectMetadata;
1384
+ return project.components.find(isProjectMetadata);
1385
+ }
1386
+ constructor(project, options = {}) {
1387
+ super(project);
1388
+ this.metadata = this.resolveMetadata(options);
1389
+ }
1390
+ /**
1391
+ * Merges explicit options with auto-detected values.
1392
+ * Auto-detection reads the repository URL from package.json
1393
+ * (via Projen's NodePackage manifest) and parses GitHub owner/name.
1394
+ * Explicit options always take precedence over auto-detected values.
1395
+ */
1396
+ resolveMetadata(options) {
1397
+ const autoDetected = this.autoDetectRepository();
1398
+ return {
1399
+ repository: {
1400
+ owner: options.repository?.owner ?? autoDetected.owner,
1401
+ name: options.repository?.name ?? autoDetected.name,
1402
+ defaultBranch: options.repository?.defaultBranch ?? "main"
1403
+ },
1404
+ githubProject: options.githubProject,
1405
+ organization: options.organization,
1406
+ slack: options.slack,
1407
+ labels: options.labels,
1408
+ milestones: options.milestones,
1409
+ docsPath: options.docsPath,
1410
+ deployment: options.deployment
1411
+ };
1412
+ }
1413
+ /**
1414
+ * Attempts to auto-detect repository owner and name from the Projen
1415
+ * project's package.json repository field.
1416
+ */
1417
+ autoDetectRepository() {
1418
+ if (!(this.project instanceof NodeProject2)) {
1419
+ return { owner: void 0, name: void 0 };
1420
+ }
1421
+ const manifest = this.project.package.manifest;
1422
+ const repoField = manifest.repository;
1423
+ if (!repoField) {
1424
+ return { owner: void 0, name: void 0 };
1425
+ }
1426
+ const url = typeof repoField === "string" ? repoField : repoField.url ?? "";
1427
+ return parseGitHubUrl(url);
1428
+ }
1429
+ };
1430
+
1259
1431
  // src/agent/renderers/claude-renderer.ts
1260
1432
  import { JsonFile as JsonFile2 } from "projen";
1261
1433
  import { TextFile as TextFile2 } from "projen/lib/textfile";
@@ -1485,6 +1657,15 @@ var ClaudeRenderer = class _ClaudeRenderer {
1485
1657
  lines.push(` - "${p}"`);
1486
1658
  }
1487
1659
  }
1660
+ if (skill.context) {
1661
+ lines.push(`context: "${skill.context}"`);
1662
+ }
1663
+ if (skill.agent) {
1664
+ lines.push(`agent: "${skill.agent}"`);
1665
+ }
1666
+ if (skill.shell) {
1667
+ lines.push(`shell: "${skill.shell}"`);
1668
+ }
1488
1669
  if (skill.allowedTools && skill.allowedTools.length > 0) {
1489
1670
  lines.push(`allowed-tools:`);
1490
1671
  for (const tool of skill.allowedTools) {
@@ -1601,6 +1782,15 @@ var CursorRenderer = class _CursorRenderer {
1601
1782
  if (skill.userInvocable === false) {
1602
1783
  lines.push(`user-invocable: false`);
1603
1784
  }
1785
+ if (skill.context) {
1786
+ lines.push(`context: "${skill.context}"`);
1787
+ }
1788
+ if (skill.agent) {
1789
+ lines.push(`agent: "${skill.agent}"`);
1790
+ }
1791
+ if (skill.shell) {
1792
+ lines.push(`shell: "${skill.shell}"`);
1793
+ }
1604
1794
  if (skill.allowedTools && skill.allowedTools.length > 0) {
1605
1795
  lines.push(`allowed-tools:`);
1606
1796
  for (const tool of skill.allowedTools) {
@@ -1707,8 +1897,57 @@ var CursorRenderer = class _CursorRenderer {
1707
1897
  }
1708
1898
  };
1709
1899
 
1900
+ // src/agent/template-resolver.ts
1901
+ var FALLBACKS = {
1902
+ "repository.owner": "<owner>",
1903
+ "repository.name": "<repo>",
1904
+ "repository.defaultBranch": "main",
1905
+ "organization.name": "<organization>",
1906
+ "organization.githubOrg": "<org>",
1907
+ "githubProject.name": "<project-name>",
1908
+ "githubProject.number": "<project-number>",
1909
+ "githubProject.nodeId": "<project-node-id>",
1910
+ docsPath: "<docs-path>"
1911
+ };
1912
+ var TEMPLATE_RE = /\{\{(\w+(?:\.\w+)*)\}\}/g;
1913
+ function getNestedValue(obj, path) {
1914
+ const parts = path.split(".");
1915
+ let current = obj;
1916
+ for (const part of parts) {
1917
+ if (current == null || typeof current !== "object") {
1918
+ return void 0;
1919
+ }
1920
+ current = current[part];
1921
+ }
1922
+ if (current == null) {
1923
+ return void 0;
1924
+ }
1925
+ return String(current);
1926
+ }
1927
+ function resolveTemplateVariables(template, metadata) {
1928
+ if (!TEMPLATE_RE.test(template)) {
1929
+ return { resolved: template, unresolvedKeys: [] };
1930
+ }
1931
+ const unresolvedKeys = [];
1932
+ TEMPLATE_RE.lastIndex = 0;
1933
+ const resolved = template.replace(TEMPLATE_RE, (_match, key) => {
1934
+ if (metadata) {
1935
+ const value = getNestedValue(
1936
+ metadata,
1937
+ key
1938
+ );
1939
+ if (value !== void 0) {
1940
+ return value;
1941
+ }
1942
+ }
1943
+ unresolvedKeys.push(key);
1944
+ return FALLBACKS[key] ?? `<${key}>`;
1945
+ });
1946
+ return { resolved, unresolvedKeys };
1947
+ }
1948
+
1710
1949
  // src/agent/agent-config.ts
1711
- var AgentConfig = class _AgentConfig extends Component7 {
1950
+ var AgentConfig = class _AgentConfig extends Component8 {
1712
1951
  /**
1713
1952
  * Find the AgentConfig component on a project.
1714
1953
  */
@@ -1727,12 +1966,20 @@ var AgentConfig = class _AgentConfig extends Component7 {
1727
1966
  const skills = this.resolveSkills();
1728
1967
  const subAgents = this.resolveSubAgents();
1729
1968
  const mcpServers = this.options.mcpServers ?? {};
1969
+ const projectMetadata = ProjectMetadata.of(this.project);
1970
+ const metadata = projectMetadata?.metadata;
1971
+ const resolvedRules = this.resolveTemplates(rules, metadata);
1972
+ const resolvedSkills = this.resolveSkillTemplates(skills, metadata);
1973
+ const resolvedSubAgents = this.resolveSubAgentTemplates(
1974
+ subAgents,
1975
+ metadata
1976
+ );
1730
1977
  if (platforms.includes(AGENT_PLATFORM.CURSOR)) {
1731
1978
  CursorRenderer.render(
1732
1979
  this,
1733
- rules,
1734
- skills,
1735
- subAgents,
1980
+ resolvedRules,
1981
+ resolvedSkills,
1982
+ resolvedSubAgents,
1736
1983
  mcpServers,
1737
1984
  this.options.cursorSettings
1738
1985
  );
@@ -1740,18 +1987,28 @@ var AgentConfig = class _AgentConfig extends Component7 {
1740
1987
  if (platforms.includes(AGENT_PLATFORM.CLAUDE)) {
1741
1988
  ClaudeRenderer.render(
1742
1989
  this,
1743
- rules,
1744
- skills,
1745
- subAgents,
1990
+ resolvedRules,
1991
+ resolvedSkills,
1992
+ resolvedSubAgents,
1746
1993
  mcpServers,
1747
1994
  this.options.claudeSettings
1748
1995
  );
1749
1996
  }
1750
1997
  if (platforms.includes(AGENT_PLATFORM.CODEX)) {
1751
- CodexRenderer.render(this, rules, skills, subAgents);
1998
+ CodexRenderer.render(
1999
+ this,
2000
+ resolvedRules,
2001
+ resolvedSkills,
2002
+ resolvedSubAgents
2003
+ );
1752
2004
  }
1753
2005
  if (platforms.includes(AGENT_PLATFORM.COPILOT)) {
1754
- CopilotRenderer.render(this, rules, skills, subAgents);
2006
+ CopilotRenderer.render(
2007
+ this,
2008
+ resolvedRules,
2009
+ resolvedSkills,
2010
+ resolvedSubAgents
2011
+ );
1755
2012
  }
1756
2013
  }
1757
2014
  resolvePlatforms() {
@@ -1854,13 +2111,65 @@ ${extra}`
1854
2111
  }
1855
2112
  return [...agentMap.values()];
1856
2113
  }
2114
+ /**
2115
+ * Resolves template variables in rule content using project metadata.
2116
+ * Emits synthesis warnings for rules with unresolved variables.
2117
+ */
2118
+ resolveTemplates(rules, metadata) {
2119
+ return rules.map((rule) => {
2120
+ const { resolved, unresolvedKeys } = resolveTemplateVariables(
2121
+ rule.content,
2122
+ metadata
2123
+ );
2124
+ if (unresolvedKeys.length > 0) {
2125
+ this.project.logger.warn(
2126
+ `AgentConfig: ProjectMetadata not found; rule '${rule.name}' using default values`
2127
+ );
2128
+ }
2129
+ return resolved !== rule.content ? { ...rule, content: resolved } : rule;
2130
+ });
2131
+ }
2132
+ /**
2133
+ * Resolves template variables in skill instructions using project metadata.
2134
+ */
2135
+ resolveSkillTemplates(skills, metadata) {
2136
+ return skills.map((skill) => {
2137
+ const { resolved, unresolvedKeys } = resolveTemplateVariables(
2138
+ skill.instructions,
2139
+ metadata
2140
+ );
2141
+ if (unresolvedKeys.length > 0) {
2142
+ this.project.logger.warn(
2143
+ `AgentConfig: ProjectMetadata not found; skill '${skill.name}' using default values`
2144
+ );
2145
+ }
2146
+ return resolved !== skill.instructions ? { ...skill, instructions: resolved } : skill;
2147
+ });
2148
+ }
2149
+ /**
2150
+ * Resolves template variables in sub-agent prompts using project metadata.
2151
+ */
2152
+ resolveSubAgentTemplates(subAgents, metadata) {
2153
+ return subAgents.map((agent) => {
2154
+ const { resolved, unresolvedKeys } = resolveTemplateVariables(
2155
+ agent.prompt,
2156
+ metadata
2157
+ );
2158
+ if (unresolvedKeys.length > 0) {
2159
+ this.project.logger.warn(
2160
+ `AgentConfig: ProjectMetadata not found; sub-agent '${agent.name}' using default values`
2161
+ );
2162
+ }
2163
+ return resolved !== agent.prompt ? { ...agent, prompt: resolved } : agent;
2164
+ });
2165
+ }
1857
2166
  };
1858
2167
 
1859
2168
  // src/aws/aws-deployment-config.ts
1860
2169
  var import_utils8 = __toESM(require_lib());
1861
2170
  import { join, relative as relative2 } from "path";
1862
- import { Component as Component8 } from "projen";
1863
- var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component8 {
2171
+ import { Component as Component9 } from "projen";
2172
+ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component9 {
1864
2173
  constructor(project) {
1865
2174
  super(project);
1866
2175
  /**
@@ -1977,8 +2286,8 @@ var AwsDeploymentConfig = class _AwsDeploymentConfig extends Component8 {
1977
2286
 
1978
2287
  // src/aws/aws-deployment-target.ts
1979
2288
  var import_utils9 = __toESM(require_lib());
1980
- import { Component as Component9 } from "projen";
1981
- var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component9 {
2289
+ import { Component as Component10 } from "projen";
2290
+ var AwsDeploymentTarget = class _AwsDeploymentTarget extends Component10 {
1982
2291
  constructor(project, options) {
1983
2292
  super(project);
1984
2293
  /**
@@ -2193,12 +2502,12 @@ var VERSION_KEYS_SKIP = [
2193
2502
 
2194
2503
  // src/jsii/jsii-faker.ts
2195
2504
  import * as spec from "@jsii/spec";
2196
- import { Component as Component10, JsonFile as JsonFile4 } from "projen";
2505
+ import { Component as Component11, JsonFile as JsonFile4 } from "projen";
2197
2506
  var ProjenBaseFqn = {
2198
2507
  TYPESCRIPT_PROJECT: "projen.typescript.TypeScriptProject",
2199
2508
  TYPESCRIPT_PROJECT_OPTIONS: "projen.typescript.TypeScriptProjectOptions"
2200
2509
  };
2201
- var JsiiFaker = class _JsiiFaker extends Component10 {
2510
+ var JsiiFaker = class _JsiiFaker extends Component11 {
2202
2511
  constructor(project) {
2203
2512
  super(project);
2204
2513
  this.project = project;
@@ -2257,7 +2566,7 @@ import {
2257
2566
  import { merge as merge2 } from "ts-deepmerge";
2258
2567
 
2259
2568
  // src/tasks/reset-task.ts
2260
- import { Component as Component11 } from "projen";
2569
+ import { Component as Component12 } from "projen";
2261
2570
 
2262
2571
  // src/projects/typescript-project.ts
2263
2572
  import { typescript } from "projen";
@@ -2432,7 +2741,7 @@ var TypeScriptProject = class extends typescript.TypeScriptProject {
2432
2741
  };
2433
2742
 
2434
2743
  // src/tasks/reset-task.ts
2435
- var ResetTask = class _ResetTask extends Component11 {
2744
+ var ResetTask = class _ResetTask extends Component12 {
2436
2745
  constructor(project, options = {}) {
2437
2746
  super(project);
2438
2747
  this.project = project;
@@ -2505,8 +2814,8 @@ var ResetTask = class _ResetTask extends Component11 {
2505
2814
  };
2506
2815
 
2507
2816
  // src/vscode/vscode.ts
2508
- import { Component as Component12, vscode } from "projen";
2509
- var VSCodeConfig = class extends Component12 {
2817
+ import { Component as Component13, vscode } from "projen";
2818
+ var VSCodeConfig = class extends Component13 {
2510
2819
  constructor(project) {
2511
2820
  super(project);
2512
2821
  const vsConfig = new vscode.VsCode(project);
@@ -2882,6 +3191,12 @@ var MonorepoProject = class extends TypeScriptAppProject {
2882
3191
  if (options.approveMergeUpgradeOptions) {
2883
3192
  addApproveMergeUpgradeWorkflow(this, options.approveMergeUpgradeOptions);
2884
3193
  }
3194
+ if (options.projectMetadata !== false) {
3195
+ new ProjectMetadata(
3196
+ this,
3197
+ typeof options.projectMetadata === "object" ? options.projectMetadata : {}
3198
+ );
3199
+ }
2885
3200
  if (options.agentConfig) {
2886
3201
  new AgentConfig(this, options.agentConfigOptions);
2887
3202
  }
@@ -2943,9 +3258,9 @@ var MonorepoProject = class extends TypeScriptAppProject {
2943
3258
 
2944
3259
  // src/typescript/typescript-config.ts
2945
3260
  import { relative as relative3 } from "path";
2946
- import { Component as Component13 } from "projen";
3261
+ import { Component as Component14 } from "projen";
2947
3262
  import { ensureRelativePathStartsWithDot } from "projen/lib/util/path";
2948
- var TypeScriptConfig = class extends Component13 {
3263
+ var TypeScriptConfig = class extends Component14 {
2949
3264
  constructor(project) {
2950
3265
  super(project);
2951
3266
  let tsPaths = {};
@@ -2973,12 +3288,12 @@ var TypeScriptConfig = class extends Component13 {
2973
3288
 
2974
3289
  // src/workflows/aws-deploy-workflow.ts
2975
3290
  var import_utils10 = __toESM(require_lib());
2976
- import { Component as Component14 } from "projen";
3291
+ import { Component as Component15 } from "projen";
2977
3292
  import { BuildWorkflow } from "projen/lib/build";
2978
3293
  import { GitHub } from "projen/lib/github";
2979
3294
  import { JobPermission as JobPermission4 } from "projen/lib/github/workflows-model";
2980
3295
  var PROD_DEPLOY_NAME = "prod-deploy";
2981
- var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component14 {
3296
+ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component15 {
2982
3297
  constructor(project, options = {}) {
2983
3298
  super(project);
2984
3299
  this.project = project;
@@ -3259,6 +3574,7 @@ export {
3259
3574
  MonorepoProject,
3260
3575
  PROD_DEPLOY_NAME,
3261
3576
  PnpmWorkspace,
3577
+ ProjectMetadata,
3262
3578
  ROOT_CI_TASK_NAME,
3263
3579
  ROOT_TURBO_TASK_NAME,
3264
3580
  ResetTask,
@@ -3280,6 +3596,7 @@ export {
3280
3596
  jestBundle,
3281
3597
  pnpmBundle,
3282
3598
  projenBundle,
3599
+ resolveTemplateVariables,
3283
3600
  turborepoBundle,
3284
3601
  typescriptBundle,
3285
3602
  vitestBundle