@promptscript/cli 1.1.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.0](https://github.com/mrwogu/promptscript/compare/v1.2.0...v1.3.0) (2026-03-11)
9
+
10
+
11
+ ### Features
12
+
13
+ * **formatters:** add mixed models support per agent/droid ([939f75a](https://github.com/mrwogu/promptscript/commit/939f75ac4d8beb2e823007826c2e266ab5c7380b))
14
+
15
+ ## [1.2.0](https://github.com/mrwogu/promptscript/compare/v1.1.0...v1.2.0) (2026-03-11)
16
+
17
+
18
+ ### Features
19
+
20
+ * **formatters:** add Factory AI droids support (.factory/droids/) ([#94](https://github.com/mrwogu/promptscript/issues/94)) ([e7f9292](https://github.com/mrwogu/promptscript/commit/e7f929273254738b2de5334f26136c8374aedc7b))
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * **cli:** handle skill resource files without PromptScript markers ([#92](https://github.com/mrwogu/promptscript/issues/92)) ([3153498](https://github.com/mrwogu/promptscript/commit/315349865335cc1013b5c60d5c418885bf6467f8))
26
+
8
27
  ## [1.1.0](https://github.com/mrwogu/promptscript/compare/v1.0.0...v1.1.0) (2026-03-11)
9
28
 
10
29
 
package/index.js CHANGED
@@ -3331,6 +3331,7 @@ var MarkdownInstructionFormatter = class extends BaseFormatter {
3331
3331
  this.addSection(sections, this.postWork(ast, renderer));
3332
3332
  this.addSection(sections, this.documentation(ast, renderer));
3333
3333
  this.addSection(sections, this.diagrams(ast, renderer));
3334
+ this.addSection(sections, this.knowledgeContent(ast, renderer));
3334
3335
  this.addSection(sections, this.restrictions(ast, renderer));
3335
3336
  }
3336
3337
  addSection(sections, content) {
@@ -3510,6 +3511,38 @@ var MarkdownInstructionFormatter = class extends BaseFormatter {
3510
3511
  const content = renderer.renderList(items);
3511
3512
  return renderer.renderSection("Diagrams", content) + "\n";
3512
3513
  }
3514
+ /**
3515
+ * Render remaining @knowledge text content that isn't consumed by other sections.
3516
+ * Strips "## Development Commands" and "## Post-Work Verification" sub-sections
3517
+ * since those are already rendered by commands() and postWork().
3518
+ */
3519
+ knowledgeContent(ast, _renderer) {
3520
+ const knowledge = this.findBlock(ast, "knowledge");
3521
+ if (!knowledge) return null;
3522
+ const text = this.extractText(knowledge.content);
3523
+ if (!text) return null;
3524
+ const consumedHeaders = ["## Development Commands", "## Post-Work Verification"];
3525
+ let remaining = text;
3526
+ for (const header of consumedHeaders) {
3527
+ const headerIndex = remaining.indexOf(header);
3528
+ if (headerIndex === -1) continue;
3529
+ const afterHeader = remaining.indexOf("\n", headerIndex);
3530
+ if (afterHeader === -1) {
3531
+ remaining = remaining.substring(0, headerIndex).trimEnd();
3532
+ continue;
3533
+ }
3534
+ const nextSection = remaining.indexOf("\n## ", afterHeader);
3535
+ if (nextSection === -1) {
3536
+ remaining = remaining.substring(0, headerIndex).trimEnd();
3537
+ } else {
3538
+ remaining = remaining.substring(0, headerIndex) + remaining.substring(nextSection + 1);
3539
+ }
3540
+ }
3541
+ remaining = remaining.trim();
3542
+ if (!remaining) return null;
3543
+ const normalizedContent = this.stripAllIndent(remaining);
3544
+ return normalizedContent + "\n";
3545
+ }
3513
3546
  restrictions(ast, renderer) {
3514
3547
  const block = this.findBlock(ast, "restrictions");
3515
3548
  if (!block) return null;
@@ -4096,6 +4129,7 @@ var GitHubFormatter = class extends BaseFormatter {
4096
4129
  description,
4097
4130
  tools: this.parseToolsArray(obj["tools"]),
4098
4131
  model: obj["model"] ? this.valueToString(obj["model"]) : void 0,
4132
+ specModel: obj["specModel"] ? this.valueToString(obj["specModel"]) : void 0,
4099
4133
  handoffs: handoffs.length > 0 ? handoffs : void 0,
4100
4134
  content: obj["content"] ? this.valueToString(obj["content"]) : ""
4101
4135
  });
@@ -4187,6 +4221,10 @@ var GitHubFormatter = class extends BaseFormatter {
4187
4221
  if (mappedModel) {
4188
4222
  lines.push(`model: ${mappedModel}`);
4189
4223
  }
4224
+ const mappedSpecModel = this.mapModelName(config.specModel);
4225
+ if (mappedSpecModel) {
4226
+ lines.push(`specModel: ${mappedSpecModel}`);
4227
+ }
4190
4228
  if (config.handoffs && config.handoffs.length > 0) {
4191
4229
  lines.push(...this.renderHandoffsYaml(config.handoffs));
4192
4230
  }
@@ -6427,7 +6465,7 @@ var FACTORY_VERSIONS = {
6427
6465
  },
6428
6466
  full: {
6429
6467
  name: "full",
6430
- description: "Multifile + additional skill supporting files",
6468
+ description: "Multifile + droids + additional supporting files",
6431
6469
  outputPath: "AGENTS.md"
6432
6470
  }
6433
6471
  };
@@ -6441,7 +6479,7 @@ var FactoryFormatter = class extends MarkdownInstructionFormatter {
6441
6479
  mainFileHeader: "# AGENTS.md",
6442
6480
  dotDir: ".factory",
6443
6481
  skillFileName: "SKILL.md",
6444
- hasAgents: false,
6482
+ hasAgents: true,
6445
6483
  hasCommands: true,
6446
6484
  hasSkills: true,
6447
6485
  skillsInMultifile: true,
@@ -6584,6 +6622,87 @@ var FactoryFormatter = class extends MarkdownInstructionFormatter {
6584
6622
  };
6585
6623
  }
6586
6624
  // ============================================================
6625
+ // Droid File Generation (Factory-specific)
6626
+ // ============================================================
6627
+ extractAgents(ast) {
6628
+ const agentsBlock = this.findBlock(ast, "agents");
6629
+ if (!agentsBlock) return [];
6630
+ const droids = [];
6631
+ const props = this.getProps(agentsBlock.content);
6632
+ for (const [name, value] of Object.entries(props)) {
6633
+ if (value && typeof value === "object" && !Array.isArray(value)) {
6634
+ const obj = value;
6635
+ const description = obj["description"] ? this.valueToString(obj["description"]) : "";
6636
+ if (!description) continue;
6637
+ droids.push({
6638
+ name: name.replace(/\./g, "-"),
6639
+ description,
6640
+ content: obj["content"] ? this.valueToString(obj["content"]) : "",
6641
+ model: obj["model"] ? this.valueToString(obj["model"]) : void 0,
6642
+ reasoningEffort: this.parseReasoningEffort(obj["reasoningEffort"]),
6643
+ specModel: obj["specModel"] ? this.valueToString(obj["specModel"]) : void 0,
6644
+ specReasoningEffort: this.parseReasoningEffort(obj["specReasoningEffort"]),
6645
+ tools: this.parseDroidTools(obj["tools"])
6646
+ });
6647
+ }
6648
+ }
6649
+ return droids;
6650
+ }
6651
+ generateAgentFile(config) {
6652
+ const droidConfig = config;
6653
+ const lines = [];
6654
+ lines.push("---");
6655
+ lines.push(`name: ${droidConfig.name}`);
6656
+ if (droidConfig.description) {
6657
+ lines.push(`description: ${this.yamlString(droidConfig.description)}`);
6658
+ }
6659
+ if (droidConfig.model) {
6660
+ lines.push(`model: ${droidConfig.model}`);
6661
+ }
6662
+ if (droidConfig.reasoningEffort) {
6663
+ lines.push(`reasoningEffort: ${droidConfig.reasoningEffort}`);
6664
+ }
6665
+ if (droidConfig.specModel) {
6666
+ lines.push(`specModel: ${droidConfig.specModel}`);
6667
+ }
6668
+ if (droidConfig.specReasoningEffort) {
6669
+ lines.push(`specReasoningEffort: ${droidConfig.specReasoningEffort}`);
6670
+ }
6671
+ if (droidConfig.tools) {
6672
+ if (typeof droidConfig.tools === "string") {
6673
+ lines.push(`tools: ${droidConfig.tools}`);
6674
+ } else if (Array.isArray(droidConfig.tools) && droidConfig.tools.length > 0) {
6675
+ const toolsArray = droidConfig.tools.map((t) => `"${t}"`).join(", ");
6676
+ lines.push(`tools: [${toolsArray}]`);
6677
+ }
6678
+ }
6679
+ lines.push("---");
6680
+ lines.push("");
6681
+ if (droidConfig.content) {
6682
+ const dedentedContent = this.dedent(droidConfig.content);
6683
+ lines.push(dedentedContent);
6684
+ }
6685
+ return {
6686
+ path: `.factory/droids/${droidConfig.name}.md`,
6687
+ content: lines.join("\n") + "\n"
6688
+ };
6689
+ }
6690
+ parseReasoningEffort(value) {
6691
+ if (!value) return void 0;
6692
+ const str = this.valueToString(value);
6693
+ const valid = ["low", "medium", "high"];
6694
+ return valid.includes(str) ? str : void 0;
6695
+ }
6696
+ parseDroidTools(value) {
6697
+ if (!value) return void 0;
6698
+ if (typeof value === "string") return value;
6699
+ if (Array.isArray(value)) {
6700
+ const arr = value.map((v) => this.valueToString(v)).filter((s) => s.length > 0);
6701
+ return arr.length > 0 ? arr : void 0;
6702
+ }
6703
+ return this.valueToString(value);
6704
+ }
6705
+ // ============================================================
6587
6706
  // Handoff Extraction (Factory-specific)
6588
6707
  // ============================================================
6589
6708
  extractHandoffs(value) {
@@ -19200,16 +19319,170 @@ function parseSkillMd(content) {
19200
19319
  }
19201
19320
  var SKIP_FILES = /* @__PURE__ */ new Set([
19202
19321
  "SKILL.md",
19322
+ ".skillignore",
19203
19323
  ".DS_Store",
19204
19324
  "Thumbs.db",
19205
19325
  ".gitignore",
19206
19326
  ".gitkeep",
19207
19327
  ".npmrc",
19328
+ ".npmignore",
19208
19329
  ".env",
19209
19330
  ".env.local",
19210
- ".env.production"
19331
+ ".env.production",
19332
+ ".editorconfig",
19333
+ ".prettierrc",
19334
+ ".prettierrc.json",
19335
+ ".prettierrc.yaml",
19336
+ ".prettierrc.yml",
19337
+ ".prettierignore",
19338
+ ".eslintrc",
19339
+ ".eslintrc.js",
19340
+ ".eslintrc.cjs",
19341
+ ".eslintrc.json",
19342
+ ".eslintrc.yaml",
19343
+ ".eslintrc.yml",
19344
+ "eslint.config.js",
19345
+ "eslint.config.cjs",
19346
+ "eslint.config.mjs",
19347
+ "eslint.base.config.cjs",
19348
+ ".release-please-manifest.json",
19349
+ "release-please-config.json",
19350
+ "package-lock.json",
19351
+ "pnpm-lock.yaml",
19352
+ "pnpm-workspace.yaml",
19353
+ "yarn.lock",
19354
+ "tsconfig.json",
19355
+ "tsconfig.base.json",
19356
+ "tsconfig.build.json",
19357
+ "tsconfig.spec.json",
19358
+ "jest.config.ts",
19359
+ "jest.config.js",
19360
+ "vitest.config.ts",
19361
+ "vitest.config.js",
19362
+ "vite.config.ts",
19363
+ "vite.config.js",
19364
+ "nx.json",
19365
+ "project.json",
19366
+ "package.json",
19367
+ "Makefile",
19368
+ "Dockerfile",
19369
+ "docker-compose.yml",
19370
+ "docker-compose.yaml",
19371
+ ".dockerignore",
19372
+ "LICENSE",
19373
+ "LICENSE.md",
19374
+ "CHANGELOG.md",
19375
+ "CONTRIBUTING.md",
19376
+ "CODE_OF_CONDUCT.md",
19377
+ "ROADMAP.md"
19378
+ ]);
19379
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
19380
+ "node_modules",
19381
+ "__pycache__",
19382
+ ".git",
19383
+ ".svn",
19384
+ ".github",
19385
+ ".husky",
19386
+ ".vscode",
19387
+ ".idea",
19388
+ ".verdaccio",
19389
+ ".nx",
19390
+ ".cache",
19391
+ ".turbo",
19392
+ "dist",
19393
+ "build",
19394
+ "out",
19395
+ "coverage",
19396
+ "tmp",
19397
+ ".tmp",
19398
+ "e2e",
19399
+ "__tests__",
19400
+ "__mocks__",
19401
+ "__fixtures__",
19402
+ "test",
19403
+ "tests",
19404
+ "spec",
19405
+ "fixtures"
19211
19406
  ]);
19212
- var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "__pycache__", ".git", ".svn"]);
19407
+ function globToRegex(pattern) {
19408
+ const isDirPattern = pattern.endsWith("/");
19409
+ const cleanPattern = isDirPattern ? pattern.slice(0, -1) : pattern;
19410
+ const matchPath = cleanPattern.includes("/");
19411
+ let regexStr = "";
19412
+ let i = 0;
19413
+ while (i < cleanPattern.length) {
19414
+ const char = cleanPattern[i];
19415
+ if (char === "*") {
19416
+ if (cleanPattern[i + 1] === "*") {
19417
+ if (cleanPattern[i + 2] === "/") {
19418
+ regexStr += "(?:.+/)?";
19419
+ i += 3;
19420
+ } else {
19421
+ regexStr += ".*";
19422
+ i += 2;
19423
+ }
19424
+ } else {
19425
+ regexStr += "[^/]*";
19426
+ i++;
19427
+ }
19428
+ } else if (char === "?") {
19429
+ regexStr += "[^/]";
19430
+ i++;
19431
+ } else if (char === "[") {
19432
+ const closeIdx = cleanPattern.indexOf("]", i + 1);
19433
+ if (closeIdx > i) {
19434
+ regexStr += cleanPattern.slice(i, closeIdx + 1);
19435
+ i = closeIdx + 1;
19436
+ } else {
19437
+ regexStr += "\\[";
19438
+ i++;
19439
+ }
19440
+ } else if (".+^${}()|\\".includes(char)) {
19441
+ regexStr += "\\" + char;
19442
+ i++;
19443
+ } else {
19444
+ regexStr += char;
19445
+ i++;
19446
+ }
19447
+ }
19448
+ if (isDirPattern) {
19449
+ return { regex: new RegExp(`^${regexStr}(?:/|$)`), matchPath: true };
19450
+ }
19451
+ return { regex: new RegExp(`^${regexStr}$`), matchPath };
19452
+ }
19453
+ function parseSkillIgnore(content) {
19454
+ const patterns = [];
19455
+ for (const rawLine of content.split("\n")) {
19456
+ const line = rawLine.trim();
19457
+ if (!line || line.startsWith("#")) continue;
19458
+ const negated = line.startsWith("!");
19459
+ const pattern = negated ? line.slice(1) : line;
19460
+ if (!pattern) continue;
19461
+ const { regex, matchPath } = globToRegex(pattern);
19462
+ patterns.push({ regex, matchPath, negated });
19463
+ }
19464
+ return { patterns };
19465
+ }
19466
+ function isIgnoredByRules(relPath, rules) {
19467
+ const basename3 = relPath.split("/").pop() ?? relPath;
19468
+ let ignored = false;
19469
+ for (const { regex, matchPath, negated } of rules.patterns) {
19470
+ const target = matchPath ? relPath : basename3;
19471
+ if (regex.test(target)) {
19472
+ ignored = !negated;
19473
+ }
19474
+ }
19475
+ return ignored;
19476
+ }
19477
+ async function loadSkillIgnore(skillDir) {
19478
+ const ignorePath = resolve4(skillDir, ".skillignore");
19479
+ try {
19480
+ const content = await readFile6(ignorePath, "utf-8");
19481
+ return parseSkillIgnore(content);
19482
+ } catch {
19483
+ return null;
19484
+ }
19485
+ }
19213
19486
  var MAX_RESOURCE_SIZE = 1048576;
19214
19487
  var MAX_TOTAL_RESOURCE_SIZE = 10485760;
19215
19488
  var MAX_RESOURCE_COUNT = 100;
@@ -19229,6 +19502,7 @@ var noopLogger2 = {
19229
19502
  }
19230
19503
  };
19231
19504
  async function discoverSkillResources(skillDir, logger = noopLogger2) {
19505
+ const ignoreRules = await loadSkillIgnore(skillDir);
19232
19506
  const entries = await readdir2(skillDir, { recursive: true, withFileTypes: true });
19233
19507
  const resources = [];
19234
19508
  let totalSize = 0;
@@ -19246,8 +19520,13 @@ async function discoverSkillResources(skillDir, logger = noopLogger2) {
19246
19520
  if (SKIP_FILES.has(entry.name)) continue;
19247
19521
  const fullPath = resolve4(entry.parentPath, entry.name);
19248
19522
  const relPath = relative(skillDir, fullPath);
19523
+ const relPathNormalized = relPath.split(sep2).join("/");
19249
19524
  const pathSegments = relPath.split(sep2);
19250
19525
  if (pathSegments.some((s) => SKIP_DIRS.has(s))) continue;
19526
+ if (ignoreRules && isIgnoredByRules(relPathNormalized, ignoreRules)) {
19527
+ logger.debug(`Skipping resource ignored by .skillignore: ${relPath}`);
19528
+ continue;
19529
+ }
19251
19530
  if (!isSafeRelativePath(relPath)) {
19252
19531
  logger.verbose(`Skipping resource with unsafe path: ${relPath}`);
19253
19532
  continue;
@@ -20771,12 +21050,13 @@ function extractGuardReferences(content) {
20771
21050
  }
20772
21051
 
20773
21052
  // packages/validator/src/walker.ts
20774
- function walkText(ast, callback) {
21053
+ function walkText(ast, callback, options) {
21054
+ const exclude = options?.excludeProperties ? new Set(options.excludeProperties) : void 0;
20775
21055
  for (const block of ast.blocks) {
20776
- walkBlockContent(block.content, block.loc, callback);
21056
+ walkBlockContent(block.content, block.loc, callback, exclude);
20777
21057
  }
20778
21058
  for (const ext of ast.extends) {
20779
- walkBlockContent(ext.content, ext.loc, callback);
21059
+ walkBlockContent(ext.content, ext.loc, callback, exclude);
20780
21060
  }
20781
21061
  }
20782
21062
  function walkBlocks(ast, callback) {
@@ -20792,40 +21072,41 @@ function walkUses(ast, callback) {
20792
21072
  callback(use);
20793
21073
  }
20794
21074
  }
20795
- function walkBlockContent(content, fallbackLoc, callback) {
21075
+ function walkBlockContent(content, fallbackLoc, callback, exclude) {
20796
21076
  switch (content.type) {
20797
21077
  case "TextContent":
20798
21078
  callback(content.value, content.loc ?? fallbackLoc);
20799
21079
  break;
20800
21080
  case "ObjectContent":
20801
- walkObjectProperties(content.properties, content.loc ?? fallbackLoc, callback);
21081
+ walkObjectProperties(content.properties, content.loc ?? fallbackLoc, callback, exclude);
20802
21082
  break;
20803
21083
  case "ArrayContent":
20804
- walkArrayElements(content.elements, content.loc ?? fallbackLoc, callback);
21084
+ walkArrayElements(content.elements, content.loc ?? fallbackLoc, callback, exclude);
20805
21085
  break;
20806
21086
  case "MixedContent":
20807
21087
  if (content.text) {
20808
21088
  callback(content.text.value, content.text.loc ?? fallbackLoc);
20809
21089
  }
20810
- walkObjectProperties(content.properties, content.loc ?? fallbackLoc, callback);
21090
+ walkObjectProperties(content.properties, content.loc ?? fallbackLoc, callback, exclude);
20811
21091
  break;
20812
21092
  }
20813
21093
  }
20814
- function walkObjectProperties(properties, loc, callback) {
20815
- for (const value of Object.values(properties)) {
20816
- walkValue(value, loc, callback);
21094
+ function walkObjectProperties(properties, loc, callback, exclude) {
21095
+ for (const [key, value] of Object.entries(properties)) {
21096
+ if (exclude?.has(key)) continue;
21097
+ walkValue(value, loc, callback, exclude);
20817
21098
  }
20818
21099
  }
20819
- function walkArrayElements(elements, loc, callback) {
21100
+ function walkArrayElements(elements, loc, callback, exclude) {
20820
21101
  for (const element of elements) {
20821
- walkValue(element, loc, callback);
21102
+ walkValue(element, loc, callback, exclude);
20822
21103
  }
20823
21104
  }
20824
- function walkValue(value, loc, callback) {
21105
+ function walkValue(value, loc, callback, exclude) {
20825
21106
  if (typeof value === "string") {
20826
21107
  callback(value, loc);
20827
21108
  } else if (Array.isArray(value)) {
20828
- walkArrayElements(value, loc, callback);
21109
+ walkArrayElements(value, loc, callback, exclude);
20829
21110
  } else if (value !== null && typeof value === "object") {
20830
21111
  if ("type" in value) {
20831
21112
  const typed = value;
@@ -20833,9 +21114,27 @@ function walkValue(value, loc, callback) {
20833
21114
  callback(typed.value, typed.loc ?? loc);
20834
21115
  }
20835
21116
  } else {
20836
- walkObjectProperties(value, loc, callback);
21117
+ walkObjectProperties(value, loc, callback, exclude);
21118
+ }
21119
+ }
21120
+ }
21121
+ function offsetLocation(baseLoc, text, charIndex) {
21122
+ let line = baseLoc.line;
21123
+ let column = baseLoc.column;
21124
+ for (let i = 0; i < charIndex && i < text.length; i++) {
21125
+ if (text[i] === "\n") {
21126
+ line++;
21127
+ column = 1;
21128
+ } else {
21129
+ column++;
20837
21130
  }
20838
21131
  }
21132
+ return {
21133
+ file: baseLoc.file,
21134
+ line,
21135
+ column,
21136
+ offset: baseLoc.offset !== void 0 ? baseLoc.offset + charIndex : void 0
21137
+ };
20839
21138
  }
20840
21139
  function hasContent(content) {
20841
21140
  switch (content.type) {
@@ -21225,6 +21524,12 @@ var BLOCKED_PATTERNS_ALL_LANGUAGES = [
21225
21524
  ...BLOCKED_PATTERNS_RO,
21226
21525
  ...BLOCKED_PATTERNS_HE
21227
21526
  ];
21527
+ var NEGATION_PREFIX = /(?:prevent|avoid|block|detect|flag|report|stop|prohibit|anti[- ]|against|don['']?t|do\s+not|never|must\s+not|should\s+not|cannot|warning.*about|protection\s+(?:from|against))\s+/i;
21528
+ function isNegatedMatch(text, matchIndex) {
21529
+ const lookbackStart = Math.max(0, matchIndex - 60);
21530
+ const prefix = text.slice(lookbackStart, matchIndex);
21531
+ return NEGATION_PREFIX.test(prefix);
21532
+ }
21228
21533
  var blockedPatterns = {
21229
21534
  id: "PS005",
21230
21535
  name: "blocked-patterns",
@@ -21237,17 +21542,30 @@ var blockedPatterns = {
21237
21542
  (p) => typeof p === "string" ? new RegExp(p, "i") : p
21238
21543
  )
21239
21544
  ];
21240
- walkText(ctx.ast, (text, loc) => {
21241
- for (const pattern of patterns) {
21242
- if (pattern.test(text)) {
21243
- ctx.report({
21244
- message: `Blocked pattern detected: ${pattern.source}`,
21245
- location: loc,
21246
- suggestion: "Remove or rephrase the flagged content"
21247
- });
21545
+ walkText(
21546
+ ctx.ast,
21547
+ (text, loc) => {
21548
+ for (const pattern of patterns) {
21549
+ const globalPattern = new RegExp(
21550
+ pattern.source,
21551
+ pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g"
21552
+ );
21553
+ let match;
21554
+ while ((match = globalPattern.exec(text)) !== null) {
21555
+ if (isNegatedMatch(text, match.index)) {
21556
+ continue;
21557
+ }
21558
+ ctx.report({
21559
+ message: `Blocked pattern detected: ${pattern.source}`,
21560
+ location: offsetLocation(loc, text, match.index),
21561
+ suggestion: "Remove or rephrase the flagged content"
21562
+ });
21563
+ break;
21564
+ }
21248
21565
  }
21249
- }
21250
- });
21566
+ },
21567
+ { excludeProperties: ["resources"] }
21568
+ );
21251
21569
  }
21252
21570
  };
21253
21571
 
@@ -21640,87 +21958,91 @@ var suspiciousUrls = {
21640
21958
  description: "Detect suspicious URLs (HTTP, shorteners, credential parameters, IDN homograph attacks)",
21641
21959
  defaultSeverity: "warning",
21642
21960
  validate: (ctx) => {
21643
- walkText(ctx.ast, (text, loc) => {
21644
- const httpMatches = text.match(HTTP_URL_PATTERN);
21645
- if (httpMatches) {
21646
- for (const match of httpMatches) {
21647
- ctx.report({
21648
- message: `Insecure HTTP URL detected: ${match}`,
21649
- location: loc,
21650
- suggestion: "Use HTTPS instead of HTTP for secure communication"
21651
- });
21652
- }
21653
- }
21654
- for (const pattern of URL_SHORTENER_PATTERNS) {
21655
- pattern.lastIndex = 0;
21656
- const shortenerMatches = text.match(pattern);
21657
- if (shortenerMatches) {
21658
- for (const match of shortenerMatches) {
21961
+ walkText(
21962
+ ctx.ast,
21963
+ (text, loc) => {
21964
+ const httpMatches = text.match(HTTP_URL_PATTERN);
21965
+ if (httpMatches) {
21966
+ for (const match of httpMatches) {
21659
21967
  ctx.report({
21660
- message: `URL shortener detected: ${match}`,
21968
+ message: `Insecure HTTP URL detected: ${match}`,
21661
21969
  location: loc,
21662
- suggestion: "Use full URLs instead of shorteners to ensure destination transparency"
21970
+ suggestion: "Use HTTPS instead of HTTP for secure communication"
21663
21971
  });
21664
21972
  }
21665
21973
  }
21666
- }
21667
- const paramMatches = text.match(SUSPICIOUS_PARAM_PATTERN);
21668
- if (paramMatches) {
21669
- for (const match of paramMatches) {
21670
- ctx.report({
21671
- message: `URL with suspicious credential parameter detected: ${match}`,
21672
- location: loc,
21673
- suggestion: "Avoid embedding credentials or tokens in URLs"
21674
- });
21675
- }
21676
- }
21677
- PUNYCODE_URL_PATTERN.lastIndex = 0;
21678
- const punycodeMatches = text.match(PUNYCODE_URL_PATTERN);
21679
- if (punycodeMatches) {
21680
- for (const match of punycodeMatches) {
21681
- const domain = extractDomain(match);
21682
- if (domain) {
21683
- const impersonatedService = detectImpersonatedService(domain);
21684
- if (impersonatedService) {
21974
+ for (const pattern of URL_SHORTENER_PATTERNS) {
21975
+ pattern.lastIndex = 0;
21976
+ const shortenerMatches = text.match(pattern);
21977
+ if (shortenerMatches) {
21978
+ for (const match of shortenerMatches) {
21685
21979
  ctx.report({
21686
- message: `Potential IDN homograph attack detected: ${match} may be impersonating "${impersonatedService}"`,
21980
+ message: `URL shortener detected: ${match}`,
21687
21981
  location: loc,
21688
- suggestion: "Punycode domains (xn--) can visually impersonate legitimate sites. Verify the actual domain carefully."
21689
- });
21690
- } else {
21691
- ctx.report({
21692
- message: `Punycode domain detected: ${match}`,
21693
- location: loc,
21694
- suggestion: "Punycode domains (xn--) can be legitimate international domains, but may also be used for homograph attacks. Verify the domain is intentional."
21982
+ suggestion: "Use full URLs instead of shorteners to ensure destination transparency"
21695
21983
  });
21696
21984
  }
21697
21985
  }
21698
21986
  }
21699
- }
21700
- ALL_URLS_PATTERN.lastIndex = 0;
21701
- const allUrls = text.match(ALL_URLS_PATTERN);
21702
- if (allUrls) {
21703
- for (const url of allUrls) {
21704
- const domain = extractDomain(url);
21705
- if (domain) {
21706
- const { isAttack, impersonatedService, mixedScripts } = checkHomographAttack(domain);
21707
- if (isAttack && impersonatedService) {
21708
- ctx.report({
21709
- message: `IDN homograph attack detected: "${domain}" uses deceptive characters to impersonate "${impersonatedService}"`,
21710
- location: loc,
21711
- suggestion: `This domain uses ${mixedScripts ? `mixed scripts (${mixedScripts.join("+")})` : "homoglyph characters"} to visually mimic a legitimate service. Do not trust this URL.`
21712
- });
21713
- } else if (mixedScripts && mixedScripts.length > 1) {
21714
- ctx.report({
21715
- message: `Mixed script domain detected: "${domain}" uses ${mixedScripts.join(" + ")} characters`,
21716
- location: loc,
21717
- suggestion: "Domains mixing different character scripts (e.g., Latin + Cyrillic) may be attempts to deceive users. Verify this is intentional."
21718
- });
21987
+ const paramMatches = text.match(SUSPICIOUS_PARAM_PATTERN);
21988
+ if (paramMatches) {
21989
+ for (const match of paramMatches) {
21990
+ ctx.report({
21991
+ message: `URL with suspicious credential parameter detected: ${match}`,
21992
+ location: loc,
21993
+ suggestion: "Avoid embedding credentials or tokens in URLs"
21994
+ });
21995
+ }
21996
+ }
21997
+ PUNYCODE_URL_PATTERN.lastIndex = 0;
21998
+ const punycodeMatches = text.match(PUNYCODE_URL_PATTERN);
21999
+ if (punycodeMatches) {
22000
+ for (const match of punycodeMatches) {
22001
+ const domain = extractDomain(match);
22002
+ if (domain) {
22003
+ const impersonatedService = detectImpersonatedService(domain);
22004
+ if (impersonatedService) {
22005
+ ctx.report({
22006
+ message: `Potential IDN homograph attack detected: ${match} may be impersonating "${impersonatedService}"`,
22007
+ location: loc,
22008
+ suggestion: "Punycode domains (xn--) can visually impersonate legitimate sites. Verify the actual domain carefully."
22009
+ });
22010
+ } else {
22011
+ ctx.report({
22012
+ message: `Punycode domain detected: ${match}`,
22013
+ location: loc,
22014
+ suggestion: "Punycode domains (xn--) can be legitimate international domains, but may also be used for homograph attacks. Verify the domain is intentional."
22015
+ });
22016
+ }
21719
22017
  }
21720
22018
  }
21721
22019
  }
21722
- }
21723
- });
22020
+ ALL_URLS_PATTERN.lastIndex = 0;
22021
+ const allUrls = text.match(ALL_URLS_PATTERN);
22022
+ if (allUrls) {
22023
+ for (const url of allUrls) {
22024
+ const domain = extractDomain(url);
22025
+ if (domain) {
22026
+ const { isAttack, impersonatedService, mixedScripts } = checkHomographAttack(domain);
22027
+ if (isAttack && impersonatedService) {
22028
+ ctx.report({
22029
+ message: `IDN homograph attack detected: "${domain}" uses deceptive characters to impersonate "${impersonatedService}"`,
22030
+ location: loc,
22031
+ suggestion: `This domain uses ${mixedScripts ? `mixed scripts (${mixedScripts.join("+")})` : "homoglyph characters"} to visually mimic a legitimate service. Do not trust this URL.`
22032
+ });
22033
+ } else if (mixedScripts && mixedScripts.length > 1) {
22034
+ ctx.report({
22035
+ message: `Mixed script domain detected: "${domain}" uses ${mixedScripts.join(" + ")} characters`,
22036
+ location: loc,
22037
+ suggestion: "Domains mixing different character scripts (e.g., Latin + Cyrillic) may be attempts to deceive users. Verify this is intentional."
22038
+ });
22039
+ }
22040
+ }
22041
+ }
22042
+ }
22043
+ },
22044
+ { excludeProperties: ["resources"] }
22045
+ );
21724
22046
  }
21725
22047
  };
21726
22048
 
@@ -21760,17 +22082,21 @@ var authorityInjection = {
21760
22082
  description: "Detect authoritative override phrases that may indicate prompt injection",
21761
22083
  defaultSeverity: "error",
21762
22084
  validate: (ctx) => {
21763
- walkText(ctx.ast, (text, loc) => {
21764
- for (const pattern of AUTHORITY_PATTERNS) {
21765
- if (pattern.test(text)) {
21766
- ctx.report({
21767
- message: `Authority injection pattern detected: ${pattern.source}`,
21768
- location: loc,
21769
- suggestion: "Remove authoritative override language that could be used for prompt injection"
21770
- });
22085
+ walkText(
22086
+ ctx.ast,
22087
+ (text, loc) => {
22088
+ for (const pattern of AUTHORITY_PATTERNS) {
22089
+ if (pattern.test(text)) {
22090
+ ctx.report({
22091
+ message: `Authority injection pattern detected: ${pattern.source}`,
22092
+ location: loc,
22093
+ suggestion: "Remove authoritative override language that could be used for prompt injection"
22094
+ });
22095
+ }
21771
22096
  }
21772
- }
21773
- });
22097
+ },
22098
+ { excludeProperties: ["resources"] }
22099
+ );
21774
22100
  }
21775
22101
  };
21776
22102
 
@@ -22156,26 +22482,30 @@ var obfuscatedContent = {
22156
22482
  description: "Detect obfuscated content using sanitization pipeline (decode and check all encodings)",
22157
22483
  defaultSeverity: "warning",
22158
22484
  validate: (ctx) => {
22159
- walkText(ctx.ast, (text, loc) => {
22160
- const encodedMatches = detectAndDecodeEncodings(text);
22161
- for (const match of encodedMatches) {
22162
- ctx.report({
22163
- message: `Malicious content detected in ${match.type}: ${match.issues.join(", ")}. Decoded: "${match.decoded.substring(0, 50)}${match.decoded.length > 50 ? "..." : ""}"`,
22164
- location: loc,
22165
- suggestion: "Encoded content is automatically decoded and checked for security patterns. Remove the malicious payload or use plain text."
22166
- });
22167
- }
22168
- const hasLongBase64 = (text.match(BASE64_PATTERN) || []).some(
22169
- (m) => m.length >= MIN_ENCODED_LENGTH * 2 && !isLikelyLegitimateBase64(text, m)
22170
- );
22171
- if (hasLongBase64 && encodedMatches.length === 0) {
22172
- ctx.report({
22173
- message: "Long Base64-encoded content detected that may hide payloads",
22174
- location: loc,
22175
- suggestion: "If this is intentional, consider using plain text or documenting the purpose"
22176
- });
22177
- }
22178
- });
22485
+ walkText(
22486
+ ctx.ast,
22487
+ (text, loc) => {
22488
+ const encodedMatches = detectAndDecodeEncodings(text);
22489
+ for (const match of encodedMatches) {
22490
+ ctx.report({
22491
+ message: `Malicious content detected in ${match.type}: ${match.issues.join(", ")}. Decoded: "${match.decoded.substring(0, 50)}${match.decoded.length > 50 ? "..." : ""}"`,
22492
+ location: loc,
22493
+ suggestion: "Encoded content is automatically decoded and checked for security patterns. Remove the malicious payload or use plain text."
22494
+ });
22495
+ }
22496
+ const hasLongBase64 = (text.match(BASE64_PATTERN) || []).some(
22497
+ (m) => m.length >= MIN_ENCODED_LENGTH * 2 && !isLikelyLegitimateBase64(text, m)
22498
+ );
22499
+ if (hasLongBase64 && encodedMatches.length === 0) {
22500
+ ctx.report({
22501
+ message: "Long Base64-encoded content detected that may hide payloads",
22502
+ location: loc,
22503
+ suggestion: "If this is intentional, consider using plain text or documenting the purpose"
22504
+ });
22505
+ }
22506
+ },
22507
+ { excludeProperties: ["resources"] }
22508
+ );
22179
22509
  }
22180
22510
  };
22181
22511
 
@@ -22292,22 +22622,32 @@ var GREEK_LOOKALIKES = /* @__PURE__ */ new Map([
22292
22622
  ["\u0396", "\u0396 (Greek) looks like Z (Latin)"]
22293
22623
  ]);
22294
22624
  var EXCESSIVE_COMBINING_PATTERN = /[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u064B-\u065F]{4,}/;
22295
- var LATIN_PATTERN = /[a-zA-Z]/;
22625
+ var LATIN_CHAR = /[a-zA-Z]/;
22626
+ var CYRILLIC_CHAR = /[\u0400-\u04FF]/;
22627
+ var GREEK_CHAR = /[\u0370-\u03FF]/;
22628
+ var WORD_PATTERN = /[a-zA-Z\u0370-\u03FF\u0400-\u04FF]+/g;
22296
22629
  function findHomographAttacks(text) {
22297
22630
  const results = [];
22298
- const hasLatin = LATIN_PATTERN.test(text);
22299
- if (!hasLatin) {
22300
- return results;
22301
- }
22302
- for (let i = 0; i < text.length; i++) {
22303
- const char = text[i];
22304
- const cyrillicDesc = CYRILLIC_LOOKALIKES.get(char);
22305
- if (cyrillicDesc) {
22306
- results.push({ char, description: cyrillicDesc, index: i });
22307
- }
22308
- const greekDesc = GREEK_LOOKALIKES.get(char);
22309
- if (greekDesc) {
22310
- results.push({ char, description: greekDesc, index: i });
22631
+ let wordMatch;
22632
+ const wordPattern = new RegExp(WORD_PATTERN.source, WORD_PATTERN.flags);
22633
+ while ((wordMatch = wordPattern.exec(text)) !== null) {
22634
+ const word = wordMatch[0];
22635
+ const wordStart = wordMatch.index;
22636
+ const hasLatin = LATIN_CHAR.test(word);
22637
+ const hasCyrillic = CYRILLIC_CHAR.test(word);
22638
+ const hasGreek = GREEK_CHAR.test(word);
22639
+ if (!hasLatin) continue;
22640
+ if (!hasCyrillic && !hasGreek) continue;
22641
+ for (let i = 0; i < word.length; i++) {
22642
+ const char = word[i];
22643
+ const cyrillicDesc = CYRILLIC_LOOKALIKES.get(char);
22644
+ if (cyrillicDesc) {
22645
+ results.push({ char, description: cyrillicDesc, index: wordStart + i });
22646
+ }
22647
+ const greekDesc = GREEK_LOOKALIKES.get(char);
22648
+ if (greekDesc) {
22649
+ results.push({ char, description: greekDesc, index: wordStart + i });
22650
+ }
22311
22651
  }
22312
22652
  }
22313
22653
  return results;
@@ -22340,47 +22680,51 @@ var unicodeSecurity = {
22340
22680
  description: "Detect Unicode-based attacks (bidi overrides, zero-width, homoglyphs)",
22341
22681
  defaultSeverity: "error",
22342
22682
  validate: (ctx) => {
22343
- walkText(ctx.ast, (text, loc) => {
22344
- const bidiChars = findBidiChars(text);
22345
- if (bidiChars.length > 0) {
22346
- const descriptions = bidiChars.slice(0, 3).map((b) => b.description).join(", ");
22347
- const suffix = bidiChars.length > 3 ? ` and ${bidiChars.length - 3} more` : "";
22348
- ctx.report({
22349
- message: `Bidirectional text override detected: ${descriptions}${suffix}`,
22350
- location: loc,
22351
- suggestion: "Remove bidirectional override characters that may hide malicious content"
22352
- });
22353
- }
22354
- const zeroWidthChars = findZeroWidthChars(text);
22355
- if (zeroWidthChars.length > 0) {
22356
- const descriptions = zeroWidthChars.slice(0, 3).map((z) => z.description).join(", ");
22357
- const suffix = zeroWidthChars.length > 3 ? ` and ${zeroWidthChars.length - 3} more` : "";
22358
- ctx.report({
22359
- message: `Zero-width characters detected: ${descriptions}${suffix}`,
22360
- location: loc,
22361
- suggestion: "Remove zero-width characters that may be used to evade pattern matching"
22362
- });
22363
- }
22364
- const homographs = findHomographAttacks(text);
22365
- if (homographs.length > 0) {
22366
- const descriptions = homographs.slice(0, 3).map((h) => h.description).join(", ");
22367
- const suffix = homographs.length > 3 ? ` and ${homographs.length - 3} more` : "";
22368
- ctx.report({
22369
- message: `Potential homograph attack: ${descriptions}${suffix}`,
22370
- location: loc,
22371
- suggestion: "Replace lookalike Cyrillic/Greek characters with Latin equivalents"
22372
- });
22373
- }
22374
- if (EXCESSIVE_COMBINING_PATTERN.test(text)) {
22375
- const match = text.match(EXCESSIVE_COMBINING_PATTERN);
22376
- const charCodes = match ? Array.from(match[0]).slice(0, 4).map((c) => `U+${c.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0")}`).join(", ") : "unknown";
22377
- ctx.report({
22378
- message: `Excessive combining characters (Zalgo text) detected: ${charCodes}...`,
22379
- location: loc,
22380
- suggestion: "Remove excessive diacritical marks that may be used to obscure content"
22381
- });
22382
- }
22383
- });
22683
+ walkText(
22684
+ ctx.ast,
22685
+ (text, loc) => {
22686
+ const bidiChars = findBidiChars(text);
22687
+ if (bidiChars.length > 0) {
22688
+ const descriptions = bidiChars.slice(0, 3).map((b) => b.description).join(", ");
22689
+ const suffix = bidiChars.length > 3 ? ` and ${bidiChars.length - 3} more` : "";
22690
+ ctx.report({
22691
+ message: `Bidirectional text override detected: ${descriptions}${suffix}`,
22692
+ location: offsetLocation(loc, text, bidiChars[0].index),
22693
+ suggestion: "Remove bidirectional override characters that may hide malicious content"
22694
+ });
22695
+ }
22696
+ const zeroWidthChars = findZeroWidthChars(text);
22697
+ if (zeroWidthChars.length > 0) {
22698
+ const descriptions = zeroWidthChars.slice(0, 3).map((z) => z.description).join(", ");
22699
+ const suffix = zeroWidthChars.length > 3 ? ` and ${zeroWidthChars.length - 3} more` : "";
22700
+ ctx.report({
22701
+ message: `Zero-width characters detected: ${descriptions}${suffix}`,
22702
+ location: offsetLocation(loc, text, zeroWidthChars[0].index),
22703
+ suggestion: "Remove zero-width characters that may be used to evade pattern matching"
22704
+ });
22705
+ }
22706
+ const homographs = findHomographAttacks(text);
22707
+ if (homographs.length > 0) {
22708
+ const descriptions = homographs.slice(0, 3).map((h) => h.description).join(", ");
22709
+ const suffix = homographs.length > 3 ? ` and ${homographs.length - 3} more` : "";
22710
+ ctx.report({
22711
+ message: `Potential homograph attack: ${descriptions}${suffix}`,
22712
+ location: offsetLocation(loc, text, homographs[0].index),
22713
+ suggestion: "Replace lookalike Cyrillic/Greek characters with Latin equivalents"
22714
+ });
22715
+ }
22716
+ const combiningMatch = EXCESSIVE_COMBINING_PATTERN.exec(text);
22717
+ if (combiningMatch) {
22718
+ const charCodes = Array.from(combiningMatch[0]).slice(0, 4).map((c) => `U+${c.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0")}`).join(", ");
22719
+ ctx.report({
22720
+ message: `Excessive combining characters (Zalgo text) detected: ${charCodes}...`,
22721
+ location: offsetLocation(loc, text, combiningMatch.index),
22722
+ suggestion: "Remove excessive diacritical marks that may be used to obscure content"
22723
+ });
22724
+ }
22725
+ },
22726
+ { excludeProperties: ["resources"] }
22727
+ );
22384
22728
  }
22385
22729
  };
22386
22730
 
@@ -23181,8 +23525,14 @@ async function writeOutputs(outputs, options, _config, services) {
23181
23525
  result.written.push(outputPath);
23182
23526
  }
23183
23527
  } else {
23184
- ConsoleOutput.warning(`Would conflict: ${outputPath} (not generated by PromptScript)`);
23185
- result.written.push(outputPath);
23528
+ const existingContent = await readFile7(outputPath, "utf-8");
23529
+ if (existingContent === output.content) {
23530
+ ConsoleOutput.dryRun(`Unchanged: ${outputPath}`);
23531
+ result.unchanged.push(outputPath);
23532
+ } else {
23533
+ ConsoleOutput.warning(`Would conflict: ${outputPath} (not generated by PromptScript)`);
23534
+ result.written.push(outputPath);
23535
+ }
23186
23536
  }
23187
23537
  } else {
23188
23538
  ConsoleOutput.dryRun(`Would write: ${outputPath}`);
@@ -23213,6 +23563,17 @@ async function writeOutputs(outputs, options, _config, services) {
23213
23563
  result.written.push(outputPath);
23214
23564
  continue;
23215
23565
  }
23566
+ let contentMatches = false;
23567
+ try {
23568
+ const existingContent = await readFile7(outputPath, "utf-8");
23569
+ contentMatches = existingContent === output.content;
23570
+ } catch {
23571
+ }
23572
+ if (contentMatches) {
23573
+ ConsoleOutput.unchanged(outputPath);
23574
+ result.unchanged.push(outputPath);
23575
+ continue;
23576
+ }
23216
23577
  if (options.force || overwriteAll) {
23217
23578
  await mkdir2(dirname6(outputPath), { recursive: true });
23218
23579
  await writeFile2(outputPath, output.content, "utf-8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptscript/cli",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "CLI for PromptScript - standardize AI instructions across GitHub Copilot, Claude, Cursor and other AI tools",
5
5
  "keywords": [
6
6
  "cli",
@@ -215,7 +215,8 @@ userInvocable, allowedTools, context ("fork" or "inherit"), agent.
215
215
 
216
216
  ### @agents
217
217
 
218
- Custom subagent definitions:
218
+ Custom subagent definitions. Compiles to `.claude/agents/` for Claude Code,
219
+ `.github/agents/` for GitHub Copilot, `.factory/droids/` for Factory AI, etc.
219
220
 
220
221
  ```
221
222
  @agents {
@@ -229,6 +230,14 @@ Custom subagent definitions:
229
230
  }
230
231
  ```
231
232
 
233
+ Supports mixed models per agent: `specModel` sets a different model for
234
+ Specification/planning mode (GitHub, Factory), `specReasoningEffort` sets reasoning
235
+ effort for the spec model (Factory only, values: "low", "medium", "high").
236
+
237
+ Factory AI droids support additional properties: `model` (any model ID or "inherit"),
238
+ `reasoningEffort` ("low", "medium", "high"), and `tools` (category name like "read-only"
239
+ or array of tool IDs).
240
+
232
241
  ### @knowledge
233
242
 
234
243
  Reference documentation as triple-quoted text. Used for command references,
@@ -331,21 +340,21 @@ prs diff --target claude # Show compilation diff
331
340
 
332
341
  38 supported targets. Key examples:
333
342
 
334
- | Target | Main File | Skills |
335
- | ----------- | ------------------------------- | ------------------------------ |
336
- | GitHub | .github/copilot-instructions.md | .github/skills/\*/SKILL.md |
337
- | Claude | CLAUDE.md | .claude/skills/\*/SKILL.md |
338
- | Cursor | .cursor/rules/project.mdc | .cursor/commands/\*.md |
339
- | Antigravity | .agent/rules/project.md | .agent/rules/\*.md |
340
- | Factory | AGENTS.md | .factory/skills/\*/SKILL.md |
341
- | OpenCode | OPENCODE.md | .opencode/skills/\*/SKILL.md |
342
- | Gemini | GEMINI.md | .gemini/skills/\*/skill.md |
343
- | Windsurf | .windsurf/rules/project.md | .windsurf/skills/\*/SKILL.md |
344
- | Cline | .clinerules | .agents/skills/\*/SKILL.md |
345
- | Roo Code | .roorules | .roo/skills/\*/SKILL.md |
346
- | Codex | AGENTS.md | .agents/skills/\*/SKILL.md |
347
- | Continue | .continue/rules/project.md | .continue/skills/\*/SKILL.md |
348
- | + 26 more | | See full list in documentation |
343
+ | Target | Main File | Skills |
344
+ | ----------- | ------------------------------- | -------------------------------------------------- |
345
+ | GitHub | .github/copilot-instructions.md | .github/skills/\*/SKILL.md |
346
+ | Claude | CLAUDE.md | .claude/skills/\*/SKILL.md |
347
+ | Cursor | .cursor/rules/project.mdc | .cursor/commands/\*.md |
348
+ | Antigravity | .agent/rules/project.md | .agent/rules/\*.md |
349
+ | Factory | AGENTS.md | .factory/skills/\*/SKILL.md, .factory/droids/\*.md |
350
+ | OpenCode | OPENCODE.md | .opencode/skills/\*/SKILL.md |
351
+ | Gemini | GEMINI.md | .gemini/skills/\*/skill.md |
352
+ | Windsurf | .windsurf/rules/project.md | .windsurf/skills/\*/SKILL.md |
353
+ | Cline | .clinerules | .agents/skills/\*/SKILL.md |
354
+ | Roo Code | .roorules | .roo/skills/\*/SKILL.md |
355
+ | Codex | AGENTS.md | .agents/skills/\*/SKILL.md |
356
+ | Continue | .continue/rules/project.md | .continue/skills/\*/SKILL.md |
357
+ | + 26 more | | See full list in documentation |
349
358
 
350
359
  ## Project Organization
351
360
 
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../../../packages/cli/src/commands/compile.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQlD,OAAO,EAAE,KAAK,WAAW,EAAyB,MAAM,gBAAgB,CAAC;AAwTzE;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,QAAQ,GAAE,WAAqC,GAC9C,OAAO,CAAC,IAAI,CAAC,CA8Gf"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../../../packages/cli/src/commands/compile.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAQlD,OAAO,EAAE,KAAK,WAAW,EAAyB,MAAM,gBAAgB,CAAC;AAgVzE;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,QAAQ,GAAE,WAAqC,GAC9C,OAAO,CAAC,IAAI,CAAC,CA8Gf"}