@promptscript/cli 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +911 -69
- package/package.json +1 -1
- package/src/cli.d.ts.map +1 -1
- package/src/commands/hook.d.ts +15 -0
- package/src/commands/hook.d.ts.map +1 -0
- package/src/commands/hooks.d.ts +12 -0
- package/src/commands/hooks.d.ts.map +1 -0
- package/src/commands/init.d.ts.map +1 -1
- package/src/hooks/debounce-lock.d.ts +4 -0
- package/src/hooks/debounce-lock.d.ts.map +1 -0
- package/src/hooks/detect-marker.d.ts +7 -0
- package/src/hooks/detect-marker.d.ts.map +1 -0
- package/src/hooks/extract-file-path.d.ts +12 -0
- package/src/hooks/extract-file-path.d.ts.map +1 -0
- package/src/hooks/tool-configs/claude.d.ts +8 -0
- package/src/hooks/tool-configs/claude.d.ts.map +1 -0
- package/src/hooks/tool-configs/cline.d.ts +3 -0
- package/src/hooks/tool-configs/cline.d.ts.map +1 -0
- package/src/hooks/tool-configs/copilot.d.ts +3 -0
- package/src/hooks/tool-configs/copilot.d.ts.map +1 -0
- package/src/hooks/tool-configs/cursor.d.ts +3 -0
- package/src/hooks/tool-configs/cursor.d.ts.map +1 -0
- package/src/hooks/tool-configs/factory.d.ts +3 -0
- package/src/hooks/tool-configs/factory.d.ts.map +1 -0
- package/src/hooks/tool-configs/gemini.d.ts +3 -0
- package/src/hooks/tool-configs/gemini.d.ts.map +1 -0
- package/src/hooks/tool-configs/index.d.ts +12 -0
- package/src/hooks/tool-configs/index.d.ts.map +1 -0
- package/src/hooks/tool-configs/types.d.ts +11 -0
- package/src/hooks/tool-configs/types.d.ts.map +1 -0
- package/src/hooks/tool-configs/windsurf.d.ts +3 -0
- package/src/hooks/tool-configs/windsurf.d.ts.map +1 -0
- package/src/types.d.ts +13 -0
- package/src/types.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -19761,20 +19761,20 @@ var init_factory = __esm({
|
|
|
19761
19761
|
return commands;
|
|
19762
19762
|
}
|
|
19763
19763
|
generateCommandFile(config) {
|
|
19764
|
-
const
|
|
19764
|
+
const factoryConfig2 = config;
|
|
19765
19765
|
const lines = [];
|
|
19766
19766
|
lines.push("---");
|
|
19767
|
-
lines.push(`description: ${this.yamlString(
|
|
19768
|
-
if (
|
|
19769
|
-
lines.push(`agent: ${
|
|
19767
|
+
lines.push(`description: ${this.yamlString(factoryConfig2.description)}`);
|
|
19768
|
+
if (factoryConfig2.agent) {
|
|
19769
|
+
lines.push(`agent: ${factoryConfig2.agent}`);
|
|
19770
19770
|
}
|
|
19771
|
-
if (
|
|
19772
|
-
const toolsArray =
|
|
19771
|
+
if (factoryConfig2.tools && factoryConfig2.tools.length > 0) {
|
|
19772
|
+
const toolsArray = factoryConfig2.tools.map((t) => `'${t}'`).join(", ");
|
|
19773
19773
|
lines.push(`tools: [${toolsArray}]`);
|
|
19774
19774
|
}
|
|
19775
|
-
if (
|
|
19775
|
+
if (factoryConfig2.handoffs && factoryConfig2.handoffs.length > 0) {
|
|
19776
19776
|
lines.push("handoffs: ");
|
|
19777
|
-
for (const handoff of
|
|
19777
|
+
for (const handoff of factoryConfig2.handoffs) {
|
|
19778
19778
|
lines.push(` - label: ${this.yamlString(handoff.label)}`);
|
|
19779
19779
|
lines.push(` agent: ${handoff.agent}`);
|
|
19780
19780
|
if (handoff.prompt) {
|
|
@@ -19787,12 +19787,12 @@ var init_factory = __esm({
|
|
|
19787
19787
|
}
|
|
19788
19788
|
lines.push("---");
|
|
19789
19789
|
lines.push("");
|
|
19790
|
-
if (
|
|
19791
|
-
const dedentedContent = this.dedent(
|
|
19790
|
+
if (factoryConfig2.content) {
|
|
19791
|
+
const dedentedContent = this.dedent(factoryConfig2.content);
|
|
19792
19792
|
lines.push(dedentedContent);
|
|
19793
19793
|
}
|
|
19794
19794
|
return {
|
|
19795
|
-
path: `.factory/commands/${
|
|
19795
|
+
path: `.factory/commands/${factoryConfig2.name}.md`,
|
|
19796
19796
|
content: lines.join("\n") + "\n"
|
|
19797
19797
|
};
|
|
19798
19798
|
}
|
|
@@ -19825,30 +19825,30 @@ var init_factory = __esm({
|
|
|
19825
19825
|
return skills;
|
|
19826
19826
|
}
|
|
19827
19827
|
generateSkillFile(config) {
|
|
19828
|
-
const
|
|
19828
|
+
const factoryConfig2 = config;
|
|
19829
19829
|
const lines = [];
|
|
19830
|
-
const skillName =
|
|
19830
|
+
const skillName = factoryConfig2.name.replace(/\./g, "-");
|
|
19831
19831
|
lines.push("---");
|
|
19832
19832
|
lines.push(`name: ${skillName}`);
|
|
19833
|
-
lines.push(`description: ${this.yamlString(
|
|
19834
|
-
if (
|
|
19833
|
+
lines.push(`description: ${this.yamlString(factoryConfig2.description)}`);
|
|
19834
|
+
if (factoryConfig2.userInvocable === false) {
|
|
19835
19835
|
lines.push("user-invocable: false");
|
|
19836
19836
|
}
|
|
19837
|
-
if (
|
|
19837
|
+
if (factoryConfig2.disableModelInvocation === true) {
|
|
19838
19838
|
lines.push("disable-model-invocation: true");
|
|
19839
19839
|
}
|
|
19840
|
-
if (
|
|
19841
|
-
const toolsArray =
|
|
19840
|
+
if (factoryConfig2.allowedTools && factoryConfig2.allowedTools.length > 0) {
|
|
19841
|
+
const toolsArray = factoryConfig2.allowedTools.map((t) => `"${t}"`).join(", ");
|
|
19842
19842
|
lines.push(`allowed-tools: [${toolsArray}]`);
|
|
19843
19843
|
}
|
|
19844
19844
|
lines.push("---");
|
|
19845
19845
|
lines.push("");
|
|
19846
|
-
if (
|
|
19847
|
-
const dedentedContent = this.dedent(
|
|
19846
|
+
if (factoryConfig2.content) {
|
|
19847
|
+
const dedentedContent = this.dedent(factoryConfig2.content);
|
|
19848
19848
|
lines.push(dedentedContent);
|
|
19849
19849
|
}
|
|
19850
|
-
const skillDirPath = `.factory/skills/${
|
|
19851
|
-
const resourceFiles = this.sanitizeResourceFiles(
|
|
19850
|
+
const skillDirPath = `.factory/skills/${factoryConfig2.name}`;
|
|
19851
|
+
const resourceFiles = this.sanitizeResourceFiles(factoryConfig2.resources, skillDirPath);
|
|
19852
19852
|
return {
|
|
19853
19853
|
path: `${skillDirPath}/SKILL.md`,
|
|
19854
19854
|
content: lines.join("\n") + "\n",
|
|
@@ -21991,6 +21991,9 @@ async function initCommand(options, services = createDefaultServices()) {
|
|
|
21991
21991
|
}
|
|
21992
21992
|
}
|
|
21993
21993
|
}
|
|
21994
|
+
ConsoleOutput.muted(
|
|
21995
|
+
"\n Tip: Run prs hooks install to set up auto-compilation hooks for your AI tools.\n"
|
|
21996
|
+
);
|
|
21994
21997
|
} catch (error) {
|
|
21995
21998
|
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
21996
21999
|
ConsoleOutput.newline();
|
|
@@ -23622,9 +23625,9 @@ var require_rate_limit = __commonJS({
|
|
|
23622
23625
|
let ttl = 0;
|
|
23623
23626
|
let ttlInSeconds = 0;
|
|
23624
23627
|
try {
|
|
23625
|
-
const res = await new Promise((
|
|
23628
|
+
const res = await new Promise((resolve18, reject2) => {
|
|
23626
23629
|
store.incr(key, (err, res2) => {
|
|
23627
|
-
err ? reject2(err) :
|
|
23630
|
+
err ? reject2(err) : resolve18(res2);
|
|
23628
23631
|
}, timeWindow, max);
|
|
23629
23632
|
});
|
|
23630
23633
|
current = res.current;
|
|
@@ -23730,13 +23733,13 @@ var init_health = __esm({
|
|
|
23730
23733
|
});
|
|
23731
23734
|
|
|
23732
23735
|
// packages/server/src/source-dirs.ts
|
|
23733
|
-
import { readFile as
|
|
23734
|
-
import { join as
|
|
23736
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
23737
|
+
import { join as join20, dirname as dirname11 } from "path";
|
|
23735
23738
|
import { parse as parseYaml12 } from "yaml";
|
|
23736
23739
|
async function readProjectConfig(workspace) {
|
|
23737
23740
|
for (const filename of CONFIG_FILENAMES) {
|
|
23738
23741
|
try {
|
|
23739
|
-
const raw = await
|
|
23742
|
+
const raw = await readFile20(join20(workspace, filename), "utf-8");
|
|
23740
23743
|
const config = parseYaml12(raw);
|
|
23741
23744
|
if (config) return config;
|
|
23742
23745
|
} catch {
|
|
@@ -23747,10 +23750,10 @@ async function readProjectConfig(workspace) {
|
|
|
23747
23750
|
async function resolveSourceDir(workspace) {
|
|
23748
23751
|
for (const filename of CONFIG_FILENAMES) {
|
|
23749
23752
|
try {
|
|
23750
|
-
const raw = await
|
|
23753
|
+
const raw = await readFile20(join20(workspace, filename), "utf-8");
|
|
23751
23754
|
const config = parseYaml12(raw);
|
|
23752
23755
|
if (config?.input?.entry) {
|
|
23753
|
-
return
|
|
23756
|
+
return dirname11(config.input.entry);
|
|
23754
23757
|
}
|
|
23755
23758
|
} catch {
|
|
23756
23759
|
}
|
|
@@ -23764,9 +23767,9 @@ async function resolveFileGlobs(workspace) {
|
|
|
23764
23767
|
async function resolveWatchPaths(workspace) {
|
|
23765
23768
|
const sourceDir = await resolveSourceDir(workspace);
|
|
23766
23769
|
return [
|
|
23767
|
-
|
|
23768
|
-
|
|
23769
|
-
|
|
23770
|
+
join20(workspace, sourceDir),
|
|
23771
|
+
join20(workspace, "promptscript.yaml"),
|
|
23772
|
+
join20(workspace, "promptscript.yml")
|
|
23770
23773
|
];
|
|
23771
23774
|
}
|
|
23772
23775
|
var DEFAULT_SOURCE_DIR, CONFIG_FILENAMES;
|
|
@@ -23793,13 +23796,13 @@ var init_config2 = __esm({
|
|
|
23793
23796
|
});
|
|
23794
23797
|
|
|
23795
23798
|
// packages/server/src/path-guard.ts
|
|
23796
|
-
import { resolve as
|
|
23799
|
+
import { resolve as resolve17, relative as relative2, isAbsolute as isAbsolute4 } from "path";
|
|
23797
23800
|
function resolveSafePath(workspace, requestedPath) {
|
|
23798
23801
|
const decoded = decodeURIComponent(requestedPath);
|
|
23799
23802
|
if (isAbsolute4(decoded)) {
|
|
23800
23803
|
throw new PathTraversalError(requestedPath);
|
|
23801
23804
|
}
|
|
23802
|
-
const resolved =
|
|
23805
|
+
const resolved = resolve17(workspace, decoded);
|
|
23803
23806
|
const rel = relative2(workspace, resolved);
|
|
23804
23807
|
if (rel.startsWith("..") || isAbsolute4(rel)) {
|
|
23805
23808
|
throw new PathTraversalError(requestedPath);
|
|
@@ -23820,8 +23823,8 @@ var init_path_guard = __esm({
|
|
|
23820
23823
|
});
|
|
23821
23824
|
|
|
23822
23825
|
// packages/server/src/routes/files.ts
|
|
23823
|
-
import { readFile as
|
|
23824
|
-
import { dirname as
|
|
23826
|
+
import { readFile as readFile21, writeFile as writeFile11, unlink as unlink2, stat as stat2, mkdir as mkdir9 } from "fs/promises";
|
|
23827
|
+
import { dirname as dirname12 } from "path";
|
|
23825
23828
|
import fg from "fast-glob";
|
|
23826
23829
|
function registerRoutes(app, workspace, readOnly) {
|
|
23827
23830
|
app.get("/api/files", async () => {
|
|
@@ -23841,7 +23844,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
23841
23844
|
const filePath = request.params["*"];
|
|
23842
23845
|
try {
|
|
23843
23846
|
const resolved = resolveSafePath(workspace, filePath);
|
|
23844
|
-
const content = await
|
|
23847
|
+
const content = await readFile21(resolved, "utf-8");
|
|
23845
23848
|
return { path: filePath, content };
|
|
23846
23849
|
} catch (err) {
|
|
23847
23850
|
if (err instanceof PathTraversalError) {
|
|
@@ -23858,7 +23861,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
23858
23861
|
const filePath = request.params["*"];
|
|
23859
23862
|
try {
|
|
23860
23863
|
const resolved = resolveSafePath(workspace, filePath);
|
|
23861
|
-
await
|
|
23864
|
+
await writeFile11(resolved, request.body.content, "utf-8");
|
|
23862
23865
|
return { path: filePath, status: "updated" };
|
|
23863
23866
|
} catch (err) {
|
|
23864
23867
|
if (err instanceof PathTraversalError) {
|
|
@@ -23881,8 +23884,8 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
23881
23884
|
return reply.status(409).send({ error: "File already exists" });
|
|
23882
23885
|
} catch {
|
|
23883
23886
|
}
|
|
23884
|
-
await
|
|
23885
|
-
await
|
|
23887
|
+
await mkdir9(dirname12(resolved), { recursive: true });
|
|
23888
|
+
await writeFile11(resolved, request.body.content, "utf-8");
|
|
23886
23889
|
return reply.status(201).send({ path: filePath, status: "created" });
|
|
23887
23890
|
} catch (err) {
|
|
23888
23891
|
if (err instanceof PathTraversalError) {
|
|
@@ -23897,7 +23900,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
23897
23900
|
const filePath = request.params["*"];
|
|
23898
23901
|
try {
|
|
23899
23902
|
const resolved = resolveSafePath(workspace, filePath);
|
|
23900
|
-
await
|
|
23903
|
+
await unlink2(resolved);
|
|
23901
23904
|
return { path: filePath, status: "deleted" };
|
|
23902
23905
|
} catch (err) {
|
|
23903
23906
|
if (err instanceof PathTraversalError) {
|
|
@@ -24107,7 +24110,7 @@ init_src();
|
|
|
24107
24110
|
init_init();
|
|
24108
24111
|
import { Command } from "commander";
|
|
24109
24112
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
24110
|
-
import { dirname as
|
|
24113
|
+
import { dirname as dirname13 } from "path";
|
|
24111
24114
|
|
|
24112
24115
|
// packages/cli/src/commands/compile.ts
|
|
24113
24116
|
init_src();
|
|
@@ -27460,7 +27463,7 @@ var HttpRegistry = class {
|
|
|
27460
27463
|
* Sleep for specified milliseconds.
|
|
27461
27464
|
*/
|
|
27462
27465
|
sleep(ms) {
|
|
27463
|
-
return new Promise((
|
|
27466
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
27464
27467
|
}
|
|
27465
27468
|
/**
|
|
27466
27469
|
* Fetch with timeout.
|
|
@@ -29938,15 +29941,15 @@ var SECURITY_STRICT_MULTILINGUAL = {
|
|
|
29938
29941
|
};
|
|
29939
29942
|
|
|
29940
29943
|
// packages/compiler/src/compiler.ts
|
|
29941
|
-
function generateHtmlMarker() {
|
|
29944
|
+
function generateHtmlMarker(source, target) {
|
|
29942
29945
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
29943
|
-
return `<!-- PromptScript ${timestamp} - do not edit -->`;
|
|
29946
|
+
return `<!-- PromptScript ${timestamp} | source: ${source} | target: ${target} - do not edit -->`;
|
|
29944
29947
|
}
|
|
29945
|
-
function generateYamlMarker() {
|
|
29948
|
+
function generateYamlMarker(source, target) {
|
|
29946
29949
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
29947
|
-
return `# promptscript-generated: ${timestamp}`;
|
|
29950
|
+
return `# promptscript-generated: ${timestamp} | source: ${source} | target: ${target}`;
|
|
29948
29951
|
}
|
|
29949
|
-
function addMarkerToOutput(output) {
|
|
29952
|
+
function addMarkerToOutput(output, source, target) {
|
|
29950
29953
|
const { content, path } = output;
|
|
29951
29954
|
if (content.includes("<!-- PromptScript") || content.includes("# promptscript-generated:")) {
|
|
29952
29955
|
return output;
|
|
@@ -29959,11 +29962,11 @@ function addMarkerToOutput(output) {
|
|
|
29959
29962
|
if (closingIndex !== -1) {
|
|
29960
29963
|
const afterOpening = content.slice(3);
|
|
29961
29964
|
const newContent = `---
|
|
29962
|
-
${generateYamlMarker()}${afterOpening}`;
|
|
29965
|
+
${generateYamlMarker(source, target)}${afterOpening}`;
|
|
29963
29966
|
return { ...output, content: newContent };
|
|
29964
29967
|
}
|
|
29965
29968
|
}
|
|
29966
|
-
const marker = generateHtmlMarker();
|
|
29969
|
+
const marker = generateHtmlMarker(source, target);
|
|
29967
29970
|
const lines = content.split("\n");
|
|
29968
29971
|
if (lines[0]?.startsWith("# ")) {
|
|
29969
29972
|
lines.splice(1, 0, "", marker);
|
|
@@ -30077,7 +30080,7 @@ var Compiler = class _Compiler {
|
|
|
30077
30080
|
});
|
|
30078
30081
|
}
|
|
30079
30082
|
outputPathOwners.set(output.path, formatter.name);
|
|
30080
|
-
outputs.set(output.path, addMarkerToOutput(output));
|
|
30083
|
+
outputs.set(output.path, addMarkerToOutput(output, entryPath, formatter.name));
|
|
30081
30084
|
if (output.additionalFiles) {
|
|
30082
30085
|
const queue = [...output.additionalFiles];
|
|
30083
30086
|
while (queue.length > 0) {
|
|
@@ -30098,7 +30101,10 @@ var Compiler = class _Compiler {
|
|
|
30098
30101
|
continue;
|
|
30099
30102
|
}
|
|
30100
30103
|
outputPathOwners.set(additionalFile.path, formatter.name);
|
|
30101
|
-
outputs.set(
|
|
30104
|
+
outputs.set(
|
|
30105
|
+
additionalFile.path,
|
|
30106
|
+
addMarkerToOutput(additionalFile, entryPath, formatter.name)
|
|
30107
|
+
);
|
|
30102
30108
|
if (additionalFile.additionalFiles) {
|
|
30103
30109
|
queue.push(...additionalFile.additionalFiles);
|
|
30104
30110
|
}
|
|
@@ -30123,13 +30129,19 @@ var Compiler = class _Compiler {
|
|
|
30123
30129
|
const skillPath = `${skillBasePath}/promptscript/${skillFileName}`;
|
|
30124
30130
|
const existingOwner = outputPathOwners.get(skillPath);
|
|
30125
30131
|
if (existingOwner) {
|
|
30126
|
-
|
|
30127
|
-
|
|
30128
|
-
|
|
30129
|
-
|
|
30130
|
-
|
|
30131
|
-
|
|
30132
|
-
|
|
30132
|
+
if (existingOwner !== formatter.name) {
|
|
30133
|
+
formatWarnings.push({
|
|
30134
|
+
ruleId: "PS4001",
|
|
30135
|
+
ruleName: "output-path-collision",
|
|
30136
|
+
severity: "warning",
|
|
30137
|
+
message: `Output path '${skillPath}' is already written by '${existingOwner}'. Skipping auto-injected PromptScript skill for '${formatter.name}'.`,
|
|
30138
|
+
suggestion: `The user-defined skill takes precedence. To use the bundled skill, remove the custom one or rename it.`
|
|
30139
|
+
});
|
|
30140
|
+
} else {
|
|
30141
|
+
this.logger.debug(
|
|
30142
|
+
` Skill path '${skillPath}' already output by '${formatter.name}' (auto-discovered). Skipping auto-injection.`
|
|
30143
|
+
);
|
|
30144
|
+
}
|
|
30133
30145
|
continue;
|
|
30134
30146
|
}
|
|
30135
30147
|
outputPathOwners.set(skillPath, `${formatter.name}:promptscript-skill`);
|
|
@@ -30137,7 +30149,7 @@ var Compiler = class _Compiler {
|
|
|
30137
30149
|
path: skillPath,
|
|
30138
30150
|
content: this.options.skillContent
|
|
30139
30151
|
};
|
|
30140
|
-
outputs.set(skillPath, addMarkerToOutput(skillOutput));
|
|
30152
|
+
outputs.set(skillPath, addMarkerToOutput(skillOutput, entryPath, "promptscript"));
|
|
30141
30153
|
this.logger.verbose(` \u2192 ${skillPath} (auto-injected promptscript skill)`);
|
|
30142
30154
|
}
|
|
30143
30155
|
}
|
|
@@ -30216,12 +30228,12 @@ var Compiler = class _Compiler {
|
|
|
30216
30228
|
*/
|
|
30217
30229
|
async watch(entryPath, options = {}) {
|
|
30218
30230
|
const { default: chokidar2 } = await import("chokidar");
|
|
30219
|
-
const { dirname:
|
|
30220
|
-
const baseDir =
|
|
30231
|
+
const { dirname: dirname14, resolve: resolve18 } = await import("path");
|
|
30232
|
+
const baseDir = dirname14(resolve18(entryPath));
|
|
30221
30233
|
const includePatterns = options.include ?? ["**/*.prs"];
|
|
30222
30234
|
const excludePatterns = options.exclude ?? ["**/node_modules/**"];
|
|
30223
30235
|
const debounceMs = options.debounce ?? 300;
|
|
30224
|
-
const watchPatterns = includePatterns.map((p) =>
|
|
30236
|
+
const watchPatterns = includePatterns.map((p) => resolve18(baseDir, p));
|
|
30225
30237
|
let debounceTimer = null;
|
|
30226
30238
|
let pendingChanges = [];
|
|
30227
30239
|
const handleChange = async (changedFiles) => {
|
|
@@ -30395,7 +30407,7 @@ var Pager = class {
|
|
|
30395
30407
|
}
|
|
30396
30408
|
return;
|
|
30397
30409
|
}
|
|
30398
|
-
return new Promise((
|
|
30410
|
+
return new Promise((resolve18) => {
|
|
30399
30411
|
const pagerCmd = getPagerCommand();
|
|
30400
30412
|
const [cmd, ...args] = pagerCmd.split(" ");
|
|
30401
30413
|
if (cmd === "less" && !args.includes("-R")) {
|
|
@@ -30403,7 +30415,7 @@ var Pager = class {
|
|
|
30403
30415
|
}
|
|
30404
30416
|
if (!cmd) {
|
|
30405
30417
|
console.log(content);
|
|
30406
|
-
|
|
30418
|
+
resolve18();
|
|
30407
30419
|
return;
|
|
30408
30420
|
}
|
|
30409
30421
|
const pager = spawn(cmd, args, {
|
|
@@ -30416,17 +30428,17 @@ var Pager = class {
|
|
|
30416
30428
|
});
|
|
30417
30429
|
pager.on("error", (_err) => {
|
|
30418
30430
|
console.log(content);
|
|
30419
|
-
|
|
30431
|
+
resolve18();
|
|
30420
30432
|
});
|
|
30421
30433
|
pager.on("close", () => {
|
|
30422
|
-
|
|
30434
|
+
resolve18();
|
|
30423
30435
|
});
|
|
30424
30436
|
if (pager.stdin) {
|
|
30425
30437
|
pager.stdin.write(content);
|
|
30426
30438
|
pager.stdin.end();
|
|
30427
30439
|
} else {
|
|
30428
30440
|
console.log(content);
|
|
30429
|
-
|
|
30441
|
+
resolve18();
|
|
30430
30442
|
}
|
|
30431
30443
|
});
|
|
30432
30444
|
}
|
|
@@ -33126,9 +33138,837 @@ async function upgradeCommand(options) {
|
|
|
33126
33138
|
${upgradedCount} file(s) ${verb}, ${skippedCount} skipped.`);
|
|
33127
33139
|
}
|
|
33128
33140
|
|
|
33141
|
+
// packages/cli/src/hooks/extract-file-path.ts
|
|
33142
|
+
function extractFilePath(input2) {
|
|
33143
|
+
if (typeof input2 !== "object" || input2 === null) return null;
|
|
33144
|
+
const obj = input2;
|
|
33145
|
+
const toolInput = obj["tool_input"];
|
|
33146
|
+
if (typeof toolInput === "object" && toolInput !== null) {
|
|
33147
|
+
const filePath = toolInput["file_path"];
|
|
33148
|
+
if (typeof filePath === "string") return filePath;
|
|
33149
|
+
}
|
|
33150
|
+
const toolName = obj["tool_name"];
|
|
33151
|
+
if (typeof toolName === "string" && /^write_|^edit_/.test(toolName)) {
|
|
33152
|
+
const args = obj["arguments"];
|
|
33153
|
+
if (typeof args === "object" && args !== null) {
|
|
33154
|
+
const filePath = args["file_path"];
|
|
33155
|
+
if (typeof filePath === "string") return filePath;
|
|
33156
|
+
}
|
|
33157
|
+
}
|
|
33158
|
+
if (typeof obj["filePath"] === "string") return obj["filePath"];
|
|
33159
|
+
if (typeof obj["file_path"] === "string" && !toolInput) return obj["file_path"];
|
|
33160
|
+
if (typeof obj["file"] === "string") return obj["file"];
|
|
33161
|
+
return null;
|
|
33162
|
+
}
|
|
33163
|
+
|
|
33164
|
+
// packages/cli/src/hooks/detect-marker.ts
|
|
33165
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
33166
|
+
var MARKER_PATTERNS = [
|
|
33167
|
+
"<!-- PromptScript",
|
|
33168
|
+
"# promptscript-generated:",
|
|
33169
|
+
"> Auto-generated by PromptScript"
|
|
33170
|
+
];
|
|
33171
|
+
var SOURCE_TARGET_RE = /\|\s*source:\s*(\S+)\s*\|\s*target:\s*(\S+)/;
|
|
33172
|
+
async function detectMarker(filePath) {
|
|
33173
|
+
let content;
|
|
33174
|
+
try {
|
|
33175
|
+
content = await readFile18(filePath, "utf-8");
|
|
33176
|
+
} catch {
|
|
33177
|
+
return { isGenerated: false, source: null, target: null };
|
|
33178
|
+
}
|
|
33179
|
+
const lines = content.split("\n").slice(0, 50);
|
|
33180
|
+
for (const line of lines) {
|
|
33181
|
+
if (MARKER_PATTERNS.some((pattern) => line.includes(pattern))) {
|
|
33182
|
+
const match = SOURCE_TARGET_RE.exec(line);
|
|
33183
|
+
return {
|
|
33184
|
+
isGenerated: true,
|
|
33185
|
+
source: match?.[1] ?? null,
|
|
33186
|
+
target: match?.[2] ?? null
|
|
33187
|
+
};
|
|
33188
|
+
}
|
|
33189
|
+
}
|
|
33190
|
+
return { isGenerated: false, source: null, target: null };
|
|
33191
|
+
}
|
|
33192
|
+
|
|
33193
|
+
// packages/cli/src/hooks/debounce-lock.ts
|
|
33194
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
33195
|
+
import { existsSync as existsSync21, writeFileSync as writeFileSync4, unlinkSync, statSync } from "node:fs";
|
|
33196
|
+
import { tmpdir } from "node:os";
|
|
33197
|
+
import { join as join19 } from "node:path";
|
|
33198
|
+
var STALE_THRESHOLD_MS = 3e4;
|
|
33199
|
+
function getLockPath(projectDir) {
|
|
33200
|
+
const hash = createHash2("md5").update(projectDir).digest("hex").slice(0, 12);
|
|
33201
|
+
return join19(tmpdir(), `prs-compile-${hash}.lock`);
|
|
33202
|
+
}
|
|
33203
|
+
function acquireLock(projectDir) {
|
|
33204
|
+
const lockPath = getLockPath(projectDir);
|
|
33205
|
+
if (existsSync21(lockPath)) {
|
|
33206
|
+
try {
|
|
33207
|
+
const stat3 = statSync(lockPath);
|
|
33208
|
+
const age = Date.now() - stat3.mtimeMs;
|
|
33209
|
+
if (age < STALE_THRESHOLD_MS) return false;
|
|
33210
|
+
unlinkSync(lockPath);
|
|
33211
|
+
} catch {
|
|
33212
|
+
}
|
|
33213
|
+
}
|
|
33214
|
+
try {
|
|
33215
|
+
writeFileSync4(lockPath, String(process.pid), { flag: "wx" });
|
|
33216
|
+
return true;
|
|
33217
|
+
} catch {
|
|
33218
|
+
return false;
|
|
33219
|
+
}
|
|
33220
|
+
}
|
|
33221
|
+
function releaseLock(projectDir) {
|
|
33222
|
+
try {
|
|
33223
|
+
unlinkSync(getLockPath(projectDir));
|
|
33224
|
+
} catch {
|
|
33225
|
+
}
|
|
33226
|
+
}
|
|
33227
|
+
|
|
33228
|
+
// packages/cli/src/commands/hook.ts
|
|
33229
|
+
async function readStdin() {
|
|
33230
|
+
const chunks = [];
|
|
33231
|
+
for await (const chunk of process.stdin) {
|
|
33232
|
+
chunks.push(chunk);
|
|
33233
|
+
}
|
|
33234
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
33235
|
+
}
|
|
33236
|
+
function parseInput(raw) {
|
|
33237
|
+
if (raw.trim() === "") return null;
|
|
33238
|
+
try {
|
|
33239
|
+
const parsed = JSON.parse(raw);
|
|
33240
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
33241
|
+
return parsed;
|
|
33242
|
+
} catch {
|
|
33243
|
+
process.exitCode = 1;
|
|
33244
|
+
process.stderr.write("prs hook: malformed JSON on stdin\n");
|
|
33245
|
+
return null;
|
|
33246
|
+
}
|
|
33247
|
+
}
|
|
33248
|
+
async function handlePreEdit(payload) {
|
|
33249
|
+
const filePath = extractFilePath(payload);
|
|
33250
|
+
if (!filePath) return;
|
|
33251
|
+
const marker = await detectMarker(filePath);
|
|
33252
|
+
if (!marker.isGenerated) return;
|
|
33253
|
+
process.exitCode = 2;
|
|
33254
|
+
if (marker.source) {
|
|
33255
|
+
process.stderr.write(
|
|
33256
|
+
`prs hook: ${filePath} is generated by PromptScript from ${marker.source} \u2014 edit the source instead
|
|
33257
|
+
`
|
|
33258
|
+
);
|
|
33259
|
+
} else {
|
|
33260
|
+
process.stderr.write(
|
|
33261
|
+
`prs hook: ${filePath} is generated by PromptScript \u2014 run prs compile after editing the source
|
|
33262
|
+
`
|
|
33263
|
+
);
|
|
33264
|
+
}
|
|
33265
|
+
}
|
|
33266
|
+
async function handlePostEdit(payload) {
|
|
33267
|
+
const filePath = extractFilePath(payload);
|
|
33268
|
+
if (!filePath) return;
|
|
33269
|
+
if (!filePath.endsWith(".prs")) return;
|
|
33270
|
+
const cwd = typeof payload["cwd"] === "string" ? payload["cwd"] : process.cwd();
|
|
33271
|
+
if (!acquireLock(cwd)) return;
|
|
33272
|
+
try {
|
|
33273
|
+
await compileCommand({ cwd });
|
|
33274
|
+
} catch (error) {
|
|
33275
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33276
|
+
process.stderr.write(`prs hook: compile error: ${message}
|
|
33277
|
+
`);
|
|
33278
|
+
} finally {
|
|
33279
|
+
releaseLock(cwd);
|
|
33280
|
+
}
|
|
33281
|
+
}
|
|
33282
|
+
async function hookCommand(action, internalOpts) {
|
|
33283
|
+
const raw = internalOpts?.stdin !== void 0 ? internalOpts.stdin : await readStdin();
|
|
33284
|
+
const payload = parseInput(raw);
|
|
33285
|
+
if (!payload) return;
|
|
33286
|
+
switch (action) {
|
|
33287
|
+
case "pre-edit":
|
|
33288
|
+
await handlePreEdit(payload);
|
|
33289
|
+
break;
|
|
33290
|
+
case "post-edit":
|
|
33291
|
+
await handlePostEdit(payload);
|
|
33292
|
+
break;
|
|
33293
|
+
default:
|
|
33294
|
+
process.exitCode = 1;
|
|
33295
|
+
process.stderr.write(`prs hook: unknown action "${action}"
|
|
33296
|
+
`);
|
|
33297
|
+
}
|
|
33298
|
+
}
|
|
33299
|
+
|
|
33300
|
+
// packages/cli/src/commands/hooks.ts
|
|
33301
|
+
init_console();
|
|
33302
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
33303
|
+
import { readFile as readFile19, writeFile as writeFile10, mkdir as mkdir8, chmod, unlink } from "node:fs/promises";
|
|
33304
|
+
import { dirname as dirname10, resolve as resolve16 } from "node:path";
|
|
33305
|
+
|
|
33306
|
+
// packages/cli/src/hooks/tool-configs/claude.ts
|
|
33307
|
+
function isPrsHookEntry(entry) {
|
|
33308
|
+
if (typeof entry !== "object" || entry === null) {
|
|
33309
|
+
return false;
|
|
33310
|
+
}
|
|
33311
|
+
const obj = entry;
|
|
33312
|
+
const hooks = obj["hooks"];
|
|
33313
|
+
if (!Array.isArray(hooks)) {
|
|
33314
|
+
return false;
|
|
33315
|
+
}
|
|
33316
|
+
return hooks.some((h) => {
|
|
33317
|
+
if (typeof h !== "object" || h === null) return false;
|
|
33318
|
+
const hook = h;
|
|
33319
|
+
return typeof hook["command"] === "string" && hook["command"].includes("prs hook");
|
|
33320
|
+
});
|
|
33321
|
+
}
|
|
33322
|
+
function getHooksSection(existing) {
|
|
33323
|
+
const hooks = existing["hooks"];
|
|
33324
|
+
if (typeof hooks === "object" && hooks !== null && !Array.isArray(hooks)) {
|
|
33325
|
+
return hooks;
|
|
33326
|
+
}
|
|
33327
|
+
return {};
|
|
33328
|
+
}
|
|
33329
|
+
function getHookArray(hooksSection, key) {
|
|
33330
|
+
const arr = hooksSection[key];
|
|
33331
|
+
return Array.isArray(arr) ? arr : [];
|
|
33332
|
+
}
|
|
33333
|
+
var claudeConfig = {
|
|
33334
|
+
name: "claude",
|
|
33335
|
+
detectPaths: [".claude"],
|
|
33336
|
+
settingsPath: ".claude/settings.json",
|
|
33337
|
+
timeoutUnit: "seconds",
|
|
33338
|
+
generatePreEditHook(prsPath) {
|
|
33339
|
+
return {
|
|
33340
|
+
matcher: "Edit|Write",
|
|
33341
|
+
hooks: [
|
|
33342
|
+
{
|
|
33343
|
+
type: "command",
|
|
33344
|
+
command: `${prsPath} hook pre-edit`,
|
|
33345
|
+
timeout: 5,
|
|
33346
|
+
statusMessage: "PromptScript: checking generated files..."
|
|
33347
|
+
}
|
|
33348
|
+
]
|
|
33349
|
+
};
|
|
33350
|
+
},
|
|
33351
|
+
generatePostEditHook(prsPath) {
|
|
33352
|
+
return {
|
|
33353
|
+
matcher: "Edit|Write",
|
|
33354
|
+
hooks: [
|
|
33355
|
+
{
|
|
33356
|
+
type: "command",
|
|
33357
|
+
command: `${prsPath} hook post-edit`,
|
|
33358
|
+
timeout: 15,
|
|
33359
|
+
statusMessage: "PromptScript: compiling..."
|
|
33360
|
+
}
|
|
33361
|
+
]
|
|
33362
|
+
};
|
|
33363
|
+
},
|
|
33364
|
+
mergeIntoSettings(existing, prsPath) {
|
|
33365
|
+
const hooksSection = getHooksSection(existing);
|
|
33366
|
+
const preToolUse = getHookArray(hooksSection, "PreToolUse");
|
|
33367
|
+
const postToolUse = getHookArray(hooksSection, "PostToolUse");
|
|
33368
|
+
const preAlreadyInstalled = preToolUse.some(isPrsHookEntry);
|
|
33369
|
+
const postAlreadyInstalled = postToolUse.some(isPrsHookEntry);
|
|
33370
|
+
const newPreToolUse = preAlreadyInstalled ? preToolUse : [...preToolUse, this.generatePreEditHook(prsPath)];
|
|
33371
|
+
const newPostToolUse = postAlreadyInstalled ? postToolUse : [...postToolUse, this.generatePostEditHook(prsPath)];
|
|
33372
|
+
return {
|
|
33373
|
+
...existing,
|
|
33374
|
+
hooks: {
|
|
33375
|
+
...hooksSection,
|
|
33376
|
+
PreToolUse: newPreToolUse,
|
|
33377
|
+
PostToolUse: newPostToolUse
|
|
33378
|
+
}
|
|
33379
|
+
};
|
|
33380
|
+
},
|
|
33381
|
+
removeFromSettings(existing) {
|
|
33382
|
+
const hooksSection = getHooksSection(existing);
|
|
33383
|
+
const preToolUse = getHookArray(hooksSection, "PreToolUse");
|
|
33384
|
+
const postToolUse = getHookArray(hooksSection, "PostToolUse");
|
|
33385
|
+
return {
|
|
33386
|
+
...existing,
|
|
33387
|
+
hooks: {
|
|
33388
|
+
...hooksSection,
|
|
33389
|
+
PreToolUse: preToolUse.filter((e) => !isPrsHookEntry(e)),
|
|
33390
|
+
PostToolUse: postToolUse.filter((e) => !isPrsHookEntry(e))
|
|
33391
|
+
}
|
|
33392
|
+
};
|
|
33393
|
+
}
|
|
33394
|
+
};
|
|
33395
|
+
|
|
33396
|
+
// packages/cli/src/hooks/tool-configs/factory.ts
|
|
33397
|
+
function getHooksSection2(existing) {
|
|
33398
|
+
const hooks = existing["hooks"];
|
|
33399
|
+
if (typeof hooks === "object" && hooks !== null && !Array.isArray(hooks)) {
|
|
33400
|
+
return hooks;
|
|
33401
|
+
}
|
|
33402
|
+
return {};
|
|
33403
|
+
}
|
|
33404
|
+
function getHookArray2(hooksSection, key) {
|
|
33405
|
+
const arr = hooksSection[key];
|
|
33406
|
+
return Array.isArray(arr) ? arr : [];
|
|
33407
|
+
}
|
|
33408
|
+
var factoryConfig = {
|
|
33409
|
+
name: "factory",
|
|
33410
|
+
detectPaths: [".factory"],
|
|
33411
|
+
settingsPath: ".factory/settings.json",
|
|
33412
|
+
timeoutUnit: "seconds",
|
|
33413
|
+
generatePreEditHook(prsPath) {
|
|
33414
|
+
return {
|
|
33415
|
+
matcher: "Create|Edit",
|
|
33416
|
+
hooks: [
|
|
33417
|
+
{
|
|
33418
|
+
type: "command",
|
|
33419
|
+
command: `${prsPath} hook pre-edit`,
|
|
33420
|
+
timeout: 5,
|
|
33421
|
+
statusMessage: "PromptScript: checking generated files..."
|
|
33422
|
+
}
|
|
33423
|
+
]
|
|
33424
|
+
};
|
|
33425
|
+
},
|
|
33426
|
+
generatePostEditHook(prsPath) {
|
|
33427
|
+
return {
|
|
33428
|
+
matcher: "Create|Edit",
|
|
33429
|
+
hooks: [
|
|
33430
|
+
{
|
|
33431
|
+
type: "command",
|
|
33432
|
+
command: `${prsPath} hook post-edit`,
|
|
33433
|
+
timeout: 15,
|
|
33434
|
+
statusMessage: "PromptScript: compiling..."
|
|
33435
|
+
}
|
|
33436
|
+
]
|
|
33437
|
+
};
|
|
33438
|
+
},
|
|
33439
|
+
mergeIntoSettings(existing, prsPath) {
|
|
33440
|
+
const hooksSection = getHooksSection2(existing);
|
|
33441
|
+
const preToolUse = getHookArray2(hooksSection, "PreToolUse");
|
|
33442
|
+
const postToolUse = getHookArray2(hooksSection, "PostToolUse");
|
|
33443
|
+
const newPreToolUse = preToolUse.some(isPrsHookEntry) ? preToolUse : [...preToolUse, this.generatePreEditHook(prsPath)];
|
|
33444
|
+
const newPostToolUse = postToolUse.some(isPrsHookEntry) ? postToolUse : [...postToolUse, this.generatePostEditHook(prsPath)];
|
|
33445
|
+
return {
|
|
33446
|
+
...existing,
|
|
33447
|
+
hooks: {
|
|
33448
|
+
...hooksSection,
|
|
33449
|
+
PreToolUse: newPreToolUse,
|
|
33450
|
+
PostToolUse: newPostToolUse
|
|
33451
|
+
}
|
|
33452
|
+
};
|
|
33453
|
+
},
|
|
33454
|
+
removeFromSettings(existing) {
|
|
33455
|
+
const hooksSection = getHooksSection2(existing);
|
|
33456
|
+
const preToolUse = getHookArray2(hooksSection, "PreToolUse");
|
|
33457
|
+
const postToolUse = getHookArray2(hooksSection, "PostToolUse");
|
|
33458
|
+
return {
|
|
33459
|
+
...existing,
|
|
33460
|
+
hooks: {
|
|
33461
|
+
...hooksSection,
|
|
33462
|
+
PreToolUse: preToolUse.filter((e) => !isPrsHookEntry(e)),
|
|
33463
|
+
PostToolUse: postToolUse.filter((e) => !isPrsHookEntry(e))
|
|
33464
|
+
}
|
|
33465
|
+
};
|
|
33466
|
+
}
|
|
33467
|
+
};
|
|
33468
|
+
|
|
33469
|
+
// packages/cli/src/hooks/tool-configs/cursor.ts
|
|
33470
|
+
function getHooksSection3(existing) {
|
|
33471
|
+
const hooks = existing["hooks"];
|
|
33472
|
+
if (typeof hooks === "object" && hooks !== null && !Array.isArray(hooks)) {
|
|
33473
|
+
return hooks;
|
|
33474
|
+
}
|
|
33475
|
+
return {};
|
|
33476
|
+
}
|
|
33477
|
+
function getHookArray3(hooksSection, key) {
|
|
33478
|
+
const arr = hooksSection[key];
|
|
33479
|
+
return Array.isArray(arr) ? arr : [];
|
|
33480
|
+
}
|
|
33481
|
+
function isPrsCursorHookEntry(entry) {
|
|
33482
|
+
if (typeof entry !== "object" || entry === null) {
|
|
33483
|
+
return false;
|
|
33484
|
+
}
|
|
33485
|
+
const obj = entry;
|
|
33486
|
+
const command = obj["command"];
|
|
33487
|
+
if (!Array.isArray(command)) {
|
|
33488
|
+
return false;
|
|
33489
|
+
}
|
|
33490
|
+
return command.some((part) => typeof part === "string" && part.includes("prs hook"));
|
|
33491
|
+
}
|
|
33492
|
+
var cursorConfig = {
|
|
33493
|
+
name: "cursor",
|
|
33494
|
+
detectPaths: [".cursor"],
|
|
33495
|
+
settingsPath: ".cursor/hooks.json",
|
|
33496
|
+
timeoutUnit: "milliseconds",
|
|
33497
|
+
generatePreEditHook(prsPath) {
|
|
33498
|
+
return {
|
|
33499
|
+
hooks: {
|
|
33500
|
+
beforeFileEdit: [
|
|
33501
|
+
{
|
|
33502
|
+
command: ["bash", "-c", `${prsPath} hook pre-edit`],
|
|
33503
|
+
timeout_ms: 5e3
|
|
33504
|
+
}
|
|
33505
|
+
]
|
|
33506
|
+
}
|
|
33507
|
+
};
|
|
33508
|
+
},
|
|
33509
|
+
generatePostEditHook(prsPath) {
|
|
33510
|
+
return {
|
|
33511
|
+
hooks: {
|
|
33512
|
+
afterFileEdit: [
|
|
33513
|
+
{
|
|
33514
|
+
command: ["bash", "-c", `${prsPath} hook post-edit`],
|
|
33515
|
+
timeout_ms: 15e3
|
|
33516
|
+
}
|
|
33517
|
+
]
|
|
33518
|
+
}
|
|
33519
|
+
};
|
|
33520
|
+
},
|
|
33521
|
+
mergeIntoSettings(existing, prsPath) {
|
|
33522
|
+
const hooksSection = getHooksSection3(existing);
|
|
33523
|
+
const beforeFileEdit = getHookArray3(hooksSection, "beforeFileEdit");
|
|
33524
|
+
const afterFileEdit = getHookArray3(hooksSection, "afterFileEdit");
|
|
33525
|
+
const newBeforeFileEdit = beforeFileEdit.some(isPrsCursorHookEntry) ? beforeFileEdit : [
|
|
33526
|
+
...beforeFileEdit,
|
|
33527
|
+
{
|
|
33528
|
+
command: ["bash", "-c", `${prsPath} hook pre-edit`],
|
|
33529
|
+
timeout_ms: 5e3
|
|
33530
|
+
}
|
|
33531
|
+
];
|
|
33532
|
+
const newAfterFileEdit = afterFileEdit.some(isPrsCursorHookEntry) ? afterFileEdit : [
|
|
33533
|
+
...afterFileEdit,
|
|
33534
|
+
{
|
|
33535
|
+
command: ["bash", "-c", `${prsPath} hook post-edit`],
|
|
33536
|
+
timeout_ms: 15e3
|
|
33537
|
+
}
|
|
33538
|
+
];
|
|
33539
|
+
return {
|
|
33540
|
+
...existing,
|
|
33541
|
+
hooks: {
|
|
33542
|
+
...hooksSection,
|
|
33543
|
+
beforeFileEdit: newBeforeFileEdit,
|
|
33544
|
+
afterFileEdit: newAfterFileEdit
|
|
33545
|
+
}
|
|
33546
|
+
};
|
|
33547
|
+
},
|
|
33548
|
+
removeFromSettings(existing) {
|
|
33549
|
+
const hooksSection = getHooksSection3(existing);
|
|
33550
|
+
const beforeFileEdit = getHookArray3(hooksSection, "beforeFileEdit");
|
|
33551
|
+
const afterFileEdit = getHookArray3(hooksSection, "afterFileEdit");
|
|
33552
|
+
return {
|
|
33553
|
+
...existing,
|
|
33554
|
+
hooks: {
|
|
33555
|
+
...hooksSection,
|
|
33556
|
+
beforeFileEdit: beforeFileEdit.filter((e) => !isPrsCursorHookEntry(e)),
|
|
33557
|
+
afterFileEdit: afterFileEdit.filter((e) => !isPrsCursorHookEntry(e))
|
|
33558
|
+
}
|
|
33559
|
+
};
|
|
33560
|
+
}
|
|
33561
|
+
};
|
|
33562
|
+
|
|
33563
|
+
// packages/cli/src/hooks/tool-configs/windsurf.ts
|
|
33564
|
+
function getHooksSection4(existing) {
|
|
33565
|
+
const hooks = existing["hooks"];
|
|
33566
|
+
if (typeof hooks === "object" && hooks !== null && !Array.isArray(hooks)) {
|
|
33567
|
+
return hooks;
|
|
33568
|
+
}
|
|
33569
|
+
return {};
|
|
33570
|
+
}
|
|
33571
|
+
function getHookArray4(hooksSection, key) {
|
|
33572
|
+
const arr = hooksSection[key];
|
|
33573
|
+
return Array.isArray(arr) ? arr : [];
|
|
33574
|
+
}
|
|
33575
|
+
var windsurfConfig = {
|
|
33576
|
+
name: "windsurf",
|
|
33577
|
+
detectPaths: [".windsurf"],
|
|
33578
|
+
settingsPath: ".windsurf/hooks.json",
|
|
33579
|
+
timeoutUnit: "milliseconds",
|
|
33580
|
+
generatePreEditHook(prsPath) {
|
|
33581
|
+
return {
|
|
33582
|
+
matcher: "Edit|Write",
|
|
33583
|
+
hooks: [
|
|
33584
|
+
{
|
|
33585
|
+
type: "command",
|
|
33586
|
+
command: `${prsPath} hook pre-edit`,
|
|
33587
|
+
timeout: 5e3,
|
|
33588
|
+
statusMessage: "PromptScript: checking generated files..."
|
|
33589
|
+
}
|
|
33590
|
+
]
|
|
33591
|
+
};
|
|
33592
|
+
},
|
|
33593
|
+
generatePostEditHook(prsPath) {
|
|
33594
|
+
return {
|
|
33595
|
+
matcher: "Edit|Write",
|
|
33596
|
+
hooks: [
|
|
33597
|
+
{
|
|
33598
|
+
type: "command",
|
|
33599
|
+
command: `${prsPath} hook post-edit`,
|
|
33600
|
+
timeout: 15e3,
|
|
33601
|
+
statusMessage: "PromptScript: compiling..."
|
|
33602
|
+
}
|
|
33603
|
+
]
|
|
33604
|
+
};
|
|
33605
|
+
},
|
|
33606
|
+
mergeIntoSettings(existing, prsPath) {
|
|
33607
|
+
const hooksSection = getHooksSection4(existing);
|
|
33608
|
+
const preWriteCode = getHookArray4(hooksSection, "pre_write_code");
|
|
33609
|
+
const postWriteCode = getHookArray4(hooksSection, "post_write_code");
|
|
33610
|
+
const newPreWriteCode = preWriteCode.some(isPrsHookEntry) ? preWriteCode : [...preWriteCode, this.generatePreEditHook(prsPath)];
|
|
33611
|
+
const newPostWriteCode = postWriteCode.some(isPrsHookEntry) ? postWriteCode : [...postWriteCode, this.generatePostEditHook(prsPath)];
|
|
33612
|
+
return {
|
|
33613
|
+
...existing,
|
|
33614
|
+
hooks: {
|
|
33615
|
+
...hooksSection,
|
|
33616
|
+
pre_write_code: newPreWriteCode,
|
|
33617
|
+
post_write_code: newPostWriteCode
|
|
33618
|
+
}
|
|
33619
|
+
};
|
|
33620
|
+
},
|
|
33621
|
+
removeFromSettings(existing) {
|
|
33622
|
+
const hooksSection = getHooksSection4(existing);
|
|
33623
|
+
const preWriteCode = getHookArray4(hooksSection, "pre_write_code");
|
|
33624
|
+
const postWriteCode = getHookArray4(hooksSection, "post_write_code");
|
|
33625
|
+
return {
|
|
33626
|
+
...existing,
|
|
33627
|
+
hooks: {
|
|
33628
|
+
...hooksSection,
|
|
33629
|
+
pre_write_code: preWriteCode.filter((e) => !isPrsHookEntry(e)),
|
|
33630
|
+
post_write_code: postWriteCode.filter((e) => !isPrsHookEntry(e))
|
|
33631
|
+
}
|
|
33632
|
+
};
|
|
33633
|
+
}
|
|
33634
|
+
};
|
|
33635
|
+
|
|
33636
|
+
// packages/cli/src/hooks/tool-configs/cline.ts
|
|
33637
|
+
var clineConfig = {
|
|
33638
|
+
name: "cline",
|
|
33639
|
+
detectPaths: [".clinerules"],
|
|
33640
|
+
settingsPath: ".clinerules/hooks/",
|
|
33641
|
+
timeoutUnit: "n/a",
|
|
33642
|
+
generatePreEditHook(prsPath) {
|
|
33643
|
+
return {
|
|
33644
|
+
scriptPath: ".clinerules/hooks/prs-pre-edit.sh",
|
|
33645
|
+
content: `#!/bin/bash
|
|
33646
|
+
${prsPath} hook pre-edit`
|
|
33647
|
+
};
|
|
33648
|
+
},
|
|
33649
|
+
generatePostEditHook(prsPath) {
|
|
33650
|
+
return {
|
|
33651
|
+
scriptPath: ".clinerules/hooks/prs-post-edit.sh",
|
|
33652
|
+
content: `#!/bin/bash
|
|
33653
|
+
${prsPath} hook post-edit`
|
|
33654
|
+
};
|
|
33655
|
+
},
|
|
33656
|
+
mergeIntoSettings(existing, _prsPath) {
|
|
33657
|
+
return existing;
|
|
33658
|
+
},
|
|
33659
|
+
removeFromSettings(existing) {
|
|
33660
|
+
return existing;
|
|
33661
|
+
}
|
|
33662
|
+
};
|
|
33663
|
+
|
|
33664
|
+
// packages/cli/src/hooks/tool-configs/copilot.ts
|
|
33665
|
+
function getHooksSection5(existing) {
|
|
33666
|
+
const hooks = existing["hooks"];
|
|
33667
|
+
if (typeof hooks === "object" && hooks !== null && !Array.isArray(hooks)) {
|
|
33668
|
+
return hooks;
|
|
33669
|
+
}
|
|
33670
|
+
return {};
|
|
33671
|
+
}
|
|
33672
|
+
function getHookArray5(hooksSection, key) {
|
|
33673
|
+
const arr = hooksSection[key];
|
|
33674
|
+
return Array.isArray(arr) ? arr : [];
|
|
33675
|
+
}
|
|
33676
|
+
var copilotConfig = {
|
|
33677
|
+
name: "copilot",
|
|
33678
|
+
detectPaths: [".vscode", ".github/copilot-instructions.md"],
|
|
33679
|
+
settingsPath: ".vscode/hooks.json",
|
|
33680
|
+
timeoutUnit: "seconds",
|
|
33681
|
+
generatePreEditHook(prsPath) {
|
|
33682
|
+
return {
|
|
33683
|
+
matcher: "Edit|Write",
|
|
33684
|
+
hooks: [
|
|
33685
|
+
{
|
|
33686
|
+
type: "command",
|
|
33687
|
+
command: `${prsPath} hook pre-edit`,
|
|
33688
|
+
timeout: 5,
|
|
33689
|
+
statusMessage: "PromptScript: checking generated files..."
|
|
33690
|
+
}
|
|
33691
|
+
]
|
|
33692
|
+
};
|
|
33693
|
+
},
|
|
33694
|
+
generatePostEditHook(prsPath) {
|
|
33695
|
+
return {
|
|
33696
|
+
matcher: "Edit|Write",
|
|
33697
|
+
hooks: [
|
|
33698
|
+
{
|
|
33699
|
+
type: "command",
|
|
33700
|
+
command: `${prsPath} hook post-edit`,
|
|
33701
|
+
timeout: 15,
|
|
33702
|
+
statusMessage: "PromptScript: compiling..."
|
|
33703
|
+
}
|
|
33704
|
+
]
|
|
33705
|
+
};
|
|
33706
|
+
},
|
|
33707
|
+
mergeIntoSettings(existing, prsPath) {
|
|
33708
|
+
const hooksSection = getHooksSection5(existing);
|
|
33709
|
+
const preToolUse = getHookArray5(hooksSection, "PreToolUse");
|
|
33710
|
+
const postToolUse = getHookArray5(hooksSection, "PostToolUse");
|
|
33711
|
+
const newPreToolUse = preToolUse.some(isPrsHookEntry) ? preToolUse : [...preToolUse, this.generatePreEditHook(prsPath)];
|
|
33712
|
+
const newPostToolUse = postToolUse.some(isPrsHookEntry) ? postToolUse : [...postToolUse, this.generatePostEditHook(prsPath)];
|
|
33713
|
+
return {
|
|
33714
|
+
...existing,
|
|
33715
|
+
hooks: {
|
|
33716
|
+
...hooksSection,
|
|
33717
|
+
PreToolUse: newPreToolUse,
|
|
33718
|
+
PostToolUse: newPostToolUse
|
|
33719
|
+
}
|
|
33720
|
+
};
|
|
33721
|
+
},
|
|
33722
|
+
removeFromSettings(existing) {
|
|
33723
|
+
const hooksSection = getHooksSection5(existing);
|
|
33724
|
+
const preToolUse = getHookArray5(hooksSection, "PreToolUse");
|
|
33725
|
+
const postToolUse = getHookArray5(hooksSection, "PostToolUse");
|
|
33726
|
+
return {
|
|
33727
|
+
...existing,
|
|
33728
|
+
hooks: {
|
|
33729
|
+
...hooksSection,
|
|
33730
|
+
PreToolUse: preToolUse.filter((e) => !isPrsHookEntry(e)),
|
|
33731
|
+
PostToolUse: postToolUse.filter((e) => !isPrsHookEntry(e))
|
|
33732
|
+
}
|
|
33733
|
+
};
|
|
33734
|
+
}
|
|
33735
|
+
};
|
|
33736
|
+
|
|
33737
|
+
// packages/cli/src/hooks/tool-configs/gemini.ts
|
|
33738
|
+
function getHooksSection6(existing) {
|
|
33739
|
+
const hooks = existing["hooks"];
|
|
33740
|
+
if (typeof hooks === "object" && hooks !== null && !Array.isArray(hooks)) {
|
|
33741
|
+
return hooks;
|
|
33742
|
+
}
|
|
33743
|
+
return {};
|
|
33744
|
+
}
|
|
33745
|
+
function getHookArray6(hooksSection, key) {
|
|
33746
|
+
const arr = hooksSection[key];
|
|
33747
|
+
return Array.isArray(arr) ? arr : [];
|
|
33748
|
+
}
|
|
33749
|
+
function isPrsGeminiHookEntry(entry) {
|
|
33750
|
+
if (typeof entry !== "object" || entry === null) {
|
|
33751
|
+
return false;
|
|
33752
|
+
}
|
|
33753
|
+
const obj = entry;
|
|
33754
|
+
const hooks = obj["hooks"];
|
|
33755
|
+
if (!Array.isArray(hooks)) {
|
|
33756
|
+
return false;
|
|
33757
|
+
}
|
|
33758
|
+
return hooks.some((h) => {
|
|
33759
|
+
if (typeof h !== "object" || h === null) return false;
|
|
33760
|
+
const hook = h;
|
|
33761
|
+
return typeof hook["command"] === "string" && hook["command"].includes("prs hook");
|
|
33762
|
+
});
|
|
33763
|
+
}
|
|
33764
|
+
var geminiConfig = {
|
|
33765
|
+
name: "gemini",
|
|
33766
|
+
detectPaths: [".gemini"],
|
|
33767
|
+
settingsPath: ".gemini/settings.json",
|
|
33768
|
+
timeoutUnit: "milliseconds",
|
|
33769
|
+
generatePreEditHook(prsPath) {
|
|
33770
|
+
return {
|
|
33771
|
+
matcher: "write_.*|edit_.*",
|
|
33772
|
+
hooks: [
|
|
33773
|
+
{
|
|
33774
|
+
type: "command",
|
|
33775
|
+
command: `${prsPath} hook pre-edit`,
|
|
33776
|
+
timeout: 5e3
|
|
33777
|
+
}
|
|
33778
|
+
]
|
|
33779
|
+
};
|
|
33780
|
+
},
|
|
33781
|
+
generatePostEditHook(prsPath) {
|
|
33782
|
+
return {
|
|
33783
|
+
matcher: "write_.*|edit_.*",
|
|
33784
|
+
hooks: [
|
|
33785
|
+
{
|
|
33786
|
+
type: "command",
|
|
33787
|
+
command: `${prsPath} hook post-edit`,
|
|
33788
|
+
timeout: 15e3
|
|
33789
|
+
}
|
|
33790
|
+
]
|
|
33791
|
+
};
|
|
33792
|
+
},
|
|
33793
|
+
mergeIntoSettings(existing, prsPath) {
|
|
33794
|
+
const hooksSection = getHooksSection6(existing);
|
|
33795
|
+
const beforeTool = getHookArray6(hooksSection, "BeforeTool");
|
|
33796
|
+
const afterTool = getHookArray6(hooksSection, "AfterTool");
|
|
33797
|
+
const newBeforeTool = beforeTool.some(isPrsGeminiHookEntry) ? beforeTool : [...beforeTool, this.generatePreEditHook(prsPath)];
|
|
33798
|
+
const newAfterTool = afterTool.some(isPrsGeminiHookEntry) ? afterTool : [...afterTool, this.generatePostEditHook(prsPath)];
|
|
33799
|
+
return {
|
|
33800
|
+
...existing,
|
|
33801
|
+
hooks: {
|
|
33802
|
+
...hooksSection,
|
|
33803
|
+
BeforeTool: newBeforeTool,
|
|
33804
|
+
AfterTool: newAfterTool
|
|
33805
|
+
}
|
|
33806
|
+
};
|
|
33807
|
+
},
|
|
33808
|
+
removeFromSettings(existing) {
|
|
33809
|
+
const hooksSection = getHooksSection6(existing);
|
|
33810
|
+
const beforeTool = getHookArray6(hooksSection, "BeforeTool");
|
|
33811
|
+
const afterTool = getHookArray6(hooksSection, "AfterTool");
|
|
33812
|
+
return {
|
|
33813
|
+
...existing,
|
|
33814
|
+
hooks: {
|
|
33815
|
+
...hooksSection,
|
|
33816
|
+
BeforeTool: beforeTool.filter((e) => !isPrsGeminiHookEntry(e)),
|
|
33817
|
+
AfterTool: afterTool.filter((e) => !isPrsGeminiHookEntry(e))
|
|
33818
|
+
}
|
|
33819
|
+
};
|
|
33820
|
+
}
|
|
33821
|
+
};
|
|
33822
|
+
|
|
33823
|
+
// packages/cli/src/hooks/tool-configs/index.ts
|
|
33824
|
+
var ALL_TOOL_CONFIGS = [
|
|
33825
|
+
claudeConfig,
|
|
33826
|
+
factoryConfig,
|
|
33827
|
+
cursorConfig,
|
|
33828
|
+
windsurfConfig,
|
|
33829
|
+
clineConfig,
|
|
33830
|
+
copilotConfig,
|
|
33831
|
+
geminiConfig
|
|
33832
|
+
];
|
|
33833
|
+
function getToolConfig(name) {
|
|
33834
|
+
return ALL_TOOL_CONFIGS.find((c) => c.name === name);
|
|
33835
|
+
}
|
|
33836
|
+
|
|
33837
|
+
// packages/cli/src/commands/hooks.ts
|
|
33838
|
+
function resolvePrsPath() {
|
|
33839
|
+
const scriptPath = process.argv[1];
|
|
33840
|
+
if (scriptPath && !scriptPath.includes("node_modules/.bin") && !scriptPath.includes("pnpm")) {
|
|
33841
|
+
return scriptPath;
|
|
33842
|
+
}
|
|
33843
|
+
if (scriptPath) {
|
|
33844
|
+
ConsoleOutput.warning(
|
|
33845
|
+
`Detected ephemeral script path (${scriptPath}), using bare "prs" instead`
|
|
33846
|
+
);
|
|
33847
|
+
}
|
|
33848
|
+
return "prs";
|
|
33849
|
+
}
|
|
33850
|
+
function detectTools() {
|
|
33851
|
+
const cwd = process.cwd();
|
|
33852
|
+
return ALL_TOOL_CONFIGS.filter(
|
|
33853
|
+
(config) => config.detectPaths.some((p) => existsSync22(resolve16(cwd, p)))
|
|
33854
|
+
);
|
|
33855
|
+
}
|
|
33856
|
+
async function readSettingsFile(path) {
|
|
33857
|
+
try {
|
|
33858
|
+
const content = await readFile19(path, "utf-8");
|
|
33859
|
+
return JSON.parse(content);
|
|
33860
|
+
} catch {
|
|
33861
|
+
return {};
|
|
33862
|
+
}
|
|
33863
|
+
}
|
|
33864
|
+
async function writeSettingsFile(path, data) {
|
|
33865
|
+
await mkdir8(dirname10(path), { recursive: true });
|
|
33866
|
+
await writeFile10(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
33867
|
+
}
|
|
33868
|
+
function settingsUnchanged(original, merged) {
|
|
33869
|
+
return JSON.stringify(original) === JSON.stringify(merged);
|
|
33870
|
+
}
|
|
33871
|
+
async function installForTool(config, prsPath) {
|
|
33872
|
+
const cwd = process.cwd();
|
|
33873
|
+
const settingsPath = resolve16(cwd, config.settingsPath);
|
|
33874
|
+
if (config.name === "cline") {
|
|
33875
|
+
await installCline(config, prsPath, cwd);
|
|
33876
|
+
return;
|
|
33877
|
+
}
|
|
33878
|
+
const existing = await readSettingsFile(settingsPath);
|
|
33879
|
+
const merged = config.mergeIntoSettings(existing, prsPath);
|
|
33880
|
+
if (settingsUnchanged(existing, merged)) {
|
|
33881
|
+
ConsoleOutput.info(`${config.name}: hooks already installed`);
|
|
33882
|
+
return;
|
|
33883
|
+
}
|
|
33884
|
+
await writeSettingsFile(settingsPath, merged);
|
|
33885
|
+
ConsoleOutput.success(`${config.name}: hooks installed (${config.settingsPath})`);
|
|
33886
|
+
}
|
|
33887
|
+
async function installCline(config, prsPath, cwd) {
|
|
33888
|
+
const preHook = config.generatePreEditHook(prsPath);
|
|
33889
|
+
const postHook = config.generatePostEditHook(prsPath);
|
|
33890
|
+
for (const hook of [preHook, postHook]) {
|
|
33891
|
+
const fullPath = resolve16(cwd, hook.scriptPath);
|
|
33892
|
+
await mkdir8(dirname10(fullPath), { recursive: true });
|
|
33893
|
+
await writeFile10(fullPath, hook.content, "utf-8");
|
|
33894
|
+
await chmod(fullPath, 493);
|
|
33895
|
+
}
|
|
33896
|
+
ConsoleOutput.success(`${config.name}: hooks installed`);
|
|
33897
|
+
}
|
|
33898
|
+
async function uninstallForTool(config) {
|
|
33899
|
+
const cwd = process.cwd();
|
|
33900
|
+
const settingsPath = resolve16(cwd, config.settingsPath);
|
|
33901
|
+
if (config.name === "cline") {
|
|
33902
|
+
await uninstallCline(config, cwd);
|
|
33903
|
+
return;
|
|
33904
|
+
}
|
|
33905
|
+
let existing;
|
|
33906
|
+
try {
|
|
33907
|
+
const content = await readFile19(settingsPath, "utf-8");
|
|
33908
|
+
existing = JSON.parse(content);
|
|
33909
|
+
} catch {
|
|
33910
|
+
ConsoleOutput.info(`${config.name}: hooks not installed (no settings file)`);
|
|
33911
|
+
return;
|
|
33912
|
+
}
|
|
33913
|
+
const cleaned = config.removeFromSettings(existing);
|
|
33914
|
+
await writeSettingsFile(settingsPath, cleaned);
|
|
33915
|
+
ConsoleOutput.success(`${config.name}: hooks uninstalled`);
|
|
33916
|
+
}
|
|
33917
|
+
async function uninstallCline(config, cwd) {
|
|
33918
|
+
const prsPath = "prs";
|
|
33919
|
+
const preHook = config.generatePreEditHook(prsPath);
|
|
33920
|
+
const postHook = config.generatePostEditHook(prsPath);
|
|
33921
|
+
for (const hook of [preHook, postHook]) {
|
|
33922
|
+
const fullPath = resolve16(cwd, hook.scriptPath);
|
|
33923
|
+
if (existsSync22(fullPath)) {
|
|
33924
|
+
await unlink(fullPath);
|
|
33925
|
+
}
|
|
33926
|
+
}
|
|
33927
|
+
ConsoleOutput.success(`${config.name}: hooks uninstalled`);
|
|
33928
|
+
}
|
|
33929
|
+
function resolveTools(tool, _options) {
|
|
33930
|
+
if (tool) {
|
|
33931
|
+
const config = getToolConfig(tool);
|
|
33932
|
+
if (!config) {
|
|
33933
|
+
ConsoleOutput.error(
|
|
33934
|
+
`Unknown tool "${tool}". Available: ${ALL_TOOL_CONFIGS.map((c) => c.name).join(", ")}`
|
|
33935
|
+
);
|
|
33936
|
+
process.exitCode = 1;
|
|
33937
|
+
return null;
|
|
33938
|
+
}
|
|
33939
|
+
return [config];
|
|
33940
|
+
}
|
|
33941
|
+
const detected = detectTools();
|
|
33942
|
+
if (detected.length === 0) {
|
|
33943
|
+
ConsoleOutput.error(
|
|
33944
|
+
"No AI tools detected. Specify a tool name or create the tool config directory first."
|
|
33945
|
+
);
|
|
33946
|
+
process.exitCode = 1;
|
|
33947
|
+
return null;
|
|
33948
|
+
}
|
|
33949
|
+
return detected;
|
|
33950
|
+
}
|
|
33951
|
+
async function hooksCommand(action, tool, options = {}) {
|
|
33952
|
+
if (action !== "install" && action !== "uninstall") {
|
|
33953
|
+
ConsoleOutput.error(`Unknown action "${action}". Use "install" or "uninstall".`);
|
|
33954
|
+
process.exitCode = 1;
|
|
33955
|
+
return;
|
|
33956
|
+
}
|
|
33957
|
+
const tools = resolveTools(tool, options);
|
|
33958
|
+
if (!tools) return;
|
|
33959
|
+
const prsPath = resolvePrsPath();
|
|
33960
|
+
for (const config of tools) {
|
|
33961
|
+
if (action === "install") {
|
|
33962
|
+
await installForTool(config, prsPath);
|
|
33963
|
+
} else {
|
|
33964
|
+
await uninstallForTool(config);
|
|
33965
|
+
}
|
|
33966
|
+
}
|
|
33967
|
+
}
|
|
33968
|
+
|
|
33129
33969
|
// packages/cli/src/cli.ts
|
|
33130
33970
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
33131
|
-
var __dirname4 =
|
|
33971
|
+
var __dirname4 = dirname13(__filename4);
|
|
33132
33972
|
var program = new Command();
|
|
33133
33973
|
program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(__dirname3, "./package.json")).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", async (thisCommand, actionCommand) => {
|
|
33134
33974
|
const opts = thisCommand.opts();
|
|
@@ -33182,6 +34022,8 @@ var vendor = program.command("vendor").description("Manage vendored dependencies
|
|
|
33182
34022
|
vendor.command("sync").description("Copy all cached dependencies to .promptscript/vendor/").option("--dry-run", "Preview without copying files").action((opts) => vendorSyncCommand(opts));
|
|
33183
34023
|
vendor.command("check").description("Verify vendor directory matches lockfile").action((opts) => vendorCheckCommand(opts));
|
|
33184
34024
|
program.command("resolve <import>").description("Show full resolution chain for an import path (debug)").action((importPath, opts) => resolveCommand(importPath, opts));
|
|
34025
|
+
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);
|
|
34026
|
+
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);
|
|
33185
34027
|
var registry = program.command("registry").description("Manage PromptScript registries");
|
|
33186
34028
|
registerRegistryCommands(registry);
|
|
33187
34029
|
function run(args = process.argv) {
|