@promptscript/cli 1.20.0 → 1.21.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/index.js +1218 -174
- package/package.json +3 -3
- package/skills/promptscript/SKILL.md +29 -3
- package/src/commands/check.d.ts.map +1 -1
- package/src/commands/compile.d.ts.map +1 -1
- package/src/commands/diff.d.ts.map +1 -1
- package/src/commands/validate.d.ts.map +1 -1
- package/src/generated/promptscript-skill.d.ts +1 -1
- package/src/generated/promptscript-skill.d.ts.map +1 -1
- package/src/services.d.ts +2 -2
- package/src/services.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -311,6 +311,13 @@ var init_manifest = __esm({
|
|
|
311
311
|
}
|
|
312
312
|
});
|
|
313
313
|
|
|
314
|
+
// packages/core/src/types/models.ts
|
|
315
|
+
var init_models = __esm({
|
|
316
|
+
"packages/core/src/types/models.ts"() {
|
|
317
|
+
"use strict";
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
314
321
|
// packages/core/src/types/policy.ts
|
|
315
322
|
var init_policy = __esm({
|
|
316
323
|
"packages/core/src/types/policy.ts"() {
|
|
@@ -362,6 +369,7 @@ var init_types = __esm({
|
|
|
362
369
|
init_convention();
|
|
363
370
|
init_lockfile();
|
|
364
371
|
init_manifest();
|
|
372
|
+
init_models();
|
|
365
373
|
init_policy();
|
|
366
374
|
init_prettier();
|
|
367
375
|
init_provenance();
|
|
@@ -7649,6 +7657,651 @@ var init_agent_capabilities = __esm({
|
|
|
7649
7657
|
}
|
|
7650
7658
|
});
|
|
7651
7659
|
|
|
7660
|
+
// packages/core/src/model-profiles.ts
|
|
7661
|
+
function release(provider, family, version, id, displayName, fields = {}) {
|
|
7662
|
+
return {
|
|
7663
|
+
id,
|
|
7664
|
+
provider,
|
|
7665
|
+
family,
|
|
7666
|
+
version,
|
|
7667
|
+
displayName,
|
|
7668
|
+
apiId: id,
|
|
7669
|
+
aliases: [],
|
|
7670
|
+
// A release with a successor is legacy unless it says otherwise.
|
|
7671
|
+
status: fields.successor === void 0 ? "current" : "legacy",
|
|
7672
|
+
targets: {},
|
|
7673
|
+
...fields
|
|
7674
|
+
};
|
|
7675
|
+
}
|
|
7676
|
+
function claude(line, version, fields = {}) {
|
|
7677
|
+
const { id = `claude-${line}-${version.replaceAll(".", "-")}`, displayName, ...rest } = fields;
|
|
7678
|
+
const name = `${line.charAt(0).toUpperCase()}${line.slice(1)}`;
|
|
7679
|
+
return release(
|
|
7680
|
+
"anthropic",
|
|
7681
|
+
`claude-${line}`,
|
|
7682
|
+
version,
|
|
7683
|
+
id,
|
|
7684
|
+
displayName ?? `Claude ${name} ${version}`,
|
|
7685
|
+
{
|
|
7686
|
+
aliases: [`${line}-${version}`],
|
|
7687
|
+
...rest
|
|
7688
|
+
}
|
|
7689
|
+
);
|
|
7690
|
+
}
|
|
7691
|
+
var openai, google, xai, MODEL_PROFILES;
|
|
7692
|
+
var init_model_profiles = __esm({
|
|
7693
|
+
"packages/core/src/model-profiles.ts"() {
|
|
7694
|
+
"use strict";
|
|
7695
|
+
openai = (family, version, id, displayName, fields) => release("openai", family, version, id, displayName, fields);
|
|
7696
|
+
google = (family, version, id, displayName, fields) => release("google", family, version, id, displayName, fields);
|
|
7697
|
+
xai = (family, version, id, displayName, fields) => release("xai", family, version, id, displayName, fields);
|
|
7698
|
+
MODEL_PROFILES = [
|
|
7699
|
+
// Anthropic
|
|
7700
|
+
claude("opus", "4", {
|
|
7701
|
+
apiId: "claude-opus-4-20250514",
|
|
7702
|
+
status: "retired",
|
|
7703
|
+
successor: "claude-opus-4-1",
|
|
7704
|
+
releaseDate: "2025-05-22",
|
|
7705
|
+
retirementDate: "2026-06-15"
|
|
7706
|
+
}),
|
|
7707
|
+
claude("opus", "4.1", {
|
|
7708
|
+
apiId: "claude-opus-4-1-20250805",
|
|
7709
|
+
status: "retired",
|
|
7710
|
+
successor: "claude-opus-4-5",
|
|
7711
|
+
releaseDate: "2025-08-05",
|
|
7712
|
+
retirementDate: "2026-08-05"
|
|
7713
|
+
}),
|
|
7714
|
+
claude("opus", "4.5", {
|
|
7715
|
+
apiId: "claude-opus-4-5-20251101",
|
|
7716
|
+
successor: "claude-opus-4-6",
|
|
7717
|
+
releaseDate: "2025-11-24"
|
|
7718
|
+
}),
|
|
7719
|
+
claude("opus", "4.6", { successor: "claude-opus-4-7" }),
|
|
7720
|
+
claude("opus", "4.7", { successor: "claude-opus-4-8" }),
|
|
7721
|
+
claude("opus", "4.8", { successor: "claude-opus-5" }),
|
|
7722
|
+
claude("opus", "5", { successor: "claude-opus-5-5", releaseDate: "2026-07-24" }),
|
|
7723
|
+
claude("opus", "5.5", { releaseDate: "2026-09-22" }),
|
|
7724
|
+
claude("sonnet", "3.5", {
|
|
7725
|
+
id: "claude-3-5-sonnet",
|
|
7726
|
+
apiId: "claude-3-5-sonnet-20241022",
|
|
7727
|
+
displayName: "Claude 3.5 Sonnet",
|
|
7728
|
+
aliases: ["sonnet-3.5", "claude-3-5-sonnet-latest"],
|
|
7729
|
+
status: "retired",
|
|
7730
|
+
successor: "claude-3-7-sonnet",
|
|
7731
|
+
releaseDate: "2024-10-22",
|
|
7732
|
+
retirementDate: "2025-10-28"
|
|
7733
|
+
}),
|
|
7734
|
+
claude("sonnet", "3.7", {
|
|
7735
|
+
id: "claude-3-7-sonnet",
|
|
7736
|
+
apiId: "claude-3-7-sonnet-20250219",
|
|
7737
|
+
displayName: "Claude 3.7 Sonnet",
|
|
7738
|
+
aliases: ["sonnet-3.7", "claude-3-7-sonnet-latest"],
|
|
7739
|
+
status: "retired",
|
|
7740
|
+
successor: "claude-sonnet-4",
|
|
7741
|
+
releaseDate: "2025-02-24",
|
|
7742
|
+
retirementDate: "2026-02-19"
|
|
7743
|
+
}),
|
|
7744
|
+
claude("sonnet", "4", {
|
|
7745
|
+
apiId: "claude-sonnet-4-20250514",
|
|
7746
|
+
status: "retired",
|
|
7747
|
+
successor: "claude-sonnet-4-5",
|
|
7748
|
+
releaseDate: "2025-05-22",
|
|
7749
|
+
retirementDate: "2026-06-15"
|
|
7750
|
+
}),
|
|
7751
|
+
claude("sonnet", "4.5", {
|
|
7752
|
+
apiId: "claude-sonnet-4-5-20250929",
|
|
7753
|
+
successor: "claude-sonnet-4-6",
|
|
7754
|
+
releaseDate: "2025-09-29"
|
|
7755
|
+
}),
|
|
7756
|
+
claude("sonnet", "4.6", { successor: "claude-sonnet-5" }),
|
|
7757
|
+
claude("sonnet", "5", { releaseDate: "2026-06-30" }),
|
|
7758
|
+
claude("haiku", "3.5", {
|
|
7759
|
+
id: "claude-3-5-haiku",
|
|
7760
|
+
apiId: "claude-3-5-haiku-20241022",
|
|
7761
|
+
displayName: "Claude 3.5 Haiku",
|
|
7762
|
+
aliases: ["haiku-3.5", "claude-3-5-haiku-latest"],
|
|
7763
|
+
status: "retired",
|
|
7764
|
+
successor: "claude-haiku-4-5",
|
|
7765
|
+
releaseDate: "2024-10-22",
|
|
7766
|
+
retirementDate: "2026-02-19"
|
|
7767
|
+
}),
|
|
7768
|
+
claude("haiku", "4.5", { apiId: "claude-haiku-4-5-20251001", releaseDate: "2025-10-15" }),
|
|
7769
|
+
claude("fable", "5", { successor: "claude-fable-5-1", releaseDate: "2026-06-09" }),
|
|
7770
|
+
claude("fable", "5.1", { releaseDate: "2026-09-01" }),
|
|
7771
|
+
claude("mythos", "5", { successor: "claude-mythos-5-1", releaseDate: "2026-06-09" }),
|
|
7772
|
+
claude("mythos", "5.1", { releaseDate: "2026-09-01" }),
|
|
7773
|
+
// OpenAI
|
|
7774
|
+
openai("gpt-4o", "4o", "gpt-4o", "GPT-4o", { successor: "gpt-4.1", releaseDate: "2024-05-13" }),
|
|
7775
|
+
openai("gpt", "4.1", "gpt-4.1", "GPT-4.1", { successor: "gpt-5.5", releaseDate: "2025-04-14" }),
|
|
7776
|
+
openai("gpt", "5", "gpt-5", "GPT-5", {
|
|
7777
|
+
status: "deprecated",
|
|
7778
|
+
successor: "gpt-5.1",
|
|
7779
|
+
releaseDate: "2025-08-07",
|
|
7780
|
+
retirementDate: "2026-12-11"
|
|
7781
|
+
}),
|
|
7782
|
+
openai("gpt", "5.1", "gpt-5.1", "GPT-5.1", {
|
|
7783
|
+
status: "deprecated",
|
|
7784
|
+
successor: "gpt-5.2",
|
|
7785
|
+
releaseDate: "2025-11-13"
|
|
7786
|
+
}),
|
|
7787
|
+
openai("gpt", "5.2", "gpt-5.2", "GPT-5.2", { successor: "gpt-5.4", releaseDate: "2025-12-11" }),
|
|
7788
|
+
openai("gpt", "5.4", "gpt-5.4", "GPT-5.4", { successor: "gpt-5.5", releaseDate: "2026-03-05" }),
|
|
7789
|
+
openai("gpt", "5.5", "gpt-5.5", "GPT-5.5", {
|
|
7790
|
+
successor: "gpt-5.6-sol",
|
|
7791
|
+
releaseDate: "2026-04-24"
|
|
7792
|
+
}),
|
|
7793
|
+
openai("gpt-mini", "5", "gpt-5-mini", "GPT-5 mini", {
|
|
7794
|
+
status: "deprecated",
|
|
7795
|
+
successor: "gpt-5.4-mini",
|
|
7796
|
+
releaseDate: "2025-08-07",
|
|
7797
|
+
retirementDate: "2026-12-11"
|
|
7798
|
+
}),
|
|
7799
|
+
openai("gpt-mini", "5.4", "gpt-5.4-mini", "GPT-5.4 mini", {
|
|
7800
|
+
successor: "gpt-5.6-terra",
|
|
7801
|
+
releaseDate: "2026-03-17"
|
|
7802
|
+
}),
|
|
7803
|
+
openai("gpt-nano", "5", "gpt-5-nano", "GPT-5 nano", {
|
|
7804
|
+
status: "deprecated",
|
|
7805
|
+
successor: "gpt-5.4-nano",
|
|
7806
|
+
releaseDate: "2025-08-07",
|
|
7807
|
+
retirementDate: "2026-12-11"
|
|
7808
|
+
}),
|
|
7809
|
+
openai("gpt-nano", "5.4", "gpt-5.4-nano", "GPT-5.4 nano", {
|
|
7810
|
+
successor: "gpt-5.6-luna",
|
|
7811
|
+
releaseDate: "2026-03-17"
|
|
7812
|
+
}),
|
|
7813
|
+
openai("gpt-codex", "5", "gpt-5-codex", "GPT-5-Codex", { successor: "gpt-5.1-codex" }),
|
|
7814
|
+
openai("gpt-codex", "5.1", "gpt-5.1-codex", "GPT-5.1-Codex", {
|
|
7815
|
+
successor: "gpt-5.2-codex",
|
|
7816
|
+
releaseDate: "2025-11-13"
|
|
7817
|
+
}),
|
|
7818
|
+
openai("gpt-codex", "5.2", "gpt-5.2-codex", "GPT-5.2-Codex", {
|
|
7819
|
+
status: "deprecated",
|
|
7820
|
+
successor: "gpt-5.3-codex",
|
|
7821
|
+
releaseDate: "2026-01-14"
|
|
7822
|
+
}),
|
|
7823
|
+
openai("gpt-codex", "5.3", "gpt-5.3-codex", "GPT-5.3-Codex", { releaseDate: "2026-02-24" }),
|
|
7824
|
+
openai("gpt-codex-max", "5.1", "gpt-5.1-codex-max", "GPT-5.1-Codex-Max", {
|
|
7825
|
+
successor: "gpt-5.2-codex",
|
|
7826
|
+
releaseDate: "2025-12-04"
|
|
7827
|
+
}),
|
|
7828
|
+
openai("gpt-codex-mini", "5.1", "gpt-5.1-codex-mini", "GPT-5.1-Codex-Mini", {
|
|
7829
|
+
successor: "gpt-5.3-codex",
|
|
7830
|
+
releaseDate: "2025-11-13"
|
|
7831
|
+
}),
|
|
7832
|
+
openai("gpt-sol", "5.6", "gpt-5.6-sol", "GPT-5.6 Sol", {
|
|
7833
|
+
successor: "gpt-6-astra",
|
|
7834
|
+
releaseDate: "2026-07-09"
|
|
7835
|
+
}),
|
|
7836
|
+
openai("gpt-sol", "6", "gpt-6-sol", "GPT-6 Sol", { releaseDate: "2026-09-22" }),
|
|
7837
|
+
openai("gpt-terra", "5.6", "gpt-5.6-terra", "GPT-5.6 Terra", {
|
|
7838
|
+
successor: "gpt-6-sol",
|
|
7839
|
+
releaseDate: "2026-07-09"
|
|
7840
|
+
}),
|
|
7841
|
+
openai("gpt-luna", "5.6", "gpt-5.6-luna", "GPT-5.6 Luna", {
|
|
7842
|
+
successor: "gpt-6-luna",
|
|
7843
|
+
releaseDate: "2026-07-09"
|
|
7844
|
+
}),
|
|
7845
|
+
openai("gpt-luna", "6", "gpt-6-luna", "GPT-6 Luna", { releaseDate: "2026-09-22" }),
|
|
7846
|
+
openai("gpt-astra", "6", "gpt-6-astra", "GPT-6 Astra", { releaseDate: "2026-09-03" }),
|
|
7847
|
+
openai("o3", "3", "o3", "o3", {
|
|
7848
|
+
status: "deprecated",
|
|
7849
|
+
successor: "gpt-5.5",
|
|
7850
|
+
releaseDate: "2025-04-16",
|
|
7851
|
+
retirementDate: "2026-12-11"
|
|
7852
|
+
}),
|
|
7853
|
+
openai("o4-mini", "4", "o4-mini", "o4-mini", {
|
|
7854
|
+
status: "deprecated",
|
|
7855
|
+
successor: "gpt-5.4-mini",
|
|
7856
|
+
releaseDate: "2025-04-16",
|
|
7857
|
+
retirementDate: "2026-10-23"
|
|
7858
|
+
}),
|
|
7859
|
+
// Google
|
|
7860
|
+
google("gemini-pro", "2.5", "gemini-2.5-pro", "Gemini 2.5 Pro", {
|
|
7861
|
+
successor: "gemini-3.1-pro-preview",
|
|
7862
|
+
releaseDate: "2025-06-17"
|
|
7863
|
+
}),
|
|
7864
|
+
google("gemini-pro", "3.1", "gemini-3.1-pro-preview", "Gemini 3.1 Pro", {
|
|
7865
|
+
aliases: ["Gemini 3.1 Pro Preview"],
|
|
7866
|
+
releaseDate: "2026-02-19"
|
|
7867
|
+
}),
|
|
7868
|
+
google("gemini-flash", "2.5", "gemini-2.5-flash", "Gemini 2.5 Flash", {
|
|
7869
|
+
successor: "gemini-3.5-flash",
|
|
7870
|
+
releaseDate: "2025-06-17"
|
|
7871
|
+
}),
|
|
7872
|
+
google("gemini-flash", "3.5", "gemini-3.5-flash", "Gemini 3.5 Flash", {
|
|
7873
|
+
successor: "gemini-3.6-flash",
|
|
7874
|
+
releaseDate: "2026-05-19"
|
|
7875
|
+
}),
|
|
7876
|
+
google("gemini-flash", "3.6", "gemini-3.6-flash", "Gemini 3.6 Flash", {
|
|
7877
|
+
successor: "gemini-3.7-flash",
|
|
7878
|
+
releaseDate: "2026-07-21"
|
|
7879
|
+
}),
|
|
7880
|
+
google("gemini-flash", "3.7", "gemini-3.7-flash", "Gemini 3.7 Flash", {
|
|
7881
|
+
successor: "gemini-3.8-flash",
|
|
7882
|
+
releaseDate: "2026-08-13"
|
|
7883
|
+
}),
|
|
7884
|
+
google("gemini-flash", "3.8", "gemini-3.8-flash", "Gemini 3.8 Flash", {
|
|
7885
|
+
releaseDate: "2026-09-02"
|
|
7886
|
+
}),
|
|
7887
|
+
google("gemini-flash-lite", "2.5", "gemini-2.5-flash-lite", "Gemini 2.5 Flash-Lite", {
|
|
7888
|
+
successor: "gemini-3.1-flash-lite",
|
|
7889
|
+
releaseDate: "2025-07-22"
|
|
7890
|
+
}),
|
|
7891
|
+
google("gemini-flash-lite", "3.1", "gemini-3.1-flash-lite", "Gemini 3.1 Flash-Lite", {
|
|
7892
|
+
status: "deprecated",
|
|
7893
|
+
successor: "gemini-3.5-flash-lite",
|
|
7894
|
+
releaseDate: "2026-05-07",
|
|
7895
|
+
retirementDate: "2027-05-07"
|
|
7896
|
+
}),
|
|
7897
|
+
google("gemini-flash-lite", "3.5", "gemini-3.5-flash-lite", "Gemini 3.5 Flash-Lite", {
|
|
7898
|
+
releaseDate: "2026-07-21"
|
|
7899
|
+
}),
|
|
7900
|
+
// xAI
|
|
7901
|
+
xai("grok", "4.5", "grok-4.5", "Grok 4.5", { successor: "grok-4.6" }),
|
|
7902
|
+
xai("grok", "4.6", "grok-4.6", "Grok 4.6", { successor: "grok-4.7" }),
|
|
7903
|
+
xai("grok", "4.7", "grok-4.7", "Grok 4.7")
|
|
7904
|
+
];
|
|
7905
|
+
}
|
|
7906
|
+
});
|
|
7907
|
+
|
|
7908
|
+
// packages/core/src/model-catalog.ts
|
|
7909
|
+
function normalizeModelName(name) {
|
|
7910
|
+
return name.trim().toLowerCase();
|
|
7911
|
+
}
|
|
7912
|
+
function ownValue(record, key) {
|
|
7913
|
+
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
7914
|
+
}
|
|
7915
|
+
function isRecord4(value) {
|
|
7916
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7917
|
+
}
|
|
7918
|
+
function nonEmptyString(value) {
|
|
7919
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
7920
|
+
}
|
|
7921
|
+
function stringList(value) {
|
|
7922
|
+
if (!Array.isArray(value)) return void 0;
|
|
7923
|
+
return value.flatMap((item) => {
|
|
7924
|
+
const name = nonEmptyString(item);
|
|
7925
|
+
return name ? [name] : [];
|
|
7926
|
+
});
|
|
7927
|
+
}
|
|
7928
|
+
function targetNames(value) {
|
|
7929
|
+
if (!isRecord4(value)) return {};
|
|
7930
|
+
return Object.fromEntries(
|
|
7931
|
+
Object.entries(value).flatMap(([key, item]) => {
|
|
7932
|
+
const target = key.trim().toLowerCase();
|
|
7933
|
+
const name = nonEmptyString(item);
|
|
7934
|
+
return target && name ? [[target, name]] : [];
|
|
7935
|
+
})
|
|
7936
|
+
);
|
|
7937
|
+
}
|
|
7938
|
+
function modelStatus(value) {
|
|
7939
|
+
return MODEL_STATUSES.find((status) => status === value);
|
|
7940
|
+
}
|
|
7941
|
+
function optionalFields(input2) {
|
|
7942
|
+
const fields = {};
|
|
7943
|
+
const provider = nonEmptyString(input2.provider);
|
|
7944
|
+
if (provider) fields.provider = provider;
|
|
7945
|
+
const family = nonEmptyString(input2.family);
|
|
7946
|
+
if (family) fields.family = family;
|
|
7947
|
+
const version = nonEmptyString(input2.version);
|
|
7948
|
+
if (version) fields.version = version;
|
|
7949
|
+
const displayName = nonEmptyString(input2.displayName);
|
|
7950
|
+
if (displayName) fields.displayName = displayName;
|
|
7951
|
+
const apiId = nonEmptyString(input2.apiId);
|
|
7952
|
+
if (apiId) fields.apiId = apiId;
|
|
7953
|
+
const aliases = stringList(input2.aliases);
|
|
7954
|
+
if (aliases) fields.aliases = aliases;
|
|
7955
|
+
const status = modelStatus(input2.status);
|
|
7956
|
+
if (status) fields.status = status;
|
|
7957
|
+
const successor = nonEmptyString(input2.successor);
|
|
7958
|
+
if (successor) fields.successor = successor;
|
|
7959
|
+
const releaseDate = nonEmptyString(input2.releaseDate);
|
|
7960
|
+
if (releaseDate) fields.releaseDate = releaseDate;
|
|
7961
|
+
const retirementDate = nonEmptyString(input2.retirementDate);
|
|
7962
|
+
if (retirementDate) fields.retirementDate = retirementDate;
|
|
7963
|
+
return fields;
|
|
7964
|
+
}
|
|
7965
|
+
function applyProfileInput(base, input2) {
|
|
7966
|
+
return {
|
|
7967
|
+
...base,
|
|
7968
|
+
...optionalFields(input2),
|
|
7969
|
+
// Per-target names merge so one override keeps the other targets.
|
|
7970
|
+
targets: { ...base.targets, ...targetNames(input2.targets) }
|
|
7971
|
+
};
|
|
7972
|
+
}
|
|
7973
|
+
function createCustomProfile(id, input2) {
|
|
7974
|
+
const fields = optionalFields(input2);
|
|
7975
|
+
return {
|
|
7976
|
+
id,
|
|
7977
|
+
provider: "custom",
|
|
7978
|
+
family: id,
|
|
7979
|
+
version: "",
|
|
7980
|
+
displayName: id,
|
|
7981
|
+
apiId: id,
|
|
7982
|
+
aliases: [],
|
|
7983
|
+
status: "current",
|
|
7984
|
+
...fields,
|
|
7985
|
+
targets: targetNames(input2.targets)
|
|
7986
|
+
};
|
|
7987
|
+
}
|
|
7988
|
+
function mergeProfiles(builtIns, overrides) {
|
|
7989
|
+
if (!isRecord4(overrides)) return builtIns;
|
|
7990
|
+
const pending = /* @__PURE__ */ new Map();
|
|
7991
|
+
for (const [key, input2] of Object.entries(overrides)) {
|
|
7992
|
+
const id = nonEmptyString(key);
|
|
7993
|
+
if (id && isRecord4(input2)) {
|
|
7994
|
+
pending.set(normalizeModelName(id), { id, input: input2 });
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
const merged = builtIns.map((profile) => {
|
|
7998
|
+
const key = normalizeModelName(profile.id);
|
|
7999
|
+
const override = pending.get(key);
|
|
8000
|
+
if (!override) return profile;
|
|
8001
|
+
pending.delete(key);
|
|
8002
|
+
return applyProfileInput(profile, override.input);
|
|
8003
|
+
});
|
|
8004
|
+
for (const { id, input: input2 } of pending.values()) {
|
|
8005
|
+
merged.push(createCustomProfile(id, input2));
|
|
8006
|
+
}
|
|
8007
|
+
return merged;
|
|
8008
|
+
}
|
|
8009
|
+
function splitVersionPart(part) {
|
|
8010
|
+
let end = 0;
|
|
8011
|
+
while (end < part.length) {
|
|
8012
|
+
const character = part[end];
|
|
8013
|
+
if (character === void 0 || character < "0" || character > "9") break;
|
|
8014
|
+
end++;
|
|
8015
|
+
}
|
|
8016
|
+
const digits = part.slice(0, end);
|
|
8017
|
+
return { number: digits.length > 0 ? Number(digits) : 0, suffix: part.slice(end) };
|
|
8018
|
+
}
|
|
8019
|
+
function compareVersionParts(leftPart, rightPart) {
|
|
8020
|
+
const left = splitVersionPart(leftPart);
|
|
8021
|
+
const right = splitVersionPart(rightPart);
|
|
8022
|
+
if (left.number !== right.number) return left.number - right.number;
|
|
8023
|
+
const leftSuffix = left.suffix;
|
|
8024
|
+
const rightSuffix = right.suffix;
|
|
8025
|
+
if (leftSuffix === rightSuffix) return 0;
|
|
8026
|
+
if (leftSuffix === "") return 1;
|
|
8027
|
+
if (rightSuffix === "") return -1;
|
|
8028
|
+
return leftSuffix.localeCompare(rightSuffix);
|
|
8029
|
+
}
|
|
8030
|
+
function compareModelVersions(a, b) {
|
|
8031
|
+
const left = a.split(".");
|
|
8032
|
+
const right = b.split(".");
|
|
8033
|
+
for (let index = 0; index < Math.max(left.length, right.length); index++) {
|
|
8034
|
+
const diff = compareVersionParts(left[index] ?? "0", right[index] ?? "0");
|
|
8035
|
+
if (diff !== 0) return diff;
|
|
8036
|
+
}
|
|
8037
|
+
return 0;
|
|
8038
|
+
}
|
|
8039
|
+
function compareModelReleases(a, b) {
|
|
8040
|
+
return compareModelVersions(a.version, b.version) || (a.releaseDate ?? "").localeCompare(b.releaseDate ?? "");
|
|
8041
|
+
}
|
|
8042
|
+
function createModelCatalog(config, builtIns = MODEL_PROFILES) {
|
|
8043
|
+
const profiles = mergeProfiles(builtIns, config?.profiles);
|
|
8044
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8045
|
+
const byName = /* @__PURE__ */ new Map();
|
|
8046
|
+
for (const profile of profiles) {
|
|
8047
|
+
byId.set(normalizeModelName(profile.id), profile);
|
|
8048
|
+
for (const name of [profile.apiId, profile.displayName, ...profile.aliases]) {
|
|
8049
|
+
byName.set(normalizeModelName(name), profile);
|
|
8050
|
+
}
|
|
8051
|
+
}
|
|
8052
|
+
const getProfile = (id) => byId.get(normalizeModelName(id));
|
|
8053
|
+
const getLatest = (family) => {
|
|
8054
|
+
const members = profiles.filter((profile) => profile.family === family);
|
|
8055
|
+
const served = members.filter((profile) => profile.status !== "retired");
|
|
8056
|
+
const pool = served.length > 0 ? served : members;
|
|
8057
|
+
return pool.reduce(
|
|
8058
|
+
(newest, profile) => newest === void 0 || compareModelReleases(profile, newest) > 0 ? profile : newest,
|
|
8059
|
+
void 0
|
|
8060
|
+
);
|
|
8061
|
+
};
|
|
8062
|
+
const resolve45 = (reference) => {
|
|
8063
|
+
const name = normalizeModelName(reference);
|
|
8064
|
+
if (!name) return void 0;
|
|
8065
|
+
if (name === INHERIT_MODEL) return { kind: "inherit" };
|
|
8066
|
+
const exact = byId.get(name);
|
|
8067
|
+
if (exact) return { kind: "profile", profile: exact };
|
|
8068
|
+
const family = ownValue(FLOATING_MODEL_ALIASES, name);
|
|
8069
|
+
const latest = family ? getLatest(family) : void 0;
|
|
8070
|
+
if (latest) return { kind: "floating", alias: name, profile: latest };
|
|
8071
|
+
const named = byName.get(name);
|
|
8072
|
+
return named ? { kind: "profile", profile: named } : void 0;
|
|
8073
|
+
};
|
|
8074
|
+
const getReplacement = (id) => {
|
|
8075
|
+
const visited = /* @__PURE__ */ new Set();
|
|
8076
|
+
let current = getProfile(id);
|
|
8077
|
+
while (current?.successor && !visited.has(current.id)) {
|
|
8078
|
+
visited.add(current.id);
|
|
8079
|
+
const next = getProfile(current.successor);
|
|
8080
|
+
if (!next) break;
|
|
8081
|
+
if (next.status === "current") return next;
|
|
8082
|
+
current = next;
|
|
8083
|
+
}
|
|
8084
|
+
return void 0;
|
|
8085
|
+
};
|
|
8086
|
+
return { profiles, getProfile, getLatest, resolve: resolve45, getReplacement };
|
|
8087
|
+
}
|
|
8088
|
+
function getModelCatalog(config) {
|
|
8089
|
+
if (!config?.profiles) {
|
|
8090
|
+
defaultCatalog ??= createModelCatalog();
|
|
8091
|
+
return defaultCatalog;
|
|
8092
|
+
}
|
|
8093
|
+
let catalog = catalogCache.get(config);
|
|
8094
|
+
if (!catalog) {
|
|
8095
|
+
catalog = createModelCatalog(config);
|
|
8096
|
+
catalogCache.set(config, catalog);
|
|
8097
|
+
}
|
|
8098
|
+
return catalog;
|
|
8099
|
+
}
|
|
8100
|
+
function duplicateIdIssues(profiles) {
|
|
8101
|
+
const issues = [];
|
|
8102
|
+
const ids = /* @__PURE__ */ new Set();
|
|
8103
|
+
for (const profile of profiles) {
|
|
8104
|
+
const id = normalizeModelName(profile.id);
|
|
8105
|
+
if (ids.has(id)) issues.push(`duplicate model id "${profile.id}"`);
|
|
8106
|
+
ids.add(id);
|
|
8107
|
+
}
|
|
8108
|
+
return issues;
|
|
8109
|
+
}
|
|
8110
|
+
function nameIssues(profile, owners) {
|
|
8111
|
+
const issues = [];
|
|
8112
|
+
const names = new Set(
|
|
8113
|
+
[profile.id, profile.apiId, profile.displayName, ...profile.aliases].map(normalizeModelName)
|
|
8114
|
+
);
|
|
8115
|
+
for (const name of names) {
|
|
8116
|
+
const owner = owners.get(name);
|
|
8117
|
+
if (Object.hasOwn(FLOATING_MODEL_ALIASES, name) || name === INHERIT_MODEL) {
|
|
8118
|
+
issues.push(`model "${profile.id}" uses reserved name "${name}"`);
|
|
8119
|
+
} else if (owner === void 0) {
|
|
8120
|
+
owners.set(name, profile.id);
|
|
8121
|
+
} else if (owner !== profile.id) {
|
|
8122
|
+
issues.push(`model name "${name}" is used by "${owner}" and "${profile.id}"`);
|
|
8123
|
+
}
|
|
8124
|
+
}
|
|
8125
|
+
return issues;
|
|
8126
|
+
}
|
|
8127
|
+
function successorIssues(profile, ids) {
|
|
8128
|
+
if (profile.successor === void 0) return [];
|
|
8129
|
+
const successor = normalizeModelName(profile.successor);
|
|
8130
|
+
if (successor === normalizeModelName(profile.id)) {
|
|
8131
|
+
return [`model "${profile.id}" names itself as successor`];
|
|
8132
|
+
}
|
|
8133
|
+
if (ids.has(successor)) return [];
|
|
8134
|
+
return [`model "${profile.id}" names unknown successor "${profile.successor}"`];
|
|
8135
|
+
}
|
|
8136
|
+
function dateIssues(profile) {
|
|
8137
|
+
const dates = [
|
|
8138
|
+
["release date", profile.releaseDate],
|
|
8139
|
+
["retirement date", profile.retirementDate]
|
|
8140
|
+
];
|
|
8141
|
+
return dates.flatMap(
|
|
8142
|
+
([label, date]) => date === void 0 || DATE_PATTERN.test(date) ? [] : [`model "${profile.id}" has invalid ${label} "${date}", expected YYYY-MM-DD`]
|
|
8143
|
+
);
|
|
8144
|
+
}
|
|
8145
|
+
function targetIssues(profile) {
|
|
8146
|
+
const writers = Object.keys(MODEL_TARGET_SCHEMES).join(", ");
|
|
8147
|
+
return Object.keys(profile.targets).filter((target) => !getModelTargetScheme(target)).map(
|
|
8148
|
+
(target) => `model "${profile.id}" sets a name for "${target}", which is not a target that writes model names (${writers})`
|
|
8149
|
+
);
|
|
8150
|
+
}
|
|
8151
|
+
function successorLoopIssues(profiles) {
|
|
8152
|
+
const byId = new Map(profiles.map((profile) => [normalizeModelName(profile.id), profile]));
|
|
8153
|
+
const successorOf = (profile) => profile.successor ? byId.get(normalizeModelName(profile.successor)) : void 0;
|
|
8154
|
+
const issues = [];
|
|
8155
|
+
for (const profile of profiles) {
|
|
8156
|
+
const visited = /* @__PURE__ */ new Set([normalizeModelName(profile.id)]);
|
|
8157
|
+
let next = successorOf(profile);
|
|
8158
|
+
while (next) {
|
|
8159
|
+
const id = normalizeModelName(next.id);
|
|
8160
|
+
if (visited.has(id)) {
|
|
8161
|
+
issues.push(`successor chain of "${profile.id}" loops`);
|
|
8162
|
+
break;
|
|
8163
|
+
}
|
|
8164
|
+
visited.add(id);
|
|
8165
|
+
next = successorOf(next);
|
|
8166
|
+
}
|
|
8167
|
+
}
|
|
8168
|
+
return issues;
|
|
8169
|
+
}
|
|
8170
|
+
function floatingAliasIssues(profiles) {
|
|
8171
|
+
return Object.entries(FLOATING_MODEL_ALIASES).filter(([, family]) => !profiles.some((profile) => profile.family === family)).map(([alias, family]) => `floating alias "${alias}" names unknown family "${family}"`);
|
|
8172
|
+
}
|
|
8173
|
+
function validateModelCatalog(profiles = MODEL_PROFILES) {
|
|
8174
|
+
const ids = new Set(profiles.map((profile) => normalizeModelName(profile.id)));
|
|
8175
|
+
const owners = /* @__PURE__ */ new Map();
|
|
8176
|
+
const profileIssues = profiles.flatMap((profile) => [
|
|
8177
|
+
...nameIssues(profile, owners),
|
|
8178
|
+
...successorIssues(profile, ids),
|
|
8179
|
+
...dateIssues(profile),
|
|
8180
|
+
...targetIssues(profile)
|
|
8181
|
+
]);
|
|
8182
|
+
return [
|
|
8183
|
+
...duplicateIdIssues(profiles),
|
|
8184
|
+
...profileIssues,
|
|
8185
|
+
...successorLoopIssues(profiles),
|
|
8186
|
+
...floatingAliasIssues(profiles)
|
|
8187
|
+
];
|
|
8188
|
+
}
|
|
8189
|
+
function resolveModelSet(entries, catalog) {
|
|
8190
|
+
const profileIds = /* @__PURE__ */ new Set();
|
|
8191
|
+
const floatingAliases = /* @__PURE__ */ new Set();
|
|
8192
|
+
const unknown = [];
|
|
8193
|
+
for (const entry of entries) {
|
|
8194
|
+
const resolution = catalog.resolve(entry);
|
|
8195
|
+
if (!resolution) {
|
|
8196
|
+
unknown.push(entry);
|
|
8197
|
+
continue;
|
|
8198
|
+
}
|
|
8199
|
+
if (resolution.kind === "inherit") continue;
|
|
8200
|
+
if (resolution.kind === "floating") floatingAliases.add(resolution.alias);
|
|
8201
|
+
profileIds.add(resolution.profile.id);
|
|
8202
|
+
}
|
|
8203
|
+
return { profileIds, floatingAliases, unknown };
|
|
8204
|
+
}
|
|
8205
|
+
function isInModelSet(resolution, set) {
|
|
8206
|
+
if (resolution.kind === "inherit") return true;
|
|
8207
|
+
if (resolution.kind === "floating" && set.floatingAliases.has(resolution.alias)) return true;
|
|
8208
|
+
return set.profileIds.has(resolution.profile.id);
|
|
8209
|
+
}
|
|
8210
|
+
function getModelTargetScheme(target) {
|
|
8211
|
+
return Object.hasOwn(MODEL_TARGET_SCHEMES, target) ? MODEL_TARGET_SCHEMES[target] : void 0;
|
|
8212
|
+
}
|
|
8213
|
+
function profileName(value, profile) {
|
|
8214
|
+
return UNSAFE_MODEL_NAME.test(value) ? { issue: "invalid-name", profile } : { value, profile };
|
|
8215
|
+
}
|
|
8216
|
+
function mapProfileToTarget(resolution, target, scheme) {
|
|
8217
|
+
const { profile } = resolution;
|
|
8218
|
+
const explicit = ownValue(profile.targets, target);
|
|
8219
|
+
if (explicit !== void 0) return profileName(explicit, profile);
|
|
8220
|
+
if (resolution.kind === "floating" && scheme.keepsFloatingAliases) {
|
|
8221
|
+
return { value: resolution.alias, profile };
|
|
8222
|
+
}
|
|
8223
|
+
if (scheme.providers && !scheme.providers.includes(profile.provider)) {
|
|
8224
|
+
return { issue: "unsupported-provider", profile };
|
|
8225
|
+
}
|
|
8226
|
+
return profileName(profile[scheme.naming], profile);
|
|
8227
|
+
}
|
|
8228
|
+
function mapModelToTarget(reference, target, catalog = getModelCatalog()) {
|
|
8229
|
+
const written = reference.trim();
|
|
8230
|
+
if (!written) return {};
|
|
8231
|
+
if (UNSAFE_MODEL_NAME.test(written)) return { issue: "invalid-name" };
|
|
8232
|
+
const scheme = getModelTargetScheme(target);
|
|
8233
|
+
if (!scheme) return { value: written };
|
|
8234
|
+
const resolution = catalog.resolve(written);
|
|
8235
|
+
if (!resolution) {
|
|
8236
|
+
const native = ownValue(scheme.nativeValues ?? {}, normalizeModelName(written));
|
|
8237
|
+
return native !== void 0 ? { value: native } : { value: written, issue: "unknown-model" };
|
|
8238
|
+
}
|
|
8239
|
+
if (resolution.kind === "inherit") {
|
|
8240
|
+
return scheme.writesInherit ? { value: INHERIT_MODEL } : {};
|
|
8241
|
+
}
|
|
8242
|
+
return mapProfileToTarget(resolution, target, scheme);
|
|
8243
|
+
}
|
|
8244
|
+
var FLOATING_MODEL_ALIASES, INHERIT_MODEL, MODEL_STATUSES, defaultCatalog, catalogCache, DATE_PATTERN, CLAUDE_MODEL_SCHEME, MODEL_TARGET_SCHEMES, UNSAFE_MODEL_NAME;
|
|
8245
|
+
var init_model_catalog = __esm({
|
|
8246
|
+
"packages/core/src/model-catalog.ts"() {
|
|
8247
|
+
"use strict";
|
|
8248
|
+
init_model_profiles();
|
|
8249
|
+
FLOATING_MODEL_ALIASES = {
|
|
8250
|
+
opus: "claude-opus",
|
|
8251
|
+
sonnet: "claude-sonnet",
|
|
8252
|
+
haiku: "claude-haiku",
|
|
8253
|
+
fable: "claude-fable"
|
|
8254
|
+
};
|
|
8255
|
+
INHERIT_MODEL = "inherit";
|
|
8256
|
+
MODEL_STATUSES = ["current", "legacy", "deprecated", "retired"];
|
|
8257
|
+
catalogCache = /* @__PURE__ */ new WeakMap();
|
|
8258
|
+
DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
8259
|
+
CLAUDE_MODEL_SCHEME = {
|
|
8260
|
+
providers: ["anthropic"],
|
|
8261
|
+
naming: "apiId",
|
|
8262
|
+
keepsFloatingAliases: true,
|
|
8263
|
+
writesInherit: true
|
|
8264
|
+
};
|
|
8265
|
+
MODEL_TARGET_SCHEMES = {
|
|
8266
|
+
claude: CLAUDE_MODEL_SCHEME,
|
|
8267
|
+
// Grok delegates agent files to the Claude formatter.
|
|
8268
|
+
grok: CLAUDE_MODEL_SCHEME,
|
|
8269
|
+
github: {
|
|
8270
|
+
naming: "displayName",
|
|
8271
|
+
keepsFloatingAliases: false,
|
|
8272
|
+
writesInherit: false,
|
|
8273
|
+
nativeValues: { auto: "Auto" }
|
|
8274
|
+
},
|
|
8275
|
+
factory: {
|
|
8276
|
+
naming: "apiId",
|
|
8277
|
+
keepsFloatingAliases: false,
|
|
8278
|
+
writesInherit: true
|
|
8279
|
+
},
|
|
8280
|
+
codex: {
|
|
8281
|
+
providers: ["openai"],
|
|
8282
|
+
naming: "apiId",
|
|
8283
|
+
keepsFloatingAliases: false,
|
|
8284
|
+
writesInherit: false
|
|
8285
|
+
},
|
|
8286
|
+
// Cursor has its own model ids and documents them without snapshot dates.
|
|
8287
|
+
cursor: {
|
|
8288
|
+
naming: "id",
|
|
8289
|
+
keepsFloatingAliases: false,
|
|
8290
|
+
writesInherit: true
|
|
8291
|
+
}
|
|
8292
|
+
};
|
|
8293
|
+
UNSAFE_MODEL_NAME = /[\p{Cc}\u2028\u2029]/u;
|
|
8294
|
+
}
|
|
8295
|
+
});
|
|
8296
|
+
|
|
8297
|
+
// packages/core/src/model-drift.ts
|
|
8298
|
+
var init_model_drift = __esm({
|
|
8299
|
+
"packages/core/src/model-drift.ts"() {
|
|
8300
|
+
"use strict";
|
|
8301
|
+
init_model_catalog();
|
|
8302
|
+
}
|
|
8303
|
+
});
|
|
8304
|
+
|
|
7652
8305
|
// packages/core/src/git-timeout.ts
|
|
7653
8306
|
function isGitTimeoutError(error) {
|
|
7654
8307
|
const errorWithCode = error;
|
|
@@ -7727,7 +8380,7 @@ function normalizeManagedPaths(output) {
|
|
|
7727
8380
|
...files.length > 0 ? { files } : {}
|
|
7728
8381
|
};
|
|
7729
8382
|
}
|
|
7730
|
-
function
|
|
8383
|
+
function isRecord5(value) {
|
|
7731
8384
|
return typeof value === "object" && value !== null;
|
|
7732
8385
|
}
|
|
7733
8386
|
function valuesEqual(left, right) {
|
|
@@ -7739,7 +8392,7 @@ function valuesEqual(left, right) {
|
|
|
7739
8392
|
}
|
|
7740
8393
|
return left.every((value, index) => valuesEqual(value, right[index]));
|
|
7741
8394
|
}
|
|
7742
|
-
if (!
|
|
8395
|
+
if (!isRecord5(left) || !isRecord5(right)) return false;
|
|
7743
8396
|
const leftKeys = Object.keys(left).sort();
|
|
7744
8397
|
const rightKeys = Object.keys(right).sort();
|
|
7745
8398
|
if (leftKeys.length !== rightKeys.length) return false;
|
|
@@ -7985,6 +8638,9 @@ var init_src = __esm({
|
|
|
7985
8638
|
init_target_catalog();
|
|
7986
8639
|
init_target_capabilities();
|
|
7987
8640
|
init_agent_capabilities();
|
|
8641
|
+
init_model_catalog();
|
|
8642
|
+
init_model_drift();
|
|
8643
|
+
init_model_profiles();
|
|
7988
8644
|
init_hook_capabilities();
|
|
7989
8645
|
init_git_timeout();
|
|
7990
8646
|
init_structured_output();
|
|
@@ -8044,7 +8700,7 @@ var PROMPTSCRIPT_SKILL_CONTENT;
|
|
|
8044
8700
|
var init_promptscript_skill = __esm({
|
|
8045
8701
|
"packages/cli/src/generated/promptscript-skill.ts"() {
|
|
8046
8702
|
"use strict";
|
|
8047
|
-
PROMPTSCRIPT_SKILL_CONTENT = '---\nname: promptscript\ndescription: >-\n PromptScript language expert for reading, writing, modifying, and\n troubleshooting .prs files. Use when working with PromptScript syntax,\n creating or editing .prs files, adding blocks like @identity, @standards,\n @restrictions, @shortcuts, @skills, or @agents, configuring\n promptscript.yaml, resolving compilation errors, understanding inheritance\n (@inherit), composition (@use, @extend, @override), contextual @header\n metadata, or migrating AI instructions\n to PromptScript. Also use when asked about the 50 built-in compilation\n targets, including GitHub Copilot, Claude Code, Cursor, Antigravity,\n Factory AI, and AGENTS.md-based platforms.\nlicense: MIT\nmetadata:\n author: PromptScript\n homepage: https://getpromptscript.dev\ncompatibility:\n - claude-code\n - github-copilot\n - cursor\n - factory-ai\n - gemini-cli\n - opencode\n - windsurf\n - cline\n - roo\n - codex\n - continue\n - augment\n - goose\n - kilo\n - amp\n - trae\n - junie\n - kiro-cli\nallowed-tools:\n - Read\n - Write\n - Glob\n - Grep\n - Bash\nuser-invocable: true\n---\n\n# PromptScript Language Guide\n\nPromptScript is a domain-specific language that compiles `.prs` files into native instruction formats for AI coding assistants (GitHub Copilot, Claude Code, Cursor, Antigravity, Factory AI, OpenCode, Gemini CLI). One source of truth, multiple outputs.\n\n## File Structure\n\nA `.prs` file contains ordered declarations. Syntax `1.5.0` applies `@inherit`,\n`@use`, local blocks, `@extend`, and `@override` in source order. Put `@meta`\nfirst.\n\n```\n# Comments start with #\n\n@meta { ... } # Required metadata\n@inherit @path # Single inheritance (optional)\n@use @path [as alias] # Imports/mixins (optional, multiple)\n\n@identity { ... } # AI persona\n@context { ... } # Project context\n@standards { ... } # Coding conventions\n@restrictions { ... } # Hard rules\n@shortcuts { ... } # Command aliases\n@knowledge { ... } # Reference documentation\n@skills { ... } # Reusable skill definitions\n@agents { ... } # Subagent definitions\n@workflows { ... } # Repeatable agent procedures\n@examples { ... } # Few-shot input/output examples (syntax 1.2.0+)\n@params { ... } # Template parameters\n@guards { ... } # File globs and priorities\n@hooks { ... } # Portable lifecycle hooks (syntax 1.4.0+)\n@mcpServers { ... } # MCP server configurations (syntax 1.4.0+)\n@plugins { ... } # Capability bundles (syntax 1.4.0+)\n@local { ... } # Private config (not committed)\n@extend path { ... } # Modify imported blocks\n@override path { ... } # Replace one complete existing target (syntax 1.5.0+)\n@custom-name { ... } # Arbitrary named blocks\n```\n\nContextual `@header` entries appear inside supported owner blocks, not at the\ntop level.\n\n## Content Types\n\nPromptScript has four canonical content shapes inside blocks:\n\n### Text Content\n\nUse triple quotes (three double-quote characters) to wrap multiline text.\nText is automatically dedented - leading whitespace from source indentation is stripped.\nUse for prose, markdown, or freeform content.\n\nExample: `@identity` with a text block describing an AI persona starting with "You are..."\n\n### Object Content (key-value pairs)\n\n```\n@context {\n project: "My App"\n team: "Frontend"\n monorepo: {\n tool: "Nx"\n packageManager: "pnpm"\n }\n}\n```\n\nValues can be strings (quoted or unquoted), numbers, booleans, nested objects, or arrays.\n\n### Array Content\n\n```\n@standards {\n code: [\n "Use strict TypeScript",\n "Named exports only"\n ]\n}\n\n@restrictions {\n - "Never use any type"\n - "Never commit secrets"\n}\n```\n\n### Mixed Content\n\nBlocks can contain both object properties and text in the same block.\nPlace the triple-quoted text block alongside key-value pairs.\n\n## Block Reference\n\n### @meta (required)\n\n```\n@meta {\n id: "project-id" # Required: unique identifier\n syntax: "1.0.0" # Required: syntax version (semver)\n org: "Company Name" # Optional\n team: "Frontend" # Optional\n tags: [react, ts] # Optional\n params: { # Optional: template parameters\n projectName: string\n port: number = 3000\n debug?: boolean\n framework: enum("react", "vue") = "react"\n }\n}\n```\n\n### @identity\n\nDefines AI persona. Start with "You are..." for consistent output across all formatters.\nContains a triple-quoted text block with the persona description.\n\n### @context\n\nProject context with structured properties (project, team, languages, runtime)\nplus optional triple-quoted text for architecture details, diagrams, etc.\n\n### @standards\n\nCategory-based conventions. Any category name is valid:\n\n```\n@standards {\n typescript: ["Strict mode", "No any type"]\n naming: ["Files: kebab-case.ts", "Classes: PascalCase"]\n git: {\n format: "Conventional Commits"\n types: [feat, fix, docs, refactor, test, chore]\n }\n}\n```\n\nCategory names are arbitrary. `@standards` can also contain free-form text:\n\n```\n@standards {\n """\n ## Formatting\n Preserve heading structure and use four-space indentation.\n\n ## Testing\n Add regression coverage for every behavior change.\n """\n typescript: ["Strict mode", "Named exports only"]\n git: {\n format: "Conventional Commits"\n }\n}\n```\n\nFree-form text is dedented and rendered with its Markdown heading structure. Factory\nmonolith output nests it under `Conventions & Patterns`; split Factory rules adjust\nheading levels relative to the generated section. Custom structured categories remain\navailable to formatters that support them.\n\n### @restrictions\n\nHard rules as a list of dash-prefixed strings:\n\n```\n@restrictions {\n - "Never expose API keys"\n - "Never commit secrets to version control"\n - "Always validate user input"\n}\n```\n\n### @shortcuts\n\nSimple strings appear as documentation. Objects with `prompt: true` generate\nexecutable prompt/command files for GitHub Copilot and Cursor:\n\n```\n@shortcuts {\n "/review": "Review code for quality"\n "/test": {\n prompt: true\n description: "Write unit tests"\n content: (triple-quoted text with instructions)\n }\n}\n```\n\n> `@commands` is a backwards-compatible alias for `@shortcuts` \u2014 prefer `@shortcuts` in new files.\n\n### @skills\n\nReusable skill definitions with metadata:\n\n```\n@skills {\n commit: {\n description: "Create git commits"\n trigger: "commit, git commit"\n disableModelInvocation: true\n userInvocable: true\n allowedTools: ["Bash", "Read"]\n content: (triple-quoted text with skill instructions)\n }\n}\n```\n\nProperties: description (required), content (required), trigger, disableModelInvocation,\nuserInvocable, allowedTools, context ("fork" or "inherit"), agent, requires, references, inputs, outputs.\n\nThe `references` property attaches external files to the skill\'s context:\n\n```\n@skills {\n architecture-review: {\n description: "Review architecture decisions"\n references: [\n ./references/architecture.md\n ./references/modules.md\n ]\n content: (triple-quoted text)\n }\n}\n```\n\nAllowed file types: `.md`, `.json`, `.yaml`, `.yml`, `.txt`, `.csv`. Paths are resolved relative\nto the `.prs` file. Formatters emit referenced files alongside SKILL.md in the output directory.\n\n### Parameterized Skills\n\nSkills in `.promptscript/skills/<name>/SKILL.md` support template parameters via\nYAML frontmatter. Define `params` in frontmatter and use `{{variable}}` in content:\n\n```yaml\n---\nname: review\ndescription: \'Review {{language}} code for {{standard}}\'\nparams:\n language:\n type: string\n standard:\n type: string\n default: \'best practices\'\nreferences:\n - references/architecture.md\n---\nReview the code using {{language}} conventions following {{standard}}.\n```\n\nThe `references` field in SKILL.md frontmatter lists files to attach to the skill\'s context.\nPaths are relative to the SKILL.md file.\n\nImported SKILL.md frontmatter is bounded: 256 KiB per document, 10,000 YAML nodes, 32 nesting\nlevels, 2,000 entries per mapping or sequence, and 64 KiB per string value. Documents over any\nlimit are rejected before their YAML values are converted.\n\nPass values in `@skills` block:\n\n```\n@skills {\n review: {\n description: "Review code"\n language: "typescript"\n standard: "strict mode"\n }\n}\n```\n\nNon-reserved properties (anything other than description, content, trigger,\nuserInvocable, allowedTools, disableModelInvocation, context, agent, requires,\ninputs, outputs) are treated as skill parameter arguments.\n\n### Skill Dependencies\n\nSkills can declare dependencies on other skills via `requires`:\n\n```\n@skills {\n deploy: {\n description: "Deploy service"\n requires: ["lint-check", "test-suite"]\n content: (triple-quoted text)\n }\n}\n```\n\nThe validator (PS016) checks that required skills exist, detects self-references,\nand catches circular dependency chains.\n\n### Skill Contracts (Inputs/Outputs)\n\nSkills can declare typed inputs and outputs in SKILL.md frontmatter:\n\n```yaml\n---\nname: security-scan\ndescription: \'Scan for vulnerabilities\'\ninputs:\n files:\n description: \'Files to scan\'\n type: string\n severity:\n description: \'Minimum severity\'\n type: enum\n options: [low, medium, high]\n default: medium\noutputs:\n report:\n description: \'Scan report\'\n type: string\n passed:\n description: \'Whether scan passed\'\n type: boolean\n---\n```\n\nField types: `string`, `number`, `boolean`, `enum` (with `options` list).\nThe validator (PS017) checks field types, ensures enum fields have options,\nand warns if param names collide with input names.\n\n### Shared Resources\n\nSkills in a folder can share common resources via `.promptscript/shared/`:\n\n```\n.promptscript/\n shared/\n templates.md # Shared across all skills\n style-guide.md\n skills/\n review/\n SKILL.md # Gets @shared/templates.md, @shared/style-guide.md\n deploy/\n SKILL.md # Also gets shared resources\n```\n\nFiles in `shared/` are automatically included in every skill with `@shared/` prefix.\n\n### @agents\n\nCustom subagent definitions. Compiles to `.claude/agents/` for Claude Code,\n`.github/agents/` for GitHub Copilot, `.factory/droids/` for Factory AI, etc.\n\n```\n@agents {\n code-reviewer: {\n description: "Reviews code quality"\n tools: ["Read", "Grep", "Glob", "Bash"]\n model: "sonnet"\n permissionMode: "default"\n content: (triple-quoted text with agent instructions)\n }\n}\n```\n\nImported agent definitions are qualified by an aliased `@use`:\n\n```\n@use ./frontend-team as frontend\n@use ./backend-team as backend\n```\n\nIf both imports define `reviewer`, the resolved names are `frontend.reviewer` and\n`backend.reviewer`. Unique unaliased imports keep their original names. Conflicting unaliased\ndefinitions stop compilation with source and import diagnostics instead of silently overwriting\none another. Native targets map dots to hyphens, so `frontend.reviewer` becomes\n`frontend-reviewer`.\n\nSupports mixed models per agent: `specModel` sets a different model for\nSpecification/planning mode (GitHub, Factory), `specReasoningEffort` sets reasoning\neffort for the spec model (Factory only, values: "low", "medium", "high").\n\nFactory AI droids support additional properties: `model` (any model ID or "inherit"),\n`reasoningEffort` ("low", "medium", "high"), and `tools` (category name like "read-only"\nor array of tool IDs).\n\n### @workflows\n\nRepeatable multi-step agent procedures. Requires syntax `1.1.0`.\n\n```\n@workflows {\n release: {\n description: "Prepare a validated release"\n content: """\n 1. Run formatting, linting, type checks, and tests.\n 2. Validate compiled output.\n 3. Stop before publishing and request approval.\n """\n }\n}\n```\n\nTargets with native workflow discovery emit dedicated workflow files. Other targets\nretain workflow instructions in their main output when supported.\n\n### @examples\n\nStructured few-shot examples for AI assistants (requires syntax `1.2.0`):\n\n```\n@meta {\n id: "commit-style"\n syntax: "1.2.0"\n}\n\n@examples {\n feat-commit: {\n description: "Feature commit with scope"\n input: "Added user authentication with JWT tokens"\n output: "feat(auth): add JWT-based user authentication"\n }\n}\n```\n\nEach entry is a named example with `input` and `output` (both required),\nplus optional `description`. Multi-line content uses triple-quoted strings.\n\nExamples can also be attached to skills via the `examples` property:\n\n```\n@skills {\n commit: {\n description: "Create conventional commits"\n examples: {\n basic: {\n input: "Added dark mode toggle"\n output: "feat(settings): add dark mode toggle"\n }\n }\n content: (triple-quoted text)\n }\n}\n```\n\n### @knowledge\n\nReference documentation as triple-quoted text. Used for command references,\nAPI docs, and other material that should appear in the output.\n\n### @params\n\nTemplate parameter definitions with types: string, number, boolean, enum("a", "b").\nOptional parameters use `?` suffix. Defaults use `= value`.\n\n### @guards\n\nFile glob patterns and priority rules for path-specific instructions.\n\n### @hooks\n\nPortable lifecycle hooks. Requires syntax `1.4.0`. Each hook needs exactly one of\n`command` or `script`.\n\n```\n@hooks {\n validate-types: {\n event: "post-tool-use"\n matcher: "Edit|Write"\n script: {\n path: ".promptscript/scripts/validate.py"\n interpreter: "python3"\n args: ["--strict"]\n }\n cwd: "project"\n timeoutMs: 120000\n statusMessage: "Checking TypeScript"\n continueOnFailure: false\n enabled: true\n targets: {\n factory: { matcher: "Execute" }\n vscode: { matcher: "run_in_terminal" }\n github: { enabled: false }\n }\n }\n}\n```\n\nPortable events:\n\n| Event | Meaning |\n| ---------------------- | ------------------------- |\n| `pre-terminal-command` | Before a terminal command |\n| `pre-tool-use` | Before a tool invocation |\n| `post-tool-use` | After a tool invocation |\n| `session-start` | Agent session start |\n| `setup` | Session setup |\n| `subagent-start` | Subagent start |\n| `notification` | Agent notification |\n| `stop` | Agent stop |\n\n`command` is a non-empty string array. Shell interpolation (`$()`, backticks,\n`${...}`) is forbidden. `script` requires:\n\n- `path` under `.promptscript/scripts/`, using forward slashes.\n- Existing regular file at compile time.\n- No traversal, absolute path, invalid segment, or symlink escape.\n- Explicit interpreter: `python3`, `python`, `node`, `deno`, `bun`, `ruby`, `php`,\n `perl`, `bash`, `sh`, `zsh`, `pwsh`, or `powershell`.\n- Optional `args` string array; each argument remains one argument.\n\n`cwd: "project"` runs from project root. Other values are portable forward-slash\npaths relative to project root. Hook config file location does not set command cwd.\nEnvironment-root and Git-root wrappers exit before script or command execution when\nthe required root is unavailable. Native-cwd and workspace-cwd targets retain host\ncwd fields and report `PS4002` when PromptScript cannot verify that cwd.\n`timeoutMs` range is 100-600000. `matcher` uses target-native tool names, so a\nmatcher valid for one target may match nothing on another.\n\n`pre-terminal-command` supplies native defaults: Factory `Execute`, Claude and\nCodex `Bash`, Windsurf `pre_run_command`, Cursor `run_terminal_cmd`, Gemini\n`run_shell_command`, and VS Code `run_in_terminal`. Override a native tool name\nwith `targets.<name>.matcher`. Cursor, Gemini, and VS Code report best-effort\n`PS4002` warnings. GitHub and Grok omit the event with `PS4002`.\n\nTarget overrides may change `event`, `matcher`, `timeoutMs`, `statusMessage`,\n`continueOnFailure`, `enabled`, or `cwd`. Native hook files are emitted only in\ntarget modes that support additional files:\n\n| Target | Hook output | Mode |\n| -------------- | ---------------------------------------------------------------------- | ------------------- |\n| Claude Code | `.claude/settings.json` | `full` |\n| Factory AI | `.factory/hooks.json` | `multifile`, `full` |\n| GitHub Copilot | `.github/hooks/promptscript.json` | `multifile`, `full` |\n| Cursor | `.cursor/hooks.json` | `full` |\n| Codex | `.codex/hooks.json` | `multifile`, `full` |\n| Gemini CLI | `.gemini/settings.json` | `multifile`, `full` |\n| Windsurf | `.windsurf/hooks.json` | `multifile`, `full` |\n| Grok Build | `.grok/hooks/promptscript.json` | `full` |\n| OpenCode | `.opencode/plugins/promptscript.ts` (generated plugin) | `multifile`, `full` |\n| VS Code Agent | `.github/hooks/promptscript-vscode.json` when `vscode` override exists | target-specific |\n\nOpenCode starts generated hook commands asynchronously. It enforces authored\ntimeouts or a 30-second default, then escalates from `SIGTERM` to `SIGKILL`.\nPayloads are bounded by UTF-8 byte length. OpenCode tool hooks expose tool\narguments, session ID, and call ID, but no model or agent context; compilation\nreports that limitation with `PS4002`.\n\nSimple mode and targets without native project hooks report `PS4002` instead of\nsilently dropping hooks. Use `prs compile --watch` as fallback. Plugin-only and\nagent-scoped integrations are not emitted as universal project hooks.\n\nEach generated command carries a PromptScript ownership marker. CLI cleanup removes\nonly marked entries and preserves user hooks/settings. Removing `@hooks` removes a\nfully owned generated hook file and prunes directories left empty. `prs hooks install factory`\nmigrates unambiguous legacy hooks from `.factory/settings.json`; ambiguous\nentries remain for manual review.\n\nFactory compilation performs the same migration when `.factory/hooks.json` is\nabsent. Use `prs compile --dry-run` to preview the changes or\n`--no-migrate-factory-hooks` to keep warning-only behavior. Unknown events,\nmalformed entries, and mixed ownership abort without a partial migration.\n\n`@hooks` compilation is separate from `prs hooks install`. The latter installs\nauto-compilation and generated-output protection for supported AI tools. Copilot VS\nCode Agent hooks use `promptscript-vscode.json`; GitHub Copilot repository hooks use\n`promptscript.json`.\n\n### @mcpServers\n\nProject-local Model Context Protocol servers. Requires syntax `1.4.0`.\n\n```\n@mcpServers {\n issue-tracker: {\n transport: "stdio"\n command: ["node", "./tools/issues.mjs"]\n env: { LOG_LEVEL: "info" }\n }\n}\n```\n\nUse `stdio` with `command`, or `http`/`sse` with `url`. Keep credentials out of\n`.prs` files and provide them through target-native secret management.\n\n### @plugins\n\nPortable capability bundles. Requires syntax `1.4.0`.\n\n```\n@plugins {\n security-suite: {\n description: "Security review tooling"\n version: "1.0.0"\n skills: ["security-review"]\n hooks: ["validate-types"]\n mcpServers: ["issue-tracker"]\n }\n}\n```\n\n### @local\n\nPrivate local configuration. Not included in compiled output or committed to git.\n\n## Inheritance and Composition\n\n### @inherit (single, linear)\n\nOne per file. Child blocks merge on top of parent:\n\n```\n@inherit @company/frontend-team\n@inherit ./parent\n@inherit @stacks/react-app(projectName: "my-app", port: 3000)\n```\n\n### @use (multiple, mixins)\n\nImport and merge fragments:\n\n```\n@use @core/security\n@use @core/quality\n@use ./local-config\n@use @core/typescript as ts # alias enables @extend access\n```\n\n#### URL imports (Go-module style)\n\nImport directly from any Git repository by host path - no alias required:\n\n```\n@use github.com/acme/shared-standards/@fragments/security\n@use gitlab.com/myorg/prompts/@stacks/python\n```\n\nVersion pinning with `@`:\n\n```\n@use github.com/acme/shared-standards/@org/base@1.2.0 # exact version\n@use github.com/acme/shared-standards/@org/base@^1.0.0 # semver range\n@use github.com/acme/shared-standards/@org/base@main # branch\n```\n\n#### Registry aliases\n\nShort names for Git repository URLs, configured in `promptscript.yaml`:\n\n```yaml\nregistries:\n company:\n url: github.com/acme/promptscript-registry\n```\n\nThen use the alias as scope prefix:\n\n```\n@use @company/security\n@inherit @company/base-config\n```\n\nMerge rules:\n\n- Text: concatenated with deduplication\n- Objects: deep merged (imported source wins same-shape conflicts)\n- Arrays: unique concatenation\n- Shape mismatch: existing target body wins\n\nUnder syntax `1.5.0`, later local blocks, `@extend`, and `@override`\noperations apply to the accumulated import result in declaration order.\n\n### Block Filtering\n\nControl which blocks are imported using the reserved `only` and `exclude` parameters:\n\n```\n@use ./shared-config(only: ["skills", "context"])\n@use ./shared-config(exclude: ["knowledge"])\n@use ./shared-config(exclude: ["knowledge"], mode: "strict")\n```\n\nRules:\n\n- `only` and `exclude` are mutually exclusive \u2014 using both is a validation error (PS021)\n- Values are block type names: `identity`, `context`, `standards`, `knowledge`, `skills`, `shortcuts`, `agents`, etc.\n- Block filtering does not apply to `@inherit` directives\n\n### Markdown Imports\n\nImport skills directly from `.md` files (v1.8+). No external tools needed:\n\n```\n@use ./skills/frontend-design.md\n@use ./shared/commit.md as commit\n@use github.com/anthropics/skills/commit@1.0.0\n@use github.com/repo/skills/gitnexus # directory \u2192 SKILL.md\n```\n\nContent detection: PromptScript blocks in `.md` are parsed as a `.prs` fragment;\nYAML frontmatter with `name`/`description` is loaded as a skill definition;\notherwise content is treated as free-form knowledge.\n\nCLI management:\n\n```\nprs skills add github.com/anthropics/skills/commit@1.0.0\nprs skills remove commit\nprs skills list\nprs skills update\n```\n\n### @extend (modify existing or imported blocks)\n\nUse a direct path for inherited or local blocks:\n\n```\n@extend standards.testing {\n coverage: 95\n}\n```\n\nUse an alias when targeting a specific imported block:\n\n```\n@use @core/typescript as ts\n\n@extend ts.standards {\n testing: { coverage: 95 }\n}\n```\n\n#### Replacing regular block fields\n\nSyntax `1.3.0` supports explicit replacement of complete regular block field values:\n\n```\n@meta { id: "project" syntax: "1.3.0" }\n\n@inherit ./company-base\n\n@extend standards {\n testing!: ["Use Vitest"]\n linting: ["Use ESLint"]\n}\n```\n\n`testing!` replaces the inherited value. Fields without `!` keep normal merge behavior.\nReplacement works after `@inherit` and `@use`, including aliases and nested target paths.\nA missing field is set. The modifier is rejected for `@skills`, which retain their dedicated\nmerge and sealing semantics.\n\n#### Replacing complete targets with @override\n\nSyntax `1.5.0` adds atomic replacement for an existing block or nested value:\n\n```\n@meta { id: "project" syntax: "1.5.0" }\n\n@standards {\n testing: ["Use Jest", "Use Mocha"]\n}\n\n@override standards.testing {\n ["Use Vitest"]\n}\n```\n\n`@override` requires the complete target path to exist, applies in declaration\norder, and cannot bypass sealed skill properties. Later `@extend` declarations\nmerge into the replacement. Use `@extend` for additive changes, `field!` for\ncompatibility replacement of one direct regular field, and `@override` for\nintentional complete replacement.\n\n#### Skill-aware @extend semantics\n\nWhen extending a skill definition via `@extend`, individual skill properties follow specific merge\nstrategies rather than the generic block merge rules:\n\n| Strategy | Properties |\n| ----------------- | ----------------------------------------------------------------------------------------------------------- |\n| **Replace** | content, description, trigger, userInvocable, allowedTools, disableModelInvocation, context, agent, license |\n| **Append** | references, examples, requires |\n| **Shallow merge** | params, inputs, outputs |\n\nExample \u2014 extending a base skill to add references and override content:\n\n```\n@use @company/skills as skills\n\n@extend skills.code-review {\n content: (triple-quoted text with overridden instructions)\n references: [\n ./extra-context.md\n ]\n}\n```\n\nThe `references` array from the base skill and the overlay are combined (append). The `content`\nfield from the overlay replaces the base (replace).\n\n#### Reference negation\n\nUse `!` prefix in `@extend` to remove entries from a lower layer\'s append-strategy arrays:\n\n```\n@extend skills.code-review {\n references: [\n "!references/deprecated.md"\n "references/replacement.md"\n ]\n}\n```\n\nPath matching is normalized (`"!./foo.md"` matches `"foo.md"`). Only works in `@extend` blocks\non `references` and `requires`. Validator PS028 warns about `!` in base definitions.\n\n#### Overlay consistency warnings\n\nThe resolver emits warnings during compile when an overlay drifts from its base. Always shown\n(not gated by `--verbose`):\n\n- **Orphaned extend** \u2014 `@extend target "X" not found \u2014 overlay will be ignored.` Triggered when\n the targeted block doesn\'t exist (base removed or renamed).\n- **Stale skill target** \u2014 `@extend creates new skill "X" \u2014 base does not define it.` Triggered\n when an `@extend` inside `@skills` would create a new skill instead of extending an existing one.\n- **Negation orphan** \u2014 `Negation "!path" did not match any base entry \u2014 it may be stale.`\n Triggered when a `!entry` in references/requires doesn\'t match anything in the base.\n\nThese come from the resolver, not the validator (PS0XX rules). They appear during `prs compile`,\nnot `prs validate`.\n\n#### Sealed properties\n\nPrevent `@extend` from overriding specified replace-strategy properties:\n\n```\n@skills {\n deploy: {\n content: (triple-quoted text with critical workflow)\n sealed: ["content", "description"]\n }\n}\n```\n\n`sealed: true` seals all replace-strategy properties. Attempting to override a sealed\nproperty is a hard compilation error. Only the base skill author can set `sealed` \u2014\noverlays cannot add or modify it. Append-strategy properties remain extendable.\nValidator PS029 warns about invalid entries in `sealed`.\n\n#### Skill composition (inline @use)\n\nImport sub-skills within a `@skills` block to compose multi-phase workflows:\n\n```\n@skills {\n ops: {\n description: "Production triage"\n content: (triple-quoted text with orchestrator instructions)\n }\n @use ./phases/health-scan\n @use ./phases/triage\n @use ./phases/code-fix as autofix\n}\n```\n\nEach `@use` resolves the referenced `.prs` file, extracts its skill definition and context\nblocks, and flattens them as numbered phase sections into the parent skill\'s content. The\n`as alias` form controls the phase display name. Validator PS027 checks composition validity.\n\n### Parameterized Inheritance (Template Variables)\n\nUse `{{variable}}` placeholders in a **parent/template** file, and pass values\nfrom the **child** file via `@inherit` or `@use` with `(key: value)` syntax.\n\n**IMPORTANT:** Variables are NOT set from `promptscript.yaml` or CLI. They are\npassed from one `.prs` file to another through `@inherit` or `@use`.\n\n**Step 1: Create the template** (parent file with `params` in `@meta`):\n\n```\n# base.prs - reusable template\n@meta {\n id: "service-template"\n syntax: "1.0.0"\n params: {\n serviceName: string\n port?: number = 3000\n }\n}\n\n@identity {\n """\n You are working on {{serviceName}} running on port {{port}}.\n """\n}\n```\n\n**Step 2: Inherit with values** (child file passes params):\n\n```\n# project.prs - concrete project\n@meta { id: "user-api" syntax: "1.0.0" }\n\n@inherit ./base(serviceName: "user-api", port: 8080)\n```\n\nAfter compilation, `{{serviceName}}` becomes `user-api` and `{{port}}` becomes `8080`.\n\nThe same works with `@use`:\n\n```\n@use ./base(serviceName: "auth-service") as auth\n```\n\n**Parameter types:** `string`, `number`, `boolean`, `enum("a", "b")`.\nOptional params use `?` suffix. Defaults use `= value`.\nMissing required params produce a compile error.\n\n**Multi-service pattern** - reuse one template across many projects:\n\n```\nservices/\n base.prs # template with params\n user-api/\n promptscript.yaml # source: project.prs\n project.prs # @inherit ../base(serviceName: "user-api")\n auth-service/\n promptscript.yaml\n project.prs # @inherit ../base(serviceName: "auth-service")\n```\n\n## Configuration: promptscript.yaml\n\n### Auto-injection\n\nThis skill is automatically included when compiling with `prs compile`. No manual copying needed.\nTo disable, set `includePromptScriptSkill: false` in your `promptscript.yaml`.\n\n```\nid: my-project\nsyntax: "1.1.0"\ndescription: "My project description"\ninput:\n entry: .promptscript/project.prs\n include: [\'.promptscript/**/*.prs\']\ntargets:\n github:\n version: full # simple | multifile | full\n claude:\n version: full\n cursor:\n version: standard\n antigravity:\n version: frontmatter\n factory:\n version: full\n windsurf: # 41 additional targets supported\n version: simple\n cline:\n version: simple\nregistry:\n git: https://github.com/org/registry.git\n ref: main\nregistries:\n company:\n url: github.com/acme/promptscript-registry\n oss:\n url: github.com/prscrpt/community-registry\n ref: v2\npolicies:\n - name: adjacent-layers-only\n kind: layer-boundary\n severity: error\n layers: [\'@core\', \'@team\', \'@project\']\n maxDistance: 1\n```\n\n### Lockfile: `promptscript.lock`\n\nWhen remote imports are used, run `prs lock` to generate or update the lockfile\nbefore compilation. It records the exact resolved commit for each dependency.\nIntegrity hashes (SHA-256) are included for registry references to detect\ntampering or drift. This enables reproducible builds across machines and CI.\nCommit `promptscript.lock` to version control.\n\nUse `--ignore-hashes` on `prs compile` or `prs validate` to skip integrity\nhash verification when needed.\n\n### Policy Engine\n\nDefine organizational policies in `promptscript.yaml` to validate skill extensions:\n\n```yaml\npolicies:\n - name: adjacent-layers-only\n kind: layer-boundary\n description: \'Only adjacent layers can extend each other\'\n severity: error\n layers: [\'@core\', \'@team\', \'@project\']\n maxDistance: 1\n\n - name: protect-content\n kind: property-protection\n description: \'Content override requires explicit approval\'\n severity: warning\n properties: [\'content\', \'description\']\n\n - name: approved-registries\n kind: registry-allowlist\n description: \'Extensions must come from approved registries\'\n severity: error\n allowed: [\'@core\', \'@team\']\n```\n\nPolicy kinds: `layer-boundary` (controls layer distance), `property-protection`\n(prevents overriding specific properties), `registry-allowlist` (restricts extension sources).\nSeverity: `error` (fails validation) or `warning` (reported only).\nSkip with `--skip-policies` during development (never in CI).\n\n## Syntax Version Validation\n\nThe `syntax` field in `@meta` declares the PromptScript language version (semver).\n\n### Known Versions\n\n| Version | What it adds |\n| ------- | ----------------------------------------------------------------------------------------------------------------------- |\n| `1.0.0` | Core blocks (identity, context, standards, restrictions, knowledge, shortcuts, commands, guards, params, skills, local) |\n| `1.1.0` | Adds `@agents` and `@workflows`; reserves internal `@prompts` |\n| `1.2.0` | Adds `@examples` (few-shot input/output pairs) |\n| `1.3.0` | Adds explicit regular block field replacement in `@extend` |\n| `1.4.0` | Adds `@hooks`, `@mcpServers`, and `@plugins` |\n| `1.5.0` | Adds `@header` section titles, `@override` replacement, and unquoted `${VAR}` values |\n\n### Block Version Requirements\n\n| Block | Minimum Syntax Version |\n| ------------- | ---------------------- |\n| `@agents` | `1.1.0` |\n| `@workflows` | `1.1.0` |\n| `@examples` | `1.2.0` |\n| `@hooks` | `1.4.0` |\n| `@mcpServers` | `1.4.0` |\n| `@plugins` | `1.4.0` |\n\nAll other built-in blocks are available from `1.0.0`.\nRegular block field replacement with `field!: value` requires syntax `1.3.0`.\nGenerated section title overrides with `@header` require syntax `1.5.0`.\nAtomic replacement with `@override` requires syntax `1.5.0`.\nUnquoted `${VAR}` references as values require syntax `1.5.0`.\n\n### Generated Section Headers\n\nUse `@header` inside a registered owner block to rename human-readable output\nsections without changing filenames, frontmatter, XML tags, or structured keys:\n\n```promptscript\n@meta { id: "localized" syntax: "1.5.0" }\n\n@standards {\n @header "Coding Rules"\n @header git-commits "Commit Rules"\n code: ["Use strict TypeScript"]\n}\n```\n\n- `@header "Title"` targets the block\'s primary section.\n- `@header <section-key> "Title"` targets an owned derived section.\n- Titles must be non-empty, single-line strings.\n- Source overrides take precedence over formatter configuration and target defaults.\n- Child inheritance, imported source, and the latest root extension take precedence.\n- An initial `## Heading` in a registered text-only primary owner is a syntax\n `1.5.0` compatibility fallback. Explicit `@header` metadata wins.\n- Ordinary `header` and `headers` fields remain domain data.\n\n### Validation Rules\n\n- **PS018 (`syntax-version-compat`)**: warns when resolved blocks or syntax features require a higher version than declared. Requirements from inheritance, imports, and skill composition are included. Suggestion: run `prs validate --fix`.\n- **PS019 (`unknown-block-name`)**: warns when a block name is not a known PromptScript type, with fuzzy-match suggestions for typos.\n- **PS037 (`valid-section-headers`)**: rejects invalid titles, unknown or unowned section keys, duplicate overrides, and nested extension overrides.\n- **PS038 (`valid-block-shape`)**: rejects unsupported built-in block shapes and warns about formatter-sensitive legacy shapes or multiline shortcut scalars.\n- **PS039 (`agent-namespaces`)**: validates qualified agent name segments and checks them against recorded import provenance.\n- **PS040 (`import-excludes`)**: errors when a `validation.excludes` entry for an import does not record the commit pinned in promptscript.lock, so consumers re-review imports whose pinned commit changed.\n- **PS021 (`use-block-filter`)**: errors when `only` and `exclude` are both specified in `@use` parameters.\n- **PS025 (`valid-skill-references`)**: errors when a `references` entry points to a file with a disallowed extension or a path that cannot be resolved.\n- **PS026 (`safe-reference-content`)**: warns when a referenced file contains potentially sensitive content (e.g., secrets, credentials).\n- **PS027 (`valid-skill-composition`)**: warns about conflicting phase names or excessive phases in composed skills.\n- **PS028 (`valid-append-negation`)**: warns when negation prefix `!` appears in base skill definitions (only effective in `@extend`).\n- **PS029 (`valid-sealed-property`)**: warns when `sealed` contains non-replace-strategy property names.\n- **PS030 (`policy-compliance`)**: validates skill extensions against organizational policies defined in `promptscript.yaml`.\n- **PS034 (`valid-hooks`)**: validates portable hook events, commands/scripts, paths, interpreters, timeouts, cwd, and target overrides.\n\nTarget formatters report **PS4002** when a hook event or field has no native equivalent,\nwhen a target cannot guarantee project-root execution, or when output mode cannot emit\nthe additional hook file.\n\n### Fixing Syntax Versions\n\n```\nprs validate --fix # Auto-fix syntax versions in .prs files\nprs upgrade # Upgrade all .prs files to the latest version\n```\n\n`--fix` rewrites the `syntax: "..."` line in each file\'s `@meta` block to match the minimum version required by resolved blocks and syntax features. It follows inheritance, imports, and skill composition. It only upgrades, never downgrades.\n\n`prs upgrade` upgrades all files to the latest known syntax version regardless of what blocks they use.\n\n## CLI Commands\n\n```\nprs init # Initialize project (auto-detects existing files)\nprs init --yes --targets claude factory\nprs init --dry-run # Preview initialization\nprs init --auto-import # Initialize + static import of existing files\nprs migrate # Interactive migration flow\nprs migrate --static # Non-interactive static import\nprs migrate --llm # Generate AI-assisted migration prompt\nprs migrate --static --dry-run\nprs compile # Compile to all targets\nprs compile --watch # Watch mode\nprs compile --ignore-hashes # Skip integrity hash verification\nprs build <name> # Compile a named build profile\nprs validate --strict # Validate syntax\nprs validate --fix # Auto-fix syntax version declarations\nprs validate --skip-policies # Skip policy engine evaluation\nprs upgrade # Upgrade all .prs files to latest syntax version\nprs import CLAUDE.md # Import existing AI instructions\nprs import CLAUDE.md --dry-run # Preview import conversion\nprs inspect <skill> # Show skill composition provenance\nprs inspect <skill> --layers # Show layer-level breakdown\nprs explain <path> # Explain source and composition provenance\nprs explain <path> --format json # Machine-readable provenance history\nprs hooks install # Install auto-compilation hooks for AI tools\nprs hooks install claude # Install hooks for a specific tool\nprs hooks uninstall # Remove installed auto-compilation hooks\nprs hooks uninstall claude # Remove hooks for a specific tool\nprs skills add <source> # Add a remote skill (@use + lock update + SKILL.md validation)\nprs skills add <source> --strict # Treat validation warnings as errors\nprs skills add <source> --skip-validation # Bypass Agent Skills spec checks (not recommended)\nprs skills remove <name> # Remove a skill (@use line + lock entry)\nprs skills list # List all imported skills\nprs skills update # Re-resolve markdown-imported skills (re-validates + re-hashes)\nprs pull # Update registry\nprs diff --target claude # Show compilation diff\nprs diff --all --format json # Machine-readable diff report for CI\nprs diff --format json --include-content # Include generated content in the report\nprs lock # Generate/update promptscript.lock\nprs lock --dry-run # Preview lockfile changes\nprs update # Re-resolve all remote imports to latest\nprs update <url> # Update a specific registry\nprs vendor sync # Copy cached deps to .promptscript/vendor/\nprs vendor check # Verify vendor matches lockfile\nprs resolve @alias/path # Debug: show how an import resolves\nprs registry list # Show configured registries and aliases\nprs registry add <alias> <url> # Add a registry alias\n```\n\n`prs init --yes` requires explicit, detected, or user-configured targets. It does not invent\ndefault tools. For existing projects, `prs migrate` preserves `promptscript.yaml`, isolates static\noutput under `.promptscript/migrated/`, leaves source instructions untouched, and performs no\nwrites when no candidates are detected.\n\n## Output Targets\n\n50 supported targets. Key examples:\n\n| Target | Main File | Skills |\n| ----------- | ------------------------------- | -------------------------------------------------- |\n| GitHub | .github/copilot-instructions.md | .github/skills/\\*/SKILL.md |\n| Claude | CLAUDE.md | .claude/skills/\\*/SKILL.md |\n| Cursor | .cursor/rules/project.mdc | .agents/skills/\\*/SKILL.md |\n| Antigravity | .agent/rules/project.md | - |\n| Factory | AGENTS.md | .factory/skills/\\*/SKILL.md, .factory/droids/\\*.md |\n| OpenCode | OPENCODE.md | .opencode/skills/\\*/SKILL.md |\n| Gemini | GEMINI.md | .agents/skills/\\*/skill.md |\n| Windsurf | .windsurf/rules/project.md | .windsurf/skills/\\*/SKILL.md |\n| Cline | .clinerules | - |\n| Roo Code | .roorules | - |\n| Codex | AGENTS.md | .agents/skills/\\*/SKILL.md |\n| Continue | .continue/rules/project.md | - |\n| Hermes | AGENTS.md | - |\n| + 37 more | | See full list in documentation |\n\nTargets that share an output path (for example Factory, Codex, and every AGENTS.md target) are\nreconciled in one output plan before anything is written. Identical content merges silently;\ndiffering content reports `PS4001`, where a formatter output replaces an earlier one and a\nresource or the auto-injected PromptScript skill keeps the file already planned. Paths are compared\ncase-insensitively and NFC-normalized on every platform.\n\n### Formatter Documentation\n\nFor detailed information about each formatter\'s output paths, supported features, quirks, and example outputs:\n\n- **Full formatter reference:** `docs/reference/formatters/` (7 dedicated pages + index of all 50)\n- **llms-full.txt:** Available at the docs site root - contains all documentation in a single file for LLM consumption\n- **Dedicated pages exist for:** Claude Code, GitHub Copilot, Cursor, Antigravity, Factory AI, Gemini CLI, OpenCode\n- **All 50 formatters indexed at:** `docs/reference/formatters/index.md` with output paths, tier, and feature flags\n\n### Auto-Compilation Hooks\n\nInstead of running `prs compile --watch` manually, install hooks so your AI tool\ntriggers compilation automatically when you edit `.prs` files:\n\n```\nprs hooks install # Auto-detect and install for all detected tools\nprs hooks install claude # Install for a specific tool\n```\n\nHooks also protect generated files from direct edits \u2014 when an AI agent tries\nto edit a compiled output (e.g., CLAUDE.md), the write is blocked with a message\npointing to the source `.prs` file. Supported tools: Claude Code, Factory AI,\nCursor, Windsurf, Cline, GitHub Copilot, Gemini CLI.\n\n## Project Organization\n\nTypical modular structure:\n\n```\n.promptscript/\n project.prs # Entry: @meta, @inherit, @use, @identity, @agents\n context.prs # @context (architecture, tech stack)\n standards.prs # @standards (coding conventions)\n restrictions.prs # @restrictions (hard rules)\n commands.prs # @shortcuts and @knowledge\n```\n\nThe entry file uses `@use ./context`, `@use ./standards`, etc. to compose them.\n\n## Common Mistakes\n\n1. Missing @meta block - every .prs file needs `@meta` with `id` and `syntax`\n2. Multiple @inherit - only one per file; use `@use` for additional imports\n3. Extending an unknown path - target an inherited or local block, or use an imported alias\n4. Unquoted strings with special chars - quote strings containing `:`, `#`, `{`, `}`\n5. Forgetting to compile - `.prs` changes need `prs compile` to take effect\n6. Triple quotes inside triple quotes - not supported; describe content textually instead\n7. Using `{{var}}` in the root file without `@inherit` - template variables only work\n in a parent file that defines `params` in `@meta`, with values passed by the child\n via `@inherit ./parent(key: value)` or `@use ./fragment(key: value)`. They are NOT\n set from `promptscript.yaml` or CLI flags\n8. Using `@examples` with `syntax: "1.0.0"` or `"1.1.0"` - `@examples` requires\n syntax version `1.2.0`. Run `prs validate --fix` to auto-upgrade\n\n## Migrating Existing AI Instructions to PromptScript\n\n### Automated: `prs import`\n\nThe fastest way to convert existing AI instructions to PromptScript:\n\n```\nprs import CLAUDE.md # Convert a single file\nprs import .github/copilot-instructions.md\nprs import AGENTS.md --output ./imported\nprs import --dry-run CLAUDE.md # Preview without writing\n```\n\n`prs import` automatically:\n\n- Detects the source format (Claude, GitHub Copilot, Cursor, Factory, etc.)\n- Maps content to appropriate PromptScript blocks (@identity, @standards, etc.)\n- Generates a valid `.prs` file with `@meta` block\n- Preserves the original intent and structure\n\nSupported source formats:\n\n- `CLAUDE.md` (Claude Code)\n- `.github/copilot-instructions.md` (GitHub Copilot)\n- `.cursorrules` or `.cursor/rules/*.mdc` (Cursor)\n- `AGENTS.md` (Factory AI / Codex)\n- `.clinerules` (Cline), `.roorules` (Roo Code)\n- `.windsurf/rules/*.md` (Windsurf)\n- Any Markdown-based AI instruction file\n\n### Manual Migration\n\nFor complex migrations or when `prs import` needs refinement:\n\n| Source Pattern | PromptScript Block |\n| ----------------------------------- | ------------------ |\n| "You are..." persona text | `@identity` |\n| Project description, tech stack | `@context` |\n| Coding conventions, style rules | `@standards` |\n| "Never...", "Always...", hard rules | `@restrictions` |\n| `/command` definitions | `@shortcuts` |\n| Skill/tool definitions | `@skills` |\n| Agent/subagent configs | `@agents` |\n| Reference docs, API specs | `@knowledge` |\n\nAfter import, split into modular files (`context.prs`, `standards.prs`, etc.)\nand compose with `@use` in `project.prs`. Run `prs validate --strict` then\n`prs compile` to verify output matches the original.\n';
|
|
8703
|
+
PROMPTSCRIPT_SKILL_CONTENT = '---\nname: promptscript\ndescription: >-\n PromptScript language expert for reading, writing, modifying, and\n troubleshooting .prs files. Use when working with PromptScript syntax,\n creating or editing .prs files, adding blocks like @identity, @standards,\n @restrictions, @shortcuts, @skills, or @agents, configuring\n promptscript.yaml, resolving compilation errors, understanding inheritance\n (@inherit), composition (@use, @extend, @override), contextual @header\n metadata, or migrating AI instructions\n to PromptScript. Also use when asked about the 50 built-in compilation\n targets, including GitHub Copilot, Claude Code, Cursor, Antigravity,\n Factory AI, and AGENTS.md-based platforms.\nlicense: MIT\nmetadata:\n author: PromptScript\n homepage: https://getpromptscript.dev\ncompatibility:\n - claude-code\n - github-copilot\n - cursor\n - factory-ai\n - gemini-cli\n - opencode\n - windsurf\n - cline\n - roo\n - codex\n - continue\n - augment\n - goose\n - kilo\n - amp\n - trae\n - junie\n - kiro-cli\nallowed-tools:\n - Read\n - Write\n - Glob\n - Grep\n - Bash\nuser-invocable: true\n---\n\n# PromptScript Language Guide\n\nPromptScript is a domain-specific language that compiles `.prs` files into native instruction formats for AI coding assistants (GitHub Copilot, Claude Code, Cursor, Antigravity, Factory AI, OpenCode, Gemini CLI). One source of truth, multiple outputs.\n\n## File Structure\n\nA `.prs` file contains ordered declarations. Syntax `1.5.0` applies `@inherit`,\n`@use`, local blocks, `@extend`, and `@override` in source order. Put `@meta`\nfirst.\n\n```\n# Comments start with #\n\n@meta { ... } # Required metadata\n@inherit @path # Single inheritance (optional)\n@use @path [as alias] # Imports/mixins (optional, multiple)\n\n@identity { ... } # AI persona\n@context { ... } # Project context\n@standards { ... } # Coding conventions\n@restrictions { ... } # Hard rules\n@shortcuts { ... } # Command aliases\n@knowledge { ... } # Reference documentation\n@skills { ... } # Reusable skill definitions\n@agents { ... } # Subagent definitions\n@workflows { ... } # Repeatable agent procedures\n@examples { ... } # Few-shot input/output examples (syntax 1.2.0+)\n@params { ... } # Template parameters\n@guards { ... } # File globs and priorities\n@hooks { ... } # Portable lifecycle hooks (syntax 1.4.0+)\n@mcpServers { ... } # MCP server configurations (syntax 1.4.0+)\n@plugins { ... } # Capability bundles (syntax 1.4.0+)\n@local { ... } # Private config (not committed)\n@extend path { ... } # Modify imported blocks\n@override path { ... } # Replace one complete existing target (syntax 1.5.0+)\n@custom-name { ... } # Arbitrary named blocks\n```\n\nContextual `@header` entries appear inside supported owner blocks, not at the\ntop level.\n\n## Content Types\n\nPromptScript has four canonical content shapes inside blocks:\n\n### Text Content\n\nUse triple quotes (three double-quote characters) to wrap multiline text.\nText is automatically dedented - leading whitespace from source indentation is stripped.\nUse for prose, markdown, or freeform content.\n\nExample: `@identity` with a text block describing an AI persona starting with "You are..."\n\n### Object Content (key-value pairs)\n\n```\n@context {\n project: "My App"\n team: "Frontend"\n monorepo: {\n tool: "Nx"\n packageManager: "pnpm"\n }\n}\n```\n\nValues can be strings (quoted or unquoted), numbers, booleans, nested objects, or arrays.\n\n### Array Content\n\n```\n@standards {\n code: [\n "Use strict TypeScript",\n "Named exports only"\n ]\n}\n\n@restrictions {\n - "Never use any type"\n - "Never commit secrets"\n}\n```\n\n### Mixed Content\n\nBlocks can contain both object properties and text in the same block.\nPlace the triple-quoted text block alongside key-value pairs.\n\n## Block Reference\n\n### @meta (required)\n\n```\n@meta {\n id: "project-id" # Required: unique identifier\n syntax: "1.0.0" # Required: syntax version (semver)\n org: "Company Name" # Optional\n team: "Frontend" # Optional\n tags: [react, ts] # Optional\n params: { # Optional: template parameters\n projectName: string\n port: number = 3000\n debug?: boolean\n framework: enum("react", "vue") = "react"\n }\n}\n```\n\n### @identity\n\nDefines AI persona. Start with "You are..." for consistent output across all formatters.\nContains a triple-quoted text block with the persona description.\n\n### @context\n\nProject context with structured properties (project, team, languages, runtime)\nplus optional triple-quoted text for architecture details, diagrams, etc.\n\n### @standards\n\nCategory-based conventions. Any category name is valid:\n\n```\n@standards {\n typescript: ["Strict mode", "No any type"]\n naming: ["Files: kebab-case.ts", "Classes: PascalCase"]\n git: {\n format: "Conventional Commits"\n types: [feat, fix, docs, refactor, test, chore]\n }\n}\n```\n\nCategory names are arbitrary. `@standards` can also contain free-form text:\n\n```\n@standards {\n """\n ## Formatting\n Preserve heading structure and use four-space indentation.\n\n ## Testing\n Add regression coverage for every behavior change.\n """\n typescript: ["Strict mode", "Named exports only"]\n git: {\n format: "Conventional Commits"\n }\n}\n```\n\nFree-form text is dedented and rendered with its Markdown heading structure. Factory\nmonolith output nests it under `Conventions & Patterns`; split Factory rules adjust\nheading levels relative to the generated section. Custom structured categories remain\navailable to formatters that support them.\n\n### @restrictions\n\nHard rules as a list of dash-prefixed strings:\n\n```\n@restrictions {\n - "Never expose API keys"\n - "Never commit secrets to version control"\n - "Always validate user input"\n}\n```\n\n### @shortcuts\n\nSimple strings appear as documentation. Objects with `prompt: true` generate\nexecutable prompt/command files for GitHub Copilot and Cursor:\n\n```\n@shortcuts {\n "/review": "Review code for quality"\n "/test": {\n prompt: true\n description: "Write unit tests"\n content: (triple-quoted text with instructions)\n }\n}\n```\n\n> `@commands` is a backwards-compatible alias for `@shortcuts` \u2014 prefer `@shortcuts` in new files.\n\n### @skills\n\nReusable skill definitions with metadata:\n\n```\n@skills {\n commit: {\n description: "Create git commits"\n trigger: "commit, git commit"\n disableModelInvocation: true\n userInvocable: true\n allowedTools: ["Bash", "Read"]\n content: (triple-quoted text with skill instructions)\n }\n}\n```\n\nProperties: description (required), content (required), trigger, disableModelInvocation,\nuserInvocable, allowedTools, context ("fork" or "inherit"), agent, model (Claude Code and Grok\nBuild, mapped through the model catalog), requires, references, inputs, outputs.\n\nThe `references` property attaches external files to the skill\'s context:\n\n```\n@skills {\n architecture-review: {\n description: "Review architecture decisions"\n references: [\n ./references/architecture.md\n ./references/modules.md\n ]\n content: (triple-quoted text)\n }\n}\n```\n\nAllowed file types: `.md`, `.json`, `.yaml`, `.yml`, `.txt`, `.csv`. Paths are resolved relative\nto the `.prs` file. Formatters emit referenced files alongside SKILL.md in the output directory.\n\n### Parameterized Skills\n\nSkills in `.promptscript/skills/<name>/SKILL.md` support template parameters via\nYAML frontmatter. Define `params` in frontmatter and use `{{variable}}` in content:\n\n```yaml\n---\nname: review\ndescription: \'Review {{language}} code for {{standard}}\'\nparams:\n language:\n type: string\n standard:\n type: string\n default: \'best practices\'\nreferences:\n - references/architecture.md\n---\nReview the code using {{language}} conventions following {{standard}}.\n```\n\nThe `references` field in SKILL.md frontmatter lists files to attach to the skill\'s context.\nPaths are relative to the SKILL.md file.\n\nImported SKILL.md frontmatter is bounded: 256 KiB per document, 10,000 YAML nodes, 32 nesting\nlevels, 2,000 entries per mapping or sequence, and 64 KiB per string value. Documents over any\nlimit are rejected before their YAML values are converted.\n\nPass values in `@skills` block:\n\n```\n@skills {\n review: {\n description: "Review code"\n language: "typescript"\n standard: "strict mode"\n }\n}\n```\n\nNon-reserved properties (anything other than description, content, trigger,\nuserInvocable, allowedTools, disableModelInvocation, context, agent, requires,\ninputs, outputs, model) are treated as skill parameter arguments.\n\n### Skill Dependencies\n\nSkills can declare dependencies on other skills via `requires`:\n\n```\n@skills {\n deploy: {\n description: "Deploy service"\n requires: ["lint-check", "test-suite"]\n content: (triple-quoted text)\n }\n}\n```\n\nThe validator (PS016) checks that required skills exist, detects self-references,\nand catches circular dependency chains.\n\n### Skill Contracts (Inputs/Outputs)\n\nSkills can declare typed inputs and outputs in SKILL.md frontmatter:\n\n```yaml\n---\nname: security-scan\ndescription: \'Scan for vulnerabilities\'\ninputs:\n files:\n description: \'Files to scan\'\n type: string\n severity:\n description: \'Minimum severity\'\n type: enum\n options: [low, medium, high]\n default: medium\noutputs:\n report:\n description: \'Scan report\'\n type: string\n passed:\n description: \'Whether scan passed\'\n type: boolean\n---\n```\n\nField types: `string`, `number`, `boolean`, `enum` (with `options` list).\nThe validator (PS017) checks field types, ensures enum fields have options,\nand warns if param names collide with input names.\n\n### Shared Resources\n\nSkills in a folder can share common resources via `.promptscript/shared/`:\n\n```\n.promptscript/\n shared/\n templates.md # Shared across all skills\n style-guide.md\n skills/\n review/\n SKILL.md # Gets @shared/templates.md, @shared/style-guide.md\n deploy/\n SKILL.md # Also gets shared resources\n```\n\nFiles in `shared/` are automatically included in every skill with `@shared/` prefix.\n\n### @agents\n\nCustom subagent definitions. Compiles to `.claude/agents/` for Claude Code,\n`.github/agents/` for GitHub Copilot, `.factory/droids/` for Factory AI, etc.\n\n```\n@agents {\n code-reviewer: {\n description: "Reviews code quality"\n tools: ["Read", "Grep", "Glob", "Bash"]\n model: "sonnet"\n permissionMode: "default"\n content: (triple-quoted text with agent instructions)\n }\n}\n```\n\nImported agent definitions are qualified by an aliased `@use`:\n\n```\n@use ./frontend-team as frontend\n@use ./backend-team as backend\n```\n\nIf both imports define `reviewer`, the resolved names are `frontend.reviewer` and\n`backend.reviewer`. Unique unaliased imports keep their original names. Conflicting unaliased\ndefinitions stop compilation with source and import diagnostics instead of silently overwriting\none another. Native targets map dots to hyphens, so `frontend.reviewer` becomes\n`frontend-reviewer`.\n\nSupports mixed models per agent: `specModel` sets a different model for\nSpecification/planning mode (GitHub, Factory), `specReasoningEffort` sets reasoning\neffort for the spec model (Factory only, values: "low", "medium", "high").\n\nFactory AI droids support additional properties: `model` (any model ID or "inherit"),\n`reasoningEffort` ("low", "medium", "high"), and `tools` (category name like "read-only"\nor array of tool IDs).\n\nAgent `model`/`specModel` and skill `model` values resolve against the model catalog\n(built-in profiles plus `models.profiles` in promptscript.yaml). Write a floating alias\n(`sonnet`, `opus`, `haiku`, `fable` - newest Claude release), a pinned model (profile id,\nalias, API id, or display name such as `claude-opus-5-5` or `Claude Opus 5.5`), or\n`inherit`. Each target gets its native name: Claude Code keeps aliases and uses API ids\nfor pinned Claude models, GitHub Copilot gets display names (`Claude Sonnet 5`), Factory\nAI and Codex get API ids, and Cursor gets dateless ids. Names outside the catalog pass\nthrough unchanged, unless the target has its own spelling for them (GitHub Copilot writes\n`auto` as `Auto`). A model from a provider the target cannot run (a GPT model on Claude\nCode, a Claude model on Codex), or a name with a line break or control character, is\nomitted with a PS4004 warning.\n\n### @workflows\n\nRepeatable multi-step agent procedures. Requires syntax `1.1.0`.\n\n```\n@workflows {\n release: {\n description: "Prepare a validated release"\n content: """\n 1. Run formatting, linting, type checks, and tests.\n 2. Validate compiled output.\n 3. Stop before publishing and request approval.\n """\n }\n}\n```\n\nTargets with native workflow discovery emit dedicated workflow files. Other targets\nretain workflow instructions in their main output when supported.\n\n### @examples\n\nStructured few-shot examples for AI assistants (requires syntax `1.2.0`):\n\n```\n@meta {\n id: "commit-style"\n syntax: "1.2.0"\n}\n\n@examples {\n feat-commit: {\n description: "Feature commit with scope"\n input: "Added user authentication with JWT tokens"\n output: "feat(auth): add JWT-based user authentication"\n }\n}\n```\n\nEach entry is a named example with `input` and `output` (both required),\nplus optional `description`. Multi-line content uses triple-quoted strings.\n\nExamples can also be attached to skills via the `examples` property:\n\n```\n@skills {\n commit: {\n description: "Create conventional commits"\n examples: {\n basic: {\n input: "Added dark mode toggle"\n output: "feat(settings): add dark mode toggle"\n }\n }\n content: (triple-quoted text)\n }\n}\n```\n\n### @knowledge\n\nReference documentation as triple-quoted text. Used for command references,\nAPI docs, and other material that should appear in the output.\n\n### @params\n\nTemplate parameter definitions with types: string, number, boolean, enum("a", "b").\nOptional parameters use `?` suffix. Defaults use `= value`.\n\n### @guards\n\nFile glob patterns and priority rules for path-specific instructions.\n\n### @hooks\n\nPortable lifecycle hooks. Requires syntax `1.4.0`. Each hook needs exactly one of\n`command` or `script`.\n\n```\n@hooks {\n validate-types: {\n event: "post-tool-use"\n matcher: "Edit|Write"\n script: {\n path: ".promptscript/scripts/validate.py"\n interpreter: "python3"\n args: ["--strict"]\n }\n cwd: "project"\n timeoutMs: 120000\n statusMessage: "Checking TypeScript"\n continueOnFailure: false\n enabled: true\n targets: {\n factory: { matcher: "Execute" }\n vscode: { matcher: "run_in_terminal" }\n github: { enabled: false }\n }\n }\n}\n```\n\nPortable events:\n\n| Event | Meaning |\n| ---------------------- | ------------------------- |\n| `pre-terminal-command` | Before a terminal command |\n| `pre-tool-use` | Before a tool invocation |\n| `post-tool-use` | After a tool invocation |\n| `session-start` | Agent session start |\n| `setup` | Session setup |\n| `subagent-start` | Subagent start |\n| `notification` | Agent notification |\n| `stop` | Agent stop |\n\n`command` is a non-empty string array. Shell interpolation (`$()`, backticks,\n`${...}`) is forbidden. `script` requires:\n\n- `path` under `.promptscript/scripts/`, using forward slashes.\n- Existing regular file at compile time.\n- No traversal, absolute path, invalid segment, or symlink escape.\n- Explicit interpreter: `python3`, `python`, `node`, `deno`, `bun`, `ruby`, `php`,\n `perl`, `bash`, `sh`, `zsh`, `pwsh`, or `powershell`.\n- Optional `args` string array; each argument remains one argument.\n\n`cwd: "project"` runs from project root. Other values are portable forward-slash\npaths relative to project root. Hook config file location does not set command cwd.\nEnvironment-root and Git-root wrappers exit before script or command execution when\nthe required root is unavailable. Native-cwd and workspace-cwd targets retain host\ncwd fields and report `PS4002` when PromptScript cannot verify that cwd.\n`timeoutMs` range is 100-600000. `matcher` uses target-native tool names, so a\nmatcher valid for one target may match nothing on another.\n\n`pre-terminal-command` supplies native defaults: Factory `Execute`, Claude and\nCodex `Bash`, Windsurf `pre_run_command`, Cursor `run_terminal_cmd`, Gemini\n`run_shell_command`, and VS Code `run_in_terminal`. Override a native tool name\nwith `targets.<name>.matcher`. Cursor, Gemini, and VS Code report best-effort\n`PS4002` warnings. GitHub and Grok omit the event with `PS4002`.\n\nTarget overrides may change `event`, `matcher`, `timeoutMs`, `statusMessage`,\n`continueOnFailure`, `enabled`, or `cwd`. Native hook files are emitted only in\ntarget modes that support additional files:\n\n| Target | Hook output | Mode |\n| -------------- | ---------------------------------------------------------------------- | ------------------- |\n| Claude Code | `.claude/settings.json` | `full` |\n| Factory AI | `.factory/hooks.json` | `multifile`, `full` |\n| GitHub Copilot | `.github/hooks/promptscript.json` | `multifile`, `full` |\n| Cursor | `.cursor/hooks.json` | `full` |\n| Codex | `.codex/hooks.json` | `multifile`, `full` |\n| Gemini CLI | `.gemini/settings.json` | `multifile`, `full` |\n| Windsurf | `.windsurf/hooks.json` | `multifile`, `full` |\n| Grok Build | `.grok/hooks/promptscript.json` | `full` |\n| OpenCode | `.opencode/plugins/promptscript.ts` (generated plugin) | `multifile`, `full` |\n| VS Code Agent | `.github/hooks/promptscript-vscode.json` when `vscode` override exists | target-specific |\n\nOpenCode starts generated hook commands asynchronously. It enforces authored\ntimeouts or a 30-second default, then escalates from `SIGTERM` to `SIGKILL`.\nPayloads are bounded by UTF-8 byte length. OpenCode tool hooks expose tool\narguments, session ID, and call ID, but no model or agent context; compilation\nreports that limitation with `PS4002`.\n\nSimple mode and targets without native project hooks report `PS4002` instead of\nsilently dropping hooks. Use `prs compile --watch` as fallback. Plugin-only and\nagent-scoped integrations are not emitted as universal project hooks.\n\nEach generated command carries a PromptScript ownership marker. CLI cleanup removes\nonly marked entries and preserves user hooks/settings. Removing `@hooks` removes a\nfully owned generated hook file and prunes directories left empty. `prs hooks install factory`\nmigrates unambiguous legacy hooks from `.factory/settings.json`; ambiguous\nentries remain for manual review.\n\nFactory compilation performs the same migration when `.factory/hooks.json` is\nabsent. Use `prs compile --dry-run` to preview the changes or\n`--no-migrate-factory-hooks` to keep warning-only behavior. Unknown events,\nmalformed entries, and mixed ownership abort without a partial migration.\n\n`@hooks` compilation is separate from `prs hooks install`. The latter installs\nauto-compilation and generated-output protection for supported AI tools. Copilot VS\nCode Agent hooks use `promptscript-vscode.json`; GitHub Copilot repository hooks use\n`promptscript.json`.\n\n### @mcpServers\n\nProject-local Model Context Protocol servers. Requires syntax `1.4.0`.\n\n```\n@mcpServers {\n issue-tracker: {\n transport: "stdio"\n command: ["node", "./tools/issues.mjs"]\n env: { LOG_LEVEL: "info" }\n }\n}\n```\n\nUse `stdio` with `command`, or `http`/`sse` with `url`. Keep credentials out of\n`.prs` files and provide them through target-native secret management.\n\n### @plugins\n\nPortable capability bundles. Requires syntax `1.4.0`.\n\n```\n@plugins {\n security-suite: {\n description: "Security review tooling"\n version: "1.0.0"\n skills: ["security-review"]\n hooks: ["validate-types"]\n mcpServers: ["issue-tracker"]\n }\n}\n```\n\n### @local\n\nPrivate local configuration. Not included in compiled output or committed to git.\n\n## Inheritance and Composition\n\n### @inherit (single, linear)\n\nOne per file. Child blocks merge on top of parent:\n\n```\n@inherit @company/frontend-team\n@inherit ./parent\n@inherit @stacks/react-app(projectName: "my-app", port: 3000)\n```\n\n### @use (multiple, mixins)\n\nImport and merge fragments:\n\n```\n@use @core/security\n@use @core/quality\n@use ./local-config\n@use @core/typescript as ts # alias enables @extend access\n```\n\n#### URL imports (Go-module style)\n\nImport directly from any Git repository by host path - no alias required:\n\n```\n@use github.com/acme/shared-standards/@fragments/security\n@use gitlab.com/myorg/prompts/@stacks/python\n```\n\nVersion pinning with `@`:\n\n```\n@use github.com/acme/shared-standards/@org/base@1.2.0 # exact version\n@use github.com/acme/shared-standards/@org/base@^1.0.0 # semver range\n@use github.com/acme/shared-standards/@org/base@main # branch\n```\n\n#### Registry aliases\n\nShort names for Git repository URLs, configured in `promptscript.yaml`:\n\n```yaml\nregistries:\n company:\n url: github.com/acme/promptscript-registry\n```\n\nThen use the alias as scope prefix:\n\n```\n@use @company/security\n@inherit @company/base-config\n```\n\nMerge rules:\n\n- Text: concatenated with deduplication\n- Objects: deep merged (imported source wins same-shape conflicts)\n- Arrays: unique concatenation\n- Shape mismatch: existing target body wins\n\nUnder syntax `1.5.0`, later local blocks, `@extend`, and `@override`\noperations apply to the accumulated import result in declaration order.\n\n### Block Filtering\n\nControl which blocks are imported using the reserved `only` and `exclude` parameters:\n\n```\n@use ./shared-config(only: ["skills", "context"])\n@use ./shared-config(exclude: ["knowledge"])\n@use ./shared-config(exclude: ["knowledge"], mode: "strict")\n```\n\nRules:\n\n- `only` and `exclude` are mutually exclusive \u2014 using both is a validation error (PS021)\n- Values are block type names: `identity`, `context`, `standards`, `knowledge`, `skills`, `shortcuts`, `agents`, etc.\n- Block filtering does not apply to `@inherit` directives\n\n### Markdown Imports\n\nImport skills directly from `.md` files (v1.8+). No external tools needed:\n\n```\n@use ./skills/frontend-design.md\n@use ./shared/commit.md as commit\n@use github.com/anthropics/skills/commit@1.0.0\n@use github.com/repo/skills/gitnexus # directory \u2192 SKILL.md\n```\n\nContent detection: PromptScript blocks in `.md` are parsed as a `.prs` fragment;\nYAML frontmatter with `name`/`description` is loaded as a skill definition;\notherwise content is treated as free-form knowledge.\n\nCLI management:\n\n```\nprs skills add github.com/anthropics/skills/commit@1.0.0\nprs skills remove commit\nprs skills list\nprs skills update\n```\n\n### @extend (modify existing or imported blocks)\n\nUse a direct path for inherited or local blocks:\n\n```\n@extend standards.testing {\n coverage: 95\n}\n```\n\nUse an alias when targeting a specific imported block:\n\n```\n@use @core/typescript as ts\n\n@extend ts.standards {\n testing: { coverage: 95 }\n}\n```\n\n#### Replacing regular block fields\n\nSyntax `1.3.0` supports explicit replacement of complete regular block field values:\n\n```\n@meta { id: "project" syntax: "1.3.0" }\n\n@inherit ./company-base\n\n@extend standards {\n testing!: ["Use Vitest"]\n linting: ["Use ESLint"]\n}\n```\n\n`testing!` replaces the inherited value. Fields without `!` keep normal merge behavior.\nReplacement works after `@inherit` and `@use`, including aliases and nested target paths.\nA missing field is set. The modifier is rejected for `@skills`, which retain their dedicated\nmerge and sealing semantics.\n\n#### Replacing complete targets with @override\n\nSyntax `1.5.0` adds atomic replacement for an existing block or nested value:\n\n```\n@meta { id: "project" syntax: "1.5.0" }\n\n@standards {\n testing: ["Use Jest", "Use Mocha"]\n}\n\n@override standards.testing {\n ["Use Vitest"]\n}\n```\n\n`@override` requires the complete target path to exist, applies in declaration\norder, and cannot bypass sealed skill properties. Later `@extend` declarations\nmerge into the replacement. Use `@extend` for additive changes, `field!` for\ncompatibility replacement of one direct regular field, and `@override` for\nintentional complete replacement.\n\n#### Skill-aware @extend semantics\n\nWhen extending a skill definition via `@extend`, individual skill properties follow specific merge\nstrategies rather than the generic block merge rules:\n\n| Strategy | Properties |\n| ----------------- | ----------------------------------------------------------------------------------------------------------- |\n| **Replace** | content, description, trigger, userInvocable, allowedTools, disableModelInvocation, context, agent, license |\n| **Append** | references, examples, requires |\n| **Shallow merge** | params, inputs, outputs |\n\nExample \u2014 extending a base skill to add references and override content:\n\n```\n@use @company/skills as skills\n\n@extend skills.code-review {\n content: (triple-quoted text with overridden instructions)\n references: [\n ./extra-context.md\n ]\n}\n```\n\nThe `references` array from the base skill and the overlay are combined (append). The `content`\nfield from the overlay replaces the base (replace).\n\n#### Reference negation\n\nUse `!` prefix in `@extend` to remove entries from a lower layer\'s append-strategy arrays:\n\n```\n@extend skills.code-review {\n references: [\n "!references/deprecated.md"\n "references/replacement.md"\n ]\n}\n```\n\nPath matching is normalized (`"!./foo.md"` matches `"foo.md"`). Only works in `@extend` blocks\non `references` and `requires`. Validator PS028 warns about `!` in base definitions.\n\n#### Overlay consistency warnings\n\nThe resolver emits warnings during compile when an overlay drifts from its base. Always shown\n(not gated by `--verbose`):\n\n- **Orphaned extend** \u2014 `@extend target "X" not found \u2014 overlay will be ignored.` Triggered when\n the targeted block doesn\'t exist (base removed or renamed).\n- **Stale skill target** \u2014 `@extend creates new skill "X" \u2014 base does not define it.` Triggered\n when an `@extend` inside `@skills` would create a new skill instead of extending an existing one.\n- **Negation orphan** \u2014 `Negation "!path" did not match any base entry \u2014 it may be stale.`\n Triggered when a `!entry` in references/requires doesn\'t match anything in the base.\n\nThese come from the resolver, not the validator (PS0XX rules). They appear during `prs compile`,\nnot `prs validate`.\n\n#### Sealed properties\n\nPrevent `@extend` from overriding specified replace-strategy properties:\n\n```\n@skills {\n deploy: {\n content: (triple-quoted text with critical workflow)\n sealed: ["content", "description"]\n }\n}\n```\n\n`sealed: true` seals all replace-strategy properties. Attempting to override a sealed\nproperty is a hard compilation error. Only the base skill author can set `sealed` \u2014\noverlays cannot add or modify it. Append-strategy properties remain extendable.\nValidator PS029 warns about invalid entries in `sealed`.\n\n#### Skill composition (inline @use)\n\nImport sub-skills within a `@skills` block to compose multi-phase workflows:\n\n```\n@skills {\n ops: {\n description: "Production triage"\n content: (triple-quoted text with orchestrator instructions)\n }\n @use ./phases/health-scan\n @use ./phases/triage\n @use ./phases/code-fix as autofix\n}\n```\n\nEach `@use` resolves the referenced `.prs` file, extracts its skill definition and context\nblocks, and flattens them as numbered phase sections into the parent skill\'s content. The\n`as alias` form controls the phase display name. Validator PS027 checks composition validity.\n\n### Parameterized Inheritance (Template Variables)\n\nUse `{{variable}}` placeholders in a **parent/template** file, and pass values\nfrom the **child** file via `@inherit` or `@use` with `(key: value)` syntax.\n\n**IMPORTANT:** Variables are NOT set from `promptscript.yaml` or CLI. They are\npassed from one `.prs` file to another through `@inherit` or `@use`.\n\n**Step 1: Create the template** (parent file with `params` in `@meta`):\n\n```\n# base.prs - reusable template\n@meta {\n id: "service-template"\n syntax: "1.0.0"\n params: {\n serviceName: string\n port?: number = 3000\n }\n}\n\n@identity {\n """\n You are working on {{serviceName}} running on port {{port}}.\n """\n}\n```\n\n**Step 2: Inherit with values** (child file passes params):\n\n```\n# project.prs - concrete project\n@meta { id: "user-api" syntax: "1.0.0" }\n\n@inherit ./base(serviceName: "user-api", port: 8080)\n```\n\nAfter compilation, `{{serviceName}}` becomes `user-api` and `{{port}}` becomes `8080`.\n\nThe same works with `@use`:\n\n```\n@use ./base(serviceName: "auth-service") as auth\n```\n\n**Parameter types:** `string`, `number`, `boolean`, `enum("a", "b")`.\nOptional params use `?` suffix. Defaults use `= value`.\nMissing required params produce a compile error.\n\n**Multi-service pattern** - reuse one template across many projects:\n\n```\nservices/\n base.prs # template with params\n user-api/\n promptscript.yaml # source: project.prs\n project.prs # @inherit ../base(serviceName: "user-api")\n auth-service/\n promptscript.yaml\n project.prs # @inherit ../base(serviceName: "auth-service")\n```\n\n## Configuration: promptscript.yaml\n\n### Auto-injection\n\nThis skill is automatically included when compiling with `prs compile`. No manual copying needed.\nTo disable, set `includePromptScriptSkill: false` in your `promptscript.yaml`.\n\n```\nid: my-project\nsyntax: "1.1.0"\ndescription: "My project description"\ninput:\n entry: .promptscript/project.prs\n include: [\'.promptscript/**/*.prs\']\ntargets:\n github:\n version: full # simple | multifile | full\n claude:\n version: full\n cursor:\n version: standard\n antigravity:\n version: frontmatter\n factory:\n version: full\n windsurf: # 41 additional targets supported\n version: simple\n cline:\n version: simple\nregistry:\n git: https://github.com/org/registry.git\n ref: main\nregistries:\n company:\n url: github.com/acme/promptscript-registry\n oss:\n url: github.com/prscrpt/community-registry\n ref: v2\npolicies:\n - name: adjacent-layers-only\n kind: layer-boundary\n severity: error\n layers: [\'@core\', \'@team\', \'@project\']\n maxDistance: 1\nmodels:\n supported: [opus, sonnet, gpt-5.3-codex] # PS041 reports models outside this set\n profiles: # add models or override built-in profiles\n claude-opus-9:\n provider: anthropic\n family: claude-opus # joins the family, so `opus` now resolves here\n version: \'9\'\n displayName: Claude Opus 9\n targets:\n github: Claude Opus 9 (Preview) # per-target name always wins\n```\n\n### Lockfile: `promptscript.lock`\n\nWhen remote imports are used, run `prs lock` to generate or update the lockfile\nbefore compilation. It records the exact resolved commit for each dependency.\nIntegrity hashes (SHA-256) are included for registry references to detect\ntampering or drift. This enables reproducible builds across machines and CI.\nCommit `promptscript.lock` to version control.\n\nUse `--ignore-hashes` on `prs compile` or `prs validate` to skip integrity\nhash verification when needed.\n\n### Policy Engine\n\nDefine organizational policies in `promptscript.yaml` to validate skill extensions:\n\n```yaml\npolicies:\n - name: adjacent-layers-only\n kind: layer-boundary\n description: \'Only adjacent layers can extend each other\'\n severity: error\n layers: [\'@core\', \'@team\', \'@project\']\n maxDistance: 1\n\n - name: protect-content\n kind: property-protection\n description: \'Content override requires explicit approval\'\n severity: warning\n properties: [\'content\', \'description\']\n\n - name: approved-registries\n kind: registry-allowlist\n description: \'Extensions must come from approved registries\'\n severity: error\n allowed: [\'@core\', \'@team\']\n```\n\nPolicy kinds: `layer-boundary` (controls layer distance), `property-protection`\n(prevents overriding specific properties), `registry-allowlist` (restricts extension sources).\nSeverity: `error` (fails validation) or `warning` (reported only).\nSkip with `--skip-policies` during development (never in CI).\n\n## Syntax Version Validation\n\nThe `syntax` field in `@meta` declares the PromptScript language version (semver).\n\n### Known Versions\n\n| Version | What it adds |\n| ------- | ----------------------------------------------------------------------------------------------------------------------- |\n| `1.0.0` | Core blocks (identity, context, standards, restrictions, knowledge, shortcuts, commands, guards, params, skills, local) |\n| `1.1.0` | Adds `@agents` and `@workflows`; reserves internal `@prompts` |\n| `1.2.0` | Adds `@examples` (few-shot input/output pairs) |\n| `1.3.0` | Adds explicit regular block field replacement in `@extend` |\n| `1.4.0` | Adds `@hooks`, `@mcpServers`, and `@plugins` |\n| `1.5.0` | Adds `@header` section titles, `@override` replacement, and unquoted `${VAR}` values |\n\n### Block Version Requirements\n\n| Block | Minimum Syntax Version |\n| ------------- | ---------------------- |\n| `@agents` | `1.1.0` |\n| `@workflows` | `1.1.0` |\n| `@examples` | `1.2.0` |\n| `@hooks` | `1.4.0` |\n| `@mcpServers` | `1.4.0` |\n| `@plugins` | `1.4.0` |\n\nAll other built-in blocks are available from `1.0.0`.\nRegular block field replacement with `field!: value` requires syntax `1.3.0`.\nGenerated section title overrides with `@header` require syntax `1.5.0`.\nAtomic replacement with `@override` requires syntax `1.5.0`.\nUnquoted `${VAR}` references as values require syntax `1.5.0`.\n\n### Generated Section Headers\n\nUse `@header` inside a registered owner block to rename human-readable output\nsections without changing filenames, frontmatter, XML tags, or structured keys:\n\n```promptscript\n@meta { id: "localized" syntax: "1.5.0" }\n\n@standards {\n @header "Coding Rules"\n @header git-commits "Commit Rules"\n code: ["Use strict TypeScript"]\n}\n```\n\n- `@header "Title"` targets the block\'s primary section.\n- `@header <section-key> "Title"` targets an owned derived section.\n- Titles must be non-empty, single-line strings.\n- Source overrides take precedence over formatter configuration and target defaults.\n- Child inheritance, imported source, and the latest root extension take precedence.\n- An initial `## Heading` in a registered text-only primary owner is a syntax\n `1.5.0` compatibility fallback. Explicit `@header` metadata wins.\n- Ordinary `header` and `headers` fields remain domain data.\n\n### Validation Rules\n\n- **PS018 (`syntax-version-compat`)**: warns when resolved blocks or syntax features require a higher version than declared. Requirements from inheritance, imports, and skill composition are included. Suggestion: run `prs validate --fix`.\n- **PS019 (`unknown-block-name`)**: warns when a block name is not a known PromptScript type, with fuzzy-match suggestions for typos.\n- **PS037 (`valid-section-headers`)**: rejects invalid titles, unknown or unowned section keys, duplicate overrides, and nested extension overrides.\n- **PS038 (`valid-block-shape`)**: rejects unsupported built-in block shapes and warns about formatter-sensitive legacy shapes or multiline shortcut scalars.\n- **PS039 (`agent-namespaces`)**: validates qualified agent name segments and checks them against recorded import provenance.\n- **PS040 (`import-excludes`)**: errors when a `validation.excludes` entry for an import does not record the commit pinned in promptscript.lock, so consumers re-review imports whose pinned commit changed.\n- **PS041 (`valid-model-reference`)**: warns when an agent `model`/`specModel` or skill `model` resolves to a deprecated or retired model, suggesting the successor. With `models.supported` set, it also warns about models outside the set, models missing from the catalog, and unknown `models.supported` entries. It also reports `models.profiles` problems: a name shared by two profiles or taken from a floating alias or `inherit`, unknown or looping successors, dates not in `YYYY-MM-DD`, and `targets` keys for targets that write no model names.\n- **PS021 (`use-block-filter`)**: errors when `only` and `exclude` are both specified in `@use` parameters.\n- **PS025 (`valid-skill-references`)**: errors when a `references` entry points to a file with a disallowed extension or a path that cannot be resolved.\n- **PS026 (`safe-reference-content`)**: warns when a referenced file contains potentially sensitive content (e.g., secrets, credentials).\n- **PS027 (`valid-skill-composition`)**: warns about conflicting phase names or excessive phases in composed skills.\n- **PS028 (`valid-append-negation`)**: warns when negation prefix `!` appears in base skill definitions (only effective in `@extend`).\n- **PS029 (`valid-sealed-property`)**: warns when `sealed` contains non-replace-strategy property names.\n- **PS030 (`policy-compliance`)**: validates skill extensions against organizational policies defined in `promptscript.yaml`.\n- **PS034 (`valid-hooks`)**: validates portable hook events, commands/scripts, paths, interpreters, timeouts, cwd, and target overrides.\n\nTarget formatters report **PS4002** when a hook event or field has no native equivalent,\nwhen a target cannot guarantee project-root execution, or when output mode cannot emit\nthe additional hook file. They report **PS4004** when an agent or skill model comes from\na provider the target cannot run, or when its name has a line break or control character;\nthe model field is omitted for that target.\n\n### Fixing Syntax Versions\n\n```\nprs validate --fix # Auto-fix syntax versions in .prs files\nprs upgrade # Upgrade all .prs files to the latest version\n```\n\n`--fix` rewrites the `syntax: "..."` line in each file\'s `@meta` block to match the minimum version required by resolved blocks and syntax features. It follows inheritance, imports, and skill composition. It only upgrades, never downgrades.\n\n`prs upgrade` upgrades all files to the latest known syntax version regardless of what blocks they use.\n\n## CLI Commands\n\n```\nprs init # Initialize project (auto-detects existing files)\nprs init --yes --targets claude factory\nprs init --dry-run # Preview initialization\nprs init --auto-import # Initialize + static import of existing files\nprs migrate # Interactive migration flow\nprs migrate --static # Non-interactive static import\nprs migrate --llm # Generate AI-assisted migration prompt\nprs migrate --static --dry-run\nprs compile # Compile to all targets\nprs compile --watch # Watch mode\nprs compile --ignore-hashes # Skip integrity hash verification\nprs build <name> # Compile a named build profile\nprs validate --strict # Validate syntax\nprs validate --fix # Auto-fix syntax version declarations\nprs validate --skip-policies # Skip policy engine evaluation\nprs upgrade # Upgrade all .prs files to latest syntax version\nprs import CLAUDE.md # Import existing AI instructions\nprs import CLAUDE.md --dry-run # Preview import conversion\nprs inspect <skill> # Show skill composition provenance\nprs inspect <skill> --layers # Show layer-level breakdown\nprs explain <path> # Explain source and composition provenance\nprs explain <path> --format json # Machine-readable provenance history\nprs hooks install # Install auto-compilation hooks for AI tools\nprs hooks install claude # Install hooks for a specific tool\nprs hooks uninstall # Remove installed auto-compilation hooks\nprs hooks uninstall claude # Remove hooks for a specific tool\nprs skills add <source> # Add a remote skill (@use + lock update + SKILL.md validation)\nprs skills add <source> --strict # Treat validation warnings as errors\nprs skills add <source> --skip-validation # Bypass Agent Skills spec checks (not recommended)\nprs skills remove <name> # Remove a skill (@use line + lock entry)\nprs skills list # List all imported skills\nprs skills update # Re-resolve markdown-imported skills (re-validates + re-hashes)\nprs pull # Update registry\nprs diff --target claude # Show compilation diff\nprs diff --all --format json # Machine-readable diff report for CI\nprs diff --format json --include-content # Include generated content in the report\nprs lock # Generate/update promptscript.lock\nprs lock --dry-run # Preview lockfile changes\nprs update # Re-resolve all remote imports to latest\nprs update <url> # Update a specific registry\nprs vendor sync # Copy cached deps to .promptscript/vendor/\nprs vendor check # Verify vendor matches lockfile\nprs resolve @alias/path # Debug: show how an import resolves\nprs registry list # Show configured registries and aliases\nprs registry add <alias> <url> # Add a registry alias\n```\n\n`prs init --yes` requires explicit, detected, or user-configured targets. It does not invent\ndefault tools. For existing projects, `prs migrate` preserves `promptscript.yaml`, isolates static\noutput under `.promptscript/migrated/`, leaves source instructions untouched, and performs no\nwrites when no candidates are detected.\n\n## Output Targets\n\n50 supported targets. Key examples:\n\n| Target | Main File | Skills |\n| ----------- | ------------------------------- | -------------------------------------------------- |\n| GitHub | .github/copilot-instructions.md | .github/skills/\\*/SKILL.md |\n| Claude | CLAUDE.md | .claude/skills/\\*/SKILL.md |\n| Cursor | .cursor/rules/project.mdc | .agents/skills/\\*/SKILL.md |\n| Antigravity | .agent/rules/project.md | - |\n| Factory | AGENTS.md | .factory/skills/\\*/SKILL.md, .factory/droids/\\*.md |\n| OpenCode | OPENCODE.md | .opencode/skills/\\*/SKILL.md |\n| Gemini | GEMINI.md | .agents/skills/\\*/skill.md |\n| Windsurf | .windsurf/rules/project.md | .windsurf/skills/\\*/SKILL.md |\n| Cline | .clinerules | - |\n| Roo Code | .roorules | - |\n| Codex | AGENTS.md | .agents/skills/\\*/SKILL.md |\n| Continue | .continue/rules/project.md | - |\n| Hermes | AGENTS.md | - |\n| + 37 more | | See full list in documentation |\n\nTargets that share an output path (for example Factory, Codex, and every AGENTS.md target) are\nreconciled in one output plan before anything is written. Identical content merges silently;\ndiffering content reports `PS4001`, where a formatter output replaces an earlier one and a\nresource or the auto-injected PromptScript skill keeps the file already planned. Paths are compared\ncase-insensitively and NFC-normalized on every platform.\n\n### Formatter Documentation\n\nFor detailed information about each formatter\'s output paths, supported features, quirks, and example outputs:\n\n- **Full formatter reference:** `docs/reference/formatters/` (7 dedicated pages + index of all 50)\n- **llms-full.txt:** Available at the docs site root - contains all documentation in a single file for LLM consumption\n- **Dedicated pages exist for:** Claude Code, GitHub Copilot, Cursor, Antigravity, Factory AI, Gemini CLI, OpenCode\n- **All 50 formatters indexed at:** `docs/reference/formatters/index.md` with output paths, tier, and feature flags\n\n### Auto-Compilation Hooks\n\nInstead of running `prs compile --watch` manually, install hooks so your AI tool\ntriggers compilation automatically when you edit `.prs` files:\n\n```\nprs hooks install # Auto-detect and install for all detected tools\nprs hooks install claude # Install for a specific tool\n```\n\nHooks also protect generated files from direct edits \u2014 when an AI agent tries\nto edit a compiled output (e.g., CLAUDE.md), the write is blocked with a message\npointing to the source `.prs` file. Supported tools: Claude Code, Factory AI,\nCursor, Windsurf, Cline, GitHub Copilot, Gemini CLI.\n\n## Project Organization\n\nTypical modular structure:\n\n```\n.promptscript/\n project.prs # Entry: @meta, @inherit, @use, @identity, @agents\n context.prs # @context (architecture, tech stack)\n standards.prs # @standards (coding conventions)\n restrictions.prs # @restrictions (hard rules)\n commands.prs # @shortcuts and @knowledge\n```\n\nThe entry file uses `@use ./context`, `@use ./standards`, etc. to compose them.\n\n## Common Mistakes\n\n1. Missing @meta block - every .prs file needs `@meta` with `id` and `syntax`\n2. Multiple @inherit - only one per file; use `@use` for additional imports\n3. Extending an unknown path - target an inherited or local block, or use an imported alias\n4. Unquoted strings with special chars - quote strings containing `:`, `#`, `{`, `}`\n5. Forgetting to compile - `.prs` changes need `prs compile` to take effect\n6. Triple quotes inside triple quotes - not supported; describe content textually instead\n7. Using `{{var}}` in the root file without `@inherit` - template variables only work\n in a parent file that defines `params` in `@meta`, with values passed by the child\n via `@inherit ./parent(key: value)` or `@use ./fragment(key: value)`. They are NOT\n set from `promptscript.yaml` or CLI flags\n8. Using `@examples` with `syntax: "1.0.0"` or `"1.1.0"` - `@examples` requires\n syntax version `1.2.0`. Run `prs validate --fix` to auto-upgrade\n\n## Migrating Existing AI Instructions to PromptScript\n\n### Automated: `prs import`\n\nThe fastest way to convert existing AI instructions to PromptScript:\n\n```\nprs import CLAUDE.md # Convert a single file\nprs import .github/copilot-instructions.md\nprs import AGENTS.md --output ./imported\nprs import --dry-run CLAUDE.md # Preview without writing\n```\n\n`prs import` automatically:\n\n- Detects the source format (Claude, GitHub Copilot, Cursor, Factory, etc.)\n- Maps content to appropriate PromptScript blocks (@identity, @standards, etc.)\n- Generates a valid `.prs` file with `@meta` block\n- Preserves the original intent and structure\n\nSupported source formats:\n\n- `CLAUDE.md` (Claude Code)\n- `.github/copilot-instructions.md` (GitHub Copilot)\n- `.cursorrules` or `.cursor/rules/*.mdc` (Cursor)\n- `AGENTS.md` (Factory AI / Codex)\n- `.clinerules` (Cline), `.roorules` (Roo Code)\n- `.windsurf/rules/*.md` (Windsurf)\n- Any Markdown-based AI instruction file\n\n### Manual Migration\n\nFor complex migrations or when `prs import` needs refinement:\n\n| Source Pattern | PromptScript Block |\n| ----------------------------------- | ------------------ |\n| "You are..." persona text | `@identity` |\n| Project description, tech stack | `@context` |\n| Coding conventions, style rules | `@standards` |\n| "Never...", "Always...", hard rules | `@restrictions` |\n| `/command` definitions | `@shortcuts` |\n| Skill/tool definitions | `@skills` |\n| Agent/subagent configs | `@agents` |\n| Reference docs, API specs | `@knowledge` |\n\nAfter import, split into modular files (`context.prs`, `standards.prs`, etc.)\nand compose with `@use` in `project.prs`. Run `prs validate --strict` then\n`prs compile` to verify output matches the original.\n';
|
|
8048
8704
|
}
|
|
8049
8705
|
});
|
|
8050
8706
|
|
|
@@ -20767,6 +21423,214 @@ var init_agent_capability_warnings = __esm({
|
|
|
20767
21423
|
}
|
|
20768
21424
|
});
|
|
20769
21425
|
|
|
21426
|
+
// packages/formatters/src/model-mapping.ts
|
|
21427
|
+
function unquoteYamlToken(token) {
|
|
21428
|
+
const quote = token[0];
|
|
21429
|
+
if ((quote === "'" || quote === '"') && token.at(-1) === quote && token.length >= 2) {
|
|
21430
|
+
return token.slice(1, -1);
|
|
21431
|
+
}
|
|
21432
|
+
return token;
|
|
21433
|
+
}
|
|
21434
|
+
function stripYamlComment(value) {
|
|
21435
|
+
let quote;
|
|
21436
|
+
for (let i = 0; i < value.length; i++) {
|
|
21437
|
+
const char = value[i];
|
|
21438
|
+
if (quote === void 0) {
|
|
21439
|
+
if (char === "'" || char === '"') {
|
|
21440
|
+
quote = char;
|
|
21441
|
+
} else if (char === "#" && (i === 0 || value[i - 1] === " " || value[i - 1] === " ")) {
|
|
21442
|
+
return value.slice(0, i);
|
|
21443
|
+
}
|
|
21444
|
+
} else if (char === quote) {
|
|
21445
|
+
quote = void 0;
|
|
21446
|
+
}
|
|
21447
|
+
}
|
|
21448
|
+
return value;
|
|
21449
|
+
}
|
|
21450
|
+
function splitYamlMapping(line) {
|
|
21451
|
+
if (line.startsWith(" ") || line.startsWith(" ")) return void 0;
|
|
21452
|
+
const colon = line.indexOf(":");
|
|
21453
|
+
if (colon < 0) return void 0;
|
|
21454
|
+
const rest = line.slice(colon + 1);
|
|
21455
|
+
if (rest !== "" && !rest.startsWith(" ") && !rest.startsWith(" ")) return void 0;
|
|
21456
|
+
return [unquoteYamlToken(line.slice(0, colon).trim()), rest];
|
|
21457
|
+
}
|
|
21458
|
+
function isFrontmatterModelLine(line) {
|
|
21459
|
+
return splitYamlMapping(line)?.[0] === MODEL_KEY;
|
|
21460
|
+
}
|
|
21461
|
+
function blockScalarKind(value) {
|
|
21462
|
+
const first2 = value[0];
|
|
21463
|
+
if (first2 !== "|" && first2 !== ">") return void 0;
|
|
21464
|
+
for (const char of value.slice(1)) {
|
|
21465
|
+
if (char !== "-" && char !== "+" && (char < "1" || char > "9")) return void 0;
|
|
21466
|
+
}
|
|
21467
|
+
return first2 === "|" ? "literal" : "folded";
|
|
21468
|
+
}
|
|
21469
|
+
function blockScalarLines(lines, keyIndex) {
|
|
21470
|
+
const block = [];
|
|
21471
|
+
for (const line of lines.slice(keyIndex + 1)) {
|
|
21472
|
+
if (line === "" || line.startsWith(" ") || line.startsWith(" ")) {
|
|
21473
|
+
block.push(line);
|
|
21474
|
+
} else {
|
|
21475
|
+
break;
|
|
21476
|
+
}
|
|
21477
|
+
}
|
|
21478
|
+
while (block.length > 0 && block.at(-1) === "") block.pop();
|
|
21479
|
+
return block;
|
|
21480
|
+
}
|
|
21481
|
+
function blockScalarValue(block, kind) {
|
|
21482
|
+
const indents = block.filter((line) => line !== "").map((line) => line.length - line.trimStart().length);
|
|
21483
|
+
const indent = indents.length > 0 ? Math.min(...indents) : 0;
|
|
21484
|
+
const content = block.map((line) => line === "" ? "" : line.slice(indent));
|
|
21485
|
+
if (kind === "literal") {
|
|
21486
|
+
return content.join("\n").trim();
|
|
21487
|
+
}
|
|
21488
|
+
const paragraphs = [[]];
|
|
21489
|
+
for (const line of content) {
|
|
21490
|
+
if (line === "") {
|
|
21491
|
+
paragraphs.push([]);
|
|
21492
|
+
} else {
|
|
21493
|
+
paragraphs.at(-1)?.push(line.trim());
|
|
21494
|
+
}
|
|
21495
|
+
}
|
|
21496
|
+
return paragraphs.map((lines) => lines.join(" ")).join("\n").trim();
|
|
21497
|
+
}
|
|
21498
|
+
function findRawFrontmatterModel(frontmatter) {
|
|
21499
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
21500
|
+
for (let i = 0; i < lines.length; i++) {
|
|
21501
|
+
const line = lines[i];
|
|
21502
|
+
if (line === void 0 || !isFrontmatterModelLine(line)) continue;
|
|
21503
|
+
const valuePart = stripYamlComment(line.slice(line.indexOf(":") + 1)).trim();
|
|
21504
|
+
if (!valuePart) {
|
|
21505
|
+
return { keyIndex: i, lineCount: 1, value: void 0 };
|
|
21506
|
+
}
|
|
21507
|
+
const kind = blockScalarKind(valuePart);
|
|
21508
|
+
if (kind === void 0) {
|
|
21509
|
+
return { keyIndex: i, lineCount: 1, value: unquoteYamlToken(valuePart) };
|
|
21510
|
+
}
|
|
21511
|
+
const block = blockScalarLines(lines, i);
|
|
21512
|
+
const value = blockScalarValue(block, kind);
|
|
21513
|
+
return { keyIndex: i, lineCount: 1 + block.length, value: value === "" ? void 0 : value };
|
|
21514
|
+
}
|
|
21515
|
+
return void 0;
|
|
21516
|
+
}
|
|
21517
|
+
function extractRawFrontmatterModel(frontmatter) {
|
|
21518
|
+
return findRawFrontmatterModel(frontmatter)?.value;
|
|
21519
|
+
}
|
|
21520
|
+
function toTargetModel(model, target, models) {
|
|
21521
|
+
if (model === void 0) return void 0;
|
|
21522
|
+
return mapModelToTarget(model, target, getModelCatalog(models)).value;
|
|
21523
|
+
}
|
|
21524
|
+
function describeInvalidName(label, target, profile) {
|
|
21525
|
+
if (!profile) {
|
|
21526
|
+
return {
|
|
21527
|
+
message: `${label} has a line break or control character, so it is omitted.`,
|
|
21528
|
+
suggestion: "Write the model name on one line, without control characters."
|
|
21529
|
+
};
|
|
21530
|
+
}
|
|
21531
|
+
return {
|
|
21532
|
+
message: `${label} maps to a name with a line break or control character on target "${target}", so it is omitted.`,
|
|
21533
|
+
suggestion: `Remove line breaks and control characters from models.profiles.${profile.id} in promptscript.yaml.`
|
|
21534
|
+
};
|
|
21535
|
+
}
|
|
21536
|
+
function describeOmission(label, target, mapped) {
|
|
21537
|
+
if (mapped.issue === "invalid-name") return describeInvalidName(label, target, mapped.profile);
|
|
21538
|
+
if (mapped.issue !== "unsupported-provider" || !mapped.profile) return void 0;
|
|
21539
|
+
const providers = (getModelTargetScheme(target)?.providers ?? []).map((provider) => `"${provider}"`).join(" or ");
|
|
21540
|
+
return {
|
|
21541
|
+
message: `${label} comes from provider "${mapped.profile.provider}", which target "${target}" cannot run, so it is omitted.`,
|
|
21542
|
+
suggestion: `Use a model from provider ${providers}, or set models.profiles.${mapped.profile.id}.targets.${target} in promptscript.yaml.`
|
|
21543
|
+
};
|
|
21544
|
+
}
|
|
21545
|
+
function blockEntries(ast, name) {
|
|
21546
|
+
const block = ast.blocks.find((candidate) => candidate.name === name);
|
|
21547
|
+
if (!block) return [];
|
|
21548
|
+
if (block.content.type !== "ObjectContent" && block.content.type !== "MixedContent") return [];
|
|
21549
|
+
return Object.entries(block.content.properties).flatMap(
|
|
21550
|
+
([entryName, value]) => value && typeof value === "object" && !Array.isArray(value) ? [
|
|
21551
|
+
[entryName, value, block]
|
|
21552
|
+
] : []
|
|
21553
|
+
);
|
|
21554
|
+
}
|
|
21555
|
+
function emitsResource(target, kind, version) {
|
|
21556
|
+
return TARGET_CAPABILITIES[target].resources.some(
|
|
21557
|
+
(resource) => resource.kind === kind && resource.versions.includes(version)
|
|
21558
|
+
);
|
|
21559
|
+
}
|
|
21560
|
+
function modelReferenceWarning(owner, field, value, block, target, catalog) {
|
|
21561
|
+
if (typeof value !== "string") return void 0;
|
|
21562
|
+
const described = describeOmission(
|
|
21563
|
+
`${owner}: ${field} ${JSON.stringify(value.trim())}`,
|
|
21564
|
+
target,
|
|
21565
|
+
mapModelToTarget(value, target, catalog)
|
|
21566
|
+
);
|
|
21567
|
+
return described && {
|
|
21568
|
+
code: MODEL_COMPATIBILITY_CODE,
|
|
21569
|
+
ruleName: "model-compatibility",
|
|
21570
|
+
...described,
|
|
21571
|
+
location: block.loc
|
|
21572
|
+
};
|
|
21573
|
+
}
|
|
21574
|
+
function agentModelWarnings(ast, target, version, catalog) {
|
|
21575
|
+
if (!emitsResource(target, "agents", version)) return [];
|
|
21576
|
+
const fields = AGENT_MODEL_FIELDS.filter(
|
|
21577
|
+
(field) => getAgentFieldStatus(target, field) !== "not-supported"
|
|
21578
|
+
);
|
|
21579
|
+
return blockEntries(ast, "agents").flatMap(
|
|
21580
|
+
([name, entry, block]) => fields.flatMap((field) => {
|
|
21581
|
+
const warning = modelReferenceWarning(
|
|
21582
|
+
`Agent "${name}"`,
|
|
21583
|
+
field,
|
|
21584
|
+
entry[field],
|
|
21585
|
+
block,
|
|
21586
|
+
target,
|
|
21587
|
+
catalog
|
|
21588
|
+
);
|
|
21589
|
+
return warning ? [warning] : [];
|
|
21590
|
+
})
|
|
21591
|
+
);
|
|
21592
|
+
}
|
|
21593
|
+
function frontmatterModelWarning(name, entry, block, target, catalog) {
|
|
21594
|
+
if (entry["model"] !== void 0) return void 0;
|
|
21595
|
+
const frontmatter = typeof entry["__rawFrontmatter"] === "string" ? entry["__rawFrontmatter"] : void 0;
|
|
21596
|
+
const rawModel = frontmatter !== void 0 ? extractRawFrontmatterModel(frontmatter) : void 0;
|
|
21597
|
+
return rawModel === void 0 ? void 0 : modelReferenceWarning(`Skill "${name}"`, "model", rawModel, block, target, catalog);
|
|
21598
|
+
}
|
|
21599
|
+
function skillModelWarnings(ast, target, version, catalog) {
|
|
21600
|
+
if (!SKILL_MODEL_TARGETS.has(target) || !emitsResource(target, "skills", version)) return [];
|
|
21601
|
+
return blockEntries(ast, "skills").flatMap(([name, entry, block]) => {
|
|
21602
|
+
const warnings = [
|
|
21603
|
+
modelReferenceWarning(`Skill "${name}"`, "model", entry["model"], block, target, catalog),
|
|
21604
|
+
frontmatterModelWarning(name, entry, block, target, catalog)
|
|
21605
|
+
].flatMap((warning) => warning ? [warning] : []);
|
|
21606
|
+
return warnings;
|
|
21607
|
+
});
|
|
21608
|
+
}
|
|
21609
|
+
function getModelCompatibilityWarnings(ast, target, version, models) {
|
|
21610
|
+
if (!isKnownTarget(target) || !getModelTargetScheme(target)) return [];
|
|
21611
|
+
const catalog = getModelCatalog(models);
|
|
21612
|
+
return [
|
|
21613
|
+
...agentModelWarnings(ast, target, version, catalog),
|
|
21614
|
+
...skillModelWarnings(ast, target, version, catalog)
|
|
21615
|
+
];
|
|
21616
|
+
}
|
|
21617
|
+
function appendModelCompatibilityWarnings(output, ast, target, version, models) {
|
|
21618
|
+
const warnings = getModelCompatibilityWarnings(ast, target, version, models);
|
|
21619
|
+
if (warnings.length === 0) return output;
|
|
21620
|
+
return { ...output, warnings: [...output.warnings ?? [], ...warnings] };
|
|
21621
|
+
}
|
|
21622
|
+
var MODEL_COMPATIBILITY_CODE, AGENT_MODEL_FIELDS, SKILL_MODEL_TARGETS, MODEL_KEY;
|
|
21623
|
+
var init_model_mapping = __esm({
|
|
21624
|
+
"packages/formatters/src/model-mapping.ts"() {
|
|
21625
|
+
"use strict";
|
|
21626
|
+
init_src();
|
|
21627
|
+
MODEL_COMPATIBILITY_CODE = "PS4004";
|
|
21628
|
+
AGENT_MODEL_FIELDS = ["model", "specModel"];
|
|
21629
|
+
SKILL_MODEL_TARGETS = /* @__PURE__ */ new Set(["claude", "grok"]);
|
|
21630
|
+
MODEL_KEY = "model";
|
|
21631
|
+
}
|
|
21632
|
+
});
|
|
21633
|
+
|
|
20770
21634
|
// packages/formatters/src/mcp-helpers.ts
|
|
20771
21635
|
function findMcpServersBlock(ast) {
|
|
20772
21636
|
return ast.blocks.find((b) => b.name === "mcpServers");
|
|
@@ -20951,6 +21815,7 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
20951
21815
|
init_hook_adapters();
|
|
20952
21816
|
init_hook_capability_warnings();
|
|
20953
21817
|
init_agent_capability_warnings();
|
|
21818
|
+
init_model_mapping();
|
|
20954
21819
|
init_mcp_helpers();
|
|
20955
21820
|
init_section_title_resolver();
|
|
20956
21821
|
MarkdownInstructionFormatter = class extends BaseFormatter {
|
|
@@ -20998,7 +21863,14 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
20998
21863
|
const agentOutput = appendAgentCapabilityWarnings(warnedOutput, ast, this.name, version, {
|
|
20999
21864
|
blockWarningHandled: (this.config.unsupportedBlocks ?? []).includes("agents")
|
|
21000
21865
|
});
|
|
21001
|
-
|
|
21866
|
+
const modelOutput = appendModelCompatibilityWarnings(
|
|
21867
|
+
agentOutput,
|
|
21868
|
+
ast,
|
|
21869
|
+
this.name,
|
|
21870
|
+
version,
|
|
21871
|
+
options?.models
|
|
21872
|
+
);
|
|
21873
|
+
return appendTargetHookCapabilityWarnings(modelOutput, ast, this.name, version);
|
|
21002
21874
|
}
|
|
21003
21875
|
hasEnabledHooks(ast) {
|
|
21004
21876
|
const hooksBlock = ast.blocks.find((block) => block.name === "hooks");
|
|
@@ -21138,7 +22010,7 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
21138
22010
|
}
|
|
21139
22011
|
}
|
|
21140
22012
|
if (this.config.hasAgents) {
|
|
21141
|
-
const agents = this.extractAgents(ast);
|
|
22013
|
+
const agents = this.extractAgents(ast, options);
|
|
21142
22014
|
for (const agent of agents) {
|
|
21143
22015
|
additionalFiles.push(this.generateAgentFile(agent));
|
|
21144
22016
|
}
|
|
@@ -21278,27 +22150,32 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
21278
22150
|
// ============================================================
|
|
21279
22151
|
// Agent Extraction & File Generation
|
|
21280
22152
|
// ============================================================
|
|
21281
|
-
extractAgents(ast) {
|
|
22153
|
+
extractAgents(ast, _options) {
|
|
21282
22154
|
const agentsBlock = this.findBlock(ast, "agents");
|
|
21283
22155
|
if (!agentsBlock) return [];
|
|
21284
22156
|
const agents = [];
|
|
21285
22157
|
const props = this.getProps(agentsBlock.content);
|
|
21286
22158
|
const nativeNames = this.getNativeAgentNameMap(ast);
|
|
21287
22159
|
for (const [name, value] of Object.entries(props)) {
|
|
21288
|
-
if (value
|
|
21289
|
-
|
|
21290
|
-
|
|
21291
|
-
|
|
21292
|
-
if (!description) continue;
|
|
21293
|
-
agents.push({
|
|
21294
|
-
name: nativeNames.get(name) ?? name,
|
|
21295
|
-
description,
|
|
21296
|
-
content: obj["content"] ? this.valueToString(obj["content"]) : ""
|
|
21297
|
-
});
|
|
21298
|
-
}
|
|
22160
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
22161
|
+
if (!this.isSafeAgentName(name)) continue;
|
|
22162
|
+
const agent = this.parseAgent(nativeNames.get(name) ?? name, value);
|
|
22163
|
+
if (agent) agents.push(agent);
|
|
21299
22164
|
}
|
|
21300
22165
|
return agents;
|
|
21301
22166
|
}
|
|
22167
|
+
/**
|
|
22168
|
+
* Build one agent config, or undefined when it has no description.
|
|
22169
|
+
*/
|
|
22170
|
+
parseAgent(nativeName, obj) {
|
|
22171
|
+
const description = obj["description"] ? this.valueToString(obj["description"]) : "";
|
|
22172
|
+
if (!description) return void 0;
|
|
22173
|
+
return {
|
|
22174
|
+
name: nativeName,
|
|
22175
|
+
description,
|
|
22176
|
+
content: obj["content"] ? this.valueToString(obj["content"]) : ""
|
|
22177
|
+
};
|
|
22178
|
+
}
|
|
21302
22179
|
generateAgentFile(config) {
|
|
21303
22180
|
const lines = [];
|
|
21304
22181
|
lines.push("---");
|
|
@@ -22247,7 +23124,7 @@ var init_feature_matrix = __esm({
|
|
|
22247
23124
|
});
|
|
22248
23125
|
|
|
22249
23126
|
// packages/formatters/src/formatters/github.ts
|
|
22250
|
-
var GITHUB_VERSIONS, TOOL_NAME_MAPPING,
|
|
23127
|
+
var GITHUB_VERSIONS, TOOL_NAME_MAPPING, GitHubFormatter;
|
|
22251
23128
|
var init_github2 = __esm({
|
|
22252
23129
|
"packages/formatters/src/formatters/github.ts"() {
|
|
22253
23130
|
"use strict";
|
|
@@ -22257,6 +23134,7 @@ var init_github2 = __esm({
|
|
|
22257
23134
|
init_hook_adapters();
|
|
22258
23135
|
init_hook_capability_warnings();
|
|
22259
23136
|
init_agent_capability_warnings();
|
|
23137
|
+
init_model_mapping();
|
|
22260
23138
|
init_section_title_resolver();
|
|
22261
23139
|
GITHUB_VERSIONS = {
|
|
22262
23140
|
simple: {
|
|
@@ -22298,28 +23176,6 @@ var init_github2 = __esm({
|
|
|
22298
23176
|
TodoWrite: "todo",
|
|
22299
23177
|
TodoRead: "todo"
|
|
22300
23178
|
};
|
|
22301
|
-
MODEL_NAME_MAPPING = {
|
|
22302
|
-
// Claude models - default aliases map to latest versions (4.5)
|
|
22303
|
-
sonnet: "Claude Sonnet 4.5",
|
|
22304
|
-
opus: "Claude Opus 4.5",
|
|
22305
|
-
haiku: "Claude Haiku 4.5",
|
|
22306
|
-
// Explicit version mappings
|
|
22307
|
-
"sonnet-4": "Claude Sonnet 4",
|
|
22308
|
-
"sonnet-4.5": "Claude Sonnet 4.5",
|
|
22309
|
-
"opus-4": "Claude Opus 4",
|
|
22310
|
-
"opus-4.5": "Claude Opus 4.5",
|
|
22311
|
-
"haiku-4": "Claude Haiku 4",
|
|
22312
|
-
"haiku-4.5": "Claude Haiku 4.5",
|
|
22313
|
-
// OpenAI models (pass through if already in correct format)
|
|
22314
|
-
"gpt-4o": "GPT-4o",
|
|
22315
|
-
"gpt-4.1": "GPT-4.1",
|
|
22316
|
-
"gpt-5": "GPT-5",
|
|
22317
|
-
"gpt-5-mini": "GPT-5 mini",
|
|
22318
|
-
// Special values
|
|
22319
|
-
inherit: "",
|
|
22320
|
-
// Empty string means omit the model property
|
|
22321
|
-
auto: "Auto"
|
|
22322
|
-
};
|
|
22323
23179
|
GitHubFormatter = class extends BaseFormatter {
|
|
22324
23180
|
name = "github";
|
|
22325
23181
|
outputPath = ".github/copilot-instructions.md";
|
|
@@ -22352,6 +23208,7 @@ var init_github2 = __esm({
|
|
|
22352
23208
|
}
|
|
22353
23209
|
output = appendTargetHookCapabilityWarnings(output, ast, this.name, version);
|
|
22354
23210
|
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
23211
|
+
output = appendModelCompatibilityWarnings(output, ast, this.name, version, options?.models);
|
|
22355
23212
|
const hooksBlock = ast.blocks.find((block) => block.name === "hooks");
|
|
22356
23213
|
const hooks = hooksBlock ? extractHooks(hooksBlock) : [];
|
|
22357
23214
|
const hookWarnings = hooksBlock && version !== "simple" ? [
|
|
@@ -22450,7 +23307,7 @@ var init_github2 = __esm({
|
|
|
22450
23307
|
for (const skill of skills2) {
|
|
22451
23308
|
additionalFiles.push(this.generateCopilotSkillFile(skill, options));
|
|
22452
23309
|
}
|
|
22453
|
-
const customAgents = this.extractCustomAgents(ast);
|
|
23310
|
+
const customAgents = this.extractCustomAgents(ast, options);
|
|
22454
23311
|
for (const agent of customAgents) {
|
|
22455
23312
|
additionalFiles.push(this.generateCustomAgentFile(agent));
|
|
22456
23313
|
}
|
|
@@ -22824,7 +23681,7 @@ ${f.content}`
|
|
|
22824
23681
|
*
|
|
22825
23682
|
* @see https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-custom-agents
|
|
22826
23683
|
*/
|
|
22827
|
-
extractCustomAgents(ast) {
|
|
23684
|
+
extractCustomAgents(ast, options) {
|
|
22828
23685
|
const agentsBlock = this.findBlock(ast, "agents");
|
|
22829
23686
|
if (!agentsBlock) return [];
|
|
22830
23687
|
const agents = [];
|
|
@@ -22833,36 +23690,43 @@ ${f.content}`
|
|
|
22833
23690
|
const mcpServersBlock = findMcpServersBlock(ast);
|
|
22834
23691
|
const availableMcpServers = mcpServersBlock ? extractMcpServers(mcpServersBlock) : [];
|
|
22835
23692
|
for (const [name, value] of Object.entries(props)) {
|
|
22836
|
-
if (value
|
|
22837
|
-
|
|
22838
|
-
|
|
22839
|
-
|
|
22840
|
-
|
|
22841
|
-
|
|
22842
|
-
|
|
22843
|
-
|
|
22844
|
-
|
|
22845
|
-
|
|
22846
|
-
const mcpServerNames = Array.isArray(mcpServersValue) ? mcpServersValue.filter(
|
|
22847
|
-
(serverName) => typeof serverName === "string"
|
|
22848
|
-
) : [];
|
|
22849
|
-
const mcpServers = availableMcpServers.filter(
|
|
22850
|
-
(server) => mcpServerNames.includes(server.name)
|
|
22851
|
-
);
|
|
22852
|
-
agents.push({
|
|
22853
|
-
name: nativeNames.get(name) ?? name,
|
|
22854
|
-
description,
|
|
22855
|
-
tools: this.parseToolsArray(obj["tools"]),
|
|
22856
|
-
model: obj["model"] ? this.valueToString(obj["model"]) : void 0,
|
|
22857
|
-
specModel: obj["specModel"] ? this.valueToString(obj["specModel"]) : void 0,
|
|
22858
|
-
handoffs: handoffs.length > 0 ? handoffs : void 0,
|
|
22859
|
-
mcpServers: mcpServers.length > 0 ? mcpServers : void 0,
|
|
22860
|
-
content: obj["content"] ? this.valueToString(obj["content"]) : ""
|
|
22861
|
-
});
|
|
22862
|
-
}
|
|
23693
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
23694
|
+
if (!this.isSafeAgentName(name)) continue;
|
|
23695
|
+
const agent = this.parseCustomAgent(
|
|
23696
|
+
ast,
|
|
23697
|
+
nativeNames.get(name) ?? name,
|
|
23698
|
+
value,
|
|
23699
|
+
availableMcpServers,
|
|
23700
|
+
options
|
|
23701
|
+
);
|
|
23702
|
+
if (agent) agents.push(agent);
|
|
22863
23703
|
}
|
|
22864
23704
|
return agents;
|
|
22865
23705
|
}
|
|
23706
|
+
/**
|
|
23707
|
+
* Build one custom agent config, or undefined when it has no description.
|
|
23708
|
+
*/
|
|
23709
|
+
parseCustomAgent(ast, nativeName, obj, availableMcpServers, options) {
|
|
23710
|
+
const description = obj["description"] ? this.valueToString(obj["description"]) : "";
|
|
23711
|
+
if (!description) return void 0;
|
|
23712
|
+
const handoffs = this.extractHandoffs(obj["handoffs"]).map((handoff) => ({
|
|
23713
|
+
...handoff,
|
|
23714
|
+
agent: this.getNativeAgentName(ast, handoff.agent)
|
|
23715
|
+
}));
|
|
23716
|
+
const mcpServersValue = obj["mcpServers"];
|
|
23717
|
+
const mcpServerNames = Array.isArray(mcpServersValue) ? mcpServersValue.filter((serverName) => typeof serverName === "string") : [];
|
|
23718
|
+
const mcpServers = availableMcpServers.filter((server) => mcpServerNames.includes(server.name));
|
|
23719
|
+
return {
|
|
23720
|
+
name: nativeName,
|
|
23721
|
+
description,
|
|
23722
|
+
tools: this.parseToolsArray(obj["tools"]),
|
|
23723
|
+
model: this.copilotModel(obj["model"], options),
|
|
23724
|
+
specModel: this.copilotModel(obj["specModel"], options),
|
|
23725
|
+
handoffs: handoffs.length > 0 ? handoffs : void 0,
|
|
23726
|
+
mcpServers: mcpServers.length > 0 ? mcpServers : void 0,
|
|
23727
|
+
content: obj["content"] ? this.valueToString(obj["content"]) : ""
|
|
23728
|
+
};
|
|
23729
|
+
}
|
|
22866
23730
|
/**
|
|
22867
23731
|
* Parse tools array from a Value and map to GitHub Copilot canonical names.
|
|
22868
23732
|
*
|
|
@@ -22919,15 +23783,14 @@ ${f.content}`
|
|
|
22919
23783
|
return lines;
|
|
22920
23784
|
}
|
|
22921
23785
|
/**
|
|
22922
|
-
* Map a model
|
|
23786
|
+
* Map a model reference to its Copilot model name through the model catalog.
|
|
23787
|
+
* Names missing from the catalog pass through, so Copilot names work as-is.
|
|
22923
23788
|
*
|
|
22924
|
-
* @returns The
|
|
23789
|
+
* @returns The Copilot model name, or undefined when the model is omitted (e.g., "inherit")
|
|
22925
23790
|
*/
|
|
22926
|
-
|
|
22927
|
-
if (!
|
|
22928
|
-
|
|
22929
|
-
if (mapped === "") return void 0;
|
|
22930
|
-
return mapped ?? model;
|
|
23791
|
+
copilotModel(value, options) {
|
|
23792
|
+
if (!value) return void 0;
|
|
23793
|
+
return toTargetModel(this.valueToString(value), this.name, options?.models);
|
|
22931
23794
|
}
|
|
22932
23795
|
/**
|
|
22933
23796
|
* Generate a .github/agents/<name>.md file.
|
|
@@ -22943,13 +23806,11 @@ ${f.content}`
|
|
|
22943
23806
|
const toolsArray = config.tools.map((t) => `'${t}'`).join(", ");
|
|
22944
23807
|
lines.push(`tools: [${toolsArray}]`);
|
|
22945
23808
|
}
|
|
22946
|
-
|
|
22947
|
-
|
|
22948
|
-
lines.push(`model: ${mappedModel}`);
|
|
23809
|
+
if (config.model) {
|
|
23810
|
+
lines.push(`model: ${this.yamlString(config.model)}`);
|
|
22949
23811
|
}
|
|
22950
|
-
|
|
22951
|
-
|
|
22952
|
-
lines.push(`specModel: ${mappedSpecModel}`);
|
|
23812
|
+
if (config.specModel) {
|
|
23813
|
+
lines.push(`specModel: ${this.yamlString(config.specModel)}`);
|
|
22953
23814
|
}
|
|
22954
23815
|
if (config.handoffs && config.handoffs.length > 0) {
|
|
22955
23816
|
lines.push(...this.renderHandoffsYaml(config.handoffs));
|
|
@@ -23423,6 +24284,7 @@ var init_claude2 = __esm({
|
|
|
23423
24284
|
init_hook_adapters();
|
|
23424
24285
|
init_hook_capability_warnings();
|
|
23425
24286
|
init_agent_capability_warnings();
|
|
24287
|
+
init_model_mapping();
|
|
23426
24288
|
init_mcp_helpers();
|
|
23427
24289
|
init_section_title_resolver();
|
|
23428
24290
|
CLAUDE_VERSIONS = {
|
|
@@ -23443,6 +24305,16 @@ var init_claude2 = __esm({
|
|
|
23443
24305
|
}
|
|
23444
24306
|
};
|
|
23445
24307
|
ClaudeFormatter = class extends BaseFormatter {
|
|
24308
|
+
/**
|
|
24309
|
+
* @param modelTarget - Target whose model names agent and skill files use.
|
|
24310
|
+
* Grok writes its agent and skill files through this formatter, so its
|
|
24311
|
+
* `models.profiles` names must apply to them.
|
|
24312
|
+
*/
|
|
24313
|
+
constructor(modelTarget = "claude") {
|
|
24314
|
+
super();
|
|
24315
|
+
this.modelTarget = modelTarget;
|
|
24316
|
+
}
|
|
24317
|
+
modelTarget;
|
|
23446
24318
|
name = "claude";
|
|
23447
24319
|
outputPath = "CLAUDE.md";
|
|
23448
24320
|
description = "Claude Code instructions (concise Markdown)";
|
|
@@ -23474,6 +24346,7 @@ var init_claude2 = __esm({
|
|
|
23474
24346
|
}
|
|
23475
24347
|
output = appendTargetHookCapabilityWarnings(output, ast, this.name, version);
|
|
23476
24348
|
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
24349
|
+
output = appendModelCompatibilityWarnings(output, ast, this.name, version, options?.models);
|
|
23477
24350
|
output = {
|
|
23478
24351
|
...output,
|
|
23479
24352
|
managedOutputFiles: [
|
|
@@ -23549,7 +24422,7 @@ var init_claude2 = __esm({
|
|
|
23549
24422
|
for (const skill of skills2) {
|
|
23550
24423
|
additionalFiles.push(this.generateClaudeSkillFile(skill, options));
|
|
23551
24424
|
}
|
|
23552
|
-
const agents = this.extractAgents(ast);
|
|
24425
|
+
const agents = this.extractAgents(ast, options);
|
|
23553
24426
|
for (const agent of agents) {
|
|
23554
24427
|
additionalFiles.push(this.generateAgentFile(agent));
|
|
23555
24428
|
}
|
|
@@ -23790,7 +24663,7 @@ var init_claude2 = __esm({
|
|
|
23790
24663
|
userInvocable: obj["userInvocable"] === true,
|
|
23791
24664
|
disableModelInvocation: obj["disableModelInvocation"] === true,
|
|
23792
24665
|
argumentHint: obj["argumentHint"] ? this.valueToString(obj["argumentHint"]) : void 0,
|
|
23793
|
-
model:
|
|
24666
|
+
model: this.claudeModel(obj["model"], options),
|
|
23794
24667
|
content: obj["content"] ? this.valueToString(obj["content"]) : "",
|
|
23795
24668
|
resources: obj["resources"] && Array.isArray(obj["resources"]) ? obj["resources"].map((r) => ({
|
|
23796
24669
|
relativePath: r["relativePath"],
|
|
@@ -23804,6 +24677,31 @@ var init_claude2 = __esm({
|
|
|
23804
24677
|
}
|
|
23805
24678
|
return skills2;
|
|
23806
24679
|
}
|
|
24680
|
+
/**
|
|
24681
|
+
* Apply the model field to raw SKILL.md frontmatter.
|
|
24682
|
+
*
|
|
24683
|
+
* A `.prs` model wins over the frontmatter one; without it, the
|
|
24684
|
+
* frontmatter value is mapped through the model catalog for this target.
|
|
24685
|
+
* Unmappable values drop the whole property, block scalars included,
|
|
24686
|
+
* and PS4004 reports why; frontmatter without a model line gains one
|
|
24687
|
+
* from `.prs`. Empty values are left untouched.
|
|
24688
|
+
*/
|
|
24689
|
+
applyRawFrontmatterModel(rawFrontmatter, prsModel, options) {
|
|
24690
|
+
const field = findRawFrontmatterModel(rawFrontmatter);
|
|
24691
|
+
if (field === void 0) {
|
|
24692
|
+
return prsModel === void 0 ? rawFrontmatter : `${rawFrontmatter}
|
|
24693
|
+
model: ${this.yamlString(prsModel)}`;
|
|
24694
|
+
}
|
|
24695
|
+
if (field.value === void 0) return rawFrontmatter;
|
|
24696
|
+
const lines = rawFrontmatter.split(/\r?\n/);
|
|
24697
|
+
const mapped = prsModel ?? this.claudeModel(field.value, options);
|
|
24698
|
+
if (mapped === void 0) {
|
|
24699
|
+
lines.splice(field.keyIndex, field.lineCount);
|
|
24700
|
+
} else {
|
|
24701
|
+
lines.splice(field.keyIndex, field.lineCount, `model: ${this.yamlString(mapped)}`);
|
|
24702
|
+
}
|
|
24703
|
+
return lines.join("\n");
|
|
24704
|
+
}
|
|
23807
24705
|
/**
|
|
23808
24706
|
* Generate a .claude/skills/<name>/SKILL.md file.
|
|
23809
24707
|
*/
|
|
@@ -23811,7 +24709,7 @@ var init_claude2 = __esm({
|
|
|
23811
24709
|
const lines = [];
|
|
23812
24710
|
lines.push("---");
|
|
23813
24711
|
if (config.rawFrontmatter) {
|
|
23814
|
-
lines.push(config.rawFrontmatter);
|
|
24712
|
+
lines.push(this.applyRawFrontmatterModel(config.rawFrontmatter, config.model, options));
|
|
23815
24713
|
} else {
|
|
23816
24714
|
lines.push(`name: '${config.name}'`);
|
|
23817
24715
|
const descQuote = config.description.includes("'") ? '"' : "'";
|
|
@@ -23838,7 +24736,7 @@ var init_claude2 = __esm({
|
|
|
23838
24736
|
lines.push(`argument-hint: '${config.argumentHint}'`);
|
|
23839
24737
|
}
|
|
23840
24738
|
if (config.model) {
|
|
23841
|
-
lines.push(`model: ${config.model}`);
|
|
24739
|
+
lines.push(`model: ${this.yamlString(config.model)}`);
|
|
23842
24740
|
}
|
|
23843
24741
|
}
|
|
23844
24742
|
lines.push("---");
|
|
@@ -23936,7 +24834,7 @@ ${f.content}`
|
|
|
23936
24834
|
/**
|
|
23937
24835
|
* Extract agent configurations from @agents block.
|
|
23938
24836
|
*/
|
|
23939
|
-
extractAgents(ast) {
|
|
24837
|
+
extractAgents(ast, options) {
|
|
23940
24838
|
const agentsBlock = this.findBlock(ast, "agents");
|
|
23941
24839
|
if (!agentsBlock) return [];
|
|
23942
24840
|
const agents = [];
|
|
@@ -23946,7 +24844,7 @@ ${f.content}`
|
|
|
23946
24844
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
23947
24845
|
if (!this.isSafeAgentName(name)) continue;
|
|
23948
24846
|
const obj = value;
|
|
23949
|
-
const agent = this.parseAgentConfig(nativeNames.get(name) ?? name, obj);
|
|
24847
|
+
const agent = this.parseAgentConfig(nativeNames.get(name) ?? name, obj, options);
|
|
23950
24848
|
if (agent) {
|
|
23951
24849
|
agents.push(agent);
|
|
23952
24850
|
}
|
|
@@ -23957,7 +24855,7 @@ ${f.content}`
|
|
|
23957
24855
|
/**
|
|
23958
24856
|
* Parse a single agent configuration from object properties.
|
|
23959
24857
|
*/
|
|
23960
|
-
parseAgentConfig(name, obj) {
|
|
24858
|
+
parseAgentConfig(name, obj, options) {
|
|
23961
24859
|
const description = obj["description"] ? this.valueToString(obj["description"]) : "";
|
|
23962
24860
|
if (!description) return null;
|
|
23963
24861
|
return {
|
|
@@ -23965,7 +24863,7 @@ ${f.content}`
|
|
|
23965
24863
|
description,
|
|
23966
24864
|
tools: this.parseStringArray(obj["tools"]),
|
|
23967
24865
|
disallowedTools: this.parseStringArray(obj["disallowedTools"]),
|
|
23968
|
-
model: this.
|
|
24866
|
+
model: this.claudeModel(obj["model"], options),
|
|
23969
24867
|
permissionMode: this.parsePermissionMode(obj["permissionMode"]),
|
|
23970
24868
|
skills: this.parseStringArray(obj["skills"]),
|
|
23971
24869
|
maxTurns: obj["maxTurns"] !== void 0 && typeof obj["maxTurns"] === "number" ? obj["maxTurns"] : void 0,
|
|
@@ -23985,13 +24883,13 @@ ${f.content}`
|
|
|
23985
24883
|
return arr.length > 0 ? arr : void 0;
|
|
23986
24884
|
}
|
|
23987
24885
|
/**
|
|
23988
|
-
*
|
|
24886
|
+
* Map a model reference to a Claude Code model value through the model
|
|
24887
|
+
* catalog. Floating aliases, inherit, and names missing from the catalog
|
|
24888
|
+
* stay as written; models from other providers are omitted (PS4004).
|
|
23989
24889
|
*/
|
|
23990
|
-
|
|
24890
|
+
claudeModel(value, options) {
|
|
23991
24891
|
if (!value) return void 0;
|
|
23992
|
-
|
|
23993
|
-
const validModels = ["sonnet", "opus", "haiku", "inherit"];
|
|
23994
|
-
return validModels.includes(str) ? str : void 0;
|
|
24892
|
+
return toTargetModel(this.valueToString(value), this.modelTarget, options?.models);
|
|
23995
24893
|
}
|
|
23996
24894
|
/**
|
|
23997
24895
|
* Parse permission mode value.
|
|
@@ -24036,7 +24934,7 @@ ${f.content}`
|
|
|
24036
24934
|
lines.push(`disallowedTools: [${arr}]`);
|
|
24037
24935
|
}
|
|
24038
24936
|
if (config.model) {
|
|
24039
|
-
lines.push(`model: ${config.model}`);
|
|
24937
|
+
lines.push(`model: ${this.yamlString(config.model)}`);
|
|
24040
24938
|
}
|
|
24041
24939
|
if (config.permissionMode) {
|
|
24042
24940
|
lines.push(`permissionMode: ${config.permissionMode}`);
|
|
@@ -24520,6 +25418,7 @@ var init_cursor2 = __esm({
|
|
|
24520
25418
|
init_hook_adapters();
|
|
24521
25419
|
init_hook_capability_warnings();
|
|
24522
25420
|
init_agent_capability_warnings();
|
|
25421
|
+
init_model_mapping();
|
|
24523
25422
|
init_mcp_helpers();
|
|
24524
25423
|
init_plugin_helpers();
|
|
24525
25424
|
init_section_title_resolver();
|
|
@@ -24628,6 +25527,7 @@ var init_cursor2 = __esm({
|
|
|
24628
25527
|
}
|
|
24629
25528
|
output = appendTargetHookCapabilityWarnings(output, ast, this.name, version);
|
|
24630
25529
|
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
25530
|
+
output = appendModelCompatibilityWarnings(output, ast, this.name, version, options?.models);
|
|
24631
25531
|
return {
|
|
24632
25532
|
...output,
|
|
24633
25533
|
managedOutputFiles: [
|
|
@@ -24797,7 +25697,8 @@ ${content}`);
|
|
|
24797
25697
|
agentLines.push(`name: ${nativeAgentName}`);
|
|
24798
25698
|
if (description) agentLines.push(`description: ${JSON.stringify(description)}`);
|
|
24799
25699
|
const model = obj["model"];
|
|
24800
|
-
|
|
25700
|
+
const cursorModel = typeof model === "string" ? toTargetModel(model, this.name, options?.models) : void 0;
|
|
25701
|
+
if (cursorModel) agentLines.push(`model: ${this.yamlString(cursorModel)}`);
|
|
24801
25702
|
const agentMcp = obj["mcpServers"];
|
|
24802
25703
|
if (agentMcp && typeof agentMcp === "object" && !Array.isArray(agentMcp)) {
|
|
24803
25704
|
const mcpNames = Object.keys(agentMcp);
|
|
@@ -26355,6 +27256,7 @@ var init_factory = __esm({
|
|
|
26355
27256
|
init_mcp_helpers();
|
|
26356
27257
|
init_plugin_helpers();
|
|
26357
27258
|
init_section_title_resolver();
|
|
27259
|
+
init_model_mapping();
|
|
26358
27260
|
FACTORY_VERSIONS = {
|
|
26359
27261
|
simple: {
|
|
26360
27262
|
name: "simple",
|
|
@@ -26702,32 +27604,45 @@ ${f.content}`
|
|
|
26702
27604
|
// ============================================================
|
|
26703
27605
|
// Droid File Generation (Factory-specific)
|
|
26704
27606
|
// ============================================================
|
|
26705
|
-
extractAgents(ast) {
|
|
27607
|
+
extractAgents(ast, options) {
|
|
26706
27608
|
const agentsBlock = this.findBlock(ast, "agents");
|
|
26707
27609
|
if (!agentsBlock) return [];
|
|
26708
27610
|
const droids = [];
|
|
26709
27611
|
const props = this.getProps(agentsBlock.content);
|
|
26710
27612
|
for (const [name, value] of Object.entries(props)) {
|
|
26711
27613
|
if (!this.isSafeAgentName(name)) continue;
|
|
26712
|
-
if (value
|
|
26713
|
-
|
|
26714
|
-
|
|
26715
|
-
if (!description) continue;
|
|
26716
|
-
droids.push({
|
|
26717
|
-
name: this.getNativeAgentName(ast, name),
|
|
26718
|
-
description,
|
|
26719
|
-
content: obj["content"] ? this.valueToString(obj["content"]) : "",
|
|
26720
|
-
model: obj["model"] ? this.valueToString(obj["model"]) : void 0,
|
|
26721
|
-
reasoningEffort: this.parseReasoningEffort(obj["reasoningEffort"]),
|
|
26722
|
-
specModel: obj["specModel"] ? this.valueToString(obj["specModel"]) : void 0,
|
|
26723
|
-
specReasoningEffort: this.parseReasoningEffort(obj["specReasoningEffort"]),
|
|
26724
|
-
tools: this.parseDroidTools(obj["tools"]),
|
|
26725
|
-
mcpServers: this.parseMcpServerNames(obj["mcpServers"])
|
|
26726
|
-
});
|
|
26727
|
-
}
|
|
27614
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
27615
|
+
const droid = this.parseDroid(ast, name, value, options);
|
|
27616
|
+
if (droid) droids.push(droid);
|
|
26728
27617
|
}
|
|
26729
27618
|
return droids;
|
|
26730
27619
|
}
|
|
27620
|
+
/**
|
|
27621
|
+
* Build one droid config, or undefined when it has no description.
|
|
27622
|
+
*/
|
|
27623
|
+
parseDroid(ast, name, obj, options) {
|
|
27624
|
+
const description = obj["description"] ? this.valueToString(obj["description"]) : "";
|
|
27625
|
+
if (!description) return void 0;
|
|
27626
|
+
return {
|
|
27627
|
+
name: this.getNativeAgentName(ast, name),
|
|
27628
|
+
description,
|
|
27629
|
+
content: obj["content"] ? this.valueToString(obj["content"]) : "",
|
|
27630
|
+
model: this.droidModel(obj["model"], options),
|
|
27631
|
+
reasoningEffort: this.parseReasoningEffort(obj["reasoningEffort"]),
|
|
27632
|
+
specModel: this.droidModel(obj["specModel"], options),
|
|
27633
|
+
specReasoningEffort: this.parseReasoningEffort(obj["specReasoningEffort"]),
|
|
27634
|
+
tools: this.parseDroidTools(obj["tools"]),
|
|
27635
|
+
mcpServers: this.parseMcpServerNames(obj["mcpServers"])
|
|
27636
|
+
};
|
|
27637
|
+
}
|
|
27638
|
+
/**
|
|
27639
|
+
* Map a model reference to a Factory model ID through the model catalog.
|
|
27640
|
+
* Names missing from the catalog pass through unchanged.
|
|
27641
|
+
*/
|
|
27642
|
+
droidModel(value, options) {
|
|
27643
|
+
if (!value) return void 0;
|
|
27644
|
+
return toTargetModel(this.valueToString(value), this.name, options?.models);
|
|
27645
|
+
}
|
|
26731
27646
|
generateAgentFile(config) {
|
|
26732
27647
|
const droidConfig = config;
|
|
26733
27648
|
const lines = [];
|
|
@@ -26737,13 +27652,13 @@ ${f.content}`
|
|
|
26737
27652
|
lines.push(`description: ${this.yamlString(droidConfig.description)}`);
|
|
26738
27653
|
}
|
|
26739
27654
|
if (droidConfig.model) {
|
|
26740
|
-
lines.push(`model: ${droidConfig.model}`);
|
|
27655
|
+
lines.push(`model: ${this.yamlString(droidConfig.model)}`);
|
|
26741
27656
|
}
|
|
26742
27657
|
if (droidConfig.reasoningEffort) {
|
|
26743
27658
|
lines.push(`reasoningEffort: ${droidConfig.reasoningEffort}`);
|
|
26744
27659
|
}
|
|
26745
27660
|
if (droidConfig.specModel) {
|
|
26746
|
-
lines.push(`specModel: ${droidConfig.specModel}`);
|
|
27661
|
+
lines.push(`specModel: ${this.yamlString(droidConfig.specModel)}`);
|
|
26747
27662
|
}
|
|
26748
27663
|
if (droidConfig.specReasoningEffort) {
|
|
26749
27664
|
lines.push(`specReasoningEffort: ${droidConfig.specReasoningEffort}`);
|
|
@@ -27078,7 +27993,7 @@ ${trimmed}`;
|
|
|
27078
27993
|
additionalFiles.push(this.generateSkillFile(skill, options));
|
|
27079
27994
|
}
|
|
27080
27995
|
if (this.config.hasAgents) {
|
|
27081
|
-
const agents = this.extractAgents(ast);
|
|
27996
|
+
const agents = this.extractAgents(ast, options);
|
|
27082
27997
|
for (const agent of agents) {
|
|
27083
27998
|
additionalFiles.push(this.generateAgentFile(agent));
|
|
27084
27999
|
}
|
|
@@ -27736,7 +28651,7 @@ function agentMcpServerLines(mcpServers, resolvedMcpServers) {
|
|
|
27736
28651
|
}
|
|
27737
28652
|
return [];
|
|
27738
28653
|
}
|
|
27739
|
-
function serializeAgentToml(agentName, agent, resolvedMcpServers) {
|
|
28654
|
+
function serializeAgentToml(agentName, agent, resolvedMcpServers, models) {
|
|
27740
28655
|
const lines = [];
|
|
27741
28656
|
lines.push(`name = "${escapeTomlString2(agentName)}"`);
|
|
27742
28657
|
const description = agent["description"];
|
|
@@ -27745,8 +28660,9 @@ function serializeAgentToml(agentName, agent, resolvedMcpServers) {
|
|
|
27745
28660
|
}
|
|
27746
28661
|
lines.push(...agentInstructionsLines(agent["content"]));
|
|
27747
28662
|
const model = agent["model"];
|
|
27748
|
-
|
|
27749
|
-
|
|
28663
|
+
const codexModel = typeof model === "string" ? toTargetModel(model, "codex", models) : void 0;
|
|
28664
|
+
if (codexModel) {
|
|
28665
|
+
lines.push(`model = "${escapeTomlString2(codexModel)}"`);
|
|
27750
28666
|
}
|
|
27751
28667
|
const reasoningEffort = agent["reasoningEffort"];
|
|
27752
28668
|
if (typeof reasoningEffort === "string") {
|
|
@@ -27786,6 +28702,7 @@ var init_codex = __esm({
|
|
|
27786
28702
|
init_hook_adapters();
|
|
27787
28703
|
init_hook_capability_warnings();
|
|
27788
28704
|
init_agent_capability_warnings();
|
|
28705
|
+
init_model_mapping();
|
|
27789
28706
|
init_mcp_helpers();
|
|
27790
28707
|
init_plugin_helpers();
|
|
27791
28708
|
CODEX_VERSIONS = {
|
|
@@ -27974,7 +28891,12 @@ var init_codex = __esm({
|
|
|
27974
28891
|
if (!isValidAgentName(agentName)) continue;
|
|
27975
28892
|
const agent = agentValue;
|
|
27976
28893
|
const nativeAgentName = nativeNames.get(agentName) ?? agentName;
|
|
27977
|
-
const toml = serializeAgentToml(
|
|
28894
|
+
const toml = serializeAgentToml(
|
|
28895
|
+
nativeAgentName,
|
|
28896
|
+
agent,
|
|
28897
|
+
resolvedMcpServers,
|
|
28898
|
+
options?.models
|
|
28899
|
+
);
|
|
27978
28900
|
extraFiles.push({
|
|
27979
28901
|
path: `.codex/agents/${nativeAgentName}.toml`,
|
|
27980
28902
|
content: toml
|
|
@@ -28729,6 +29651,7 @@ var init_grok = __esm({
|
|
|
28729
29651
|
init_hook_adapters();
|
|
28730
29652
|
init_hook_capability_warnings();
|
|
28731
29653
|
init_agent_capability_warnings();
|
|
29654
|
+
init_model_mapping();
|
|
28732
29655
|
GROK_VERSIONS = {
|
|
28733
29656
|
simple: {
|
|
28734
29657
|
name: "simple",
|
|
@@ -28751,7 +29674,7 @@ var init_grok = __esm({
|
|
|
28751
29674
|
outputPath = "AGENTS.md";
|
|
28752
29675
|
description = "Grok Build instructions (AGENTS.md + Claude delegation)";
|
|
28753
29676
|
defaultConvention = "markdown";
|
|
28754
|
-
claudeFormatter = new ClaudeFormatter();
|
|
29677
|
+
claudeFormatter = new ClaudeFormatter("grok");
|
|
28755
29678
|
static getSupportedVersions() {
|
|
28756
29679
|
return GROK_VERSIONS;
|
|
28757
29680
|
}
|
|
@@ -28776,6 +29699,7 @@ var init_grok = __esm({
|
|
|
28776
29699
|
}
|
|
28777
29700
|
output = appendTargetHookCapabilityWarnings(output, ast, this.name, version);
|
|
28778
29701
|
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
29702
|
+
output = appendModelCompatibilityWarnings(output, ast, this.name, version, options?.models);
|
|
28779
29703
|
return {
|
|
28780
29704
|
...output,
|
|
28781
29705
|
managedOutputFiles: [
|
|
@@ -28809,7 +29733,7 @@ var init_grok = __esm({
|
|
|
28809
29733
|
});
|
|
28810
29734
|
const additionalFiles = [];
|
|
28811
29735
|
if (claudeMultifile.path !== "AGENTS.md") {
|
|
28812
|
-
additionalFiles.push(claudeMultifile);
|
|
29736
|
+
additionalFiles.push({ ...claudeMultifile, warnings: void 0 });
|
|
28813
29737
|
}
|
|
28814
29738
|
if (claudeMultifile.additionalFiles) {
|
|
28815
29739
|
additionalFiles.push(...claudeMultifile.additionalFiles);
|
|
@@ -28833,7 +29757,7 @@ var init_grok = __esm({
|
|
|
28833
29757
|
});
|
|
28834
29758
|
const additionalFiles = [];
|
|
28835
29759
|
if (claudeFull.path !== "AGENTS.md") {
|
|
28836
|
-
additionalFiles.push({ ...claudeFull, additionalFiles: void 0 });
|
|
29760
|
+
additionalFiles.push({ ...claudeFull, additionalFiles: void 0, warnings: void 0 });
|
|
28837
29761
|
}
|
|
28838
29762
|
if (claudeFull.additionalFiles) {
|
|
28839
29763
|
additionalFiles.push(
|
|
@@ -29341,7 +30265,7 @@ function formatMigrationHint(detection) {
|
|
|
29341
30265
|
lines.push("");
|
|
29342
30266
|
lines.push(" These can be migrated to PromptScript for unified management.");
|
|
29343
30267
|
lines.push(" Run: prs migrate");
|
|
29344
|
-
lines.push(" See: https://getpromptscript.dev/
|
|
30268
|
+
lines.push(" See: https://getpromptscript.dev/guides/ai-migration-best-practices");
|
|
29345
30269
|
return lines;
|
|
29346
30270
|
}
|
|
29347
30271
|
var PROMPTSCRIPT_MARKERS, INSTRUCTION_FILES, FILE_TOOL_NAMES, AI_TOOL_PATTERNS, INSTRUCTION_DIRECTORY_PATTERNS;
|
|
@@ -33577,7 +34501,7 @@ import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
|
33577
34501
|
// packages/cli/package.json
|
|
33578
34502
|
var package_default = {
|
|
33579
34503
|
name: "@promptscript/cli",
|
|
33580
|
-
version: "1.
|
|
34504
|
+
version: "1.21.0",
|
|
33581
34505
|
description: "CLI for PromptScript - standardize AI instructions across GitHub Copilot, Claude, Cursor and other AI tools",
|
|
33582
34506
|
keywords: [
|
|
33583
34507
|
"cli",
|
|
@@ -33629,12 +34553,12 @@ var package_default = {
|
|
|
33629
34553
|
commander: "^15.0.0",
|
|
33630
34554
|
minimatch: "^10.2.6",
|
|
33631
34555
|
ora: "^9.4.1",
|
|
33632
|
-
prettier: "^3.9.
|
|
34556
|
+
prettier: "^3.9.9",
|
|
33633
34557
|
yaml: "^2.9.1"
|
|
33634
34558
|
},
|
|
33635
34559
|
devDependencies: {
|
|
33636
34560
|
"@codecov/bundle-analyzer": "^2.0.1",
|
|
33637
|
-
memfs: "^4.
|
|
34561
|
+
memfs: "^4.79.0"
|
|
33638
34562
|
}
|
|
33639
34563
|
};
|
|
33640
34564
|
|
|
@@ -36977,7 +37901,7 @@ function parseYamlFrontmatter(frontmatter, sourceFile) {
|
|
|
36977
37901
|
if (document.contents === null) {
|
|
36978
37902
|
return { fields: {}, fieldLocations, fieldItemLocations, fieldValueLocations };
|
|
36979
37903
|
}
|
|
36980
|
-
if (!
|
|
37904
|
+
if (!isRecord6(value)) {
|
|
36981
37905
|
throw frontmatterError(
|
|
36982
37906
|
"top-level frontmatter value must be a YAML mapping",
|
|
36983
37907
|
sourceFile,
|
|
@@ -37135,7 +38059,7 @@ function enforceFrontmatterLimits(value, sourceFile, frontmatter, depth = 0, sta
|
|
|
37135
38059
|
}
|
|
37136
38060
|
}
|
|
37137
38061
|
}
|
|
37138
|
-
function
|
|
38062
|
+
function isRecord6(value) {
|
|
37139
38063
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37140
38064
|
}
|
|
37141
38065
|
function createSafeRecord() {
|
|
@@ -37278,7 +38202,7 @@ function parseParamsField(fields, sourceFile, location) {
|
|
|
37278
38202
|
if (!Object.hasOwn(fields, "params")) return void 0;
|
|
37279
38203
|
const value = fields["params"];
|
|
37280
38204
|
if (value === null) return [];
|
|
37281
|
-
if (!
|
|
38205
|
+
if (!isRecord6(value)) {
|
|
37282
38206
|
throw frontmatterError(
|
|
37283
38207
|
'field "params" must be a mapping of parameter names',
|
|
37284
38208
|
sourceFile,
|
|
@@ -37294,7 +38218,7 @@ function parseParamsField(fields, sourceFile, location) {
|
|
|
37294
38218
|
location
|
|
37295
38219
|
);
|
|
37296
38220
|
}
|
|
37297
|
-
if (!
|
|
38221
|
+
if (!isRecord6(definition)) {
|
|
37298
38222
|
throw frontmatterError(`parameter "${name}" must be a mapping`, sourceFile, location);
|
|
37299
38223
|
}
|
|
37300
38224
|
const paramType = parseTypedFieldType(definition, `parameter "${name}"`, sourceFile, location);
|
|
@@ -37333,7 +38257,7 @@ function parseContractFieldsField(fields, fieldName, sourceFile, location) {
|
|
|
37333
38257
|
if (!Object.hasOwn(fields, fieldName)) return void 0;
|
|
37334
38258
|
const value = fields[fieldName];
|
|
37335
38259
|
if (value === null) return createSafeRecord();
|
|
37336
|
-
if (!
|
|
38260
|
+
if (!isRecord6(value)) {
|
|
37337
38261
|
throw frontmatterError(
|
|
37338
38262
|
`field "${fieldName}" must be a mapping of fields`,
|
|
37339
38263
|
sourceFile,
|
|
@@ -37342,7 +38266,7 @@ function parseContractFieldsField(fields, fieldName, sourceFile, location) {
|
|
|
37342
38266
|
}
|
|
37343
38267
|
const result = createSafeRecord();
|
|
37344
38268
|
for (const [name, definition] of Object.entries(value)) {
|
|
37345
|
-
if (!
|
|
38269
|
+
if (!isRecord6(definition)) {
|
|
37346
38270
|
throw frontmatterError(
|
|
37347
38271
|
`${fieldName} field "${name}" must be a mapping`,
|
|
37348
38272
|
sourceFile,
|
|
@@ -37446,7 +38370,7 @@ function toPromptValue(value, context, sourceFile, location) {
|
|
|
37446
38370
|
if (Array.isArray(value)) {
|
|
37447
38371
|
return value.map((item) => toPromptValue(item, context, sourceFile, location));
|
|
37448
38372
|
}
|
|
37449
|
-
if (
|
|
38373
|
+
if (isRecord6(value)) {
|
|
37450
38374
|
const result = createSafeRecord();
|
|
37451
38375
|
for (const [key, item] of Object.entries(value)) {
|
|
37452
38376
|
result[key] = toPromptValue(item, context, sourceFile, location);
|
|
@@ -37472,7 +38396,7 @@ function readOptionalStringFromRecord(record, field, context, sourceFile, locati
|
|
|
37472
38396
|
function parseMetadataField(fields, sourceFile, location, valueLocations) {
|
|
37473
38397
|
if (!Object.hasOwn(fields, "metadata")) return void 0;
|
|
37474
38398
|
const value = fields["metadata"];
|
|
37475
|
-
if (!
|
|
38399
|
+
if (!isRecord6(value)) {
|
|
37476
38400
|
throw frontmatterError('field "metadata" must be a mapping', sourceFile, location);
|
|
37477
38401
|
}
|
|
37478
38402
|
const metadata = createSafeRecord();
|
|
@@ -41537,7 +42461,7 @@ function createResolutionContext() {
|
|
|
41537
42461
|
parsedFiles: /* @__PURE__ */ new Map()
|
|
41538
42462
|
};
|
|
41539
42463
|
}
|
|
41540
|
-
function
|
|
42464
|
+
function isRecord7(value) {
|
|
41541
42465
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41542
42466
|
}
|
|
41543
42467
|
function collectDependencyPaths(value, dependencies) {
|
|
@@ -41547,9 +42471,9 @@ function collectDependencyPaths(value, dependencies) {
|
|
|
41547
42471
|
}
|
|
41548
42472
|
return;
|
|
41549
42473
|
}
|
|
41550
|
-
if (!
|
|
42474
|
+
if (!isRecord7(value)) return;
|
|
41551
42475
|
const loc = value["loc"];
|
|
41552
|
-
if (
|
|
42476
|
+
if (isRecord7(loc) && typeof loc["file"] === "string") {
|
|
41553
42477
|
const file = loc["file"];
|
|
41554
42478
|
if (isAbsolute7(file)) dependencies.add(file);
|
|
41555
42479
|
}
|
|
@@ -46921,7 +47845,7 @@ var VALID_KINDS = [
|
|
|
46921
47845
|
"registry-allowlist"
|
|
46922
47846
|
];
|
|
46923
47847
|
var VALID_SEVERITIES = ["error", "warning"];
|
|
46924
|
-
function
|
|
47848
|
+
function isRecord8(value) {
|
|
46925
47849
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
46926
47850
|
}
|
|
46927
47851
|
function isStringArray(value) {
|
|
@@ -46998,7 +47922,7 @@ function validateRegistryAllowlist(raw, name) {
|
|
|
46998
47922
|
}
|
|
46999
47923
|
function parseOnePolicy(raw, index, seenNames) {
|
|
47000
47924
|
const errors = [];
|
|
47001
|
-
if (!
|
|
47925
|
+
if (!isRecord8(raw)) {
|
|
47002
47926
|
errors.push(`Policy at index ${index}: must be an object`);
|
|
47003
47927
|
return { policy: null, errors };
|
|
47004
47928
|
}
|
|
@@ -47051,15 +47975,15 @@ function parsePolicies(input2) {
|
|
|
47051
47975
|
}
|
|
47052
47976
|
|
|
47053
47977
|
// packages/validator/src/policy/evaluator.ts
|
|
47054
|
-
function
|
|
47978
|
+
function isRecord9(value) {
|
|
47055
47979
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47056
47980
|
}
|
|
47057
47981
|
function isLayerTraceEntry(value) {
|
|
47058
|
-
if (!
|
|
47982
|
+
if (!isRecord9(value)) return false;
|
|
47059
47983
|
return typeof value["property"] === "string" && typeof value["source"] === "string" && typeof value["strategy"] === "string" && typeof value["action"] === "string";
|
|
47060
47984
|
}
|
|
47061
47985
|
function isComposedFromEntry(value) {
|
|
47062
|
-
if (!
|
|
47986
|
+
if (!isRecord9(value)) return false;
|
|
47063
47987
|
return typeof value["source"] === "string" && typeof value["phase"] === "string";
|
|
47064
47988
|
}
|
|
47065
47989
|
function getLayerTrace(skill) {
|
|
@@ -47167,7 +48091,7 @@ function evaluatePolicies(policies, ast) {
|
|
|
47167
48091
|
const content = skillsBlock.content;
|
|
47168
48092
|
const violations = [];
|
|
47169
48093
|
for (const [skillName, skillValue] of Object.entries(content.properties)) {
|
|
47170
|
-
if (!
|
|
48094
|
+
if (!isRecord9(skillValue)) continue;
|
|
47171
48095
|
const skill = skillValue;
|
|
47172
48096
|
const baseSource = typeof skill["__baseSource"] === "string" ? skill["__baseSource"] : null;
|
|
47173
48097
|
for (const policy of policies) {
|
|
@@ -48209,7 +49133,7 @@ function isShortcutObject(value) {
|
|
|
48209
49133
|
function isShortcutNameConflict(existing, blockName, sourceName) {
|
|
48210
49134
|
return existing.sourceName !== sourceName || existing.blockName === blockName;
|
|
48211
49135
|
}
|
|
48212
|
-
function validateShortcutValues(ctx, block, normalizedNames,
|
|
49136
|
+
function validateShortcutValues(ctx, block, normalizedNames, targetNames2) {
|
|
48213
49137
|
if (block.content.type !== "ObjectContent") return;
|
|
48214
49138
|
for (const [name, value] of Object.entries(block.content.properties)) {
|
|
48215
49139
|
const normalizedName = name.replace(/^\/+/, "");
|
|
@@ -48229,7 +49153,7 @@ function validateShortcutValues(ctx, block, normalizedNames, targetNames) {
|
|
|
48229
49153
|
});
|
|
48230
49154
|
}
|
|
48231
49155
|
const targetName = normalizedName.toLowerCase().replace(/\s+/g, "-");
|
|
48232
|
-
const existingTargetName =
|
|
49156
|
+
const existingTargetName = targetNames2.get(targetName);
|
|
48233
49157
|
if (!hasNormalizedConflict && existingTargetName && isShortcutNameConflict(existingTargetName, block.name, name)) {
|
|
48234
49158
|
ctx.report({
|
|
48235
49159
|
message: `@${block.name} entry "${name}" resolves to the same target-normalized file name as @${existingTargetName.blockName} entry "${existingTargetName.sourceName}".`,
|
|
@@ -48238,7 +49162,7 @@ function validateShortcutValues(ctx, block, normalizedNames, targetNames) {
|
|
|
48238
49162
|
severity: "error"
|
|
48239
49163
|
});
|
|
48240
49164
|
} else if (!existingTargetName) {
|
|
48241
|
-
|
|
49165
|
+
targetNames2.set(targetName, {
|
|
48242
49166
|
blockName: block.name,
|
|
48243
49167
|
sourceName: name
|
|
48244
49168
|
});
|
|
@@ -48386,6 +49310,108 @@ var importExcludes = {
|
|
|
48386
49310
|
}
|
|
48387
49311
|
};
|
|
48388
49312
|
|
|
49313
|
+
// packages/validator/src/rules/valid-model-reference.ts
|
|
49314
|
+
init_src();
|
|
49315
|
+
var MODEL_FIELDS = {
|
|
49316
|
+
agents: { label: "Agent", fields: ["model", "specModel"] },
|
|
49317
|
+
skills: { label: "Skill", fields: ["model"] }
|
|
49318
|
+
};
|
|
49319
|
+
function blockEntries2(block) {
|
|
49320
|
+
if (block.content.type !== "ObjectContent" && block.content.type !== "MixedContent") return [];
|
|
49321
|
+
return Object.entries(block.content.properties).flatMap(
|
|
49322
|
+
([name, value]) => value && typeof value === "object" && !Array.isArray(value) ? [[name, value]] : []
|
|
49323
|
+
);
|
|
49324
|
+
}
|
|
49325
|
+
function collectReferences(ctx) {
|
|
49326
|
+
const references = [];
|
|
49327
|
+
for (const block of ctx.ast.blocks) {
|
|
49328
|
+
const spec = Object.hasOwn(MODEL_FIELDS, block.name) ? MODEL_FIELDS[block.name] : void 0;
|
|
49329
|
+
if (!spec) continue;
|
|
49330
|
+
for (const [name, entry] of blockEntries2(block)) {
|
|
49331
|
+
for (const field of spec.fields) {
|
|
49332
|
+
const value = entry[field];
|
|
49333
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
49334
|
+
references.push({
|
|
49335
|
+
owner: `${spec.label} "${name}"`,
|
|
49336
|
+
field,
|
|
49337
|
+
value: value.trim(),
|
|
49338
|
+
location: block.loc
|
|
49339
|
+
});
|
|
49340
|
+
}
|
|
49341
|
+
}
|
|
49342
|
+
}
|
|
49343
|
+
}
|
|
49344
|
+
return references;
|
|
49345
|
+
}
|
|
49346
|
+
var PROFILES_HINT = "or declare it under models.profiles in promptscript.yaml";
|
|
49347
|
+
var MODELS_DOC_LINK = "https://getpromptscript.dev/reference/models/";
|
|
49348
|
+
function readSupportedModels(config, catalog) {
|
|
49349
|
+
if (!Array.isArray(config?.supported)) return void 0;
|
|
49350
|
+
const entries = config.supported.filter((entry) => typeof entry === "string");
|
|
49351
|
+
return { entries, set: resolveModelSet(entries, catalog) };
|
|
49352
|
+
}
|
|
49353
|
+
function reportProfileIssues(ctx, catalog) {
|
|
49354
|
+
for (const issue of validateModelCatalog(catalog.profiles)) {
|
|
49355
|
+
ctx.report({
|
|
49356
|
+
message: `models.profiles: ${issue}`,
|
|
49357
|
+
suggestion: "Update the profile under models.profiles in promptscript.yaml."
|
|
49358
|
+
});
|
|
49359
|
+
}
|
|
49360
|
+
}
|
|
49361
|
+
function reportLifecycle(ctx, label, location, profile, catalog) {
|
|
49362
|
+
if (profile.status !== "retired" && profile.status !== "deprecated") return;
|
|
49363
|
+
const replacement = catalog.getReplacement(profile.id);
|
|
49364
|
+
ctx.report({
|
|
49365
|
+
message: `${label} resolves to ${profile.displayName}, which is ${profile.status}`,
|
|
49366
|
+
location,
|
|
49367
|
+
suggestion: replacement ? `Switch to ${replacement.id} (${replacement.displayName}).` : "Switch to a current model."
|
|
49368
|
+
});
|
|
49369
|
+
}
|
|
49370
|
+
function checkReference(ctx, reference, catalog, supported) {
|
|
49371
|
+
const label = `${reference.owner}: ${reference.field} "${reference.value}"`;
|
|
49372
|
+
const resolution = catalog.resolve(reference.value);
|
|
49373
|
+
if (!resolution) {
|
|
49374
|
+
if (supported) {
|
|
49375
|
+
ctx.report({
|
|
49376
|
+
message: `${label} is not in the model catalog`,
|
|
49377
|
+
location: reference.location,
|
|
49378
|
+
suggestion: `Use a model from models.supported, ${PROFILES_HINT}. See the model list: ${MODELS_DOC_LINK}`
|
|
49379
|
+
});
|
|
49380
|
+
}
|
|
49381
|
+
return;
|
|
49382
|
+
}
|
|
49383
|
+
if (resolution.kind === "inherit") return;
|
|
49384
|
+
reportLifecycle(ctx, label, reference.location, resolution.profile, catalog);
|
|
49385
|
+
if (supported && !isInModelSet(resolution, supported.set)) {
|
|
49386
|
+
ctx.report({
|
|
49387
|
+
message: `${label} is outside the supported model set`,
|
|
49388
|
+
location: reference.location,
|
|
49389
|
+
suggestion: `Use one of: ${supported.entries.join(", ")}; or add it to models.supported.`
|
|
49390
|
+
});
|
|
49391
|
+
}
|
|
49392
|
+
}
|
|
49393
|
+
var validModelReference = {
|
|
49394
|
+
id: "PS041",
|
|
49395
|
+
name: "valid-model-reference",
|
|
49396
|
+
description: "Model references must name served models from the project model set",
|
|
49397
|
+
defaultSeverity: "warning",
|
|
49398
|
+
validate: (ctx) => {
|
|
49399
|
+
const config = ctx.config.models;
|
|
49400
|
+
const catalog = getModelCatalog(config);
|
|
49401
|
+
const supported = readSupportedModels(config, catalog);
|
|
49402
|
+
if (config?.profiles !== void 0) reportProfileIssues(ctx, catalog);
|
|
49403
|
+
for (const entry of supported?.set.unknown ?? []) {
|
|
49404
|
+
ctx.report({
|
|
49405
|
+
message: `models.supported entry "${entry}" is not in the model catalog`,
|
|
49406
|
+
suggestion: `Use a catalog model id or alias, ${PROFILES_HINT}. See the model list: ${MODELS_DOC_LINK}`
|
|
49407
|
+
});
|
|
49408
|
+
}
|
|
49409
|
+
for (const reference of collectReferences(ctx)) {
|
|
49410
|
+
checkReference(ctx, reference, catalog, supported);
|
|
49411
|
+
}
|
|
49412
|
+
}
|
|
49413
|
+
};
|
|
49414
|
+
|
|
48389
49415
|
// packages/validator/src/rules/index.ts
|
|
48390
49416
|
var allRules = [
|
|
48391
49417
|
// Required meta rules (PS001, PS002)
|
|
@@ -48457,6 +49483,8 @@ var allRules = [
|
|
|
48457
49483
|
agentNamespaces,
|
|
48458
49484
|
// Import validation excludes bound to lockfile commits (PS040)
|
|
48459
49485
|
importExcludes,
|
|
49486
|
+
// Valid model references (PS041)
|
|
49487
|
+
validModelReference,
|
|
48460
49488
|
// Security rules (PS010, PS011, PS012, PS013, PS014)
|
|
48461
49489
|
suspiciousUrls,
|
|
48462
49490
|
authorityInjection,
|
|
@@ -48857,8 +49885,13 @@ var Compiler = class _Compiler {
|
|
|
48857
49885
|
constructor(options) {
|
|
48858
49886
|
this.options = options;
|
|
48859
49887
|
this.logger = options.logger ?? noopLogger;
|
|
49888
|
+
this.models = options.validator?.models ?? options.models;
|
|
48860
49889
|
this.resolver = new Resolver({ ...options.resolver, logger: this.logger });
|
|
48861
|
-
this.validator = new Validator({
|
|
49890
|
+
this.validator = new Validator({
|
|
49891
|
+
...options.validator,
|
|
49892
|
+
models: this.models,
|
|
49893
|
+
logger: this.logger
|
|
49894
|
+
});
|
|
48862
49895
|
this.loadedFormatters = this.loadFormatters(options.formatters);
|
|
48863
49896
|
this.logger.debug(`Compiler initialized with ${this.loadedFormatters.length} formatters`);
|
|
48864
49897
|
}
|
|
@@ -48868,6 +49901,9 @@ var Compiler = class _Compiler {
|
|
|
48868
49901
|
validator;
|
|
48869
49902
|
loadedFormatters;
|
|
48870
49903
|
logger;
|
|
49904
|
+
// Validation and formatting share one catalog, so PS041 and the target
|
|
49905
|
+
// model mapping agree about every name.
|
|
49906
|
+
models;
|
|
48871
49907
|
resolvedDependencies = /* @__PURE__ */ new Map();
|
|
48872
49908
|
hasExplicitResolverScope() {
|
|
48873
49909
|
return Boolean(this.options.resolver.localPath || this.options.resolver.projectRoot);
|
|
@@ -49669,7 +50705,8 @@ var Compiler = class _Compiler {
|
|
|
49669
50705
|
outputPath: config?.output,
|
|
49670
50706
|
version: config?.version,
|
|
49671
50707
|
prettier: prettierOptions,
|
|
49672
|
-
targetConfig: config
|
|
50708
|
+
targetConfig: config,
|
|
50709
|
+
models: this.models
|
|
49673
50710
|
};
|
|
49674
50711
|
const conventionName = config?.convention;
|
|
49675
50712
|
if (conventionName && customConventions?.[conventionName]) {
|
|
@@ -50108,11 +51145,11 @@ import { fileURLToPath } from "node:url";
|
|
|
50108
51145
|
import { promisify as promisify2 } from "node:util";
|
|
50109
51146
|
|
|
50110
51147
|
// packages/cli/src/runtime/runtime-info.ts
|
|
50111
|
-
function
|
|
51148
|
+
function isRecord10(value) {
|
|
50112
51149
|
return typeof value === "object" && value !== null;
|
|
50113
51150
|
}
|
|
50114
51151
|
function isDenoNamespace(value) {
|
|
50115
|
-
return
|
|
51152
|
+
return isRecord10(value) && isRecord10(value["build"]);
|
|
50116
51153
|
}
|
|
50117
51154
|
function getDenoNamespace() {
|
|
50118
51155
|
const denoCandidate = "Deno" in globalThis ? globalThis.Deno : void 0;
|
|
@@ -50130,7 +51167,7 @@ function getRuntimeVersion() {
|
|
|
50130
51167
|
if (denoNamespace === void 0) {
|
|
50131
51168
|
return process.versions.node;
|
|
50132
51169
|
}
|
|
50133
|
-
return
|
|
51170
|
+
return isRecord10(denoNamespace.version) && typeof denoNamespace.version["deno"] === "string" ? denoNamespace.version["deno"] : "0";
|
|
50134
51171
|
}
|
|
50135
51172
|
|
|
50136
51173
|
// packages/cli/src/runtime/self-invocation.ts
|
|
@@ -51835,6 +52872,9 @@ function printWarnings(warnings) {
|
|
|
51835
52872
|
console.log(`Warnings (${warnings.length}):`);
|
|
51836
52873
|
for (const warn of warnings) {
|
|
51837
52874
|
ConsoleOutput.warning(`${warn.ruleId}: ${warn.message}`);
|
|
52875
|
+
if (warn.suggestion) {
|
|
52876
|
+
ConsoleOutput.muted(`suggestion: ${warn.suggestion}`);
|
|
52877
|
+
}
|
|
51838
52878
|
}
|
|
51839
52879
|
}
|
|
51840
52880
|
}
|
|
@@ -52013,6 +53053,7 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
52013
53053
|
formatters: targets,
|
|
52014
53054
|
customConventions: config.customConventions,
|
|
52015
53055
|
prettier: prettierOptions,
|
|
53056
|
+
models: config.models,
|
|
52016
53057
|
logger,
|
|
52017
53058
|
skillContent,
|
|
52018
53059
|
ignoreHashes: options.ignoreHashes
|
|
@@ -52521,6 +53562,7 @@ async function validateCommand2(options) {
|
|
|
52521
53562
|
policies: options.skipPolicies ? void 0 : config.policies,
|
|
52522
53563
|
skipPolicies: options.skipPolicies
|
|
52523
53564
|
},
|
|
53565
|
+
models: config.models,
|
|
52524
53566
|
ignoreHashes: options.ignoreHashes,
|
|
52525
53567
|
formatters: []
|
|
52526
53568
|
// No formatters needed for validation only
|
|
@@ -53341,6 +54383,7 @@ async function diffCommand(options) {
|
|
|
53341
54383
|
formatters: targets,
|
|
53342
54384
|
customConventions: config.customConventions,
|
|
53343
54385
|
prettier: prettierOptions,
|
|
54386
|
+
models: config.models,
|
|
53344
54387
|
logger,
|
|
53345
54388
|
skillContent
|
|
53346
54389
|
});
|
|
@@ -53697,8 +54740,8 @@ async function checkCommand(_options) {
|
|
|
53697
54740
|
hasErrors = true;
|
|
53698
54741
|
}
|
|
53699
54742
|
}
|
|
53700
|
-
const
|
|
53701
|
-
const
|
|
54743
|
+
const targetIssues2 = [];
|
|
54744
|
+
const targetNames2 = /* @__PURE__ */ new Set();
|
|
53702
54745
|
const rootTargetValue = config.targets;
|
|
53703
54746
|
const rootTargets = Array.isArray(rootTargetValue) ? rootTargetValue : [];
|
|
53704
54747
|
const buildsValue = config.builds;
|
|
@@ -53706,50 +54749,50 @@ async function checkCommand(_options) {
|
|
|
53706
54749
|
const appendTargetAnalysis = (location, entries) => {
|
|
53707
54750
|
const analysis = analyzeTargetEntries(entries);
|
|
53708
54751
|
for (const name of analysis.names) {
|
|
53709
|
-
|
|
54752
|
+
targetNames2.add(name);
|
|
53710
54753
|
}
|
|
53711
|
-
|
|
54754
|
+
targetIssues2.push(...analysis.errors.map((error) => `${location}: ${error}`));
|
|
53712
54755
|
if (entries.length > 0 && analysis.names.length === 0 && analysis.errors.length === 0) {
|
|
53713
|
-
|
|
54756
|
+
targetIssues2.push(`${location}: no enabled targets`);
|
|
53714
54757
|
}
|
|
53715
54758
|
};
|
|
53716
54759
|
if (rootTargetValue !== void 0 && !Array.isArray(rootTargetValue)) {
|
|
53717
|
-
|
|
54760
|
+
targetIssues2.push("config.targets must be an array");
|
|
53718
54761
|
} else if (rootTargets.length > 0) {
|
|
53719
54762
|
appendTargetAnalysis("config.targets", rootTargets);
|
|
53720
54763
|
}
|
|
53721
54764
|
if (buildsValue !== void 0 && (buildsValue === null || typeof buildsValue !== "object" || Array.isArray(buildsValue))) {
|
|
53722
|
-
|
|
54765
|
+
targetIssues2.push("config.builds must be an object");
|
|
53723
54766
|
}
|
|
53724
54767
|
for (const [buildName, profile] of builds) {
|
|
53725
54768
|
if (!profile || typeof profile !== "object" || Array.isArray(profile)) {
|
|
53726
|
-
|
|
54769
|
+
targetIssues2.push(`config.builds.${buildName} must be an object`);
|
|
53727
54770
|
continue;
|
|
53728
54771
|
}
|
|
53729
54772
|
const profileTargetValue = profile.targets;
|
|
53730
54773
|
if (profileTargetValue === void 0) {
|
|
53731
54774
|
if (rootTargets.length === 0) {
|
|
53732
|
-
|
|
54775
|
+
targetIssues2.push(
|
|
53733
54776
|
`config.builds.${buildName}.targets is missing and config.targets is empty`
|
|
53734
54777
|
);
|
|
53735
54778
|
}
|
|
53736
54779
|
continue;
|
|
53737
54780
|
}
|
|
53738
54781
|
if (!Array.isArray(profileTargetValue)) {
|
|
53739
|
-
|
|
54782
|
+
targetIssues2.push(`config.builds.${buildName}.targets must be an array`);
|
|
53740
54783
|
continue;
|
|
53741
54784
|
}
|
|
53742
54785
|
if (profileTargetValue.length === 0) {
|
|
53743
|
-
|
|
54786
|
+
targetIssues2.push(`config.builds.${buildName}.targets is empty`);
|
|
53744
54787
|
continue;
|
|
53745
54788
|
}
|
|
53746
54789
|
appendTargetAnalysis(`config.builds.${buildName}.targets`, profileTargetValue);
|
|
53747
54790
|
}
|
|
53748
|
-
if (
|
|
54791
|
+
if (targetIssues2.length > 0) {
|
|
53749
54792
|
results.push({
|
|
53750
54793
|
name: "Targets",
|
|
53751
54794
|
status: "error",
|
|
53752
|
-
message:
|
|
54795
|
+
message: targetIssues2.join("; ")
|
|
53753
54796
|
});
|
|
53754
54797
|
hasErrors = true;
|
|
53755
54798
|
} else if (rootTargets.length === 0 && builds.length === 0) {
|
|
@@ -53763,7 +54806,7 @@ async function checkCommand(_options) {
|
|
|
53763
54806
|
results.push({
|
|
53764
54807
|
name: "Targets",
|
|
53765
54808
|
status: "ok",
|
|
53766
|
-
message: rootTargets.length === 0 ? `${
|
|
54809
|
+
message: rootTargets.length === 0 ? `${targetNames2.size} target(s) configured in build profiles` : `${targetNames2.size} target(s) configured`
|
|
53767
54810
|
});
|
|
53768
54811
|
}
|
|
53769
54812
|
const existingEntries = effectiveEntries.filter((entry) => existsSync18(resolve28(entry)));
|
|
@@ -53800,6 +54843,7 @@ async function checkCommand(_options) {
|
|
|
53800
54843
|
...config.validation,
|
|
53801
54844
|
policies: config.policies
|
|
53802
54845
|
},
|
|
54846
|
+
models: config.models,
|
|
53803
54847
|
formatters: []
|
|
53804
54848
|
});
|
|
53805
54849
|
for (const entry of existingEntries) {
|
|
@@ -56318,7 +57362,7 @@ function tokenizePath(path2) {
|
|
|
56318
57362
|
}
|
|
56319
57363
|
return tokens;
|
|
56320
57364
|
}
|
|
56321
|
-
function
|
|
57365
|
+
function isRecord11(value) {
|
|
56322
57366
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
56323
57367
|
}
|
|
56324
57368
|
function readValueAtPath(ast, path2) {
|
|
@@ -56329,7 +57373,7 @@ function readValueAtPath(ast, path2) {
|
|
|
56329
57373
|
if (!block) return void 0;
|
|
56330
57374
|
let value = block.content;
|
|
56331
57375
|
for (const token of tokens) {
|
|
56332
|
-
if (token === "text" &&
|
|
57376
|
+
if (token === "text" && isRecord11(value)) {
|
|
56333
57377
|
const contentType = value["type"];
|
|
56334
57378
|
if (contentType === "TextContent") {
|
|
56335
57379
|
value = value["value"];
|
|
@@ -56343,21 +57387,21 @@ function readValueAtPath(ast, path2) {
|
|
|
56343
57387
|
value = value[token];
|
|
56344
57388
|
} else if (typeof value === "string") {
|
|
56345
57389
|
value = value[token];
|
|
56346
|
-
} else if (
|
|
57390
|
+
} else if (isRecord11(value) && value["type"] === "TextContent") {
|
|
56347
57391
|
value = value["value"];
|
|
56348
57392
|
} else {
|
|
56349
57393
|
return void 0;
|
|
56350
57394
|
}
|
|
56351
57395
|
continue;
|
|
56352
57396
|
}
|
|
56353
|
-
if (
|
|
57397
|
+
if (isRecord11(value)) {
|
|
56354
57398
|
if (value["type"] === "TextContent" && token === "value") {
|
|
56355
57399
|
value = value["value"];
|
|
56356
57400
|
} else if (Object.hasOwn(value, token)) {
|
|
56357
57401
|
value = value[token];
|
|
56358
|
-
} else if (value["type"] === "ObjectContent" &&
|
|
57402
|
+
} else if (value["type"] === "ObjectContent" && isRecord11(value["properties"])) {
|
|
56359
57403
|
value = value["properties"][token];
|
|
56360
|
-
} else if (value["type"] === "MixedContent" &&
|
|
57404
|
+
} else if (value["type"] === "MixedContent" && isRecord11(value["properties"])) {
|
|
56361
57405
|
value = value["properties"][token];
|
|
56362
57406
|
} else {
|
|
56363
57407
|
return void 0;
|
|
@@ -56398,7 +57442,7 @@ function sanitizeValue(value, options, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
56398
57442
|
seen.add(value);
|
|
56399
57443
|
return value.map((item) => sanitizeValue(item, options, seen));
|
|
56400
57444
|
}
|
|
56401
|
-
if (
|
|
57445
|
+
if (isRecord11(value)) {
|
|
56402
57446
|
if (seen.has(value)) return value;
|
|
56403
57447
|
seen.add(value);
|
|
56404
57448
|
return Object.fromEntries(
|