@promptscript/cli 1.18.1 → 1.19.1
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/README.md +26 -6
- package/index.js +2134 -591
- package/package.json +5 -5
- package/skills/promptscript/SKILL.md +12 -5
- package/src/cli.d.ts.map +1 -1
- package/src/commands/compile.d.ts.map +1 -1
- package/src/commands/init.d.ts.map +1 -1
- package/src/commands/lock-scanner.d.ts +2 -2
- package/src/commands/lock-scanner.d.ts.map +1 -1
- package/src/commands/lock.d.ts.map +1 -1
- package/src/commands/resolve-cmd.d.ts.map +1 -1
- package/src/commands/skills.d.ts +27 -5
- package/src/commands/skills.d.ts.map +1 -1
- package/src/config/merge-config.d.ts.map +1 -1
- package/src/types.d.ts +10 -0
- package/src/types.d.ts.map +1 -1
- package/src/utils/global-output-safety.d.ts +12 -0
- package/src/utils/global-output-safety.d.ts.map +1 -0
- package/src/utils/managed-output-cleanup.d.ts.map +1 -1
- package/src/utils/output-ownership.d.ts +1 -1
- package/src/utils/output-ownership.d.ts.map +1 -1
- package/src/utils/registry-resolver.d.ts +2 -0
- package/src/utils/registry-resolver.d.ts.map +1 -1
- package/src/utils/resource-filter.d.ts +35 -0
- package/src/utils/resource-filter.d.ts.map +1 -0
package/index.js
CHANGED
|
@@ -113,7 +113,9 @@ var init_target_output_paths = __esm({
|
|
|
113
113
|
mimo: "AGENTS.md",
|
|
114
114
|
"deep-agents": "AGENTS.md",
|
|
115
115
|
forgecode: "AGENTS.md",
|
|
116
|
-
hermes: "AGENTS.md"
|
|
116
|
+
hermes: "AGENTS.md",
|
|
117
|
+
// GitLab Duo (Agent Platform)
|
|
118
|
+
"gitlab-duo": "AGENTS.md"
|
|
117
119
|
};
|
|
118
120
|
DEFAULT_OUTPUT_PATHS = DEFAULT_OUTPUT_PATH_VALUES;
|
|
119
121
|
}
|
|
@@ -184,7 +186,9 @@ var init_config = __esm({
|
|
|
184
186
|
"mimo",
|
|
185
187
|
"deep-agents",
|
|
186
188
|
"forgecode",
|
|
187
|
-
"hermes"
|
|
189
|
+
"hermes",
|
|
190
|
+
// GitLab Duo (Agent Platform)
|
|
191
|
+
"gitlab-duo"
|
|
188
192
|
];
|
|
189
193
|
}
|
|
190
194
|
});
|
|
@@ -914,10 +918,12 @@ var init_levenshtein = __esm({
|
|
|
914
918
|
|
|
915
919
|
// packages/core/src/utils/markers.ts
|
|
916
920
|
function stripPromptScriptMarkers(content) {
|
|
917
|
-
return content.replace(new RegExp(`${HTML_MARKER}\\n*`, "g"), "").replace(new RegExp(`${YAML_MARKER}[^\\n]+\\n*`, "g"), "");
|
|
921
|
+
return content.replace(new RegExp(`${HTML_MARKER}\\n*`, "g"), "").replace(new RegExp(`${TYPESCRIPT_MARKER}[^\\n]+\\n*`, "g"), "").replace(new RegExp(`${LEGACY_TYPESCRIPT_MARKER}\\n*`, "g"), "").replace(new RegExp(`${YAML_MARKER}[^\\n]+\\n*`, "g"), "");
|
|
918
922
|
}
|
|
919
923
|
function isGeneratedByPromptScript(content) {
|
|
920
|
-
const head = new RegExp(
|
|
924
|
+
const head = new RegExp(
|
|
925
|
+
`^\\s*(?:${YAML_MARKER}|${TYPESCRIPT_MARKER}|${LEGACY_TYPESCRIPT_MARKER}|${HTML_MARKER})`
|
|
926
|
+
);
|
|
921
927
|
return content.split("\n", 5).some((line) => head.test(line));
|
|
922
928
|
}
|
|
923
929
|
function stripFrontmatterMarkerLines(frontmatter) {
|
|
@@ -927,12 +933,14 @@ function stripFrontmatterMarkerLines(frontmatter) {
|
|
|
927
933
|
function stripLeadingHtmlMarker(body) {
|
|
928
934
|
return body.replace(new RegExp(`^\\s*${HTML_MARKER}\\n*`), "");
|
|
929
935
|
}
|
|
930
|
-
var HTML_MARKER, YAML_MARKER;
|
|
936
|
+
var HTML_MARKER, YAML_MARKER, TYPESCRIPT_MARKER, LEGACY_TYPESCRIPT_MARKER;
|
|
931
937
|
var init_markers = __esm({
|
|
932
938
|
"packages/core/src/utils/markers.ts"() {
|
|
933
939
|
"use strict";
|
|
934
940
|
HTML_MARKER = "<!--\\s*PromptScript [^>]+-->";
|
|
935
941
|
YAML_MARKER = "#\\s*promptscript-generated:";
|
|
942
|
+
TYPESCRIPT_MARKER = "//\\s*promptscript-generated:";
|
|
943
|
+
LEGACY_TYPESCRIPT_MARKER = "//\\s*Generated by PromptScript - do not edit\\.";
|
|
936
944
|
}
|
|
937
945
|
});
|
|
938
946
|
|
|
@@ -5092,10 +5100,21 @@ var init_hook_capabilities = __esm({
|
|
|
5092
5100
|
}
|
|
5093
5101
|
},
|
|
5094
5102
|
opencode: {
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5103
|
+
status: "native",
|
|
5104
|
+
configPath: ".opencode/plugins/promptscript.ts",
|
|
5105
|
+
events: ["pre-tool-use", "post-tool-use"],
|
|
5106
|
+
commandFormat: "generated TypeScript plugin with bounded JSON payloads",
|
|
5107
|
+
timeoutUnit: "milliseconds",
|
|
5108
|
+
projectRootStrategy: "plugin-context",
|
|
5109
|
+
platforms: ["unix"],
|
|
5110
|
+
nativeVersions: ["multifile", "full"],
|
|
5111
|
+
fallback: "Use OpenCode version 'multifile' or 'full'.",
|
|
5112
|
+
documentationUrl: "https://opencode.ai/docs/plugins/",
|
|
5113
|
+
terminal: {
|
|
5114
|
+
guarantee: "not-guaranteed",
|
|
5115
|
+
toolNames: [],
|
|
5116
|
+
notes: "OpenCode exposes tool.execute.before and tool.execute.after only; a dedicated terminal command event is not guaranteed, and MCP tool calls plus some subagent paths may not fire plugin hooks at all."
|
|
5117
|
+
}
|
|
5099
5118
|
},
|
|
5100
5119
|
gemini: {
|
|
5101
5120
|
status: "native",
|
|
@@ -5233,7 +5252,8 @@ var init_hook_capabilities = __esm({
|
|
|
5233
5252
|
mimo: UNSUPPORTED_CAPABILITY,
|
|
5234
5253
|
"deep-agents": UNSUPPORTED_CAPABILITY,
|
|
5235
5254
|
forgecode: UNSUPPORTED_CAPABILITY,
|
|
5236
|
-
hermes: UNSUPPORTED_CAPABILITY
|
|
5255
|
+
hermes: UNSUPPORTED_CAPABILITY,
|
|
5256
|
+
"gitlab-duo": UNSUPPORTED_CAPABILITY
|
|
5237
5257
|
};
|
|
5238
5258
|
VSCODE_HOOK_CAPABILITY = {
|
|
5239
5259
|
status: "compatible",
|
|
@@ -6122,12 +6142,12 @@ var init_target_capabilities = __esm({
|
|
|
6122
6142
|
simple: { name: "simple", description: "Single OPENCODE.md file", outputPath: "OPENCODE.md" },
|
|
6123
6143
|
multifile: {
|
|
6124
6144
|
name: "multifile",
|
|
6125
|
-
description: "OPENCODE.md + .opencode/commands/<name>.md (skills via full mode)",
|
|
6145
|
+
description: "OPENCODE.md + .opencode/commands/<name>.md + .opencode/plugins/promptscript.ts (skills via full mode)",
|
|
6126
6146
|
outputPath: "OPENCODE.md"
|
|
6127
6147
|
},
|
|
6128
6148
|
full: {
|
|
6129
6149
|
name: "full",
|
|
6130
|
-
description: "Multifile + .opencode/skills/<name>/SKILL.md + .opencode/commands/<name>.md + .opencode/agents/<name>.md",
|
|
6150
|
+
description: "Multifile + .opencode/skills/<name>/SKILL.md + .opencode/commands/<name>.md + .opencode/agents/<name>.md + .opencode/plugins/promptscript.ts",
|
|
6131
6151
|
outputPath: "OPENCODE.md"
|
|
6132
6152
|
}
|
|
6133
6153
|
}
|
|
@@ -6832,6 +6852,21 @@ var init_target_capabilities = __esm({
|
|
|
6832
6852
|
},
|
|
6833
6853
|
full: { name: "full", description: "Single AGENTS.md file", outputPath: "AGENTS.md" }
|
|
6834
6854
|
}
|
|
6855
|
+
},
|
|
6856
|
+
"gitlab-duo": {
|
|
6857
|
+
versions: {
|
|
6858
|
+
simple: { name: "simple", description: "Single AGENTS.md file", outputPath: "AGENTS.md" },
|
|
6859
|
+
multifile: {
|
|
6860
|
+
name: "multifile",
|
|
6861
|
+
description: "Single AGENTS.md file (skills via full mode)",
|
|
6862
|
+
outputPath: "AGENTS.md"
|
|
6863
|
+
},
|
|
6864
|
+
full: {
|
|
6865
|
+
name: "full",
|
|
6866
|
+
description: "Multifile + skills/<name>/SKILL.md",
|
|
6867
|
+
outputPath: "AGENTS.md"
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6835
6870
|
}
|
|
6836
6871
|
};
|
|
6837
6872
|
}
|
|
@@ -7383,6 +7418,22 @@ var init_target_catalog = __esm({
|
|
|
7383
7418
|
hasCommands: false
|
|
7384
7419
|
}
|
|
7385
7420
|
},
|
|
7421
|
+
// GitLab Duo (Agent Platform)
|
|
7422
|
+
"gitlab-duo": {
|
|
7423
|
+
name: "gitlab-duo",
|
|
7424
|
+
outputPath: DEFAULT_OUTPUT_PATHS["gitlab-duo"],
|
|
7425
|
+
family: "simple",
|
|
7426
|
+
// Duo reads Agent Skills from a repository-root `skills/` directory,
|
|
7427
|
+
// so the dot-directory skill layout does not apply to this target.
|
|
7428
|
+
features: {
|
|
7429
|
+
defaultEnabled: false,
|
|
7430
|
+
defaultVersion: "full",
|
|
7431
|
+
hasSkills: true,
|
|
7432
|
+
hasAgents: false,
|
|
7433
|
+
hasCommands: false
|
|
7434
|
+
},
|
|
7435
|
+
skillPath: { basePath: "skills", fileName: "SKILL.md" }
|
|
7436
|
+
},
|
|
7386
7437
|
// AGENTS.md-only targets
|
|
7387
7438
|
aider: {
|
|
7388
7439
|
name: "aider",
|
|
@@ -7557,6 +7608,89 @@ var init_target_catalog = __esm({
|
|
|
7557
7608
|
}
|
|
7558
7609
|
});
|
|
7559
7610
|
|
|
7611
|
+
// packages/core/src/agent-capabilities.ts
|
|
7612
|
+
function fieldStatus(groups, target) {
|
|
7613
|
+
if (groups.emitted?.includes(target)) return "emitted";
|
|
7614
|
+
if (groups.transformed?.includes(target)) return "transformed";
|
|
7615
|
+
return "not-supported";
|
|
7616
|
+
}
|
|
7617
|
+
function getAgentFieldStatus(target, field) {
|
|
7618
|
+
return fieldStatus(AGENT_FIELD_STATUS_GROUPS[field], target);
|
|
7619
|
+
}
|
|
7620
|
+
function listAgentFieldSupportTargets(field) {
|
|
7621
|
+
const groups = AGENT_FIELD_STATUS_GROUPS[field];
|
|
7622
|
+
return { emitted: groups.emitted ?? [], transformed: groups.transformed ?? [] };
|
|
7623
|
+
}
|
|
7624
|
+
var CANONICAL_AGENT_FIELDS, ALL_NATIVE_AGENT_TARGETS, CLAUDE_CONTRACT_TARGETS, MARKDOWN_CONTRACT_TARGETS, AGENT_FIELD_STATUS_GROUPS;
|
|
7625
|
+
var init_agent_capabilities = __esm({
|
|
7626
|
+
"packages/core/src/agent-capabilities.ts"() {
|
|
7627
|
+
"use strict";
|
|
7628
|
+
init_target_catalog();
|
|
7629
|
+
CANONICAL_AGENT_FIELDS = [
|
|
7630
|
+
"description",
|
|
7631
|
+
"content",
|
|
7632
|
+
"tools",
|
|
7633
|
+
"model",
|
|
7634
|
+
"reasoningEffort",
|
|
7635
|
+
"specModel",
|
|
7636
|
+
"specReasoningEffort",
|
|
7637
|
+
"disallowedTools",
|
|
7638
|
+
"permissionMode",
|
|
7639
|
+
"skills",
|
|
7640
|
+
"mcpServers",
|
|
7641
|
+
"sandboxMode",
|
|
7642
|
+
"nicknameCandidates",
|
|
7643
|
+
"handoffs",
|
|
7644
|
+
"maxTurns",
|
|
7645
|
+
"memory",
|
|
7646
|
+
"background",
|
|
7647
|
+
"isolation"
|
|
7648
|
+
];
|
|
7649
|
+
ALL_NATIVE_AGENT_TARGETS = [
|
|
7650
|
+
"github",
|
|
7651
|
+
"claude",
|
|
7652
|
+
"cursor",
|
|
7653
|
+
"factory",
|
|
7654
|
+
"codex",
|
|
7655
|
+
"opencode",
|
|
7656
|
+
"augment",
|
|
7657
|
+
"amp",
|
|
7658
|
+
"grok"
|
|
7659
|
+
];
|
|
7660
|
+
CLAUDE_CONTRACT_TARGETS = ["claude", "grok"];
|
|
7661
|
+
MARKDOWN_CONTRACT_TARGETS = ["opencode", "augment", "amp"];
|
|
7662
|
+
AGENT_FIELD_STATUS_GROUPS = {
|
|
7663
|
+
description: { emitted: ALL_NATIVE_AGENT_TARGETS },
|
|
7664
|
+
content: {
|
|
7665
|
+
emitted: [...MARKDOWN_CONTRACT_TARGETS, "github", "claude", "cursor", "factory", "grok"],
|
|
7666
|
+
transformed: ["codex"]
|
|
7667
|
+
},
|
|
7668
|
+
tools: { emitted: [...CLAUDE_CONTRACT_TARGETS, "factory"], transformed: ["github"] },
|
|
7669
|
+
model: {
|
|
7670
|
+
emitted: [...CLAUDE_CONTRACT_TARGETS, "cursor", "factory", "codex"],
|
|
7671
|
+
transformed: ["github"]
|
|
7672
|
+
},
|
|
7673
|
+
reasoningEffort: { emitted: ["factory"], transformed: ["codex"] },
|
|
7674
|
+
specModel: { emitted: ["factory"], transformed: ["github"] },
|
|
7675
|
+
specReasoningEffort: { emitted: ["factory"] },
|
|
7676
|
+
disallowedTools: { emitted: CLAUDE_CONTRACT_TARGETS },
|
|
7677
|
+
permissionMode: { emitted: CLAUDE_CONTRACT_TARGETS },
|
|
7678
|
+
skills: { emitted: CLAUDE_CONTRACT_TARGETS, transformed: ["codex"] },
|
|
7679
|
+
mcpServers: {
|
|
7680
|
+
emitted: [...CLAUDE_CONTRACT_TARGETS, "cursor", "factory"],
|
|
7681
|
+
transformed: ["github", "codex"]
|
|
7682
|
+
},
|
|
7683
|
+
sandboxMode: { transformed: ["codex"] },
|
|
7684
|
+
nicknameCandidates: { transformed: ["codex"] },
|
|
7685
|
+
handoffs: { emitted: ["github"] },
|
|
7686
|
+
maxTurns: { emitted: CLAUDE_CONTRACT_TARGETS },
|
|
7687
|
+
memory: { emitted: CLAUDE_CONTRACT_TARGETS },
|
|
7688
|
+
background: { emitted: CLAUDE_CONTRACT_TARGETS },
|
|
7689
|
+
isolation: { emitted: CLAUDE_CONTRACT_TARGETS }
|
|
7690
|
+
};
|
|
7691
|
+
}
|
|
7692
|
+
});
|
|
7693
|
+
|
|
7560
7694
|
// packages/core/src/git-timeout.ts
|
|
7561
7695
|
function isGitTimeoutError(error) {
|
|
7562
7696
|
const errorWithCode = error;
|
|
@@ -7795,6 +7929,80 @@ var init_output_plan = __esm({
|
|
|
7795
7929
|
}
|
|
7796
7930
|
});
|
|
7797
7931
|
|
|
7932
|
+
// packages/core/src/output-resources.ts
|
|
7933
|
+
function isOutputResourceKind(value) {
|
|
7934
|
+
return OUTPUT_RESOURCE_KINDS.includes(value);
|
|
7935
|
+
}
|
|
7936
|
+
function keepBestMatch(current, next) {
|
|
7937
|
+
return current && current.specificity >= next.specificity ? current : next;
|
|
7938
|
+
}
|
|
7939
|
+
function matchResourcePath(resourcePath, normalizedPath) {
|
|
7940
|
+
const nameIndex = resourcePath.indexOf("<name>");
|
|
7941
|
+
if (nameIndex >= 0) {
|
|
7942
|
+
const prefix = resourcePath.slice(0, nameIndex);
|
|
7943
|
+
return normalizedPath.startsWith(prefix) ? prefix.length : 0;
|
|
7944
|
+
}
|
|
7945
|
+
return resourcePath === normalizedPath ? resourcePath.length + 2 : 0;
|
|
7946
|
+
}
|
|
7947
|
+
function normalizeBaseDir(dir) {
|
|
7948
|
+
return dir.replaceAll("\\", "/").split("/").filter(Boolean).join("/");
|
|
7949
|
+
}
|
|
7950
|
+
function hooksSiblingSpecificity(resource, normalizedPath) {
|
|
7951
|
+
const directory = resource.path.slice(0, resource.path.lastIndexOf("/"));
|
|
7952
|
+
if (!directory.endsWith("/hooks")) return 0;
|
|
7953
|
+
if (!normalizedPath.startsWith(`${directory}/`)) return 0;
|
|
7954
|
+
if (normalizedPath.slice(directory.length + 1).includes("/")) return 0;
|
|
7955
|
+
return directory.length + 2;
|
|
7956
|
+
}
|
|
7957
|
+
function matchCatalogResource(target, normalizedPath) {
|
|
7958
|
+
let best;
|
|
7959
|
+
for (const resource of TARGET_CAPABILITIES[target].resources) {
|
|
7960
|
+
let next;
|
|
7961
|
+
if (resource.kind === "main") {
|
|
7962
|
+
const specificity = matchResourcePath(resource.path, normalizedPath);
|
|
7963
|
+
if (specificity > 0) {
|
|
7964
|
+
next = { kind: "main", specificity };
|
|
7965
|
+
}
|
|
7966
|
+
} else {
|
|
7967
|
+
const specificity = matchResourcePath(resource.path, normalizedPath) || (resource.kind === "hooks" ? hooksSiblingSpecificity(resource, normalizedPath) : 0);
|
|
7968
|
+
if (specificity > 0) {
|
|
7969
|
+
next = { kind: resource.kind, specificity };
|
|
7970
|
+
}
|
|
7971
|
+
}
|
|
7972
|
+
if (next) {
|
|
7973
|
+
best = keepBestMatch(best, next);
|
|
7974
|
+
}
|
|
7975
|
+
}
|
|
7976
|
+
return best?.kind;
|
|
7977
|
+
}
|
|
7978
|
+
function classifyOutputResource(target, path2, skillBaseDir) {
|
|
7979
|
+
const normalizedPath = path2.replaceAll("\\", "/");
|
|
7980
|
+
const catalogKind = isKnownTarget(target) ? matchCatalogResource(target, normalizedPath) : void 0;
|
|
7981
|
+
if (catalogKind) return catalogKind;
|
|
7982
|
+
const baseDir = skillBaseDir ? normalizeBaseDir(skillBaseDir) : "";
|
|
7983
|
+
if (baseDir && (normalizedPath === baseDir || normalizedPath.startsWith(`${baseDir}/`))) {
|
|
7984
|
+
return "skills";
|
|
7985
|
+
}
|
|
7986
|
+
return "main";
|
|
7987
|
+
}
|
|
7988
|
+
var OUTPUT_RESOURCE_KINDS;
|
|
7989
|
+
var init_output_resources = __esm({
|
|
7990
|
+
"packages/core/src/output-resources.ts"() {
|
|
7991
|
+
"use strict";
|
|
7992
|
+
init_target_catalog();
|
|
7993
|
+
init_config();
|
|
7994
|
+
OUTPUT_RESOURCE_KINDS = [
|
|
7995
|
+
"agents",
|
|
7996
|
+
"skills",
|
|
7997
|
+
"commands",
|
|
7998
|
+
"mcp",
|
|
7999
|
+
"hooks",
|
|
8000
|
+
"plugins",
|
|
8001
|
+
"main"
|
|
8002
|
+
];
|
|
8003
|
+
}
|
|
8004
|
+
});
|
|
8005
|
+
|
|
7798
8006
|
// packages/core/src/index.ts
|
|
7799
8007
|
var init_src = __esm({
|
|
7800
8008
|
"packages/core/src/index.ts"() {
|
|
@@ -7818,10 +8026,12 @@ var init_src = __esm({
|
|
|
7818
8026
|
init_section_registry();
|
|
7819
8027
|
init_target_catalog();
|
|
7820
8028
|
init_target_capabilities();
|
|
8029
|
+
init_agent_capabilities();
|
|
7821
8030
|
init_hook_capabilities();
|
|
7822
8031
|
init_git_timeout();
|
|
7823
8032
|
init_structured_output();
|
|
7824
8033
|
init_output_plan();
|
|
8034
|
+
init_output_resources();
|
|
7825
8035
|
}
|
|
7826
8036
|
});
|
|
7827
8037
|
|
|
@@ -17039,12 +17249,35 @@ var init_visitor2 = __esm({
|
|
|
17039
17249
|
return expression ? expression.loc : fallbackLoc;
|
|
17040
17250
|
}
|
|
17041
17251
|
parseTextBlock(token) {
|
|
17042
|
-
let value = token.image.slice(3, -3)
|
|
17252
|
+
let value = this.normalizeTextBlockIndent(token.image.slice(3, -3));
|
|
17043
17253
|
if (this.interpolateEnv) {
|
|
17044
17254
|
value = this.interpolateEnvVars(value);
|
|
17045
17255
|
}
|
|
17046
17256
|
return value;
|
|
17047
17257
|
}
|
|
17258
|
+
/**
|
|
17259
|
+
* Strip the common leading whitespace shared by the non-empty lines of a
|
|
17260
|
+
* text block, then trim. A plain trim() would leave the first line at
|
|
17261
|
+
* column 0 while every following line keeps its source indentation, so
|
|
17262
|
+
* markdown consumers read the block body indent as list nesting. Stripping
|
|
17263
|
+
* the common indent keeps relative (nested) indentation intact.
|
|
17264
|
+
*/
|
|
17265
|
+
normalizeTextBlockIndent(raw) {
|
|
17266
|
+
let minIndent = Infinity;
|
|
17267
|
+
for (const line of raw.split("\n")) {
|
|
17268
|
+
if (line.trim().length === 0) continue;
|
|
17269
|
+
const match2 = /^([ \t]*)/.exec(line);
|
|
17270
|
+
const indent = match2?.[1]?.length ?? 0;
|
|
17271
|
+
if (indent < minIndent) {
|
|
17272
|
+
minIndent = indent;
|
|
17273
|
+
if (minIndent === 0) break;
|
|
17274
|
+
}
|
|
17275
|
+
}
|
|
17276
|
+
if (minIndent === Infinity || minIndent === 0) {
|
|
17277
|
+
return raw.trim();
|
|
17278
|
+
}
|
|
17279
|
+
return raw.split("\n").map((line) => line.slice(minIndent)).join("\n").trim();
|
|
17280
|
+
}
|
|
17048
17281
|
/**
|
|
17049
17282
|
* Parse a string literal, handling escape sequences.
|
|
17050
17283
|
*/
|
|
@@ -18600,6 +18833,52 @@ var init_extractors = __esm({
|
|
|
18600
18833
|
}
|
|
18601
18834
|
});
|
|
18602
18835
|
|
|
18836
|
+
// packages/formatters/src/section-title-resolver.ts
|
|
18837
|
+
function presentationSectionId(blockName, entry) {
|
|
18838
|
+
if (entry.sectionId) return getSectionContract(entry.sectionId)?.id ?? entry.sectionId;
|
|
18839
|
+
return getPrimarySectionForBlock(blockName)?.id;
|
|
18840
|
+
}
|
|
18841
|
+
function sourceTitle(ast, section) {
|
|
18842
|
+
const owners = [section.primaryOwner, ...section.fallbackOwners];
|
|
18843
|
+
for (const source of ["explicit", "legacy"]) {
|
|
18844
|
+
for (const owner of owners) {
|
|
18845
|
+
const candidates = ast.blocks.filter((block) => block.name === owner).flatMap(
|
|
18846
|
+
(block) => (block.canonicalBody?.entries ?? []).filter(
|
|
18847
|
+
(entry) => entry.type === "PresentationEntry" && entry.source === source && presentationSectionId(block.name, entry) === section.id
|
|
18848
|
+
)
|
|
18849
|
+
);
|
|
18850
|
+
const selected = candidates.at(-1);
|
|
18851
|
+
if (selected) return selected.title;
|
|
18852
|
+
}
|
|
18853
|
+
}
|
|
18854
|
+
return void 0;
|
|
18855
|
+
}
|
|
18856
|
+
function resolveSourceSectionTitle(ast, sectionIdOrAlias) {
|
|
18857
|
+
const section = getSectionContract(sectionIdOrAlias);
|
|
18858
|
+
return section ? sourceTitle(ast, section) : void 0;
|
|
18859
|
+
}
|
|
18860
|
+
function configuredTitle(section, titles) {
|
|
18861
|
+
if (!titles) return void 0;
|
|
18862
|
+
for (const key of [section.id, ...section.formatterAliases]) {
|
|
18863
|
+
const title = titles[key];
|
|
18864
|
+
if (title !== void 0) return title;
|
|
18865
|
+
}
|
|
18866
|
+
return void 0;
|
|
18867
|
+
}
|
|
18868
|
+
function resolveSectionTitle(ast, sectionIdOrAlias, options = {}) {
|
|
18869
|
+
const section = getSectionContract(sectionIdOrAlias);
|
|
18870
|
+
if (!section) {
|
|
18871
|
+
return options.defaultTitle ?? sectionIdOrAlias;
|
|
18872
|
+
}
|
|
18873
|
+
return (options.sourceOverrides === false ? void 0 : sourceTitle(ast, section)) ?? configuredTitle(section, options.formatterTitles) ?? options.defaultTitle ?? section.defaultTitle;
|
|
18874
|
+
}
|
|
18875
|
+
var init_section_title_resolver = __esm({
|
|
18876
|
+
"packages/formatters/src/section-title-resolver.ts"() {
|
|
18877
|
+
"use strict";
|
|
18878
|
+
init_src();
|
|
18879
|
+
}
|
|
18880
|
+
});
|
|
18881
|
+
|
|
18603
18882
|
// packages/formatters/src/base-formatter.ts
|
|
18604
18883
|
import { posix } from "path";
|
|
18605
18884
|
var BaseFormatter;
|
|
@@ -18609,6 +18888,7 @@ var init_base_formatter = __esm({
|
|
|
18609
18888
|
init_src();
|
|
18610
18889
|
init_convention_renderer();
|
|
18611
18890
|
init_extractors();
|
|
18891
|
+
init_section_title_resolver();
|
|
18612
18892
|
BaseFormatter = class _BaseFormatter {
|
|
18613
18893
|
/**
|
|
18614
18894
|
* Canonical entry point for legacy implementations.
|
|
@@ -18793,6 +19073,21 @@ var init_base_formatter = __esm({
|
|
|
18793
19073
|
}
|
|
18794
19074
|
return this.valueToString(value).split("\n")[0] ?? fallback;
|
|
18795
19075
|
}
|
|
19076
|
+
/**
|
|
19077
|
+
* Whether the `@context` block's text is consumed by the project/intro
|
|
19078
|
+
* fallback. Must mirror the project() and intro() consumption conditions
|
|
19079
|
+
* exactly, otherwise the context section would either duplicate the text
|
|
19080
|
+
* (predicate too narrow) or drop it entirely (predicate too wide).
|
|
19081
|
+
*/
|
|
19082
|
+
contextTextConsumedByProject(ast) {
|
|
19083
|
+
const identity = this.findBlock(ast, "identity");
|
|
19084
|
+
if (identity) return false;
|
|
19085
|
+
const context = this.findBlock(ast, "context");
|
|
19086
|
+
if (!context) return false;
|
|
19087
|
+
if (resolveSourceSectionTitle(ast, "context")) return false;
|
|
19088
|
+
const projectTitle = resolveSourceSectionTitle(ast, "project");
|
|
19089
|
+
return context.content.type === "MixedContent" || projectTitle !== void 0;
|
|
19090
|
+
}
|
|
18796
19091
|
/**
|
|
18797
19092
|
* `@context` keys that already have a dedicated rendering path. Anything
|
|
18798
19093
|
* outside this set is generic and must be surfaced by the context section.
|
|
@@ -19160,9 +19455,12 @@ var init_base_formatter = __esm({
|
|
|
19160
19455
|
return `${skillBasePath}/${skillName}`;
|
|
19161
19456
|
}
|
|
19162
19457
|
const normalizedOutputDir = this.normalizeOutputDir(outputDir);
|
|
19458
|
+
if (!normalizedOutputDir) {
|
|
19459
|
+
return `${skillBasePath}/${skillName}`;
|
|
19460
|
+
}
|
|
19163
19461
|
if (!configuredBaseDir) {
|
|
19164
|
-
const targetRoot = defaultSkillBasePath.replace(
|
|
19165
|
-
return `${targetRoot}/${normalizedOutputDir}
|
|
19462
|
+
const targetRoot = defaultSkillBasePath.replace(/(?:^|\/)skills$/, "");
|
|
19463
|
+
return targetRoot ? `${targetRoot}/${normalizedOutputDir}` : normalizedOutputDir;
|
|
19166
19464
|
}
|
|
19167
19465
|
const outputSegments = normalizedOutputDir.split("/").filter((segment) => segment.length > 0);
|
|
19168
19466
|
const relativeOutputDir = outputSegments[0] === "skills" ? outputSegments.slice(1).join("/") : outputSegments.join("/");
|
|
@@ -19726,7 +20024,7 @@ function getEnabledHookScriptResources(hook) {
|
|
|
19726
20024
|
}
|
|
19727
20025
|
function mapEvent(event, target) {
|
|
19728
20026
|
if (target === "windsurf") return WINDSURF_EVENT_MAP[event][0] ?? null;
|
|
19729
|
-
const map = target
|
|
20027
|
+
const map = EVENT_MAPS[target] ?? CODEX_EVENT_MAP;
|
|
19730
20028
|
return map[event] || null;
|
|
19731
20029
|
}
|
|
19732
20030
|
function mapEvents(event) {
|
|
@@ -19743,6 +20041,7 @@ function getEffectiveMatcher(hook, target) {
|
|
|
19743
20041
|
function convertTimeout(timeoutMs, target) {
|
|
19744
20042
|
if (target === "gemini") return timeoutMs;
|
|
19745
20043
|
if (target === "windsurf") return timeoutMs;
|
|
20044
|
+
if (target === "opencode") return timeoutMs;
|
|
19746
20045
|
if (target === "claude" || target === "cursor" || target === "codex" || target === "factory" || target === "github" || target === "vscode" || target === "grok")
|
|
19747
20046
|
return Math.ceil(timeoutMs / 1e3);
|
|
19748
20047
|
return timeoutMs;
|
|
@@ -20022,7 +20321,37 @@ function generateCodexHookConfig(hooks) {
|
|
|
20022
20321
|
}
|
|
20023
20322
|
return result;
|
|
20024
20323
|
}
|
|
20025
|
-
|
|
20324
|
+
function buildOpenCodeHookRule(hook) {
|
|
20325
|
+
const nativeEvent = mapEvent(hook.event, "opencode");
|
|
20326
|
+
if (nativeEvent !== "tool.execute.before" && nativeEvent !== "tool.execute.after") return null;
|
|
20327
|
+
const command = hook.command ?? (hook.script ? [
|
|
20328
|
+
...getPosixInterpreter(hook.script.interpreter),
|
|
20329
|
+
getScriptPathFromNativeCwd(hook),
|
|
20330
|
+
...hook.script.args
|
|
20331
|
+
] : []);
|
|
20332
|
+
if (command.length === 0) return null;
|
|
20333
|
+
const rule = {
|
|
20334
|
+
id: hook.id.replace(/[^A-Za-z0-9._-]/g, "-"),
|
|
20335
|
+
event: nativeEvent,
|
|
20336
|
+
...hook.matcher !== void 0 ? { matcher: hook.matcher } : {},
|
|
20337
|
+
command,
|
|
20338
|
+
...hook.cwd !== void 0 && hook.cwd !== "project" ? { cwd: hook.cwd } : {},
|
|
20339
|
+
...hook.timeoutMs !== void 0 ? { timeoutMs: hook.timeoutMs } : {}
|
|
20340
|
+
};
|
|
20341
|
+
return rule;
|
|
20342
|
+
}
|
|
20343
|
+
function generateOpenCodePlugin(hooks) {
|
|
20344
|
+
const rules = [];
|
|
20345
|
+
for (const hook of applyHookTargetOverrides(hooks, "opencode")) {
|
|
20346
|
+
if (hook.enabled === false) continue;
|
|
20347
|
+
const rule = buildOpenCodeHookRule(hook);
|
|
20348
|
+
if (rule) rules.push(rule);
|
|
20349
|
+
}
|
|
20350
|
+
if (rules.length === 0) return null;
|
|
20351
|
+
const serializedRules = rules.map((rule) => JSON.stringify(rule));
|
|
20352
|
+
return "// promptscript-generated: opencode-plugin\n// Generated by PromptScript - do not edit.\n// Recompiling the .promptscript sources rewrites this file; manual edits are discarded.\n// Scope: OpenCode fires these hooks for local tool executions only. MCP tool calls,\n// some subagent paths, and failed tool calls have no dedicated plugin hook.\n// OpenCode tool hooks do not expose model or agent context; payloads omit those fields.\n\ninterface OpenCodeHookRule {\n id: string;\n event: 'tool.execute.before' | 'tool.execute.after';\n matcher?: string;\n command: string[];\n cwd?: string;\n timeoutMs?: number;\n}\n\nconst HOOK_RULES: readonly OpenCodeHookRule[] = [\n" + serializedRules.map((rule) => " " + rule).join(",\n") + "\n];\n" + OPENCODE_PLUGIN_RUNTIME;
|
|
20353
|
+
}
|
|
20354
|
+
var HOOK_OWNERSHIP_MARKER, SHELL_SAFE_ARGUMENT, HOST_CWD_STRATEGIES, CLAUDE_EVENT_MAP, CURSOR_EVENT_MAP, CODEX_EVENT_MAP, FACTORY_EVENT_MAP, GITHUB_EVENT_MAP, GEMINI_EVENT_MAP, GROK_EVENT_MAP, WINDSURF_EVENT_MAP, VSCODE_EVENT_MAP, OPENCODE_EVENT_MAP, EVENT_MAPS, GITHUB_MATCHER_EVENTS, OPENCODE_PLUGIN_PATH, OPENCODE_PLUGIN_RUNTIME;
|
|
20026
20355
|
var init_hook_adapters = __esm({
|
|
20027
20356
|
"packages/formatters/src/hook-adapters.ts"() {
|
|
20028
20357
|
"use strict";
|
|
@@ -20034,6 +20363,7 @@ var init_hook_adapters = __esm({
|
|
|
20034
20363
|
"git-root": false,
|
|
20035
20364
|
"native-cwd": true,
|
|
20036
20365
|
"workspace-cwd": true,
|
|
20366
|
+
"plugin-context": false,
|
|
20037
20367
|
none: false
|
|
20038
20368
|
};
|
|
20039
20369
|
CLAUDE_EVENT_MAP = {
|
|
@@ -20125,12 +20455,199 @@ var init_hook_adapters = __esm({
|
|
|
20125
20455
|
notification: "",
|
|
20126
20456
|
stop: "Stop"
|
|
20127
20457
|
};
|
|
20458
|
+
OPENCODE_EVENT_MAP = {
|
|
20459
|
+
"pre-terminal-command": "",
|
|
20460
|
+
"pre-tool-use": "tool.execute.before",
|
|
20461
|
+
"post-tool-use": "tool.execute.after",
|
|
20462
|
+
"session-start": "",
|
|
20463
|
+
setup: "",
|
|
20464
|
+
"subagent-start": "",
|
|
20465
|
+
notification: "",
|
|
20466
|
+
stop: ""
|
|
20467
|
+
};
|
|
20468
|
+
EVENT_MAPS = {
|
|
20469
|
+
claude: CLAUDE_EVENT_MAP,
|
|
20470
|
+
cursor: CURSOR_EVENT_MAP,
|
|
20471
|
+
factory: FACTORY_EVENT_MAP,
|
|
20472
|
+
github: GITHUB_EVENT_MAP,
|
|
20473
|
+
gemini: GEMINI_EVENT_MAP,
|
|
20474
|
+
grok: GROK_EVENT_MAP,
|
|
20475
|
+
vscode: VSCODE_EVENT_MAP,
|
|
20476
|
+
opencode: OPENCODE_EVENT_MAP
|
|
20477
|
+
};
|
|
20128
20478
|
GITHUB_MATCHER_EVENTS = /* @__PURE__ */ new Set([
|
|
20129
20479
|
"pre-tool-use",
|
|
20130
20480
|
"post-tool-use",
|
|
20131
20481
|
"subagent-start",
|
|
20132
20482
|
"notification"
|
|
20133
20483
|
]);
|
|
20484
|
+
OPENCODE_PLUGIN_PATH = ".opencode/plugins/promptscript.ts";
|
|
20485
|
+
OPENCODE_PLUGIN_RUNTIME = `
|
|
20486
|
+
const PAYLOAD_LIMIT_BYTES = 32768;
|
|
20487
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
20488
|
+
const KILL_GRACE_MS = 1000;
|
|
20489
|
+
|
|
20490
|
+
interface OpenCodePluginContext {
|
|
20491
|
+
directory: string;
|
|
20492
|
+
worktree?: string;
|
|
20493
|
+
}
|
|
20494
|
+
|
|
20495
|
+
interface OpenCodeToolInput {
|
|
20496
|
+
tool?: string;
|
|
20497
|
+
sessionID?: string;
|
|
20498
|
+
callID?: string;
|
|
20499
|
+
args?: unknown;
|
|
20500
|
+
}
|
|
20501
|
+
|
|
20502
|
+
interface OpenCodeToolOutput {
|
|
20503
|
+
args?: unknown;
|
|
20504
|
+
}
|
|
20505
|
+
|
|
20506
|
+
interface OpenCodeToolResult {
|
|
20507
|
+
title?: unknown;
|
|
20508
|
+
output?: unknown;
|
|
20509
|
+
metadata?: unknown;
|
|
20510
|
+
}
|
|
20511
|
+
|
|
20512
|
+
interface OpenCodeCompiledRule {
|
|
20513
|
+
rule: OpenCodeHookRule;
|
|
20514
|
+
matcher: RegExp | null;
|
|
20515
|
+
}
|
|
20516
|
+
|
|
20517
|
+
function safeStringify(value: unknown): string {
|
|
20518
|
+
try {
|
|
20519
|
+
return JSON.stringify(value);
|
|
20520
|
+
} catch {
|
|
20521
|
+
return '{}';
|
|
20522
|
+
}
|
|
20523
|
+
}
|
|
20524
|
+
|
|
20525
|
+
function payloadByteLength(value: string): number {
|
|
20526
|
+
return new TextEncoder().encode(value).byteLength;
|
|
20527
|
+
}
|
|
20528
|
+
|
|
20529
|
+
// Keep the payload bounded: drop oversized tool arguments first, then the
|
|
20530
|
+
// result object, so the command always receives parseable JSON.
|
|
20531
|
+
function boundedPayload(payload: Record<string, unknown>): string {
|
|
20532
|
+
const full = safeStringify(payload);
|
|
20533
|
+
if (payloadByteLength(full) <= PAYLOAD_LIMIT_BYTES) return full;
|
|
20534
|
+
const withoutArgs = { ...payload, args: '[truncated]' };
|
|
20535
|
+
const trimmed = safeStringify(withoutArgs);
|
|
20536
|
+
if (payloadByteLength(trimmed) <= PAYLOAD_LIMIT_BYTES) return trimmed;
|
|
20537
|
+
const withoutResult = safeStringify({ ...withoutArgs, result: '[truncated]' });
|
|
20538
|
+
if (payloadByteLength(withoutResult) <= PAYLOAD_LIMIT_BYTES) return withoutResult;
|
|
20539
|
+
return '{"target":"opencode","args":"[truncated]","result":"[truncated]"}';
|
|
20540
|
+
}
|
|
20541
|
+
|
|
20542
|
+
function buildPayload(
|
|
20543
|
+
rule: OpenCodeHookRule,
|
|
20544
|
+
input: OpenCodeToolInput,
|
|
20545
|
+
args: unknown,
|
|
20546
|
+
result?: unknown
|
|
20547
|
+
): Record<string, unknown> {
|
|
20548
|
+
const payload: Record<string, unknown> = {
|
|
20549
|
+
target: 'opencode',
|
|
20550
|
+
hook: rule.id,
|
|
20551
|
+
event: rule.event === 'tool.execute.before' ? 'pre-tool-use' : 'post-tool-use',
|
|
20552
|
+
tool: input.tool,
|
|
20553
|
+
args,
|
|
20554
|
+
sessionID: input.sessionID,
|
|
20555
|
+
callID: input.callID,
|
|
20556
|
+
timestamp: new Date().toISOString()
|
|
20557
|
+
};
|
|
20558
|
+
if (result !== undefined) payload.result = result;
|
|
20559
|
+
return payload;
|
|
20560
|
+
}
|
|
20561
|
+
|
|
20562
|
+
// Hooks observe tool execution asynchronously. Failures are logged, and every
|
|
20563
|
+
// process gets a bounded lifetime so a broken hook cannot disrupt the session.
|
|
20564
|
+
async function runRule(
|
|
20565
|
+
rule: OpenCodeHookRule,
|
|
20566
|
+
projectRoot: string,
|
|
20567
|
+
payload: string
|
|
20568
|
+
): Promise<void> {
|
|
20569
|
+
try {
|
|
20570
|
+
const cwd = rule.cwd ? projectRoot + '/' + rule.cwd : projectRoot;
|
|
20571
|
+
const proc = Bun.spawn({
|
|
20572
|
+
cmd: rule.command,
|
|
20573
|
+
cwd,
|
|
20574
|
+
stdin: 'pipe',
|
|
20575
|
+
stdout: 'inherit',
|
|
20576
|
+
stderr: 'inherit'
|
|
20577
|
+
});
|
|
20578
|
+
const timeoutMs = rule.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
20579
|
+
let graceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
20580
|
+
const timer = setTimeout(() => {
|
|
20581
|
+
proc.kill('SIGTERM');
|
|
20582
|
+
graceTimer = setTimeout(() => proc.kill('SIGKILL'), KILL_GRACE_MS);
|
|
20583
|
+
}, timeoutMs);
|
|
20584
|
+
try {
|
|
20585
|
+
proc.stdin.write(payload);
|
|
20586
|
+
proc.stdin.end();
|
|
20587
|
+
const exitCode = await proc.exited;
|
|
20588
|
+
if (exitCode !== 0) {
|
|
20589
|
+
console.error(
|
|
20590
|
+
'[promptscript] opencode hook "' + rule.id + '" exited with code ' + exitCode
|
|
20591
|
+
);
|
|
20592
|
+
}
|
|
20593
|
+
} finally {
|
|
20594
|
+
clearTimeout(timer);
|
|
20595
|
+
if (graceTimer !== undefined) clearTimeout(graceTimer);
|
|
20596
|
+
}
|
|
20597
|
+
} catch (error: unknown) {
|
|
20598
|
+
console.error('[promptscript] opencode hook "' + rule.id + '" failed:', error);
|
|
20599
|
+
}
|
|
20600
|
+
}
|
|
20601
|
+
|
|
20602
|
+
export const PromptScriptHooks = async (context: OpenCodePluginContext) => {
|
|
20603
|
+
const projectRoot = context.worktree || context.directory;
|
|
20604
|
+
const compiled: OpenCodeCompiledRule[] = [];
|
|
20605
|
+
for (const rule of HOOK_RULES) {
|
|
20606
|
+
if (rule.matcher === undefined) {
|
|
20607
|
+
compiled.push({ rule, matcher: null });
|
|
20608
|
+
continue;
|
|
20609
|
+
}
|
|
20610
|
+
try {
|
|
20611
|
+
compiled.push({ rule, matcher: new RegExp(rule.matcher) });
|
|
20612
|
+
} catch (error: unknown) {
|
|
20613
|
+
console.error(
|
|
20614
|
+
'[promptscript] opencode hook "' + rule.id + '" has an invalid matcher:',
|
|
20615
|
+
rule.matcher
|
|
20616
|
+
);
|
|
20617
|
+
}
|
|
20618
|
+
}
|
|
20619
|
+
|
|
20620
|
+
return {
|
|
20621
|
+
'tool.execute.before': async (input: OpenCodeToolInput, output: OpenCodeToolOutput) => {
|
|
20622
|
+
for (const entry of compiled) {
|
|
20623
|
+
if (entry.rule.event !== 'tool.execute.before') continue;
|
|
20624
|
+
if (entry.matcher !== null && !entry.matcher.test(String(input.tool))) continue;
|
|
20625
|
+
void runRule(
|
|
20626
|
+
entry.rule,
|
|
20627
|
+
projectRoot,
|
|
20628
|
+
boundedPayload(buildPayload(entry.rule, input, output.args))
|
|
20629
|
+
);
|
|
20630
|
+
}
|
|
20631
|
+
},
|
|
20632
|
+
'tool.execute.after': async (input: OpenCodeToolInput, output: OpenCodeToolResult) => {
|
|
20633
|
+
for (const entry of compiled) {
|
|
20634
|
+
if (entry.rule.event !== 'tool.execute.after') continue;
|
|
20635
|
+
if (entry.matcher !== null && !entry.matcher.test(String(input.tool))) continue;
|
|
20636
|
+
const result = {
|
|
20637
|
+
title: output.title,
|
|
20638
|
+
output: output.output,
|
|
20639
|
+
metadata: output.metadata
|
|
20640
|
+
};
|
|
20641
|
+
void runRule(
|
|
20642
|
+
entry.rule,
|
|
20643
|
+
projectRoot,
|
|
20644
|
+
boundedPayload(buildPayload(entry.rule, input, input.args, result))
|
|
20645
|
+
);
|
|
20646
|
+
}
|
|
20647
|
+
}
|
|
20648
|
+
};
|
|
20649
|
+
};
|
|
20650
|
+
`;
|
|
20134
20651
|
}
|
|
20135
20652
|
});
|
|
20136
20653
|
|
|
@@ -20181,6 +20698,108 @@ var init_hook_capability_warnings = __esm({
|
|
|
20181
20698
|
}
|
|
20182
20699
|
});
|
|
20183
20700
|
|
|
20701
|
+
// packages/formatters/src/agent-capability-warnings.ts
|
|
20702
|
+
function collectAgentEntries(ast) {
|
|
20703
|
+
const agentsBlock = ast.blocks.find((block) => block.name === "agents");
|
|
20704
|
+
if (!agentsBlock) return null;
|
|
20705
|
+
const entries = [];
|
|
20706
|
+
if (agentsBlock.content.type === "ObjectContent" || agentsBlock.content.type === "MixedContent") {
|
|
20707
|
+
for (const [name, value] of Object.entries(agentsBlock.content.properties)) {
|
|
20708
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
20709
|
+
const fields = Object.fromEntries(
|
|
20710
|
+
Object.entries(value).filter(([key]) => !key.startsWith("__"))
|
|
20711
|
+
);
|
|
20712
|
+
entries.push({ name, fields });
|
|
20713
|
+
}
|
|
20714
|
+
}
|
|
20715
|
+
}
|
|
20716
|
+
return { location: agentsBlock.loc, entries };
|
|
20717
|
+
}
|
|
20718
|
+
function describeFieldSupport(field) {
|
|
20719
|
+
const support = listAgentFieldSupportTargets(field);
|
|
20720
|
+
const parts = [];
|
|
20721
|
+
if (support.emitted.length > 0) {
|
|
20722
|
+
parts.push(`emitted by ${support.emitted.join(", ")}`);
|
|
20723
|
+
}
|
|
20724
|
+
if (support.transformed.length > 0) {
|
|
20725
|
+
parts.push(`transformed by ${support.transformed.join(", ")}`);
|
|
20726
|
+
}
|
|
20727
|
+
return parts.length > 0 ? `Field support: ${parts.join("; ")}.` : "";
|
|
20728
|
+
}
|
|
20729
|
+
function describeBlockOmission(agentsResource, target, version, location, options) {
|
|
20730
|
+
if (!agentsResource) {
|
|
20731
|
+
if (options?.blockWarningHandled) return [];
|
|
20732
|
+
return [
|
|
20733
|
+
{
|
|
20734
|
+
code: AGENT_COMPATIBILITY_CODE,
|
|
20735
|
+
ruleName: "agent-compatibility",
|
|
20736
|
+
message: `Target "${target}" has no native agent output and will omit @agents.`,
|
|
20737
|
+
suggestion: "Compile a target with agent support or move agent guidance into instruction blocks.",
|
|
20738
|
+
location
|
|
20739
|
+
}
|
|
20740
|
+
];
|
|
20741
|
+
}
|
|
20742
|
+
return [
|
|
20743
|
+
{
|
|
20744
|
+
code: AGENT_COMPATIBILITY_CODE,
|
|
20745
|
+
ruleName: "agent-compatibility",
|
|
20746
|
+
message: `Target "${target}" version "${version}" cannot emit @agents and will omit it.`,
|
|
20747
|
+
suggestion: `Use a version that emits agents: ${agentsResource.versions.join(", ")}.`,
|
|
20748
|
+
location
|
|
20749
|
+
}
|
|
20750
|
+
];
|
|
20751
|
+
}
|
|
20752
|
+
function describeFieldLoss(agentName, field, target, location) {
|
|
20753
|
+
if (field === "description" || field === "content") return void 0;
|
|
20754
|
+
const isCanonical = CANONICAL_AGENT_FIELDS.includes(field);
|
|
20755
|
+
const status = isCanonical ? getAgentFieldStatus(target, field) : "not-supported";
|
|
20756
|
+
if (status !== "not-supported") return void 0;
|
|
20757
|
+
return {
|
|
20758
|
+
code: AGENT_COMPATIBILITY_CODE,
|
|
20759
|
+
ruleName: "agent-compatibility",
|
|
20760
|
+
message: `Agent "${agentName}": field "${field}" is not supported by target "${target}" and will be omitted.`,
|
|
20761
|
+
suggestion: isCanonical ? describeFieldSupport(field) : "Not a canonical @agents field; remove it or model it through a supported block.",
|
|
20762
|
+
location
|
|
20763
|
+
};
|
|
20764
|
+
}
|
|
20765
|
+
function collectEntryFieldWarnings(entry, target, location) {
|
|
20766
|
+
const warnings = [];
|
|
20767
|
+
for (const field of Object.keys(entry.fields)) {
|
|
20768
|
+
const warning = describeFieldLoss(entry.name, field, target, location);
|
|
20769
|
+
if (warning) warnings.push(warning);
|
|
20770
|
+
}
|
|
20771
|
+
return warnings;
|
|
20772
|
+
}
|
|
20773
|
+
function getAgentCapabilityWarnings(ast, target, version, options) {
|
|
20774
|
+
const agentsBlock = collectAgentEntries(ast);
|
|
20775
|
+
if (!agentsBlock || !isKnownTarget(target)) return [];
|
|
20776
|
+
const agentsResource = TARGET_CAPABILITIES[target].resources.find(
|
|
20777
|
+
(resource) => resource.kind === "agents"
|
|
20778
|
+
);
|
|
20779
|
+
if (!agentsResource?.versions.includes(version)) {
|
|
20780
|
+
return describeBlockOmission(agentsResource, target, version, agentsBlock.location, options);
|
|
20781
|
+
}
|
|
20782
|
+
return agentsBlock.entries.flatMap(
|
|
20783
|
+
(entry) => collectEntryFieldWarnings(entry, target, agentsBlock.location)
|
|
20784
|
+
);
|
|
20785
|
+
}
|
|
20786
|
+
function appendAgentCapabilityWarnings(output, ast, target, version, options) {
|
|
20787
|
+
const warnings = getAgentCapabilityWarnings(ast, target, version, options);
|
|
20788
|
+
if (warnings.length === 0) return output;
|
|
20789
|
+
return {
|
|
20790
|
+
...output,
|
|
20791
|
+
warnings: [...output.warnings ?? [], ...warnings]
|
|
20792
|
+
};
|
|
20793
|
+
}
|
|
20794
|
+
var AGENT_COMPATIBILITY_CODE;
|
|
20795
|
+
var init_agent_capability_warnings = __esm({
|
|
20796
|
+
"packages/formatters/src/agent-capability-warnings.ts"() {
|
|
20797
|
+
"use strict";
|
|
20798
|
+
init_src();
|
|
20799
|
+
AGENT_COMPATIBILITY_CODE = "PS4003";
|
|
20800
|
+
}
|
|
20801
|
+
});
|
|
20802
|
+
|
|
20184
20803
|
// packages/formatters/src/mcp-helpers.ts
|
|
20185
20804
|
function findMcpServersBlock(ast) {
|
|
20186
20805
|
return ast.blocks.find((b) => b.name === "mcpServers");
|
|
@@ -20356,52 +20975,6 @@ var init_mcp_helpers = __esm({
|
|
|
20356
20975
|
}
|
|
20357
20976
|
});
|
|
20358
20977
|
|
|
20359
|
-
// packages/formatters/src/section-title-resolver.ts
|
|
20360
|
-
function presentationSectionId(blockName, entry) {
|
|
20361
|
-
if (entry.sectionId) return getSectionContract(entry.sectionId)?.id ?? entry.sectionId;
|
|
20362
|
-
return getPrimarySectionForBlock(blockName)?.id;
|
|
20363
|
-
}
|
|
20364
|
-
function sourceTitle(ast, section) {
|
|
20365
|
-
const owners = [section.primaryOwner, ...section.fallbackOwners];
|
|
20366
|
-
for (const source of ["explicit", "legacy"]) {
|
|
20367
|
-
for (const owner of owners) {
|
|
20368
|
-
const candidates = ast.blocks.filter((block) => block.name === owner).flatMap(
|
|
20369
|
-
(block) => (block.canonicalBody?.entries ?? []).filter(
|
|
20370
|
-
(entry) => entry.type === "PresentationEntry" && entry.source === source && presentationSectionId(block.name, entry) === section.id
|
|
20371
|
-
)
|
|
20372
|
-
);
|
|
20373
|
-
const selected = candidates.at(-1);
|
|
20374
|
-
if (selected) return selected.title;
|
|
20375
|
-
}
|
|
20376
|
-
}
|
|
20377
|
-
return void 0;
|
|
20378
|
-
}
|
|
20379
|
-
function resolveSourceSectionTitle(ast, sectionIdOrAlias) {
|
|
20380
|
-
const section = getSectionContract(sectionIdOrAlias);
|
|
20381
|
-
return section ? sourceTitle(ast, section) : void 0;
|
|
20382
|
-
}
|
|
20383
|
-
function configuredTitle(section, titles) {
|
|
20384
|
-
if (!titles) return void 0;
|
|
20385
|
-
for (const key of [section.id, ...section.formatterAliases]) {
|
|
20386
|
-
const title = titles[key];
|
|
20387
|
-
if (title !== void 0) return title;
|
|
20388
|
-
}
|
|
20389
|
-
return void 0;
|
|
20390
|
-
}
|
|
20391
|
-
function resolveSectionTitle(ast, sectionIdOrAlias, options = {}) {
|
|
20392
|
-
const section = getSectionContract(sectionIdOrAlias);
|
|
20393
|
-
if (!section) {
|
|
20394
|
-
return options.defaultTitle ?? sectionIdOrAlias;
|
|
20395
|
-
}
|
|
20396
|
-
return (options.sourceOverrides === false ? void 0 : sourceTitle(ast, section)) ?? configuredTitle(section, options.formatterTitles) ?? options.defaultTitle ?? section.defaultTitle;
|
|
20397
|
-
}
|
|
20398
|
-
var init_section_title_resolver = __esm({
|
|
20399
|
-
"packages/formatters/src/section-title-resolver.ts"() {
|
|
20400
|
-
"use strict";
|
|
20401
|
-
init_src();
|
|
20402
|
-
}
|
|
20403
|
-
});
|
|
20404
|
-
|
|
20405
20978
|
// packages/formatters/src/markdown-instruction-formatter.ts
|
|
20406
20979
|
var MarkdownInstructionFormatter;
|
|
20407
20980
|
var init_markdown_instruction_formatter = __esm({
|
|
@@ -20410,6 +20983,7 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
20410
20983
|
init_base_formatter();
|
|
20411
20984
|
init_hook_adapters();
|
|
20412
20985
|
init_hook_capability_warnings();
|
|
20986
|
+
init_agent_capability_warnings();
|
|
20413
20987
|
init_mcp_helpers();
|
|
20414
20988
|
init_section_title_resolver();
|
|
20415
20989
|
MarkdownInstructionFormatter = class extends BaseFormatter {
|
|
@@ -20428,7 +21002,7 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
20428
21002
|
}
|
|
20429
21003
|
getSkillBasePath() {
|
|
20430
21004
|
if (!this.config.hasSkills) return null;
|
|
20431
|
-
return `${this.config.dotDir}/skills`;
|
|
21005
|
+
return this.config.skillsDir ?? `${this.config.dotDir}/skills`;
|
|
20432
21006
|
}
|
|
20433
21007
|
getSkillFileName() {
|
|
20434
21008
|
if (!this.config.hasSkills) return null;
|
|
@@ -20454,7 +21028,10 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
20454
21028
|
hookWarnings.length > 0 || !hasEnabledHooks ? /* @__PURE__ */ new Set(["hooks"]) : void 0
|
|
20455
21029
|
);
|
|
20456
21030
|
const warnedOutput = unsupportedWarnings.length > 0 ? { ...output, warnings: [...output.warnings ?? [], ...unsupportedWarnings] } : output;
|
|
20457
|
-
|
|
21031
|
+
const agentOutput = appendAgentCapabilityWarnings(warnedOutput, ast, this.name, version, {
|
|
21032
|
+
blockWarningHandled: (this.config.unsupportedBlocks ?? []).includes("agents")
|
|
21033
|
+
});
|
|
21034
|
+
return appendTargetHookCapabilityWarnings(agentOutput, ast, this.name, version);
|
|
20458
21035
|
}
|
|
20459
21036
|
hasEnabledHooks(ast) {
|
|
20460
21037
|
const hooksBlock = ast.blocks.find((block) => block.name === "hooks");
|
|
@@ -20889,17 +21466,16 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
20889
21466
|
context(ast, renderer) {
|
|
20890
21467
|
const contextBlock = this.findBlock(ast, "context");
|
|
20891
21468
|
if (!contextBlock) return null;
|
|
20892
|
-
const
|
|
20893
|
-
const textIsConsumedByProject = !identity && !resolveSourceSectionTitle(ast, "context");
|
|
21469
|
+
const textIsConsumedByProject = this.contextTextConsumedByProject(ast);
|
|
20894
21470
|
const propertyItems = this.contextPropertyItems(ast);
|
|
20895
21471
|
let body = "";
|
|
20896
21472
|
if (!textIsConsumedByProject) {
|
|
20897
21473
|
const text = this.extractText(contextBlock.content);
|
|
20898
21474
|
const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
|
|
20899
21475
|
const strippedText = archMatch ? text.replace(archMatch, "") : text;
|
|
20900
|
-
const remainingText =
|
|
21476
|
+
const remainingText = strippedText.trim();
|
|
20901
21477
|
if (remainingText) {
|
|
20902
|
-
const downgradedText = remainingText.replace(/^(\
|
|
21478
|
+
const downgradedText = remainingText.replace(/^([ \t]*)## /gm, "$1### ");
|
|
20903
21479
|
body = this.normalizeMarkdownForPrettier(downgradedText).trim();
|
|
20904
21480
|
}
|
|
20905
21481
|
}
|
|
@@ -21114,24 +21690,28 @@ ${list}` : list;
|
|
|
21114
21690
|
});
|
|
21115
21691
|
|
|
21116
21692
|
// packages/formatters/src/create-simple-formatter.ts
|
|
21117
|
-
function buildVersions(
|
|
21693
|
+
function buildVersions(inputs) {
|
|
21694
|
+
const { outputPath, dotDir, hasSkills, hasAgents, hasCommands } = inputs;
|
|
21695
|
+
const { skillFileName, skillsInMultifile, mcpConfigPath, skillsDir } = inputs;
|
|
21118
21696
|
const isNested = outputPath.startsWith(dotDir + "/");
|
|
21119
21697
|
const simpleDesc = `Single ${outputPath} file`;
|
|
21120
|
-
const
|
|
21698
|
+
const defaultSkillsDir = `${dotDir}/skills`;
|
|
21699
|
+
const resolvedSkillsDir = skillsDir ?? defaultSkillsDir;
|
|
21700
|
+
const skillPath = `${resolvedSkillsDir}/<name>/${skillFileName}`;
|
|
21121
21701
|
const commandPath = `${dotDir}/commands/<name>.md`;
|
|
21122
21702
|
const agentPath = `${dotDir}/agents/<name>.md`;
|
|
21123
|
-
const
|
|
21124
|
-
|
|
21703
|
+
const skillsFullModeOnly = hasSkills && !skillsInMultifile;
|
|
21704
|
+
const describeAdditionalFiles = (version, paths, fullModeSkillsOnly) => {
|
|
21705
|
+
if (paths.length === 0 && fullModeSkillsOnly) {
|
|
21125
21706
|
return `Single ${outputPath} file (skills via full mode)`;
|
|
21126
21707
|
}
|
|
21127
21708
|
if (paths.length === 0) return `Single ${outputPath} file`;
|
|
21128
21709
|
const prefix = !isNested && version === "full" && hasSkills ? "Multifile" : outputPath;
|
|
21129
|
-
const suffix =
|
|
21710
|
+
const suffix = fullModeSkillsOnly ? " (skills via full mode)" : "";
|
|
21130
21711
|
return `${prefix} + ${paths.join(" + ")}${suffix}`;
|
|
21131
21712
|
};
|
|
21132
21713
|
const multifilePaths = [];
|
|
21133
|
-
|
|
21134
|
-
if (hasSkills && !skillsInFullMode) multifilePaths.push(skillPath);
|
|
21714
|
+
if (hasSkills && skillsInMultifile) multifilePaths.push(skillPath);
|
|
21135
21715
|
if (hasCommands) multifilePaths.push(commandPath);
|
|
21136
21716
|
if (mcpConfigPath) multifilePaths.push(mcpConfigPath);
|
|
21137
21717
|
const fullPaths = [];
|
|
@@ -21139,7 +21719,7 @@ function buildVersions(outputPath, dotDir, hasSkills, hasAgents, hasCommands, sk
|
|
|
21139
21719
|
if (hasCommands) fullPaths.push(commandPath);
|
|
21140
21720
|
if (hasAgents) fullPaths.push(agentPath);
|
|
21141
21721
|
if (mcpConfigPath) fullPaths.push(mcpConfigPath);
|
|
21142
|
-
const multifileDesc = describeAdditionalFiles("multifile", multifilePaths,
|
|
21722
|
+
const multifileDesc = describeAdditionalFiles("multifile", multifilePaths, skillsFullModeOnly);
|
|
21143
21723
|
const fullDesc = describeAdditionalFiles("full", fullPaths, false);
|
|
21144
21724
|
return {
|
|
21145
21725
|
simple: { name: "simple", description: simpleDesc, outputPath },
|
|
@@ -21157,20 +21737,24 @@ function createSimpleMarkdownFormatter(opts) {
|
|
|
21157
21737
|
hasAgents = false,
|
|
21158
21738
|
hasCommands = false,
|
|
21159
21739
|
hasSkills = true,
|
|
21740
|
+
skillsInMultifile = false,
|
|
21160
21741
|
skillFileName = "SKILL.md",
|
|
21161
21742
|
mcpConfigPath,
|
|
21162
21743
|
mcpConfigFormat,
|
|
21163
|
-
unsupportedBlocks
|
|
21744
|
+
unsupportedBlocks,
|
|
21745
|
+
skillsDir
|
|
21164
21746
|
} = opts;
|
|
21165
|
-
const versions = buildVersions(
|
|
21747
|
+
const versions = buildVersions({
|
|
21166
21748
|
outputPath,
|
|
21167
21749
|
dotDir,
|
|
21168
21750
|
hasSkills,
|
|
21169
21751
|
hasAgents,
|
|
21170
21752
|
hasCommands,
|
|
21753
|
+
skillsInMultifile,
|
|
21171
21754
|
skillFileName,
|
|
21172
|
-
mcpConfigPath
|
|
21173
|
-
|
|
21755
|
+
...mcpConfigPath ? { mcpConfigPath } : {},
|
|
21756
|
+
...skillsDir ? { skillsDir } : {}
|
|
21757
|
+
});
|
|
21174
21758
|
class SimpleFormatter extends MarkdownInstructionFormatter {
|
|
21175
21759
|
constructor() {
|
|
21176
21760
|
super({
|
|
@@ -21184,6 +21768,8 @@ function createSimpleMarkdownFormatter(opts) {
|
|
|
21184
21768
|
hasAgents,
|
|
21185
21769
|
hasCommands,
|
|
21186
21770
|
hasSkills,
|
|
21771
|
+
skillsInMultifile,
|
|
21772
|
+
...skillsDir ? { skillsDir } : {},
|
|
21187
21773
|
...mcpConfigPath ? { mcpConfigPath } : {},
|
|
21188
21774
|
...mcpConfigFormat ? { mcpConfigFormat } : {},
|
|
21189
21775
|
...unsupportedBlocks ? { unsupportedBlocks } : {}
|
|
@@ -21703,6 +22289,7 @@ var init_github2 = __esm({
|
|
|
21703
22289
|
init_mcp_helpers();
|
|
21704
22290
|
init_hook_adapters();
|
|
21705
22291
|
init_hook_capability_warnings();
|
|
22292
|
+
init_agent_capability_warnings();
|
|
21706
22293
|
init_section_title_resolver();
|
|
21707
22294
|
GITHUB_VERSIONS = {
|
|
21708
22295
|
simple: {
|
|
@@ -21797,6 +22384,7 @@ var init_github2 = __esm({
|
|
|
21797
22384
|
output = this.formatSimple(ast, options);
|
|
21798
22385
|
}
|
|
21799
22386
|
output = appendTargetHookCapabilityWarnings(output, ast, this.name, version);
|
|
22387
|
+
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
21800
22388
|
const hooksBlock = ast.blocks.find((block) => block.name === "hooks");
|
|
21801
22389
|
const hooks = hooksBlock ? extractHooks(hooksBlock) : [];
|
|
21802
22390
|
const hookWarnings = hooksBlock && version !== "simple" ? [
|
|
@@ -22573,8 +23161,7 @@ ${f.content}`
|
|
|
22573
23161
|
contextSection(ast, renderer) {
|
|
22574
23162
|
const contextBlock = this.findBlock(ast, "context");
|
|
22575
23163
|
if (!contextBlock) return null;
|
|
22576
|
-
const
|
|
22577
|
-
const textIsConsumedByProject = !identity && !resolveSourceSectionTitle(ast, "context");
|
|
23164
|
+
const textIsConsumedByProject = this.contextTextConsumedByProject(ast);
|
|
22578
23165
|
const propertyItems = this.contextPropertyItems(ast);
|
|
22579
23166
|
let body = "";
|
|
22580
23167
|
if (!textIsConsumedByProject) {
|
|
@@ -22582,7 +23169,7 @@ ${f.content}`
|
|
|
22582
23169
|
const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
|
|
22583
23170
|
const remainingText = archMatch ? text.replace(archMatch, "").trim() : text.trim();
|
|
22584
23171
|
if (remainingText) {
|
|
22585
|
-
const downgradedText = remainingText.replace(/^(\
|
|
23172
|
+
const downgradedText = remainingText.replace(/^([ \t]*)## /gm, "$1### ");
|
|
22586
23173
|
body = this.stripAllIndent(downgradedText);
|
|
22587
23174
|
}
|
|
22588
23175
|
}
|
|
@@ -22868,6 +23455,7 @@ var init_claude2 = __esm({
|
|
|
22868
23455
|
init_base_formatter();
|
|
22869
23456
|
init_hook_adapters();
|
|
22870
23457
|
init_hook_capability_warnings();
|
|
23458
|
+
init_agent_capability_warnings();
|
|
22871
23459
|
init_mcp_helpers();
|
|
22872
23460
|
init_section_title_resolver();
|
|
22873
23461
|
CLAUDE_VERSIONS = {
|
|
@@ -22918,6 +23506,7 @@ var init_claude2 = __esm({
|
|
|
22918
23506
|
output = this.formatSimple(ast, options);
|
|
22919
23507
|
}
|
|
22920
23508
|
output = appendTargetHookCapabilityWarnings(output, ast, this.name, version);
|
|
23509
|
+
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
22921
23510
|
output = {
|
|
22922
23511
|
...output,
|
|
22923
23512
|
managedOutputFiles: [
|
|
@@ -23686,8 +24275,7 @@ ${f.content}`
|
|
|
23686
24275
|
contextSection(ast, renderer) {
|
|
23687
24276
|
const contextBlock = this.findBlock(ast, "context");
|
|
23688
24277
|
if (!contextBlock) return null;
|
|
23689
|
-
const
|
|
23690
|
-
const textIsConsumedByProject = !identity && !resolveSourceSectionTitle(ast, "context");
|
|
24278
|
+
const textIsConsumedByProject = this.contextTextConsumedByProject(ast);
|
|
23691
24279
|
const propertyItems = this.contextPropertyItems(ast);
|
|
23692
24280
|
let body = "";
|
|
23693
24281
|
if (!textIsConsumedByProject) {
|
|
@@ -23695,7 +24283,7 @@ ${f.content}`
|
|
|
23695
24283
|
const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
|
|
23696
24284
|
const remainingText = archMatch ? text.replace(archMatch, "").trim() : text.trim();
|
|
23697
24285
|
if (remainingText) {
|
|
23698
|
-
const downgradedText = remainingText.replace(/^(\
|
|
24286
|
+
const downgradedText = remainingText.replace(/^([ \t]*)## /gm, "$1### ");
|
|
23699
24287
|
body = this.normalizeMarkdownForPrettier(downgradedText).trim();
|
|
23700
24288
|
}
|
|
23701
24289
|
}
|
|
@@ -23964,6 +24552,7 @@ var init_cursor2 = __esm({
|
|
|
23964
24552
|
init_glob_categorizer();
|
|
23965
24553
|
init_hook_adapters();
|
|
23966
24554
|
init_hook_capability_warnings();
|
|
24555
|
+
init_agent_capability_warnings();
|
|
23967
24556
|
init_mcp_helpers();
|
|
23968
24557
|
init_plugin_helpers();
|
|
23969
24558
|
init_section_title_resolver();
|
|
@@ -24052,10 +24641,11 @@ var init_cursor2 = __esm({
|
|
|
24052
24641
|
const hooks = hooksBlock ? extractHooks(hooksBlock) : [];
|
|
24053
24642
|
const effectiveHooks = applyHookTargetOverrides(hooks, "cursor");
|
|
24054
24643
|
if (hooksBlock && effectiveHooks.some((hook) => hook.enabled !== false)) {
|
|
24644
|
+
const withAgentWarnings = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
24055
24645
|
return {
|
|
24056
|
-
...
|
|
24646
|
+
...withAgentWarnings,
|
|
24057
24647
|
warnings: [
|
|
24058
|
-
...
|
|
24648
|
+
...withAgentWarnings.warnings ?? [],
|
|
24059
24649
|
{
|
|
24060
24650
|
code: "PS4002",
|
|
24061
24651
|
message: `Cursor ${version} mode cannot emit @hooks and will omit them.`,
|
|
@@ -24070,6 +24660,7 @@ var init_cursor2 = __esm({
|
|
|
24070
24660
|
}
|
|
24071
24661
|
}
|
|
24072
24662
|
output = appendTargetHookCapabilityWarnings(output, ast, this.name, version);
|
|
24663
|
+
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
24073
24664
|
return {
|
|
24074
24665
|
...output,
|
|
24075
24666
|
managedOutputFiles: [
|
|
@@ -24591,10 +25182,14 @@ ${text2.trim()}`;
|
|
|
24591
25182
|
}
|
|
24592
25183
|
}
|
|
24593
25184
|
const text = this.extractText(context.content);
|
|
24594
|
-
|
|
24595
|
-
|
|
24596
|
-
|
|
24597
|
-
|
|
25185
|
+
const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
|
|
25186
|
+
if (!archMatch) return null;
|
|
25187
|
+
const content = archMatch.replace("## Architecture", "");
|
|
25188
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(content);
|
|
25189
|
+
const title = resolveSectionTitle(ast, "architecture", { defaultTitle: "Architecture" });
|
|
25190
|
+
return `## ${title}
|
|
25191
|
+
|
|
25192
|
+
${normalizedContent.trim()}`;
|
|
24598
25193
|
}
|
|
24599
25194
|
contextSection(ast) {
|
|
24600
25195
|
const context = this.findBlock(ast, "context");
|
|
@@ -24606,13 +25201,13 @@ ${text2.trim()}`;
|
|
|
24606
25201
|
consumedByIntro ? ["tech-stack", "project"] : ["tech-stack"]
|
|
24607
25202
|
);
|
|
24608
25203
|
let body = "";
|
|
24609
|
-
if (
|
|
25204
|
+
if (!this.contextTextConsumedByProject(ast)) {
|
|
24610
25205
|
const text = this.extractText(context.content);
|
|
24611
25206
|
const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
|
|
24612
25207
|
const remainingText = archMatch ? text.replace(archMatch, "").trim() : text.trim();
|
|
24613
25208
|
if (remainingText) {
|
|
24614
|
-
const downgradedText = remainingText.replace(/^(\
|
|
24615
|
-
body =
|
|
25209
|
+
const downgradedText = remainingText.replace(/^([ \t]*)## /gm, "$1### ");
|
|
25210
|
+
body = downgradedText.trim();
|
|
24616
25211
|
}
|
|
24617
25212
|
}
|
|
24618
25213
|
if (propertyItems.length > 0) {
|
|
@@ -25480,8 +26075,7 @@ ${this.stripAllIndent(content)}`;
|
|
|
25480
26075
|
contextSection(ast, _renderer) {
|
|
25481
26076
|
const contextBlock = this.findBlock(ast, "context");
|
|
25482
26077
|
if (!contextBlock) return null;
|
|
25483
|
-
const
|
|
25484
|
-
const textIsConsumedByProject = !identity && !resolveSourceSectionTitle(ast, "context");
|
|
26078
|
+
const textIsConsumedByProject = this.contextTextConsumedByProject(ast);
|
|
25485
26079
|
const propertyItems = this.contextPropertyItems(ast, ["frameworks"]);
|
|
25486
26080
|
let body = "";
|
|
25487
26081
|
if (!textIsConsumedByProject) {
|
|
@@ -25489,7 +26083,7 @@ ${this.stripAllIndent(content)}`;
|
|
|
25489
26083
|
const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
|
|
25490
26084
|
const remainingText = archMatch ? text.replace(archMatch, "").trim() : text.trim();
|
|
25491
26085
|
if (remainingText) {
|
|
25492
|
-
const downgradedText = remainingText.replace(/^(\
|
|
26086
|
+
const downgradedText = remainingText.replace(/^([ \t]*)## /gm, "$1### ");
|
|
25493
26087
|
body = this.stripAllIndent(downgradedText);
|
|
25494
26088
|
}
|
|
25495
26089
|
}
|
|
@@ -26721,20 +27315,115 @@ ${filteredFrontmatter}
|
|
|
26721
27315
|
});
|
|
26722
27316
|
|
|
26723
27317
|
// packages/formatters/src/formatters/opencode.ts
|
|
26724
|
-
var
|
|
27318
|
+
var OPENCODE_VERSIONS, OpenCodeFormatter;
|
|
26725
27319
|
var init_opencode = __esm({
|
|
26726
27320
|
"packages/formatters/src/formatters/opencode.ts"() {
|
|
26727
27321
|
"use strict";
|
|
26728
|
-
|
|
26729
|
-
(
|
|
26730
|
-
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
27322
|
+
init_hook_adapters();
|
|
27323
|
+
init_markdown_instruction_formatter();
|
|
27324
|
+
OPENCODE_VERSIONS = {
|
|
27325
|
+
simple: {
|
|
27326
|
+
name: "simple",
|
|
27327
|
+
description: "Single OPENCODE.md file",
|
|
27328
|
+
outputPath: "OPENCODE.md"
|
|
27329
|
+
},
|
|
27330
|
+
multifile: {
|
|
27331
|
+
name: "multifile",
|
|
27332
|
+
description: "OPENCODE.md + .opencode/commands/<name>.md + .opencode/plugins/promptscript.ts (skills via full mode)",
|
|
27333
|
+
outputPath: "OPENCODE.md"
|
|
27334
|
+
},
|
|
27335
|
+
full: {
|
|
27336
|
+
name: "full",
|
|
27337
|
+
description: "Multifile + .opencode/skills/<name>/SKILL.md + .opencode/commands/<name>.md + .opencode/agents/<name>.md + .opencode/plugins/promptscript.ts",
|
|
27338
|
+
outputPath: "OPENCODE.md"
|
|
27339
|
+
}
|
|
27340
|
+
};
|
|
27341
|
+
OpenCodeFormatter = class extends MarkdownInstructionFormatter {
|
|
27342
|
+
constructor() {
|
|
27343
|
+
super({
|
|
27344
|
+
name: "opencode",
|
|
27345
|
+
outputPath: "OPENCODE.md",
|
|
27346
|
+
description: "OpenCode instructions (Markdown)",
|
|
27347
|
+
defaultConvention: "markdown",
|
|
27348
|
+
mainFileHeader: "# OPENCODE.md",
|
|
27349
|
+
dotDir: ".opencode",
|
|
27350
|
+
skillFileName: "SKILL.md",
|
|
27351
|
+
hasAgents: true,
|
|
27352
|
+
hasCommands: true,
|
|
27353
|
+
hasSkills: true
|
|
27354
|
+
});
|
|
27355
|
+
}
|
|
27356
|
+
/**
|
|
27357
|
+
* Return supported OpenCode output modes.
|
|
27358
|
+
*/
|
|
27359
|
+
static getSupportedVersions() {
|
|
27360
|
+
return OPENCODE_VERSIONS;
|
|
27361
|
+
}
|
|
27362
|
+
/**
|
|
27363
|
+
* Format single-file output while retaining stale-plugin cleanup metadata.
|
|
27364
|
+
*/
|
|
27365
|
+
formatSimple(ast, options) {
|
|
27366
|
+
const output = super.formatSimple(ast, options);
|
|
27367
|
+
return {
|
|
27368
|
+
...output,
|
|
27369
|
+
managedOutputFiles: [
|
|
27370
|
+
.../* @__PURE__ */ new Set([...output.managedOutputFiles ?? [], OPENCODE_PLUGIN_PATH])
|
|
27371
|
+
]
|
|
27372
|
+
};
|
|
27373
|
+
}
|
|
27374
|
+
/**
|
|
27375
|
+
* Format multifile output and attach the generated hook plugin.
|
|
27376
|
+
*/
|
|
27377
|
+
formatMultifile(ast, options) {
|
|
27378
|
+
return this.appendHookPlugin(ast, super.formatMultifile(ast, options));
|
|
27379
|
+
}
|
|
27380
|
+
/**
|
|
27381
|
+
* Format full output and attach the generated hook plugin.
|
|
27382
|
+
*/
|
|
27383
|
+
formatFull(ast, options) {
|
|
27384
|
+
return this.appendHookPlugin(ast, super.formatFull(ast, options));
|
|
27385
|
+
}
|
|
27386
|
+
/**
|
|
27387
|
+
* Attach the generated OpenCode plugin and its compatibility warnings.
|
|
27388
|
+
*
|
|
27389
|
+
* The plugin path is always registered as managed output so recompiles and
|
|
27390
|
+
* version downgrades remove only PromptScript-owned files; user plugins in
|
|
27391
|
+
* the same directory are never touched.
|
|
27392
|
+
*/
|
|
27393
|
+
appendHookPlugin(ast, output) {
|
|
27394
|
+
const managedFiles = [.../* @__PURE__ */ new Set([...output.managedOutputFiles ?? [], OPENCODE_PLUGIN_PATH])];
|
|
27395
|
+
const hooksBlock = ast.blocks.find((block) => block.name === "hooks");
|
|
27396
|
+
if (!hooksBlock) {
|
|
27397
|
+
return { ...output, managedOutputFiles: managedFiles };
|
|
27398
|
+
}
|
|
27399
|
+
const hooks = extractHooks(hooksBlock);
|
|
27400
|
+
const plugin = generateOpenCodePlugin(hooks);
|
|
27401
|
+
const warnings = [
|
|
27402
|
+
...getHookCompatibilityWarnings(hooks, "opencode"),
|
|
27403
|
+
...plugin !== null ? [
|
|
27404
|
+
{
|
|
27405
|
+
code: "PS4002",
|
|
27406
|
+
message: "OpenCode tool hooks do not expose model or agent context; the generated payload omits those fields.",
|
|
27407
|
+
suggestion: "Use sessionID and callID for attribution."
|
|
27408
|
+
}
|
|
27409
|
+
] : []
|
|
27410
|
+
].map((warning) => ({ ...warning, location: hooksBlock.loc }));
|
|
27411
|
+
return {
|
|
27412
|
+
...output,
|
|
27413
|
+
...plugin !== null ? {
|
|
27414
|
+
additionalFiles: [
|
|
27415
|
+
...output.additionalFiles ?? [],
|
|
27416
|
+
{ path: OPENCODE_PLUGIN_PATH, content: plugin }
|
|
27417
|
+
]
|
|
27418
|
+
} : {},
|
|
27419
|
+
...warnings.length > 0 ? { warnings: [...output.warnings ?? [], ...warnings] } : {},
|
|
27420
|
+
managedOutputFiles: managedFiles,
|
|
27421
|
+
managedOutputDirectories: [
|
|
27422
|
+
.../* @__PURE__ */ new Set([...output.managedOutputDirectories ?? [], ".opencode/plugins"])
|
|
27423
|
+
]
|
|
27424
|
+
};
|
|
27425
|
+
}
|
|
27426
|
+
};
|
|
26738
27427
|
}
|
|
26739
27428
|
});
|
|
26740
27429
|
|
|
@@ -27040,20 +27729,54 @@ ${escaped}"""`;
|
|
|
27040
27729
|
}
|
|
27041
27730
|
return '""';
|
|
27042
27731
|
}
|
|
27043
|
-
function
|
|
27732
|
+
function mcpServerTomlConfig(server) {
|
|
27733
|
+
const config = {};
|
|
27734
|
+
if (server.command) config["command"] = server.command;
|
|
27735
|
+
if (server.url) config["url"] = server.url;
|
|
27736
|
+
if (server.headers) config["headers"] = server.headers;
|
|
27737
|
+
if (server.env) config["env"] = server.env;
|
|
27738
|
+
return config;
|
|
27739
|
+
}
|
|
27740
|
+
function agentInstructionsLines(content) {
|
|
27741
|
+
if (content !== null && typeof content === "object" && content.type === "TextContent") {
|
|
27742
|
+
const textContent = content.value ?? "";
|
|
27743
|
+
return [`developer_instructions = ${serializeTomlValue(textContent)}`];
|
|
27744
|
+
}
|
|
27745
|
+
if (typeof content === "string") {
|
|
27746
|
+
return [`developer_instructions = ${serializeTomlValue(content)}`];
|
|
27747
|
+
}
|
|
27748
|
+
return [];
|
|
27749
|
+
}
|
|
27750
|
+
function mcpServerTomlTableHeader(serverName) {
|
|
27751
|
+
return /^[A-Za-z0-9_-]+$/.test(serverName) ? `[mcp_servers.${serverName}]` : `[mcp_servers."${escapeTomlString2(serverName)}"]`;
|
|
27752
|
+
}
|
|
27753
|
+
function mcpServerTableLines(serverName, config) {
|
|
27754
|
+
return [
|
|
27755
|
+
mcpServerTomlTableHeader(serverName),
|
|
27756
|
+
...Object.entries(config).map(([key, val]) => `${key} = ${serializeTomlValue(val)}`)
|
|
27757
|
+
];
|
|
27758
|
+
}
|
|
27759
|
+
function agentMcpServerLines(mcpServers, resolvedMcpServers) {
|
|
27760
|
+
if (mcpServers !== null && typeof mcpServers === "object" && !Array.isArray(mcpServers)) {
|
|
27761
|
+
const servers = mcpServers;
|
|
27762
|
+
return Object.entries(servers).filter(([, config]) => config !== null && typeof config === "object").flatMap(([name, config]) => mcpServerTableLines(name, config));
|
|
27763
|
+
}
|
|
27764
|
+
if (Array.isArray(mcpServers)) {
|
|
27765
|
+
return mcpServers.filter((serverName) => typeof serverName === "string").flatMap((serverName) => {
|
|
27766
|
+
const config = resolvedMcpServers?.get(serverName);
|
|
27767
|
+
return config ? mcpServerTableLines(serverName, config) : [];
|
|
27768
|
+
});
|
|
27769
|
+
}
|
|
27770
|
+
return [];
|
|
27771
|
+
}
|
|
27772
|
+
function serializeAgentToml(agentName, agent, resolvedMcpServers) {
|
|
27044
27773
|
const lines = [];
|
|
27045
27774
|
lines.push(`name = "${escapeTomlString2(agentName)}"`);
|
|
27046
27775
|
const description = agent["description"];
|
|
27047
27776
|
if (typeof description === "string") {
|
|
27048
27777
|
lines.push(`description = "${escapeTomlString2(description)}"`);
|
|
27049
27778
|
}
|
|
27050
|
-
|
|
27051
|
-
if (content !== null && typeof content === "object" && content.type === "TextContent") {
|
|
27052
|
-
const textContent = content.value ?? "";
|
|
27053
|
-
lines.push(`developer_instructions = ${serializeTomlValue(textContent)}`);
|
|
27054
|
-
} else if (typeof content === "string") {
|
|
27055
|
-
lines.push(`developer_instructions = ${serializeTomlValue(content)}`);
|
|
27056
|
-
}
|
|
27779
|
+
lines.push(...agentInstructionsLines(agent["content"]));
|
|
27057
27780
|
const model = agent["model"];
|
|
27058
27781
|
if (typeof model === "string") {
|
|
27059
27782
|
lines.push(`model = "${escapeTomlString2(model)}"`);
|
|
@@ -27071,30 +27794,14 @@ function serializeAgentToml(agentName, agent) {
|
|
|
27071
27794
|
const escaped = nicknameCandidates.filter((c) => typeof c === "string").map((c) => `"${escapeTomlString2(c)}"`);
|
|
27072
27795
|
lines.push(`nickname_candidates = [${escaped.join(", ")}]`);
|
|
27073
27796
|
}
|
|
27074
|
-
const
|
|
27075
|
-
if (
|
|
27076
|
-
|
|
27077
|
-
const serverEntries = Object.entries(servers);
|
|
27078
|
-
if (serverEntries.length > 0) {
|
|
27079
|
-
lines.push("");
|
|
27080
|
-
lines.push("[mcp_servers]");
|
|
27081
|
-
for (const [serverName, serverConfig] of serverEntries) {
|
|
27082
|
-
if (serverConfig !== null && typeof serverConfig === "object") {
|
|
27083
|
-
lines.push(`[mcp_servers.${serverName}]`);
|
|
27084
|
-
const config = serverConfig;
|
|
27085
|
-
for (const [key, val] of Object.entries(config)) {
|
|
27086
|
-
lines.push(`${key} = ${serializeTomlValue(val)}`);
|
|
27087
|
-
}
|
|
27088
|
-
}
|
|
27089
|
-
}
|
|
27090
|
-
}
|
|
27797
|
+
const serverTables = agentMcpServerLines(agent["mcpServers"], resolvedMcpServers);
|
|
27798
|
+
if (serverTables.length > 0) {
|
|
27799
|
+
lines.push("", "[mcp_servers]", ...serverTables);
|
|
27091
27800
|
}
|
|
27092
27801
|
const skills2 = agent["skills"];
|
|
27093
27802
|
if (Array.isArray(skills2)) {
|
|
27094
27803
|
const skillNames = skills2.filter((s) => typeof s === "string").map((s) => `"${escapeTomlString2(s)}"`);
|
|
27095
|
-
lines.push("");
|
|
27096
|
-
lines.push("[skills]");
|
|
27097
|
-
lines.push(`config = [${skillNames.join(", ")}]`);
|
|
27804
|
+
lines.push("", "[skills]", `config = [${skillNames.join(", ")}]`);
|
|
27098
27805
|
}
|
|
27099
27806
|
return lines.join("\n") + "\n";
|
|
27100
27807
|
}
|
|
@@ -27111,6 +27818,7 @@ var init_codex = __esm({
|
|
|
27111
27818
|
init_markdown_instruction_formatter();
|
|
27112
27819
|
init_hook_adapters();
|
|
27113
27820
|
init_hook_capability_warnings();
|
|
27821
|
+
init_agent_capability_warnings();
|
|
27114
27822
|
init_mcp_helpers();
|
|
27115
27823
|
init_plugin_helpers();
|
|
27116
27824
|
CODEX_VERSIONS = {
|
|
@@ -27209,12 +27917,13 @@ var init_codex = __esm({
|
|
|
27209
27917
|
format(ast, options) {
|
|
27210
27918
|
const version = this.resolveVersion(options?.version);
|
|
27211
27919
|
if (version === "simple") {
|
|
27212
|
-
|
|
27920
|
+
let output = appendTargetHookCapabilityWarnings(
|
|
27213
27921
|
this.formatSimple(ast, options),
|
|
27214
27922
|
ast,
|
|
27215
27923
|
this.name,
|
|
27216
27924
|
version
|
|
27217
27925
|
);
|
|
27926
|
+
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
27218
27927
|
return {
|
|
27219
27928
|
...output,
|
|
27220
27929
|
managedOutputFiles: [
|
|
@@ -27267,16 +27976,17 @@ var init_codex = __esm({
|
|
|
27267
27976
|
if (!managedDirs.includes(".codex")) managedDirs.push(".codex");
|
|
27268
27977
|
}
|
|
27269
27978
|
const mcpServersBlock = findMcpServersBlock(ast);
|
|
27270
|
-
|
|
27271
|
-
|
|
27272
|
-
|
|
27273
|
-
|
|
27274
|
-
|
|
27275
|
-
|
|
27276
|
-
|
|
27277
|
-
managedDirs.push(".codex");
|
|
27278
|
-
}
|
|
27979
|
+
const definedMcpServers = mcpServersBlock ? extractMcpServers(mcpServersBlock) : [];
|
|
27980
|
+
if (definedMcpServers.length > 0) {
|
|
27981
|
+
extraFiles.push({
|
|
27982
|
+
path: ".codex/mcp.json",
|
|
27983
|
+
content: serializeMcpServersToJsonString(definedMcpServers)
|
|
27984
|
+
});
|
|
27985
|
+
managedDirs.push(".codex");
|
|
27279
27986
|
}
|
|
27987
|
+
const resolvedMcpServers = new Map(
|
|
27988
|
+
definedMcpServers.map((server) => [server.name, mcpServerTomlConfig(server)])
|
|
27989
|
+
);
|
|
27280
27990
|
const pluginsBlock = findPluginsBlock(ast);
|
|
27281
27991
|
if (pluginsBlock) {
|
|
27282
27992
|
const plugins = extractPlugins(pluginsBlock);
|
|
@@ -27297,7 +28007,7 @@ var init_codex = __esm({
|
|
|
27297
28007
|
if (!isValidAgentName(agentName)) continue;
|
|
27298
28008
|
const agent = agentValue;
|
|
27299
28009
|
const nativeAgentName = nativeNames.get(agentName) ?? agentName;
|
|
27300
|
-
const toml = serializeAgentToml(nativeAgentName, agent);
|
|
28010
|
+
const toml = serializeAgentToml(nativeAgentName, agent, resolvedMcpServers);
|
|
27301
28011
|
extraFiles.push({
|
|
27302
28012
|
path: `.codex/agents/${nativeAgentName}.toml`,
|
|
27303
28013
|
content: toml
|
|
@@ -28051,6 +28761,7 @@ var init_grok = __esm({
|
|
|
28051
28761
|
init_plugin_helpers();
|
|
28052
28762
|
init_hook_adapters();
|
|
28053
28763
|
init_hook_capability_warnings();
|
|
28764
|
+
init_agent_capability_warnings();
|
|
28054
28765
|
GROK_VERSIONS = {
|
|
28055
28766
|
simple: {
|
|
28056
28767
|
name: "simple",
|
|
@@ -28097,6 +28808,7 @@ var init_grok = __esm({
|
|
|
28097
28808
|
output = this.formatFull(ast, options);
|
|
28098
28809
|
}
|
|
28099
28810
|
output = appendTargetHookCapabilityWarnings(output, ast, this.name, version);
|
|
28811
|
+
output = appendAgentCapabilityWarnings(output, ast, this.name, version);
|
|
28100
28812
|
return {
|
|
28101
28813
|
...output,
|
|
28102
28814
|
managedOutputFiles: [
|
|
@@ -28264,6 +28976,25 @@ var init_hermes = __esm({
|
|
|
28264
28976
|
}
|
|
28265
28977
|
});
|
|
28266
28978
|
|
|
28979
|
+
// packages/formatters/src/formatters/gitlab-duo.ts
|
|
28980
|
+
var GitlabDuoFormatter, GITLAB_DUO_VERSIONS;
|
|
28981
|
+
var init_gitlab_duo = __esm({
|
|
28982
|
+
"packages/formatters/src/formatters/gitlab-duo.ts"() {
|
|
28983
|
+
"use strict";
|
|
28984
|
+
init_create_simple_formatter();
|
|
28985
|
+
({ Formatter: GitlabDuoFormatter, VERSIONS: GITLAB_DUO_VERSIONS } = createSimpleMarkdownFormatter({
|
|
28986
|
+
name: "gitlab-duo",
|
|
28987
|
+
outputPath: "AGENTS.md",
|
|
28988
|
+
description: "GitLab Duo instructions (Markdown)",
|
|
28989
|
+
mainFileHeader: "# AGENTS.md",
|
|
28990
|
+
dotDir: ".gitlab-duo",
|
|
28991
|
+
hasSkills: true,
|
|
28992
|
+
skillsInMultifile: false,
|
|
28993
|
+
skillsDir: "skills"
|
|
28994
|
+
}));
|
|
28995
|
+
}
|
|
28996
|
+
});
|
|
28997
|
+
|
|
28267
28998
|
// packages/formatters/src/formatters/index.ts
|
|
28268
28999
|
var init_formatters = __esm({
|
|
28269
29000
|
"packages/formatters/src/formatters/index.ts"() {
|
|
@@ -28317,6 +29048,7 @@ var init_formatters = __esm({
|
|
|
28317
29048
|
init_deep_agents();
|
|
28318
29049
|
init_forgecode();
|
|
28319
29050
|
init_hermes();
|
|
29051
|
+
init_gitlab_duo();
|
|
28320
29052
|
}
|
|
28321
29053
|
});
|
|
28322
29054
|
|
|
@@ -28374,6 +29106,7 @@ var init_builtin_formatters = __esm({
|
|
|
28374
29106
|
init_deep_agents();
|
|
28375
29107
|
init_forgecode();
|
|
28376
29108
|
init_hermes();
|
|
29109
|
+
init_gitlab_duo();
|
|
28377
29110
|
BUILTIN_FORMATTERS = {
|
|
28378
29111
|
// Original 7
|
|
28379
29112
|
github: GitHubFormatter,
|
|
@@ -28429,7 +29162,9 @@ var init_builtin_formatters = __esm({
|
|
|
28429
29162
|
mimo: MimoFormatter,
|
|
28430
29163
|
"deep-agents": DeepAgentsFormatter,
|
|
28431
29164
|
forgecode: ForgecodeFormatter,
|
|
28432
|
-
hermes: HermesFormatter
|
|
29165
|
+
hermes: HermesFormatter,
|
|
29166
|
+
// GitLab Duo (Agent Platform)
|
|
29167
|
+
"gitlab-duo": GitlabDuoFormatter
|
|
28433
29168
|
};
|
|
28434
29169
|
}
|
|
28435
29170
|
});
|
|
@@ -30738,6 +31473,7 @@ function userConfigToProjectRegistry(userRegistry) {
|
|
|
30738
31473
|
...userRegistry.git.fallbackUrl ? { fallbackUrl: userRegistry.git.fallbackUrl } : {},
|
|
30739
31474
|
...userRegistry.git.ref ? { ref: userRegistry.git.ref } : {},
|
|
30740
31475
|
...userRegistry.git.path ? { path: userRegistry.git.path } : {},
|
|
31476
|
+
...userRegistry.git.timeout !== void 0 ? { timeout: userRegistry.git.timeout } : {},
|
|
30741
31477
|
...userRegistry.git.auth ? { auth: userRegistry.git.auth } : {}
|
|
30742
31478
|
};
|
|
30743
31479
|
}
|
|
@@ -31252,6 +31988,7 @@ async function runInitCommand(options, services) {
|
|
|
31252
31988
|
});
|
|
31253
31989
|
}
|
|
31254
31990
|
let migrationPrompt;
|
|
31991
|
+
let migrationPromptCopied = false;
|
|
31255
31992
|
if (migrationMode === "llm") {
|
|
31256
31993
|
migrationPrompt = prepareLlmMigration(aiToolsDetection.migrationCandidates, options);
|
|
31257
31994
|
writes.push({ path: ".promptscript/migration-prompt.md", content: migrationPrompt });
|
|
@@ -31286,7 +32023,7 @@ async function runInitCommand(options, services) {
|
|
|
31286
32023
|
return;
|
|
31287
32024
|
}
|
|
31288
32025
|
if (migrationPrompt) {
|
|
31289
|
-
deliverMigrationPrompt(migrationPrompt);
|
|
32026
|
+
migrationPromptCopied = deliverMigrationPrompt(migrationPrompt);
|
|
31290
32027
|
}
|
|
31291
32028
|
const installedHookTargets = [];
|
|
31292
32029
|
if (options.hooks !== false) {
|
|
@@ -31353,13 +32090,17 @@ async function runInitCommand(options, services) {
|
|
|
31353
32090
|
ConsoleOutput.newline();
|
|
31354
32091
|
ConsoleOutput.stats("Next steps:");
|
|
31355
32092
|
if (migrationMode === "llm") {
|
|
31356
|
-
ConsoleOutput.
|
|
31357
|
-
|
|
31358
|
-
|
|
31359
|
-
|
|
32093
|
+
ConsoleOutput.info("1. Start your AI agent in this project");
|
|
32094
|
+
if (migrationPromptCopied) {
|
|
32095
|
+
ConsoleOutput.info("2. Paste the migration prompt - it is already in your clipboard");
|
|
32096
|
+
} else {
|
|
32097
|
+
ConsoleOutput.info(
|
|
32098
|
+
"2. Copy the migration prompt from the terminal output and paste it into your agent"
|
|
32099
|
+
);
|
|
31360
32100
|
}
|
|
31361
|
-
ConsoleOutput.
|
|
31362
|
-
ConsoleOutput.
|
|
32101
|
+
ConsoleOutput.info("3. Let the agent migrate your instructions");
|
|
32102
|
+
ConsoleOutput.info("4. Review the generated files, then run: prs validate --strict");
|
|
32103
|
+
ConsoleOutput.info("5. Run: prs compile");
|
|
31363
32104
|
} else if (migrationMode === "static") {
|
|
31364
32105
|
ConsoleOutput.muted("1. Review imported .promptscript/ files");
|
|
31365
32106
|
ConsoleOutput.muted("2. Run: prs validate --strict");
|
|
@@ -31499,6 +32240,7 @@ function deliverMigrationPrompt(prompt) {
|
|
|
31499
32240
|
console.log(prompt);
|
|
31500
32241
|
}
|
|
31501
32242
|
ConsoleOutput.info("Saved to .promptscript/migration-prompt.md");
|
|
32243
|
+
return copied;
|
|
31502
32244
|
}
|
|
31503
32245
|
function getSkillWrites(targets) {
|
|
31504
32246
|
const writes = [];
|
|
@@ -31841,16 +32583,6 @@ function getTargetSkillDir(target, skillName) {
|
|
|
31841
32583
|
const dir = `${basePath}/${skillName}`;
|
|
31842
32584
|
return { dir, path: `${dir}/${fileName}` };
|
|
31843
32585
|
}
|
|
31844
|
-
function getSkillInvocationHints(targets) {
|
|
31845
|
-
const hints = [];
|
|
31846
|
-
for (const target of targets) {
|
|
31847
|
-
const formatter = FormatterRegistry.get(target);
|
|
31848
|
-
if (formatter && formatter.getSkillBasePath()) {
|
|
31849
|
-
hints.push(`${formatter.description}: /promptscript`);
|
|
31850
|
-
}
|
|
31851
|
-
}
|
|
31852
|
-
return hints;
|
|
31853
|
-
}
|
|
31854
32586
|
function generateConfig(config) {
|
|
31855
32587
|
const syntaxVersion = getLatestSyntaxVersion();
|
|
31856
32588
|
const output = {
|
|
@@ -35272,9 +36004,9 @@ var require_rate_limit = __commonJS({
|
|
|
35272
36004
|
throw new Error("The configured rate-limit store does not implement a `read` method, which is required to use `{ increment: false }`");
|
|
35273
36005
|
}
|
|
35274
36006
|
try {
|
|
35275
|
-
const res = await new Promise((
|
|
36007
|
+
const res = await new Promise((resolve43, reject) => {
|
|
35276
36008
|
store[storeMethod](key, (err, res2) => {
|
|
35277
|
-
err ? reject(err) :
|
|
36009
|
+
err ? reject(err) : resolve43(res2);
|
|
35278
36010
|
}, timeWindow, max);
|
|
35279
36011
|
});
|
|
35280
36012
|
current = res.current;
|
|
@@ -35444,16 +36176,16 @@ var init_config2 = __esm({
|
|
|
35444
36176
|
});
|
|
35445
36177
|
|
|
35446
36178
|
// packages/server/src/path-guard.ts
|
|
35447
|
-
import { resolve as
|
|
36179
|
+
import { resolve as resolve41, relative as relative21, isAbsolute as isAbsolute19, dirname as dirname25, basename as basename19 } from "path";
|
|
35448
36180
|
import { realpath as realpath6 } from "fs/promises";
|
|
35449
36181
|
async function resolveSafePath(workspace, requestedPath) {
|
|
35450
36182
|
const decoded = decodeURIComponent(requestedPath);
|
|
35451
|
-
if (
|
|
36183
|
+
if (isAbsolute19(decoded)) {
|
|
35452
36184
|
throw new PathTraversalError(requestedPath);
|
|
35453
36185
|
}
|
|
35454
|
-
const resolved =
|
|
35455
|
-
const rel =
|
|
35456
|
-
if (rel.startsWith("..") ||
|
|
36186
|
+
const resolved = resolve41(workspace, decoded);
|
|
36187
|
+
const rel = relative21(workspace, resolved);
|
|
36188
|
+
if (rel.startsWith("..") || isAbsolute19(rel)) {
|
|
35457
36189
|
throw new PathTraversalError(requestedPath);
|
|
35458
36190
|
}
|
|
35459
36191
|
let realWorkspace;
|
|
@@ -35469,7 +36201,7 @@ async function resolveSafePath(workspace, requestedPath) {
|
|
|
35469
36201
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
35470
36202
|
try {
|
|
35471
36203
|
const realParent = await realpath6(dirname25(resolved));
|
|
35472
|
-
real =
|
|
36204
|
+
real = resolve41(realParent, basename19(resolved));
|
|
35473
36205
|
} catch {
|
|
35474
36206
|
return resolved;
|
|
35475
36207
|
}
|
|
@@ -35477,8 +36209,8 @@ async function resolveSafePath(workspace, requestedPath) {
|
|
|
35477
36209
|
throw err;
|
|
35478
36210
|
}
|
|
35479
36211
|
}
|
|
35480
|
-
const realRel =
|
|
35481
|
-
if (realRel.startsWith("..") ||
|
|
36212
|
+
const realRel = relative21(realWorkspace, real);
|
|
36213
|
+
if (realRel.startsWith("..") || isAbsolute19(realRel)) {
|
|
35482
36214
|
throw new PathTraversalError(requestedPath);
|
|
35483
36215
|
}
|
|
35484
36216
|
return resolved;
|
|
@@ -35497,7 +36229,7 @@ var init_path_guard = __esm({
|
|
|
35497
36229
|
});
|
|
35498
36230
|
|
|
35499
36231
|
// packages/server/src/routes/files.ts
|
|
35500
|
-
import { readFile as readFile32, writeFile as writeFile12, unlink as unlink3, stat as stat4, mkdir as
|
|
36232
|
+
import { readFile as readFile32, writeFile as writeFile12, unlink as unlink3, stat as stat4, mkdir as mkdir12 } from "fs/promises";
|
|
35501
36233
|
import { dirname as dirname26 } from "path";
|
|
35502
36234
|
import fg from "fast-glob";
|
|
35503
36235
|
function registerRoutes(app, workspace, readOnly) {
|
|
@@ -35558,7 +36290,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
35558
36290
|
return reply.status(409).send({ error: "File already exists" });
|
|
35559
36291
|
} catch {
|
|
35560
36292
|
}
|
|
35561
|
-
await
|
|
36293
|
+
await mkdir12(dirname26(resolved), { recursive: true });
|
|
35562
36294
|
await writeFile12(resolved, request.body.content, "utf-8");
|
|
35563
36295
|
return reply.status(201).send({ path: filePath, status: "created" });
|
|
35564
36296
|
} catch (err) {
|
|
@@ -35596,7 +36328,7 @@ var init_files = __esm({
|
|
|
35596
36328
|
|
|
35597
36329
|
// packages/server/src/watcher.ts
|
|
35598
36330
|
import { watch } from "chokidar";
|
|
35599
|
-
import { relative as
|
|
36331
|
+
import { relative as relative22 } from "path";
|
|
35600
36332
|
function isWatchedFile(filePath) {
|
|
35601
36333
|
const base = filePath.split("/").pop() ?? "";
|
|
35602
36334
|
if (CONFIG_FILENAMES2.has(base)) return true;
|
|
@@ -35612,15 +36344,15 @@ function createFileWatcher(workspace, watchPaths, onEvent) {
|
|
|
35612
36344
|
});
|
|
35613
36345
|
watcher.on("add", (filePath) => {
|
|
35614
36346
|
if (!ready || !isWatchedFile(filePath)) return;
|
|
35615
|
-
onEvent({ type: "file:created", path:
|
|
36347
|
+
onEvent({ type: "file:created", path: relative22(workspace, filePath) });
|
|
35616
36348
|
});
|
|
35617
36349
|
watcher.on("change", (filePath) => {
|
|
35618
36350
|
if (!ready || !isWatchedFile(filePath)) return;
|
|
35619
|
-
onEvent({ type: "file:changed", path:
|
|
36351
|
+
onEvent({ type: "file:changed", path: relative22(workspace, filePath) });
|
|
35620
36352
|
});
|
|
35621
36353
|
watcher.on("unlink", (filePath) => {
|
|
35622
36354
|
if (!ready || !isWatchedFile(filePath)) return;
|
|
35623
|
-
onEvent({ type: "file:deleted", path:
|
|
36355
|
+
onEvent({ type: "file:deleted", path: relative22(workspace, filePath) });
|
|
35624
36356
|
});
|
|
35625
36357
|
return watcher;
|
|
35626
36358
|
}
|
|
@@ -35787,7 +36519,7 @@ var migrate_exports = {};
|
|
|
35787
36519
|
__export(migrate_exports, {
|
|
35788
36520
|
migrateCommand: () => migrateCommand
|
|
35789
36521
|
});
|
|
35790
|
-
import { dirname as dirname27, relative as
|
|
36522
|
+
import { dirname as dirname27, relative as relative23, resolve as resolve42 } from "path";
|
|
35791
36523
|
import { parse as parseYaml17 } from "yaml";
|
|
35792
36524
|
async function migrateCommand(options, services = createDefaultServices()) {
|
|
35793
36525
|
const previousOutputStream = getContext().outputStream;
|
|
@@ -36045,7 +36777,7 @@ function extractTargetNames(targets) {
|
|
|
36045
36777
|
}
|
|
36046
36778
|
async function createStaticMigrationWrites(candidates, config, entryPaths, services) {
|
|
36047
36779
|
const result = await importMultipleFiles(
|
|
36048
|
-
candidates.map((candidate) =>
|
|
36780
|
+
candidates.map((candidate) => resolve42(services.cwd, candidate.path)),
|
|
36049
36781
|
{
|
|
36050
36782
|
projectName: config.id,
|
|
36051
36783
|
syntaxVersion: config.syntax || getLatestSyntaxVersion()
|
|
@@ -36079,7 +36811,7 @@ async function createStaticMigrationWrites(candidates, config, entryPaths, servi
|
|
|
36079
36811
|
const migratedProjectPath = ".promptscript/migrated/project.prs";
|
|
36080
36812
|
writes.push({ path: migratedProjectPath, content: markMigrationOutput(importedProject) });
|
|
36081
36813
|
for (const entryPath of entryPaths) {
|
|
36082
|
-
const useLine = `@use ${
|
|
36814
|
+
const useLine = `@use ${toUsePath2(entryPath, migratedProjectPath)}`;
|
|
36083
36815
|
if (services.fs.existsSync(entryPath)) {
|
|
36084
36816
|
const existing = await services.fs.readFile(entryPath, "utf-8");
|
|
36085
36817
|
const hasUseLine = existing.split(/\r?\n/).some((line) => line.trim() === useLine);
|
|
@@ -36137,9 +36869,9 @@ function markMigrationOutput(content) {
|
|
|
36137
36869
|
return `# promptscript-generated: migration
|
|
36138
36870
|
${content}`;
|
|
36139
36871
|
}
|
|
36140
|
-
function
|
|
36872
|
+
function toUsePath2(entryPath, targetPath) {
|
|
36141
36873
|
const withoutExtension = targetPath.replace(/\.prs$/, "");
|
|
36142
|
-
const path2 =
|
|
36874
|
+
const path2 = relative23(dirname27(entryPath), withoutExtension).replace(/\\/g, "/");
|
|
36143
36875
|
return path2.startsWith("./") || path2.startsWith("../") ? path2 : `./${path2}`;
|
|
36144
36876
|
}
|
|
36145
36877
|
function validateProjectPath(path2) {
|
|
@@ -36204,12 +36936,13 @@ import { fileURLToPath as fileURLToPath4, pathToFileURL } from "url";
|
|
|
36204
36936
|
import { dirname as dirname28 } from "path";
|
|
36205
36937
|
|
|
36206
36938
|
// packages/cli/src/commands/compile.ts
|
|
36207
|
-
import { resolve as
|
|
36208
|
-
import {
|
|
36209
|
-
import {
|
|
36939
|
+
import { resolve as resolve22, isAbsolute as isAbsolute14, relative as relative15 } from "node:path";
|
|
36940
|
+
import { homedir as homedir4 } from "node:os";
|
|
36941
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
36942
|
+
import { existsSync as existsSync14, lstatSync as lstatSync2 } from "node:fs";
|
|
36210
36943
|
import chokidar from "chokidar";
|
|
36211
36944
|
|
|
36212
|
-
// node_modules/.pnpm/balanced-match@4.0.
|
|
36945
|
+
// node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js
|
|
36213
36946
|
var balanced = (a, b, str) => {
|
|
36214
36947
|
const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
|
|
36215
36948
|
const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
|
|
@@ -38149,6 +38882,14 @@ function findFallbackUrl(repoUrl, registries) {
|
|
|
38149
38882
|
}
|
|
38150
38883
|
return void 0;
|
|
38151
38884
|
}
|
|
38885
|
+
function findRegistryEntry(repoUrl, registries) {
|
|
38886
|
+
for (const entry of Object.values(registries)) {
|
|
38887
|
+
if (typeof entry !== "string" && entry.url === repoUrl) {
|
|
38888
|
+
return entry;
|
|
38889
|
+
}
|
|
38890
|
+
}
|
|
38891
|
+
return void 0;
|
|
38892
|
+
}
|
|
38152
38893
|
|
|
38153
38894
|
// packages/resolver/src/loader.ts
|
|
38154
38895
|
var REGISTRY_MARKER_PREFIX = "__registry__:";
|
|
@@ -39169,7 +39910,8 @@ function parseSkillMd(content, sourceFile = "<skill>") {
|
|
|
39169
39910
|
sourceFile,
|
|
39170
39911
|
frontmatter.location,
|
|
39171
39912
|
yamlFrontmatter.fieldLocations,
|
|
39172
|
-
yamlFrontmatter.fieldItemLocations
|
|
39913
|
+
yamlFrontmatter.fieldItemLocations,
|
|
39914
|
+
yamlFrontmatter.fieldValueLocations
|
|
39173
39915
|
);
|
|
39174
39916
|
const bodyContent = stripLeadingHtmlMarker(frontmatter.body).trim();
|
|
39175
39917
|
const result = {
|
|
@@ -39294,11 +40036,11 @@ function parseYamlFrontmatter(frontmatter, sourceFile) {
|
|
|
39294
40036
|
}
|
|
39295
40037
|
rejectUnsafeYamlNodes(document.contents, sourceFile, frontmatter);
|
|
39296
40038
|
enforceFrontmatterLimits(document.contents, sourceFile, frontmatter);
|
|
39297
|
-
const {
|
|
39298
|
-
|
|
39299
|
-
|
|
39300
|
-
|
|
39301
|
-
);
|
|
40039
|
+
const {
|
|
40040
|
+
fields: fieldLocations,
|
|
40041
|
+
items: fieldItemLocations,
|
|
40042
|
+
values: fieldValueLocations
|
|
40043
|
+
} = getTopLevelFieldLocations(document.contents, sourceFile, frontmatter);
|
|
39302
40044
|
let value;
|
|
39303
40045
|
try {
|
|
39304
40046
|
value = document.toJS({ maxAliasCount: 0 });
|
|
@@ -39311,7 +40053,7 @@ function parseYamlFrontmatter(frontmatter, sourceFile) {
|
|
|
39311
40053
|
);
|
|
39312
40054
|
}
|
|
39313
40055
|
if (document.contents === null) {
|
|
39314
|
-
return { fields: {}, fieldLocations, fieldItemLocations };
|
|
40056
|
+
return { fields: {}, fieldLocations, fieldItemLocations, fieldValueLocations };
|
|
39315
40057
|
}
|
|
39316
40058
|
if (!isRecord5(value)) {
|
|
39317
40059
|
throw frontmatterError(
|
|
@@ -39320,7 +40062,7 @@ function parseYamlFrontmatter(frontmatter, sourceFile) {
|
|
|
39320
40062
|
frontmatter.location
|
|
39321
40063
|
);
|
|
39322
40064
|
}
|
|
39323
|
-
return { fields: value, fieldLocations, fieldItemLocations };
|
|
40065
|
+
return { fields: value, fieldLocations, fieldItemLocations, fieldValueLocations };
|
|
39324
40066
|
}
|
|
39325
40067
|
function getYamlLocation(frontmatter, sourceFile, offset) {
|
|
39326
40068
|
const source = frontmatter.raw;
|
|
@@ -39339,7 +40081,10 @@ function getYamlLocation(frontmatter, sourceFile, offset) {
|
|
|
39339
40081
|
function getTopLevelFieldLocations(node, sourceFile, frontmatter) {
|
|
39340
40082
|
const fieldLocations = /* @__PURE__ */ new Map();
|
|
39341
40083
|
const fieldItemLocations = /* @__PURE__ */ new Map();
|
|
39342
|
-
|
|
40084
|
+
const fieldValueLocations = /* @__PURE__ */ new Map();
|
|
40085
|
+
if (!isCollection(node)) {
|
|
40086
|
+
return { fields: fieldLocations, items: fieldItemLocations, values: fieldValueLocations };
|
|
40087
|
+
}
|
|
39343
40088
|
for (const item of node.items) {
|
|
39344
40089
|
if (!isPair(item)) continue;
|
|
39345
40090
|
const key = getScalarString(item.key);
|
|
@@ -39351,9 +40096,21 @@ function getTopLevelFieldLocations(node, sourceFile, frontmatter) {
|
|
|
39351
40096
|
key,
|
|
39352
40097
|
item.value.items.map((value) => getNodeLocation(value, sourceFile, frontmatter))
|
|
39353
40098
|
);
|
|
40099
|
+
fieldValueLocations.set(key, getNestedFieldLocations(item.value, sourceFile, frontmatter));
|
|
39354
40100
|
}
|
|
39355
40101
|
}
|
|
39356
|
-
return { fields: fieldLocations, items: fieldItemLocations };
|
|
40102
|
+
return { fields: fieldLocations, items: fieldItemLocations, values: fieldValueLocations };
|
|
40103
|
+
}
|
|
40104
|
+
function getNestedFieldLocations(collection, sourceFile, frontmatter) {
|
|
40105
|
+
const valueLocations = /* @__PURE__ */ new Map();
|
|
40106
|
+
if (!isCollection(collection)) return valueLocations;
|
|
40107
|
+
for (const item of collection.items) {
|
|
40108
|
+
if (!isPair(item)) continue;
|
|
40109
|
+
const key = getScalarString(item.key);
|
|
40110
|
+
if (key === void 0) continue;
|
|
40111
|
+
valueLocations.set(key, getNodeLocation(item.value ?? item.key, sourceFile, frontmatter));
|
|
40112
|
+
}
|
|
40113
|
+
return valueLocations;
|
|
39357
40114
|
}
|
|
39358
40115
|
function getScalarString(node) {
|
|
39359
40116
|
if (!isNode(node) || !("value" in node)) return void 0;
|
|
@@ -39468,7 +40225,7 @@ function frontmatterError(message, sourceFile, location) {
|
|
|
39468
40225
|
file: sourceFile
|
|
39469
40226
|
});
|
|
39470
40227
|
}
|
|
39471
|
-
function parseFrontmatterFields(fields, sourceFile, fallbackLocation, fieldLocations, fieldItemLocations) {
|
|
40228
|
+
function parseFrontmatterFields(fields, sourceFile, fallbackLocation, fieldLocations, fieldItemLocations, fieldValueLocations) {
|
|
39472
40229
|
const name = readOptionalString(
|
|
39473
40230
|
fields,
|
|
39474
40231
|
"name",
|
|
@@ -39526,7 +40283,8 @@ function parseFrontmatterFields(fields, sourceFile, fallbackLocation, fieldLocat
|
|
|
39526
40283
|
const metadata = parseMetadataField(
|
|
39527
40284
|
fields,
|
|
39528
40285
|
sourceFile,
|
|
39529
|
-
getFieldLocation("metadata", fieldLocations, fallbackLocation)
|
|
40286
|
+
getFieldLocation("metadata", fieldLocations, fallbackLocation),
|
|
40287
|
+
fieldValueLocations.get("metadata")
|
|
39530
40288
|
);
|
|
39531
40289
|
const allowedTools = parseAllowedToolsField(
|
|
39532
40290
|
fields,
|
|
@@ -39789,7 +40547,7 @@ function readOptionalStringFromRecord(record, field, context, sourceFile, locati
|
|
|
39789
40547
|
}
|
|
39790
40548
|
return value;
|
|
39791
40549
|
}
|
|
39792
|
-
function parseMetadataField(fields, sourceFile, location) {
|
|
40550
|
+
function parseMetadataField(fields, sourceFile, location, valueLocations) {
|
|
39793
40551
|
if (!Object.hasOwn(fields, "metadata")) return void 0;
|
|
39794
40552
|
const value = fields["metadata"];
|
|
39795
40553
|
if (!isRecord5(value)) {
|
|
@@ -39798,7 +40556,11 @@ function parseMetadataField(fields, sourceFile, location) {
|
|
|
39798
40556
|
const metadata = createSafeRecord();
|
|
39799
40557
|
for (const [key, item] of Object.entries(value)) {
|
|
39800
40558
|
if (typeof item !== "string") {
|
|
39801
|
-
throw frontmatterError(
|
|
40559
|
+
throw frontmatterError(
|
|
40560
|
+
`metadata value "${key}" must be a string; quote scalar values (for example, 16 as "16")`,
|
|
40561
|
+
sourceFile,
|
|
40562
|
+
valueLocations?.get(key) ?? location
|
|
40563
|
+
);
|
|
39802
40564
|
}
|
|
39803
40565
|
metadata[key] = item;
|
|
39804
40566
|
}
|
|
@@ -40977,26 +41739,29 @@ var VIRTUAL_LOC = {
|
|
|
40977
41739
|
column: 1,
|
|
40978
41740
|
offset: 0
|
|
40979
41741
|
};
|
|
40980
|
-
function
|
|
41742
|
+
function fileLoc(file) {
|
|
41743
|
+
return { file, line: 1, column: 1, offset: 0 };
|
|
41744
|
+
}
|
|
41745
|
+
function makeObjectContent(properties, file) {
|
|
40981
41746
|
return {
|
|
40982
41747
|
type: "ObjectContent",
|
|
40983
41748
|
properties,
|
|
40984
|
-
loc: VIRTUAL_LOC
|
|
41749
|
+
loc: file ? fileLoc(file) : VIRTUAL_LOC
|
|
40985
41750
|
};
|
|
40986
41751
|
}
|
|
40987
41752
|
function makeTextContent(value, file) {
|
|
40988
41753
|
return {
|
|
40989
41754
|
type: "TextContent",
|
|
40990
41755
|
value,
|
|
40991
|
-
loc: file ?
|
|
41756
|
+
loc: file ? fileLoc(file) : VIRTUAL_LOC
|
|
40992
41757
|
};
|
|
40993
41758
|
}
|
|
40994
|
-
function makeBlock(name, content) {
|
|
41759
|
+
function makeBlock(name, content, file) {
|
|
40995
41760
|
return {
|
|
40996
41761
|
type: "Block",
|
|
40997
41762
|
name,
|
|
40998
41763
|
content,
|
|
40999
|
-
loc: VIRTUAL_LOC
|
|
41764
|
+
loc: file ? fileLoc(file) : VIRTUAL_LOC
|
|
41000
41765
|
};
|
|
41001
41766
|
}
|
|
41002
41767
|
|
|
@@ -41848,9 +42613,29 @@ var GitCacheManager = class {
|
|
|
41848
42613
|
// packages/resolver/src/git-registry.ts
|
|
41849
42614
|
var TAGS_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
41850
42615
|
var DEFAULT_GIT_TIMEOUT_MS = 6e4;
|
|
42616
|
+
var GIT_TIMEOUT_ENV_VAR = "PROMPTSCRIPT_GIT_TIMEOUT";
|
|
41851
42617
|
var SEMVER_TAG_RE = /^v?\d+\.\d+\.\d+/;
|
|
42618
|
+
function envGitTimeout() {
|
|
42619
|
+
const raw = process.env[GIT_TIMEOUT_ENV_VAR];
|
|
42620
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
42621
|
+
return void 0;
|
|
42622
|
+
}
|
|
42623
|
+
const parsed = Number(raw);
|
|
42624
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
42625
|
+
}
|
|
41852
42626
|
function normalizeGitTimeout(timeout) {
|
|
41853
|
-
|
|
42627
|
+
if (timeout !== void 0 && Number.isFinite(timeout) && timeout > 0) {
|
|
42628
|
+
return timeout;
|
|
42629
|
+
}
|
|
42630
|
+
const fromEnv = envGitTimeout();
|
|
42631
|
+
if (fromEnv !== void 0) {
|
|
42632
|
+
return fromEnv;
|
|
42633
|
+
}
|
|
42634
|
+
return DEFAULT_GIT_TIMEOUT_MS;
|
|
42635
|
+
}
|
|
42636
|
+
function isPartialCloneUnsupported(error) {
|
|
42637
|
+
const message = error.message.toLowerCase();
|
|
42638
|
+
return message.includes("filtering not recognized") || message.includes("does not support filtering") || message.includes("invalid filter") || message.includes("filter is not supported") || /unknown (option|switch)[`' ]*(sparse|filter)/.test(message);
|
|
41854
42639
|
}
|
|
41855
42640
|
var GitCloneError = class extends Error {
|
|
41856
42641
|
constructor(message, url, cause) {
|
|
@@ -42082,17 +42867,20 @@ var GitRegistry = class {
|
|
|
42082
42867
|
* @param tag - Git tag to check out, or undefined for the default branch
|
|
42083
42868
|
* @param targetDir - Directory to clone into
|
|
42084
42869
|
* @param fallbackRepoUrl - Optional fallback URL to try on auth failure
|
|
42870
|
+
* @param sparsePath - Optional subdirectory to limit the checkout to.
|
|
42871
|
+
* Uses `--filter=blob:none --sparse` + `sparse-checkout` so only the
|
|
42872
|
+
* blobs for that subdirectory are fetched. Falls back to a plain
|
|
42873
|
+
* shallow clone when the server does not support partial clones.
|
|
42085
42874
|
*/
|
|
42086
|
-
async cloneAtTag(repoUrl, tag, targetDir, fallbackRepoUrl) {
|
|
42875
|
+
async cloneAtTag(repoUrl, tag, targetDir, fallbackRepoUrl, sparsePath) {
|
|
42087
42876
|
if (existsSync8(targetDir)) {
|
|
42088
42877
|
await fs2.rm(targetDir, { recursive: true, force: true });
|
|
42089
42878
|
}
|
|
42090
42879
|
await fs2.mkdir(targetDir, { recursive: true });
|
|
42091
42880
|
const git = this.createGit();
|
|
42092
|
-
const cloneOptions = tag ? ["--depth=1", `--branch=${tag}`, "--single-branch"] : ["--depth=1"];
|
|
42093
42881
|
const cloneUrl = this.auth ? this.transportUrl : repoUrl;
|
|
42094
42882
|
try {
|
|
42095
|
-
await
|
|
42883
|
+
await this.performClone(git, cloneUrl, targetDir, tag, sparsePath);
|
|
42096
42884
|
} catch (err) {
|
|
42097
42885
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
42098
42886
|
if (isGitTimeoutError(error)) {
|
|
@@ -42104,24 +42892,15 @@ var GitRegistry = class {
|
|
|
42104
42892
|
if (this.isAccessError(error)) {
|
|
42105
42893
|
if (fallbackRepoUrl) {
|
|
42106
42894
|
try {
|
|
42107
|
-
await
|
|
42895
|
+
await this.performClone(git, fallbackRepoUrl, targetDir, tag, sparsePath);
|
|
42108
42896
|
return;
|
|
42109
42897
|
} catch (fallbackErr) {
|
|
42110
|
-
|
|
42111
|
-
|
|
42112
|
-
throw createGitTimeoutError(fallbackRepoUrl, this.timeout, fbError);
|
|
42113
|
-
}
|
|
42114
|
-
if (this.isAccessError(fbError)) {
|
|
42115
|
-
throw new GitAuthError(
|
|
42116
|
-
`Authentication failed for both ${repoUrl} and fallback ${fallbackRepoUrl}`,
|
|
42117
|
-
fallbackRepoUrl,
|
|
42118
|
-
fbError
|
|
42119
|
-
);
|
|
42120
|
-
}
|
|
42121
|
-
throw new GitCloneError(
|
|
42122
|
-
`Failed to clone ${fallbackRepoUrl} at ${tag ?? "default branch"}: ${fbError.message}`,
|
|
42898
|
+
throw this.classifyFallbackCloneError(
|
|
42899
|
+
repoUrl,
|
|
42123
42900
|
fallbackRepoUrl,
|
|
42124
|
-
|
|
42901
|
+
fallbackErr,
|
|
42902
|
+
tag,
|
|
42903
|
+
"clone"
|
|
42125
42904
|
);
|
|
42126
42905
|
}
|
|
42127
42906
|
}
|
|
@@ -42134,6 +42913,92 @@ var GitRegistry = class {
|
|
|
42134
42913
|
);
|
|
42135
42914
|
}
|
|
42136
42915
|
}
|
|
42916
|
+
/**
|
|
42917
|
+
* Clone shallow, optionally partial (`--filter=blob:none --sparse`) with a
|
|
42918
|
+
* sparse-checkout cone of `sparsePath`. Retries without the partial-clone
|
|
42919
|
+
* flags when the server or local Git does not support them.
|
|
42920
|
+
*/
|
|
42921
|
+
async performClone(git, url, targetDir, tag, sparsePath) {
|
|
42922
|
+
const tagOptions = tag ? ["--branch=" + tag, "--single-branch"] : [];
|
|
42923
|
+
const baseOptions = ["--depth=1", ...tagOptions];
|
|
42924
|
+
if (!sparsePath) {
|
|
42925
|
+
await git.clone(url, targetDir, baseOptions);
|
|
42926
|
+
return;
|
|
42927
|
+
}
|
|
42928
|
+
try {
|
|
42929
|
+
await git.clone(url, targetDir, [...baseOptions, "--filter=blob:none", "--sparse"]);
|
|
42930
|
+
} catch (err) {
|
|
42931
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
42932
|
+
if (!isPartialCloneUnsupported(error)) {
|
|
42933
|
+
throw error;
|
|
42934
|
+
}
|
|
42935
|
+
if (existsSync8(targetDir)) {
|
|
42936
|
+
await fs2.rm(targetDir, { recursive: true, force: true });
|
|
42937
|
+
}
|
|
42938
|
+
await fs2.mkdir(targetDir, { recursive: true });
|
|
42939
|
+
await git.clone(url, targetDir, baseOptions);
|
|
42940
|
+
return;
|
|
42941
|
+
}
|
|
42942
|
+
const repoGit = this.createGit(targetDir);
|
|
42943
|
+
await repoGit.raw(["sparse-checkout", "set", sparsePath]);
|
|
42944
|
+
}
|
|
42945
|
+
/**
|
|
42946
|
+
* Check whether the checkout at `targetDir` uses sparse-checkout.
|
|
42947
|
+
*/
|
|
42948
|
+
async isSparseCheckout(targetDir) {
|
|
42949
|
+
const git = this.createGit(targetDir);
|
|
42950
|
+
try {
|
|
42951
|
+
const out = await git.raw(["config", "core.sparseCheckout"]);
|
|
42952
|
+
return out.trim() === "true";
|
|
42953
|
+
} catch {
|
|
42954
|
+
return false;
|
|
42955
|
+
}
|
|
42956
|
+
}
|
|
42957
|
+
/**
|
|
42958
|
+
* Disable sparse-checkout, materializing the full working tree.
|
|
42959
|
+
* Used when a root-level import needs the whole repository but the
|
|
42960
|
+
* cached clone is partial.
|
|
42961
|
+
*/
|
|
42962
|
+
async disableSparseCheckout(targetDir) {
|
|
42963
|
+
try {
|
|
42964
|
+
await this.createGit(targetDir).raw(["sparse-checkout", "disable"]);
|
|
42965
|
+
} catch (err) {
|
|
42966
|
+
this.throwIfGitTimeout(err);
|
|
42967
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
42968
|
+
}
|
|
42969
|
+
}
|
|
42970
|
+
/** Convert a raw Git error into a GitCloneError when it is a timeout. */
|
|
42971
|
+
throwIfGitTimeout(err) {
|
|
42972
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
42973
|
+
if (isGitTimeoutError(error)) {
|
|
42974
|
+
throw createGitTimeoutError(this.url, this.timeout, error);
|
|
42975
|
+
}
|
|
42976
|
+
}
|
|
42977
|
+
/**
|
|
42978
|
+
* Add paths to an existing sparse checkout so their blobs are fetched
|
|
42979
|
+
* on demand. Used when a cached partial clone needs a subdirectory that
|
|
42980
|
+
* the original cone did not cover.
|
|
42981
|
+
*/
|
|
42982
|
+
async addSparsePaths(targetDir, paths) {
|
|
42983
|
+
const extra = paths.filter((p) => p.length > 0 && p !== ".");
|
|
42984
|
+
if (extra.length === 0) {
|
|
42985
|
+
return;
|
|
42986
|
+
}
|
|
42987
|
+
const git = this.createGit(targetDir);
|
|
42988
|
+
try {
|
|
42989
|
+
await git.raw(["sparse-checkout", "add", ...extra]);
|
|
42990
|
+
} catch (addError) {
|
|
42991
|
+
const original = addError instanceof Error ? addError : new Error(String(addError));
|
|
42992
|
+
try {
|
|
42993
|
+
const listed = await git.raw(["sparse-checkout", "list"]);
|
|
42994
|
+
const current = listed.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
42995
|
+
await git.raw(["sparse-checkout", "set", .../* @__PURE__ */ new Set([...current, ...extra])]);
|
|
42996
|
+
} catch (setErr) {
|
|
42997
|
+
this.throwIfGitTimeout(setErr);
|
|
42998
|
+
throw original;
|
|
42999
|
+
}
|
|
43000
|
+
}
|
|
43001
|
+
}
|
|
42137
43002
|
/**
|
|
42138
43003
|
* List all semver tags from a remote repo.
|
|
42139
43004
|
* Results are cached via RegistryCache to avoid repeated ls-remote calls.
|
|
@@ -42199,8 +43064,9 @@ var GitRegistry = class {
|
|
|
42199
43064
|
}
|
|
42200
43065
|
await fs2.mkdir(targetDir, { recursive: true });
|
|
42201
43066
|
const git = this.createGit();
|
|
43067
|
+
const cloneUrl = this.auth ? this.transportUrl : repoUrl;
|
|
42202
43068
|
try {
|
|
42203
|
-
await this.
|
|
43069
|
+
await this.performClone(git, cloneUrl, targetDir, ref, sparsePath);
|
|
42204
43070
|
} catch (err) {
|
|
42205
43071
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
42206
43072
|
if (isGitTimeoutError(error)) {
|
|
@@ -42208,24 +43074,15 @@ var GitRegistry = class {
|
|
|
42208
43074
|
}
|
|
42209
43075
|
if (this.isAccessError(error) && fallbackRepoUrl) {
|
|
42210
43076
|
try {
|
|
42211
|
-
await this.
|
|
43077
|
+
await this.performClone(git, fallbackRepoUrl, targetDir, ref, sparsePath);
|
|
42212
43078
|
return;
|
|
42213
43079
|
} catch (fallbackErr) {
|
|
42214
|
-
|
|
42215
|
-
|
|
42216
|
-
throw createGitTimeoutError(fallbackRepoUrl, this.timeout, fbError);
|
|
42217
|
-
}
|
|
42218
|
-
if (this.isAccessError(fbError)) {
|
|
42219
|
-
throw new GitAuthError(
|
|
42220
|
-
`Authentication failed for both ${repoUrl} and fallback ${fallbackRepoUrl}`,
|
|
42221
|
-
fallbackRepoUrl,
|
|
42222
|
-
fbError
|
|
42223
|
-
);
|
|
42224
|
-
}
|
|
42225
|
-
throw new GitCloneError(
|
|
42226
|
-
`Failed to sparse-clone ${fallbackRepoUrl} at ${ref}: ${fbError.message}`,
|
|
43080
|
+
throw this.classifyFallbackCloneError(
|
|
43081
|
+
repoUrl,
|
|
42227
43082
|
fallbackRepoUrl,
|
|
42228
|
-
|
|
43083
|
+
fallbackErr,
|
|
43084
|
+
ref,
|
|
43085
|
+
"sparse-clone"
|
|
42229
43086
|
);
|
|
42230
43087
|
}
|
|
42231
43088
|
}
|
|
@@ -42243,22 +43100,26 @@ var GitRegistry = class {
|
|
|
42243
43100
|
}
|
|
42244
43101
|
}
|
|
42245
43102
|
/**
|
|
42246
|
-
*
|
|
43103
|
+
* Classify an error from the fallback clone attempt shared by
|
|
43104
|
+
* `cloneAtTag` and `cloneSparse`: timeout, auth, or a plain clone failure.
|
|
42247
43105
|
*/
|
|
42248
|
-
|
|
42249
|
-
|
|
42250
|
-
|
|
43106
|
+
classifyFallbackCloneError(repoUrl, fallbackRepoUrl, err, ref, action) {
|
|
43107
|
+
const fbError = err instanceof Error ? err : new Error(String(err));
|
|
43108
|
+
if (isGitTimeoutError(fbError)) {
|
|
43109
|
+
return createGitTimeoutError(fallbackRepoUrl, this.timeout, fbError);
|
|
43110
|
+
}
|
|
43111
|
+
if (this.isAccessError(fbError)) {
|
|
43112
|
+
return new GitAuthError(
|
|
43113
|
+
`Authentication failed for both ${repoUrl} and fallback ${fallbackRepoUrl}`,
|
|
43114
|
+
fallbackRepoUrl,
|
|
43115
|
+
fbError
|
|
43116
|
+
);
|
|
42251
43117
|
}
|
|
42252
|
-
|
|
42253
|
-
|
|
42254
|
-
|
|
42255
|
-
|
|
42256
|
-
|
|
42257
|
-
"--filter=blob:none",
|
|
42258
|
-
"--sparse"
|
|
42259
|
-
]);
|
|
42260
|
-
const repoGit = this.createGit(targetDir);
|
|
42261
|
-
await repoGit.raw(["sparse-checkout", "set", sparsePath]);
|
|
43118
|
+
return new GitCloneError(
|
|
43119
|
+
`Failed to ${action} ${fallbackRepoUrl} at ${ref ?? "default branch"}: ${fbError.message}`,
|
|
43120
|
+
fallbackRepoUrl,
|
|
43121
|
+
fbError
|
|
43122
|
+
);
|
|
42262
43123
|
}
|
|
42263
43124
|
/**
|
|
42264
43125
|
* Ensure the repository is cloned and at the correct ref.
|
|
@@ -42657,7 +43518,7 @@ function createGitTimeoutError(url, timeout, cause) {
|
|
|
42657
43518
|
return cause;
|
|
42658
43519
|
}
|
|
42659
43520
|
return new GitCloneError(
|
|
42660
|
-
`Git operation timed out after ${timeout}ms while contacting ${url}
|
|
43521
|
+
`Git operation timed out after ${timeout}ms while contacting ${url}. Raise the limit with the 'timeout' field on the registry entry or the ${GIT_TIMEOUT_ENV_VAR} environment variable.`,
|
|
42661
43522
|
url,
|
|
42662
43523
|
cause
|
|
42663
43524
|
);
|
|
@@ -43069,7 +43930,7 @@ async function discoverNativeContent(dir, logger) {
|
|
|
43069
43930
|
}
|
|
43070
43931
|
}
|
|
43071
43932
|
if (Object.keys(mergedSkills).length > 0) {
|
|
43072
|
-
blocks.push(makeBlock("skills", makeObjectContent(mergedSkills)));
|
|
43933
|
+
blocks.push(makeBlock("skills", makeObjectContent(mergedSkills, dir), dir));
|
|
43073
43934
|
}
|
|
43074
43935
|
const rootAgents = await discoverAgents(dir) ?? {};
|
|
43075
43936
|
const wrappedAgents = {};
|
|
@@ -43082,7 +43943,7 @@ async function discoverNativeContent(dir, logger) {
|
|
|
43082
43943
|
}
|
|
43083
43944
|
const mergedAgents = { ...wrappedAgents, ...rootAgents };
|
|
43084
43945
|
if (Object.keys(mergedAgents).length > 0) {
|
|
43085
|
-
blocks.push(makeBlock("agents", makeObjectContent(mergedAgents)));
|
|
43946
|
+
blocks.push(makeBlock("agents", makeObjectContent(mergedAgents, dir), dir));
|
|
43086
43947
|
}
|
|
43087
43948
|
const rootCommands = await discoverCommands(dir) ?? {};
|
|
43088
43949
|
const wrappedCommands = {};
|
|
@@ -43095,11 +43956,11 @@ async function discoverNativeContent(dir, logger) {
|
|
|
43095
43956
|
}
|
|
43096
43957
|
const mergedCommands = { ...wrappedCommands, ...rootCommands };
|
|
43097
43958
|
if (Object.keys(mergedCommands).length > 0) {
|
|
43098
|
-
blocks.push(makeBlock("shortcuts", makeObjectContent(mergedCommands)));
|
|
43959
|
+
blocks.push(makeBlock("shortcuts", makeObjectContent(mergedCommands, dir), dir));
|
|
43099
43960
|
}
|
|
43100
43961
|
const contextContent = await discoverContext(dir);
|
|
43101
43962
|
if (contextContent) {
|
|
43102
|
-
blocks.push(makeBlock("context", contextContent));
|
|
43963
|
+
blocks.push(makeBlock("context", contextContent, contextContent.loc.file));
|
|
43103
43964
|
}
|
|
43104
43965
|
if (blocks.length === 0) {
|
|
43105
43966
|
return null;
|
|
@@ -43109,7 +43970,7 @@ async function discoverNativeContent(dir, logger) {
|
|
|
43109
43970
|
blocks,
|
|
43110
43971
|
uses: [],
|
|
43111
43972
|
extends: [],
|
|
43112
|
-
loc:
|
|
43973
|
+
loc: fileLoc(dir)
|
|
43113
43974
|
};
|
|
43114
43975
|
return program2;
|
|
43115
43976
|
}
|
|
@@ -43156,6 +44017,37 @@ async function readBoundedRegularFile(filePath, maxBytes, description) {
|
|
|
43156
44017
|
await handle.close();
|
|
43157
44018
|
}
|
|
43158
44019
|
}
|
|
44020
|
+
async function readOptionalBoundedRegularFile(filePath, description) {
|
|
44021
|
+
try {
|
|
44022
|
+
return await readBoundedRegularFile(filePath, MAX_GIT_CONFIG_BYTES, description);
|
|
44023
|
+
} catch (error) {
|
|
44024
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
44025
|
+
return Buffer.alloc(0);
|
|
44026
|
+
}
|
|
44027
|
+
throw error;
|
|
44028
|
+
}
|
|
44029
|
+
}
|
|
44030
|
+
async function readGitBooleanConfig(filePath, key, description) {
|
|
44031
|
+
try {
|
|
44032
|
+
const result = await execFileAsync(
|
|
44033
|
+
"git",
|
|
44034
|
+
["config", "--no-includes", "--file", filePath, "--bool", "--get", key],
|
|
44035
|
+
{
|
|
44036
|
+
env: {
|
|
44037
|
+
PATH: process.env["PATH"],
|
|
44038
|
+
GIT_CONFIG_GLOBAL: NULL_DEVICE,
|
|
44039
|
+
GIT_CONFIG_NOSYSTEM: "1"
|
|
44040
|
+
}
|
|
44041
|
+
}
|
|
44042
|
+
);
|
|
44043
|
+
return result.stdout.trim() === "true";
|
|
44044
|
+
} catch (error) {
|
|
44045
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === 1) {
|
|
44046
|
+
return false;
|
|
44047
|
+
}
|
|
44048
|
+
throw new Error(`Invalid ${description}: ${filePath}`, { cause: error });
|
|
44049
|
+
}
|
|
44050
|
+
}
|
|
43159
44051
|
async function resolveSafeRepositorySymlink(symlinkPath, repositoryRoot, gitMetadataRoot) {
|
|
43160
44052
|
let targetPath;
|
|
43161
44053
|
try {
|
|
@@ -43249,7 +44141,7 @@ async function hashVendorRepository(directory) {
|
|
|
43249
44141
|
await visit(directory, "");
|
|
43250
44142
|
return `sha256-${hash.digest("hex")}`;
|
|
43251
44143
|
}
|
|
43252
|
-
async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expectedCommit, allowedUntrackedFiles = /* @__PURE__ */ new Set()) {
|
|
44144
|
+
async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expectedCommit, allowedUntrackedFiles = /* @__PURE__ */ new Set(), options = {}) {
|
|
43253
44145
|
const gitDir = join9(directory, gitDirectoryName);
|
|
43254
44146
|
async function rejectMetadataSymlinks(currentDirectory) {
|
|
43255
44147
|
for (const entry of await readdir4(currentDirectory, { withFileTypes: true })) {
|
|
@@ -43287,8 +44179,23 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
|
|
|
43287
44179
|
"Vendor Git config"
|
|
43288
44180
|
);
|
|
43289
44181
|
const localConfig = localConfigContent.toString("utf-8");
|
|
43290
|
-
|
|
43291
|
-
|
|
44182
|
+
const externalGitSourcesError = () => `External or partial Git object sources are not allowed: ${gitDir}. ` + (options.allowPartial ? `Delete the checkout directory ${directory} and re-run this command to re-clone it.` : `Run 'prs vendor sync' to re-vendor the dependency, then re-run this command.`);
|
|
44183
|
+
if (/^[ \t]*\[(?:include|includeif)\b/im.test(localConfig)) {
|
|
44184
|
+
throw new Error(externalGitSourcesError());
|
|
44185
|
+
}
|
|
44186
|
+
const usesWorktreeConfig = await readGitBooleanConfig(
|
|
44187
|
+
join9(gitDir, "config"),
|
|
44188
|
+
"extensions.worktreeConfig",
|
|
44189
|
+
"Git worktree config extension"
|
|
44190
|
+
);
|
|
44191
|
+
if (!options.allowPartial && (usesWorktreeConfig || /\bpromisor\s*=\s*true\b/i.test(localConfig) || /\bpartialclone/i.test(localConfig))) {
|
|
44192
|
+
throw new Error(externalGitSourcesError());
|
|
44193
|
+
}
|
|
44194
|
+
if (options.allowPartial && usesWorktreeConfig) {
|
|
44195
|
+
const worktreeConfig = (await readOptionalBoundedRegularFile(join9(gitDir, "config.worktree"), "Git worktree config")).toString("utf-8");
|
|
44196
|
+
if (/^[ \t]*\[(?:include|includeif)\b/im.test(worktreeConfig)) {
|
|
44197
|
+
throw new Error(externalGitSourcesError());
|
|
44198
|
+
}
|
|
43292
44199
|
}
|
|
43293
44200
|
await rejectMetadataPath("commondir", "Git common directories are not allowed");
|
|
43294
44201
|
await rejectMetadataPath(
|
|
@@ -43458,7 +44365,7 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
|
|
|
43458
44365
|
}
|
|
43459
44366
|
}
|
|
43460
44367
|
await collectFiles(directory, "");
|
|
43461
|
-
if (worktreeFileCount !== trackedFiles.size || regularFiles.some((path2) => !trackedFiles.has(path2))) {
|
|
44368
|
+
if (!options.allowPartial && worktreeFileCount !== trackedFiles.size || regularFiles.some((path2) => !trackedFiles.has(path2))) {
|
|
43462
44369
|
throw new Error(`Vendored repository contents do not match commit ${expectedCommit}`);
|
|
43463
44370
|
}
|
|
43464
44371
|
const batchSize = 100;
|
|
@@ -43742,7 +44649,8 @@ var Resolver = class {
|
|
|
43742
44649
|
cacheEnabled;
|
|
43743
44650
|
logger;
|
|
43744
44651
|
options;
|
|
43745
|
-
|
|
44652
|
+
/** Lazy per-repository Git registries (carry per-entry timeout settings). */
|
|
44653
|
+
gitRegistries = /* @__PURE__ */ new Map();
|
|
43746
44654
|
registryCache;
|
|
43747
44655
|
constructor(options) {
|
|
43748
44656
|
this.options = options;
|
|
@@ -43750,7 +44658,6 @@ var Resolver = class {
|
|
|
43750
44658
|
this.cache = /* @__PURE__ */ new Map();
|
|
43751
44659
|
this.cacheEnabled = options.cache !== false;
|
|
43752
44660
|
this.logger = options.logger ?? noopLogger;
|
|
43753
|
-
this.gitRegistry = new GitRegistry({ url: "https://github.com/placeholder/placeholder.git" });
|
|
43754
44661
|
const defaultCacheDir = join10(
|
|
43755
44662
|
process.env["HOME"] ?? process.env["USERPROFILE"] ?? "/tmp",
|
|
43756
44663
|
".promptscript",
|
|
@@ -43758,6 +44665,54 @@ var Resolver = class {
|
|
|
43758
44665
|
);
|
|
43759
44666
|
this.registryCache = new RegistryCache(options.cacheDir ?? defaultCacheDir);
|
|
43760
44667
|
}
|
|
44668
|
+
/**
|
|
44669
|
+
* Get (or lazily create) the Git registry for a repository URL.
|
|
44670
|
+
*
|
|
44671
|
+
* Per-repository instances carry the `timeout` from the matching
|
|
44672
|
+
* registries config entry, so large monorepo registries can raise the
|
|
44673
|
+
* clone timeout per entry (see issue #455). Falls back to
|
|
44674
|
+
* PROMPTSCRIPT_GIT_TIMEOUT / the 60s default inside GitRegistry.
|
|
44675
|
+
*/
|
|
44676
|
+
getGitRegistry(repoUrl) {
|
|
44677
|
+
const cached = this.gitRegistries.get(repoUrl);
|
|
44678
|
+
if (cached) {
|
|
44679
|
+
return cached;
|
|
44680
|
+
}
|
|
44681
|
+
const entry = this.options.registries ? findRegistryEntry(repoUrl, this.options.registries) : void 0;
|
|
44682
|
+
const registry2 = new GitRegistry({
|
|
44683
|
+
url: repoUrl,
|
|
44684
|
+
...entry?.timeout !== void 0 ? { timeout: entry.timeout } : {}
|
|
44685
|
+
});
|
|
44686
|
+
this.gitRegistries.set(repoUrl, registry2);
|
|
44687
|
+
return registry2;
|
|
44688
|
+
}
|
|
44689
|
+
/**
|
|
44690
|
+
* Clone a registry repository into its cache path. Uses the per-entry
|
|
44691
|
+
* Git registry (so the configured timeout applies) and the fallback URL
|
|
44692
|
+
* from the lockfile or the registries config.
|
|
44693
|
+
*/
|
|
44694
|
+
async cloneRegistryIntoCache(repoUrl, cachePath, tag, sparseCone, lockEntry) {
|
|
44695
|
+
const gitRegistry = this.getGitRegistry(repoUrl);
|
|
44696
|
+
const fallbackRepoUrl = lockEntry?.gitUrl ?? (this.options.registries ? findFallbackUrl(repoUrl, this.options.registries) : void 0);
|
|
44697
|
+
await gitRegistry.cloneAtTag(repoUrl, tag, cachePath, fallbackRepoUrl, sparseCone);
|
|
44698
|
+
return gitRegistry;
|
|
44699
|
+
}
|
|
44700
|
+
/**
|
|
44701
|
+
* Verify that a cached registry checkout matches a locked commit.
|
|
44702
|
+
* Cache clones may be partial (sparse cones), so partial-clone metadata
|
|
44703
|
+
* and missing out-of-cone files are allowed.
|
|
44704
|
+
*/
|
|
44705
|
+
async verifyCacheCommit(cachePath, commit) {
|
|
44706
|
+
await verifyGitRepositoryCheckout(
|
|
44707
|
+
cachePath,
|
|
44708
|
+
".git",
|
|
44709
|
+
commit,
|
|
44710
|
+
/* @__PURE__ */ new Set([".prs-registry-meta.json"]),
|
|
44711
|
+
{
|
|
44712
|
+
allowPartial: true
|
|
44713
|
+
}
|
|
44714
|
+
);
|
|
44715
|
+
}
|
|
43761
44716
|
/**
|
|
43762
44717
|
* Resolve a PromptScript file and all its dependencies.
|
|
43763
44718
|
*
|
|
@@ -44368,10 +45323,12 @@ var Resolver = class {
|
|
|
44368
45323
|
}
|
|
44369
45324
|
const program2 = {
|
|
44370
45325
|
type: "Program",
|
|
44371
|
-
blocks: [
|
|
45326
|
+
blocks: [
|
|
45327
|
+
makeBlock("skills", makeObjectContent({ [skillName]: skillProps }, absPath), absPath)
|
|
45328
|
+
],
|
|
44372
45329
|
uses: [],
|
|
44373
45330
|
extends: [],
|
|
44374
|
-
loc:
|
|
45331
|
+
loc: fileLoc(absPath)
|
|
44375
45332
|
};
|
|
44376
45333
|
return { ast: program2 };
|
|
44377
45334
|
}
|
|
@@ -44446,7 +45403,8 @@ var Resolver = class {
|
|
|
44446
45403
|
}
|
|
44447
45404
|
errors.push(
|
|
44448
45405
|
new ResolveError(
|
|
44449
|
-
`Failed to resolve parent: ${err instanceof Error ? err.message : String(err)}
|
|
45406
|
+
`Failed to resolve parent: ${err instanceof Error ? err.message : String(err)}`,
|
|
45407
|
+
err instanceof ResolveError ? err.location : void 0
|
|
44450
45408
|
)
|
|
44451
45409
|
);
|
|
44452
45410
|
}
|
|
@@ -44545,7 +45503,8 @@ var Resolver = class {
|
|
|
44545
45503
|
}
|
|
44546
45504
|
errors.push(
|
|
44547
45505
|
new ResolveError(
|
|
44548
|
-
`Failed to resolve import: ${err instanceof Error ? err.message : String(err)}
|
|
45506
|
+
`Failed to resolve import: ${err instanceof Error ? err.message : String(err)}`,
|
|
45507
|
+
err instanceof ResolveError ? err.location : void 0
|
|
44549
45508
|
)
|
|
44550
45509
|
);
|
|
44551
45510
|
}
|
|
@@ -44681,10 +45640,14 @@ var Resolver = class {
|
|
|
44681
45640
|
const markerCommit = version && /^[0-9a-f]{40}$/i.test(version) && !/^0{40}$/.test(version) ? version : null;
|
|
44682
45641
|
const lockedCommit = lockfileCommit ?? markerCommit;
|
|
44683
45642
|
if (this.options.lockfile && !lockfileCommit) {
|
|
44684
|
-
throw new Error(
|
|
45643
|
+
throw new Error(
|
|
45644
|
+
`Remote dependency is not pinned by the lockfile: ${repoUrl}. Run 'prs lock' to pin it, then re-run this command.`
|
|
45645
|
+
);
|
|
44685
45646
|
}
|
|
44686
45647
|
const requestedRef = lockEntry?.version ?? (version || void 0);
|
|
44687
45648
|
const tag = requestedRef && requestedRef !== "latest" && !/^[0-9a-f]{40}$/i.test(requestedRef) ? requestedRef : void 0;
|
|
45649
|
+
const subPathLastSlash = subPath.lastIndexOf("/");
|
|
45650
|
+
const sparseCone = subPathLastSlash > 0 ? subPath.slice(0, subPathLastSlash) : void 0;
|
|
44688
45651
|
let cachePath;
|
|
44689
45652
|
let vendoredPath = null;
|
|
44690
45653
|
const vendorManifest = this.options.vendorDir ? await loadVendorManifest(this.options.vendorDir) : null;
|
|
@@ -44693,7 +45656,9 @@ var Resolver = class {
|
|
|
44693
45656
|
}
|
|
44694
45657
|
if (this.options.vendorDir && vendorManifest) {
|
|
44695
45658
|
if (!lockEntry || !lockedCommit) {
|
|
44696
|
-
throw new Error(
|
|
45659
|
+
throw new Error(
|
|
45660
|
+
`Vendored dependency is not pinned by the lockfile: ${repoUrl}. Run 'prs lock' to pin it, then re-run this command.`
|
|
45661
|
+
);
|
|
44697
45662
|
}
|
|
44698
45663
|
vendoredPath = await resolveVendoredRepository(
|
|
44699
45664
|
this.options.vendorDir,
|
|
@@ -44711,12 +45676,7 @@ var Resolver = class {
|
|
|
44711
45676
|
if (lockedCommit) {
|
|
44712
45677
|
let cacheMatchesLock = false;
|
|
44713
45678
|
try {
|
|
44714
|
-
await
|
|
44715
|
-
cachePath,
|
|
44716
|
-
".git",
|
|
44717
|
-
lockedCommit,
|
|
44718
|
-
/* @__PURE__ */ new Set([".prs-registry-meta.json"])
|
|
44719
|
-
);
|
|
45679
|
+
await this.verifyCacheCommit(cachePath, lockedCommit);
|
|
44720
45680
|
cacheMatchesLock = true;
|
|
44721
45681
|
} catch {
|
|
44722
45682
|
cacheMatchesLock = false;
|
|
@@ -44730,17 +45690,16 @@ var Resolver = class {
|
|
|
44730
45690
|
this.logger.verbose(
|
|
44731
45691
|
`Registry cache does not match locked commit for ${repoUrl}. Re-cloning.`
|
|
44732
45692
|
);
|
|
44733
|
-
const
|
|
44734
|
-
|
|
44735
|
-
await this.gitRegistry.cloneAtTag(cloneRepoUrl, tag, cachePath, fallbackRepoUrl);
|
|
44736
|
-
await this.gitRegistry.checkoutCommit(cachePath, lockedCommit);
|
|
44737
|
-
await this.registryCache.set(repoUrl, effectiveVersion, lockedCommit);
|
|
44738
|
-
await verifyGitRepositoryCheckout(
|
|
45693
|
+
const gitRegistry = await this.cloneRegistryIntoCache(
|
|
45694
|
+
repoUrl,
|
|
44739
45695
|
cachePath,
|
|
44740
|
-
|
|
44741
|
-
|
|
44742
|
-
|
|
45696
|
+
tag,
|
|
45697
|
+
sparseCone,
|
|
45698
|
+
lockEntry
|
|
44743
45699
|
);
|
|
45700
|
+
await gitRegistry.checkoutCommit(cachePath, lockedCommit);
|
|
45701
|
+
await this.registryCache.set(repoUrl, effectiveVersion, lockedCommit);
|
|
45702
|
+
await this.verifyCacheCommit(cachePath, lockedCommit);
|
|
44744
45703
|
}
|
|
44745
45704
|
}
|
|
44746
45705
|
} else {
|
|
@@ -44751,22 +45710,21 @@ var Resolver = class {
|
|
|
44751
45710
|
}
|
|
44752
45711
|
this.logger.verbose(`Registry cache miss, cloning: ${repoUrl}@${tag ?? "default"}`);
|
|
44753
45712
|
cachePath = this.registryCache.getCachePath(repoUrl, effectiveVersion);
|
|
44754
|
-
const
|
|
44755
|
-
|
|
44756
|
-
|
|
45713
|
+
const gitRegistry = await this.cloneRegistryIntoCache(
|
|
45714
|
+
repoUrl,
|
|
45715
|
+
cachePath,
|
|
45716
|
+
tag,
|
|
45717
|
+
sparseCone,
|
|
45718
|
+
lockEntry
|
|
45719
|
+
);
|
|
44757
45720
|
if (lockedCommit) {
|
|
44758
45721
|
this.logger.verbose(`Checking out locked commit: ${lockedCommit}`);
|
|
44759
|
-
await
|
|
45722
|
+
await gitRegistry.checkoutCommit(cachePath, lockedCommit);
|
|
44760
45723
|
}
|
|
44761
45724
|
const commitHash = lockedCommit ?? lockEntry?.commit ?? "unknown";
|
|
44762
45725
|
await this.registryCache.set(repoUrl, effectiveVersion, commitHash);
|
|
44763
45726
|
if (lockedCommit) {
|
|
44764
|
-
await
|
|
44765
|
-
cachePath,
|
|
44766
|
-
".git",
|
|
44767
|
-
lockedCommit,
|
|
44768
|
-
/* @__PURE__ */ new Set([".prs-registry-meta.json"])
|
|
44769
|
-
);
|
|
45727
|
+
await this.verifyCacheCommit(cachePath, lockedCommit);
|
|
44770
45728
|
}
|
|
44771
45729
|
}
|
|
44772
45730
|
dependencies.add(cachePath);
|
|
@@ -44807,6 +45765,26 @@ var Resolver = class {
|
|
|
44807
45765
|
};
|
|
44808
45766
|
}
|
|
44809
45767
|
}
|
|
45768
|
+
if (!this.options.readOnly && existsSync9(cachePath) && existsSync9(join10(cachePath, ".git")) && !vendoredPath) {
|
|
45769
|
+
const gitRegistry = this.getGitRegistry(repoUrl);
|
|
45770
|
+
if (await gitRegistry.isSparseCheckout(cachePath)) {
|
|
45771
|
+
const fileMissing = !isRoot && !existsSync9(resolvedFullPath);
|
|
45772
|
+
const dirMissing = !isRoot && !existsSync9(join10(cachePath, subPath));
|
|
45773
|
+
if (isRoot) {
|
|
45774
|
+
this.logger.verbose(`Materializing full checkout for root import: ${repoUrl}`);
|
|
45775
|
+
await gitRegistry.disableSparseCheckout(cachePath);
|
|
45776
|
+
} else if (fileMissing || dirMissing) {
|
|
45777
|
+
this.logger.verbose(`Widening sparse checkout for ${repoUrl}: ${subPath}`);
|
|
45778
|
+
const paths = /* @__PURE__ */ new Set();
|
|
45779
|
+
if (sparseCone) {
|
|
45780
|
+
paths.add(sparseCone);
|
|
45781
|
+
} else if (!isMdPath && !subPath.endsWith(".prs")) {
|
|
45782
|
+
paths.add(subPath);
|
|
45783
|
+
}
|
|
45784
|
+
await gitRegistry.addSparsePaths(cachePath, [...paths]);
|
|
45785
|
+
}
|
|
45786
|
+
}
|
|
45787
|
+
}
|
|
44810
45788
|
let resolvedAST = null;
|
|
44811
45789
|
if (!isRoot && existsSync9(resolvedFullPath) && isMdPath) {
|
|
44812
45790
|
this.logger.debug(`Found .md file: ${resolvedFullPath}`);
|
|
@@ -44935,10 +45913,10 @@ var Resolver = class {
|
|
|
44935
45913
|
this.logger.debug(`Found ${Object.keys(properties).length} skill(s) in directory: ${dirPath}`);
|
|
44936
45914
|
const program2 = {
|
|
44937
45915
|
type: "Program",
|
|
44938
|
-
blocks: [makeBlock("skills", makeObjectContent(properties))],
|
|
45916
|
+
blocks: [makeBlock("skills", makeObjectContent(properties, dirPath), dirPath)],
|
|
44939
45917
|
uses: [],
|
|
44940
45918
|
extends: [],
|
|
44941
|
-
loc:
|
|
45919
|
+
loc: fileLoc(dirPath)
|
|
44942
45920
|
};
|
|
44943
45921
|
return { ast: program2 };
|
|
44944
45922
|
}
|
|
@@ -45303,7 +46281,7 @@ var HttpRegistry = class {
|
|
|
45303
46281
|
* Sleep for specified milliseconds.
|
|
45304
46282
|
*/
|
|
45305
46283
|
sleep(ms) {
|
|
45306
|
-
return new Promise((
|
|
46284
|
+
return new Promise((resolve43) => setTimeout(resolve43, ms));
|
|
45307
46285
|
}
|
|
45308
46286
|
/**
|
|
45309
46287
|
* Fetch with timeout.
|
|
@@ -45448,6 +46426,7 @@ function createHttpRegistry(options) {
|
|
|
45448
46426
|
init_src();
|
|
45449
46427
|
|
|
45450
46428
|
// packages/resolver/src/skill-validation.ts
|
|
46429
|
+
init_src();
|
|
45451
46430
|
import { basename as basename12, dirname as dirname11, join as join12 } from "path";
|
|
45452
46431
|
import { existsSync as existsSync11 } from "fs";
|
|
45453
46432
|
function validateSkillFrontmatter(rawContent, options = {}) {
|
|
@@ -45464,11 +46443,15 @@ function validateSkillFrontmatter(rawContent, options = {}) {
|
|
|
45464
46443
|
try {
|
|
45465
46444
|
parsed = parseSkillMd(rawContent, options.filePath);
|
|
45466
46445
|
} catch (error) {
|
|
45467
|
-
|
|
46446
|
+
const issue = {
|
|
45468
46447
|
severity: "error",
|
|
45469
46448
|
code: "SK000",
|
|
45470
46449
|
message: error instanceof Error ? error.message : String(error)
|
|
45471
|
-
}
|
|
46450
|
+
};
|
|
46451
|
+
if (error instanceof ResolveError && error.location) {
|
|
46452
|
+
issue.location = error.location;
|
|
46453
|
+
}
|
|
46454
|
+
issues.push(issue);
|
|
45472
46455
|
return { valid: false, issues };
|
|
45473
46456
|
}
|
|
45474
46457
|
validateName(parsed, options, issues);
|
|
@@ -45648,7 +46631,8 @@ function formatSkillValidationIssues(issues) {
|
|
|
45648
46631
|
return issues.map((i) => {
|
|
45649
46632
|
const tag = i.severity === "error" ? "\u2717" : "\u26A0";
|
|
45650
46633
|
const field = i.field ? ` [${i.field}]` : "";
|
|
45651
|
-
|
|
46634
|
+
const location = i.location ? ` (at ${i.location.file}:${i.location.line}:${i.location.column})` : "";
|
|
46635
|
+
return ` ${tag} ${i.code}${field}: ${i.message}${location}`;
|
|
45652
46636
|
}).join("\n");
|
|
45653
46637
|
}
|
|
45654
46638
|
|
|
@@ -47388,6 +48372,19 @@ var authorityInjection = {
|
|
|
47388
48372
|
}
|
|
47389
48373
|
};
|
|
47390
48374
|
|
|
48375
|
+
// packages/validator/src/external-content.ts
|
|
48376
|
+
import { isAbsolute as isAbsolute8, relative as relative9, sep as sep6 } from "node:path";
|
|
48377
|
+
function isPathInside(root, candidate) {
|
|
48378
|
+
const relation = relative9(root, candidate);
|
|
48379
|
+
return relation === "" || relation !== ".." && !relation.startsWith(`..${sep6}`) && !isAbsolute8(relation);
|
|
48380
|
+
}
|
|
48381
|
+
function isExternalLocation(loc, config) {
|
|
48382
|
+
if (config.scanExternalContent || !loc) return false;
|
|
48383
|
+
const roots = config.externalRoots;
|
|
48384
|
+
if (!roots || roots.length === 0) return false;
|
|
48385
|
+
return roots.some((root) => isPathInside(root, loc.file));
|
|
48386
|
+
}
|
|
48387
|
+
|
|
47391
48388
|
// packages/validator/src/rules/obfuscated-content.ts
|
|
47392
48389
|
var MIN_ENCODED_LENGTH = 16;
|
|
47393
48390
|
var DECODED_CONTENT_SECURITY_PATTERNS = [
|
|
@@ -47568,6 +48565,19 @@ function isLikelyTechnicalContent(match2) {
|
|
|
47568
48565
|
}
|
|
47569
48566
|
return false;
|
|
47570
48567
|
}
|
|
48568
|
+
var NUCLEOTIDE_CHARS = new Set("ACGTUN");
|
|
48569
|
+
var AMINO_ACID_CHARS = new Set("ACDEFGHIKLMNPQRSTVWYBXZJOU");
|
|
48570
|
+
var BIO_CONTEXT_PATTERN = /\b(?:protein|peptide|amino[-\s]?acid|residue|sequence|nucleotide|genome|transcript|fasta|uniprot)\b/i;
|
|
48571
|
+
function isOverAlphabet(match2, alphabet) {
|
|
48572
|
+
for (const char of match2) {
|
|
48573
|
+
if (!alphabet.has(char)) return false;
|
|
48574
|
+
}
|
|
48575
|
+
return match2.length > 0;
|
|
48576
|
+
}
|
|
48577
|
+
function isLikelyBioSequence(text, match2) {
|
|
48578
|
+
if (isOverAlphabet(match2, NUCLEOTIDE_CHARS)) return true;
|
|
48579
|
+
return isOverAlphabet(match2, AMINO_ACID_CHARS) && BIO_CONTEXT_PATTERN.test(text);
|
|
48580
|
+
}
|
|
47571
48581
|
function isLikelyLegitimateBase64(text, match2) {
|
|
47572
48582
|
const matchIndex = text.indexOf(match2);
|
|
47573
48583
|
if (matchIndex === -1) return false;
|
|
@@ -47576,6 +48586,7 @@ function isLikelyLegitimateBase64(text, match2) {
|
|
|
47576
48586
|
if (precedingText.includes("data:application/json")) return true;
|
|
47577
48587
|
if (precedingText.includes("data:text/")) return true;
|
|
47578
48588
|
if (isLikelyTechnicalContent(match2)) return true;
|
|
48589
|
+
if (isLikelyBioSequence(text, match2)) return true;
|
|
47579
48590
|
return false;
|
|
47580
48591
|
}
|
|
47581
48592
|
function isLikelyHashOrUuid(hex) {
|
|
@@ -47784,12 +48795,16 @@ var obfuscatedContent = {
|
|
|
47784
48795
|
suggestion: "Encoded content is automatically decoded and checked for security patterns. Remove the malicious payload or use plain text."
|
|
47785
48796
|
});
|
|
47786
48797
|
}
|
|
47787
|
-
|
|
48798
|
+
if (isExternalLocation(loc, ctx.config)) {
|
|
48799
|
+
return;
|
|
48800
|
+
}
|
|
48801
|
+
const longBase64Match = (text.match(BASE64_PATTERN) || []).find(
|
|
47788
48802
|
(m) => m.length >= MIN_ENCODED_LENGTH * 2 && !isLikelyLegitimateBase64(text, m)
|
|
47789
48803
|
);
|
|
47790
|
-
if (
|
|
48804
|
+
if (longBase64Match && encodedMatches.length === 0) {
|
|
48805
|
+
const snippet = longBase64Match.length > 20 ? `${longBase64Match.substring(0, 20)}...` : longBase64Match;
|
|
47791
48806
|
ctx.report({
|
|
47792
|
-
message:
|
|
48807
|
+
message: `Long Base64-encoded content detected that may hide payloads (starts with "${snippet}")`,
|
|
47793
48808
|
location: loc,
|
|
47794
48809
|
suggestion: "If this is intentional, consider using plain text or documenting the purpose"
|
|
47795
48810
|
});
|
|
@@ -48697,11 +49712,14 @@ var safeReferenceContent = {
|
|
|
48697
49712
|
const content = res["content"];
|
|
48698
49713
|
if (typeof relPath !== "string" || typeof content !== "string") continue;
|
|
48699
49714
|
if (!relPath.startsWith("references/") && !relPath.includes("/references/")) continue;
|
|
49715
|
+
const origin = res["origin"];
|
|
49716
|
+
const location = typeof origin === "string" ? { file: origin, line: 1, column: 1, offset: 0 } : skillsBlock.loc;
|
|
49717
|
+
if (isExternalLocation(location, ctx.config)) continue;
|
|
48700
49718
|
const match2 = content.match(DIRECTIVE_PATTERN);
|
|
48701
49719
|
if (match2) {
|
|
48702
49720
|
ctx.report({
|
|
48703
49721
|
message: `Reference file "${relPath}" in skill "${skillName}" contains PRS directive '${match2[1]}' \u2014 references should contain data, not instructions`,
|
|
48704
|
-
location
|
|
49722
|
+
location,
|
|
48705
49723
|
suggestion: "Remove PRS directives from reference files. References are data files, not PromptScript source."
|
|
48706
49724
|
});
|
|
48707
49725
|
}
|
|
@@ -50512,18 +51530,18 @@ var SECURITY_STRICT_MULTILINGUAL = {
|
|
|
50512
51530
|
};
|
|
50513
51531
|
|
|
50514
51532
|
// packages/compiler/src/compiler.ts
|
|
50515
|
-
import { dirname as dirname13, isAbsolute as
|
|
51533
|
+
import { dirname as dirname13, isAbsolute as isAbsolute10, join as join13, relative as relative11, resolve as resolve15, sep as sep8 } from "path";
|
|
50516
51534
|
|
|
50517
51535
|
// packages/compiler/src/hook-script-validator.ts
|
|
50518
51536
|
init_src();
|
|
50519
51537
|
init_src4();
|
|
50520
51538
|
import { lstat as lstat7, realpath as realpath4 } from "fs/promises";
|
|
50521
51539
|
import { realpathSync, statSync } from "fs";
|
|
50522
|
-
import { dirname as dirname12, isAbsolute as
|
|
51540
|
+
import { dirname as dirname12, isAbsolute as isAbsolute9, relative as relative10, resolve as resolve14, sep as sep7 } from "path";
|
|
50523
51541
|
var PROJECT_MARKERS = [".promptscript", ".git", "package.json"];
|
|
50524
51542
|
function isInside2(root, candidate) {
|
|
50525
|
-
const relation =
|
|
50526
|
-
return relation === "" || !relation.startsWith(`..${
|
|
51543
|
+
const relation = relative10(root, candidate);
|
|
51544
|
+
return relation === "" || !relation.startsWith(`..${sep7}`) && relation !== ".." && !isAbsolute9(relation);
|
|
50527
51545
|
}
|
|
50528
51546
|
function isProjectMarker(directory, marker) {
|
|
50529
51547
|
const markerPath = resolve14(directory, marker);
|
|
@@ -50561,10 +51579,10 @@ function inferProjectRoot(localPath, configuredProjectRoot, entryPath) {
|
|
|
50561
51579
|
if (markedRoot) return markedRoot;
|
|
50562
51580
|
}
|
|
50563
51581
|
const local = localPath ? resolve14(localPath) : entryPath ? dirname12(resolve14(entryPath)) : process.cwd();
|
|
50564
|
-
const promptScriptDirectory = `${
|
|
50565
|
-
const nestedMarker = `${promptScriptDirectory}${
|
|
51582
|
+
const promptScriptDirectory = `${sep7}.promptscript`;
|
|
51583
|
+
const nestedMarker = `${promptScriptDirectory}${sep7}`;
|
|
50566
51584
|
const nestedIndex = local.indexOf(nestedMarker);
|
|
50567
|
-
if (nestedIndex >= 0) return local.slice(0, nestedIndex) ||
|
|
51585
|
+
if (nestedIndex >= 0) return local.slice(0, nestedIndex) || sep7;
|
|
50568
51586
|
return local.endsWith(promptScriptDirectory) ? dirname12(local) : local;
|
|
50569
51587
|
}
|
|
50570
51588
|
async function validateHookScriptResources(ast, projectRoot) {
|
|
@@ -50632,7 +51650,7 @@ function resolverCacheKey(projectRoot) {
|
|
|
50632
51650
|
function resolveEntryPath(entryPath) {
|
|
50633
51651
|
if (entryPath.startsWith("@")) return entryPath;
|
|
50634
51652
|
const fileName = entryPath.endsWith(".prs") || entryPath.endsWith(".md") ? entryPath : `${entryPath}.prs`;
|
|
50635
|
-
const absolutePath =
|
|
51653
|
+
const absolutePath = isAbsolute10(fileName) ? fileName : resolve15(process.cwd(), fileName);
|
|
50636
51654
|
try {
|
|
50637
51655
|
return realpathSync2.native(absolutePath);
|
|
50638
51656
|
} catch {
|
|
@@ -50686,7 +51704,7 @@ function normalizeOutputDir(dir) {
|
|
|
50686
51704
|
}
|
|
50687
51705
|
function matchesWatchPattern(path2, pattern, baseDir) {
|
|
50688
51706
|
const normalizedPath = path2.replace(/\\/g, "/");
|
|
50689
|
-
const relativePath =
|
|
51707
|
+
const relativePath = relative11(baseDir, path2).replace(/\\/g, "/");
|
|
50690
51708
|
const candidates = [relativePath, normalizedPath].flatMap((candidate) => [
|
|
50691
51709
|
candidate,
|
|
50692
51710
|
candidate.endsWith("/") ? candidate : `${candidate}/`
|
|
@@ -50698,7 +51716,7 @@ function matchesWatchPattern(path2, pattern, baseDir) {
|
|
|
50698
51716
|
return candidates.some((candidate) => minimatch(candidate, pattern, matcherOptions));
|
|
50699
51717
|
}
|
|
50700
51718
|
function absoluteWatchPath(path2, baseDir = process.cwd()) {
|
|
50701
|
-
return
|
|
51719
|
+
return isAbsolute10(path2) ? resolve15(path2) : resolve15(baseDir, path2);
|
|
50702
51720
|
}
|
|
50703
51721
|
function canonicalWatchPath(path2, baseDir = process.cwd()) {
|
|
50704
51722
|
const absolutePath = absoluteWatchPath(path2, baseDir);
|
|
@@ -50721,8 +51739,8 @@ function watchPathKey(path2, baseDir = process.cwd()) {
|
|
|
50721
51739
|
return normalizeWatchPath(canonicalWatchPath(path2, baseDir));
|
|
50722
51740
|
}
|
|
50723
51741
|
function isPathWithin(path2, root) {
|
|
50724
|
-
const relation =
|
|
50725
|
-
return relation === "" || relation !== ".." && !relation.startsWith(`..${
|
|
51742
|
+
const relation = relative11(normalizeWatchPath(root), normalizeWatchPath(path2));
|
|
51743
|
+
return relation === "" || relation !== ".." && !relation.startsWith(`..${sep8}`) && !isAbsolute10(relation);
|
|
50726
51744
|
}
|
|
50727
51745
|
function shouldIncludeSkill(name, config) {
|
|
50728
51746
|
const includeSkills = config?.includeSkills;
|
|
@@ -50877,7 +51895,7 @@ var Compiler = class _Compiler {
|
|
|
50877
51895
|
formatTime: 0,
|
|
50878
51896
|
totalTime: 0
|
|
50879
51897
|
};
|
|
50880
|
-
const sourceLabel =
|
|
51898
|
+
const sourceLabel = isAbsolute10(entryPath) ? relative11(process.cwd(), entryPath) : entryPath;
|
|
50881
51899
|
this.logger.verbose("=== Stage 1: Resolve ===");
|
|
50882
51900
|
const startResolve = Date.now();
|
|
50883
51901
|
let resolved;
|
|
@@ -50921,11 +51939,20 @@ var Compiler = class _Compiler {
|
|
|
50921
51939
|
};
|
|
50922
51940
|
}
|
|
50923
51941
|
const compileErrors = [];
|
|
51942
|
+
const externalRoots = [];
|
|
51943
|
+
const cacheDir = this.options.resolver.cacheDir ?? join13(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "/tmp", ".promptscript", "cache");
|
|
51944
|
+
externalRoots.push(cacheDir);
|
|
51945
|
+
if (this.options.resolver.vendorDir) {
|
|
51946
|
+
externalRoots.push(this.options.resolver.vendorDir);
|
|
51947
|
+
}
|
|
51948
|
+
for (const roots of Object.values(this.options.resolver.referenceRoots ?? {})) {
|
|
51949
|
+
externalRoots.push(...roots);
|
|
51950
|
+
}
|
|
51951
|
+
this.validator.updateConfig({ externalRoots });
|
|
50924
51952
|
if (!this.options.ignoreHashes && this.options.resolver.lockfile) {
|
|
50925
51953
|
this.logger.verbose("=== Stage 1.5: Reference Integrity ===");
|
|
50926
51954
|
const registryReferences = /* @__PURE__ */ new Set();
|
|
50927
51955
|
const registryReferencePaths = /* @__PURE__ */ new Map();
|
|
50928
|
-
const cacheDir = this.options.resolver.cacheDir ?? join13(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "/tmp", ".promptscript", "cache");
|
|
50929
51956
|
const registryCache = new RegistryCache(cacheDir);
|
|
50930
51957
|
const referenceRoots = [];
|
|
50931
51958
|
for (const [repoUrl, dependency] of Object.entries(
|
|
@@ -50964,8 +51991,8 @@ var Compiler = class _Compiler {
|
|
|
50964
51991
|
if (block.name !== "skills" || block.content.type !== "ObjectContent") continue;
|
|
50965
51992
|
const sourceFile = block.loc.file;
|
|
50966
51993
|
const referenceRoot = sourceFile ? referenceRoots.find((root) => {
|
|
50967
|
-
const relation =
|
|
50968
|
-
return relation === "" || relation !== ".." && !relation.startsWith(`..${
|
|
51994
|
+
const relation = relative11(resolve15(root.path), resolve15(sourceFile));
|
|
51995
|
+
return relation === "" || relation !== ".." && !relation.startsWith(`..${sep8}`) && !isAbsolute10(relation);
|
|
50969
51996
|
}) : void 0;
|
|
50970
51997
|
for (const [, skillValue] of Object.entries(block.content.properties)) {
|
|
50971
51998
|
if (typeof skillValue !== "object" || skillValue === null || !("references" in skillValue))
|
|
@@ -50977,8 +52004,8 @@ var Compiler = class _Compiler {
|
|
|
50977
52004
|
registryReferences.add(ref);
|
|
50978
52005
|
if (sourceFile && referenceRoot) {
|
|
50979
52006
|
const resolvedReference = resolve15(dirname13(sourceFile), ref);
|
|
50980
|
-
const relation =
|
|
50981
|
-
if (relation !== "" && relation !== ".." && !relation.startsWith(`..${
|
|
52007
|
+
const relation = relative11(resolve15(referenceRoot.path), resolvedReference);
|
|
52008
|
+
if (relation !== "" && relation !== ".." && !relation.startsWith(`..${sep8}`) && !isAbsolute10(relation)) {
|
|
50982
52009
|
const sourceReferences = registryReferencePaths.get(sourceFile) ?? /* @__PURE__ */ new Map();
|
|
50983
52010
|
const relativePath = relation.replaceAll("\\", "/");
|
|
50984
52011
|
const referenceKey = `${referenceRoot.repoUrl}\0${relativePath}\0${referenceRoot.version}`;
|
|
@@ -51082,7 +52109,7 @@ var Compiler = class _Compiler {
|
|
|
51082
52109
|
for (const warning of output.warnings ?? []) {
|
|
51083
52110
|
formatWarnings.push({
|
|
51084
52111
|
ruleId: warning.code,
|
|
51085
|
-
ruleName: "target-hook-compatibility",
|
|
52112
|
+
ruleName: warning.ruleName ?? "target-hook-compatibility",
|
|
51086
52113
|
severity: "warning",
|
|
51087
52114
|
message: warning.message,
|
|
51088
52115
|
...warning.suggestion ? { suggestion: warning.suggestion } : {},
|
|
@@ -51284,8 +52311,8 @@ var Compiler = class _Compiler {
|
|
|
51284
52311
|
*/
|
|
51285
52312
|
async watch(entryPath, options = {}) {
|
|
51286
52313
|
const { default: chokidar2 } = await import("chokidar");
|
|
51287
|
-
const { dirname: dirname29, resolve:
|
|
51288
|
-
const baseDir = dirname29(
|
|
52314
|
+
const { dirname: dirname29, resolve: resolve43 } = await import("path");
|
|
52315
|
+
const baseDir = dirname29(resolve43(entryPath));
|
|
51289
52316
|
const includePatterns = options.include ?? ["**/*.prs"];
|
|
51290
52317
|
const excludePatterns = options.exclude ?? ["**/node_modules/**"];
|
|
51291
52318
|
const debounceMs = options.debounce ?? 300;
|
|
@@ -51336,7 +52363,7 @@ var Compiler = class _Compiler {
|
|
|
51336
52363
|
".promptscriptrc.yaml",
|
|
51337
52364
|
".promptscriptrc.yml",
|
|
51338
52365
|
"promptscript.lock"
|
|
51339
|
-
].map((fileName) =>
|
|
52366
|
+
].map((fileName) => resolve43(this.watchProjectRoot(entryPath), fileName))
|
|
51340
52367
|
].map((path2) => watchPathKey(path2))
|
|
51341
52368
|
);
|
|
51342
52369
|
let dynamicDependencyPaths = toDependencyMap(this.resolvedDependencyPaths(entryPath));
|
|
@@ -51629,7 +52656,7 @@ var Pager = class {
|
|
|
51629
52656
|
}
|
|
51630
52657
|
return;
|
|
51631
52658
|
}
|
|
51632
|
-
return new Promise((
|
|
52659
|
+
return new Promise((resolve43) => {
|
|
51633
52660
|
const pagerCmd = getPagerCommand();
|
|
51634
52661
|
const [cmd, ...args] = pagerCmd.split(" ");
|
|
51635
52662
|
if (cmd === "less" && !args.includes("-R")) {
|
|
@@ -51637,7 +52664,7 @@ var Pager = class {
|
|
|
51637
52664
|
}
|
|
51638
52665
|
if (!cmd) {
|
|
51639
52666
|
console.log(content);
|
|
51640
|
-
|
|
52667
|
+
resolve43();
|
|
51641
52668
|
return;
|
|
51642
52669
|
}
|
|
51643
52670
|
const pager = spawn2(cmd, args, {
|
|
@@ -51650,17 +52677,17 @@ var Pager = class {
|
|
|
51650
52677
|
});
|
|
51651
52678
|
pager.on("error", (_err) => {
|
|
51652
52679
|
console.log(content);
|
|
51653
|
-
|
|
52680
|
+
resolve43();
|
|
51654
52681
|
});
|
|
51655
52682
|
pager.on("close", () => {
|
|
51656
|
-
|
|
52683
|
+
resolve43();
|
|
51657
52684
|
});
|
|
51658
52685
|
if (pager.stdin) {
|
|
51659
52686
|
pager.stdin.write(content);
|
|
51660
52687
|
pager.stdin.end();
|
|
51661
52688
|
} else {
|
|
51662
52689
|
console.log(content);
|
|
51663
|
-
|
|
52690
|
+
resolve43();
|
|
51664
52691
|
}
|
|
51665
52692
|
});
|
|
51666
52693
|
}
|
|
@@ -51674,14 +52701,14 @@ init_services();
|
|
|
51674
52701
|
|
|
51675
52702
|
// packages/cli/src/utils/registry-resolver.ts
|
|
51676
52703
|
import { existsSync as existsSync12 } from "fs";
|
|
51677
|
-
import { isAbsolute as
|
|
52704
|
+
import { isAbsolute as isAbsolute11, join as join14, relative as relative12, resolve as resolve16, sep as sep9 } from "path";
|
|
51678
52705
|
async function resolveRegistrySubPath(repositoryPath, subPath) {
|
|
51679
52706
|
if (!subPath) {
|
|
51680
52707
|
return repositoryPath;
|
|
51681
52708
|
}
|
|
51682
52709
|
const result = resolve16(repositoryPath, subPath);
|
|
51683
|
-
const relation =
|
|
51684
|
-
if (relation === ".." || relation.startsWith(`..${
|
|
52710
|
+
const relation = relative12(resolve16(repositoryPath), result);
|
|
52711
|
+
if (relation === ".." || relation.startsWith(`..${sep9}`) || isAbsolute11(relation)) {
|
|
51685
52712
|
throw new Error(`Registry path escapes its repository: ${subPath}`);
|
|
51686
52713
|
}
|
|
51687
52714
|
if (existsSync12(result) && !await isRealPathInside(result, repositoryPath)) {
|
|
@@ -51706,12 +52733,16 @@ async function resolveRegistryPath(config, options = {}) {
|
|
|
51706
52733
|
if (options.vendorDir && existsSync12(options.vendorDir) && !vendorManifest) {
|
|
51707
52734
|
throw new Error(`Vendor manifest is missing: ${options.vendorDir}`);
|
|
51708
52735
|
}
|
|
51709
|
-
if (options.lockfile && !lockedRepository) {
|
|
51710
|
-
throw new Error(
|
|
52736
|
+
if (options.lockfile && !lockedRepository && !options.allowMissingLockEntry) {
|
|
52737
|
+
throw new Error(
|
|
52738
|
+
`Git registry is not pinned by the lockfile: ${gitConfig.url}. Run 'prs lock' to pin it, then re-run this command.`
|
|
52739
|
+
);
|
|
51711
52740
|
}
|
|
51712
52741
|
if (options.vendorDir && vendorManifest) {
|
|
51713
52742
|
if (!lockedRepository) {
|
|
51714
|
-
throw new Error(
|
|
52743
|
+
throw new Error(
|
|
52744
|
+
`Vendored registry is not pinned by the lockfile: ${gitConfig.url}. Run 'prs lock' to pin it, then re-run this command.`
|
|
52745
|
+
);
|
|
51715
52746
|
}
|
|
51716
52747
|
const [repoUrl, dependency] = lockedRepository;
|
|
51717
52748
|
const repositoryPath = await resolveVendoredRepository(
|
|
@@ -51751,7 +52782,8 @@ async function resolveRegistryPath(config, options = {}) {
|
|
|
51751
52782
|
cachePath,
|
|
51752
52783
|
".git",
|
|
51753
52784
|
lockedRepository[1].commit,
|
|
51754
|
-
/* @__PURE__ */ new Set([".prs-cache-meta.json"])
|
|
52785
|
+
/* @__PURE__ */ new Set([".prs-cache-meta.json"]),
|
|
52786
|
+
{ allowPartial: true }
|
|
51755
52787
|
);
|
|
51756
52788
|
return {
|
|
51757
52789
|
path: await resolveRegistrySubPath(cachePath, gitConfig.path),
|
|
@@ -51780,6 +52812,7 @@ async function resolveRegistryPath(config, options = {}) {
|
|
|
51780
52812
|
ref,
|
|
51781
52813
|
path: gitConfig.path,
|
|
51782
52814
|
auth: gitConfig.auth,
|
|
52815
|
+
timeout: gitConfig.timeout,
|
|
51783
52816
|
cache: {
|
|
51784
52817
|
enabled: config.registry.cache?.enabled ?? true,
|
|
51785
52818
|
ttl: config.registry.cache?.ttl
|
|
@@ -51803,7 +52836,8 @@ async function resolveRegistryPath(config, options = {}) {
|
|
|
51803
52836
|
cachePath,
|
|
51804
52837
|
".git",
|
|
51805
52838
|
lockedRepository[1].commit,
|
|
51806
|
-
/* @__PURE__ */ new Set([".prs-cache-meta.json"])
|
|
52839
|
+
/* @__PURE__ */ new Set([".prs-cache-meta.json"]),
|
|
52840
|
+
{ allowPartial: true }
|
|
51807
52841
|
);
|
|
51808
52842
|
}
|
|
51809
52843
|
return {
|
|
@@ -51839,7 +52873,7 @@ init_src();
|
|
|
51839
52873
|
// packages/cli/src/utils/conflict-detector.ts
|
|
51840
52874
|
init_src();
|
|
51841
52875
|
import * as fs4 from "fs";
|
|
51842
|
-
import { basename as basename13, dirname as dirname14, resolve as resolve17, relative as
|
|
52876
|
+
import { basename as basename13, dirname as dirname14, resolve as resolve17, relative as relative13, isAbsolute as isAbsolute12, sep as sep10 } from "path";
|
|
51843
52877
|
function detectOutputConflicts(targets) {
|
|
51844
52878
|
const pathMap = /* @__PURE__ */ new Map();
|
|
51845
52879
|
for (const target of targets) {
|
|
@@ -51860,14 +52894,14 @@ function detectOutputConflicts(targets) {
|
|
|
51860
52894
|
function validateOutputPath(outputPath, outputRoot) {
|
|
51861
52895
|
const resolved = resolveOutputPath(outputPath, outputRoot);
|
|
51862
52896
|
const root = resolve17(outputRoot);
|
|
51863
|
-
const rel =
|
|
52897
|
+
const rel = relative13(root, resolved);
|
|
51864
52898
|
if (!isPathContained(rel, false)) {
|
|
51865
52899
|
return `Output path "${outputPath}" escapes the output directory ${outputRoot}`;
|
|
51866
52900
|
}
|
|
51867
52901
|
try {
|
|
51868
52902
|
const realRoot = resolveThroughExistingAncestor(root);
|
|
51869
52903
|
const realResolved = resolveThroughExistingAncestor(resolved);
|
|
51870
|
-
if (!isPathContained(
|
|
52904
|
+
if (!isPathContained(relative13(realRoot, realResolved), false)) {
|
|
51871
52905
|
return `Output path "${outputPath}" escapes the output directory ${outputRoot}`;
|
|
51872
52906
|
}
|
|
51873
52907
|
} catch {
|
|
@@ -51876,11 +52910,11 @@ function validateOutputPath(outputPath, outputRoot) {
|
|
|
51876
52910
|
return void 0;
|
|
51877
52911
|
}
|
|
51878
52912
|
function isPathInsideDir(dir, root) {
|
|
51879
|
-
const rel =
|
|
52913
|
+
const rel = relative13(resolve17(root), resolveOutputPath(dir, root));
|
|
51880
52914
|
return isPathContained(rel, true);
|
|
51881
52915
|
}
|
|
51882
52916
|
function isPathContained(rel, allowEqual) {
|
|
51883
|
-
return allowEqual && rel === "" || rel !== "" && rel !== ".." && !rel.startsWith(`..${
|
|
52917
|
+
return allowEqual && rel === "" || rel !== "" && rel !== ".." && !rel.startsWith(`..${sep10}`) && !isAbsolute12(rel);
|
|
51884
52918
|
}
|
|
51885
52919
|
function resolveThroughExistingAncestor(path2) {
|
|
51886
52920
|
const missingSegments = [];
|
|
@@ -51904,7 +52938,7 @@ function resolveThroughExistingAncestor(path2) {
|
|
|
51904
52938
|
return resolve17(fs4.realpathSync.native(current), ...missingSegments);
|
|
51905
52939
|
}
|
|
51906
52940
|
function resolveOutputPath(outputPath, projectRoot) {
|
|
51907
|
-
if (
|
|
52941
|
+
if (isAbsolute12(outputPath)) {
|
|
51908
52942
|
return outputPath;
|
|
51909
52943
|
}
|
|
51910
52944
|
return resolve17(projectRoot, outputPath);
|
|
@@ -51923,12 +52957,14 @@ import {
|
|
|
51923
52957
|
rmdir,
|
|
51924
52958
|
unlink as unlink2
|
|
51925
52959
|
} from "node:fs/promises";
|
|
51926
|
-
import { basename as basename14, dirname as dirname15, isAbsolute as
|
|
52960
|
+
import { basename as basename14, dirname as dirname15, isAbsolute as isAbsolute13, relative as relative14, resolve as resolve18 } from "node:path";
|
|
51927
52961
|
import { promisify as promisify2 } from "node:util";
|
|
51928
52962
|
var execFileAsync2 = promisify2(execFile2);
|
|
51929
52963
|
var PROMPTSCRIPT_MARKER_PATTERNS = [
|
|
51930
52964
|
/^<!-- PromptScript \d{4}-\d{2}-\d{2}T[\d:.]+Z \| source: .+ \| target: .+ - do not edit -->$/,
|
|
51931
52965
|
/^# promptscript-generated: \d{4}-\d{2}-\d{2}T[\d:.]+Z \| source: .+ \| target: .+$/,
|
|
52966
|
+
/^\/\/ promptscript-generated: opencode-plugin$/,
|
|
52967
|
+
/^\/\/ Generated by PromptScript - do not edit\.$/,
|
|
51932
52968
|
/^> Auto-generated by PromptScript(?:\s.*)?$/
|
|
51933
52969
|
];
|
|
51934
52970
|
var OWNED_COMMAND_FIELDS = /* @__PURE__ */ new Set([
|
|
@@ -52256,7 +53292,7 @@ function collectManagedDirectories(outputs, outputRoot) {
|
|
|
52256
53292
|
for (const output of outputs.values()) {
|
|
52257
53293
|
for (const directory of output.managedOutputDirectories ?? []) {
|
|
52258
53294
|
const normalized = directory.replace(/\\/g, "/");
|
|
52259
|
-
if (!normalized ||
|
|
53295
|
+
if (!normalized || isAbsolute13(normalized)) continue;
|
|
52260
53296
|
if (normalized.split("/").some((segment) => segment === "..")) continue;
|
|
52261
53297
|
const absolutePath = resolve18(outputRoot, normalized);
|
|
52262
53298
|
if (isWithin(outputRoot, absolutePath)) directories.add(absolutePath);
|
|
@@ -52277,7 +53313,7 @@ function collectManagedFiles(outputs, outputRoot) {
|
|
|
52277
53313
|
for (const output of outputs.values()) {
|
|
52278
53314
|
for (const file of output.managedOutputFiles ?? []) {
|
|
52279
53315
|
const normalized = file.replace(/\\/g, "/");
|
|
52280
|
-
if (!normalized ||
|
|
53316
|
+
if (!normalized || isAbsolute13(normalized)) continue;
|
|
52281
53317
|
if (normalized.split("/").some((segment) => segment === "..")) continue;
|
|
52282
53318
|
const absolutePath = resolve18(outputRoot, normalized);
|
|
52283
53319
|
if (isWithin(outputRoot, absolutePath)) files.add(absolutePath);
|
|
@@ -52540,7 +53576,7 @@ async function removeIfUnchanged(entryPath, fileStat) {
|
|
|
52540
53576
|
return true;
|
|
52541
53577
|
}
|
|
52542
53578
|
async function openAncestorDirectories(root, directory) {
|
|
52543
|
-
const relativePath =
|
|
53579
|
+
const relativePath = relative14(root, directory);
|
|
52544
53580
|
const segments = relativePath === "" ? [] : relativePath.split(/[\\/]+/);
|
|
52545
53581
|
const paths = [root];
|
|
52546
53582
|
let current = root;
|
|
@@ -52564,8 +53600,8 @@ async function openAncestorDirectories(root, directory) {
|
|
|
52564
53600
|
return guards;
|
|
52565
53601
|
}
|
|
52566
53602
|
async function openOrCreateAncestorDirectories(root, file) {
|
|
52567
|
-
const relativePath =
|
|
52568
|
-
if (relativePath.startsWith("..") ||
|
|
53603
|
+
const relativePath = relative14(root, file);
|
|
53604
|
+
if (relativePath.startsWith("..") || isAbsolute13(relativePath)) {
|
|
52569
53605
|
return void 0;
|
|
52570
53606
|
}
|
|
52571
53607
|
await mkdir4(root, { recursive: true });
|
|
@@ -53015,8 +54051,8 @@ function scanHookOwnership(value) {
|
|
|
53015
54051
|
return scanHookOwnership(Object.values(object));
|
|
53016
54052
|
}
|
|
53017
54053
|
function isWithin(root, candidate) {
|
|
53018
|
-
const relativePath =
|
|
53019
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
54054
|
+
const relativePath = relative14(root, candidate);
|
|
54055
|
+
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute13(relativePath);
|
|
53020
54056
|
}
|
|
53021
54057
|
function isNodeError(error) {
|
|
53022
54058
|
return error instanceof Error && "code" in error;
|
|
@@ -53057,6 +54093,8 @@ async function loadBundledSkillContent(logger) {
|
|
|
53057
54093
|
var PROMPTSCRIPT_MARKERS2 = [
|
|
53058
54094
|
"<!-- PromptScript",
|
|
53059
54095
|
"# promptscript-generated:",
|
|
54096
|
+
"// promptscript-generated:",
|
|
54097
|
+
"// Generated by PromptScript - do not edit.",
|
|
53060
54098
|
"> Auto-generated by PromptScript"
|
|
53061
54099
|
];
|
|
53062
54100
|
function hasPromptScriptMarker(content) {
|
|
@@ -53246,11 +54284,77 @@ async function finalizeOutputPlan(result, options) {
|
|
|
53246
54284
|
};
|
|
53247
54285
|
}
|
|
53248
54286
|
|
|
54287
|
+
// packages/cli/src/utils/resource-filter.ts
|
|
54288
|
+
init_src();
|
|
54289
|
+
function collectResourceKinds(value, previous) {
|
|
54290
|
+
return [...previous, ...value.split(",").map((item) => item.trim())].filter(Boolean);
|
|
54291
|
+
}
|
|
54292
|
+
function parseResourceSelection(values) {
|
|
54293
|
+
if (!values || values.length === 0) return { kinds: [], invalid: [] };
|
|
54294
|
+
const kinds = [];
|
|
54295
|
+
const invalid = [];
|
|
54296
|
+
for (const raw of values.flatMap((value) => value.split(","))) {
|
|
54297
|
+
const item = raw.trim();
|
|
54298
|
+
if (!item) continue;
|
|
54299
|
+
if (isOutputResourceKind(item)) {
|
|
54300
|
+
if (!kinds.includes(item)) kinds.push(item);
|
|
54301
|
+
} else if (!invalid.includes(item)) {
|
|
54302
|
+
invalid.push(item);
|
|
54303
|
+
}
|
|
54304
|
+
}
|
|
54305
|
+
return { kinds, invalid };
|
|
54306
|
+
}
|
|
54307
|
+
function filterOutputsByResources(plan, outputs, kinds, skillBaseDirs) {
|
|
54308
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
54309
|
+
for (const file of plan.files) {
|
|
54310
|
+
if (!kinds.has(classifyOutputResource(file.owner, file.path, skillBaseDirs?.get(file.owner)))) {
|
|
54311
|
+
continue;
|
|
54312
|
+
}
|
|
54313
|
+
const existing = outputs.get(file.path);
|
|
54314
|
+
filtered.set(
|
|
54315
|
+
file.path,
|
|
54316
|
+
existing ?? {
|
|
54317
|
+
path: file.path,
|
|
54318
|
+
content: file.content,
|
|
54319
|
+
...file.mode !== void 0 ? { mode: file.mode } : {},
|
|
54320
|
+
...file.merge !== void 0 ? { merge: file.merge } : {},
|
|
54321
|
+
...file.managedOutputDirectories !== void 0 ? { managedOutputDirectories: file.managedOutputDirectories } : {},
|
|
54322
|
+
...file.managedOutputFiles !== void 0 ? { managedOutputFiles: file.managedOutputFiles } : {}
|
|
54323
|
+
}
|
|
54324
|
+
);
|
|
54325
|
+
}
|
|
54326
|
+
return {
|
|
54327
|
+
outputs: filtered,
|
|
54328
|
+
kept: filtered.size,
|
|
54329
|
+
dropped: plan.files.length - filtered.size
|
|
54330
|
+
};
|
|
54331
|
+
}
|
|
54332
|
+
|
|
54333
|
+
// packages/cli/src/utils/global-output-safety.ts
|
|
54334
|
+
import { homedir as homedir3 } from "node:os";
|
|
54335
|
+
import { resolve as resolve21 } from "node:path";
|
|
54336
|
+
var PROTECTED_USER_FILES = [
|
|
54337
|
+
{
|
|
54338
|
+
// Factory AI contract: ~/.factory/AGENTS.md is a personal override file.
|
|
54339
|
+
path: ".factory/AGENTS.md",
|
|
54340
|
+
reason: "Factory personal override (~/.factory/AGENTS.md is user-owned, never compiler output)"
|
|
54341
|
+
}
|
|
54342
|
+
];
|
|
54343
|
+
function describeProtectedUserFile(absolutePath) {
|
|
54344
|
+
const home = homedir3();
|
|
54345
|
+
for (const entry of PROTECTED_USER_FILES) {
|
|
54346
|
+
if (absolutePath === resolve21(home, entry.path)) {
|
|
54347
|
+
return { displayPath: `~/${entry.path}`, reason: entry.reason };
|
|
54348
|
+
}
|
|
54349
|
+
}
|
|
54350
|
+
return void 0;
|
|
54351
|
+
}
|
|
54352
|
+
|
|
53249
54353
|
// packages/cli/src/commands/compile.ts
|
|
53250
54354
|
init_build_profile();
|
|
53251
54355
|
init_target_config();
|
|
53252
54356
|
async function loadCompileLockfile(projectRoot, logger) {
|
|
53253
|
-
const lockfilePath =
|
|
54357
|
+
const lockfilePath = resolve22(projectRoot, "promptscript.lock");
|
|
53254
54358
|
if (!existsSync14(lockfilePath)) {
|
|
53255
54359
|
return void 0;
|
|
53256
54360
|
}
|
|
@@ -53268,7 +54372,7 @@ async function loadCompileLockfile(projectRoot, logger) {
|
|
|
53268
54372
|
}
|
|
53269
54373
|
function findConfigInDir(dir) {
|
|
53270
54374
|
for (const file of CONFIG_FILES) {
|
|
53271
|
-
const fullPath =
|
|
54375
|
+
const fullPath = resolve22(dir, file);
|
|
53272
54376
|
if (existsSync14(fullPath)) {
|
|
53273
54377
|
return fullPath;
|
|
53274
54378
|
}
|
|
@@ -53309,19 +54413,19 @@ function describeEmptyTargets(context) {
|
|
|
53309
54413
|
}
|
|
53310
54414
|
function resolveOutputBase(projectRoot, output) {
|
|
53311
54415
|
if (!output) return projectRoot;
|
|
53312
|
-
return
|
|
54416
|
+
return isAbsolute14(output) ? output : resolve22(projectRoot, output);
|
|
53313
54417
|
}
|
|
53314
54418
|
function resolveProjectPath(projectRoot, path2) {
|
|
53315
|
-
return
|
|
54419
|
+
return isAbsolute14(path2) ? path2 : resolve22(projectRoot, path2);
|
|
53316
54420
|
}
|
|
53317
54421
|
function validateWritePathSymlinks(outputPath, outputRoot) {
|
|
53318
|
-
const root =
|
|
53319
|
-
const candidate =
|
|
53320
|
-
const relativePath =
|
|
53321
|
-
if (relativePath === "" || relativePath.startsWith("..") ||
|
|
54422
|
+
const root = resolve22(outputRoot);
|
|
54423
|
+
const candidate = resolve22(root, outputPath);
|
|
54424
|
+
const relativePath = relative15(root, candidate);
|
|
54425
|
+
if (relativePath === "" || relativePath.startsWith("..") || isAbsolute14(relativePath)) return;
|
|
53322
54426
|
let current = root;
|
|
53323
54427
|
for (const segment of relativePath.split(/[\\/]/)) {
|
|
53324
|
-
current =
|
|
54428
|
+
current = resolve22(current, segment);
|
|
53325
54429
|
let stat5;
|
|
53326
54430
|
try {
|
|
53327
54431
|
stat5 = lstatSync2(current);
|
|
@@ -53452,10 +54556,21 @@ ${escaping.map((m) => ` - ${m}`).join("\n")}`,
|
|
|
53452
54556
|
"PS1003" /* INVALID_PATH */
|
|
53453
54557
|
);
|
|
53454
54558
|
}
|
|
54559
|
+
const protectedHits = plannedOutputs.map((output) => describeProtectedUserFile(resolve22(outputRoot, output.path))).filter((hit) => hit !== void 0);
|
|
54560
|
+
if (protectedHits.length > 0) {
|
|
54561
|
+
const protectedList = protectedHits.map((hit) => ` - ${hit.displayPath} (${hit.reason})`).join("\n");
|
|
54562
|
+
throw new PSError(
|
|
54563
|
+
`Refusing to write protected personal file(s):
|
|
54564
|
+
${protectedList}
|
|
54565
|
+
|
|
54566
|
+
Personal override files are never compiler output. Use --resources to install generated agent and skill directories only.`,
|
|
54567
|
+
"PS1003" /* INVALID_PATH */
|
|
54568
|
+
);
|
|
54569
|
+
}
|
|
53455
54570
|
if (!options.dryRun && !options.force && !isTTY()) {
|
|
53456
54571
|
const preflightConflicts = [];
|
|
53457
54572
|
for (const output of plannedOutputs) {
|
|
53458
|
-
const outputPath = options.output ?
|
|
54573
|
+
const outputPath = options.output ? resolve22(options.output, output.path) : resolve22(output.path);
|
|
53459
54574
|
if (!existsSync14(outputPath)) continue;
|
|
53460
54575
|
const existingContent = await readFileOrUndefined(outputPath);
|
|
53461
54576
|
const contentMatches = existingContent !== void 0 && stripPromptScriptMarkers(existingContent) === stripPromptScriptMarkers(output.content);
|
|
@@ -53480,7 +54595,7 @@ Use --force to overwrite these files.`
|
|
|
53480
54595
|
}
|
|
53481
54596
|
}
|
|
53482
54597
|
async function safeOverwrite(output, outputPath, existingContent, content) {
|
|
53483
|
-
const outputRoot2 = options.output ?
|
|
54598
|
+
const outputRoot2 = options.output ? resolve22(options.output) : process.cwd();
|
|
53484
54599
|
const rewritten = existingContent !== void 0 && await rewriteHookOutputIfUnchanged(
|
|
53485
54600
|
outputPath,
|
|
53486
54601
|
outputRoot2,
|
|
@@ -53496,7 +54611,7 @@ Use --force to overwrite these files.`
|
|
|
53496
54611
|
return rewritten;
|
|
53497
54612
|
}
|
|
53498
54613
|
async function safeCreate(output, outputPath, content) {
|
|
53499
|
-
const outputRoot2 = options.output ?
|
|
54614
|
+
const outputRoot2 = options.output ? resolve22(options.output) : process.cwd();
|
|
53500
54615
|
const created = await createHookOutputSafely(outputPath, outputRoot2, content, output.mode);
|
|
53501
54616
|
if (!created) {
|
|
53502
54617
|
const msg = `Skipped '${outputPath}' because its output path was not safe to create.`;
|
|
@@ -53506,7 +54621,7 @@ Use --force to overwrite these files.`
|
|
|
53506
54621
|
return created;
|
|
53507
54622
|
}
|
|
53508
54623
|
for (const output of plannedOutputs) {
|
|
53509
|
-
const outputPath = options.output ?
|
|
54624
|
+
const outputPath = options.output ? resolve22(options.output, output.path) : resolve22(output.path);
|
|
53510
54625
|
const fileExists3 = existsSync14(outputPath);
|
|
53511
54626
|
const existingContent = fileExists3 ? await readFileOrUndefined(outputPath) : void 0;
|
|
53512
54627
|
const existingHookOutputOwned = existingContent !== void 0 && isPromptScriptOwnedHookOutput(output.path, existingContent);
|
|
@@ -53543,7 +54658,7 @@ Use --force to overwrite these files.`
|
|
|
53543
54658
|
if (output.mode !== void 0 && existingContent !== void 0) {
|
|
53544
54659
|
const modeUpdated = await rewriteHookOutputIfUnchanged(
|
|
53545
54660
|
outputPath,
|
|
53546
|
-
options.output ?
|
|
54661
|
+
options.output ? resolve22(options.output) : process.cwd(),
|
|
53547
54662
|
existingContent,
|
|
53548
54663
|
existingContent,
|
|
53549
54664
|
output.mode
|
|
@@ -53639,7 +54754,7 @@ function printWarnings(warnings) {
|
|
|
53639
54754
|
}
|
|
53640
54755
|
async function compileAllBuilds(options, services) {
|
|
53641
54756
|
const logger = createCliLogger();
|
|
53642
|
-
const projectRoot = options.cwd ?
|
|
54757
|
+
const projectRoot = options.cwd ? resolve22(options.cwd) : process.cwd();
|
|
53643
54758
|
const configPath = resolveCompileConfigPath(projectRoot, options);
|
|
53644
54759
|
const config = await loadEffectiveConfig(configPath);
|
|
53645
54760
|
const buildNames = Object.keys(getBuildProfiles(config));
|
|
@@ -53713,7 +54828,7 @@ async function compileCommandWithResult(options, services) {
|
|
|
53713
54828
|
const spinner = createSpinner("Loading configuration...").start();
|
|
53714
54829
|
const logger = createCliLogger();
|
|
53715
54830
|
try {
|
|
53716
|
-
const projectRoot = options.cwd ?
|
|
54831
|
+
const projectRoot = options.cwd ? resolve22(options.cwd) : process.cwd();
|
|
53717
54832
|
logger.verbose("Loading configuration...");
|
|
53718
54833
|
const configPath = resolveCompileConfigPath(projectRoot, options);
|
|
53719
54834
|
const config = await loadEffectiveConfig(configPath);
|
|
@@ -53728,6 +54843,17 @@ async function compileCommandWithResult(options, services) {
|
|
|
53728
54843
|
} else {
|
|
53729
54844
|
targets = parsedTargets;
|
|
53730
54845
|
}
|
|
54846
|
+
const cliResources = options.resources && options.resources.length > 0 ? options.resources : void 0;
|
|
54847
|
+
const resourceSelection = parseResourceSelection(cliResources ?? config.output?.resources);
|
|
54848
|
+
if (resourceSelection.invalid.length > 0) {
|
|
54849
|
+
spinner.fail("Invalid resource selection");
|
|
54850
|
+
ConsoleOutput.error(
|
|
54851
|
+
`Unknown resource kind(s): ${resourceSelection.invalid.join(", ")}. Valid kinds: agents, skills, commands, mcp, hooks, plugins, main.`
|
|
54852
|
+
);
|
|
54853
|
+
process.exitCode = 1;
|
|
54854
|
+
return false;
|
|
54855
|
+
}
|
|
54856
|
+
const resourceKinds = new Set(resourceSelection.kinds);
|
|
53731
54857
|
if (targets.length === 0) {
|
|
53732
54858
|
spinner.fail("No compilation targets");
|
|
53733
54859
|
ConsoleOutput.error(
|
|
@@ -53762,7 +54888,7 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
53762
54888
|
}
|
|
53763
54889
|
}
|
|
53764
54890
|
const lockfile = await loadCompileLockfile(projectRoot, logger);
|
|
53765
|
-
const vendorDir =
|
|
54891
|
+
const vendorDir = resolve22(projectRoot, ".promptscript/vendor");
|
|
53766
54892
|
let registryPath;
|
|
53767
54893
|
let referenceRoots;
|
|
53768
54894
|
if (options.registry) {
|
|
@@ -53784,7 +54910,7 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
53784
54910
|
if (config.includePromptScriptSkill !== false) {
|
|
53785
54911
|
skillContent = await loadBundledSkillContent(logger);
|
|
53786
54912
|
}
|
|
53787
|
-
const localPath =
|
|
54913
|
+
const localPath = resolve22(projectRoot, ".promptscript");
|
|
53788
54914
|
const compiler = new Compiler({
|
|
53789
54915
|
resolver: {
|
|
53790
54916
|
registryPath,
|
|
@@ -53806,7 +54932,7 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
53806
54932
|
ignoreHashes: options.ignoreHashes
|
|
53807
54933
|
});
|
|
53808
54934
|
const entry = buildProfile?.entry ?? config.input?.entry;
|
|
53809
|
-
const entryPath = entry ?
|
|
54935
|
+
const entryPath = entry ? resolve22(projectRoot, entry) : resolve22(localPath, "project.prs");
|
|
53810
54936
|
if (!existsSync14(entryPath)) {
|
|
53811
54937
|
spinner.fail("Entry file not found");
|
|
53812
54938
|
ConsoleOutput.error(`File not found: ${entryPath}`);
|
|
@@ -53838,8 +54964,13 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
53838
54964
|
output: resolveOutputBase(projectRoot, configuredOutput),
|
|
53839
54965
|
force: options.force ?? config.output?.overwrite
|
|
53840
54966
|
};
|
|
54967
|
+
if (resourceKinds.size === 0 && effectiveOptions.output === homedir4()) {
|
|
54968
|
+
ConsoleOutput.warning(
|
|
54969
|
+
`Output directory is your home directory; root instruction files (for example CLAUDE.md, AGENTS.md) will be written there. Use --resources to install only generated agent and skill directories.`
|
|
54970
|
+
);
|
|
54971
|
+
}
|
|
53841
54972
|
const hasFactoryTarget = targets.some((target) => target.name === "factory");
|
|
53842
|
-
const migrateFactoryHooks = options.migrateFactoryHooks !== false;
|
|
54973
|
+
const migrateFactoryHooks = options.migrateFactoryHooks !== false && resourceKinds.size === 0;
|
|
53843
54974
|
const legacyMigration = hasFactoryTarget && migrateFactoryHooks ? await prepareLegacyFactoryMigration(result.outputs, effectiveOptions.output) : void 0;
|
|
53844
54975
|
const legacySettingsPath = hasFactoryTarget && !migrateFactoryHooks ? await detectLegacyFactorySettingsHooks(effectiveOptions.output, true) : void 0;
|
|
53845
54976
|
const finalized = await finalizeOutputPlan(result, {
|
|
@@ -53848,7 +54979,26 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
53848
54979
|
logger,
|
|
53849
54980
|
additionalOutputPaths: legacyMigration ? [".factory/hooks.json"] : []
|
|
53850
54981
|
});
|
|
53851
|
-
|
|
54982
|
+
let effectiveOutputs = finalized.outputs;
|
|
54983
|
+
if (resourceKinds.size > 0) {
|
|
54984
|
+
const skillBaseDirs = /* @__PURE__ */ new Map();
|
|
54985
|
+
for (const target of targets) {
|
|
54986
|
+
if (target.config?.skillBaseDir) {
|
|
54987
|
+
skillBaseDirs.set(target.name, target.config.skillBaseDir);
|
|
54988
|
+
}
|
|
54989
|
+
}
|
|
54990
|
+
const filtered = filterOutputsByResources(
|
|
54991
|
+
finalized.outputPlan,
|
|
54992
|
+
finalized.outputs,
|
|
54993
|
+
resourceKinds,
|
|
54994
|
+
skillBaseDirs
|
|
54995
|
+
);
|
|
54996
|
+
effectiveOutputs = filtered.outputs;
|
|
54997
|
+
ConsoleOutput.info(
|
|
54998
|
+
`Resource-only compile: kept ${filtered.kept} file(s), omitted ${filtered.dropped}`
|
|
54999
|
+
);
|
|
55000
|
+
}
|
|
55001
|
+
const writeResult = await writeOutputs(effectiveOutputs, effectiveOptions, config, services);
|
|
53852
55002
|
if (legacyMigration) {
|
|
53853
55003
|
if (writeResult.skipped.includes(legacyMigration.hooksPath)) {
|
|
53854
55004
|
throw new Error(
|
|
@@ -53862,30 +55012,38 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
53862
55012
|
writeResult.created.includes(legacyMigration.hooksPath)
|
|
53863
55013
|
);
|
|
53864
55014
|
}
|
|
53865
|
-
|
|
53866
|
-
const cleanupResult = await cleanupManagedOutputs(plannedOutputMap, {
|
|
53867
|
-
outputRoot: effectiveOptions.output,
|
|
53868
|
-
dryRun: options.dryRun
|
|
53869
|
-
});
|
|
53870
|
-
for (const removedPath of cleanupResult.removed) {
|
|
55015
|
+
if (resourceKinds.size > 0) {
|
|
53871
55016
|
if (options.dryRun) {
|
|
53872
|
-
ConsoleOutput.dryRun(
|
|
55017
|
+
ConsoleOutput.dryRun("Would skip managed cleanup for resource-only compile");
|
|
53873
55018
|
} else {
|
|
53874
|
-
ConsoleOutput.muted(
|
|
55019
|
+
ConsoleOutput.muted("Skipped managed cleanup for resource-only compile");
|
|
53875
55020
|
}
|
|
53876
|
-
}
|
|
53877
|
-
|
|
53878
|
-
|
|
53879
|
-
|
|
53880
|
-
|
|
53881
|
-
|
|
55021
|
+
} else {
|
|
55022
|
+
const plannedOutputMap = new Map(finalized.outputs);
|
|
55023
|
+
const cleanupResult = await cleanupManagedOutputs(plannedOutputMap, {
|
|
55024
|
+
outputRoot: effectiveOptions.output,
|
|
55025
|
+
dryRun: options.dryRun
|
|
55026
|
+
});
|
|
55027
|
+
for (const removedPath of cleanupResult.removed) {
|
|
55028
|
+
if (options.dryRun) {
|
|
55029
|
+
ConsoleOutput.dryRun(`Would remove obsolete generated file: ${removedPath}`);
|
|
55030
|
+
} else {
|
|
55031
|
+
ConsoleOutput.muted(`Removed obsolete generated file: ${removedPath}`);
|
|
55032
|
+
}
|
|
53882
55033
|
}
|
|
53883
|
-
|
|
53884
|
-
|
|
53885
|
-
|
|
53886
|
-
|
|
53887
|
-
|
|
53888
|
-
|
|
55034
|
+
for (const removedDirectory of cleanupResult.removedDirectories) {
|
|
55035
|
+
if (options.dryRun) {
|
|
55036
|
+
ConsoleOutput.dryRun(`Would remove empty managed directory: ${removedDirectory}`);
|
|
55037
|
+
} else {
|
|
55038
|
+
ConsoleOutput.muted(`Removed empty managed directory: ${removedDirectory}`);
|
|
55039
|
+
}
|
|
55040
|
+
}
|
|
55041
|
+
for (const rewrittenPath of cleanupResult.rewritten ?? []) {
|
|
55042
|
+
if (options.dryRun) {
|
|
55043
|
+
ConsoleOutput.dryRun(`Would rewrite mixed managed hook file: ${rewrittenPath}`);
|
|
55044
|
+
} else {
|
|
55045
|
+
ConsoleOutput.muted(`Rewrote mixed managed hook file: ${rewrittenPath}`);
|
|
55046
|
+
}
|
|
53889
55047
|
}
|
|
53890
55048
|
}
|
|
53891
55049
|
spinner.succeed("Compilation successful");
|
|
@@ -53953,10 +55111,10 @@ function watchForChanges(projectRoot, watchConfig, callback) {
|
|
|
53953
55111
|
const includePatterns = watchConfig?.include ?? DEFAULT_WATCH_INCLUDE;
|
|
53954
55112
|
const excludePatterns = watchConfig?.exclude ?? DEFAULT_WATCH_EXCLUDE;
|
|
53955
55113
|
const matcherOptions = { dot: true, nocase: process.platform === "win32" };
|
|
53956
|
-
const relativePath = (path2) =>
|
|
55114
|
+
const relativePath = (path2) => relative15(projectRoot, path2).replace(/\\/g, "/");
|
|
53957
55115
|
const matches = (path2, patterns) => {
|
|
53958
|
-
const
|
|
53959
|
-
return patterns.some((pattern) => minimatch(
|
|
55116
|
+
const relative24 = relativePath(path2);
|
|
55117
|
+
return patterns.some((pattern) => minimatch(relative24, pattern, matcherOptions));
|
|
53960
55118
|
};
|
|
53961
55119
|
const shouldCompile = (path2) => matches(path2, includePatterns) && !matches(path2, excludePatterns);
|
|
53962
55120
|
const watcher = chokidar.watch(projectRoot, {
|
|
@@ -54030,7 +55188,7 @@ function watchForChanges(projectRoot, watchConfig, callback) {
|
|
|
54030
55188
|
// packages/cli/src/commands/validate.ts
|
|
54031
55189
|
init_loader2();
|
|
54032
55190
|
init_console();
|
|
54033
|
-
import { resolve as
|
|
55191
|
+
import { resolve as resolve23, join as join15 } from "path";
|
|
54034
55192
|
import { existsSync as existsSync15, readFileSync as readFileSync4, writeFileSync, readdirSync } from "fs";
|
|
54035
55193
|
init_src();
|
|
54036
55194
|
init_src2();
|
|
@@ -54219,7 +55377,7 @@ function discoverPrsFiles(dir, strict = false, includeSymbolicLinks = false) {
|
|
|
54219
55377
|
return results;
|
|
54220
55378
|
}
|
|
54221
55379
|
function loadValidationLockfile() {
|
|
54222
|
-
const lockfilePath =
|
|
55380
|
+
const lockfilePath = resolve23("promptscript.lock");
|
|
54223
55381
|
if (!existsSync15(lockfilePath)) {
|
|
54224
55382
|
return void 0;
|
|
54225
55383
|
}
|
|
@@ -54242,7 +55400,7 @@ async function validateCommand2(options) {
|
|
|
54242
55400
|
try {
|
|
54243
55401
|
const config = await loadConfig();
|
|
54244
55402
|
const lockfile = loadValidationLockfile();
|
|
54245
|
-
const vendorDir =
|
|
55403
|
+
const vendorDir = resolve23(".promptscript/vendor");
|
|
54246
55404
|
if (!isJsonFormat) spinner.text = "Resolving registry...";
|
|
54247
55405
|
const registry2 = await resolveRegistryPath(config, { vendorDir, lockfile });
|
|
54248
55406
|
if (!isJsonFormat) spinner.text = "Validating...";
|
|
@@ -54264,7 +55422,7 @@ async function validateCommand2(options) {
|
|
|
54264
55422
|
formatters: []
|
|
54265
55423
|
// No formatters needed for validation only
|
|
54266
55424
|
});
|
|
54267
|
-
const entryPaths = options.files && options.files.length > 0 ? options.files.map((file) =>
|
|
55425
|
+
const entryPaths = options.files && options.files.length > 0 ? options.files.map((file) => resolve23(file)) : [resolve23(config.input?.entry ?? "./.promptscript/project.prs")];
|
|
54268
55426
|
for (const entryPath of entryPaths) {
|
|
54269
55427
|
if (!existsSync15(entryPath)) {
|
|
54270
55428
|
handleEntryNotFound(entryPath, isJsonFormat, spinner);
|
|
@@ -54317,16 +55475,16 @@ function outputJsonResult(result) {
|
|
|
54317
55475
|
console.log(JSON.stringify(result, null, 2));
|
|
54318
55476
|
}
|
|
54319
55477
|
async function runFix(requestedFiles) {
|
|
54320
|
-
const files = requestedFiles && requestedFiles.length > 0 ? requestedFiles.map((file) =>
|
|
55478
|
+
const files = requestedFiles && requestedFiles.length > 0 ? requestedFiles.map((file) => resolve23(file)) : discoverPrsFiles(".promptscript");
|
|
54321
55479
|
let fixedCount = 0;
|
|
54322
55480
|
const configPath = findConfigFile();
|
|
54323
55481
|
const config = configPath ? await loadConfig(configPath) : void 0;
|
|
54324
55482
|
const lockfile = loadValidationLockfile();
|
|
54325
|
-
const vendorDir =
|
|
54326
|
-
const registryPath = config ? (await resolveRegistryPath(config, { vendorDir, lockfile })).path :
|
|
55483
|
+
const vendorDir = resolve23(".promptscript/vendor");
|
|
55484
|
+
const registryPath = config ? (await resolveRegistryPath(config, { vendorDir, lockfile })).path : resolve23("./registry");
|
|
54327
55485
|
const resolver = new Resolver({
|
|
54328
55486
|
registryPath,
|
|
54329
|
-
localPath:
|
|
55487
|
+
localPath: resolve23("./.promptscript"),
|
|
54330
55488
|
vendorDir,
|
|
54331
55489
|
lockfile,
|
|
54332
55490
|
registries: config?.registries,
|
|
@@ -54340,7 +55498,7 @@ async function runFix(requestedFiles) {
|
|
|
54340
55498
|
if (typeof declaredVersion !== "string") continue;
|
|
54341
55499
|
let ast = parseResult.ast;
|
|
54342
55500
|
try {
|
|
54343
|
-
const resolved = await resolver.resolve(
|
|
55501
|
+
const resolved = await resolver.resolve(resolve23(filePath));
|
|
54344
55502
|
ast = resolved.ast ?? ast;
|
|
54345
55503
|
} catch {
|
|
54346
55504
|
}
|
|
@@ -54389,7 +55547,7 @@ init_loader2();
|
|
|
54389
55547
|
init_console();
|
|
54390
55548
|
import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
|
|
54391
55549
|
import { existsSync as existsSync16 } from "fs";
|
|
54392
|
-
import { resolve as
|
|
55550
|
+
import { resolve as resolve24, dirname as dirname17, relative as relative16 } from "path";
|
|
54393
55551
|
async function pullCommand(options) {
|
|
54394
55552
|
const spinner = createSpinner("Loading configuration...").start();
|
|
54395
55553
|
try {
|
|
@@ -54409,10 +55567,10 @@ async function pullCommand(options) {
|
|
|
54409
55567
|
spinner.text = `Pulling ${config.inherit}...`;
|
|
54410
55568
|
const registry2 = await createRegistry(config, options, spinner);
|
|
54411
55569
|
const inheritPath = parseInheritPath(config.inherit);
|
|
54412
|
-
const destinationRoot =
|
|
54413
|
-
const destPath =
|
|
54414
|
-
const destinationRelativePath =
|
|
54415
|
-
if (destinationRelativePath.startsWith("..") ||
|
|
55570
|
+
const destinationRoot = resolve24("./.promptscript/.inherited");
|
|
55571
|
+
const destPath = resolve24(destinationRoot, inheritPath);
|
|
55572
|
+
const destinationRelativePath = relative16(destinationRoot, destPath);
|
|
55573
|
+
if (destinationRelativePath.startsWith("..") || resolve24(destPath) === resolve24(destinationRoot)) {
|
|
54416
55574
|
throw new Error(`Invalid inheritance path: ${config.inherit}`);
|
|
54417
55575
|
}
|
|
54418
55576
|
if (options.dryRun) {
|
|
@@ -54460,6 +55618,8 @@ async function createRegistry(config, options, spinner) {
|
|
|
54460
55618
|
ref: gitRef ?? gitConfig.ref,
|
|
54461
55619
|
path: gitConfig.path,
|
|
54462
55620
|
auth: gitConfig.auth,
|
|
55621
|
+
timeout: gitConfig.timeout,
|
|
55622
|
+
fallbackUrl: gitConfig.fallbackUrl,
|
|
54463
55623
|
cache: {
|
|
54464
55624
|
enabled: !options.refresh && (config.registry.cache?.enabled ?? true),
|
|
54465
55625
|
ttl: config.registry.cache?.ttl
|
|
@@ -54487,7 +55647,7 @@ async function createRegistry(config, options, spinner) {
|
|
|
54487
55647
|
if (!existsSync16(registryPath)) {
|
|
54488
55648
|
throw new Error(`Local registry path not found: ${registryPath}`);
|
|
54489
55649
|
}
|
|
54490
|
-
return createFileSystemRegistry(
|
|
55650
|
+
return createFileSystemRegistry(resolve24(registryPath));
|
|
54491
55651
|
}
|
|
54492
55652
|
function parseInheritVersion(inheritPath) {
|
|
54493
55653
|
const versionIndex = inheritPath.lastIndexOf("@");
|
|
@@ -54546,7 +55706,7 @@ function handlePullError(error, spinner) {
|
|
|
54546
55706
|
init_src();
|
|
54547
55707
|
init_loader2();
|
|
54548
55708
|
init_console();
|
|
54549
|
-
import { resolve as
|
|
55709
|
+
import { resolve as resolve26 } from "node:path";
|
|
54550
55710
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
54551
55711
|
import { existsSync as existsSync18 } from "node:fs";
|
|
54552
55712
|
init_loader();
|
|
@@ -54555,7 +55715,7 @@ init_loader();
|
|
|
54555
55715
|
import { createHash as createHash5 } from "node:crypto";
|
|
54556
55716
|
import { existsSync as existsSync17 } from "node:fs";
|
|
54557
55717
|
import { lstat as lstat9, readFile as readFile17 } from "node:fs/promises";
|
|
54558
|
-
import { isAbsolute as
|
|
55718
|
+
import { isAbsolute as isAbsolute15, relative as relative17, resolve as resolve25, sep as sep11 } from "node:path";
|
|
54559
55719
|
var DIFF_SCHEMA_VERSION = 1;
|
|
54560
55720
|
var DIFF_SCHEMA_URL = "https://getpromptscript.dev/schema/diff/v1.json";
|
|
54561
55721
|
var CHANGE_KIND_ORDER = {
|
|
@@ -54574,19 +55734,19 @@ function normalizePath2(path2) {
|
|
|
54574
55734
|
return path2.replaceAll("\\", "/");
|
|
54575
55735
|
}
|
|
54576
55736
|
function reportPath(outputRoot, path2) {
|
|
54577
|
-
const absolutePath =
|
|
54578
|
-
const relation = normalizePath2(
|
|
55737
|
+
const absolutePath = resolve25(outputRoot, path2);
|
|
55738
|
+
const relation = normalizePath2(relative17(outputRoot, absolutePath));
|
|
54579
55739
|
if (!relation || relation === ".") return normalizePath2(path2);
|
|
54580
|
-
if (relation === ".." || relation.startsWith("../") ||
|
|
55740
|
+
if (relation === ".." || relation.startsWith("../") || isAbsolute15(relation)) {
|
|
54581
55741
|
return normalizePath2(path2);
|
|
54582
55742
|
}
|
|
54583
55743
|
return relation;
|
|
54584
55744
|
}
|
|
54585
55745
|
function reportSource(projectRoot, source) {
|
|
54586
55746
|
const normalized = normalizePath2(source);
|
|
54587
|
-
if (!
|
|
54588
|
-
const relation = normalizePath2(
|
|
54589
|
-
if (relation && relation !== ".." && !relation.startsWith("../") && !
|
|
55747
|
+
if (!isAbsolute15(source)) return normalized;
|
|
55748
|
+
const relation = normalizePath2(relative17(projectRoot, source));
|
|
55749
|
+
if (relation && relation !== ".." && !relation.startsWith("../") && !isAbsolute15(relation)) {
|
|
54590
55750
|
return relation;
|
|
54591
55751
|
}
|
|
54592
55752
|
return normalized;
|
|
@@ -54745,15 +55905,15 @@ function createReport(success, changes, warnings, errors, includeContent, unchan
|
|
|
54745
55905
|
};
|
|
54746
55906
|
}
|
|
54747
55907
|
function findManagedOutputOwner(removedPath, outputs, outputRoot, entryPath, projectRoot) {
|
|
54748
|
-
const candidate =
|
|
55908
|
+
const candidate = resolve25(removedPath);
|
|
54749
55909
|
for (const output of outputs.values()) {
|
|
54750
55910
|
const managedFile = (output.managedOutputFiles ?? []).some(
|
|
54751
|
-
(file) =>
|
|
55911
|
+
(file) => resolve25(outputRoot, file) === candidate
|
|
54752
55912
|
);
|
|
54753
55913
|
const managedDirectory = (directory) => {
|
|
54754
|
-
const directoryPath =
|
|
54755
|
-
const relation =
|
|
54756
|
-
return relation === "" || relation !== ".." && !relation.startsWith(`..${
|
|
55914
|
+
const directoryPath = resolve25(outputRoot, directory);
|
|
55915
|
+
const relation = relative17(directoryPath, candidate);
|
|
55916
|
+
return relation === "" || relation !== ".." && !relation.startsWith(`..${sep11}`) && !isAbsolute15(relation);
|
|
54757
55917
|
};
|
|
54758
55918
|
if (managedFile || (output.managedOutputDirectories ?? []).some(managedDirectory)) {
|
|
54759
55919
|
return outputIdentity(output, entryPath, projectRoot);
|
|
@@ -54772,8 +55932,8 @@ function createCompilationDiffErrorReport(errors, warnings, projectRoot, include
|
|
|
54772
55932
|
);
|
|
54773
55933
|
}
|
|
54774
55934
|
async function buildCompilationDiff(options) {
|
|
54775
|
-
const projectRoot =
|
|
54776
|
-
const outputRoot =
|
|
55935
|
+
const projectRoot = resolve25(options.projectRoot);
|
|
55936
|
+
const outputRoot = resolve25(options.outputRoot);
|
|
54777
55937
|
const includeContent = options.includeContent === true;
|
|
54778
55938
|
const changes = [];
|
|
54779
55939
|
const warnings = options.warnings.map((warning) => normalizeWarning(warning, projectRoot));
|
|
@@ -54782,7 +55942,7 @@ async function buildCompilationDiff(options) {
|
|
|
54782
55942
|
for (const output of options.outputs.values()) {
|
|
54783
55943
|
const identity = outputIdentity(output, options.entryPath, projectRoot);
|
|
54784
55944
|
const path2 = reportPath(outputRoot, output.path);
|
|
54785
|
-
const outputPath =
|
|
55945
|
+
const outputPath = resolve25(outputRoot, output.path);
|
|
54786
55946
|
const desiredContent = output.content;
|
|
54787
55947
|
const common = {
|
|
54788
55948
|
target: identity.target,
|
|
@@ -54954,7 +56114,7 @@ function resolveUniversalDir2(universalDir) {
|
|
|
54954
56114
|
return { universalDir: ".agents" };
|
|
54955
56115
|
}
|
|
54956
56116
|
async function loadDiffLockfile(projectRoot) {
|
|
54957
|
-
const lockfilePath =
|
|
56117
|
+
const lockfilePath = resolve26(projectRoot, "promptscript.lock");
|
|
54958
56118
|
if (!existsSync18(lockfilePath)) {
|
|
54959
56119
|
return void 0;
|
|
54960
56120
|
}
|
|
@@ -54976,7 +56136,7 @@ function printNewFilePreview(content, showFull, pager) {
|
|
|
54976
56136
|
}
|
|
54977
56137
|
}
|
|
54978
56138
|
async function compareOutput(output, outputRoot, showFull, pager) {
|
|
54979
|
-
const outputPath =
|
|
56139
|
+
const outputPath = resolve26(outputRoot, output.path);
|
|
54980
56140
|
const newContent = output.content;
|
|
54981
56141
|
if (!existsSync18(outputPath)) {
|
|
54982
56142
|
pager.write(chalk2.green(`+ ${outputPath} (new file)`));
|
|
@@ -55049,7 +56209,7 @@ async function diffCommand(options) {
|
|
|
55049
56209
|
return;
|
|
55050
56210
|
}
|
|
55051
56211
|
const lockfile = await loadDiffLockfile(projectRoot);
|
|
55052
|
-
const vendorDir =
|
|
56212
|
+
const vendorDir = resolve26(projectRoot, ".promptscript/vendor");
|
|
55053
56213
|
if (!isJsonFormat) spinner.text = "Resolving registry...";
|
|
55054
56214
|
const registry2 = await resolveRegistryPath(config, {
|
|
55055
56215
|
vendorDir,
|
|
@@ -55060,11 +56220,11 @@ async function diffCommand(options) {
|
|
|
55060
56220
|
const logger = createDiffLogger(isJsonFormat);
|
|
55061
56221
|
const prettierOptions = await resolvePrettierOptions(config, projectRoot);
|
|
55062
56222
|
const skillContent = config.includePromptScriptSkill === false ? void 0 : await loadBundledSkillContent(logger);
|
|
55063
|
-
const outputRoot =
|
|
56223
|
+
const outputRoot = resolve26(projectRoot, buildProfile?.output ?? config.output?.baseDir ?? ".");
|
|
55064
56224
|
const compiler = new Compiler({
|
|
55065
56225
|
resolver: {
|
|
55066
56226
|
registryPath: registry2.path,
|
|
55067
|
-
localPath:
|
|
56227
|
+
localPath: resolve26(projectRoot, ".promptscript"),
|
|
55068
56228
|
projectRoot,
|
|
55069
56229
|
vendorDir,
|
|
55070
56230
|
readOnly: true,
|
|
@@ -55081,7 +56241,7 @@ async function diffCommand(options) {
|
|
|
55081
56241
|
logger,
|
|
55082
56242
|
skillContent
|
|
55083
56243
|
});
|
|
55084
|
-
const entryPath =
|
|
56244
|
+
const entryPath = resolve26(
|
|
55085
56245
|
projectRoot,
|
|
55086
56246
|
buildProfile?.entry ?? config.input?.entry ?? "./.promptscript/project.prs"
|
|
55087
56247
|
);
|
|
@@ -55234,7 +56394,7 @@ function printSimpleDiff(existingLines, newLines, showFull, pager) {
|
|
|
55234
56394
|
// packages/cli/src/commands/check.ts
|
|
55235
56395
|
import { existsSync as existsSync19 } from "fs";
|
|
55236
56396
|
import { readFile as readFile19 } from "fs/promises";
|
|
55237
|
-
import { resolve as
|
|
56397
|
+
import { resolve as resolve27 } from "path";
|
|
55238
56398
|
import { parse as parseYaml8 } from "yaml";
|
|
55239
56399
|
init_src();
|
|
55240
56400
|
init_loader2();
|
|
@@ -55393,7 +56553,7 @@ async function checkCommand(_options) {
|
|
|
55393
56553
|
}
|
|
55394
56554
|
const effectiveEntries = getEffectiveEntryPaths(config);
|
|
55395
56555
|
for (const entry of effectiveEntries) {
|
|
55396
|
-
const entryPath =
|
|
56556
|
+
const entryPath = resolve27(entry);
|
|
55397
56557
|
const name = entry === (config.input?.entry ?? ".promptscript/project.prs") ? "Entry file" : `Entry file (${entry})`;
|
|
55398
56558
|
if (existsSync19(entryPath)) {
|
|
55399
56559
|
results.push({
|
|
@@ -55418,7 +56578,7 @@ async function checkCommand(_options) {
|
|
|
55418
56578
|
});
|
|
55419
56579
|
hasWarnings = true;
|
|
55420
56580
|
} else if (config.registry?.path && !config.registry.git) {
|
|
55421
|
-
const registryPath =
|
|
56581
|
+
const registryPath = resolve27(config.registry.path);
|
|
55422
56582
|
if (existsSync19(registryPath)) {
|
|
55423
56583
|
results.push({
|
|
55424
56584
|
name: "Registry path",
|
|
@@ -55503,10 +56663,10 @@ async function checkCommand(_options) {
|
|
|
55503
56663
|
message: rootTargets.length === 0 ? `${targetNames.size} target(s) configured in build profiles` : `${targetNames.size} target(s) configured`
|
|
55504
56664
|
});
|
|
55505
56665
|
}
|
|
55506
|
-
const existingEntries = effectiveEntries.filter((entry) => existsSync19(
|
|
56666
|
+
const existingEntries = effectiveEntries.filter((entry) => existsSync19(resolve27(entry)));
|
|
55507
56667
|
if (existingEntries.length > 0) {
|
|
55508
56668
|
try {
|
|
55509
|
-
const lockfilePath =
|
|
56669
|
+
const lockfilePath = resolve27("promptscript.lock");
|
|
55510
56670
|
let lockfile;
|
|
55511
56671
|
if (existsSync19(lockfilePath)) {
|
|
55512
56672
|
const parsed = parseYaml8(await readFile19(lockfilePath, "utf-8"), {
|
|
@@ -55522,12 +56682,12 @@ async function checkCommand(_options) {
|
|
|
55522
56682
|
message: "promptscript.lock"
|
|
55523
56683
|
});
|
|
55524
56684
|
}
|
|
55525
|
-
const vendorDir =
|
|
56685
|
+
const vendorDir = resolve27(".promptscript/vendor");
|
|
55526
56686
|
const registry2 = await resolveRegistryPath(config, { vendorDir, lockfile });
|
|
55527
56687
|
const compiler = new Compiler({
|
|
55528
56688
|
resolver: {
|
|
55529
|
-
registryPath:
|
|
55530
|
-
localPath:
|
|
56689
|
+
registryPath: resolve27(registry2.path),
|
|
56690
|
+
localPath: resolve27(".promptscript"),
|
|
55531
56691
|
vendorDir,
|
|
55532
56692
|
referenceRoots: registry2.repositoryUrl && registry2.repositoryPath ? { [registry2.repositoryUrl]: [registry2.repositoryPath] } : void 0,
|
|
55533
56693
|
lockfile,
|
|
@@ -55542,7 +56702,7 @@ async function checkCommand(_options) {
|
|
|
55542
56702
|
for (const entry of existingEntries) {
|
|
55543
56703
|
const name = entry === (config.input?.entry ?? ".promptscript/project.prs") ? "Project resolution" : `Project resolution (${entry})`;
|
|
55544
56704
|
try {
|
|
55545
|
-
const result = await compiler.compile(
|
|
56705
|
+
const result = await compiler.compile(resolve27(entry));
|
|
55546
56706
|
if (result.errors.length > 0) {
|
|
55547
56707
|
results.push({
|
|
55548
56708
|
name,
|
|
@@ -55629,7 +56789,7 @@ import { dirname as dirname18 } from "path";
|
|
|
55629
56789
|
|
|
55630
56790
|
// packages/cli/src/utils/version-check.ts
|
|
55631
56791
|
init_console();
|
|
55632
|
-
import { homedir as
|
|
56792
|
+
import { homedir as homedir5 } from "os";
|
|
55633
56793
|
import { join as join16 } from "path";
|
|
55634
56794
|
import { readFile as readFile20, writeFile as writeFile5, mkdir as mkdir6 } from "fs/promises";
|
|
55635
56795
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@promptscript/cli/latest";
|
|
@@ -55679,7 +56839,7 @@ function comparePrerelease2(current, latest) {
|
|
|
55679
56839
|
return 0;
|
|
55680
56840
|
}
|
|
55681
56841
|
function getCacheDir() {
|
|
55682
|
-
return join16(
|
|
56842
|
+
return join16(homedir5(), ".promptscript", ".cache");
|
|
55683
56843
|
}
|
|
55684
56844
|
function getCachePath() {
|
|
55685
56845
|
return join16(getCacheDir(), "version.json");
|
|
@@ -56107,7 +57267,7 @@ Then reference configs with \`@inherit\` or \`@use\`:
|
|
|
56107
57267
|
}
|
|
56108
57268
|
|
|
56109
57269
|
// packages/cli/src/utils/resolve-target-directory.ts
|
|
56110
|
-
import { join as join18, resolve as
|
|
57270
|
+
import { join as join18, resolve as resolve28 } from "path";
|
|
56111
57271
|
|
|
56112
57272
|
// packages/cli/src/utils/slugify.ts
|
|
56113
57273
|
function slugify(name) {
|
|
@@ -56118,7 +57278,7 @@ function slugify(name) {
|
|
|
56118
57278
|
async function resolveTargetDirectory(options, services) {
|
|
56119
57279
|
const { cwd, directoryArg, registryName, nonInteractive } = options;
|
|
56120
57280
|
if (directoryArg) {
|
|
56121
|
-
return
|
|
57281
|
+
return resolve28(directoryArg);
|
|
56122
57282
|
}
|
|
56123
57283
|
const empty = await isDirectoryEmpty(cwd, services.fs);
|
|
56124
57284
|
if (empty && nonInteractive) {
|
|
@@ -56237,7 +57397,7 @@ async function registryInitCommand(directory, options, services = createDefaultS
|
|
|
56237
57397
|
// packages/cli/src/commands/registry/validate.ts
|
|
56238
57398
|
init_services();
|
|
56239
57399
|
init_console();
|
|
56240
|
-
import { resolve as
|
|
57400
|
+
import { resolve as resolve29 } from "path";
|
|
56241
57401
|
|
|
56242
57402
|
// packages/cli/src/utils/registry-validator.ts
|
|
56243
57403
|
import { join as join19 } from "path";
|
|
@@ -56442,7 +57602,7 @@ async function registryValidateCommand(path2, options, services = createDefaultS
|
|
|
56442
57602
|
process.exitCode = 1;
|
|
56443
57603
|
return;
|
|
56444
57604
|
}
|
|
56445
|
-
const registryPath =
|
|
57605
|
+
const registryPath = resolve29(path2 ?? ".");
|
|
56446
57606
|
const spinner = createSpinner("Validating registry...").start();
|
|
56447
57607
|
try {
|
|
56448
57608
|
const result = await validateRegistry(registryPath, services);
|
|
@@ -56494,10 +57654,10 @@ async function registryValidateCommand(path2, options, services = createDefaultS
|
|
|
56494
57654
|
// packages/cli/src/commands/registry/publish.ts
|
|
56495
57655
|
init_services();
|
|
56496
57656
|
init_console();
|
|
56497
|
-
import { resolve as
|
|
57657
|
+
import { resolve as resolve30, join as join20 } from "path";
|
|
56498
57658
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
56499
57659
|
async function registryPublishCommand(path2, options, services = createDefaultServices()) {
|
|
56500
|
-
const registryPath =
|
|
57660
|
+
const registryPath = resolve30(path2 ?? ".");
|
|
56501
57661
|
try {
|
|
56502
57662
|
if (!options.force) {
|
|
56503
57663
|
const spinner2 = createSpinner("Validating registry...").start();
|
|
@@ -56535,7 +57695,7 @@ async function registryPublishCommand(path2, options, services = createDefaultSe
|
|
|
56535
57695
|
let gitRoot;
|
|
56536
57696
|
try {
|
|
56537
57697
|
git("rev-parse", "--is-inside-work-tree");
|
|
56538
|
-
gitRoot =
|
|
57698
|
+
gitRoot = resolve30(git("rev-parse", "--show-toplevel"));
|
|
56539
57699
|
} catch {
|
|
56540
57700
|
spinner.fail("Not a git repository");
|
|
56541
57701
|
ConsoleOutput.muted("Initialize with: git init");
|
|
@@ -56813,7 +57973,7 @@ init_console();
|
|
|
56813
57973
|
init_src();
|
|
56814
57974
|
import { writeFile as writeFile7, readFile as readFile25 } from "fs/promises";
|
|
56815
57975
|
import { existsSync as existsSync22 } from "fs";
|
|
56816
|
-
import { resolve as
|
|
57976
|
+
import { resolve as resolve33 } from "path";
|
|
56817
57977
|
import { parse as parseYaml12, stringify as stringifyYaml3 } from "yaml";
|
|
56818
57978
|
|
|
56819
57979
|
// packages/cli/src/commands/lock-scanner.ts
|
|
@@ -56836,6 +57996,63 @@ async function collectRemoteImports(entryPath, options) {
|
|
|
56836
57996
|
const visited = /* @__PURE__ */ new Set();
|
|
56837
57997
|
const seen = /* @__PURE__ */ new Set();
|
|
56838
57998
|
const results = [];
|
|
57999
|
+
function recordRemoteImport(path2, sourceFile, resolved) {
|
|
58000
|
+
const marker = parseRegistryMarker(resolved);
|
|
58001
|
+
if (!marker) {
|
|
58002
|
+
return false;
|
|
58003
|
+
}
|
|
58004
|
+
const key = `${marker.repoUrl}\0${marker.path}\0${marker.version}`;
|
|
58005
|
+
if (options.deduplicate === false || !seen.has(key)) {
|
|
58006
|
+
seen.add(key);
|
|
58007
|
+
results.push({
|
|
58008
|
+
...marker,
|
|
58009
|
+
...options.includeLocations ? {
|
|
58010
|
+
sourceFile,
|
|
58011
|
+
sourceLine: path2.loc.line,
|
|
58012
|
+
rawSource: path2.raw
|
|
58013
|
+
} : {}
|
|
58014
|
+
});
|
|
58015
|
+
}
|
|
58016
|
+
return true;
|
|
58017
|
+
}
|
|
58018
|
+
async function scanLocalReference(resolved) {
|
|
58019
|
+
if (!resolved.endsWith(".prs") && !resolved.endsWith(".md")) {
|
|
58020
|
+
return;
|
|
58021
|
+
}
|
|
58022
|
+
if (existsSync21(resolved)) {
|
|
58023
|
+
await scan(resolved);
|
|
58024
|
+
return;
|
|
58025
|
+
}
|
|
58026
|
+
if (resolved.endsWith(".prs")) {
|
|
58027
|
+
const skillDirectory = resolved.slice(0, -".prs".length);
|
|
58028
|
+
if (existsSync21(skillDirectory)) {
|
|
58029
|
+
const skillFile = join21(skillDirectory, "SKILL.md");
|
|
58030
|
+
if (existsSync21(skillFile)) {
|
|
58031
|
+
await scan(skillFile);
|
|
58032
|
+
}
|
|
58033
|
+
return;
|
|
58034
|
+
}
|
|
58035
|
+
}
|
|
58036
|
+
if (options.strict) {
|
|
58037
|
+
throw new Error(`Imported PromptScript file not found: ${resolved}`);
|
|
58038
|
+
}
|
|
58039
|
+
}
|
|
58040
|
+
async function scanReference(path2, sourceFile) {
|
|
58041
|
+
let resolved;
|
|
58042
|
+
try {
|
|
58043
|
+
resolved = loader.resolveRef(path2, sourceFile);
|
|
58044
|
+
} catch (error) {
|
|
58045
|
+
if (options.strict) {
|
|
58046
|
+
throw new Error(`Cannot resolve import '${path2.raw}' in ${sourceFile}`, {
|
|
58047
|
+
cause: error
|
|
58048
|
+
});
|
|
58049
|
+
}
|
|
58050
|
+
return;
|
|
58051
|
+
}
|
|
58052
|
+
if (!recordRemoteImport(path2, sourceFile, resolved)) {
|
|
58053
|
+
await scanLocalReference(resolved);
|
|
58054
|
+
}
|
|
58055
|
+
}
|
|
56839
58056
|
async function scan(filePath) {
|
|
56840
58057
|
if (visited.has(filePath)) return;
|
|
56841
58058
|
visited.add(filePath);
|
|
@@ -56856,54 +58073,16 @@ async function collectRemoteImports(entryPath, options) {
|
|
|
56856
58073
|
throw new Error(`Cannot scan invalid PromptScript file: ${filePath}: ${errors[0].message}`);
|
|
56857
58074
|
}
|
|
56858
58075
|
if (!ast) return;
|
|
56859
|
-
|
|
56860
|
-
|
|
56861
|
-
|
|
56862
|
-
|
|
56863
|
-
|
|
56864
|
-
|
|
56865
|
-
|
|
56866
|
-
|
|
56867
|
-
|
|
56868
|
-
|
|
56869
|
-
continue;
|
|
56870
|
-
}
|
|
56871
|
-
const marker = parseRegistryMarker(resolved);
|
|
56872
|
-
if (marker) {
|
|
56873
|
-
const key = `${marker.repoUrl}\0${marker.path}\0${marker.version}`;
|
|
56874
|
-
if (options.deduplicate === false || !seen.has(key)) {
|
|
56875
|
-
seen.add(key);
|
|
56876
|
-
results.push({
|
|
56877
|
-
...marker,
|
|
56878
|
-
...options.includeLocations ? {
|
|
56879
|
-
sourceFile: filePath,
|
|
56880
|
-
sourceLine: use.path.loc.line,
|
|
56881
|
-
rawSource: use.path.raw
|
|
56882
|
-
} : {}
|
|
56883
|
-
});
|
|
56884
|
-
}
|
|
56885
|
-
} else {
|
|
56886
|
-
if (!resolved.endsWith(".prs") && !resolved.endsWith(".md")) {
|
|
56887
|
-
continue;
|
|
56888
|
-
}
|
|
56889
|
-
if (existsSync21(resolved)) {
|
|
56890
|
-
await scan(resolved);
|
|
56891
|
-
continue;
|
|
56892
|
-
}
|
|
56893
|
-
if (resolved.endsWith(".prs")) {
|
|
56894
|
-
const skillDirectory = resolved.slice(0, -".prs".length);
|
|
56895
|
-
if (existsSync21(skillDirectory)) {
|
|
56896
|
-
const skillFile = join21(skillDirectory, "SKILL.md");
|
|
56897
|
-
if (existsSync21(skillFile)) {
|
|
56898
|
-
await scan(skillFile);
|
|
56899
|
-
}
|
|
56900
|
-
continue;
|
|
56901
|
-
}
|
|
56902
|
-
}
|
|
56903
|
-
if (options.strict) {
|
|
56904
|
-
throw new Error(`Imported PromptScript file not found: ${resolved}`);
|
|
56905
|
-
}
|
|
56906
|
-
}
|
|
58076
|
+
const inlineUses = ast.blocks.flatMap(
|
|
58077
|
+
(block) => block.name === "skills" && (block.content.type === "ObjectContent" || block.content.type === "MixedContent") ? block.content.inlineUses ?? [] : []
|
|
58078
|
+
);
|
|
58079
|
+
const paths = [
|
|
58080
|
+
...ast.inherit ? [ast.inherit.path] : [],
|
|
58081
|
+
...ast.uses.map((use) => use.path),
|
|
58082
|
+
...inlineUses.map((use) => use.path)
|
|
58083
|
+
];
|
|
58084
|
+
for (const path2 of paths) {
|
|
58085
|
+
await scanReference(path2, filePath);
|
|
56907
58086
|
}
|
|
56908
58087
|
}
|
|
56909
58088
|
await scan(entryPath);
|
|
@@ -56911,16 +58090,16 @@ async function collectRemoteImports(entryPath, options) {
|
|
|
56911
58090
|
}
|
|
56912
58091
|
|
|
56913
58092
|
// packages/cli/src/commands/lock-references.ts
|
|
56914
|
-
import { homedir as
|
|
56915
|
-
import { resolve as
|
|
58093
|
+
import { homedir as homedir6 } from "os";
|
|
58094
|
+
import { resolve as resolve32, join as join22 } from "path";
|
|
56916
58095
|
|
|
56917
58096
|
// packages/cli/src/commands/lock-reference-scanner.ts
|
|
56918
58097
|
import { readFile as readFile24 } from "fs/promises";
|
|
56919
|
-
import { dirname as dirname20, isAbsolute as
|
|
58098
|
+
import { dirname as dirname20, isAbsolute as isAbsolute16, relative as relative18, resolve as resolve31, sep as sep12 } from "path";
|
|
56920
58099
|
async function collectRegistryReferences(ast, roots, existing = {}, lockedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
56921
58100
|
const references = {};
|
|
56922
58101
|
const sortedRoots = [...roots].sort(
|
|
56923
|
-
(left, right) =>
|
|
58102
|
+
(left, right) => resolve31(right.cachePath).length - resolve31(left.cachePath).length
|
|
56924
58103
|
);
|
|
56925
58104
|
for (const block of ast.blocks) {
|
|
56926
58105
|
if (block.name !== "skills" || block.content.type !== "ObjectContent") {
|
|
@@ -56934,7 +58113,7 @@ async function collectRegistryReferences(ast, roots, existing = {}, lockedAt = (
|
|
|
56934
58113
|
if (!Array.isArray(refs)) continue;
|
|
56935
58114
|
for (const ref of refs) {
|
|
56936
58115
|
if (typeof ref !== "string") continue;
|
|
56937
|
-
const referencePath =
|
|
58116
|
+
const referencePath = isAbsolute16(ref) ? resolve31(ref) : resolve31(dirname20(block.loc.file), ref);
|
|
56938
58117
|
const root = sortedRoots.find(
|
|
56939
58118
|
(candidate) => isInsideCachePath(referencePath, candidate.cachePath)
|
|
56940
58119
|
);
|
|
@@ -56942,7 +58121,7 @@ async function collectRegistryReferences(ast, roots, existing = {}, lockedAt = (
|
|
|
56942
58121
|
if (!await isRealPathInside(referencePath, root.cachePath)) {
|
|
56943
58122
|
throw new Error(`Registry reference is missing or escapes its repository: ${ref}`);
|
|
56944
58123
|
}
|
|
56945
|
-
const relativePath =
|
|
58124
|
+
const relativePath = relative18(resolve31(root.cachePath), referencePath).split(sep12).join("/");
|
|
56946
58125
|
const key = buildReferenceKey(root.repoUrl, relativePath, root.version);
|
|
56947
58126
|
const hash = hashContent(await readFile24(referencePath));
|
|
56948
58127
|
const previous = existing[key];
|
|
@@ -56962,9 +58141,9 @@ function normalizeRepository(value) {
|
|
|
56962
58141
|
}
|
|
56963
58142
|
async function generateLockfileReferences(config, entryPath, localPath, lockfile, imports) {
|
|
56964
58143
|
const registry2 = await resolveRegistryPath(config, { lockfile });
|
|
56965
|
-
const cacheDir = join22(
|
|
58144
|
+
const cacheDir = join22(homedir6(), ".promptscript", "cache");
|
|
56966
58145
|
const resolver = new Resolver({
|
|
56967
|
-
registryPath:
|
|
58146
|
+
registryPath: resolve32(registry2.path),
|
|
56968
58147
|
localPath,
|
|
56969
58148
|
lockfile,
|
|
56970
58149
|
registries: config.registries,
|
|
@@ -57095,14 +58274,15 @@ async function lockCommand(options) {
|
|
|
57095
58274
|
}
|
|
57096
58275
|
}
|
|
57097
58276
|
const projectRoot = process.cwd();
|
|
57098
|
-
const localPath =
|
|
57099
|
-
const entryPath = config.input?.entry ?
|
|
58277
|
+
const localPath = resolve33(projectRoot, ".promptscript");
|
|
58278
|
+
const entryPath = config.input?.entry ? resolve33(projectRoot, config.input.entry) : resolve33(localPath, "project.prs");
|
|
57100
58279
|
const registry2 = await resolveRegistryPath(config, {
|
|
58280
|
+
allowMissingLockEntry: true,
|
|
57101
58281
|
...!options.update && Object.keys(existing.dependencies).length > 0 ? { lockfile: existing } : {}
|
|
57102
58282
|
});
|
|
57103
58283
|
const scannedImports = await collectRemoteImports(entryPath, {
|
|
57104
58284
|
localPath,
|
|
57105
|
-
registryPath:
|
|
58285
|
+
registryPath: resolve33(registry2.path),
|
|
57106
58286
|
registries: config.registries,
|
|
57107
58287
|
strict: true
|
|
57108
58288
|
});
|
|
@@ -57116,7 +58296,8 @@ async function lockCommand(options) {
|
|
|
57116
58296
|
requestedDependencies.set(defaultGitRegistry.url, {
|
|
57117
58297
|
versions: /* @__PURE__ */ new Set([defaultGitRegistry.ref ?? "main"]),
|
|
57118
58298
|
...defaultGitRegistry.fallbackUrl ? { fallbackUrl: defaultGitRegistry.fallbackUrl } : {},
|
|
57119
|
-
...defaultGitRegistry.auth ? { auth: defaultGitRegistry.auth } : {}
|
|
58299
|
+
...defaultGitRegistry.auth ? { auth: defaultGitRegistry.auth } : {},
|
|
58300
|
+
...defaultGitRegistry.timeout !== void 0 ? { timeout: defaultGitRegistry.timeout } : {}
|
|
57120
58301
|
});
|
|
57121
58302
|
}
|
|
57122
58303
|
for (const [, entry] of aliasEntries) {
|
|
@@ -57128,6 +58309,9 @@ async function lockCommand(options) {
|
|
|
57128
58309
|
if (typeof entry !== "string" && entry.fallbackUrl) {
|
|
57129
58310
|
requested.fallbackUrl = entry.fallbackUrl;
|
|
57130
58311
|
}
|
|
58312
|
+
if (typeof entry !== "string" && entry.timeout !== void 0) {
|
|
58313
|
+
requested.timeout = entry.timeout;
|
|
58314
|
+
}
|
|
57131
58315
|
requestedDependencies.set(repoUrl, requested);
|
|
57132
58316
|
}
|
|
57133
58317
|
for (const imp of scannedImports) {
|
|
@@ -57152,7 +58336,8 @@ async function lockCommand(options) {
|
|
|
57152
58336
|
previous,
|
|
57153
58337
|
options.update === true && matchingRepos.includes(repoUrl),
|
|
57154
58338
|
requested.fallbackUrl,
|
|
57155
|
-
requested.auth
|
|
58339
|
+
requested.auth,
|
|
58340
|
+
{ timeout: requested.timeout }
|
|
57156
58341
|
);
|
|
57157
58342
|
if (previous?.skills?.length && previous.commit !== resolved.commit) {
|
|
57158
58343
|
throw new Error(
|
|
@@ -57322,7 +58507,7 @@ import {
|
|
|
57322
58507
|
writeFile as writeFile8
|
|
57323
58508
|
} from "fs/promises";
|
|
57324
58509
|
import { existsSync as existsSync23 } from "fs";
|
|
57325
|
-
import { basename as basename15, dirname as dirname21, join as join23, resolve as
|
|
58510
|
+
import { basename as basename15, dirname as dirname21, join as join23, resolve as resolve34 } from "path";
|
|
57326
58511
|
import { parse as parseYaml13 } from "yaml";
|
|
57327
58512
|
var VENDOR_DIR = ".promptscript/vendor";
|
|
57328
58513
|
async function vendorSyncCommand(options) {
|
|
@@ -57343,7 +58528,7 @@ async function vendorSyncCommand(options) {
|
|
|
57343
58528
|
spinner.succeed(`Dry run completed (${deps.length} dependencies ready)`);
|
|
57344
58529
|
return;
|
|
57345
58530
|
}
|
|
57346
|
-
const vendorDir =
|
|
58531
|
+
const vendorDir = resolve34(VENDOR_DIR);
|
|
57347
58532
|
await validateVendorParentDirectory(vendorDir, true);
|
|
57348
58533
|
const recoveredBackup = await recoverVendorDirectory(vendorDir);
|
|
57349
58534
|
if (recoveredBackup) {
|
|
@@ -57416,7 +58601,7 @@ async function vendorCheckCommand(_options) {
|
|
|
57416
58601
|
}
|
|
57417
58602
|
const deps = getRepositoryDependencies(lockfile);
|
|
57418
58603
|
validateDependencyPaths(deps);
|
|
57419
|
-
const vendorDir =
|
|
58604
|
+
const vendorDir = resolve34(VENDOR_DIR);
|
|
57420
58605
|
await validateVendorParentDirectory(vendorDir, false);
|
|
57421
58606
|
const recoveredBackup = await recoverVendorDirectory(vendorDir);
|
|
57422
58607
|
if (recoveredBackup) {
|
|
@@ -57597,10 +58782,10 @@ init_loader2();
|
|
|
57597
58782
|
init_user_config();
|
|
57598
58783
|
init_console();
|
|
57599
58784
|
import { existsSync as existsSync24, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
57600
|
-
import { join as join24, resolve as
|
|
57601
|
-
import { homedir as
|
|
58785
|
+
import { join as join24, resolve as resolve35 } from "path";
|
|
58786
|
+
import { homedir as homedir7 } from "os";
|
|
57602
58787
|
import { parse as parseYaml14 } from "yaml";
|
|
57603
|
-
var CACHE_BASE = join24(
|
|
58788
|
+
var CACHE_BASE = join24(homedir7(), ".promptscript", "cache");
|
|
57604
58789
|
var LOCKFILE_PATH2 = "promptscript.lock";
|
|
57605
58790
|
var VENDOR_DIR2 = ".promptscript/vendor";
|
|
57606
58791
|
function loadLockfile2() {
|
|
@@ -57609,7 +58794,7 @@ function loadLockfile2() {
|
|
|
57609
58794
|
}
|
|
57610
58795
|
const parsed = parseYaml14(readFileSync5(LOCKFILE_PATH2, "utf-8"), { maxAliasCount: 100 });
|
|
57611
58796
|
if (!isValidLockfile(parsed)) {
|
|
57612
|
-
throw new Error(`Invalid lockfile: ${
|
|
58797
|
+
throw new Error(`Invalid lockfile: ${resolve35(LOCKFILE_PATH2)}`);
|
|
57613
58798
|
}
|
|
57614
58799
|
return parsed;
|
|
57615
58800
|
}
|
|
@@ -57710,14 +58895,14 @@ async function resolveCommand(importPath, options) {
|
|
|
57710
58895
|
}
|
|
57711
58896
|
const reference = parseImportReference(importPath);
|
|
57712
58897
|
const loader = new FileLoader({
|
|
57713
|
-
registryPath:
|
|
57714
|
-
localPath:
|
|
58898
|
+
registryPath: resolve35(config.registry?.path ?? "./registry"),
|
|
58899
|
+
localPath: resolve35("./.promptscript"),
|
|
57715
58900
|
registries
|
|
57716
58901
|
});
|
|
57717
|
-
const resolvedReference = loader.resolveRef(reference,
|
|
58902
|
+
const resolvedReference = loader.resolveRef(reference, resolve35(".promptscript/project.prs"));
|
|
57718
58903
|
const marker = parseRegistryMarker(resolvedReference);
|
|
57719
58904
|
if (!marker) {
|
|
57720
|
-
const localPath =
|
|
58905
|
+
const localPath = resolve35(resolvedReference);
|
|
57721
58906
|
printResolution(
|
|
57722
58907
|
{
|
|
57723
58908
|
import: importPath,
|
|
@@ -57738,7 +58923,7 @@ async function resolveCommand(importPath, options) {
|
|
|
57738
58923
|
const cacheVersion = marker.version || "latest";
|
|
57739
58924
|
const registryCache = new RegistryCache(CACHE_BASE);
|
|
57740
58925
|
const cachePath = registryCache.getCachePath(marker.repoUrl, cacheVersion);
|
|
57741
|
-
const vendorDir =
|
|
58926
|
+
const vendorDir = resolve35(VENDOR_DIR2);
|
|
57742
58927
|
const manifest = await loadVendorManifest(vendorDir);
|
|
57743
58928
|
let repositoryPath = cachePath;
|
|
57744
58929
|
let source = await registryCache.has(marker.repoUrl, cacheVersion) ? "cache" : "missing";
|
|
@@ -57747,7 +58932,9 @@ async function resolveCommand(importPath, options) {
|
|
|
57747
58932
|
}
|
|
57748
58933
|
if (manifest) {
|
|
57749
58934
|
if (!lockKey || !dependency) {
|
|
57750
|
-
throw new Error(
|
|
58935
|
+
throw new Error(
|
|
58936
|
+
`Vendored dependency is not pinned by the lockfile: ${marker.repoUrl}. Run 'prs lock' to pin it, then re-run this command.`
|
|
58937
|
+
);
|
|
57751
58938
|
}
|
|
57752
58939
|
const vendoredPath = await resolveVendoredRepository(
|
|
57753
58940
|
vendorDir,
|
|
@@ -57766,7 +58953,10 @@ async function resolveCommand(importPath, options) {
|
|
|
57766
58953
|
cachePath,
|
|
57767
58954
|
".git",
|
|
57768
58955
|
dependency.commit,
|
|
57769
|
-
/* @__PURE__ */ new Set([".prs-registry-meta.json"])
|
|
58956
|
+
/* @__PURE__ */ new Set([".prs-registry-meta.json"]),
|
|
58957
|
+
{
|
|
58958
|
+
allowPartial: true
|
|
58959
|
+
}
|
|
57770
58960
|
);
|
|
57771
58961
|
} catch {
|
|
57772
58962
|
source = "stale cache";
|
|
@@ -57811,28 +59001,28 @@ async function resolveCommand(importPath, options) {
|
|
|
57811
59001
|
|
|
57812
59002
|
// packages/cli/src/commands/inspect.ts
|
|
57813
59003
|
import { existsSync as existsSync25 } from "fs";
|
|
57814
|
-
import { resolve as
|
|
59004
|
+
import { resolve as resolve36, basename as basename16 } from "path";
|
|
57815
59005
|
init_loader2();
|
|
57816
59006
|
init_console();
|
|
57817
59007
|
async function inspectCommand(skillName, options) {
|
|
57818
59008
|
const isJson = options.format === "json";
|
|
57819
59009
|
const spinner = isJson ? createSpinner("").stop() : createSpinner("Resolving...").start();
|
|
57820
59010
|
try {
|
|
57821
|
-
const projectRoot = options.cwd ?
|
|
57822
|
-
const configPath = options.config ?
|
|
59011
|
+
const projectRoot = options.cwd ? resolve36(options.cwd) : process.cwd();
|
|
59012
|
+
const configPath = options.config ? resolve36(projectRoot, options.config) : options.cwd ? CONFIG_FILES.map((file) => resolve36(projectRoot, file)).find((file) => existsSync25(file)) : void 0;
|
|
57823
59013
|
if (options.cwd && !configPath) {
|
|
57824
59014
|
throw new Error(`No PromptScript configuration found in ${projectRoot}. Run: prs init`);
|
|
57825
59015
|
}
|
|
57826
59016
|
const config = await loadConfig(configPath);
|
|
57827
59017
|
const registry2 = await resolveRegistryPath(config);
|
|
57828
|
-
const localPath =
|
|
59018
|
+
const localPath = resolve36(projectRoot, ".promptscript");
|
|
57829
59019
|
const resolver = new Resolver({
|
|
57830
|
-
registryPath: registry2.isRemote ? registry2.path :
|
|
59020
|
+
registryPath: registry2.isRemote ? registry2.path : resolve36(projectRoot, registry2.path),
|
|
57831
59021
|
localPath,
|
|
57832
59022
|
projectRoot,
|
|
57833
59023
|
registries: config.registries
|
|
57834
59024
|
});
|
|
57835
|
-
const entryPath =
|
|
59025
|
+
const entryPath = resolve36(projectRoot, config.input?.entry ?? ".promptscript/project.prs");
|
|
57836
59026
|
if (!existsSync25(entryPath)) {
|
|
57837
59027
|
spinner.stop();
|
|
57838
59028
|
ConsoleOutput.error(`Entry file not found: ${entryPath}`);
|
|
@@ -58010,7 +59200,7 @@ function outputJson(skillName, skill, trace, sealed, composedFrom, baseSource) {
|
|
|
58010
59200
|
// packages/cli/src/commands/explain.ts
|
|
58011
59201
|
init_src();
|
|
58012
59202
|
import { existsSync as existsSync26 } from "fs";
|
|
58013
|
-
import { relative as
|
|
59203
|
+
import { relative as relative19, resolve as resolve37 } from "path";
|
|
58014
59204
|
init_loader2();
|
|
58015
59205
|
init_console();
|
|
58016
59206
|
function normalizePath3(path2) {
|
|
@@ -58091,7 +59281,7 @@ function findMatches(ast, trace, requestedPath) {
|
|
|
58091
59281
|
}
|
|
58092
59282
|
function sanitizePath(path2, options) {
|
|
58093
59283
|
if (options.absolutePaths || !path2.startsWith("/")) return path2;
|
|
58094
|
-
const relativePath =
|
|
59284
|
+
const relativePath = relative19(options.projectRoot, path2);
|
|
58095
59285
|
return relativePath || ".";
|
|
58096
59286
|
}
|
|
58097
59287
|
function sanitizeText(value, options) {
|
|
@@ -58221,24 +59411,24 @@ async function explainCommand(targetPath, options) {
|
|
|
58221
59411
|
const isJson = options.format === "json";
|
|
58222
59412
|
const spinner = isJson ? createSpinner("").stop() : createSpinner("Resolving...").start();
|
|
58223
59413
|
const outputOptions = {
|
|
58224
|
-
projectRoot:
|
|
59414
|
+
projectRoot: resolve37(options.cwd ?? process.cwd()),
|
|
58225
59415
|
absolutePaths: options.absolutePaths === true
|
|
58226
59416
|
};
|
|
58227
59417
|
try {
|
|
58228
59418
|
const projectRoot = outputOptions.projectRoot;
|
|
58229
|
-
const configPath = options.config ?
|
|
59419
|
+
const configPath = options.config ? resolve37(projectRoot, options.config) : options.cwd ? CONFIG_FILES.map((file) => resolve37(projectRoot, file)).find((file) => existsSync26(file)) : void 0;
|
|
58230
59420
|
if (options.cwd && !configPath) {
|
|
58231
59421
|
throw new Error(`No PromptScript configuration found in ${projectRoot}. Run: prs init`);
|
|
58232
59422
|
}
|
|
58233
59423
|
const config = await loadConfig(configPath);
|
|
58234
59424
|
const registry2 = await resolveRegistryPath(config);
|
|
58235
59425
|
const resolver = new Resolver({
|
|
58236
|
-
registryPath: registry2.isRemote ? registry2.path :
|
|
58237
|
-
localPath:
|
|
59426
|
+
registryPath: registry2.isRemote ? registry2.path : resolve37(projectRoot, registry2.path),
|
|
59427
|
+
localPath: resolve37(projectRoot, ".promptscript"),
|
|
58238
59428
|
projectRoot,
|
|
58239
59429
|
registries: config.registries
|
|
58240
59430
|
});
|
|
58241
|
-
const entryPath =
|
|
59431
|
+
const entryPath = resolve37(projectRoot, config.input?.entry ?? ".promptscript/project.prs");
|
|
58242
59432
|
if (!existsSync26(entryPath)) {
|
|
58243
59433
|
spinner.stop();
|
|
58244
59434
|
ConsoleOutput.error(`Entry file not found: ${sanitizePath(entryPath, outputOptions)}`);
|
|
@@ -58300,7 +59490,7 @@ init_console();
|
|
|
58300
59490
|
// packages/cli/src/commands/import.ts
|
|
58301
59491
|
init_src3();
|
|
58302
59492
|
init_console();
|
|
58303
|
-
import { resolve as
|
|
59493
|
+
import { resolve as resolve38, join as join25, basename as basename17 } from "path";
|
|
58304
59494
|
import { writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync27 } from "fs";
|
|
58305
59495
|
import { stat as stat2, readdir as readdir8, readFile as readFile27, writeFile as writeFile9, mkdir as mkdir9 } from "fs/promises";
|
|
58306
59496
|
function parseInstructionFrontmatter(content) {
|
|
@@ -58355,7 +59545,7 @@ async function importDirectory(dirPath) {
|
|
|
58355
59545
|
return lines.join("\n");
|
|
58356
59546
|
}
|
|
58357
59547
|
async function importCommand(file, options) {
|
|
58358
|
-
const filepath =
|
|
59548
|
+
const filepath = resolve38(file);
|
|
58359
59549
|
const inputStat = await stat2(filepath).catch(() => null);
|
|
58360
59550
|
if (inputStat?.isDirectory()) {
|
|
58361
59551
|
try {
|
|
@@ -58364,7 +59554,7 @@ async function importCommand(file, options) {
|
|
|
58364
59554
|
console.log(prsContent);
|
|
58365
59555
|
return;
|
|
58366
59556
|
}
|
|
58367
|
-
const outputDir =
|
|
59557
|
+
const outputDir = resolve38(options.output ?? ".promptscript");
|
|
58368
59558
|
const outputPath = join25(outputDir, "imported.prs");
|
|
58369
59559
|
if (existsSync27(outputPath) && !options.force) {
|
|
58370
59560
|
reportOutputConflict(outputPath);
|
|
@@ -58398,11 +59588,11 @@ async function importCommand(file, options) {
|
|
|
58398
59588
|
console.log(result.prsContent);
|
|
58399
59589
|
return;
|
|
58400
59590
|
}
|
|
58401
|
-
const outputDir =
|
|
59591
|
+
const outputDir = resolve38(options.output ?? ".promptscript");
|
|
58402
59592
|
if (!existsSync27(outputDir)) {
|
|
58403
59593
|
mkdirSync(outputDir, { recursive: true });
|
|
58404
59594
|
}
|
|
58405
|
-
const outputPath =
|
|
59595
|
+
const outputPath = resolve38(outputDir, "imported.prs");
|
|
58406
59596
|
if (existsSync27(outputPath) && !options.force) {
|
|
58407
59597
|
spinner.stop();
|
|
58408
59598
|
reportOutputConflict(outputPath);
|
|
@@ -58772,11 +59962,11 @@ init_console();
|
|
|
58772
59962
|
// packages/cli/src/telemetry/config.ts
|
|
58773
59963
|
import { existsSync as existsSync30 } from "node:fs";
|
|
58774
59964
|
import { mkdir as mkdir10, readFile as readFile29, rename as rename2, writeFile as writeFile10 } from "node:fs/promises";
|
|
58775
|
-
import { dirname as dirname22, isAbsolute as
|
|
59965
|
+
import { dirname as dirname22, isAbsolute as isAbsolute17, join as join29, resolve as resolve39 } from "node:path";
|
|
58776
59966
|
import { parseDocument as parseDocument2 } from "yaml";
|
|
58777
59967
|
|
|
58778
59968
|
// packages/telemetry/src/config.ts
|
|
58779
|
-
import { homedir as
|
|
59969
|
+
import { homedir as homedir8 } from "node:os";
|
|
58780
59970
|
import { join as join27 } from "node:path";
|
|
58781
59971
|
var DEFAULT_TELEMETRY_ENDPOINT = "https://telemetry.guziak.net/v1/events";
|
|
58782
59972
|
function isDisabled(value) {
|
|
@@ -58825,7 +60015,7 @@ function resolveTelemetryConfig(input2 = {}) {
|
|
|
58825
60015
|
return {
|
|
58826
60016
|
enabled: vetoes.length === 0,
|
|
58827
60017
|
endpoint,
|
|
58828
|
-
cacheDirectory: input2.cacheDirectory ?? join27(
|
|
60018
|
+
cacheDirectory: input2.cacheDirectory ?? join27(homedir8(), ".promptscript", ".cache"),
|
|
58829
60019
|
vetoes
|
|
58830
60020
|
};
|
|
58831
60021
|
}
|
|
@@ -58911,7 +60101,8 @@ var promptscript_v1_default = {
|
|
|
58911
60101
|
"mimo",
|
|
58912
60102
|
"deep-agents",
|
|
58913
60103
|
"forgecode",
|
|
58914
|
-
"hermes"
|
|
60104
|
+
"hermes",
|
|
60105
|
+
"gitlab-duo"
|
|
58915
60106
|
],
|
|
58916
60107
|
limits: {
|
|
58917
60108
|
maxEvents: 25,
|
|
@@ -59433,7 +60624,7 @@ var FLUSH_BUDGET_MS = 5e3;
|
|
|
59433
60624
|
var MAX_BATCHES_PER_FLUSH = 3;
|
|
59434
60625
|
var COMPLETION_RETRIES = 20;
|
|
59435
60626
|
function delay(milliseconds) {
|
|
59436
|
-
return new Promise((
|
|
60627
|
+
return new Promise((resolve43) => setTimeout(resolve43, milliseconds));
|
|
59437
60628
|
}
|
|
59438
60629
|
async function persistCompletion(cacheDirectory, remainingRecords, rejectedRecords) {
|
|
59439
60630
|
for (let attempt = 0; attempt < COMPLETION_RETRIES; attempt += 1) {
|
|
@@ -59678,7 +60869,7 @@ function runtimeMetadata(appVersion, input2 = {}) {
|
|
|
59678
60869
|
init_loader2();
|
|
59679
60870
|
init_user_config();
|
|
59680
60871
|
function resolveFrom(baseDirectory, value) {
|
|
59681
|
-
return
|
|
60872
|
+
return isAbsolute17(value) ? value : resolve39(baseDirectory, value);
|
|
59682
60873
|
}
|
|
59683
60874
|
function findProjectConfig(cwd, customPath) {
|
|
59684
60875
|
if (customPath !== void 0) {
|
|
@@ -59723,7 +60914,7 @@ async function readUserTelemetrySetting(configPath) {
|
|
|
59723
60914
|
}
|
|
59724
60915
|
}
|
|
59725
60916
|
async function resolveCliTelemetryConfig(options = {}) {
|
|
59726
|
-
const cwd = options.cwd === void 0 ? process.cwd() :
|
|
60917
|
+
const cwd = options.cwd === void 0 ? process.cwd() : resolve39(process.cwd(), options.cwd);
|
|
59727
60918
|
const userSetting = await readUserTelemetrySetting(options.userConfigPath ?? USER_CONFIG_PATH);
|
|
59728
60919
|
const projectPath = findProjectConfig(cwd, options.config);
|
|
59729
60920
|
const environmentConfig = process.env["PROMPTSCRIPT_CONFIG"];
|
|
@@ -59937,13 +61128,41 @@ import {
|
|
|
59937
61128
|
chmod,
|
|
59938
61129
|
open as open4,
|
|
59939
61130
|
lstat as lstat11,
|
|
59940
|
-
stat as stat3
|
|
59941
|
-
|
|
59942
|
-
|
|
59943
|
-
|
|
59944
|
-
import {
|
|
61131
|
+
stat as stat3,
|
|
61132
|
+
cp,
|
|
61133
|
+
mkdir as mkdir11
|
|
61134
|
+
} from "node:fs/promises";
|
|
61135
|
+
import { resolve as resolve40, join as join30, basename as basename18, dirname as dirname23, relative as relative20, isAbsolute as isAbsolute18, sep as sep13 } from "node:path";
|
|
61136
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
61137
|
+
import { tmpdir as tmpdir3, homedir as homedir9 } from "node:os";
|
|
59945
61138
|
import { parse as parseYaml15, stringify as stringifyYaml4 } from "yaml";
|
|
59946
61139
|
var REMOTE_SOURCE_PATTERN = /^[a-zA-Z0-9][\w.-]*\.[a-zA-Z]{2,}\/.+/;
|
|
61140
|
+
var MANAGED_SKILLS_DIR = join30(".promptscript", "skills");
|
|
61141
|
+
var USE_PATH_PATTERN = /^\.{1,2}\/[a-zA-Z0-9_/.-]+$/;
|
|
61142
|
+
function expandHomePath(input2) {
|
|
61143
|
+
if (input2 === "~") return homedir9();
|
|
61144
|
+
if (input2.startsWith("~/") || input2.startsWith("~\\")) {
|
|
61145
|
+
return join30(homedir9(), input2.slice(2));
|
|
61146
|
+
}
|
|
61147
|
+
return input2;
|
|
61148
|
+
}
|
|
61149
|
+
function isLocalSkillSource(source) {
|
|
61150
|
+
const trimmed = source.trim();
|
|
61151
|
+
if (trimmed.length === 0) return false;
|
|
61152
|
+
if (trimmed.startsWith("~")) {
|
|
61153
|
+
return trimmed === "~" || trimmed.slice(1, 2) === "/" || trimmed.slice(1, 2) === "\\";
|
|
61154
|
+
}
|
|
61155
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("\\")) return true;
|
|
61156
|
+
if (/^[a-zA-Z]:[\\/]/.test(trimmed)) return true;
|
|
61157
|
+
if (trimmed.startsWith("./") || trimmed.startsWith(".\\")) return true;
|
|
61158
|
+
if (trimmed.startsWith("../") || trimmed.startsWith("..\\")) return true;
|
|
61159
|
+
if (REMOTE_SOURCE_PATTERN.test(trimmed)) return false;
|
|
61160
|
+
return existsSync31(resolve40(process.cwd(), expandHomePath(trimmed)));
|
|
61161
|
+
}
|
|
61162
|
+
function toUsePath(entryDir, skillDir) {
|
|
61163
|
+
const rel = relative20(entryDir, skillDir).split(sep13).join("/");
|
|
61164
|
+
return rel.startsWith("..") ? rel : `./${rel}`;
|
|
61165
|
+
}
|
|
59947
61166
|
function parseSkillSource(source) {
|
|
59948
61167
|
const versionSeparator = source.lastIndexOf("@");
|
|
59949
61168
|
if (versionSeparator === -1) {
|
|
@@ -59962,7 +61181,7 @@ function toDependencyPin(dependency) {
|
|
|
59962
61181
|
integrity: dependency.integrity
|
|
59963
61182
|
};
|
|
59964
61183
|
}
|
|
59965
|
-
function resolveSkillDependency(repoUrl, requestedVersions, existing, forceUpdate, gitUrl, timeout =
|
|
61184
|
+
function resolveSkillDependency(repoUrl, requestedVersions, existing, forceUpdate, gitUrl, timeout = void 0) {
|
|
59966
61185
|
return gitUrl ? resolveRemoteDependency(
|
|
59967
61186
|
repoUrl,
|
|
59968
61187
|
requestedVersions,
|
|
@@ -60006,32 +61225,32 @@ async function collectProjectRemoteImports(entryFile) {
|
|
|
60006
61225
|
const projectRoot = process.cwd();
|
|
60007
61226
|
const entryFiles = /* @__PURE__ */ new Set();
|
|
60008
61227
|
if (entryFile) {
|
|
60009
|
-
entryFiles.add(
|
|
61228
|
+
entryFiles.add(resolve40(entryFile));
|
|
60010
61229
|
}
|
|
60011
61230
|
if (config) {
|
|
60012
61231
|
if (config.input?.entry) {
|
|
60013
|
-
entryFiles.add(
|
|
61232
|
+
entryFiles.add(resolve40(projectRoot, config.input.entry));
|
|
60014
61233
|
}
|
|
60015
61234
|
for (const build of Object.values(config.builds ?? {})) {
|
|
60016
61235
|
if (build.entry) {
|
|
60017
|
-
entryFiles.add(
|
|
61236
|
+
entryFiles.add(resolve40(projectRoot, build.entry));
|
|
60018
61237
|
}
|
|
60019
61238
|
}
|
|
60020
|
-
const defaultEntry =
|
|
61239
|
+
const defaultEntry = resolve40(projectRoot, ".promptscript/project.prs");
|
|
60021
61240
|
if (!config.input?.entry && (existsSync31(defaultEntry) || entryFiles.size === 0)) {
|
|
60022
61241
|
entryFiles.add(defaultEntry);
|
|
60023
61242
|
}
|
|
60024
61243
|
} else if (!entryFile) {
|
|
60025
|
-
const defaultEntry =
|
|
61244
|
+
const defaultEntry = resolve40(projectRoot, ".promptscript/project.prs");
|
|
60026
61245
|
if (existsSync31(defaultEntry)) {
|
|
60027
61246
|
entryFiles.add(defaultEntry);
|
|
60028
61247
|
} else {
|
|
60029
|
-
const promptscriptDir =
|
|
61248
|
+
const promptscriptDir = resolve40(projectRoot, ".promptscript");
|
|
60030
61249
|
if (existsSync31(promptscriptDir)) {
|
|
60031
61250
|
const entries = await readdir9(promptscriptDir);
|
|
60032
61251
|
const prsFiles = entries.filter((entry) => entry.endsWith(".prs"));
|
|
60033
61252
|
if (prsFiles.length === 1) {
|
|
60034
|
-
entryFiles.add(
|
|
61253
|
+
entryFiles.add(resolve40(promptscriptDir, prsFiles[0]));
|
|
60035
61254
|
}
|
|
60036
61255
|
}
|
|
60037
61256
|
}
|
|
@@ -60040,7 +61259,7 @@ async function collectProjectRemoteImports(entryFile) {
|
|
|
60040
61259
|
for (const projectEntry of entryFiles) {
|
|
60041
61260
|
imports.push(
|
|
60042
61261
|
...await collectRemoteImports(projectEntry, {
|
|
60043
|
-
localPath:
|
|
61262
|
+
localPath: resolve40(projectRoot, ".promptscript"),
|
|
60044
61263
|
registries: config?.registries,
|
|
60045
61264
|
strict: true,
|
|
60046
61265
|
deduplicate: false,
|
|
@@ -60086,6 +61305,10 @@ function extractSkillFilePath(source) {
|
|
|
60086
61305
|
if (!subPath) return "SKILL.md";
|
|
60087
61306
|
return subPath.toLowerCase().endsWith(".md") ? subPath : `${subPath}/SKILL.md`;
|
|
60088
61307
|
}
|
|
61308
|
+
function getRemoteSkillDiagnosticPath(source) {
|
|
61309
|
+
const sourcePath = parseSkillSource(source).path.replace(/^https?:\/\//i, "");
|
|
61310
|
+
return sourcePath.toLowerCase().endsWith(".md") ? sourcePath : `${sourcePath}/SKILL.md`;
|
|
61311
|
+
}
|
|
60089
61312
|
function deriveSkillFolderName(source) {
|
|
60090
61313
|
const subPath = extractSubPath(source);
|
|
60091
61314
|
if (!subPath) return basename18(parseSkillSource(source).path);
|
|
@@ -60112,13 +61335,15 @@ async function fetchAndValidateRemoteSkill(source, options) {
|
|
|
60112
61335
|
const cloneDir = !skillFilePath.includes("/") ? join30(tmp, repoName) : tmp;
|
|
60113
61336
|
try {
|
|
60114
61337
|
const gitRegistry = createGitRegistry({
|
|
60115
|
-
url: repoUrl
|
|
60116
|
-
timeout: DEFAULT_GIT_TIMEOUT_MS
|
|
61338
|
+
url: repoUrl
|
|
60117
61339
|
});
|
|
60118
61340
|
const cloneRef = options.version === "latest" || /^[0-9a-f]{40}$/i.test(options.version) ? void 0 : options.version;
|
|
60119
|
-
await gitRegistry.cloneAtTag(repoUrl, cloneRef, cloneDir);
|
|
60120
|
-
await gitRegistry.checkoutCommit(cloneDir, options.commit);
|
|
60121
61341
|
const subPath = extractSubPath(source);
|
|
61342
|
+
const isMdSource = subPath.toLowerCase().endsWith(".md");
|
|
61343
|
+
const coneCandidate = isMdSource ? subPath.slice(0, subPath.lastIndexOf("/")) : subPath;
|
|
61344
|
+
const sparseCone = coneCandidate.length > 0 ? coneCandidate : void 0;
|
|
61345
|
+
await gitRegistry.cloneAtTag(repoUrl, cloneRef, cloneDir, void 0, sparseCone);
|
|
61346
|
+
await gitRegistry.checkoutCommit(cloneDir, options.commit);
|
|
60122
61347
|
if (!subPath.toLowerCase().endsWith(".md") && existsSync31(join30(cloneDir, `${subPath}.prs`))) {
|
|
60123
61348
|
return {
|
|
60124
61349
|
content: "",
|
|
@@ -60159,7 +61384,16 @@ async function fetchAndValidateRemoteSkill(source, options) {
|
|
|
60159
61384
|
filePath,
|
|
60160
61385
|
existingNames: options.existingNames
|
|
60161
61386
|
});
|
|
60162
|
-
|
|
61387
|
+
const diagnosticPath = getRemoteSkillDiagnosticPath(source);
|
|
61388
|
+
const remoteSkillDir = dirname23(diagnosticPath);
|
|
61389
|
+
const issues = result.issues.map((issue) => {
|
|
61390
|
+
const message = issue.message.replaceAll(dirname23(filePath), remoteSkillDir);
|
|
61391
|
+
if (issue.location) {
|
|
61392
|
+
return { ...issue, message, location: { ...issue.location, file: diagnosticPath } };
|
|
61393
|
+
}
|
|
61394
|
+
return { ...issue, message };
|
|
61395
|
+
});
|
|
61396
|
+
return { content, integrity, valid: result.valid, issues };
|
|
60163
61397
|
} finally {
|
|
60164
61398
|
await rm2(tmp, { recursive: true, force: true }).catch(() => {
|
|
60165
61399
|
});
|
|
@@ -60225,7 +61459,7 @@ async function isStaleSkillsAddLock(lockPath) {
|
|
|
60225
61459
|
}
|
|
60226
61460
|
}
|
|
60227
61461
|
async function acquireSkillsAddLock() {
|
|
60228
|
-
const lockPath =
|
|
61462
|
+
const lockPath = resolve40(SKILLS_ADD_LOCK_PATH);
|
|
60229
61463
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
60230
61464
|
try {
|
|
60231
61465
|
const file = await open4(lockPath, "wx", 384);
|
|
@@ -60266,7 +61500,7 @@ async function acquireSkillsAddLock() {
|
|
|
60266
61500
|
await rm2(quarantinePath, { force: true });
|
|
60267
61501
|
}
|
|
60268
61502
|
}
|
|
60269
|
-
throw new Error(`Cannot acquire skills add lock: ${
|
|
61503
|
+
throw new Error(`Cannot acquire skills add lock: ${resolve40(SKILLS_ADD_LOCK_PATH)}`);
|
|
60270
61504
|
}
|
|
60271
61505
|
async function releaseSkillsAddLock(lock) {
|
|
60272
61506
|
try {
|
|
@@ -60316,7 +61550,7 @@ function validateRemoteSource(source) {
|
|
|
60316
61550
|
}
|
|
60317
61551
|
async function resolveEntryFile(fileFlag) {
|
|
60318
61552
|
if (fileFlag) {
|
|
60319
|
-
const resolved =
|
|
61553
|
+
const resolved = resolve40(fileFlag);
|
|
60320
61554
|
if (!existsSync31(resolved)) {
|
|
60321
61555
|
throw new Error(`File not found: ${resolved}`);
|
|
60322
61556
|
}
|
|
@@ -60327,7 +61561,7 @@ async function resolveEntryFile(fileFlag) {
|
|
|
60327
61561
|
try {
|
|
60328
61562
|
const config = await loadConfig();
|
|
60329
61563
|
if (config.input?.entry) {
|
|
60330
|
-
const entryPath =
|
|
61564
|
+
const entryPath = resolve40(config.input.entry);
|
|
60331
61565
|
if (existsSync31(entryPath)) {
|
|
60332
61566
|
return entryPath;
|
|
60333
61567
|
}
|
|
@@ -60335,16 +61569,16 @@ async function resolveEntryFile(fileFlag) {
|
|
|
60335
61569
|
} catch {
|
|
60336
61570
|
}
|
|
60337
61571
|
}
|
|
60338
|
-
const promptscriptDir =
|
|
61572
|
+
const promptscriptDir = resolve40(".promptscript");
|
|
60339
61573
|
if (existsSync31(promptscriptDir)) {
|
|
60340
61574
|
const entries = await readdir9(promptscriptDir);
|
|
60341
61575
|
const prsFiles = entries.filter((f) => f.endsWith(".prs"));
|
|
60342
61576
|
if (prsFiles.length === 1) {
|
|
60343
|
-
return
|
|
61577
|
+
return resolve40(promptscriptDir, prsFiles[0]);
|
|
60344
61578
|
}
|
|
60345
61579
|
if (prsFiles.length > 1) {
|
|
60346
61580
|
if (prsFiles.includes("project.prs")) {
|
|
60347
|
-
return
|
|
61581
|
+
return resolve40(promptscriptDir, "project.prs");
|
|
60348
61582
|
}
|
|
60349
61583
|
throw new Error(
|
|
60350
61584
|
`Multiple .prs files found in .promptscript/. Use --file to specify which one.`
|
|
@@ -60570,6 +61804,306 @@ async function rollbackSkillsAdd(state) {
|
|
|
60570
61804
|
}
|
|
60571
61805
|
return new Error(rollbackErrors.map((error) => error.message).join("; "));
|
|
60572
61806
|
}
|
|
61807
|
+
function failLocalSkillStep(spinner, step) {
|
|
61808
|
+
spinner.fail(step.message);
|
|
61809
|
+
if (step.hint) {
|
|
61810
|
+
ConsoleOutput.error(step.hint);
|
|
61811
|
+
}
|
|
61812
|
+
process.exitCode = 1;
|
|
61813
|
+
}
|
|
61814
|
+
async function scanManagedSkillNames(managedRoot, skillDir) {
|
|
61815
|
+
if (!existsSync31(managedRoot)) return [];
|
|
61816
|
+
const entries = await readdir9(managedRoot, { withFileTypes: true });
|
|
61817
|
+
const names = [];
|
|
61818
|
+
for (const entry of entries) {
|
|
61819
|
+
if (!entry.isDirectory()) continue;
|
|
61820
|
+
if (resolve40(managedRoot, entry.name) === resolve40(skillDir)) continue;
|
|
61821
|
+
names.push(entry.name);
|
|
61822
|
+
}
|
|
61823
|
+
return names;
|
|
61824
|
+
}
|
|
61825
|
+
function collectLocalUseNames(lines, skillDir, entryDir) {
|
|
61826
|
+
const names = [];
|
|
61827
|
+
for (const line of lines) {
|
|
61828
|
+
const usePath = extractUsePath(line);
|
|
61829
|
+
if (!usePath || !(usePath.startsWith("./") || usePath.startsWith("../"))) continue;
|
|
61830
|
+
const absUsePath = resolve40(entryDir, usePath);
|
|
61831
|
+
const dir = usePath.endsWith(".md") ? dirname23(absUsePath) : absUsePath;
|
|
61832
|
+
if (dir === resolve40(skillDir)) continue;
|
|
61833
|
+
names.push(basename18(dir));
|
|
61834
|
+
}
|
|
61835
|
+
return names;
|
|
61836
|
+
}
|
|
61837
|
+
async function collectLocalExistingNames(lockfile, lines, skillDir, entryDir) {
|
|
61838
|
+
const names = new Set(collectExistingSkillNames(lockfile));
|
|
61839
|
+
const managedRoot = resolve40(process.cwd(), MANAGED_SKILLS_DIR);
|
|
61840
|
+
for (const name of await scanManagedSkillNames(managedRoot, skillDir)) {
|
|
61841
|
+
names.add(name);
|
|
61842
|
+
}
|
|
61843
|
+
for (const name of collectLocalUseNames(lines, skillDir, entryDir)) {
|
|
61844
|
+
names.add(name);
|
|
61845
|
+
}
|
|
61846
|
+
return names;
|
|
61847
|
+
}
|
|
61848
|
+
async function classifyLocalSkillSource(expanded, copyRequested) {
|
|
61849
|
+
const absSource = resolve40(process.cwd(), expanded);
|
|
61850
|
+
if (!existsSync31(absSource)) {
|
|
61851
|
+
return { ok: false, message: `Path not found: ${expanded}` };
|
|
61852
|
+
}
|
|
61853
|
+
const lstatDetails = await lstat11(absSource);
|
|
61854
|
+
if (lstatDetails.isSymbolicLink() && !copyRequested) {
|
|
61855
|
+
return {
|
|
61856
|
+
ok: false,
|
|
61857
|
+
message: "Symbolic-linked skill directories are not supported",
|
|
61858
|
+
hint: "The resolver skips symlinked directories at compile time. Use --copy to install the skill files."
|
|
61859
|
+
};
|
|
61860
|
+
}
|
|
61861
|
+
const details = lstatDetails.isSymbolicLink() ? await stat3(absSource) : lstatDetails;
|
|
61862
|
+
if (details.isFile()) {
|
|
61863
|
+
if (!absSource.toLowerCase().endsWith(".md")) {
|
|
61864
|
+
return {
|
|
61865
|
+
ok: false,
|
|
61866
|
+
message: `Not a SKILL.md: ${expanded}`,
|
|
61867
|
+
hint: "A local file source must be a SKILL.md, or the directory holding it."
|
|
61868
|
+
};
|
|
61869
|
+
}
|
|
61870
|
+
return { ok: true, value: { skillMdPath: absSource, skillDir: dirname23(absSource) } };
|
|
61871
|
+
}
|
|
61872
|
+
if (details.isDirectory()) {
|
|
61873
|
+
const skillMdPath = join30(absSource, "SKILL.md");
|
|
61874
|
+
if (!existsSync31(skillMdPath)) {
|
|
61875
|
+
return {
|
|
61876
|
+
ok: false,
|
|
61877
|
+
message: `No SKILL.md found in: ${expanded}`,
|
|
61878
|
+
hint: "A skill directory must contain a SKILL.md file."
|
|
61879
|
+
};
|
|
61880
|
+
}
|
|
61881
|
+
return { ok: true, value: { skillMdPath, skillDir: absSource } };
|
|
61882
|
+
}
|
|
61883
|
+
return { ok: false, message: `Unsupported file type: ${expanded}` };
|
|
61884
|
+
}
|
|
61885
|
+
function planLocalUsePath(skillDir, entryDir, copyRequested) {
|
|
61886
|
+
const skillName = basename18(skillDir);
|
|
61887
|
+
const managedDir = resolve40(process.cwd(), MANAGED_SKILLS_DIR, skillName);
|
|
61888
|
+
const sourceIsManaged = resolve40(skillDir) === resolve40(managedDir);
|
|
61889
|
+
const willCopy = copyRequested && !sourceIsManaged;
|
|
61890
|
+
if (willCopy) {
|
|
61891
|
+
return { ok: true, value: { usePath: toUsePath(entryDir, managedDir), managedDir, willCopy } };
|
|
61892
|
+
}
|
|
61893
|
+
const relToRoot = relative20(process.cwd(), skillDir);
|
|
61894
|
+
if (relToRoot.startsWith("..") || isAbsolute18(relToRoot)) {
|
|
61895
|
+
return {
|
|
61896
|
+
ok: false,
|
|
61897
|
+
message: "Skill is outside the project root",
|
|
61898
|
+
hint: `"${skillDir}" cannot be referenced in place because it lies outside the project root. Use --copy to install it into ${MANAGED_SKILLS_DIR}/.`
|
|
61899
|
+
};
|
|
61900
|
+
}
|
|
61901
|
+
const usePath = toUsePath(entryDir, skillDir);
|
|
61902
|
+
if (!USE_PATH_PATTERN.test(usePath)) {
|
|
61903
|
+
return {
|
|
61904
|
+
ok: false,
|
|
61905
|
+
message: `Cannot reference this path in a .prs file: ${usePath}`,
|
|
61906
|
+
hint: "@use paths allow only letters, digits, underscores, hyphens, dots and slashes. Use --copy to install the skill under .promptscript/skills/, or rename the folder."
|
|
61907
|
+
};
|
|
61908
|
+
}
|
|
61909
|
+
return { ok: true, value: { usePath, managedDir, willCopy } };
|
|
61910
|
+
}
|
|
61911
|
+
function validateLocalSkillFrontmatter(options, skillMdPath, skillContent, existingNames, usePath, spinner) {
|
|
61912
|
+
if (options.skipValidation) return "pass";
|
|
61913
|
+
const result = validateSkillFrontmatter(skillContent, { filePath: skillMdPath, existingNames });
|
|
61914
|
+
const hasWarnings = result.issues.some((issue) => issue.severity === "warning");
|
|
61915
|
+
if (!result.valid || options.strict && hasWarnings) {
|
|
61916
|
+
spinner.fail("SKILL.md failed validation");
|
|
61917
|
+
ConsoleOutput.error(usePath);
|
|
61918
|
+
printValidationIssues(result.issues);
|
|
61919
|
+
return "fail";
|
|
61920
|
+
}
|
|
61921
|
+
if (hasWarnings) {
|
|
61922
|
+
spinner.warn("SKILL.md has validation warnings");
|
|
61923
|
+
printValidationIssues(result.issues);
|
|
61924
|
+
spinner.start("Adding skill...");
|
|
61925
|
+
}
|
|
61926
|
+
return "pass";
|
|
61927
|
+
}
|
|
61928
|
+
async function commitLocalSkillAdd(plan, spinner) {
|
|
61929
|
+
spinner.text = plan.willCopy ? "Copying skill..." : "Adding skill...";
|
|
61930
|
+
let replacedBackupDir;
|
|
61931
|
+
let copyStarted = false;
|
|
61932
|
+
try {
|
|
61933
|
+
if (plan.willCopy) {
|
|
61934
|
+
await mkdir11(dirname23(plan.managedDir), { recursive: true });
|
|
61935
|
+
if (existsSync31(plan.managedDir)) {
|
|
61936
|
+
replacedBackupDir = `${plan.managedDir}.replaced-${process.pid}-${Date.now()}`;
|
|
61937
|
+
await rename3(plan.managedDir, replacedBackupDir);
|
|
61938
|
+
}
|
|
61939
|
+
copyStarted = true;
|
|
61940
|
+
await cp(plan.skillDir, plan.managedDir, { recursive: true, dereference: true });
|
|
61941
|
+
}
|
|
61942
|
+
await assertFileUnchanged(plan.entryFile, plan.originalEntryContent);
|
|
61943
|
+
await writeFileAtomically(plan.entryFile, plan.updatedEntryContent);
|
|
61944
|
+
} catch (error) {
|
|
61945
|
+
if (copyStarted) {
|
|
61946
|
+
await rm2(plan.managedDir, { recursive: true, force: true }).catch(() => {
|
|
61947
|
+
});
|
|
61948
|
+
}
|
|
61949
|
+
if (replacedBackupDir) {
|
|
61950
|
+
await rename3(replacedBackupDir, plan.managedDir).catch(() => {
|
|
61951
|
+
});
|
|
61952
|
+
}
|
|
61953
|
+
throw error;
|
|
61954
|
+
}
|
|
61955
|
+
if (replacedBackupDir) {
|
|
61956
|
+
await rm2(replacedBackupDir, { recursive: true, force: true }).catch(() => {
|
|
61957
|
+
});
|
|
61958
|
+
}
|
|
61959
|
+
}
|
|
61960
|
+
function checkSkillNameCollision(skillName, willCopy, force, existingNames) {
|
|
61961
|
+
if (existingNames.has(skillName) && willCopy && !force) {
|
|
61962
|
+
return `Skill "${skillName}" already exists in this project`;
|
|
61963
|
+
}
|
|
61964
|
+
if (force) {
|
|
61965
|
+
existingNames.delete(skillName);
|
|
61966
|
+
}
|
|
61967
|
+
return void 0;
|
|
61968
|
+
}
|
|
61969
|
+
async function prepareLocalSkillAdd(expanded, options) {
|
|
61970
|
+
const parsed = parseSkillSource(expanded);
|
|
61971
|
+
if (parsed.version !== "latest") {
|
|
61972
|
+
return {
|
|
61973
|
+
ok: false,
|
|
61974
|
+
message: "Versioned local paths are not supported",
|
|
61975
|
+
hint: "Local skills are referenced in place, so there is no version to pin."
|
|
61976
|
+
};
|
|
61977
|
+
}
|
|
61978
|
+
const classified = await classifyLocalSkillSource(expanded, options.copy === true);
|
|
61979
|
+
if (!classified.ok) {
|
|
61980
|
+
return classified;
|
|
61981
|
+
}
|
|
61982
|
+
const { skillDir, skillMdPath } = classified.value;
|
|
61983
|
+
const entryFile = await resolveEntryFile(options.file);
|
|
61984
|
+
const originalEntryContent = await readFile30(entryFile, "utf-8");
|
|
61985
|
+
const plan = planLocalUsePath(skillDir, dirname23(entryFile), options.copy === true);
|
|
61986
|
+
if (!plan.ok) {
|
|
61987
|
+
return plan;
|
|
61988
|
+
}
|
|
61989
|
+
return {
|
|
61990
|
+
ok: true,
|
|
61991
|
+
value: {
|
|
61992
|
+
skillMdPath,
|
|
61993
|
+
skillDir,
|
|
61994
|
+
skillName: basename18(skillDir),
|
|
61995
|
+
entryFile,
|
|
61996
|
+
entryDir: dirname23(entryFile),
|
|
61997
|
+
originalEntryContent,
|
|
61998
|
+
usePath: plan.value.usePath,
|
|
61999
|
+
managedDir: plan.value.managedDir,
|
|
62000
|
+
willCopy: plan.value.willCopy
|
|
62001
|
+
}
|
|
62002
|
+
};
|
|
62003
|
+
}
|
|
62004
|
+
function printLocalSkillDryRun(ctx, newLine, spinner) {
|
|
62005
|
+
spinner.succeed("Dry run \u2014 no files modified");
|
|
62006
|
+
ConsoleOutput.newline();
|
|
62007
|
+
ConsoleOutput.dryRun(`Would add to ${ctx.entryFile}:`);
|
|
62008
|
+
ConsoleOutput.dryRun(` ${newLine}`);
|
|
62009
|
+
if (ctx.willCopy) {
|
|
62010
|
+
ConsoleOutput.dryRun(`Would copy ${ctx.skillDir} \u2192 ${ctx.managedDir}`);
|
|
62011
|
+
}
|
|
62012
|
+
ConsoleOutput.dryRun("No lockfile entry (local skills are not pinned)");
|
|
62013
|
+
}
|
|
62014
|
+
function reportLocalSkillAdded(ctx, newLine, spinner) {
|
|
62015
|
+
spinner.succeed("Skill added");
|
|
62016
|
+
ConsoleOutput.newline();
|
|
62017
|
+
ConsoleOutput.success(`${newLine} \u2192 ${ctx.entryFile}`);
|
|
62018
|
+
if (ctx.willCopy) {
|
|
62019
|
+
ConsoleOutput.muted(`Copied into ${MANAGED_SKILLS_DIR}/${ctx.skillName}`);
|
|
62020
|
+
}
|
|
62021
|
+
ConsoleOutput.muted("Local skill - not recorded in the lockfile");
|
|
62022
|
+
}
|
|
62023
|
+
function reportLocalSkillError(error, spinner) {
|
|
62024
|
+
spinner.fail("Failed to add skill");
|
|
62025
|
+
ConsoleOutput.error(error instanceof Error ? error.message : String(error));
|
|
62026
|
+
process.exitCode = 1;
|
|
62027
|
+
}
|
|
62028
|
+
async function addLocalSkill(rawSource, options, spinner) {
|
|
62029
|
+
let transactionLock;
|
|
62030
|
+
try {
|
|
62031
|
+
if (!options.dryRun) {
|
|
62032
|
+
transactionLock = await acquireSkillsAddLock();
|
|
62033
|
+
}
|
|
62034
|
+
spinner.text = "Resolving skill source...";
|
|
62035
|
+
const prepared = await prepareLocalSkillAdd(expandHomePath(rawSource.trim()), options);
|
|
62036
|
+
if (!prepared.ok) {
|
|
62037
|
+
failLocalSkillStep(spinner, prepared);
|
|
62038
|
+
return;
|
|
62039
|
+
}
|
|
62040
|
+
const ctx = prepared.value;
|
|
62041
|
+
const lines = ctx.originalEntryContent.split("\n");
|
|
62042
|
+
const alreadyImported = lines.some((line) => extractUsePath(line) === ctx.usePath);
|
|
62043
|
+
if (alreadyImported) {
|
|
62044
|
+
spinner.warn(`Skill already imported: ${ctx.usePath}`);
|
|
62045
|
+
return;
|
|
62046
|
+
}
|
|
62047
|
+
const lockfile = await loadLockfile3();
|
|
62048
|
+
const existingNames = await collectLocalExistingNames(
|
|
62049
|
+
lockfile,
|
|
62050
|
+
lines,
|
|
62051
|
+
ctx.skillDir,
|
|
62052
|
+
ctx.entryDir
|
|
62053
|
+
);
|
|
62054
|
+
const refusal = checkSkillNameCollision(
|
|
62055
|
+
ctx.skillName,
|
|
62056
|
+
ctx.willCopy,
|
|
62057
|
+
options.force === true,
|
|
62058
|
+
existingNames
|
|
62059
|
+
);
|
|
62060
|
+
if (refusal) {
|
|
62061
|
+
spinner.fail(refusal);
|
|
62062
|
+
ConsoleOutput.error("Use --force to replace it.");
|
|
62063
|
+
process.exitCode = 1;
|
|
62064
|
+
return;
|
|
62065
|
+
}
|
|
62066
|
+
spinner.text = options.skipValidation ? "Reading SKILL.md..." : "Validating SKILL.md...";
|
|
62067
|
+
const skillContent = await readFile30(ctx.skillMdPath, "utf-8");
|
|
62068
|
+
const validationOutcome = validateLocalSkillFrontmatter(
|
|
62069
|
+
options,
|
|
62070
|
+
ctx.skillMdPath,
|
|
62071
|
+
skillContent,
|
|
62072
|
+
existingNames,
|
|
62073
|
+
ctx.usePath,
|
|
62074
|
+
spinner
|
|
62075
|
+
);
|
|
62076
|
+
if (validationOutcome === "fail") {
|
|
62077
|
+
process.exitCode = 1;
|
|
62078
|
+
return;
|
|
62079
|
+
}
|
|
62080
|
+
const insertionPoint = findInsertionPoint(lines);
|
|
62081
|
+
const newLine = `@use ${ctx.usePath}`;
|
|
62082
|
+
lines.splice(insertionPoint, 0, newLine);
|
|
62083
|
+
if (options.dryRun) {
|
|
62084
|
+
printLocalSkillDryRun(ctx, newLine, spinner);
|
|
62085
|
+
return;
|
|
62086
|
+
}
|
|
62087
|
+
await commitLocalSkillAdd(
|
|
62088
|
+
{
|
|
62089
|
+
entryFile: ctx.entryFile,
|
|
62090
|
+
originalEntryContent: ctx.originalEntryContent,
|
|
62091
|
+
updatedEntryContent: lines.join("\n"),
|
|
62092
|
+
willCopy: ctx.willCopy,
|
|
62093
|
+
skillDir: ctx.skillDir,
|
|
62094
|
+
managedDir: ctx.managedDir
|
|
62095
|
+
},
|
|
62096
|
+
spinner
|
|
62097
|
+
);
|
|
62098
|
+
reportLocalSkillAdded(ctx, newLine, spinner);
|
|
62099
|
+
} catch (error) {
|
|
62100
|
+
reportLocalSkillError(error, spinner);
|
|
62101
|
+
} finally {
|
|
62102
|
+
if (transactionLock) {
|
|
62103
|
+
await releaseSkillsAddLock(transactionLock);
|
|
62104
|
+
}
|
|
62105
|
+
}
|
|
62106
|
+
}
|
|
60573
62107
|
async function skillsAddCommand(rawSource, options) {
|
|
60574
62108
|
const spinner = createSpinner("Adding skill...").start();
|
|
60575
62109
|
let rollbackState;
|
|
@@ -60583,6 +62117,10 @@ async function skillsAddCommand(rawSource, options) {
|
|
|
60583
62117
|
process.exitCode = 1;
|
|
60584
62118
|
return;
|
|
60585
62119
|
}
|
|
62120
|
+
if (isLocalSkillSource(rawSource)) {
|
|
62121
|
+
await addLocalSkill(rawSource, options, spinner);
|
|
62122
|
+
return;
|
|
62123
|
+
}
|
|
60586
62124
|
const sshCloneUrl = extractSshCloneUrl(rawSource);
|
|
60587
62125
|
const source = normalizeSkillSource(rawSource);
|
|
60588
62126
|
const sourceError = validateRemoteSource(source);
|
|
@@ -61052,6 +62590,8 @@ var __filename4 = fileURLToPath4(import.meta.url);
|
|
|
61052
62590
|
var __dirname4 = dirname28(__filename4);
|
|
61053
62591
|
var CLI_VERSION = getPackageVersion(__dirname3, "./package.json");
|
|
61054
62592
|
var program = new Command();
|
|
62593
|
+
var RESOURCES_FLAG = "--resources <items>";
|
|
62594
|
+
var RESOURCES_DESCRIPTION = "Compile only these resources (agents, skills, commands, mcp, hooks, plugins, main)";
|
|
61055
62595
|
program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(CLI_VERSION).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", async (thisCommand, actionCommand) => {
|
|
61056
62596
|
if (actionCommand.name() === "__telemetry-flush" && process.env["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1") {
|
|
61057
62597
|
return;
|
|
@@ -61088,11 +62628,11 @@ program.command("init").description("Initialize PromptScript in current director
|
|
|
61088
62628
|
}
|
|
61089
62629
|
return initCommand(opts);
|
|
61090
62630
|
});
|
|
61091
|
-
program.command("compile").description("Compile PromptScript to target formats").option("-b, --build <name>", "Named build profile from config.builds").option("--all-builds", "Compile all named build profiles in deterministic order").option("-t, --target <target>", "Specific target (github, claude, cursor)").option("-f, --format <format>", "Output format (alias for --target)").option("-a, --all", "All configured targets").option("-w, --watch", "Watch mode").option("-o, --output <dir>", "Output directory").option("--dry-run", "Preview changes").option(
|
|
62631
|
+
program.command("compile").description("Compile PromptScript to target formats").option("-b, --build <name>", "Named build profile from config.builds").option("--all-builds", "Compile all named build profiles in deterministic order").option("-t, --target <target>", "Specific target (github, claude, cursor)").option("-f, --format <format>", "Output format (alias for --target)").option("-a, --all", "All configured targets").option("-w, --watch", "Watch mode").option("-o, --output <dir>", "Output directory").option(RESOURCES_FLAG, RESOURCES_DESCRIPTION, collectResourceKinds, []).option("--dry-run", "Preview changes").option(
|
|
61092
62632
|
"--no-migrate-factory-hooks",
|
|
61093
62633
|
"Warn instead of migrating legacy .factory/settings.json hooks"
|
|
61094
62634
|
).option("--registry <path>", "Registry path (overrides config)").option("-c, --config <path>", "Path to custom config file").option("--force", "Force overwrite existing files without prompts").option("--strict", "Treat output path conflicts as errors").option("--ignore-hashes", "Skip reference integrity hash verification").option("--cwd <dir>", "Working directory (project root)").action((opts) => compileCommand(opts));
|
|
61095
|
-
program.command("build <name>").description("Compile a named build profile").option("-t, --target <target>", "Specific target (github, claude, cursor)").option("-f, --format <format>", "Output format (alias for --target)").option("-w, --watch", "Watch mode").option("-o, --output <dir>", "Output directory").option("--dry-run", "Preview changes").option(
|
|
62635
|
+
program.command("build <name>").description("Compile a named build profile").option("-t, --target <target>", "Specific target (github, claude, cursor)").option("-f, --format <format>", "Output format (alias for --target)").option("-w, --watch", "Watch mode").option("-o, --output <dir>", "Output directory").option(RESOURCES_FLAG, RESOURCES_DESCRIPTION, collectResourceKinds, []).option("--dry-run", "Preview changes").option(
|
|
61096
62636
|
"--no-migrate-factory-hooks",
|
|
61097
62637
|
"Warn instead of migrating legacy .factory/settings.json hooks"
|
|
61098
62638
|
).option("--registry <path>", "Registry path (overrides config)").option("-c, --config <path>", "Path to custom config file").option("--force", "Force overwrite existing files without prompts").option("--strict", "Treat output path conflicts as errors").option("--ignore-hashes", "Skip reference integrity hash verification").option("--cwd <dir>", "Working directory (project root)").action((name, opts) => compileCommand({ ...opts, build: name }));
|
|
@@ -61124,7 +62664,10 @@ program.command("resolve <import>").description("Show the lock pin and local sou
|
|
|
61124
62664
|
program.command("hook").argument("<action>", "Hook action: pre-edit or post-edit").description("Hook handler invoked by AI tools (not for direct use)").action(hookCommand);
|
|
61125
62665
|
program.command("hooks").argument("<action>", "Action: install or uninstall").argument("[tool]", "Tool: claude, gemini, copilot, cursor, windsurf, factory, cline").option("--all", "Install/uninstall for all detected tools").description("Manage PromptScript hooks for AI tools").action(hooksCommand);
|
|
61126
62666
|
var skills = program.command("skills").description("Manage PromptScript skills");
|
|
61127
|
-
skills.command("add <source>").description("Add a remote skill to the project").option("-f, --file <file>", "Target .prs file to modify").option("--dry-run", "Preview changes without writing").option("--skip-validation", "Skip SKILL.md frontmatter validation (not recommended)").option("--strict", "Treat validation warnings as errors").
|
|
62667
|
+
skills.command("add <source>").description("Add a remote or local skill to the project").option("-f, --file <file>", "Target .prs file to modify").option("--dry-run", "Preview changes without writing").option("--skip-validation", "Skip SKILL.md frontmatter validation (not recommended)").option("--strict", "Treat validation warnings as errors").option(
|
|
62668
|
+
"--copy",
|
|
62669
|
+
"Local sources: copy into .promptscript/skills/ instead of referencing in place"
|
|
62670
|
+
).option("--force", "Local sources: replace an existing skill with the same name").action((source, opts) => skillsAddCommand(source, opts));
|
|
61128
62671
|
skills.command("remove <name>").description("Remove a skill from the project").option("--dry-run", "Preview changes without writing").action((name, opts) => skillsRemoveCommand(name, opts));
|
|
61129
62672
|
skills.command("list").description("List skills imported in the current project").action(() => skillsListCommand());
|
|
61130
62673
|
skills.command("update [name]").description("Update lock entries for markdown-sourced skills").option("--dry-run", "Preview changes without writing").option("--skip-validation", "Skip SKILL.md frontmatter validation (not recommended)").option("--strict", "Treat validation warnings as errors").action((name, opts) => skillsUpdateCommand(name, opts));
|