@promptscript/cli 1.0.0-alpha.4 → 1.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +2 -0
- package/index.js +933 -48
- package/package.json +27 -26
- 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/output/console.d.ts +11 -1
- package/src/output/console.d.ts.map +1 -1
- package/src/templates/migrate-skill.d.ts +9 -0
- package/src/templates/migrate-skill.d.ts.map +1 -0
- package/src/types.d.ts +2 -0
- package/src/types.d.ts.map +1 -1
- package/src/utils/ai-tools-detector.d.ts +10 -0
- package/src/utils/ai-tools-detector.d.ts.map +1 -1
- package/package.publish.json +0 -50
package/index.js
CHANGED
|
@@ -296,6 +296,14 @@ function getPackageVersion(baseDir, relativePath = "../package.json") {
|
|
|
296
296
|
return getPackageInfo(baseDir, relativePath).version;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
// packages/core/src/logger.ts
|
|
300
|
+
var noopLogger = {
|
|
301
|
+
verbose: () => {
|
|
302
|
+
},
|
|
303
|
+
debug: () => {
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
299
307
|
// packages/cli/src/commands/init.ts
|
|
300
308
|
import { fileURLToPath } from "url";
|
|
301
309
|
import { dirname as dirname2 } from "path";
|
|
@@ -331,6 +339,7 @@ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
|
331
339
|
LogLevel2[LogLevel2["Quiet"] = 0] = "Quiet";
|
|
332
340
|
LogLevel2[LogLevel2["Normal"] = 1] = "Normal";
|
|
333
341
|
LogLevel2[LogLevel2["Verbose"] = 2] = "Verbose";
|
|
342
|
+
LogLevel2[LogLevel2["Debug"] = 3] = "Debug";
|
|
334
343
|
return LogLevel2;
|
|
335
344
|
})(LogLevel || {});
|
|
336
345
|
var globalContext = {
|
|
@@ -349,6 +358,9 @@ function isVerbose() {
|
|
|
349
358
|
function isQuiet() {
|
|
350
359
|
return globalContext.logLevel <= 0 /* Quiet */;
|
|
351
360
|
}
|
|
361
|
+
function isDebug() {
|
|
362
|
+
return globalContext.logLevel >= 3 /* Debug */;
|
|
363
|
+
}
|
|
352
364
|
function createSpinner(text) {
|
|
353
365
|
if (isQuiet()) {
|
|
354
366
|
const noopSpinner = ora({ isSilent: true });
|
|
@@ -393,6 +405,13 @@ var ConsoleOutput = {
|
|
|
393
405
|
if (isQuiet()) return;
|
|
394
406
|
console.log(chalk.yellow(` \u2298 ${message}`));
|
|
395
407
|
},
|
|
408
|
+
/**
|
|
409
|
+
* Print an unchanged file message.
|
|
410
|
+
*/
|
|
411
|
+
unchanged(message) {
|
|
412
|
+
if (isQuiet()) return;
|
|
413
|
+
console.log(chalk.gray(` \u25CB ${message}`));
|
|
414
|
+
},
|
|
396
415
|
/**
|
|
397
416
|
* Print an info message.
|
|
398
417
|
*/
|
|
@@ -617,6 +636,16 @@ async function detectFrameworks(services) {
|
|
|
617
636
|
}
|
|
618
637
|
|
|
619
638
|
// packages/cli/src/utils/ai-tools-detector.ts
|
|
639
|
+
var PROMPTSCRIPT_MARKER = "PromptScript";
|
|
640
|
+
var INSTRUCTION_FILES = [
|
|
641
|
+
"CLAUDE.md",
|
|
642
|
+
"claude.md",
|
|
643
|
+
".cursorrules",
|
|
644
|
+
".github/copilot-instructions.md",
|
|
645
|
+
"AGENTS.md",
|
|
646
|
+
"AI_INSTRUCTIONS.md",
|
|
647
|
+
"AI.md"
|
|
648
|
+
];
|
|
620
649
|
var AI_TOOL_PATTERNS = [
|
|
621
650
|
{
|
|
622
651
|
target: "github",
|
|
@@ -648,6 +677,15 @@ async function directoryHasContent(dir, services) {
|
|
|
648
677
|
return false;
|
|
649
678
|
}
|
|
650
679
|
}
|
|
680
|
+
async function isPromptScriptGenerated(filePath, services) {
|
|
681
|
+
try {
|
|
682
|
+
const content = await services.fs.readFile(filePath, "utf-8");
|
|
683
|
+
const header = content.slice(0, 500);
|
|
684
|
+
return header.includes(PROMPTSCRIPT_MARKER);
|
|
685
|
+
} catch {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
651
689
|
async function detectAITools(services = createDefaultServices()) {
|
|
652
690
|
const detected = [];
|
|
653
691
|
const details = {
|
|
@@ -656,6 +694,7 @@ async function detectAITools(services = createDefaultServices()) {
|
|
|
656
694
|
cursor: [],
|
|
657
695
|
antigravity: []
|
|
658
696
|
};
|
|
697
|
+
const migrationCandidates = [];
|
|
659
698
|
for (const pattern of AI_TOOL_PATTERNS) {
|
|
660
699
|
const foundFiles = [];
|
|
661
700
|
for (const file of pattern.files) {
|
|
@@ -673,7 +712,15 @@ async function detectAITools(services = createDefaultServices()) {
|
|
|
673
712
|
details[pattern.target] = foundFiles;
|
|
674
713
|
}
|
|
675
714
|
}
|
|
676
|
-
|
|
715
|
+
for (const file of INSTRUCTION_FILES) {
|
|
716
|
+
if (services.fs.existsSync(file)) {
|
|
717
|
+
const isGenerated = await isPromptScriptGenerated(file, services);
|
|
718
|
+
if (!isGenerated) {
|
|
719
|
+
migrationCandidates.push(file);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return { detected, details, migrationCandidates };
|
|
677
724
|
}
|
|
678
725
|
function getAllTargets() {
|
|
679
726
|
return ["github", "claude", "cursor", "antigravity"];
|
|
@@ -694,6 +741,25 @@ function formatDetectionResults(detection) {
|
|
|
694
741
|
}
|
|
695
742
|
return lines;
|
|
696
743
|
}
|
|
744
|
+
function hasMigrationCandidates(detection) {
|
|
745
|
+
return detection.migrationCandidates.length > 0;
|
|
746
|
+
}
|
|
747
|
+
function formatMigrationHint(detection) {
|
|
748
|
+
if (detection.migrationCandidates.length === 0) {
|
|
749
|
+
return [];
|
|
750
|
+
}
|
|
751
|
+
const lines = [];
|
|
752
|
+
lines.push("");
|
|
753
|
+
lines.push("\u{1F4CB} Existing instruction files detected:");
|
|
754
|
+
for (const file of detection.migrationCandidates) {
|
|
755
|
+
lines.push(` \u2022 ${file}`);
|
|
756
|
+
}
|
|
757
|
+
lines.push("");
|
|
758
|
+
lines.push(" These can be migrated to PromptScript for unified management.");
|
|
759
|
+
lines.push(" See: https://getpromptscript.dev/latest/guides/ai-migration-best-practices");
|
|
760
|
+
lines.push(" Or use the /migrate skill in Claude Code.");
|
|
761
|
+
return lines;
|
|
762
|
+
}
|
|
697
763
|
|
|
698
764
|
// packages/cli/src/prettier/loader.ts
|
|
699
765
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -800,6 +866,506 @@ async function resolvePrettierOptions(config, basePath = process.cwd()) {
|
|
|
800
866
|
return result;
|
|
801
867
|
}
|
|
802
868
|
|
|
869
|
+
// packages/cli/src/templates/migrate-skill.ts
|
|
870
|
+
var MIGRATE_SKILL_CLAUDE = `---
|
|
871
|
+
name: 'migrate-to-promptscript'
|
|
872
|
+
description: 'Migrate existing AI instruction files to PromptScript format'
|
|
873
|
+
allowed-tools:
|
|
874
|
+
- Read
|
|
875
|
+
- Write
|
|
876
|
+
- Glob
|
|
877
|
+
- Grep
|
|
878
|
+
- Bash
|
|
879
|
+
user-invocable: true
|
|
880
|
+
---
|
|
881
|
+
|
|
882
|
+
# Migrate to PromptScript
|
|
883
|
+
|
|
884
|
+
## Overview
|
|
885
|
+
|
|
886
|
+
This skill guides you through migrating existing AI instruction files
|
|
887
|
+
to PromptScript format, creating a unified source of truth for all
|
|
888
|
+
AI coding assistants.
|
|
889
|
+
|
|
890
|
+
## Step 1: Discovery
|
|
891
|
+
|
|
892
|
+
Search for existing instruction files using these patterns:
|
|
893
|
+
|
|
894
|
+
Claude Code:
|
|
895
|
+
|
|
896
|
+
- CLAUDE.md, claude.md, CLAUDE.local.md
|
|
897
|
+
|
|
898
|
+
Cursor:
|
|
899
|
+
|
|
900
|
+
- .cursorrules
|
|
901
|
+
- .cursor/rules/\\*.md
|
|
902
|
+
- .cursor/rules/\\*.mdc
|
|
903
|
+
|
|
904
|
+
GitHub Copilot:
|
|
905
|
+
|
|
906
|
+
- .github/copilot-instructions.md
|
|
907
|
+
- .github/instructions/\\*.md
|
|
908
|
+
|
|
909
|
+
Other:
|
|
910
|
+
|
|
911
|
+
- AGENTS.md
|
|
912
|
+
- AI_INSTRUCTIONS.md
|
|
913
|
+
- AI.md
|
|
914
|
+
- .ai/instructions.md
|
|
915
|
+
|
|
916
|
+
Use Glob tool to find these files.
|
|
917
|
+
|
|
918
|
+
## Step 2: Read and Analyze
|
|
919
|
+
|
|
920
|
+
For each discovered file:
|
|
921
|
+
|
|
922
|
+
1. Read the full content using the Read tool
|
|
923
|
+
2. Identify sections by headers (##, ###) and patterns
|
|
924
|
+
3. Classify content type using the mapping table below
|
|
925
|
+
4. Note any tool-specific content that may need special handling
|
|
926
|
+
|
|
927
|
+
## Step 3: Content Mapping
|
|
928
|
+
|
|
929
|
+
Map source content to PromptScript blocks:
|
|
930
|
+
|
|
931
|
+
| Source Pattern | PromptScript Block |
|
|
932
|
+
| ------------------------------------ | ------------------ |
|
|
933
|
+
| You are, persona, identity, role | @identity |
|
|
934
|
+
| Tech stack, languages, frameworks | @context |
|
|
935
|
+
| Coding standards, conventions, rules | @standards |
|
|
936
|
+
| Don't, Never, restrictions | @restrictions |
|
|
937
|
+
| Commands, shortcuts | @shortcuts |
|
|
938
|
+
| API docs, references, knowledge base | @knowledge |
|
|
939
|
+
| Parameters, config values | @params |
|
|
940
|
+
| File patterns, globs, applyTo | @guards |
|
|
941
|
+
| Skills, capabilities | @skills |
|
|
942
|
+
| Agents, subagents | @agents |
|
|
943
|
+
| Local-only settings | @local |
|
|
944
|
+
|
|
945
|
+
## Step 4: Generate PromptScript
|
|
946
|
+
|
|
947
|
+
### Required: @meta block
|
|
948
|
+
|
|
949
|
+
Every PromptScript file needs metadata with id and syntax fields.
|
|
950
|
+
|
|
951
|
+
### Identity (persona)
|
|
952
|
+
|
|
953
|
+
Convert persona descriptions to @identity block with triple-quote string.
|
|
954
|
+
|
|
955
|
+
### Context (project info)
|
|
956
|
+
|
|
957
|
+
Convert tech stack to @context block with structured properties
|
|
958
|
+
like project, languages, frameworks.
|
|
959
|
+
|
|
960
|
+
### Standards (conventions)
|
|
961
|
+
|
|
962
|
+
Convert coding standards to @standards block organized by category:
|
|
963
|
+
code, naming, commits, etc.
|
|
964
|
+
|
|
965
|
+
### Restrictions (don'ts)
|
|
966
|
+
|
|
967
|
+
Convert restrictions to @restrictions block using dash prefix for each item.
|
|
968
|
+
|
|
969
|
+
### Shortcuts (commands)
|
|
970
|
+
|
|
971
|
+
Convert custom commands to @shortcuts block. Simple shortcuts use
|
|
972
|
+
key-value format. Complex shortcuts use object format with
|
|
973
|
+
prompt, description, and content fields.
|
|
974
|
+
|
|
975
|
+
### Knowledge (references)
|
|
976
|
+
|
|
977
|
+
Convert API docs and reference material to @knowledge block
|
|
978
|
+
using triple-quote string for rich content.
|
|
979
|
+
|
|
980
|
+
### Guards (file patterns)
|
|
981
|
+
|
|
982
|
+
Convert file-specific rules to @guards block with globs array
|
|
983
|
+
specifying file patterns.
|
|
984
|
+
|
|
985
|
+
### Params (configuration)
|
|
986
|
+
|
|
987
|
+
Convert configuration parameters to @params block with type annotations:
|
|
988
|
+
range(), enum(), boolean.
|
|
989
|
+
|
|
990
|
+
### Skills (capabilities)
|
|
991
|
+
|
|
992
|
+
Convert skill definitions to @skills block with description,
|
|
993
|
+
trigger, and content fields.
|
|
994
|
+
|
|
995
|
+
### Agents (subagents)
|
|
996
|
+
|
|
997
|
+
Convert agent definitions to @agents block with description,
|
|
998
|
+
tools, model, and content fields.
|
|
999
|
+
|
|
1000
|
+
## Step 5: File Organization
|
|
1001
|
+
|
|
1002
|
+
Simple Projects - single file structure:
|
|
1003
|
+
|
|
1004
|
+
- .promptscript/project.prs
|
|
1005
|
+
- promptscript.yaml
|
|
1006
|
+
|
|
1007
|
+
Complex Projects - modular file structure:
|
|
1008
|
+
|
|
1009
|
+
- .promptscript/project.prs (main with @use imports)
|
|
1010
|
+
- .promptscript/context.prs
|
|
1011
|
+
- .promptscript/standards.prs
|
|
1012
|
+
- .promptscript/restrictions.prs
|
|
1013
|
+
- .promptscript/commands.prs
|
|
1014
|
+
- promptscript.yaml
|
|
1015
|
+
|
|
1016
|
+
## Step 6: Configuration
|
|
1017
|
+
|
|
1018
|
+
Create promptscript.yaml with:
|
|
1019
|
+
|
|
1020
|
+
- version: '1'
|
|
1021
|
+
- project.id
|
|
1022
|
+
- input.entry pointing to main .prs file
|
|
1023
|
+
- targets for github, claude, cursor
|
|
1024
|
+
|
|
1025
|
+
## Step 7: Validation
|
|
1026
|
+
|
|
1027
|
+
After generating PromptScript files:
|
|
1028
|
+
|
|
1029
|
+
1. Validate syntax: prs validate
|
|
1030
|
+
2. Test compilation: prs compile --dry-run
|
|
1031
|
+
3. Compare output with original files
|
|
1032
|
+
4. Iterate if content is missing or incorrect
|
|
1033
|
+
|
|
1034
|
+
## Common Patterns
|
|
1035
|
+
|
|
1036
|
+
### Merging Multiple Sources
|
|
1037
|
+
|
|
1038
|
+
When instructions exist in multiple files:
|
|
1039
|
+
|
|
1040
|
+
1. Identity: Take from most detailed source
|
|
1041
|
+
2. Standards: Merge all, deduplicate
|
|
1042
|
+
3. Restrictions: Combine all (union)
|
|
1043
|
+
4. Commands: Merge, resolve conflicts
|
|
1044
|
+
|
|
1045
|
+
### Tool-Specific Content
|
|
1046
|
+
|
|
1047
|
+
Handle tool-specific content:
|
|
1048
|
+
|
|
1049
|
+
- GitHub prompts: Use @shortcuts with prompt: true
|
|
1050
|
+
- Claude agents: Use @agents block
|
|
1051
|
+
- Cursor rules: Map to @standards
|
|
1052
|
+
- Local content: Use @local block
|
|
1053
|
+
|
|
1054
|
+
### Preserving Formatting
|
|
1055
|
+
|
|
1056
|
+
Use triple-quote multiline strings for:
|
|
1057
|
+
|
|
1058
|
+
- Rich markdown content
|
|
1059
|
+
- Code examples
|
|
1060
|
+
- Complex instructions
|
|
1061
|
+
|
|
1062
|
+
## Syntax Rules
|
|
1063
|
+
|
|
1064
|
+
Quick reference for PromptScript syntax:
|
|
1065
|
+
|
|
1066
|
+
- Strings: quoted or identifier
|
|
1067
|
+
- Multi-line: triple quotes
|
|
1068
|
+
- Arrays: [item1, item2] or - item prefix
|
|
1069
|
+
- Objects: { key: value }
|
|
1070
|
+
- Comments: # comment
|
|
1071
|
+
- Required @meta fields: id, syntax
|
|
1072
|
+
|
|
1073
|
+
## Quality Checklist
|
|
1074
|
+
|
|
1075
|
+
Before completing migration:
|
|
1076
|
+
|
|
1077
|
+
- @meta block has id and syntax
|
|
1078
|
+
- Identity is clear and specific
|
|
1079
|
+
- Standards are organized by category
|
|
1080
|
+
- Restrictions use dash prefix (-)
|
|
1081
|
+
- Shortcuts work in target tools
|
|
1082
|
+
- prs validate passes
|
|
1083
|
+
- prs compile produces correct output
|
|
1084
|
+
- No duplicate content across blocks
|
|
1085
|
+
|
|
1086
|
+
## Troubleshooting
|
|
1087
|
+
|
|
1088
|
+
### Missing @meta Error
|
|
1089
|
+
Add required metadata block at the start.
|
|
1090
|
+
|
|
1091
|
+
### Multiline String in Object Error
|
|
1092
|
+
Assign multiline strings to named keys, don't leave them loose
|
|
1093
|
+
inside objects.
|
|
1094
|
+
|
|
1095
|
+
### Content Not Appearing in Output
|
|
1096
|
+
Check block names match expected patterns and
|
|
1097
|
+
verify syntax with prs validate --verbose.
|
|
1098
|
+
`;
|
|
1099
|
+
var MIGRATE_SKILL_CURSOR = `# Migrate to PromptScript
|
|
1100
|
+
|
|
1101
|
+
Use this command to migrate existing AI instruction files to PromptScript format.
|
|
1102
|
+
|
|
1103
|
+
## Step 1: Discovery
|
|
1104
|
+
|
|
1105
|
+
Search for existing instruction files:
|
|
1106
|
+
|
|
1107
|
+
- CLAUDE.md, claude.md, CLAUDE.local.md
|
|
1108
|
+
- .cursorrules, .cursor/rules/*.mdc
|
|
1109
|
+
- .github/copilot-instructions.md
|
|
1110
|
+
- AGENTS.md, AI_INSTRUCTIONS.md
|
|
1111
|
+
|
|
1112
|
+
## Step 2: Content Mapping
|
|
1113
|
+
|
|
1114
|
+
Map source content to PromptScript blocks:
|
|
1115
|
+
|
|
1116
|
+
| Source Pattern | PromptScript Block |
|
|
1117
|
+
| ------------------------------------ | ------------------ |
|
|
1118
|
+
| You are, persona, identity, role | @identity |
|
|
1119
|
+
| Tech stack, languages, frameworks | @context |
|
|
1120
|
+
| Coding standards, conventions, rules | @standards |
|
|
1121
|
+
| Don't, Never, restrictions | @restrictions |
|
|
1122
|
+
| Commands, shortcuts | @shortcuts |
|
|
1123
|
+
| API docs, references | @knowledge |
|
|
1124
|
+
|
|
1125
|
+
## Step 3: Generate PromptScript
|
|
1126
|
+
|
|
1127
|
+
Create \`.promptscript/project.prs\` with:
|
|
1128
|
+
|
|
1129
|
+
1. **@meta** block with id and syntax fields (required)
|
|
1130
|
+
2. **@identity** for persona/role descriptions
|
|
1131
|
+
3. **@context** for tech stack info
|
|
1132
|
+
4. **@standards** organized by category
|
|
1133
|
+
5. **@restrictions** with dash prefix for each item
|
|
1134
|
+
6. **@shortcuts** for commands
|
|
1135
|
+
|
|
1136
|
+
## Step 4: Validation
|
|
1137
|
+
|
|
1138
|
+
After generating:
|
|
1139
|
+
|
|
1140
|
+
1. Run \`prs validate\`
|
|
1141
|
+
2. Run \`prs compile --dry-run\`
|
|
1142
|
+
3. Compare output with original files
|
|
1143
|
+
|
|
1144
|
+
## Syntax Quick Reference
|
|
1145
|
+
|
|
1146
|
+
- Strings: quoted or identifier
|
|
1147
|
+
- Multi-line: triple quotes (\`"""\`)
|
|
1148
|
+
- Arrays: \`[item1, item2]\`
|
|
1149
|
+
- Objects: \`{ key: value }\`
|
|
1150
|
+
- Comments: \`# comment\`
|
|
1151
|
+
|
|
1152
|
+
## Quality Checklist
|
|
1153
|
+
|
|
1154
|
+
- @meta block has id and syntax
|
|
1155
|
+
- Standards organized by category
|
|
1156
|
+
- Restrictions use dash prefix (-)
|
|
1157
|
+
- prs validate passes
|
|
1158
|
+
`;
|
|
1159
|
+
var MIGRATE_SKILL_ANTIGRAVITY = `# Migrate to PromptScript
|
|
1160
|
+
|
|
1161
|
+
This rule provides guidance for migrating existing AI instruction files to PromptScript format.
|
|
1162
|
+
|
|
1163
|
+
## Overview
|
|
1164
|
+
|
|
1165
|
+
PromptScript creates a unified source of truth for all AI coding assistants,
|
|
1166
|
+
compiling to native formats for Claude, GitHub Copilot, Cursor, and Antigravity.
|
|
1167
|
+
|
|
1168
|
+
## Step 1: Discovery
|
|
1169
|
+
|
|
1170
|
+
Search for existing instruction files:
|
|
1171
|
+
|
|
1172
|
+
- CLAUDE.md, claude.md, CLAUDE.local.md
|
|
1173
|
+
- .cursorrules, .cursor/rules/*.mdc
|
|
1174
|
+
- .github/copilot-instructions.md
|
|
1175
|
+
- .agent/rules/*.md
|
|
1176
|
+
- AGENTS.md, AI_INSTRUCTIONS.md
|
|
1177
|
+
|
|
1178
|
+
## Step 2: Content Mapping
|
|
1179
|
+
|
|
1180
|
+
Map source content to PromptScript blocks:
|
|
1181
|
+
|
|
1182
|
+
| Source Pattern | PromptScript Block |
|
|
1183
|
+
| ------------------------------------ | ------------------ |
|
|
1184
|
+
| You are, persona, identity, role | @identity |
|
|
1185
|
+
| Tech stack, languages, frameworks | @context |
|
|
1186
|
+
| Coding standards, conventions, rules | @standards |
|
|
1187
|
+
| Don't, Never, restrictions | @restrictions |
|
|
1188
|
+
| Commands, shortcuts | @shortcuts |
|
|
1189
|
+
| API docs, references | @knowledge |
|
|
1190
|
+
|
|
1191
|
+
## Step 3: Generate PromptScript
|
|
1192
|
+
|
|
1193
|
+
Create \`.promptscript/project.prs\` with required blocks:
|
|
1194
|
+
|
|
1195
|
+
1. **@meta** - id and syntax fields (required)
|
|
1196
|
+
2. **@identity** - persona/role descriptions
|
|
1197
|
+
3. **@context** - tech stack info
|
|
1198
|
+
4. **@standards** - coding rules organized by category
|
|
1199
|
+
5. **@restrictions** - things to avoid (dash prefix)
|
|
1200
|
+
6. **@shortcuts** - commands
|
|
1201
|
+
|
|
1202
|
+
## Step 4: Validation
|
|
1203
|
+
|
|
1204
|
+
After generating PromptScript files:
|
|
1205
|
+
|
|
1206
|
+
1. Run \`prs validate\` to check syntax
|
|
1207
|
+
2. Run \`prs compile --dry-run\` to preview output
|
|
1208
|
+
3. Compare compiled output with original files
|
|
1209
|
+
|
|
1210
|
+
## Syntax Reference
|
|
1211
|
+
|
|
1212
|
+
- Strings: quoted or identifier
|
|
1213
|
+
- Multi-line: triple quotes
|
|
1214
|
+
- Arrays: [item1, item2]
|
|
1215
|
+
- Objects: { key: value }
|
|
1216
|
+
- Comments: # comment
|
|
1217
|
+
`;
|
|
1218
|
+
var MIGRATE_SKILL_GITHUB = `---
|
|
1219
|
+
name: 'migrate-to-promptscript'
|
|
1220
|
+
description: 'Migrate existing AI instruction files to PromptScript format'
|
|
1221
|
+
allowed-tools:
|
|
1222
|
+
- read
|
|
1223
|
+
- write
|
|
1224
|
+
- glob
|
|
1225
|
+
- grep
|
|
1226
|
+
- execute
|
|
1227
|
+
user-invocable: true
|
|
1228
|
+
---
|
|
1229
|
+
|
|
1230
|
+
# Migrate to PromptScript
|
|
1231
|
+
|
|
1232
|
+
## Overview
|
|
1233
|
+
|
|
1234
|
+
This skill guides you through migrating existing AI instruction files
|
|
1235
|
+
to PromptScript format, creating a unified source of truth for all
|
|
1236
|
+
AI coding assistants.
|
|
1237
|
+
|
|
1238
|
+
## Step 1: Discovery
|
|
1239
|
+
|
|
1240
|
+
Search for existing instruction files using these patterns:
|
|
1241
|
+
|
|
1242
|
+
Claude Code:
|
|
1243
|
+
|
|
1244
|
+
- CLAUDE.md, claude.md, CLAUDE.local.md
|
|
1245
|
+
|
|
1246
|
+
Cursor:
|
|
1247
|
+
|
|
1248
|
+
- .cursorrules
|
|
1249
|
+
- .cursor/rules/\\*.md
|
|
1250
|
+
- .cursor/rules/\\*.mdc
|
|
1251
|
+
|
|
1252
|
+
GitHub Copilot:
|
|
1253
|
+
|
|
1254
|
+
- .github/copilot-instructions.md
|
|
1255
|
+
- .github/instructions/\\*.md
|
|
1256
|
+
|
|
1257
|
+
Other:
|
|
1258
|
+
|
|
1259
|
+
- AGENTS.md
|
|
1260
|
+
- AI_INSTRUCTIONS.md
|
|
1261
|
+
- AI.md
|
|
1262
|
+
- .ai/instructions.md
|
|
1263
|
+
|
|
1264
|
+
## Step 2: Read and Analyze
|
|
1265
|
+
|
|
1266
|
+
For each discovered file:
|
|
1267
|
+
|
|
1268
|
+
1. Read the full content
|
|
1269
|
+
2. Identify sections by headers (##, ###) and patterns
|
|
1270
|
+
3. Classify content type using the mapping table below
|
|
1271
|
+
4. Note any tool-specific content that may need special handling
|
|
1272
|
+
|
|
1273
|
+
## Step 3: Content Mapping
|
|
1274
|
+
|
|
1275
|
+
Map source content to PromptScript blocks:
|
|
1276
|
+
|
|
1277
|
+
| Source Pattern | PromptScript Block |
|
|
1278
|
+
| ------------------------------------ | ------------------ |
|
|
1279
|
+
| You are, persona, identity, role | @identity |
|
|
1280
|
+
| Tech stack, languages, frameworks | @context |
|
|
1281
|
+
| Coding standards, conventions, rules | @standards |
|
|
1282
|
+
| Don't, Never, restrictions | @restrictions |
|
|
1283
|
+
| Commands, shortcuts | @shortcuts |
|
|
1284
|
+
| API docs, references, knowledge base | @knowledge |
|
|
1285
|
+
| Parameters, config values | @params |
|
|
1286
|
+
| File patterns, globs, applyTo | @guards |
|
|
1287
|
+
| Skills, capabilities | @skills |
|
|
1288
|
+
| Agents, subagents | @agents |
|
|
1289
|
+
| Local-only settings | @local |
|
|
1290
|
+
|
|
1291
|
+
## Step 4: Generate PromptScript
|
|
1292
|
+
|
|
1293
|
+
### Required: @meta block
|
|
1294
|
+
|
|
1295
|
+
Every PromptScript file needs metadata with id and syntax fields.
|
|
1296
|
+
|
|
1297
|
+
### Identity (persona)
|
|
1298
|
+
|
|
1299
|
+
Convert persona descriptions to @identity block with triple-quote string.
|
|
1300
|
+
|
|
1301
|
+
### Context (project info)
|
|
1302
|
+
|
|
1303
|
+
Convert tech stack to @context block with structured properties
|
|
1304
|
+
like project, languages, frameworks.
|
|
1305
|
+
|
|
1306
|
+
### Standards (conventions)
|
|
1307
|
+
|
|
1308
|
+
Convert coding standards to @standards block organized by category:
|
|
1309
|
+
code, naming, commits, etc.
|
|
1310
|
+
|
|
1311
|
+
### Restrictions (don'ts)
|
|
1312
|
+
|
|
1313
|
+
Convert restrictions to @restrictions block using dash prefix for each item.
|
|
1314
|
+
|
|
1315
|
+
### Shortcuts (commands)
|
|
1316
|
+
|
|
1317
|
+
Convert custom commands to @shortcuts block. Simple shortcuts use
|
|
1318
|
+
key-value format. Complex shortcuts use object format with
|
|
1319
|
+
prompt, description, and content fields.
|
|
1320
|
+
|
|
1321
|
+
## Step 5: File Organization
|
|
1322
|
+
|
|
1323
|
+
Simple Projects - single file structure:
|
|
1324
|
+
|
|
1325
|
+
- .promptscript/project.prs
|
|
1326
|
+
- promptscript.yaml
|
|
1327
|
+
|
|
1328
|
+
Complex Projects - modular file structure:
|
|
1329
|
+
|
|
1330
|
+
- .promptscript/project.prs (main with @use imports)
|
|
1331
|
+
- .promptscript/context.prs
|
|
1332
|
+
- .promptscript/standards.prs
|
|
1333
|
+
- .promptscript/restrictions.prs
|
|
1334
|
+
- .promptscript/commands.prs
|
|
1335
|
+
- promptscript.yaml
|
|
1336
|
+
|
|
1337
|
+
## Step 6: Configuration
|
|
1338
|
+
|
|
1339
|
+
Create promptscript.yaml with:
|
|
1340
|
+
|
|
1341
|
+
- version: '1'
|
|
1342
|
+
- project.id
|
|
1343
|
+
- input.entry pointing to main .prs file
|
|
1344
|
+
- targets for github, claude, cursor
|
|
1345
|
+
|
|
1346
|
+
## Step 7: Validation
|
|
1347
|
+
|
|
1348
|
+
After generating PromptScript files:
|
|
1349
|
+
|
|
1350
|
+
1. Validate syntax: prs validate
|
|
1351
|
+
2. Test compilation: prs compile --dry-run
|
|
1352
|
+
3. Compare output with original files
|
|
1353
|
+
4. Iterate if content is missing or incorrect
|
|
1354
|
+
|
|
1355
|
+
## Quality Checklist
|
|
1356
|
+
|
|
1357
|
+
Before completing migration:
|
|
1358
|
+
|
|
1359
|
+
- @meta block has id and syntax
|
|
1360
|
+
- Identity is clear and specific
|
|
1361
|
+
- Standards are organized by category
|
|
1362
|
+
- Restrictions use dash prefix (-)
|
|
1363
|
+
- Shortcuts work in target tools
|
|
1364
|
+
- prs validate passes
|
|
1365
|
+
- prs compile produces correct output
|
|
1366
|
+
- No duplicate content across blocks
|
|
1367
|
+
`;
|
|
1368
|
+
|
|
803
1369
|
// packages/cli/src/commands/init.ts
|
|
804
1370
|
var __filename = fileURLToPath(import.meta.url);
|
|
805
1371
|
var __dirname = dirname2(__filename);
|
|
@@ -827,11 +1393,45 @@ async function initCommand(options, services = createDefaultServices()) {
|
|
|
827
1393
|
await fs4.writeFile("promptscript.yaml", configContent, "utf-8");
|
|
828
1394
|
const projectPsContent = generateProjectPs(config, projectInfo);
|
|
829
1395
|
await fs4.writeFile(".promptscript/project.prs", projectPsContent, "utf-8");
|
|
1396
|
+
const installedSkillPaths = [];
|
|
1397
|
+
if (options.migrate) {
|
|
1398
|
+
if (config.targets.includes("claude")) {
|
|
1399
|
+
await fs4.mkdir(".claude/skills/migrate-to-promptscript", { recursive: true });
|
|
1400
|
+
await fs4.writeFile(
|
|
1401
|
+
".claude/skills/migrate-to-promptscript/SKILL.md",
|
|
1402
|
+
MIGRATE_SKILL_CLAUDE,
|
|
1403
|
+
"utf-8"
|
|
1404
|
+
);
|
|
1405
|
+
installedSkillPaths.push(".claude/skills/migrate-to-promptscript/SKILL.md");
|
|
1406
|
+
}
|
|
1407
|
+
if (config.targets.includes("github")) {
|
|
1408
|
+
await fs4.mkdir(".github/skills/migrate-to-promptscript", { recursive: true });
|
|
1409
|
+
await fs4.writeFile(
|
|
1410
|
+
".github/skills/migrate-to-promptscript/SKILL.md",
|
|
1411
|
+
MIGRATE_SKILL_GITHUB,
|
|
1412
|
+
"utf-8"
|
|
1413
|
+
);
|
|
1414
|
+
installedSkillPaths.push(".github/skills/migrate-to-promptscript/SKILL.md");
|
|
1415
|
+
}
|
|
1416
|
+
if (config.targets.includes("cursor")) {
|
|
1417
|
+
await fs4.mkdir(".cursor/commands", { recursive: true });
|
|
1418
|
+
await fs4.writeFile(".cursor/commands/migrate.md", MIGRATE_SKILL_CURSOR, "utf-8");
|
|
1419
|
+
installedSkillPaths.push(".cursor/commands/migrate.md");
|
|
1420
|
+
}
|
|
1421
|
+
if (config.targets.includes("antigravity")) {
|
|
1422
|
+
await fs4.mkdir(".agent/rules", { recursive: true });
|
|
1423
|
+
await fs4.writeFile(".agent/rules/migrate.md", MIGRATE_SKILL_ANTIGRAVITY, "utf-8");
|
|
1424
|
+
installedSkillPaths.push(".agent/rules/migrate.md");
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
830
1427
|
spinner.succeed("PromptScript initialized");
|
|
831
1428
|
ConsoleOutput.newline();
|
|
832
1429
|
console.log("Created:");
|
|
833
1430
|
ConsoleOutput.success("promptscript.yaml");
|
|
834
1431
|
ConsoleOutput.success(".promptscript/project.prs");
|
|
1432
|
+
for (const skillPath of installedSkillPaths) {
|
|
1433
|
+
ConsoleOutput.success(skillPath);
|
|
1434
|
+
}
|
|
835
1435
|
ConsoleOutput.newline();
|
|
836
1436
|
console.log("Configuration:");
|
|
837
1437
|
ConsoleOutput.muted(` Project: ${config.projectId}`);
|
|
@@ -852,8 +1452,38 @@ async function initCommand(options, services = createDefaultServices()) {
|
|
|
852
1452
|
}
|
|
853
1453
|
ConsoleOutput.newline();
|
|
854
1454
|
console.log("Next steps:");
|
|
855
|
-
|
|
856
|
-
|
|
1455
|
+
if (options.migrate && installedSkillPaths.length > 0) {
|
|
1456
|
+
ConsoleOutput.muted("1. Use the migration skill to convert existing instructions:");
|
|
1457
|
+
if (config.targets.includes("claude")) {
|
|
1458
|
+
ConsoleOutput.muted(" Claude Code: /migrate");
|
|
1459
|
+
}
|
|
1460
|
+
if (config.targets.includes("github")) {
|
|
1461
|
+
ConsoleOutput.muted(" GitHub Copilot: @workspace /migrate");
|
|
1462
|
+
}
|
|
1463
|
+
if (config.targets.includes("cursor")) {
|
|
1464
|
+
ConsoleOutput.muted(" Cursor: /migrate");
|
|
1465
|
+
}
|
|
1466
|
+
if (config.targets.includes("antigravity")) {
|
|
1467
|
+
ConsoleOutput.muted(' Antigravity: Ask to "migrate to PromptScript"');
|
|
1468
|
+
}
|
|
1469
|
+
ConsoleOutput.muted("2. Review generated .promptscript/project.prs");
|
|
1470
|
+
ConsoleOutput.muted("3. Run: prs compile");
|
|
1471
|
+
} else {
|
|
1472
|
+
ConsoleOutput.muted("1. Edit .promptscript/project.prs to customize your AI instructions");
|
|
1473
|
+
ConsoleOutput.muted("2. Run: prs compile");
|
|
1474
|
+
if (hasMigrationCandidates(aiToolsDetection)) {
|
|
1475
|
+
const migrationHint = formatMigrationHint(aiToolsDetection);
|
|
1476
|
+
for (const line of migrationHint) {
|
|
1477
|
+
if (line.startsWith("\u{1F4CB}") || line.includes("migrated") || line.includes("See:") || line.includes("Or use")) {
|
|
1478
|
+
ConsoleOutput.info(line.replace(/^\s+/, ""));
|
|
1479
|
+
} else if (line.trim().startsWith("\u2022")) {
|
|
1480
|
+
ConsoleOutput.muted(line);
|
|
1481
|
+
} else if (line.trim()) {
|
|
1482
|
+
console.log(line);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
857
1487
|
} catch (error) {
|
|
858
1488
|
if (error.name === "ExitPromptError") {
|
|
859
1489
|
ConsoleOutput.newline();
|
|
@@ -1294,10 +1924,26 @@ ${this.convention.rootWrapper.end}`;
|
|
|
1294
1924
|
/**
|
|
1295
1925
|
* Escape markdown special characters for Prettier compatibility.
|
|
1296
1926
|
* - Escapes __ to \_\_ (to avoid emphasis)
|
|
1297
|
-
* - Escapes
|
|
1927
|
+
* - Escapes * in glob patterns (like packages/*) outside backticks
|
|
1298
1928
|
*/
|
|
1299
1929
|
escapeMarkdownSpecialChars(content) {
|
|
1300
|
-
return content.
|
|
1930
|
+
return content.split("\n").map((line) => {
|
|
1931
|
+
let result = line.replace(/__/g, "\\_\\_");
|
|
1932
|
+
result = this.escapeGlobAsteriskOutsideBackticks(result);
|
|
1933
|
+
return result;
|
|
1934
|
+
}).join("\n");
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Escape glob asterisks (like packages/* or .cursor/rules/*.md) outside of backticks.
|
|
1938
|
+
*/
|
|
1939
|
+
escapeGlobAsteriskOutsideBackticks(line) {
|
|
1940
|
+
const parts = line.split("`");
|
|
1941
|
+
return parts.map((part, index) => {
|
|
1942
|
+
if (index % 2 === 0) {
|
|
1943
|
+
return part.replace(/\/\*/g, "/\\*");
|
|
1944
|
+
}
|
|
1945
|
+
return part;
|
|
1946
|
+
}).join("`");
|
|
1301
1947
|
}
|
|
1302
1948
|
indentContent(content, renderer, level) {
|
|
1303
1949
|
const indent = renderer.indent ?? "";
|
|
@@ -1469,6 +2115,7 @@ var BaseFormatter = class {
|
|
|
1469
2115
|
* - Trims trailing whitespace from lines
|
|
1470
2116
|
* - Normalizes markdown table formatting
|
|
1471
2117
|
* - Adds blank lines before lists when preceded by text
|
|
2118
|
+
* - Adds blank lines before code blocks when preceded by text
|
|
1472
2119
|
* - Escapes markdown special characters in paths
|
|
1473
2120
|
*/
|
|
1474
2121
|
normalizeMarkdownForPrettier(content) {
|
|
@@ -1477,8 +2124,13 @@ var BaseFormatter = class {
|
|
|
1477
2124
|
let inCodeBlock = false;
|
|
1478
2125
|
for (const line of lines) {
|
|
1479
2126
|
const trimmed = line.trimEnd();
|
|
1480
|
-
if (trimmed.startsWith("```")) {
|
|
2127
|
+
if (trimmed.trimStart().startsWith("```")) {
|
|
1481
2128
|
inCodeBlock = !inCodeBlock;
|
|
2129
|
+
if (trimmed.length > 0) {
|
|
2130
|
+
const match2 = line.match(/^(\s*)/);
|
|
2131
|
+
const leadingSpaces2 = match2?.[1]?.length ?? 0;
|
|
2132
|
+
minIndent = Math.min(minIndent, leadingSpaces2);
|
|
2133
|
+
}
|
|
1482
2134
|
continue;
|
|
1483
2135
|
}
|
|
1484
2136
|
if (inCodeBlock) continue;
|
|
@@ -1494,17 +2146,30 @@ var BaseFormatter = class {
|
|
|
1494
2146
|
inCodeBlock = false;
|
|
1495
2147
|
for (const line of lines) {
|
|
1496
2148
|
const trimmedLine = line.trimEnd();
|
|
1497
|
-
|
|
2149
|
+
const unindentedLine = minIndent > 0 ? trimmedLine.slice(minIndent) : trimmedLine;
|
|
2150
|
+
const isCodeBlockMarker = trimmedLine.trimStart().startsWith("```");
|
|
2151
|
+
const isCodeBlockStart = isCodeBlockMarker && !inCodeBlock;
|
|
2152
|
+
if (isCodeBlockMarker) {
|
|
1498
2153
|
inCodeBlock = !inCodeBlock;
|
|
1499
2154
|
}
|
|
1500
|
-
const unindentedLine = minIndent > 0 ? trimmedLine.slice(minIndent) : trimmedLine;
|
|
1501
2155
|
if (inCodeBlock || unindentedLine.startsWith("```")) {
|
|
2156
|
+
if (isCodeBlockStart) {
|
|
2157
|
+
const prevLine2 = result.length > 0 ? result[result.length - 1] ?? "" : "";
|
|
2158
|
+
if (prevLine2.trim() && !prevLine2.startsWith("#")) {
|
|
2159
|
+
result.push("");
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
1502
2162
|
result.push(unindentedLine);
|
|
1503
2163
|
continue;
|
|
1504
2164
|
}
|
|
1505
2165
|
let processedLine = unindentedLine;
|
|
1506
2166
|
processedLine = processedLine.replace(/__([^_]+)__/g, "\\_\\_$1\\_\\_");
|
|
1507
|
-
processedLine =
|
|
2167
|
+
processedLine = this.escapeGlobAsteriskOutsideBackticks(processedLine);
|
|
2168
|
+
const prevLine = result.length > 0 ? result[result.length - 1] : "";
|
|
2169
|
+
const isHeader = prevLine?.trimStart().startsWith("#");
|
|
2170
|
+
if (isHeader && processedLine.trim()) {
|
|
2171
|
+
result.push("");
|
|
2172
|
+
}
|
|
1508
2173
|
if (processedLine.trimStart().startsWith("|") && processedLine.trimEnd().endsWith("|")) {
|
|
1509
2174
|
inTable = true;
|
|
1510
2175
|
tableLines.push(processedLine.trim());
|
|
@@ -1514,9 +2179,14 @@ var BaseFormatter = class {
|
|
|
1514
2179
|
tableLines = [];
|
|
1515
2180
|
inTable = false;
|
|
1516
2181
|
}
|
|
1517
|
-
const prevLine = result.length > 0 ? result[result.length - 1] : "";
|
|
1518
2182
|
const isListItem = processedLine.trimStart().startsWith("- ");
|
|
1519
|
-
|
|
2183
|
+
const isNumberedItem = /^\d+\.\s/.test(processedLine.trimStart());
|
|
2184
|
+
const prevLineTrimmed = prevLine?.trimStart() ?? "";
|
|
2185
|
+
const isPrevListItem = prevLineTrimmed.startsWith("- ") || /^\d+\.\s/.test(prevLineTrimmed);
|
|
2186
|
+
const prevEndsWithColon = prevLine?.trimEnd().endsWith(":") ?? false;
|
|
2187
|
+
if ((isListItem || isNumberedItem) && prevLine && !isPrevListItem && !isHeader) {
|
|
2188
|
+
result.push("");
|
|
2189
|
+
} else if ((isListItem || isNumberedItem) && prevEndsWithColon) {
|
|
1520
2190
|
result.push("");
|
|
1521
2191
|
}
|
|
1522
2192
|
result.push(processedLine);
|
|
@@ -1527,6 +2197,19 @@ var BaseFormatter = class {
|
|
|
1527
2197
|
}
|
|
1528
2198
|
return result.join("\n");
|
|
1529
2199
|
}
|
|
2200
|
+
/**
|
|
2201
|
+
* Escape glob asterisks (like packages/* or .cursor/rules/*.md) outside of backticks.
|
|
2202
|
+
* Prettier escapes these to prevent them from being interpreted as emphasis markers.
|
|
2203
|
+
*/
|
|
2204
|
+
escapeGlobAsteriskOutsideBackticks(line) {
|
|
2205
|
+
const parts = line.split("`");
|
|
2206
|
+
return parts.map((part, index) => {
|
|
2207
|
+
if (index % 2 === 0) {
|
|
2208
|
+
return part.replace(/\/\*/g, "/\\*");
|
|
2209
|
+
}
|
|
2210
|
+
return part;
|
|
2211
|
+
}).join("`");
|
|
2212
|
+
}
|
|
1530
2213
|
/**
|
|
1531
2214
|
* Strip all leading indentation from markdown content.
|
|
1532
2215
|
* Used for AGENTS.md where content from multiple sources has inconsistent indentation.
|
|
@@ -1540,7 +2223,7 @@ var BaseFormatter = class {
|
|
|
1540
2223
|
const trimmedEnd = line.trimEnd();
|
|
1541
2224
|
if (trimmedEnd.trimStart().startsWith("```")) {
|
|
1542
2225
|
if (!inCodeBlock) {
|
|
1543
|
-
const prevLine2 = result.length > 0 ? result[result.length - 1] : "";
|
|
2226
|
+
const prevLine2 = result.length > 0 ? result[result.length - 1] ?? "" : "";
|
|
1544
2227
|
if (prevLine2 && prevLine2.trim()) {
|
|
1545
2228
|
result.push("");
|
|
1546
2229
|
}
|
|
@@ -1555,9 +2238,14 @@ var BaseFormatter = class {
|
|
|
1555
2238
|
}
|
|
1556
2239
|
let stripped = trimmedEnd.trimStart();
|
|
1557
2240
|
stripped = stripped.replace(/__/g, "\\_\\_");
|
|
1558
|
-
stripped =
|
|
1559
|
-
const prevLine = result.length > 0 ? result[result.length - 1] : "";
|
|
1560
|
-
|
|
2241
|
+
stripped = this.escapeGlobAsteriskOutsideBackticks(stripped);
|
|
2242
|
+
const prevLine = result.length > 0 ? result[result.length - 1] ?? "" : "";
|
|
2243
|
+
const isListItem = stripped.startsWith("- ") || /^\d+\.\s/.test(stripped);
|
|
2244
|
+
const isPrevList = prevLine.startsWith("- ") || /^\d+\.\s/.test(prevLine);
|
|
2245
|
+
const prevEndsWithColon = prevLine.trimEnd().endsWith(":");
|
|
2246
|
+
if (isListItem && prevLine && !isPrevList) {
|
|
2247
|
+
result.push("");
|
|
2248
|
+
} else if (isListItem && prevEndsWithColon) {
|
|
1561
2249
|
result.push("");
|
|
1562
2250
|
}
|
|
1563
2251
|
result.push(stripped);
|
|
@@ -1898,7 +2586,9 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
1898
2586
|
lines.push(`# ${config.description}`);
|
|
1899
2587
|
lines.push("");
|
|
1900
2588
|
if (config.content) {
|
|
1901
|
-
|
|
2589
|
+
const dedentedContent = this.dedent(config.content);
|
|
2590
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
2591
|
+
lines.push(normalizedContent);
|
|
1902
2592
|
}
|
|
1903
2593
|
return {
|
|
1904
2594
|
path: `.github/instructions/${config.name}.instructions.md`,
|
|
@@ -1986,7 +2676,8 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
1986
2676
|
generatePromptFile(config) {
|
|
1987
2677
|
const lines = [];
|
|
1988
2678
|
lines.push("---");
|
|
1989
|
-
|
|
2679
|
+
const descQuote = config.description.includes("'") ? '"' : "'";
|
|
2680
|
+
lines.push(`description: ${descQuote}${config.description}${descQuote}`);
|
|
1990
2681
|
if (config.mode === "agent") {
|
|
1991
2682
|
lines.push("mode: agent");
|
|
1992
2683
|
if (config.tools && config.tools.length > 0) {
|
|
@@ -1998,11 +2689,13 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
1998
2689
|
lines.push("---");
|
|
1999
2690
|
lines.push("");
|
|
2000
2691
|
if (config.content) {
|
|
2001
|
-
|
|
2692
|
+
const dedentedContent = this.dedent(config.content);
|
|
2693
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
2694
|
+
lines.push(normalizedContent);
|
|
2002
2695
|
}
|
|
2003
2696
|
return {
|
|
2004
2697
|
path: `.github/prompts/${config.name}.prompt.md`,
|
|
2005
|
-
content: lines.join("\n")
|
|
2698
|
+
content: lines.join("\n") + "\n"
|
|
2006
2699
|
};
|
|
2007
2700
|
}
|
|
2008
2701
|
// ============================================================
|
|
@@ -2044,7 +2737,8 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
2044
2737
|
lines.push("---");
|
|
2045
2738
|
lines.push("");
|
|
2046
2739
|
if (config.content) {
|
|
2047
|
-
const
|
|
2740
|
+
const dedentedContent = this.dedent(config.content);
|
|
2741
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
2048
2742
|
lines.push(normalizedContent);
|
|
2049
2743
|
}
|
|
2050
2744
|
return {
|
|
@@ -2052,6 +2746,28 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
2052
2746
|
content: lines.join("\n") + "\n"
|
|
2053
2747
|
};
|
|
2054
2748
|
}
|
|
2749
|
+
/**
|
|
2750
|
+
* Remove common leading indentation from multiline text.
|
|
2751
|
+
* Calculates minimum indent from lines 2+ only, since line 1 may have been
|
|
2752
|
+
* trimmed (losing its indentation) while subsequent lines retain theirs.
|
|
2753
|
+
*/
|
|
2754
|
+
dedent(text) {
|
|
2755
|
+
const lines = text.split("\n");
|
|
2756
|
+
if (lines.length <= 1) return text.trim();
|
|
2757
|
+
const minIndent = lines.slice(1).filter((line) => line.trim().length > 0).reduce((min, line) => {
|
|
2758
|
+
const match = line.match(/^(\s*)/);
|
|
2759
|
+
const indent = match?.[1]?.length ?? 0;
|
|
2760
|
+
return Math.min(min, indent);
|
|
2761
|
+
}, Infinity);
|
|
2762
|
+
if (minIndent === 0 || minIndent === Infinity) {
|
|
2763
|
+
return text.trim();
|
|
2764
|
+
}
|
|
2765
|
+
const firstLine = lines[0] ?? "";
|
|
2766
|
+
return [
|
|
2767
|
+
firstLine.trim(),
|
|
2768
|
+
...lines.slice(1).map((line) => line.trim().length > 0 ? line.slice(minIndent) : "")
|
|
2769
|
+
].join("\n").trim();
|
|
2770
|
+
}
|
|
2055
2771
|
// ============================================================
|
|
2056
2772
|
// AGENTS.md Generation
|
|
2057
2773
|
// ============================================================
|
|
@@ -2187,7 +2903,9 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
2187
2903
|
lines.push("---");
|
|
2188
2904
|
lines.push("");
|
|
2189
2905
|
if (config.content) {
|
|
2190
|
-
|
|
2906
|
+
const dedentedContent = this.dedent(config.content);
|
|
2907
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
2908
|
+
lines.push(normalizedContent);
|
|
2191
2909
|
}
|
|
2192
2910
|
return {
|
|
2193
2911
|
path: `.github/agents/${config.name}.md`,
|
|
@@ -2642,7 +3360,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2642
3360
|
lines.push(`# ${config.description}`);
|
|
2643
3361
|
lines.push("");
|
|
2644
3362
|
if (config.content) {
|
|
2645
|
-
|
|
3363
|
+
const dedentedContent = this.dedent(config.content);
|
|
3364
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
3365
|
+
lines.push(normalizedContent);
|
|
2646
3366
|
}
|
|
2647
3367
|
return {
|
|
2648
3368
|
path: `.claude/rules/${config.name}.md`,
|
|
@@ -2741,7 +3461,8 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2741
3461
|
lines.push("---");
|
|
2742
3462
|
lines.push("");
|
|
2743
3463
|
if (config.content) {
|
|
2744
|
-
const
|
|
3464
|
+
const dedentedContent = this.dedent(config.content);
|
|
3465
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
2745
3466
|
lines.push(normalizedContent);
|
|
2746
3467
|
}
|
|
2747
3468
|
return {
|
|
@@ -2749,6 +3470,28 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2749
3470
|
content: lines.join("\n") + "\n"
|
|
2750
3471
|
};
|
|
2751
3472
|
}
|
|
3473
|
+
/**
|
|
3474
|
+
* Remove common leading indentation from multiline text.
|
|
3475
|
+
* Calculates minimum indent from lines 2+ only, since line 1 may have been
|
|
3476
|
+
* trimmed (losing its indentation) while subsequent lines retain theirs.
|
|
3477
|
+
*/
|
|
3478
|
+
dedent(text) {
|
|
3479
|
+
const lines = text.split("\n");
|
|
3480
|
+
if (lines.length <= 1) return text.trim();
|
|
3481
|
+
const minIndent = lines.slice(1).filter((line) => line.trim().length > 0).reduce((min, line) => {
|
|
3482
|
+
const match = line.match(/^(\s*)/);
|
|
3483
|
+
const indent = match?.[1]?.length ?? 0;
|
|
3484
|
+
return Math.min(min, indent);
|
|
3485
|
+
}, Infinity);
|
|
3486
|
+
if (minIndent === 0 || minIndent === Infinity) {
|
|
3487
|
+
return text.trim();
|
|
3488
|
+
}
|
|
3489
|
+
const firstLine = lines[0] ?? "";
|
|
3490
|
+
return [
|
|
3491
|
+
firstLine.trim(),
|
|
3492
|
+
...lines.slice(1).map((line) => line.trim().length > 0 ? line.slice(minIndent) : "")
|
|
3493
|
+
].join("\n").trim();
|
|
3494
|
+
}
|
|
2752
3495
|
// ============================================================
|
|
2753
3496
|
// Agent File Generation
|
|
2754
3497
|
// ============================================================
|
|
@@ -2851,7 +3594,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2851
3594
|
lines.push("---");
|
|
2852
3595
|
lines.push("");
|
|
2853
3596
|
if (config.content) {
|
|
2854
|
-
|
|
3597
|
+
const dedentedContent = this.dedent(config.content);
|
|
3598
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
3599
|
+
lines.push(normalizedContent);
|
|
2855
3600
|
}
|
|
2856
3601
|
return {
|
|
2857
3602
|
path: `.claude/agents/${config.name}.md`,
|
|
@@ -2906,7 +3651,8 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2906
3651
|
const cleanText = text.split(/\n{2,}/).map(
|
|
2907
3652
|
(para) => para.split("\n").map((line) => line.trim()).filter((line) => line).join("\n")
|
|
2908
3653
|
).filter((para) => para).join("\n\n");
|
|
2909
|
-
|
|
3654
|
+
const normalizedText = this.normalizeMarkdownForPrettier(cleanText);
|
|
3655
|
+
return renderer.renderSection("Project", normalizedText) + "\n";
|
|
2910
3656
|
}
|
|
2911
3657
|
techStack(ast, renderer) {
|
|
2912
3658
|
const context = this.findBlock(ast, "context");
|
|
@@ -2964,8 +3710,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2964
3710
|
const text = this.extractText(context.content);
|
|
2965
3711
|
const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
|
|
2966
3712
|
if (!archMatch) return null;
|
|
2967
|
-
const content = archMatch.replace("## Architecture", "")
|
|
2968
|
-
|
|
3713
|
+
const content = archMatch.replace("## Architecture", "");
|
|
3714
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(content);
|
|
3715
|
+
return renderer.renderSection("Architecture", normalizedContent.trim()) + "\n";
|
|
2969
3716
|
}
|
|
2970
3717
|
codeStandards(ast, renderer) {
|
|
2971
3718
|
const standards = this.findBlock(ast, "standards");
|
|
@@ -3046,7 +3793,7 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
3046
3793
|
const props = this.getProps(shortcuts.content);
|
|
3047
3794
|
for (const [cmd, desc] of Object.entries(props)) {
|
|
3048
3795
|
const shortDesc = this.valueToString(desc).split("\n")[0] ?? "";
|
|
3049
|
-
commandLines.push(`${cmd.padEnd(10)} - ${shortDesc}
|
|
3796
|
+
commandLines.push(`${cmd.padEnd(10)} - ${shortDesc}`.trimEnd());
|
|
3050
3797
|
}
|
|
3051
3798
|
}
|
|
3052
3799
|
if (commandLines.length === 0) return null;
|
|
@@ -3055,8 +3802,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
3055
3802
|
const text = this.extractText(knowledge.content);
|
|
3056
3803
|
const match = this.extractSectionWithCodeBlock(text, "## Development Commands");
|
|
3057
3804
|
if (match) {
|
|
3058
|
-
const devCmds = match.replace("## Development Commands", "")
|
|
3059
|
-
|
|
3805
|
+
const devCmds = match.replace("## Development Commands", "");
|
|
3806
|
+
const normalizedDevCmds = this.normalizeMarkdownForPrettier(devCmds);
|
|
3807
|
+
content += "\n\n" + normalizedDevCmds.trim();
|
|
3060
3808
|
}
|
|
3061
3809
|
}
|
|
3062
3810
|
return renderer.renderSection("Commands", content) + "\n";
|
|
@@ -3067,8 +3815,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
3067
3815
|
const text = this.extractText(knowledge.content);
|
|
3068
3816
|
const match = this.extractSectionWithCodeBlock(text, "## Post-Work Verification");
|
|
3069
3817
|
if (!match) return null;
|
|
3070
|
-
const content = match.replace("## Post-Work Verification", "")
|
|
3071
|
-
|
|
3818
|
+
const content = match.replace("## Post-Work Verification", "");
|
|
3819
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(content);
|
|
3820
|
+
return renderer.renderSection("Post-Work Verification", normalizedContent.trim()) + "\n";
|
|
3072
3821
|
}
|
|
3073
3822
|
documentation(ast, renderer) {
|
|
3074
3823
|
const standards = this.findBlock(ast, "standards");
|
|
@@ -3446,13 +4195,39 @@ var CursorFormatter = class extends BaseFormatter {
|
|
|
3446
4195
|
if (never) sections.push(never);
|
|
3447
4196
|
}
|
|
3448
4197
|
intro(ast) {
|
|
3449
|
-
const
|
|
3450
|
-
if (
|
|
3451
|
-
|
|
4198
|
+
const identity2 = this.findBlock(ast, "identity");
|
|
4199
|
+
if (identity2) {
|
|
4200
|
+
const fullText = this.dedent(this.extractText(identity2.content));
|
|
4201
|
+
if (fullText.toLowerCase().startsWith("you are")) {
|
|
4202
|
+
return fullText;
|
|
4203
|
+
}
|
|
3452
4204
|
}
|
|
4205
|
+
const projectInfo = this.extractProjectInfo(ast);
|
|
3453
4206
|
const orgSuffix = projectInfo.org ? ` at ${projectInfo.org}` : "";
|
|
3454
4207
|
return `You are working on ${projectInfo.text}${orgSuffix}.`;
|
|
3455
4208
|
}
|
|
4209
|
+
/**
|
|
4210
|
+
* Remove common leading whitespace from all lines (dedent).
|
|
4211
|
+
* Handles the case where trim() was already called, causing the first line
|
|
4212
|
+
* to lose its indentation while subsequent lines retain theirs.
|
|
4213
|
+
*/
|
|
4214
|
+
dedent(text) {
|
|
4215
|
+
const lines = text.split("\n");
|
|
4216
|
+
if (lines.length <= 1) return text.trim();
|
|
4217
|
+
const minIndent = lines.slice(1).filter((line) => line.trim().length > 0).reduce((min, line) => {
|
|
4218
|
+
const match = line.match(/^(\s*)/);
|
|
4219
|
+
const indent = match?.[1]?.length ?? 0;
|
|
4220
|
+
return Math.min(min, indent);
|
|
4221
|
+
}, Infinity);
|
|
4222
|
+
if (minIndent === 0 || minIndent === Infinity) {
|
|
4223
|
+
return text.trim();
|
|
4224
|
+
}
|
|
4225
|
+
const firstLine = lines[0] ?? "";
|
|
4226
|
+
return [
|
|
4227
|
+
firstLine.trim(),
|
|
4228
|
+
...lines.slice(1).map((line) => line.trim().length > 0 ? line.slice(minIndent) : "")
|
|
4229
|
+
].join("\n").trim();
|
|
4230
|
+
}
|
|
3456
4231
|
/**
|
|
3457
4232
|
* Generate YAML frontmatter for Cursor MDC format.
|
|
3458
4233
|
* @see https://cursor.com/docs/context/rules
|
|
@@ -15378,11 +16153,13 @@ var Resolver = class {
|
|
|
15378
16153
|
cache;
|
|
15379
16154
|
resolving;
|
|
15380
16155
|
cacheEnabled;
|
|
16156
|
+
logger;
|
|
15381
16157
|
constructor(options) {
|
|
15382
16158
|
this.loader = new FileLoader(options);
|
|
15383
16159
|
this.cache = /* @__PURE__ */ new Map();
|
|
15384
16160
|
this.resolving = /* @__PURE__ */ new Set();
|
|
15385
16161
|
this.cacheEnabled = options.cache !== false;
|
|
16162
|
+
this.logger = options.logger ?? noopLogger;
|
|
15386
16163
|
}
|
|
15387
16164
|
/**
|
|
15388
16165
|
* Resolve a PromptScript file and all its dependencies.
|
|
@@ -15394,15 +16171,19 @@ var Resolver = class {
|
|
|
15394
16171
|
async resolve(entryPath) {
|
|
15395
16172
|
const absPath = this.loader.toAbsolutePath(entryPath);
|
|
15396
16173
|
if (this.resolving.has(absPath)) {
|
|
16174
|
+
this.logger.debug(`Circular dependency detected: ${absPath}`);
|
|
15397
16175
|
throw new CircularDependencyError([...this.resolving, absPath]);
|
|
15398
16176
|
}
|
|
15399
16177
|
if (this.cacheEnabled && this.cache.has(absPath)) {
|
|
16178
|
+
this.logger.debug(`Cache hit: ${absPath}`);
|
|
15400
16179
|
return this.cache.get(absPath);
|
|
15401
16180
|
}
|
|
15402
16181
|
this.resolving.add(absPath);
|
|
16182
|
+
this.logger.verbose(`Parsing ${absPath}`);
|
|
15403
16183
|
try {
|
|
15404
16184
|
const result = await this.doResolve(absPath);
|
|
15405
16185
|
if (this.cacheEnabled) {
|
|
16186
|
+
this.logger.debug(`Cache store: ${absPath}`);
|
|
15406
16187
|
this.cache.set(absPath, result);
|
|
15407
16188
|
}
|
|
15408
16189
|
return result;
|
|
@@ -15421,16 +16202,33 @@ var Resolver = class {
|
|
|
15421
16202
|
return { ast: null, sources, errors };
|
|
15422
16203
|
}
|
|
15423
16204
|
let ast = parseData.ast;
|
|
16205
|
+
this.logger.debug(`AST node count: ${this.countNodes(ast)}`);
|
|
15424
16206
|
ast = await this.resolveInherit(ast, absPath, sources, errors);
|
|
15425
16207
|
ast = await this.resolveImports(ast, absPath, sources, errors);
|
|
16208
|
+
if (ast.extends.length > 0) {
|
|
16209
|
+
this.logger.debug(`Applying ${ast.extends.length} extension(s)`);
|
|
16210
|
+
}
|
|
15426
16211
|
ast = applyExtends(ast);
|
|
15427
16212
|
ast = await resolveNativeSkills(ast, this.loader.getRegistryPath(), absPath);
|
|
16213
|
+
this.logger.debug(`Resolved ${sources.length} source file(s)`);
|
|
15428
16214
|
return {
|
|
15429
16215
|
ast,
|
|
15430
16216
|
sources: [...new Set(sources)],
|
|
15431
16217
|
errors
|
|
15432
16218
|
};
|
|
15433
16219
|
}
|
|
16220
|
+
/**
|
|
16221
|
+
* Count nodes in AST for debug output.
|
|
16222
|
+
*/
|
|
16223
|
+
countNodes(ast) {
|
|
16224
|
+
let count = 1;
|
|
16225
|
+
if (ast.meta) count++;
|
|
16226
|
+
if (ast.inherit) count++;
|
|
16227
|
+
count += ast.uses.length;
|
|
16228
|
+
count += ast.blocks.length;
|
|
16229
|
+
count += ast.extends.length;
|
|
16230
|
+
return count;
|
|
16231
|
+
}
|
|
15434
16232
|
/**
|
|
15435
16233
|
* Load and parse a file.
|
|
15436
16234
|
*/
|
|
@@ -15465,11 +16263,14 @@ var Resolver = class {
|
|
|
15465
16263
|
return ast;
|
|
15466
16264
|
}
|
|
15467
16265
|
const parentPath = this.loader.resolveRef(ast.inherit.path, absPath);
|
|
16266
|
+
this.logger.verbose(`Resolving inherit: ${ast.inherit.path.raw}`);
|
|
16267
|
+
this.logger.verbose(` \u2192 ${parentPath}`);
|
|
15468
16268
|
try {
|
|
15469
16269
|
const parent = await this.resolve(parentPath);
|
|
15470
16270
|
sources.push(...parent.sources);
|
|
15471
16271
|
errors.push(...parent.errors);
|
|
15472
16272
|
if (parent.ast) {
|
|
16273
|
+
this.logger.debug(`Merging with parent AST`);
|
|
15473
16274
|
return resolveInheritance(parent.ast, ast);
|
|
15474
16275
|
}
|
|
15475
16276
|
} catch (err) {
|
|
@@ -15487,11 +16288,14 @@ var Resolver = class {
|
|
|
15487
16288
|
let result = ast;
|
|
15488
16289
|
for (const use of ast.uses) {
|
|
15489
16290
|
const importPath = this.loader.resolveRef(use.path, absPath);
|
|
16291
|
+
this.logger.verbose(`Resolving import: ${use.path.raw}`);
|
|
16292
|
+
this.logger.verbose(` \u2192 ${importPath}`);
|
|
15490
16293
|
try {
|
|
15491
16294
|
const imported = await this.resolve(importPath);
|
|
15492
16295
|
sources.push(...imported.sources);
|
|
15493
16296
|
errors.push(...imported.errors);
|
|
15494
16297
|
if (imported.ast) {
|
|
16298
|
+
this.logger.debug(`Merging import${use.alias ? ` as "${use.alias}"` : ""}`);
|
|
15495
16299
|
result = resolveUses(result, use, imported.ast);
|
|
15496
16300
|
}
|
|
15497
16301
|
} catch (err) {
|
|
@@ -16765,6 +17569,7 @@ var Validator = class {
|
|
|
16765
17569
|
rules;
|
|
16766
17570
|
config;
|
|
16767
17571
|
disabledRules;
|
|
17572
|
+
logger;
|
|
16768
17573
|
/**
|
|
16769
17574
|
* Create a new validator instance.
|
|
16770
17575
|
*
|
|
@@ -16774,11 +17579,13 @@ var Validator = class {
|
|
|
16774
17579
|
this.config = config;
|
|
16775
17580
|
this.rules = [...allRules];
|
|
16776
17581
|
this.disabledRules = new Set(config.disableRules ?? []);
|
|
17582
|
+
this.logger = config.logger ?? noopLogger;
|
|
16777
17583
|
if (config.customRules) {
|
|
16778
17584
|
for (const rule of config.customRules) {
|
|
16779
17585
|
this.rules.push(rule);
|
|
16780
17586
|
}
|
|
16781
17587
|
}
|
|
17588
|
+
this.logger.debug(`Validator initialized with ${this.rules.length} rules`);
|
|
16782
17589
|
}
|
|
16783
17590
|
/**
|
|
16784
17591
|
* Validate an AST.
|
|
@@ -16788,15 +17595,22 @@ var Validator = class {
|
|
|
16788
17595
|
*/
|
|
16789
17596
|
validate(ast) {
|
|
16790
17597
|
const messages = [];
|
|
17598
|
+
const activeRules = this.rules.filter(
|
|
17599
|
+
(r) => !this.disabledRules.has(r.name) && !this.disabledRules.has(r.id)
|
|
17600
|
+
);
|
|
17601
|
+
this.logger.verbose(`Running ${activeRules.length} validation rules`);
|
|
16791
17602
|
for (const rule of this.rules) {
|
|
16792
17603
|
if (this.disabledRules.has(rule.name) || this.disabledRules.has(rule.id)) {
|
|
17604
|
+
this.logger.debug(`Skipping disabled rule: ${rule.name}`);
|
|
16793
17605
|
continue;
|
|
16794
17606
|
}
|
|
16795
17607
|
const configuredSeverity = this.config.rules?.[rule.name];
|
|
16796
17608
|
const severity = configuredSeverity ?? rule.defaultSeverity;
|
|
16797
17609
|
if (severity === "off") {
|
|
17610
|
+
this.logger.debug(`Skipping off rule: ${rule.name}`);
|
|
16798
17611
|
continue;
|
|
16799
17612
|
}
|
|
17613
|
+
this.logger.debug(`Running rule: ${rule.name} (${severity})`);
|
|
16800
17614
|
const ctx = {
|
|
16801
17615
|
ast,
|
|
16802
17616
|
config: this.config,
|
|
@@ -16814,6 +17628,9 @@ var Validator = class {
|
|
|
16814
17628
|
const errors = messages.filter((m) => m.severity === "error");
|
|
16815
17629
|
const warnings = messages.filter((m) => m.severity === "warning");
|
|
16816
17630
|
const infos = messages.filter((m) => m.severity === "info");
|
|
17631
|
+
if (errors.length > 0 || warnings.length > 0) {
|
|
17632
|
+
this.logger.verbose(`Validation: ${errors.length} error(s), ${warnings.length} warning(s)`);
|
|
17633
|
+
}
|
|
16817
17634
|
return {
|
|
16818
17635
|
valid: errors.length === 0,
|
|
16819
17636
|
errors,
|
|
@@ -16895,13 +17712,16 @@ ${afterMarker.replace(/^\n+/, "\n")}`;
|
|
|
16895
17712
|
var Compiler = class _Compiler {
|
|
16896
17713
|
constructor(options) {
|
|
16897
17714
|
this.options = options;
|
|
16898
|
-
this.
|
|
16899
|
-
this.
|
|
17715
|
+
this.logger = options.logger ?? noopLogger;
|
|
17716
|
+
this.resolver = new Resolver({ ...options.resolver, logger: this.logger });
|
|
17717
|
+
this.validator = new Validator({ ...options.validator, logger: this.logger });
|
|
16900
17718
|
this.loadedFormatters = this.loadFormatters(options.formatters);
|
|
17719
|
+
this.logger.debug(`Compiler initialized with ${this.loadedFormatters.length} formatters`);
|
|
16901
17720
|
}
|
|
16902
17721
|
resolver;
|
|
16903
17722
|
validator;
|
|
16904
17723
|
loadedFormatters;
|
|
17724
|
+
logger;
|
|
16905
17725
|
/**
|
|
16906
17726
|
* Compile a PromptScript file through the full pipeline.
|
|
16907
17727
|
*
|
|
@@ -16909,6 +17729,10 @@ var Compiler = class _Compiler {
|
|
|
16909
17729
|
* @returns Compilation result with outputs, errors, and stats
|
|
16910
17730
|
*/
|
|
16911
17731
|
async compile(entryPath) {
|
|
17732
|
+
this.logger.verbose(`Entry: ${entryPath}`);
|
|
17733
|
+
this.logger.verbose(
|
|
17734
|
+
`Targets: ${this.loadedFormatters.map((f) => f.formatter.name).join(", ")}`
|
|
17735
|
+
);
|
|
16912
17736
|
const startTotal = Date.now();
|
|
16913
17737
|
const stats = {
|
|
16914
17738
|
resolveTime: 0,
|
|
@@ -16916,6 +17740,7 @@ var Compiler = class _Compiler {
|
|
|
16916
17740
|
formatTime: 0,
|
|
16917
17741
|
totalTime: 0
|
|
16918
17742
|
};
|
|
17743
|
+
this.logger.verbose("=== Stage 1: Resolve ===");
|
|
16919
17744
|
const startResolve = Date.now();
|
|
16920
17745
|
let resolved;
|
|
16921
17746
|
try {
|
|
@@ -16923,6 +17748,7 @@ var Compiler = class _Compiler {
|
|
|
16923
17748
|
} catch (err) {
|
|
16924
17749
|
stats.resolveTime = Date.now() - startResolve;
|
|
16925
17750
|
stats.totalTime = Date.now() - startTotal;
|
|
17751
|
+
this.logger.verbose(`Resolve failed (${stats.resolveTime}ms)`);
|
|
16926
17752
|
return {
|
|
16927
17753
|
success: false,
|
|
16928
17754
|
outputs: /* @__PURE__ */ new Map(),
|
|
@@ -16932,6 +17758,7 @@ var Compiler = class _Compiler {
|
|
|
16932
17758
|
};
|
|
16933
17759
|
}
|
|
16934
17760
|
stats.resolveTime = Date.now() - startResolve;
|
|
17761
|
+
this.logger.verbose(`Resolve completed (${stats.resolveTime}ms)`);
|
|
16935
17762
|
if (resolved.errors.length > 0 || !resolved.ast) {
|
|
16936
17763
|
stats.totalTime = Date.now() - startTotal;
|
|
16937
17764
|
return {
|
|
@@ -16942,9 +17769,11 @@ var Compiler = class _Compiler {
|
|
|
16942
17769
|
stats
|
|
16943
17770
|
};
|
|
16944
17771
|
}
|
|
17772
|
+
this.logger.verbose("=== Stage 2: Validate ===");
|
|
16945
17773
|
const startValidate = Date.now();
|
|
16946
17774
|
const validation = this.validator.validate(resolved.ast);
|
|
16947
17775
|
stats.validateTime = Date.now() - startValidate;
|
|
17776
|
+
this.logger.verbose(`Validate completed (${stats.validateTime}ms)`);
|
|
16948
17777
|
if (!validation.valid) {
|
|
16949
17778
|
stats.totalTime = Date.now() - startTotal;
|
|
16950
17779
|
return {
|
|
@@ -16955,16 +17784,23 @@ var Compiler = class _Compiler {
|
|
|
16955
17784
|
stats
|
|
16956
17785
|
};
|
|
16957
17786
|
}
|
|
17787
|
+
this.logger.verbose("=== Stage 3: Format ===");
|
|
16958
17788
|
const startFormat = Date.now();
|
|
16959
17789
|
const outputs = /* @__PURE__ */ new Map();
|
|
16960
17790
|
const formatErrors = [];
|
|
16961
17791
|
for (const { formatter, config } of this.loadedFormatters) {
|
|
17792
|
+
const formatterStart = Date.now();
|
|
17793
|
+
this.logger.verbose(`Formatting for ${formatter.name}`);
|
|
16962
17794
|
try {
|
|
16963
17795
|
const formatOptions = this.getFormatOptionsForTarget(formatter.name, config);
|
|
17796
|
+
this.logger.debug(` Convention: ${formatOptions.convention ?? "default"}`);
|
|
16964
17797
|
const output = formatter.format(resolved.ast, formatOptions);
|
|
17798
|
+
const formatterTime = Date.now() - formatterStart;
|
|
17799
|
+
this.logger.verbose(` \u2192 ${output.path} (${formatterTime}ms)`);
|
|
16965
17800
|
outputs.set(output.path, addMarkerToOutput(output));
|
|
16966
17801
|
if (output.additionalFiles) {
|
|
16967
17802
|
for (const additionalFile of output.additionalFiles) {
|
|
17803
|
+
this.logger.verbose(` \u2192 ${additionalFile.path} (additional)`);
|
|
16968
17804
|
outputs.set(additionalFile.path, addMarkerToOutput(additionalFile));
|
|
16969
17805
|
}
|
|
16970
17806
|
}
|
|
@@ -16978,6 +17814,7 @@ var Compiler = class _Compiler {
|
|
|
16978
17814
|
}
|
|
16979
17815
|
stats.formatTime = Date.now() - startFormat;
|
|
16980
17816
|
stats.totalTime = Date.now() - startTotal;
|
|
17817
|
+
this.logger.verbose(`Format completed (${stats.formatTime}ms)`);
|
|
16981
17818
|
if (formatErrors.length > 0) {
|
|
16982
17819
|
return {
|
|
16983
17820
|
success: false,
|
|
@@ -17277,6 +18114,24 @@ var PROMPTSCRIPT_MARKERS = [
|
|
|
17277
18114
|
"> Auto-generated by PromptScript"
|
|
17278
18115
|
// Legacy - for backwards compatibility
|
|
17279
18116
|
];
|
|
18117
|
+
var MARKER_REGEX = /<!-- PromptScript [^>]+ -->\n*/g;
|
|
18118
|
+
function stripMarkers(content) {
|
|
18119
|
+
return content.replace(MARKER_REGEX, "");
|
|
18120
|
+
}
|
|
18121
|
+
function createCliLogger() {
|
|
18122
|
+
return {
|
|
18123
|
+
verbose: (message) => {
|
|
18124
|
+
if (isVerbose() || isDebug()) {
|
|
18125
|
+
ConsoleOutput.verbose(message);
|
|
18126
|
+
}
|
|
18127
|
+
},
|
|
18128
|
+
debug: (message) => {
|
|
18129
|
+
if (isDebug()) {
|
|
18130
|
+
ConsoleOutput.debug(message);
|
|
18131
|
+
}
|
|
18132
|
+
}
|
|
18133
|
+
};
|
|
18134
|
+
}
|
|
17280
18135
|
function parseTargets(targets) {
|
|
17281
18136
|
return targets.map((entry) => {
|
|
17282
18137
|
if (typeof entry === "string") {
|
|
@@ -17290,10 +18145,10 @@ function parseTargets(targets) {
|
|
|
17290
18145
|
return { name, config };
|
|
17291
18146
|
}).filter((target) => target.config?.enabled !== false);
|
|
17292
18147
|
}
|
|
17293
|
-
async function
|
|
18148
|
+
async function isPromptScriptGenerated2(filePath) {
|
|
17294
18149
|
try {
|
|
17295
18150
|
const content = await readFile6(filePath, "utf-8");
|
|
17296
|
-
const lines = content.split("\n").slice(0,
|
|
18151
|
+
const lines = content.split("\n").slice(0, 20);
|
|
17297
18152
|
return lines.some((line) => PROMPTSCRIPT_MARKERS.some((marker) => line.includes(marker)));
|
|
17298
18153
|
} catch {
|
|
17299
18154
|
return false;
|
|
@@ -17321,7 +18176,7 @@ function printErrors(errors) {
|
|
|
17321
18176
|
}
|
|
17322
18177
|
}
|
|
17323
18178
|
async function writeOutputs(outputs, options, _config, services) {
|
|
17324
|
-
const result = { written: [], skipped: [] };
|
|
18179
|
+
const result = { written: [], skipped: [], unchanged: [] };
|
|
17325
18180
|
let overwriteAll = false;
|
|
17326
18181
|
const conflicts = [];
|
|
17327
18182
|
for (const output of outputs.values()) {
|
|
@@ -17329,16 +18184,26 @@ async function writeOutputs(outputs, options, _config, services) {
|
|
|
17329
18184
|
const fileExists2 = existsSync7(outputPath);
|
|
17330
18185
|
if (options.dryRun) {
|
|
17331
18186
|
if (fileExists2) {
|
|
17332
|
-
const isGenerated2 = await
|
|
18187
|
+
const isGenerated2 = await isPromptScriptGenerated2(outputPath);
|
|
17333
18188
|
if (isGenerated2) {
|
|
17334
|
-
|
|
18189
|
+
const existingContent = await readFile6(outputPath, "utf-8");
|
|
18190
|
+
const existingWithoutMarker = stripMarkers(existingContent);
|
|
18191
|
+
const newWithoutMarker = stripMarkers(output.content);
|
|
18192
|
+
if (existingWithoutMarker === newWithoutMarker) {
|
|
18193
|
+
ConsoleOutput.dryRun(`Unchanged: ${outputPath}`);
|
|
18194
|
+
result.unchanged.push(outputPath);
|
|
18195
|
+
} else {
|
|
18196
|
+
ConsoleOutput.dryRun(`Would overwrite: ${outputPath}`);
|
|
18197
|
+
result.written.push(outputPath);
|
|
18198
|
+
}
|
|
17335
18199
|
} else {
|
|
17336
18200
|
ConsoleOutput.warning(`Would conflict: ${outputPath} (not generated by PromptScript)`);
|
|
18201
|
+
result.written.push(outputPath);
|
|
17337
18202
|
}
|
|
17338
18203
|
} else {
|
|
17339
18204
|
ConsoleOutput.dryRun(`Would write: ${outputPath}`);
|
|
18205
|
+
result.written.push(outputPath);
|
|
17340
18206
|
}
|
|
17341
|
-
result.written.push(outputPath);
|
|
17342
18207
|
continue;
|
|
17343
18208
|
}
|
|
17344
18209
|
if (!fileExists2) {
|
|
@@ -17348,8 +18213,16 @@ async function writeOutputs(outputs, options, _config, services) {
|
|
|
17348
18213
|
result.written.push(outputPath);
|
|
17349
18214
|
continue;
|
|
17350
18215
|
}
|
|
17351
|
-
const isGenerated = await
|
|
18216
|
+
const isGenerated = await isPromptScriptGenerated2(outputPath);
|
|
17352
18217
|
if (isGenerated) {
|
|
18218
|
+
const existingContent = await readFile6(outputPath, "utf-8");
|
|
18219
|
+
const existingWithoutMarker = stripMarkers(existingContent);
|
|
18220
|
+
const newWithoutMarker = stripMarkers(output.content);
|
|
18221
|
+
if (existingWithoutMarker === newWithoutMarker) {
|
|
18222
|
+
ConsoleOutput.unchanged(outputPath);
|
|
18223
|
+
result.unchanged.push(outputPath);
|
|
18224
|
+
continue;
|
|
18225
|
+
}
|
|
17353
18226
|
await mkdir2(dirname5(outputPath), { recursive: true });
|
|
17354
18227
|
await writeFile2(outputPath, output.content, "utf-8");
|
|
17355
18228
|
ConsoleOutput.success(outputPath);
|
|
@@ -17416,12 +18289,16 @@ function printWarnings(warnings) {
|
|
|
17416
18289
|
}
|
|
17417
18290
|
async function compileCommand(options, services = createDefaultServices()) {
|
|
17418
18291
|
const spinner = createSpinner("Loading configuration...").start();
|
|
18292
|
+
const logger = createCliLogger();
|
|
17419
18293
|
try {
|
|
18294
|
+
logger.verbose("Loading configuration...");
|
|
17420
18295
|
const config = await loadConfig(options.config);
|
|
17421
18296
|
spinner.text = "Compiling...";
|
|
17422
18297
|
const selectedTarget = options.target ?? options.format;
|
|
17423
18298
|
const targets = selectedTarget ? [{ name: selectedTarget }] : parseTargets(config.targets);
|
|
17424
18299
|
const registryPath = options.registry ?? config.registry?.path ?? "./registry";
|
|
18300
|
+
logger.verbose(`Registry: ${registryPath}`);
|
|
18301
|
+
logger.debug(`Config: ${JSON.stringify(config, null, 2)}`);
|
|
17425
18302
|
const prettierOptions = await resolvePrettierOptions(config, process.cwd());
|
|
17426
18303
|
const compiler = new Compiler({
|
|
17427
18304
|
resolver: {
|
|
@@ -17431,7 +18308,8 @@ async function compileCommand(options, services = createDefaultServices()) {
|
|
|
17431
18308
|
validator: config.validation,
|
|
17432
18309
|
formatters: targets,
|
|
17433
18310
|
customConventions: config.customConventions,
|
|
17434
|
-
prettier: prettierOptions
|
|
18311
|
+
prettier: prettierOptions,
|
|
18312
|
+
logger
|
|
17435
18313
|
});
|
|
17436
18314
|
const entryPath = resolve4("./.promptscript/project.prs");
|
|
17437
18315
|
if (!existsSync7(entryPath)) {
|
|
@@ -17450,6 +18328,9 @@ async function compileCommand(options, services = createDefaultServices()) {
|
|
|
17450
18328
|
spinner.succeed("Compilation successful");
|
|
17451
18329
|
ConsoleOutput.newline();
|
|
17452
18330
|
const writeResult = await writeOutputs(result.outputs, options, config, services);
|
|
18331
|
+
if (writeResult.unchanged.length > 0) {
|
|
18332
|
+
ConsoleOutput.muted(`Unchanged ${writeResult.unchanged.length} file(s)`);
|
|
18333
|
+
}
|
|
17453
18334
|
if (writeResult.skipped.length > 0) {
|
|
17454
18335
|
ConsoleOutput.muted(`Skipped ${writeResult.skipped.length} file(s)`);
|
|
17455
18336
|
}
|
|
@@ -18080,18 +18961,22 @@ function printResults(results) {
|
|
|
18080
18961
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
18081
18962
|
var __dirname2 = dirname7(__filename2);
|
|
18082
18963
|
var program = new Command();
|
|
18083
|
-
program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(__dirname2, "./package.json")).option("--verbose", "Enable verbose output").option("--quiet", "Suppress non-error output").hook("preAction", (thisCommand) => {
|
|
18964
|
+
program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(__dirname2, "./package.json")).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", (thisCommand) => {
|
|
18084
18965
|
const opts = thisCommand.opts();
|
|
18085
18966
|
if (opts["quiet"]) {
|
|
18086
18967
|
setContext({ logLevel: 0 /* Quiet */ });
|
|
18968
|
+
} else if (opts["debug"]) {
|
|
18969
|
+
setContext({ logLevel: 3 /* Debug */ });
|
|
18087
18970
|
} else if (opts["verbose"]) {
|
|
18088
18971
|
setContext({ logLevel: 2 /* Verbose */ });
|
|
18089
18972
|
}
|
|
18090
|
-
if (process.env["
|
|
18973
|
+
if (process.env["PROMPTSCRIPT_DEBUG"] === "1" || process.env["PROMPTSCRIPT_DEBUG"] === "true") {
|
|
18974
|
+
setContext({ logLevel: 3 /* Debug */ });
|
|
18975
|
+
} else if (process.env["PROMPTSCRIPT_VERBOSE"] === "1" || process.env["PROMPTSCRIPT_VERBOSE"] === "true") {
|
|
18091
18976
|
setContext({ logLevel: 2 /* Verbose */ });
|
|
18092
18977
|
}
|
|
18093
18978
|
});
|
|
18094
|
-
program.command("init").description("Initialize PromptScript in current directory").option("-n, --name <name>", "Project name (auto-detected from package.json, etc.)").option("-t, --team <team>", "Team namespace").option("--inherit <path>", "Inheritance path (e.g., @company/team)").option("--registry <path>", "Registry path").option("--targets <targets...>", "Target AI tools (github, claude, cursor)").option("-i, --interactive", "Force interactive mode").option("-y, --yes", "Skip prompts, use defaults").option("-f, --force", "Force reinitialize even if already initialized").action((opts) => initCommand(opts));
|
|
18979
|
+
program.command("init").description("Initialize PromptScript in current directory").option("-n, --name <name>", "Project name (auto-detected from package.json, etc.)").option("-t, --team <team>", "Team namespace").option("--inherit <path>", "Inheritance path (e.g., @company/team)").option("--registry <path>", "Registry path").option("--targets <targets...>", "Target AI tools (github, claude, cursor)").option("-i, --interactive", "Force interactive mode").option("-y, --yes", "Skip prompts, use defaults").option("-f, --force", "Force reinitialize even if already initialized").option("-m, --migrate", "Install migration skill for AI-assisted migration").action((opts) => initCommand(opts));
|
|
18095
18980
|
program.command("compile").description("Compile PromptScript to target formats").option("-t, --target <target>", "Specific target (github, claude, cursor)").option("-f, --format <format>", "Output format (alias for --target)").option("-a, --all", "All configured targets", true).option("-w, --watch", "Watch mode").option("-o, --output <dir>", "Output directory").option("--dry-run", "Preview changes").option("--registry <path>", "Registry path (overrides config)").option("-c, --config <path>", "Path to custom config file").option("--force", "Force overwrite existing files without prompts").action((opts) => compileCommand(opts));
|
|
18096
18981
|
program.command("validate").description("Validate PromptScript files").option("--strict", "Treat warnings as errors").option("--format <format>", "Output format (text, json)", "text").action(validateCommand);
|
|
18097
18982
|
program.command("pull").description("Pull updates from registry").option("-f, --force", "Force overwrite").option("--dry-run", "Preview changes without pulling").option("-b, --branch <name>", "Git branch to pull from").option("--tag <name>", "Git tag to pull from").option("--commit <hash>", "Git commit to pull from").option("--refresh", "Force re-fetch from remote (ignore cache)").action(pullCommand);
|