@promptscript/cli 1.0.0-alpha.5 → 1.0.0-alpha.7
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 +29 -0
- package/README.md +36 -0
- package/index.js +1682 -119
- package/package.json +1 -1
- package/src/cli.d.ts.map +1 -1
- package/src/commands/compile.d.ts.map +1 -1
- package/src/commands/diff.d.ts.map +1 -1
- package/src/commands/init.d.ts.map +1 -1
- package/src/commands/update-check.d.ts +6 -0
- package/src/commands/update-check.d.ts.map +1 -0
- package/src/commands/validate.d.ts.map +1 -1
- package/src/index.d.ts +3 -0
- package/src/index.d.ts.map +1 -1
- package/src/output/console.d.ts +4 -0
- 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 -2
- package/src/types.d.ts.map +1 -1
- package/src/utils/manifest-loader.d.ts +157 -0
- package/src/utils/manifest-loader.d.ts.map +1 -0
- package/src/utils/registry-resolver.d.ts +41 -0
- package/src/utils/registry-resolver.d.ts.map +1 -0
- package/src/utils/suggestion-engine.d.ts +56 -0
- package/src/utils/suggestion-engine.d.ts.map +1 -0
- package/src/utils/version-check.d.ts +48 -0
- package/src/utils/version-check.d.ts.map +1 -0
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// packages/cli/src/cli.ts
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { fileURLToPath as
|
|
4
|
-
import { dirname as
|
|
3
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4
|
+
import { dirname as dirname9 } from "path";
|
|
5
5
|
|
|
6
6
|
// packages/core/src/types/constants.ts
|
|
7
7
|
var BLOCK_TYPES = [
|
|
@@ -306,7 +306,7 @@ var noopLogger = {
|
|
|
306
306
|
|
|
307
307
|
// packages/cli/src/commands/init.ts
|
|
308
308
|
import { fileURLToPath } from "url";
|
|
309
|
-
import { dirname as
|
|
309
|
+
import { dirname as dirname3 } from "path";
|
|
310
310
|
|
|
311
311
|
// packages/cli/src/services.ts
|
|
312
312
|
import { writeFile, mkdir, readFile, readdir } from "fs/promises";
|
|
@@ -405,6 +405,13 @@ var ConsoleOutput = {
|
|
|
405
405
|
if (isQuiet()) return;
|
|
406
406
|
console.log(chalk.yellow(` \u2298 ${message}`));
|
|
407
407
|
},
|
|
408
|
+
/**
|
|
409
|
+
* Print an unchanged file message.
|
|
410
|
+
*/
|
|
411
|
+
unchanged(message) {
|
|
412
|
+
if (isQuiet()) return;
|
|
413
|
+
console.log(chalk.gray(` \u25CB ${message}`));
|
|
414
|
+
},
|
|
408
415
|
/**
|
|
409
416
|
* Print an info message.
|
|
410
417
|
*/
|
|
@@ -859,9 +866,910 @@ async function resolvePrettierOptions(config, basePath = process.cwd()) {
|
|
|
859
866
|
return result;
|
|
860
867
|
}
|
|
861
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
|
+
|
|
1369
|
+
// packages/cli/src/utils/manifest-loader.ts
|
|
1370
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
1371
|
+
import { parse as parseYaml2 } from "yaml";
|
|
1372
|
+
var OFFICIAL_REGISTRY = {
|
|
1373
|
+
name: "PromptScript Official Registry",
|
|
1374
|
+
url: "https://github.com/mrwogu/promptscript-registry.git",
|
|
1375
|
+
branch: "main",
|
|
1376
|
+
manifestUrl: "https://raw.githubusercontent.com/mrwogu/promptscript-registry/main/registry-manifest.yaml"
|
|
1377
|
+
};
|
|
1378
|
+
var ManifestLoadError = class extends Error {
|
|
1379
|
+
originalCause;
|
|
1380
|
+
constructor(message, cause) {
|
|
1381
|
+
super(message);
|
|
1382
|
+
this.name = "ManifestLoadError";
|
|
1383
|
+
this.originalCause = cause;
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
var manifestCache = /* @__PURE__ */ new Map();
|
|
1387
|
+
var MANIFEST_FILENAME = "registry-manifest.yaml";
|
|
1388
|
+
async function loadManifest(options = {}, services = createDefaultServices()) {
|
|
1389
|
+
const { registryPath = findDefaultRegistryPath(services), useCache = true } = options;
|
|
1390
|
+
const manifestPath = resolveManifestPath(registryPath, services);
|
|
1391
|
+
if (useCache && manifestCache.has(manifestPath)) {
|
|
1392
|
+
return {
|
|
1393
|
+
manifest: manifestCache.get(manifestPath),
|
|
1394
|
+
path: manifestPath,
|
|
1395
|
+
cached: true
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
const manifest = await loadManifestFromPath(manifestPath, services);
|
|
1399
|
+
validateManifest(manifest);
|
|
1400
|
+
if (useCache) {
|
|
1401
|
+
manifestCache.set(manifestPath, manifest);
|
|
1402
|
+
}
|
|
1403
|
+
return {
|
|
1404
|
+
manifest,
|
|
1405
|
+
path: manifestPath,
|
|
1406
|
+
cached: false
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
function findDefaultRegistryPath(services) {
|
|
1410
|
+
const candidates = ["./registry", "../promptscript-registry", "./.promptscript-registry"];
|
|
1411
|
+
for (const candidate of candidates) {
|
|
1412
|
+
const manifestPath = join3(services.cwd, candidate, MANIFEST_FILENAME);
|
|
1413
|
+
if (services.fs.existsSync(manifestPath)) {
|
|
1414
|
+
return candidate;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
return "./registry";
|
|
1418
|
+
}
|
|
1419
|
+
function resolveManifestPath(registryPath, services) {
|
|
1420
|
+
if (registryPath.endsWith(".yaml") || registryPath.endsWith(".yml")) {
|
|
1421
|
+
return join3(services.cwd, registryPath);
|
|
1422
|
+
}
|
|
1423
|
+
return join3(services.cwd, registryPath, MANIFEST_FILENAME);
|
|
1424
|
+
}
|
|
1425
|
+
async function loadManifestFromPath(manifestPath, services) {
|
|
1426
|
+
if (!services.fs.existsSync(manifestPath)) {
|
|
1427
|
+
throw new ManifestLoadError(
|
|
1428
|
+
`Registry manifest not found at: ${manifestPath}
|
|
1429
|
+
Run \`prs pull\` to fetch the registry or check your registry configuration.`
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
try {
|
|
1433
|
+
const content = await services.fs.readFile(manifestPath, "utf-8");
|
|
1434
|
+
const manifest = parseYaml2(content);
|
|
1435
|
+
return manifest;
|
|
1436
|
+
} catch (error) {
|
|
1437
|
+
throw new ManifestLoadError(
|
|
1438
|
+
`Failed to parse manifest: ${manifestPath}`,
|
|
1439
|
+
error instanceof Error ? error : void 0
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
function validateManifest(manifest) {
|
|
1444
|
+
if (!manifest.version) {
|
|
1445
|
+
throw new ManifestLoadError("Manifest missing required field: version");
|
|
1446
|
+
}
|
|
1447
|
+
if (manifest.version !== "1") {
|
|
1448
|
+
throw new ManifestLoadError(`Unsupported manifest version: ${manifest.version}`);
|
|
1449
|
+
}
|
|
1450
|
+
if (!manifest.meta) {
|
|
1451
|
+
throw new ManifestLoadError("Manifest missing required field: meta");
|
|
1452
|
+
}
|
|
1453
|
+
if (!manifest.catalog) {
|
|
1454
|
+
throw new ManifestLoadError("Manifest missing required field: catalog");
|
|
1455
|
+
}
|
|
1456
|
+
if (!Array.isArray(manifest.catalog)) {
|
|
1457
|
+
throw new ManifestLoadError("Manifest catalog must be an array");
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
async function loadManifestFromUrl(url = OFFICIAL_REGISTRY.manifestUrl, useCache = true) {
|
|
1461
|
+
if (useCache && manifestCache.has(url)) {
|
|
1462
|
+
return {
|
|
1463
|
+
manifest: manifestCache.get(url),
|
|
1464
|
+
url,
|
|
1465
|
+
cached: true
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
try {
|
|
1469
|
+
const response = await fetch(url, {
|
|
1470
|
+
headers: {
|
|
1471
|
+
Accept: "application/x-yaml, text/yaml, text/plain",
|
|
1472
|
+
"User-Agent": "PromptScript-CLI"
|
|
1473
|
+
}
|
|
1474
|
+
});
|
|
1475
|
+
if (!response.ok) {
|
|
1476
|
+
throw new ManifestLoadError(
|
|
1477
|
+
`Failed to fetch manifest from ${url}: ${response.status} ${response.statusText}`
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
const content = await response.text();
|
|
1481
|
+
const manifest = parseYaml2(content);
|
|
1482
|
+
validateManifest(manifest);
|
|
1483
|
+
if (useCache) {
|
|
1484
|
+
manifestCache.set(url, manifest);
|
|
1485
|
+
}
|
|
1486
|
+
return {
|
|
1487
|
+
manifest,
|
|
1488
|
+
url,
|
|
1489
|
+
cached: false
|
|
1490
|
+
};
|
|
1491
|
+
} catch (error) {
|
|
1492
|
+
if (error instanceof ManifestLoadError) {
|
|
1493
|
+
throw error;
|
|
1494
|
+
}
|
|
1495
|
+
throw new ManifestLoadError(
|
|
1496
|
+
`Failed to load manifest from ${url}`,
|
|
1497
|
+
error instanceof Error ? error : void 0
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
function isValidGitUrl(url, allowedHosts) {
|
|
1502
|
+
try {
|
|
1503
|
+
if (url.startsWith("git@")) {
|
|
1504
|
+
const hostMatch = url.match(/^git@([^:]+):/);
|
|
1505
|
+
if (hostMatch && hostMatch[1]) {
|
|
1506
|
+
const host = hostMatch[1];
|
|
1507
|
+
return allowedHosts ? allowedHosts.includes(host) : true;
|
|
1508
|
+
}
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
if (!url.startsWith("https://") && !url.startsWith("http://")) {
|
|
1512
|
+
return false;
|
|
1513
|
+
}
|
|
1514
|
+
const parsed = new URL(url);
|
|
1515
|
+
if (!parsed.hostname || parsed.hostname.length === 0) {
|
|
1516
|
+
return false;
|
|
1517
|
+
}
|
|
1518
|
+
if (allowedHosts) {
|
|
1519
|
+
return allowedHosts.includes(parsed.hostname);
|
|
1520
|
+
}
|
|
1521
|
+
return true;
|
|
1522
|
+
} catch {
|
|
1523
|
+
return false;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// packages/cli/src/utils/suggestion-engine.ts
|
|
1528
|
+
async function buildProjectContext(projectInfo, services = createDefaultServices()) {
|
|
1529
|
+
const files = await detectProjectFiles(services);
|
|
1530
|
+
const dependencies = await detectDependencies(services);
|
|
1531
|
+
return {
|
|
1532
|
+
files,
|
|
1533
|
+
dependencies,
|
|
1534
|
+
languages: projectInfo.languages,
|
|
1535
|
+
frameworks: projectInfo.frameworks
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
async function detectProjectFiles(services) {
|
|
1539
|
+
const relevantFiles = [
|
|
1540
|
+
"package.json",
|
|
1541
|
+
"tsconfig.json",
|
|
1542
|
+
"pyproject.toml",
|
|
1543
|
+
"requirements.txt",
|
|
1544
|
+
"Cargo.toml",
|
|
1545
|
+
"go.mod",
|
|
1546
|
+
"pom.xml",
|
|
1547
|
+
"build.gradle",
|
|
1548
|
+
".git",
|
|
1549
|
+
".env",
|
|
1550
|
+
".env.example",
|
|
1551
|
+
"jest.config.js",
|
|
1552
|
+
"vitest.config.ts",
|
|
1553
|
+
"vitest.config.js",
|
|
1554
|
+
"pytest.ini",
|
|
1555
|
+
"next.config.js",
|
|
1556
|
+
"next.config.mjs",
|
|
1557
|
+
"next.config.ts",
|
|
1558
|
+
"nuxt.config.js",
|
|
1559
|
+
"nuxt.config.ts",
|
|
1560
|
+
"vite.config.js",
|
|
1561
|
+
"vite.config.ts",
|
|
1562
|
+
"Dockerfile",
|
|
1563
|
+
"docker-compose.yml"
|
|
1564
|
+
];
|
|
1565
|
+
const existingFiles = [];
|
|
1566
|
+
for (const file of relevantFiles) {
|
|
1567
|
+
if (services.fs.existsSync(file)) {
|
|
1568
|
+
existingFiles.push(file);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return existingFiles;
|
|
1572
|
+
}
|
|
1573
|
+
async function detectDependencies(services) {
|
|
1574
|
+
if (!services.fs.existsSync("package.json")) {
|
|
1575
|
+
return [];
|
|
1576
|
+
}
|
|
1577
|
+
try {
|
|
1578
|
+
const content = await services.fs.readFile("package.json", "utf-8");
|
|
1579
|
+
const pkg = JSON.parse(content);
|
|
1580
|
+
return [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})];
|
|
1581
|
+
} catch {
|
|
1582
|
+
return [];
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
function calculateSuggestions(manifest, context) {
|
|
1586
|
+
const result = {
|
|
1587
|
+
inherit: void 0,
|
|
1588
|
+
use: [],
|
|
1589
|
+
skills: [],
|
|
1590
|
+
reasoning: []
|
|
1591
|
+
};
|
|
1592
|
+
const suggestedUse = /* @__PURE__ */ new Set();
|
|
1593
|
+
const suggestedSkills = /* @__PURE__ */ new Set();
|
|
1594
|
+
for (const rule of manifest.suggestionRules) {
|
|
1595
|
+
const match = matchCondition(rule.condition, context);
|
|
1596
|
+
if (match.matches) {
|
|
1597
|
+
if (rule.suggest.inherit) {
|
|
1598
|
+
if (!result.inherit) {
|
|
1599
|
+
result.inherit = rule.suggest.inherit;
|
|
1600
|
+
result.reasoning.push({
|
|
1601
|
+
suggestion: `inherit: ${rule.suggest.inherit}`,
|
|
1602
|
+
reason: match.reason,
|
|
1603
|
+
trigger: match.trigger,
|
|
1604
|
+
matchedValue: match.matchedValue
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
if (rule.suggest.use) {
|
|
1609
|
+
for (const useItem of rule.suggest.use) {
|
|
1610
|
+
if (!suggestedUse.has(useItem)) {
|
|
1611
|
+
suggestedUse.add(useItem);
|
|
1612
|
+
result.use.push(useItem);
|
|
1613
|
+
result.reasoning.push({
|
|
1614
|
+
suggestion: `use: ${useItem}`,
|
|
1615
|
+
reason: match.reason,
|
|
1616
|
+
trigger: match.trigger,
|
|
1617
|
+
matchedValue: match.matchedValue
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
if (rule.suggest.skills) {
|
|
1623
|
+
for (const skill of rule.suggest.skills) {
|
|
1624
|
+
if (!suggestedSkills.has(skill)) {
|
|
1625
|
+
suggestedSkills.add(skill);
|
|
1626
|
+
result.skills.push(skill);
|
|
1627
|
+
result.reasoning.push({
|
|
1628
|
+
suggestion: `skill: ${skill}`,
|
|
1629
|
+
reason: match.reason,
|
|
1630
|
+
trigger: match.trigger,
|
|
1631
|
+
matchedValue: match.matchedValue
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
return result;
|
|
1639
|
+
}
|
|
1640
|
+
function matchCondition(condition, context) {
|
|
1641
|
+
if (condition.always) {
|
|
1642
|
+
return {
|
|
1643
|
+
matches: true,
|
|
1644
|
+
reason: "Default recommendation",
|
|
1645
|
+
trigger: "always"
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
if (condition.files) {
|
|
1649
|
+
for (const file of condition.files) {
|
|
1650
|
+
if (context.files.includes(file)) {
|
|
1651
|
+
return {
|
|
1652
|
+
matches: true,
|
|
1653
|
+
reason: `Detected file: ${file}`,
|
|
1654
|
+
trigger: "file",
|
|
1655
|
+
matchedValue: file
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (condition.dependencies) {
|
|
1661
|
+
for (const dep of condition.dependencies) {
|
|
1662
|
+
if (context.dependencies.includes(dep)) {
|
|
1663
|
+
return {
|
|
1664
|
+
matches: true,
|
|
1665
|
+
reason: `Detected dependency: ${dep}`,
|
|
1666
|
+
trigger: "dependency",
|
|
1667
|
+
matchedValue: dep
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
if (condition.languages) {
|
|
1673
|
+
for (const lang of condition.languages) {
|
|
1674
|
+
if (context.languages.includes(lang)) {
|
|
1675
|
+
return {
|
|
1676
|
+
matches: true,
|
|
1677
|
+
reason: `Detected language: ${lang}`,
|
|
1678
|
+
trigger: "language",
|
|
1679
|
+
matchedValue: lang
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
if (condition.frameworks) {
|
|
1685
|
+
for (const framework of condition.frameworks) {
|
|
1686
|
+
if (context.frameworks.includes(framework)) {
|
|
1687
|
+
return {
|
|
1688
|
+
matches: true,
|
|
1689
|
+
reason: `Detected framework: ${framework}`,
|
|
1690
|
+
trigger: "framework",
|
|
1691
|
+
matchedValue: framework
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
return {
|
|
1697
|
+
matches: false,
|
|
1698
|
+
reason: "",
|
|
1699
|
+
trigger: "always"
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function formatSuggestionResult(result) {
|
|
1703
|
+
const lines = [];
|
|
1704
|
+
if (result.inherit) {
|
|
1705
|
+
lines.push(`\u{1F4E6} Inherit: ${result.inherit}`);
|
|
1706
|
+
}
|
|
1707
|
+
if (result.use.length > 0) {
|
|
1708
|
+
lines.push(`\u{1F527} Use: ${result.use.join(", ")}`);
|
|
1709
|
+
}
|
|
1710
|
+
if (result.skills.length > 0) {
|
|
1711
|
+
lines.push(`\u26A1 Skills: ${result.skills.join(", ")}`);
|
|
1712
|
+
}
|
|
1713
|
+
if (result.reasoning.length > 0) {
|
|
1714
|
+
lines.push("");
|
|
1715
|
+
lines.push("Reasoning:");
|
|
1716
|
+
for (const r of result.reasoning) {
|
|
1717
|
+
lines.push(` \u2022 ${r.suggestion} (${r.reason})`);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
return lines;
|
|
1721
|
+
}
|
|
1722
|
+
function createSuggestionChoices(manifest, result) {
|
|
1723
|
+
const choices = [];
|
|
1724
|
+
if (result.inherit) {
|
|
1725
|
+
const entry = manifest.catalog.find((e) => e.id === result.inherit);
|
|
1726
|
+
choices.push({
|
|
1727
|
+
name: `${result.inherit} (inherit)`,
|
|
1728
|
+
value: `inherit:${result.inherit}`,
|
|
1729
|
+
checked: true,
|
|
1730
|
+
description: entry?.description
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
for (const use of result.use) {
|
|
1734
|
+
const entry = manifest.catalog.find((e) => e.id === use);
|
|
1735
|
+
choices.push({
|
|
1736
|
+
name: `${use} (use)`,
|
|
1737
|
+
value: `use:${use}`,
|
|
1738
|
+
checked: true,
|
|
1739
|
+
description: entry?.description
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
for (const skill of result.skills) {
|
|
1743
|
+
const entry = manifest.catalog.find((e) => e.id === skill);
|
|
1744
|
+
choices.push({
|
|
1745
|
+
name: `${skill} (skill)`,
|
|
1746
|
+
value: `skill:${skill}`,
|
|
1747
|
+
checked: true,
|
|
1748
|
+
description: entry?.description
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
return choices;
|
|
1752
|
+
}
|
|
1753
|
+
function parseSelectedChoices(selected) {
|
|
1754
|
+
const result = {
|
|
1755
|
+
use: [],
|
|
1756
|
+
skills: []
|
|
1757
|
+
};
|
|
1758
|
+
for (const choice of selected) {
|
|
1759
|
+
if (choice.startsWith("inherit:")) {
|
|
1760
|
+
result.inherit = choice.slice("inherit:".length);
|
|
1761
|
+
} else if (choice.startsWith("use:")) {
|
|
1762
|
+
result.use.push(choice.slice("use:".length));
|
|
1763
|
+
} else if (choice.startsWith("skill:")) {
|
|
1764
|
+
result.skills.push(choice.slice("skill:".length));
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return result;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
862
1770
|
// packages/cli/src/commands/init.ts
|
|
863
1771
|
var __filename = fileURLToPath(import.meta.url);
|
|
864
|
-
var __dirname =
|
|
1772
|
+
var __dirname = dirname3(__filename);
|
|
865
1773
|
async function initCommand(options, services = createDefaultServices()) {
|
|
866
1774
|
const { fs: fs4 } = services;
|
|
867
1775
|
if (fs4.existsSync("promptscript.yaml") && !options.force) {
|
|
@@ -873,11 +1781,22 @@ async function initCommand(options, services = createDefaultServices()) {
|
|
|
873
1781
|
const projectInfo = await detectProject(services);
|
|
874
1782
|
const aiToolsDetection = await detectAITools(services);
|
|
875
1783
|
const prettierConfigPath = findPrettierConfig(process.cwd());
|
|
1784
|
+
let manifest;
|
|
1785
|
+
try {
|
|
1786
|
+
const registryPath = options.registry ?? "./registry";
|
|
1787
|
+
const { manifest: loadedManifest } = await loadManifest({ registryPath }, services);
|
|
1788
|
+
manifest = loadedManifest;
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
if (!(error instanceof ManifestLoadError)) {
|
|
1791
|
+
throw error;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
876
1794
|
const config = await resolveConfig(
|
|
877
1795
|
options,
|
|
878
1796
|
projectInfo,
|
|
879
1797
|
aiToolsDetection,
|
|
880
1798
|
prettierConfigPath,
|
|
1799
|
+
manifest,
|
|
881
1800
|
services
|
|
882
1801
|
);
|
|
883
1802
|
const spinner = createSpinner("Creating PromptScript configuration...").start();
|
|
@@ -886,11 +1805,45 @@ async function initCommand(options, services = createDefaultServices()) {
|
|
|
886
1805
|
await fs4.writeFile("promptscript.yaml", configContent, "utf-8");
|
|
887
1806
|
const projectPsContent = generateProjectPs(config, projectInfo);
|
|
888
1807
|
await fs4.writeFile(".promptscript/project.prs", projectPsContent, "utf-8");
|
|
1808
|
+
const installedSkillPaths = [];
|
|
1809
|
+
if (options.migrate) {
|
|
1810
|
+
if (config.targets.includes("claude")) {
|
|
1811
|
+
await fs4.mkdir(".claude/skills/migrate-to-promptscript", { recursive: true });
|
|
1812
|
+
await fs4.writeFile(
|
|
1813
|
+
".claude/skills/migrate-to-promptscript/SKILL.md",
|
|
1814
|
+
MIGRATE_SKILL_CLAUDE,
|
|
1815
|
+
"utf-8"
|
|
1816
|
+
);
|
|
1817
|
+
installedSkillPaths.push(".claude/skills/migrate-to-promptscript/SKILL.md");
|
|
1818
|
+
}
|
|
1819
|
+
if (config.targets.includes("github")) {
|
|
1820
|
+
await fs4.mkdir(".github/skills/migrate-to-promptscript", { recursive: true });
|
|
1821
|
+
await fs4.writeFile(
|
|
1822
|
+
".github/skills/migrate-to-promptscript/SKILL.md",
|
|
1823
|
+
MIGRATE_SKILL_GITHUB,
|
|
1824
|
+
"utf-8"
|
|
1825
|
+
);
|
|
1826
|
+
installedSkillPaths.push(".github/skills/migrate-to-promptscript/SKILL.md");
|
|
1827
|
+
}
|
|
1828
|
+
if (config.targets.includes("cursor")) {
|
|
1829
|
+
await fs4.mkdir(".cursor/commands", { recursive: true });
|
|
1830
|
+
await fs4.writeFile(".cursor/commands/migrate.md", MIGRATE_SKILL_CURSOR, "utf-8");
|
|
1831
|
+
installedSkillPaths.push(".cursor/commands/migrate.md");
|
|
1832
|
+
}
|
|
1833
|
+
if (config.targets.includes("antigravity")) {
|
|
1834
|
+
await fs4.mkdir(".agent/rules", { recursive: true });
|
|
1835
|
+
await fs4.writeFile(".agent/rules/migrate.md", MIGRATE_SKILL_ANTIGRAVITY, "utf-8");
|
|
1836
|
+
installedSkillPaths.push(".agent/rules/migrate.md");
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
889
1839
|
spinner.succeed("PromptScript initialized");
|
|
890
1840
|
ConsoleOutput.newline();
|
|
891
1841
|
console.log("Created:");
|
|
892
1842
|
ConsoleOutput.success("promptscript.yaml");
|
|
893
1843
|
ConsoleOutput.success(".promptscript/project.prs");
|
|
1844
|
+
for (const skillPath of installedSkillPaths) {
|
|
1845
|
+
ConsoleOutput.success(skillPath);
|
|
1846
|
+
}
|
|
894
1847
|
ConsoleOutput.newline();
|
|
895
1848
|
console.log("Configuration:");
|
|
896
1849
|
ConsoleOutput.muted(` Project: ${config.projectId}`);
|
|
@@ -899,7 +1852,14 @@ async function initCommand(options, services = createDefaultServices()) {
|
|
|
899
1852
|
ConsoleOutput.muted(` Inherit: ${config.inherit}`);
|
|
900
1853
|
}
|
|
901
1854
|
if (config.registry) {
|
|
902
|
-
|
|
1855
|
+
if (config.registry.type === "git") {
|
|
1856
|
+
ConsoleOutput.muted(` Registry: ${config.registry.url} (${config.registry.ref})`);
|
|
1857
|
+
} else {
|
|
1858
|
+
ConsoleOutput.muted(` Registry: ${config.registry.path} (local)`);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
if (config.use && config.use.length > 0) {
|
|
1862
|
+
ConsoleOutput.muted(` Use: ${config.use.join(", ")}`);
|
|
903
1863
|
}
|
|
904
1864
|
ConsoleOutput.newline();
|
|
905
1865
|
if (config.prettierConfigPath) {
|
|
@@ -911,17 +1871,35 @@ async function initCommand(options, services = createDefaultServices()) {
|
|
|
911
1871
|
}
|
|
912
1872
|
ConsoleOutput.newline();
|
|
913
1873
|
console.log("Next steps:");
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1874
|
+
if (options.migrate && installedSkillPaths.length > 0) {
|
|
1875
|
+
ConsoleOutput.muted("1. Use the migration skill to convert existing instructions:");
|
|
1876
|
+
if (config.targets.includes("claude")) {
|
|
1877
|
+
ConsoleOutput.muted(" Claude Code: /migrate");
|
|
1878
|
+
}
|
|
1879
|
+
if (config.targets.includes("github")) {
|
|
1880
|
+
ConsoleOutput.muted(" GitHub Copilot: @workspace /migrate");
|
|
1881
|
+
}
|
|
1882
|
+
if (config.targets.includes("cursor")) {
|
|
1883
|
+
ConsoleOutput.muted(" Cursor: /migrate");
|
|
1884
|
+
}
|
|
1885
|
+
if (config.targets.includes("antigravity")) {
|
|
1886
|
+
ConsoleOutput.muted(' Antigravity: Ask to "migrate to PromptScript"');
|
|
1887
|
+
}
|
|
1888
|
+
ConsoleOutput.muted("2. Review generated .promptscript/project.prs");
|
|
1889
|
+
ConsoleOutput.muted("3. Run: prs compile");
|
|
1890
|
+
} else {
|
|
1891
|
+
ConsoleOutput.muted("1. Edit .promptscript/project.prs to customize your AI instructions");
|
|
1892
|
+
ConsoleOutput.muted("2. Run: prs compile");
|
|
1893
|
+
if (hasMigrationCandidates(aiToolsDetection)) {
|
|
1894
|
+
const migrationHint = formatMigrationHint(aiToolsDetection);
|
|
1895
|
+
for (const line of migrationHint) {
|
|
1896
|
+
if (line.startsWith("\u{1F4CB}") || line.includes("migrated") || line.includes("See:") || line.includes("Or use")) {
|
|
1897
|
+
ConsoleOutput.info(line.replace(/^\s+/, ""));
|
|
1898
|
+
} else if (line.trim().startsWith("\u2022")) {
|
|
1899
|
+
ConsoleOutput.muted(line);
|
|
1900
|
+
} else if (line.trim()) {
|
|
1901
|
+
console.log(line);
|
|
1902
|
+
}
|
|
925
1903
|
}
|
|
926
1904
|
}
|
|
927
1905
|
}
|
|
@@ -935,23 +1913,46 @@ async function initCommand(options, services = createDefaultServices()) {
|
|
|
935
1913
|
process.exit(1);
|
|
936
1914
|
}
|
|
937
1915
|
}
|
|
938
|
-
async function resolveConfig(options, projectInfo, aiToolsDetection, prettierConfigPath, services) {
|
|
1916
|
+
async function resolveConfig(options, projectInfo, aiToolsDetection, prettierConfigPath, manifest, services) {
|
|
939
1917
|
if (options.yes) {
|
|
1918
|
+
let inherit = options.inherit;
|
|
1919
|
+
let use;
|
|
1920
|
+
let activeManifest = manifest;
|
|
1921
|
+
if (!activeManifest) {
|
|
1922
|
+
try {
|
|
1923
|
+
const { manifest: remoteManifest } = await loadManifestFromUrl();
|
|
1924
|
+
activeManifest = remoteManifest;
|
|
1925
|
+
} catch {
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
if (activeManifest) {
|
|
1929
|
+
const context = await buildProjectContext(projectInfo, services);
|
|
1930
|
+
const suggestions = calculateSuggestions(activeManifest, context);
|
|
1931
|
+
if (!inherit && suggestions.inherit) {
|
|
1932
|
+
inherit = suggestions.inherit;
|
|
1933
|
+
}
|
|
1934
|
+
if (suggestions.use.length > 0) {
|
|
1935
|
+
use = suggestions.use;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
const registry = options.registry ? { type: "local", path: options.registry } : { type: "git", url: OFFICIAL_REGISTRY.url, ref: OFFICIAL_REGISTRY.branch };
|
|
940
1939
|
return {
|
|
941
1940
|
projectId: options.name ?? projectInfo.name,
|
|
942
1941
|
team: options.team,
|
|
943
|
-
inherit
|
|
944
|
-
|
|
1942
|
+
inherit,
|
|
1943
|
+
use,
|
|
1944
|
+
registry,
|
|
945
1945
|
targets: options.targets ?? getSuggestedTargets(aiToolsDetection),
|
|
946
1946
|
prettierConfigPath
|
|
947
1947
|
};
|
|
948
1948
|
}
|
|
949
1949
|
if (!options.interactive && options.name && options.targets) {
|
|
1950
|
+
const registry = options.registry ? { type: "local", path: options.registry } : void 0;
|
|
950
1951
|
return {
|
|
951
1952
|
projectId: options.name,
|
|
952
1953
|
team: options.team,
|
|
953
1954
|
inherit: options.inherit,
|
|
954
|
-
registry
|
|
1955
|
+
registry,
|
|
955
1956
|
targets: options.targets,
|
|
956
1957
|
prettierConfigPath
|
|
957
1958
|
};
|
|
@@ -961,10 +1962,11 @@ async function resolveConfig(options, projectInfo, aiToolsDetection, prettierCon
|
|
|
961
1962
|
projectInfo,
|
|
962
1963
|
aiToolsDetection,
|
|
963
1964
|
prettierConfigPath,
|
|
1965
|
+
manifest,
|
|
964
1966
|
services
|
|
965
1967
|
);
|
|
966
1968
|
}
|
|
967
|
-
async function runInteractivePrompts(options, projectInfo, aiToolsDetection, prettierConfigPath, services) {
|
|
1969
|
+
async function runInteractivePrompts(options, projectInfo, aiToolsDetection, prettierConfigPath, manifest, services) {
|
|
968
1970
|
const { prompts: prompts2 } = services;
|
|
969
1971
|
ConsoleOutput.newline();
|
|
970
1972
|
console.log("\u{1F680} PromptScript Setup");
|
|
@@ -990,33 +1992,124 @@ async function runInteractivePrompts(options, projectInfo, aiToolsDetection, pre
|
|
|
990
1992
|
message: "Project name:",
|
|
991
1993
|
default: options.name ?? projectInfo.name
|
|
992
1994
|
});
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1995
|
+
let registry;
|
|
1996
|
+
let activeManifest = manifest;
|
|
1997
|
+
const registryChoice = await prompts2.select({
|
|
1998
|
+
message: "Registry configuration:",
|
|
1999
|
+
choices: [
|
|
2000
|
+
{
|
|
2001
|
+
name: `\u{1F4E6} Use official PromptScript Registry (${OFFICIAL_REGISTRY.url})`,
|
|
2002
|
+
value: "official"
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
name: "\u{1F517} Connect to a custom Git registry",
|
|
2006
|
+
value: "custom-git"
|
|
2007
|
+
},
|
|
2008
|
+
{
|
|
2009
|
+
name: "\u{1F4C1} Use a local registry directory",
|
|
2010
|
+
value: "local"
|
|
2011
|
+
},
|
|
2012
|
+
{
|
|
2013
|
+
name: "\u23ED\uFE0F Skip registry (configure later)",
|
|
2014
|
+
value: "skip"
|
|
2015
|
+
}
|
|
2016
|
+
],
|
|
2017
|
+
default: manifest ? "official" : "skip"
|
|
996
2018
|
});
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
2019
|
+
if (registryChoice === "official") {
|
|
2020
|
+
registry = {
|
|
2021
|
+
type: "git",
|
|
2022
|
+
url: OFFICIAL_REGISTRY.url,
|
|
2023
|
+
ref: OFFICIAL_REGISTRY.branch
|
|
2024
|
+
};
|
|
2025
|
+
if (!activeManifest) {
|
|
2026
|
+
try {
|
|
2027
|
+
ConsoleOutput.muted("Fetching registry manifest...");
|
|
2028
|
+
const { manifest: remoteManifest } = await loadManifestFromUrl();
|
|
2029
|
+
activeManifest = remoteManifest;
|
|
2030
|
+
ConsoleOutput.success(
|
|
2031
|
+
`Loaded ${remoteManifest.catalog.length} configurations from official registry`
|
|
2032
|
+
);
|
|
2033
|
+
} catch {
|
|
2034
|
+
ConsoleOutput.warn("Could not fetch registry manifest - suggestions will be limited");
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
} else if (registryChoice === "custom-git") {
|
|
2038
|
+
const gitUrl = await prompts2.input({
|
|
2039
|
+
message: "Git repository URL:",
|
|
2040
|
+
default: "https://github.com/your-org/your-registry.git",
|
|
1002
2041
|
validate: (value) => {
|
|
1003
|
-
if (!value
|
|
1004
|
-
return "
|
|
2042
|
+
if (!isValidGitUrl(value)) {
|
|
2043
|
+
return "Please enter a valid Git repository URL (https:// or git@)";
|
|
1005
2044
|
}
|
|
1006
2045
|
return true;
|
|
1007
2046
|
}
|
|
1008
2047
|
});
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
2048
|
+
const gitRef = await prompts2.input({
|
|
2049
|
+
message: "Branch or tag:",
|
|
2050
|
+
default: "main"
|
|
2051
|
+
});
|
|
2052
|
+
registry = {
|
|
2053
|
+
type: "git",
|
|
2054
|
+
url: gitUrl,
|
|
2055
|
+
ref: gitRef
|
|
2056
|
+
};
|
|
2057
|
+
} else if (registryChoice === "local") {
|
|
2058
|
+
const localPath = await prompts2.input({
|
|
2059
|
+
message: "Local registry path:",
|
|
1018
2060
|
default: options.registry ?? "./registry"
|
|
1019
2061
|
});
|
|
2062
|
+
registry = {
|
|
2063
|
+
type: "local",
|
|
2064
|
+
path: localPath
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
let inherit = options.inherit;
|
|
2068
|
+
let use;
|
|
2069
|
+
if (activeManifest) {
|
|
2070
|
+
const context = await buildProjectContext(projectInfo, services);
|
|
2071
|
+
const suggestions = calculateSuggestions(activeManifest, context);
|
|
2072
|
+
if (suggestions.inherit || suggestions.use.length > 0) {
|
|
2073
|
+
ConsoleOutput.newline();
|
|
2074
|
+
console.log("\u{1F4E6} Suggested configurations based on your project:");
|
|
2075
|
+
const suggestionLines = formatSuggestionResult(suggestions);
|
|
2076
|
+
for (const line of suggestionLines) {
|
|
2077
|
+
ConsoleOutput.muted(` ${line}`);
|
|
2078
|
+
}
|
|
2079
|
+
ConsoleOutput.newline();
|
|
2080
|
+
const choices = createSuggestionChoices(activeManifest, suggestions);
|
|
2081
|
+
if (choices.length > 0) {
|
|
2082
|
+
const selected = await prompts2.checkbox({
|
|
2083
|
+
message: "Select configurations to use:",
|
|
2084
|
+
choices: choices.map((c) => ({
|
|
2085
|
+
name: c.description ? `${c.name} - ${c.description}` : c.name,
|
|
2086
|
+
value: c.value,
|
|
2087
|
+
checked: c.checked
|
|
2088
|
+
}))
|
|
2089
|
+
});
|
|
2090
|
+
const parsed = parseSelectedChoices(selected);
|
|
2091
|
+
inherit = parsed.inherit;
|
|
2092
|
+
use = parsed.use.length > 0 ? parsed.use : void 0;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
if (!inherit) {
|
|
2097
|
+
const wantsInherit = await prompts2.confirm({
|
|
2098
|
+
message: "Do you want to inherit from a parent configuration?",
|
|
2099
|
+
default: false
|
|
2100
|
+
});
|
|
2101
|
+
if (wantsInherit) {
|
|
2102
|
+
inherit = await prompts2.input({
|
|
2103
|
+
message: "Inheritance path (e.g., @stacks/react):",
|
|
2104
|
+
default: options.inherit ?? "@stacks/react",
|
|
2105
|
+
validate: (value) => {
|
|
2106
|
+
if (!value.startsWith("@")) {
|
|
2107
|
+
return "Inheritance path should start with @";
|
|
2108
|
+
}
|
|
2109
|
+
return true;
|
|
2110
|
+
}
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
1020
2113
|
}
|
|
1021
2114
|
const suggestedTargets = getSuggestedTargets(aiToolsDetection);
|
|
1022
2115
|
const allTargets = getAllTargets();
|
|
@@ -1045,6 +2138,7 @@ async function runInteractivePrompts(options, projectInfo, aiToolsDetection, pre
|
|
|
1045
2138
|
projectId,
|
|
1046
2139
|
team,
|
|
1047
2140
|
inherit,
|
|
2141
|
+
use,
|
|
1048
2142
|
registry,
|
|
1049
2143
|
targets,
|
|
1050
2144
|
prettierConfigPath
|
|
@@ -1068,13 +2162,34 @@ function generateConfig(config) {
|
|
|
1068
2162
|
if (config.inherit) {
|
|
1069
2163
|
lines.push(`inherit: '${config.inherit}'`);
|
|
1070
2164
|
} else {
|
|
1071
|
-
lines.push("# inherit: '@
|
|
2165
|
+
lines.push("# inherit: '@stacks/react'");
|
|
2166
|
+
}
|
|
2167
|
+
lines.push("");
|
|
2168
|
+
if (config.use && config.use.length > 0) {
|
|
2169
|
+
lines.push("use:");
|
|
2170
|
+
for (const useItem of config.use) {
|
|
2171
|
+
lines.push(` - '${useItem}'`);
|
|
2172
|
+
}
|
|
2173
|
+
} else {
|
|
2174
|
+
lines.push("# use:", "# - '@fragments/testing'", "# - '@fragments/typescript'");
|
|
1072
2175
|
}
|
|
1073
2176
|
lines.push("");
|
|
1074
2177
|
if (config.registry) {
|
|
1075
|
-
|
|
2178
|
+
if (config.registry.type === "git") {
|
|
2179
|
+
lines.push("registry:", " git:", ` url: '${config.registry.url}'`);
|
|
2180
|
+
if (config.registry.ref) {
|
|
2181
|
+
lines.push(` ref: '${config.registry.ref}'`);
|
|
2182
|
+
}
|
|
2183
|
+
} else {
|
|
2184
|
+
lines.push("registry:", ` path: '${config.registry.path}'`);
|
|
2185
|
+
}
|
|
1076
2186
|
} else {
|
|
1077
|
-
lines.push(
|
|
2187
|
+
lines.push(
|
|
2188
|
+
"# registry:",
|
|
2189
|
+
"# git:",
|
|
2190
|
+
"# url: 'https://github.com/mrwogu/promptscript-registry.git'",
|
|
2191
|
+
"# ref: 'main'"
|
|
2192
|
+
);
|
|
1078
2193
|
}
|
|
1079
2194
|
lines.push("", "targets:");
|
|
1080
2195
|
for (const target of config.targets) {
|
|
@@ -1095,7 +2210,13 @@ function generateConfig(config) {
|
|
|
1095
2210
|
return lines.join("\n");
|
|
1096
2211
|
}
|
|
1097
2212
|
function generateProjectPs(config, projectInfo) {
|
|
1098
|
-
const inheritLine = config.inherit ? `@inherit ${config.inherit}` : "# @inherit @
|
|
2213
|
+
const inheritLine = config.inherit ? `@inherit ${config.inherit}` : "# @inherit @stacks/react";
|
|
2214
|
+
let useLines = "";
|
|
2215
|
+
if (config.use && config.use.length > 0) {
|
|
2216
|
+
useLines = config.use.map((u) => `@use ${u}`).join("\n");
|
|
2217
|
+
} else {
|
|
2218
|
+
useLines = "# @use @fragments/testing\n# @use @fragments/typescript";
|
|
2219
|
+
}
|
|
1099
2220
|
const languagesLine = projectInfo.languages.length > 0 ? ` languages: [${projectInfo.languages.join(", ")}]` : " # languages: [typescript]";
|
|
1100
2221
|
const frameworksLine = projectInfo.frameworks.length > 0 ? ` frameworks: [${projectInfo.frameworks.join(", ")}]` : " # frameworks: []";
|
|
1101
2222
|
const syntaxVersion = getPackageVersion(__dirname, "./package.json");
|
|
@@ -1108,11 +2229,12 @@ function generateProjectPs(config, projectInfo) {
|
|
|
1108
2229
|
}
|
|
1109
2230
|
|
|
1110
2231
|
${inheritLine}
|
|
2232
|
+
${useLines}
|
|
1111
2233
|
|
|
1112
2234
|
@identity {
|
|
1113
2235
|
"""
|
|
1114
2236
|
You are working on the ${config.projectId} project.
|
|
1115
|
-
|
|
2237
|
+
|
|
1116
2238
|
[Describe your project here]
|
|
1117
2239
|
"""
|
|
1118
2240
|
}
|
|
@@ -1141,7 +2263,7 @@ ${frameworksLine}
|
|
|
1141
2263
|
}
|
|
1142
2264
|
|
|
1143
2265
|
// packages/cli/src/commands/compile.ts
|
|
1144
|
-
import { resolve as resolve4, dirname as
|
|
2266
|
+
import { resolve as resolve4, dirname as dirname6 } from "path";
|
|
1145
2267
|
import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile6 } from "fs/promises";
|
|
1146
2268
|
import { existsSync as existsSync7 } from "fs";
|
|
1147
2269
|
import chokidar from "chokidar";
|
|
@@ -1149,7 +2271,7 @@ import chokidar from "chokidar";
|
|
|
1149
2271
|
// packages/cli/src/config/loader.ts
|
|
1150
2272
|
import { readFile as readFile3 } from "fs/promises";
|
|
1151
2273
|
import { existsSync as existsSync3 } from "fs";
|
|
1152
|
-
import { parse as
|
|
2274
|
+
import { parse as parseYaml3 } from "yaml";
|
|
1153
2275
|
var CONFIG_FILES = [
|
|
1154
2276
|
"promptscript.yaml",
|
|
1155
2277
|
"promptscript.yml",
|
|
@@ -1196,7 +2318,7 @@ async function loadConfig(customPath) {
|
|
|
1196
2318
|
let content = await readFile3(configFile, "utf-8");
|
|
1197
2319
|
content = interpolateEnvVars(content);
|
|
1198
2320
|
try {
|
|
1199
|
-
return
|
|
2321
|
+
return parseYaml3(content);
|
|
1200
2322
|
} catch (error) {
|
|
1201
2323
|
const message = error instanceof Error ? error.message : "Unknown parse error";
|
|
1202
2324
|
throw new Error(`Failed to parse ${configFile}: ${message}`);
|
|
@@ -1365,10 +2487,26 @@ ${this.convention.rootWrapper.end}`;
|
|
|
1365
2487
|
/**
|
|
1366
2488
|
* Escape markdown special characters for Prettier compatibility.
|
|
1367
2489
|
* - Escapes __ to \_\_ (to avoid emphasis)
|
|
1368
|
-
* - Escapes
|
|
2490
|
+
* - Escapes * in glob patterns (like packages/*) outside backticks
|
|
1369
2491
|
*/
|
|
1370
2492
|
escapeMarkdownSpecialChars(content) {
|
|
1371
|
-
return content.
|
|
2493
|
+
return content.split("\n").map((line) => {
|
|
2494
|
+
let result = line.replace(/__/g, "\\_\\_");
|
|
2495
|
+
result = this.escapeGlobAsteriskOutsideBackticks(result);
|
|
2496
|
+
return result;
|
|
2497
|
+
}).join("\n");
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* Escape glob asterisks (like packages/* or .cursor/rules/*.md) outside of backticks.
|
|
2501
|
+
*/
|
|
2502
|
+
escapeGlobAsteriskOutsideBackticks(line) {
|
|
2503
|
+
const parts = line.split("`");
|
|
2504
|
+
return parts.map((part, index) => {
|
|
2505
|
+
if (index % 2 === 0) {
|
|
2506
|
+
return part.replace(/\/\*/g, "/\\*");
|
|
2507
|
+
}
|
|
2508
|
+
return part;
|
|
2509
|
+
}).join("`");
|
|
1372
2510
|
}
|
|
1373
2511
|
indentContent(content, renderer, level) {
|
|
1374
2512
|
const indent = renderer.indent ?? "";
|
|
@@ -1540,6 +2678,7 @@ var BaseFormatter = class {
|
|
|
1540
2678
|
* - Trims trailing whitespace from lines
|
|
1541
2679
|
* - Normalizes markdown table formatting
|
|
1542
2680
|
* - Adds blank lines before lists when preceded by text
|
|
2681
|
+
* - Adds blank lines before code blocks when preceded by text
|
|
1543
2682
|
* - Escapes markdown special characters in paths
|
|
1544
2683
|
*/
|
|
1545
2684
|
normalizeMarkdownForPrettier(content) {
|
|
@@ -1548,8 +2687,13 @@ var BaseFormatter = class {
|
|
|
1548
2687
|
let inCodeBlock = false;
|
|
1549
2688
|
for (const line of lines) {
|
|
1550
2689
|
const trimmed = line.trimEnd();
|
|
1551
|
-
if (trimmed.startsWith("```")) {
|
|
2690
|
+
if (trimmed.trimStart().startsWith("```")) {
|
|
1552
2691
|
inCodeBlock = !inCodeBlock;
|
|
2692
|
+
if (trimmed.length > 0) {
|
|
2693
|
+
const match2 = line.match(/^(\s*)/);
|
|
2694
|
+
const leadingSpaces2 = match2?.[1]?.length ?? 0;
|
|
2695
|
+
minIndent = Math.min(minIndent, leadingSpaces2);
|
|
2696
|
+
}
|
|
1553
2697
|
continue;
|
|
1554
2698
|
}
|
|
1555
2699
|
if (inCodeBlock) continue;
|
|
@@ -1565,17 +2709,30 @@ var BaseFormatter = class {
|
|
|
1565
2709
|
inCodeBlock = false;
|
|
1566
2710
|
for (const line of lines) {
|
|
1567
2711
|
const trimmedLine = line.trimEnd();
|
|
1568
|
-
|
|
2712
|
+
const unindentedLine = minIndent > 0 ? trimmedLine.slice(minIndent) : trimmedLine;
|
|
2713
|
+
const isCodeBlockMarker = trimmedLine.trimStart().startsWith("```");
|
|
2714
|
+
const isCodeBlockStart = isCodeBlockMarker && !inCodeBlock;
|
|
2715
|
+
if (isCodeBlockMarker) {
|
|
1569
2716
|
inCodeBlock = !inCodeBlock;
|
|
1570
2717
|
}
|
|
1571
|
-
const unindentedLine = minIndent > 0 ? trimmedLine.slice(minIndent) : trimmedLine;
|
|
1572
2718
|
if (inCodeBlock || unindentedLine.startsWith("```")) {
|
|
2719
|
+
if (isCodeBlockStart) {
|
|
2720
|
+
const prevLine2 = result.length > 0 ? result[result.length - 1] ?? "" : "";
|
|
2721
|
+
if (prevLine2.trim() && !prevLine2.startsWith("#")) {
|
|
2722
|
+
result.push("");
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
1573
2725
|
result.push(unindentedLine);
|
|
1574
2726
|
continue;
|
|
1575
2727
|
}
|
|
1576
2728
|
let processedLine = unindentedLine;
|
|
1577
2729
|
processedLine = processedLine.replace(/__([^_]+)__/g, "\\_\\_$1\\_\\_");
|
|
1578
|
-
processedLine =
|
|
2730
|
+
processedLine = this.escapeGlobAsteriskOutsideBackticks(processedLine);
|
|
2731
|
+
const prevLine = result.length > 0 ? result[result.length - 1] : "";
|
|
2732
|
+
const isHeader = prevLine?.trimStart().startsWith("#");
|
|
2733
|
+
if (isHeader && processedLine.trim()) {
|
|
2734
|
+
result.push("");
|
|
2735
|
+
}
|
|
1579
2736
|
if (processedLine.trimStart().startsWith("|") && processedLine.trimEnd().endsWith("|")) {
|
|
1580
2737
|
inTable = true;
|
|
1581
2738
|
tableLines.push(processedLine.trim());
|
|
@@ -1585,9 +2742,14 @@ var BaseFormatter = class {
|
|
|
1585
2742
|
tableLines = [];
|
|
1586
2743
|
inTable = false;
|
|
1587
2744
|
}
|
|
1588
|
-
const prevLine = result.length > 0 ? result[result.length - 1] : "";
|
|
1589
2745
|
const isListItem = processedLine.trimStart().startsWith("- ");
|
|
1590
|
-
|
|
2746
|
+
const isNumberedItem = /^\d+\.\s/.test(processedLine.trimStart());
|
|
2747
|
+
const prevLineTrimmed = prevLine?.trimStart() ?? "";
|
|
2748
|
+
const isPrevListItem = prevLineTrimmed.startsWith("- ") || /^\d+\.\s/.test(prevLineTrimmed);
|
|
2749
|
+
const prevEndsWithColon = prevLine?.trimEnd().endsWith(":") ?? false;
|
|
2750
|
+
if ((isListItem || isNumberedItem) && prevLine && !isPrevListItem && !isHeader) {
|
|
2751
|
+
result.push("");
|
|
2752
|
+
} else if ((isListItem || isNumberedItem) && prevEndsWithColon) {
|
|
1591
2753
|
result.push("");
|
|
1592
2754
|
}
|
|
1593
2755
|
result.push(processedLine);
|
|
@@ -1598,6 +2760,19 @@ var BaseFormatter = class {
|
|
|
1598
2760
|
}
|
|
1599
2761
|
return result.join("\n");
|
|
1600
2762
|
}
|
|
2763
|
+
/**
|
|
2764
|
+
* Escape glob asterisks (like packages/* or .cursor/rules/*.md) outside of backticks.
|
|
2765
|
+
* Prettier escapes these to prevent them from being interpreted as emphasis markers.
|
|
2766
|
+
*/
|
|
2767
|
+
escapeGlobAsteriskOutsideBackticks(line) {
|
|
2768
|
+
const parts = line.split("`");
|
|
2769
|
+
return parts.map((part, index) => {
|
|
2770
|
+
if (index % 2 === 0) {
|
|
2771
|
+
return part.replace(/\/\*/g, "/\\*");
|
|
2772
|
+
}
|
|
2773
|
+
return part;
|
|
2774
|
+
}).join("`");
|
|
2775
|
+
}
|
|
1601
2776
|
/**
|
|
1602
2777
|
* Strip all leading indentation from markdown content.
|
|
1603
2778
|
* Used for AGENTS.md where content from multiple sources has inconsistent indentation.
|
|
@@ -1611,7 +2786,7 @@ var BaseFormatter = class {
|
|
|
1611
2786
|
const trimmedEnd = line.trimEnd();
|
|
1612
2787
|
if (trimmedEnd.trimStart().startsWith("```")) {
|
|
1613
2788
|
if (!inCodeBlock) {
|
|
1614
|
-
const prevLine2 = result.length > 0 ? result[result.length - 1] : "";
|
|
2789
|
+
const prevLine2 = result.length > 0 ? result[result.length - 1] ?? "" : "";
|
|
1615
2790
|
if (prevLine2 && prevLine2.trim()) {
|
|
1616
2791
|
result.push("");
|
|
1617
2792
|
}
|
|
@@ -1626,9 +2801,14 @@ var BaseFormatter = class {
|
|
|
1626
2801
|
}
|
|
1627
2802
|
let stripped = trimmedEnd.trimStart();
|
|
1628
2803
|
stripped = stripped.replace(/__/g, "\\_\\_");
|
|
1629
|
-
stripped =
|
|
1630
|
-
const prevLine = result.length > 0 ? result[result.length - 1] : "";
|
|
1631
|
-
|
|
2804
|
+
stripped = this.escapeGlobAsteriskOutsideBackticks(stripped);
|
|
2805
|
+
const prevLine = result.length > 0 ? result[result.length - 1] ?? "" : "";
|
|
2806
|
+
const isListItem = stripped.startsWith("- ") || /^\d+\.\s/.test(stripped);
|
|
2807
|
+
const isPrevList = prevLine.startsWith("- ") || /^\d+\.\s/.test(prevLine);
|
|
2808
|
+
const prevEndsWithColon = prevLine.trimEnd().endsWith(":");
|
|
2809
|
+
if (isListItem && prevLine && !isPrevList) {
|
|
2810
|
+
result.push("");
|
|
2811
|
+
} else if (isListItem && prevEndsWithColon) {
|
|
1632
2812
|
result.push("");
|
|
1633
2813
|
}
|
|
1634
2814
|
result.push(stripped);
|
|
@@ -1969,7 +3149,9 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
1969
3149
|
lines.push(`# ${config.description}`);
|
|
1970
3150
|
lines.push("");
|
|
1971
3151
|
if (config.content) {
|
|
1972
|
-
|
|
3152
|
+
const dedentedContent = this.dedent(config.content);
|
|
3153
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
3154
|
+
lines.push(normalizedContent);
|
|
1973
3155
|
}
|
|
1974
3156
|
return {
|
|
1975
3157
|
path: `.github/instructions/${config.name}.instructions.md`,
|
|
@@ -2057,7 +3239,8 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
2057
3239
|
generatePromptFile(config) {
|
|
2058
3240
|
const lines = [];
|
|
2059
3241
|
lines.push("---");
|
|
2060
|
-
|
|
3242
|
+
const descQuote = config.description.includes("'") ? '"' : "'";
|
|
3243
|
+
lines.push(`description: ${descQuote}${config.description}${descQuote}`);
|
|
2061
3244
|
if (config.mode === "agent") {
|
|
2062
3245
|
lines.push("mode: agent");
|
|
2063
3246
|
if (config.tools && config.tools.length > 0) {
|
|
@@ -2069,11 +3252,13 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
2069
3252
|
lines.push("---");
|
|
2070
3253
|
lines.push("");
|
|
2071
3254
|
if (config.content) {
|
|
2072
|
-
|
|
3255
|
+
const dedentedContent = this.dedent(config.content);
|
|
3256
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
3257
|
+
lines.push(normalizedContent);
|
|
2073
3258
|
}
|
|
2074
3259
|
return {
|
|
2075
3260
|
path: `.github/prompts/${config.name}.prompt.md`,
|
|
2076
|
-
content: lines.join("\n")
|
|
3261
|
+
content: lines.join("\n") + "\n"
|
|
2077
3262
|
};
|
|
2078
3263
|
}
|
|
2079
3264
|
// ============================================================
|
|
@@ -2115,7 +3300,8 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
2115
3300
|
lines.push("---");
|
|
2116
3301
|
lines.push("");
|
|
2117
3302
|
if (config.content) {
|
|
2118
|
-
const
|
|
3303
|
+
const dedentedContent = this.dedent(config.content);
|
|
3304
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
2119
3305
|
lines.push(normalizedContent);
|
|
2120
3306
|
}
|
|
2121
3307
|
return {
|
|
@@ -2123,6 +3309,28 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
2123
3309
|
content: lines.join("\n") + "\n"
|
|
2124
3310
|
};
|
|
2125
3311
|
}
|
|
3312
|
+
/**
|
|
3313
|
+
* Remove common leading indentation from multiline text.
|
|
3314
|
+
* Calculates minimum indent from lines 2+ only, since line 1 may have been
|
|
3315
|
+
* trimmed (losing its indentation) while subsequent lines retain theirs.
|
|
3316
|
+
*/
|
|
3317
|
+
dedent(text) {
|
|
3318
|
+
const lines = text.split("\n");
|
|
3319
|
+
if (lines.length <= 1) return text.trim();
|
|
3320
|
+
const minIndent = lines.slice(1).filter((line) => line.trim().length > 0).reduce((min, line) => {
|
|
3321
|
+
const match = line.match(/^(\s*)/);
|
|
3322
|
+
const indent = match?.[1]?.length ?? 0;
|
|
3323
|
+
return Math.min(min, indent);
|
|
3324
|
+
}, Infinity);
|
|
3325
|
+
if (minIndent === 0 || minIndent === Infinity) {
|
|
3326
|
+
return text.trim();
|
|
3327
|
+
}
|
|
3328
|
+
const firstLine = lines[0] ?? "";
|
|
3329
|
+
return [
|
|
3330
|
+
firstLine.trim(),
|
|
3331
|
+
...lines.slice(1).map((line) => line.trim().length > 0 ? line.slice(minIndent) : "")
|
|
3332
|
+
].join("\n").trim();
|
|
3333
|
+
}
|
|
2126
3334
|
// ============================================================
|
|
2127
3335
|
// AGENTS.md Generation
|
|
2128
3336
|
// ============================================================
|
|
@@ -2258,7 +3466,9 @@ var GitHubFormatter = class extends BaseFormatter {
|
|
|
2258
3466
|
lines.push("---");
|
|
2259
3467
|
lines.push("");
|
|
2260
3468
|
if (config.content) {
|
|
2261
|
-
|
|
3469
|
+
const dedentedContent = this.dedent(config.content);
|
|
3470
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
3471
|
+
lines.push(normalizedContent);
|
|
2262
3472
|
}
|
|
2263
3473
|
return {
|
|
2264
3474
|
path: `.github/agents/${config.name}.md`,
|
|
@@ -2713,7 +3923,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2713
3923
|
lines.push(`# ${config.description}`);
|
|
2714
3924
|
lines.push("");
|
|
2715
3925
|
if (config.content) {
|
|
2716
|
-
|
|
3926
|
+
const dedentedContent = this.dedent(config.content);
|
|
3927
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
3928
|
+
lines.push(normalizedContent);
|
|
2717
3929
|
}
|
|
2718
3930
|
return {
|
|
2719
3931
|
path: `.claude/rules/${config.name}.md`,
|
|
@@ -2812,7 +4024,8 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2812
4024
|
lines.push("---");
|
|
2813
4025
|
lines.push("");
|
|
2814
4026
|
if (config.content) {
|
|
2815
|
-
const
|
|
4027
|
+
const dedentedContent = this.dedent(config.content);
|
|
4028
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
2816
4029
|
lines.push(normalizedContent);
|
|
2817
4030
|
}
|
|
2818
4031
|
return {
|
|
@@ -2820,6 +4033,28 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2820
4033
|
content: lines.join("\n") + "\n"
|
|
2821
4034
|
};
|
|
2822
4035
|
}
|
|
4036
|
+
/**
|
|
4037
|
+
* Remove common leading indentation from multiline text.
|
|
4038
|
+
* Calculates minimum indent from lines 2+ only, since line 1 may have been
|
|
4039
|
+
* trimmed (losing its indentation) while subsequent lines retain theirs.
|
|
4040
|
+
*/
|
|
4041
|
+
dedent(text) {
|
|
4042
|
+
const lines = text.split("\n");
|
|
4043
|
+
if (lines.length <= 1) return text.trim();
|
|
4044
|
+
const minIndent = lines.slice(1).filter((line) => line.trim().length > 0).reduce((min, line) => {
|
|
4045
|
+
const match = line.match(/^(\s*)/);
|
|
4046
|
+
const indent = match?.[1]?.length ?? 0;
|
|
4047
|
+
return Math.min(min, indent);
|
|
4048
|
+
}, Infinity);
|
|
4049
|
+
if (minIndent === 0 || minIndent === Infinity) {
|
|
4050
|
+
return text.trim();
|
|
4051
|
+
}
|
|
4052
|
+
const firstLine = lines[0] ?? "";
|
|
4053
|
+
return [
|
|
4054
|
+
firstLine.trim(),
|
|
4055
|
+
...lines.slice(1).map((line) => line.trim().length > 0 ? line.slice(minIndent) : "")
|
|
4056
|
+
].join("\n").trim();
|
|
4057
|
+
}
|
|
2823
4058
|
// ============================================================
|
|
2824
4059
|
// Agent File Generation
|
|
2825
4060
|
// ============================================================
|
|
@@ -2922,7 +4157,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2922
4157
|
lines.push("---");
|
|
2923
4158
|
lines.push("");
|
|
2924
4159
|
if (config.content) {
|
|
2925
|
-
|
|
4160
|
+
const dedentedContent = this.dedent(config.content);
|
|
4161
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(dedentedContent);
|
|
4162
|
+
lines.push(normalizedContent);
|
|
2926
4163
|
}
|
|
2927
4164
|
return {
|
|
2928
4165
|
path: `.claude/agents/${config.name}.md`,
|
|
@@ -2977,7 +4214,8 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
2977
4214
|
const cleanText = text.split(/\n{2,}/).map(
|
|
2978
4215
|
(para) => para.split("\n").map((line) => line.trim()).filter((line) => line).join("\n")
|
|
2979
4216
|
).filter((para) => para).join("\n\n");
|
|
2980
|
-
|
|
4217
|
+
const normalizedText = this.normalizeMarkdownForPrettier(cleanText);
|
|
4218
|
+
return renderer.renderSection("Project", normalizedText) + "\n";
|
|
2981
4219
|
}
|
|
2982
4220
|
techStack(ast, renderer) {
|
|
2983
4221
|
const context = this.findBlock(ast, "context");
|
|
@@ -3035,8 +4273,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
3035
4273
|
const text = this.extractText(context.content);
|
|
3036
4274
|
const archMatch = this.extractSectionWithCodeBlock(text, "## Architecture");
|
|
3037
4275
|
if (!archMatch) return null;
|
|
3038
|
-
const content = archMatch.replace("## Architecture", "")
|
|
3039
|
-
|
|
4276
|
+
const content = archMatch.replace("## Architecture", "");
|
|
4277
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(content);
|
|
4278
|
+
return renderer.renderSection("Architecture", normalizedContent.trim()) + "\n";
|
|
3040
4279
|
}
|
|
3041
4280
|
codeStandards(ast, renderer) {
|
|
3042
4281
|
const standards = this.findBlock(ast, "standards");
|
|
@@ -3117,7 +4356,7 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
3117
4356
|
const props = this.getProps(shortcuts.content);
|
|
3118
4357
|
for (const [cmd, desc] of Object.entries(props)) {
|
|
3119
4358
|
const shortDesc = this.valueToString(desc).split("\n")[0] ?? "";
|
|
3120
|
-
commandLines.push(`${cmd.padEnd(10)} - ${shortDesc}
|
|
4359
|
+
commandLines.push(`${cmd.padEnd(10)} - ${shortDesc}`.trimEnd());
|
|
3121
4360
|
}
|
|
3122
4361
|
}
|
|
3123
4362
|
if (commandLines.length === 0) return null;
|
|
@@ -3126,8 +4365,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
3126
4365
|
const text = this.extractText(knowledge.content);
|
|
3127
4366
|
const match = this.extractSectionWithCodeBlock(text, "## Development Commands");
|
|
3128
4367
|
if (match) {
|
|
3129
|
-
const devCmds = match.replace("## Development Commands", "")
|
|
3130
|
-
|
|
4368
|
+
const devCmds = match.replace("## Development Commands", "");
|
|
4369
|
+
const normalizedDevCmds = this.normalizeMarkdownForPrettier(devCmds);
|
|
4370
|
+
content += "\n\n" + normalizedDevCmds.trim();
|
|
3131
4371
|
}
|
|
3132
4372
|
}
|
|
3133
4373
|
return renderer.renderSection("Commands", content) + "\n";
|
|
@@ -3138,8 +4378,9 @@ var ClaudeFormatter = class extends BaseFormatter {
|
|
|
3138
4378
|
const text = this.extractText(knowledge.content);
|
|
3139
4379
|
const match = this.extractSectionWithCodeBlock(text, "## Post-Work Verification");
|
|
3140
4380
|
if (!match) return null;
|
|
3141
|
-
const content = match.replace("## Post-Work Verification", "")
|
|
3142
|
-
|
|
4381
|
+
const content = match.replace("## Post-Work Verification", "");
|
|
4382
|
+
const normalizedContent = this.normalizeMarkdownForPrettier(content);
|
|
4383
|
+
return renderer.renderSection("Post-Work Verification", normalizedContent.trim()) + "\n";
|
|
3143
4384
|
}
|
|
3144
4385
|
documentation(ast, renderer) {
|
|
3145
4386
|
const standards = this.findBlock(ast, "standards");
|
|
@@ -3517,13 +4758,39 @@ var CursorFormatter = class extends BaseFormatter {
|
|
|
3517
4758
|
if (never) sections.push(never);
|
|
3518
4759
|
}
|
|
3519
4760
|
intro(ast) {
|
|
3520
|
-
const
|
|
3521
|
-
if (
|
|
3522
|
-
|
|
4761
|
+
const identity2 = this.findBlock(ast, "identity");
|
|
4762
|
+
if (identity2) {
|
|
4763
|
+
const fullText = this.dedent(this.extractText(identity2.content));
|
|
4764
|
+
if (fullText.toLowerCase().startsWith("you are")) {
|
|
4765
|
+
return fullText;
|
|
4766
|
+
}
|
|
3523
4767
|
}
|
|
4768
|
+
const projectInfo = this.extractProjectInfo(ast);
|
|
3524
4769
|
const orgSuffix = projectInfo.org ? ` at ${projectInfo.org}` : "";
|
|
3525
4770
|
return `You are working on ${projectInfo.text}${orgSuffix}.`;
|
|
3526
4771
|
}
|
|
4772
|
+
/**
|
|
4773
|
+
* Remove common leading whitespace from all lines (dedent).
|
|
4774
|
+
* Handles the case where trim() was already called, causing the first line
|
|
4775
|
+
* to lose its indentation while subsequent lines retain theirs.
|
|
4776
|
+
*/
|
|
4777
|
+
dedent(text) {
|
|
4778
|
+
const lines = text.split("\n");
|
|
4779
|
+
if (lines.length <= 1) return text.trim();
|
|
4780
|
+
const minIndent = lines.slice(1).filter((line) => line.trim().length > 0).reduce((min, line) => {
|
|
4781
|
+
const match = line.match(/^(\s*)/);
|
|
4782
|
+
const indent = match?.[1]?.length ?? 0;
|
|
4783
|
+
return Math.min(min, indent);
|
|
4784
|
+
}, Infinity);
|
|
4785
|
+
if (minIndent === 0 || minIndent === Infinity) {
|
|
4786
|
+
return text.trim();
|
|
4787
|
+
}
|
|
4788
|
+
const firstLine = lines[0] ?? "";
|
|
4789
|
+
return [
|
|
4790
|
+
firstLine.trim(),
|
|
4791
|
+
...lines.slice(1).map((line) => line.trim().length > 0 ? line.slice(minIndent) : "")
|
|
4792
|
+
].join("\n").trim();
|
|
4793
|
+
}
|
|
3527
4794
|
/**
|
|
3528
4795
|
* Generate YAML frontmatter for Cursor MDC format.
|
|
3529
4796
|
* @see https://cursor.com/docs/context/rules
|
|
@@ -14613,7 +15880,7 @@ function parse(source, options = {}) {
|
|
|
14613
15880
|
|
|
14614
15881
|
// packages/resolver/src/loader.ts
|
|
14615
15882
|
import { readFile as readFile4 } from "fs/promises";
|
|
14616
|
-
import { resolve as resolve2, dirname as
|
|
15883
|
+
import { resolve as resolve2, dirname as dirname4, isAbsolute } from "path";
|
|
14617
15884
|
var FileLoader = class {
|
|
14618
15885
|
registryPath;
|
|
14619
15886
|
localPath;
|
|
@@ -14674,7 +15941,7 @@ var FileLoader = class {
|
|
|
14674
15941
|
*/
|
|
14675
15942
|
resolveRef(ref, fromFile) {
|
|
14676
15943
|
if (ref.isRelative) {
|
|
14677
|
-
const dir =
|
|
15944
|
+
const dir = dirname4(fromFile);
|
|
14678
15945
|
const rawPath = ref.raw.endsWith(".prs") ? ref.raw : `${ref.raw}.prs`;
|
|
14679
15946
|
return resolve2(dir, rawPath);
|
|
14680
15947
|
}
|
|
@@ -15336,7 +16603,7 @@ function uniqueConcat3(parent, child) {
|
|
|
15336
16603
|
|
|
15337
16604
|
// packages/resolver/src/skills.ts
|
|
15338
16605
|
import { readFile as readFile5, access } from "fs/promises";
|
|
15339
|
-
import { resolve as resolve3, dirname as
|
|
16606
|
+
import { resolve as resolve3, dirname as dirname5 } from "path";
|
|
15340
16607
|
function parseSkillMd(content) {
|
|
15341
16608
|
const lines = content.split("\n");
|
|
15342
16609
|
let inFrontmatter = false;
|
|
@@ -15391,7 +16658,7 @@ async function resolveNativeSkills(ast, registryPath, sourceFile) {
|
|
|
15391
16658
|
const skillsContent = skillsBlock.content;
|
|
15392
16659
|
const updatedProperties = { ...skillsContent.properties };
|
|
15393
16660
|
let hasUpdates = false;
|
|
15394
|
-
const sourceDir =
|
|
16661
|
+
const sourceDir = dirname5(sourceFile);
|
|
15395
16662
|
const isSkillsDir = sourceDir.includes("@skills");
|
|
15396
16663
|
for (const [skillName, skillValue] of Object.entries(skillsContent.properties)) {
|
|
15397
16664
|
if (typeof skillValue !== "object" || skillValue === null || Array.isArray(skillValue)) {
|
|
@@ -15619,7 +16886,7 @@ var Resolver = class {
|
|
|
15619
16886
|
|
|
15620
16887
|
// packages/resolver/src/registry.ts
|
|
15621
16888
|
import { existsSync as existsSync4, promises as fs } from "fs";
|
|
15622
|
-
import { join as
|
|
16889
|
+
import { join as join4 } from "path";
|
|
15623
16890
|
var FileSystemRegistry = class {
|
|
15624
16891
|
rootPath;
|
|
15625
16892
|
constructor(options) {
|
|
@@ -15629,7 +16896,7 @@ var FileSystemRegistry = class {
|
|
|
15629
16896
|
* Resolve a path relative to the registry root.
|
|
15630
16897
|
*/
|
|
15631
16898
|
resolvePath(path) {
|
|
15632
|
-
return
|
|
16899
|
+
return join4(this.rootPath, path);
|
|
15633
16900
|
}
|
|
15634
16901
|
async fetch(path) {
|
|
15635
16902
|
const fullPath = this.resolvePath(path);
|
|
@@ -15844,12 +17111,12 @@ function createHttpRegistry(options) {
|
|
|
15844
17111
|
|
|
15845
17112
|
// packages/resolver/src/git-registry.ts
|
|
15846
17113
|
import { existsSync as existsSync6, promises as fs3 } from "fs";
|
|
15847
|
-
import { join as
|
|
17114
|
+
import { join as join6 } from "path";
|
|
15848
17115
|
import { simpleGit } from "simple-git";
|
|
15849
17116
|
|
|
15850
17117
|
// packages/resolver/src/git-cache-manager.ts
|
|
15851
17118
|
import { existsSync as existsSync5, promises as fs2 } from "fs";
|
|
15852
|
-
import { join as
|
|
17119
|
+
import { join as join5 } from "path";
|
|
15853
17120
|
import { homedir } from "os";
|
|
15854
17121
|
|
|
15855
17122
|
// packages/resolver/src/git-url-utils.ts
|
|
@@ -15957,7 +17224,7 @@ function parseVersionedPath(path) {
|
|
|
15957
17224
|
}
|
|
15958
17225
|
|
|
15959
17226
|
// packages/resolver/src/git-cache-manager.ts
|
|
15960
|
-
var DEFAULT_CACHE_DIR =
|
|
17227
|
+
var DEFAULT_CACHE_DIR = join5(homedir(), ".promptscript", ".cache", "git");
|
|
15961
17228
|
var DEFAULT_TTL = 36e5;
|
|
15962
17229
|
var METADATA_FILE = ".prs-cache-meta.json";
|
|
15963
17230
|
var GitCacheManager = class {
|
|
@@ -15976,7 +17243,7 @@ var GitCacheManager = class {
|
|
|
15976
17243
|
*/
|
|
15977
17244
|
getCachePath(url, ref) {
|
|
15978
17245
|
const cacheKey = getCacheKey(url, ref);
|
|
15979
|
-
return
|
|
17246
|
+
return join5(this.cacheDir, cacheKey);
|
|
15980
17247
|
}
|
|
15981
17248
|
/**
|
|
15982
17249
|
* Check if a cache entry exists and is not stale.
|
|
@@ -16082,7 +17349,7 @@ var GitCacheManager = class {
|
|
|
16082
17349
|
if (!dir.isDirectory()) {
|
|
16083
17350
|
continue;
|
|
16084
17351
|
}
|
|
16085
|
-
const cachePath =
|
|
17352
|
+
const cachePath = join5(this.cacheDir, dir.name);
|
|
16086
17353
|
const metadata = await this.readMetadata(cachePath);
|
|
16087
17354
|
if (metadata) {
|
|
16088
17355
|
const isStale = Date.now() - metadata.lastUpdated > this.ttl;
|
|
@@ -16130,7 +17397,7 @@ var GitCacheManager = class {
|
|
|
16130
17397
|
let size = 0;
|
|
16131
17398
|
const entries = await fs2.readdir(dirPath, { withFileTypes: true });
|
|
16132
17399
|
for (const entry of entries) {
|
|
16133
|
-
const fullPath =
|
|
17400
|
+
const fullPath = join5(dirPath, entry.name);
|
|
16134
17401
|
if (entry.isDirectory()) {
|
|
16135
17402
|
size += await this.calculateDirSize(fullPath);
|
|
16136
17403
|
} else {
|
|
@@ -16144,7 +17411,7 @@ var GitCacheManager = class {
|
|
|
16144
17411
|
* Read metadata from a cache directory.
|
|
16145
17412
|
*/
|
|
16146
17413
|
async readMetadata(cachePath) {
|
|
16147
|
-
const metadataPath =
|
|
17414
|
+
const metadataPath = join5(cachePath, METADATA_FILE);
|
|
16148
17415
|
if (!existsSync5(metadataPath)) {
|
|
16149
17416
|
return null;
|
|
16150
17417
|
}
|
|
@@ -16159,7 +17426,7 @@ var GitCacheManager = class {
|
|
|
16159
17426
|
* Write metadata to a cache directory.
|
|
16160
17427
|
*/
|
|
16161
17428
|
async writeMetadata(cachePath, metadata) {
|
|
16162
|
-
const metadataPath =
|
|
17429
|
+
const metadataPath = join5(cachePath, METADATA_FILE);
|
|
16163
17430
|
await fs2.writeFile(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
16164
17431
|
}
|
|
16165
17432
|
};
|
|
@@ -16450,9 +17717,9 @@ var GitRegistry = class {
|
|
|
16450
17717
|
cleanPath += ".prs";
|
|
16451
17718
|
}
|
|
16452
17719
|
if (this.subPath) {
|
|
16453
|
-
return
|
|
17720
|
+
return join6(repoPath, this.subPath, cleanPath);
|
|
16454
17721
|
}
|
|
16455
|
-
return
|
|
17722
|
+
return join6(repoPath, cleanPath);
|
|
16456
17723
|
}
|
|
16457
17724
|
/**
|
|
16458
17725
|
* Resolve a directory path within the repository.
|
|
@@ -16462,9 +17729,9 @@ var GitRegistry = class {
|
|
|
16462
17729
|
*/
|
|
16463
17730
|
resolveDirectoryPath(repoPath, relativePath) {
|
|
16464
17731
|
if (this.subPath) {
|
|
16465
|
-
return
|
|
17732
|
+
return join6(repoPath, this.subPath, relativePath);
|
|
16466
17733
|
}
|
|
16467
|
-
return
|
|
17734
|
+
return join6(repoPath, relativePath);
|
|
16468
17735
|
}
|
|
16469
17736
|
/**
|
|
16470
17737
|
* Check if an error is an authentication error.
|
|
@@ -17182,8 +18449,8 @@ var Compiler = class _Compiler {
|
|
|
17182
18449
|
*/
|
|
17183
18450
|
async watch(entryPath, options = {}) {
|
|
17184
18451
|
const { default: chokidar2 } = await import("chokidar");
|
|
17185
|
-
const { dirname:
|
|
17186
|
-
const baseDir =
|
|
18452
|
+
const { dirname: dirname10, resolve: resolve9 } = await import("path");
|
|
18453
|
+
const baseDir = dirname10(resolve9(entryPath));
|
|
17187
18454
|
const includePatterns = options.include ?? ["**/*.prs"];
|
|
17188
18455
|
const excludePatterns = options.exclude ?? ["**/node_modules/**"];
|
|
17189
18456
|
const debounceMs = options.debounce ?? 300;
|
|
@@ -17403,6 +18670,69 @@ function createPager(enabled = true) {
|
|
|
17403
18670
|
return new Pager(enabled);
|
|
17404
18671
|
}
|
|
17405
18672
|
|
|
18673
|
+
// packages/cli/src/utils/registry-resolver.ts
|
|
18674
|
+
import { join as join7 } from "path";
|
|
18675
|
+
async function resolveRegistryPath(config) {
|
|
18676
|
+
if (config.registry?.git) {
|
|
18677
|
+
const gitConfig = config.registry.git;
|
|
18678
|
+
const ref = gitConfig.ref ?? "main";
|
|
18679
|
+
const cacheManager = new GitCacheManager({
|
|
18680
|
+
ttl: config.registry.cache?.ttl
|
|
18681
|
+
});
|
|
18682
|
+
const normalizedUrl = normalizeGitUrl(gitConfig.url);
|
|
18683
|
+
const cachePath = cacheManager.getCachePath(normalizedUrl, ref);
|
|
18684
|
+
const isValid = await cacheManager.isValid(normalizedUrl, ref);
|
|
18685
|
+
if (isValid) {
|
|
18686
|
+
const subPath2 = gitConfig.path ?? "";
|
|
18687
|
+
return {
|
|
18688
|
+
path: subPath2 ? join7(cachePath, subPath2) : cachePath,
|
|
18689
|
+
isRemote: true,
|
|
18690
|
+
source: "git"
|
|
18691
|
+
};
|
|
18692
|
+
}
|
|
18693
|
+
const registry = createGitRegistry({
|
|
18694
|
+
url: gitConfig.url,
|
|
18695
|
+
ref,
|
|
18696
|
+
path: gitConfig.path,
|
|
18697
|
+
auth: gitConfig.auth,
|
|
18698
|
+
cache: {
|
|
18699
|
+
enabled: config.registry.cache?.enabled ?? true,
|
|
18700
|
+
ttl: config.registry.cache?.ttl
|
|
18701
|
+
}
|
|
18702
|
+
});
|
|
18703
|
+
try {
|
|
18704
|
+
await registry.fetch("registry-manifest.yaml");
|
|
18705
|
+
} catch (error) {
|
|
18706
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18707
|
+
if (!message.includes("not found") && !message.includes("FileNotFoundError")) {
|
|
18708
|
+
throw new Error(
|
|
18709
|
+
`Failed to clone registry from ${gitConfig.url}: ${message}. Please check your network connection and registry configuration.`
|
|
18710
|
+
);
|
|
18711
|
+
}
|
|
18712
|
+
}
|
|
18713
|
+
const subPath = gitConfig.path ?? "";
|
|
18714
|
+
return {
|
|
18715
|
+
path: subPath ? join7(cachePath, subPath) : cachePath,
|
|
18716
|
+
isRemote: true,
|
|
18717
|
+
source: "git"
|
|
18718
|
+
};
|
|
18719
|
+
}
|
|
18720
|
+
if (config.registry?.url) {
|
|
18721
|
+
const localPath = config.registry?.path ?? "./registry";
|
|
18722
|
+
return {
|
|
18723
|
+
path: localPath,
|
|
18724
|
+
isRemote: true,
|
|
18725
|
+
source: "http"
|
|
18726
|
+
};
|
|
18727
|
+
}
|
|
18728
|
+
const registryPath = config.registry?.path ?? "./registry";
|
|
18729
|
+
return {
|
|
18730
|
+
path: registryPath,
|
|
18731
|
+
isRemote: false,
|
|
18732
|
+
source: "local"
|
|
18733
|
+
};
|
|
18734
|
+
}
|
|
18735
|
+
|
|
17406
18736
|
// packages/cli/src/commands/compile.ts
|
|
17407
18737
|
var PROMPTSCRIPT_MARKERS = [
|
|
17408
18738
|
"<!-- PromptScript",
|
|
@@ -17410,6 +18740,10 @@ var PROMPTSCRIPT_MARKERS = [
|
|
|
17410
18740
|
"> Auto-generated by PromptScript"
|
|
17411
18741
|
// Legacy - for backwards compatibility
|
|
17412
18742
|
];
|
|
18743
|
+
var MARKER_REGEX = /<!-- PromptScript [^>]+ -->\n*/g;
|
|
18744
|
+
function stripMarkers(content) {
|
|
18745
|
+
return content.replace(MARKER_REGEX, "");
|
|
18746
|
+
}
|
|
17413
18747
|
function createCliLogger() {
|
|
17414
18748
|
return {
|
|
17415
18749
|
verbose: (message) => {
|
|
@@ -17468,7 +18802,7 @@ function printErrors(errors) {
|
|
|
17468
18802
|
}
|
|
17469
18803
|
}
|
|
17470
18804
|
async function writeOutputs(outputs, options, _config, services) {
|
|
17471
|
-
const result = { written: [], skipped: [] };
|
|
18805
|
+
const result = { written: [], skipped: [], unchanged: [] };
|
|
17472
18806
|
let overwriteAll = false;
|
|
17473
18807
|
const conflicts = [];
|
|
17474
18808
|
for (const output of outputs.values()) {
|
|
@@ -17478,18 +18812,28 @@ async function writeOutputs(outputs, options, _config, services) {
|
|
|
17478
18812
|
if (fileExists2) {
|
|
17479
18813
|
const isGenerated2 = await isPromptScriptGenerated2(outputPath);
|
|
17480
18814
|
if (isGenerated2) {
|
|
17481
|
-
|
|
18815
|
+
const existingContent = await readFile6(outputPath, "utf-8");
|
|
18816
|
+
const existingWithoutMarker = stripMarkers(existingContent);
|
|
18817
|
+
const newWithoutMarker = stripMarkers(output.content);
|
|
18818
|
+
if (existingWithoutMarker === newWithoutMarker) {
|
|
18819
|
+
ConsoleOutput.dryRun(`Unchanged: ${outputPath}`);
|
|
18820
|
+
result.unchanged.push(outputPath);
|
|
18821
|
+
} else {
|
|
18822
|
+
ConsoleOutput.dryRun(`Would overwrite: ${outputPath}`);
|
|
18823
|
+
result.written.push(outputPath);
|
|
18824
|
+
}
|
|
17482
18825
|
} else {
|
|
17483
18826
|
ConsoleOutput.warning(`Would conflict: ${outputPath} (not generated by PromptScript)`);
|
|
18827
|
+
result.written.push(outputPath);
|
|
17484
18828
|
}
|
|
17485
18829
|
} else {
|
|
17486
18830
|
ConsoleOutput.dryRun(`Would write: ${outputPath}`);
|
|
18831
|
+
result.written.push(outputPath);
|
|
17487
18832
|
}
|
|
17488
|
-
result.written.push(outputPath);
|
|
17489
18833
|
continue;
|
|
17490
18834
|
}
|
|
17491
18835
|
if (!fileExists2) {
|
|
17492
|
-
await mkdir2(
|
|
18836
|
+
await mkdir2(dirname6(outputPath), { recursive: true });
|
|
17493
18837
|
await writeFile2(outputPath, output.content, "utf-8");
|
|
17494
18838
|
ConsoleOutput.success(outputPath);
|
|
17495
18839
|
result.written.push(outputPath);
|
|
@@ -17497,14 +18841,22 @@ async function writeOutputs(outputs, options, _config, services) {
|
|
|
17497
18841
|
}
|
|
17498
18842
|
const isGenerated = await isPromptScriptGenerated2(outputPath);
|
|
17499
18843
|
if (isGenerated) {
|
|
17500
|
-
await
|
|
18844
|
+
const existingContent = await readFile6(outputPath, "utf-8");
|
|
18845
|
+
const existingWithoutMarker = stripMarkers(existingContent);
|
|
18846
|
+
const newWithoutMarker = stripMarkers(output.content);
|
|
18847
|
+
if (existingWithoutMarker === newWithoutMarker) {
|
|
18848
|
+
ConsoleOutput.unchanged(outputPath);
|
|
18849
|
+
result.unchanged.push(outputPath);
|
|
18850
|
+
continue;
|
|
18851
|
+
}
|
|
18852
|
+
await mkdir2(dirname6(outputPath), { recursive: true });
|
|
17501
18853
|
await writeFile2(outputPath, output.content, "utf-8");
|
|
17502
18854
|
ConsoleOutput.success(outputPath);
|
|
17503
18855
|
result.written.push(outputPath);
|
|
17504
18856
|
continue;
|
|
17505
18857
|
}
|
|
17506
18858
|
if (options.force || overwriteAll) {
|
|
17507
|
-
await mkdir2(
|
|
18859
|
+
await mkdir2(dirname6(outputPath), { recursive: true });
|
|
17508
18860
|
await writeFile2(outputPath, output.content, "utf-8");
|
|
17509
18861
|
ConsoleOutput.success(outputPath);
|
|
17510
18862
|
result.written.push(outputPath);
|
|
@@ -17517,7 +18869,7 @@ async function writeOutputs(outputs, options, _config, services) {
|
|
|
17517
18869
|
const response = await promptForOverwrite(outputPath, services);
|
|
17518
18870
|
switch (response) {
|
|
17519
18871
|
case "yes":
|
|
17520
|
-
await mkdir2(
|
|
18872
|
+
await mkdir2(dirname6(outputPath), { recursive: true });
|
|
17521
18873
|
await writeFile2(outputPath, output.content, "utf-8");
|
|
17522
18874
|
ConsoleOutput.success(outputPath);
|
|
17523
18875
|
result.written.push(outputPath);
|
|
@@ -17528,7 +18880,7 @@ async function writeOutputs(outputs, options, _config, services) {
|
|
|
17528
18880
|
break;
|
|
17529
18881
|
case "all":
|
|
17530
18882
|
overwriteAll = true;
|
|
17531
|
-
await mkdir2(
|
|
18883
|
+
await mkdir2(dirname6(outputPath), { recursive: true });
|
|
17532
18884
|
await writeFile2(outputPath, output.content, "utf-8");
|
|
17533
18885
|
ConsoleOutput.success(outputPath);
|
|
17534
18886
|
result.written.push(outputPath);
|
|
@@ -17567,10 +18919,20 @@ async function compileCommand(options, services = createDefaultServices()) {
|
|
|
17567
18919
|
try {
|
|
17568
18920
|
logger.verbose("Loading configuration...");
|
|
17569
18921
|
const config = await loadConfig(options.config);
|
|
17570
|
-
spinner.text = "Compiling...";
|
|
17571
18922
|
const selectedTarget = options.target ?? options.format;
|
|
17572
18923
|
const targets = selectedTarget ? [{ name: selectedTarget }] : parseTargets(config.targets);
|
|
17573
|
-
|
|
18924
|
+
let registryPath;
|
|
18925
|
+
if (options.registry) {
|
|
18926
|
+
registryPath = options.registry;
|
|
18927
|
+
} else {
|
|
18928
|
+
spinner.text = "Resolving registry...";
|
|
18929
|
+
const registry = await resolveRegistryPath(config);
|
|
18930
|
+
registryPath = registry.path;
|
|
18931
|
+
if (registry.isRemote) {
|
|
18932
|
+
logger.verbose(`Using cached git registry: ${registryPath}`);
|
|
18933
|
+
}
|
|
18934
|
+
}
|
|
18935
|
+
spinner.text = "Compiling...";
|
|
17574
18936
|
logger.verbose(`Registry: ${registryPath}`);
|
|
17575
18937
|
logger.debug(`Config: ${JSON.stringify(config, null, 2)}`);
|
|
17576
18938
|
const prettierOptions = await resolvePrettierOptions(config, process.cwd());
|
|
@@ -17602,6 +18964,9 @@ async function compileCommand(options, services = createDefaultServices()) {
|
|
|
17602
18964
|
spinner.succeed("Compilation successful");
|
|
17603
18965
|
ConsoleOutput.newline();
|
|
17604
18966
|
const writeResult = await writeOutputs(result.outputs, options, config, services);
|
|
18967
|
+
if (writeResult.unchanged.length > 0) {
|
|
18968
|
+
ConsoleOutput.muted(`Unchanged ${writeResult.unchanged.length} file(s)`);
|
|
18969
|
+
}
|
|
17605
18970
|
if (writeResult.skipped.length > 0) {
|
|
17606
18971
|
ConsoleOutput.muted(`Skipped ${writeResult.skipped.length} file(s)`);
|
|
17607
18972
|
}
|
|
@@ -17743,10 +19108,12 @@ async function validateCommand(options) {
|
|
|
17743
19108
|
const spinner = isJsonFormat ? createSpinner("").stop() : createSpinner("Loading configuration...").start();
|
|
17744
19109
|
try {
|
|
17745
19110
|
const config = await loadConfig();
|
|
19111
|
+
if (!isJsonFormat) spinner.text = "Resolving registry...";
|
|
19112
|
+
const registry = await resolveRegistryPath(config);
|
|
17746
19113
|
if (!isJsonFormat) spinner.text = "Validating...";
|
|
17747
19114
|
const compiler = new Compiler({
|
|
17748
19115
|
resolver: {
|
|
17749
|
-
registryPath:
|
|
19116
|
+
registryPath: registry.path,
|
|
17750
19117
|
localPath: "./.promptscript"
|
|
17751
19118
|
},
|
|
17752
19119
|
validator: config.validation,
|
|
@@ -17795,7 +19162,7 @@ function outputJsonResult(result) {
|
|
|
17795
19162
|
// packages/cli/src/commands/pull.ts
|
|
17796
19163
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
17797
19164
|
import { existsSync as existsSync9 } from "fs";
|
|
17798
|
-
import { resolve as resolve6, dirname as
|
|
19165
|
+
import { resolve as resolve6, dirname as dirname7 } from "path";
|
|
17799
19166
|
async function pullCommand(options) {
|
|
17800
19167
|
const spinner = createSpinner("Loading configuration...").start();
|
|
17801
19168
|
try {
|
|
@@ -17834,7 +19201,7 @@ async function pullCommand(options) {
|
|
|
17834
19201
|
}
|
|
17835
19202
|
return;
|
|
17836
19203
|
}
|
|
17837
|
-
await mkdir3(
|
|
19204
|
+
await mkdir3(dirname7(destPath), { recursive: true });
|
|
17838
19205
|
await writeFile3(destPath, content, "utf-8");
|
|
17839
19206
|
spinner.succeed("Pulled from registry");
|
|
17840
19207
|
ConsoleOutput.success(destPath);
|
|
@@ -17990,11 +19357,13 @@ async function diffCommand(options) {
|
|
|
17990
19357
|
const spinner = createSpinner("Loading configuration...").start();
|
|
17991
19358
|
try {
|
|
17992
19359
|
const config = await loadConfig();
|
|
19360
|
+
spinner.text = "Resolving registry...";
|
|
19361
|
+
const registry = await resolveRegistryPath(config);
|
|
17993
19362
|
spinner.text = "Compiling...";
|
|
17994
19363
|
const targets = options.target ? [{ name: options.target }] : parseTargets2(config.targets);
|
|
17995
19364
|
const compiler = new Compiler({
|
|
17996
19365
|
resolver: {
|
|
17997
|
-
registryPath:
|
|
19366
|
+
registryPath: registry.path,
|
|
17998
19367
|
localPath: "./.promptscript"
|
|
17999
19368
|
},
|
|
18000
19369
|
validator: config.validation,
|
|
@@ -18228,11 +19597,189 @@ function printResults(results) {
|
|
|
18228
19597
|
}
|
|
18229
19598
|
}
|
|
18230
19599
|
|
|
18231
|
-
// packages/cli/src/
|
|
19600
|
+
// packages/cli/src/commands/update-check.ts
|
|
19601
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19602
|
+
import { dirname as dirname8 } from "path";
|
|
19603
|
+
|
|
19604
|
+
// packages/cli/src/utils/version-check.ts
|
|
19605
|
+
import { homedir as homedir2 } from "os";
|
|
19606
|
+
import { join as join8 } from "path";
|
|
19607
|
+
import { existsSync as existsSync12, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
19608
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@promptscript/cli/latest";
|
|
19609
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
19610
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
19611
|
+
function getCacheDir() {
|
|
19612
|
+
return join8(homedir2(), ".promptscript", ".cache");
|
|
19613
|
+
}
|
|
19614
|
+
function getCachePath() {
|
|
19615
|
+
return join8(getCacheDir(), "version.json");
|
|
19616
|
+
}
|
|
19617
|
+
function readCache() {
|
|
19618
|
+
try {
|
|
19619
|
+
const cachePath = getCachePath();
|
|
19620
|
+
if (!existsSync12(cachePath)) {
|
|
19621
|
+
return null;
|
|
19622
|
+
}
|
|
19623
|
+
const content = readFileSync3(cachePath, "utf-8");
|
|
19624
|
+
return JSON.parse(content);
|
|
19625
|
+
} catch {
|
|
19626
|
+
return null;
|
|
19627
|
+
}
|
|
19628
|
+
}
|
|
19629
|
+
function writeCache(cache) {
|
|
19630
|
+
try {
|
|
19631
|
+
const cacheDir = getCacheDir();
|
|
19632
|
+
if (!existsSync12(cacheDir)) {
|
|
19633
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
19634
|
+
}
|
|
19635
|
+
const cachePath = getCachePath();
|
|
19636
|
+
writeFileSync(cachePath, JSON.stringify(cache, null, 2), "utf-8");
|
|
19637
|
+
} catch {
|
|
19638
|
+
}
|
|
19639
|
+
}
|
|
19640
|
+
function isCacheValid(cache) {
|
|
19641
|
+
try {
|
|
19642
|
+
const lastCheck = new Date(cache.lastCheck).getTime();
|
|
19643
|
+
const now = Date.now();
|
|
19644
|
+
return now - lastCheck < CACHE_TTL_MS;
|
|
19645
|
+
} catch {
|
|
19646
|
+
return false;
|
|
19647
|
+
}
|
|
19648
|
+
}
|
|
19649
|
+
async function fetchLatestVersion() {
|
|
19650
|
+
try {
|
|
19651
|
+
const controller = new AbortController();
|
|
19652
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
19653
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
19654
|
+
signal: controller.signal,
|
|
19655
|
+
headers: {
|
|
19656
|
+
Accept: "application/json"
|
|
19657
|
+
}
|
|
19658
|
+
});
|
|
19659
|
+
clearTimeout(timeout);
|
|
19660
|
+
if (!response.ok) {
|
|
19661
|
+
if (isVerbose()) {
|
|
19662
|
+
ConsoleOutput.verbose(`Could not check for updates: HTTP ${response.status}`);
|
|
19663
|
+
}
|
|
19664
|
+
return null;
|
|
19665
|
+
}
|
|
19666
|
+
const data = await response.json();
|
|
19667
|
+
return data.version ?? null;
|
|
19668
|
+
} catch (error) {
|
|
19669
|
+
if (isVerbose()) {
|
|
19670
|
+
const code = error.code;
|
|
19671
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
19672
|
+
ConsoleOutput.verbose(`Could not check for updates: ${code ?? message}`);
|
|
19673
|
+
}
|
|
19674
|
+
return null;
|
|
19675
|
+
}
|
|
19676
|
+
}
|
|
19677
|
+
function isNewerVersion(currentVersion, latestVersion) {
|
|
19678
|
+
const cleanCurrent = currentVersion.replace(/^v/, "");
|
|
19679
|
+
const cleanLatest = latestVersion.replace(/^v/, "");
|
|
19680
|
+
const [currentBase, currentPrerelease] = cleanCurrent.split("-");
|
|
19681
|
+
const [latestBase, latestPrerelease] = cleanLatest.split("-");
|
|
19682
|
+
const currentParts = (currentBase ?? "").split(".").map((p) => parseInt(p, 10) || 0);
|
|
19683
|
+
const latestParts = (latestBase ?? "").split(".").map((p) => parseInt(p, 10) || 0);
|
|
19684
|
+
for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
|
|
19685
|
+
const c = currentParts[i] ?? 0;
|
|
19686
|
+
const l = latestParts[i] ?? 0;
|
|
19687
|
+
if (l > c) return true;
|
|
19688
|
+
if (l < c) return false;
|
|
19689
|
+
}
|
|
19690
|
+
if (currentPrerelease && !latestPrerelease) {
|
|
19691
|
+
return true;
|
|
19692
|
+
}
|
|
19693
|
+
return false;
|
|
19694
|
+
}
|
|
19695
|
+
async function checkForUpdates(currentVersion) {
|
|
19696
|
+
if (process.env["PROMPTSCRIPT_NO_UPDATE_CHECK"]) {
|
|
19697
|
+
return null;
|
|
19698
|
+
}
|
|
19699
|
+
if (isQuiet()) {
|
|
19700
|
+
return null;
|
|
19701
|
+
}
|
|
19702
|
+
const cache = readCache();
|
|
19703
|
+
if (cache && isCacheValid(cache) && cache.currentVersion === currentVersion) {
|
|
19704
|
+
if (isNewerVersion(currentVersion, cache.latestVersion)) {
|
|
19705
|
+
return {
|
|
19706
|
+
currentVersion,
|
|
19707
|
+
latestVersion: cache.latestVersion,
|
|
19708
|
+
updateAvailable: true
|
|
19709
|
+
};
|
|
19710
|
+
}
|
|
19711
|
+
return null;
|
|
19712
|
+
}
|
|
19713
|
+
const latestVersion = await fetchLatestVersion();
|
|
19714
|
+
if (!latestVersion) {
|
|
19715
|
+
return null;
|
|
19716
|
+
}
|
|
19717
|
+
writeCache({
|
|
19718
|
+
lastCheck: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19719
|
+
latestVersion,
|
|
19720
|
+
currentVersion
|
|
19721
|
+
});
|
|
19722
|
+
if (isNewerVersion(currentVersion, latestVersion)) {
|
|
19723
|
+
return {
|
|
19724
|
+
currentVersion,
|
|
19725
|
+
latestVersion,
|
|
19726
|
+
updateAvailable: true
|
|
19727
|
+
};
|
|
19728
|
+
}
|
|
19729
|
+
return null;
|
|
19730
|
+
}
|
|
19731
|
+
async function forceCheckForUpdates(currentVersion) {
|
|
19732
|
+
const latestVersion = await fetchLatestVersion();
|
|
19733
|
+
if (!latestVersion) {
|
|
19734
|
+
return { info: null, error: true };
|
|
19735
|
+
}
|
|
19736
|
+
writeCache({
|
|
19737
|
+
lastCheck: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19738
|
+
latestVersion,
|
|
19739
|
+
currentVersion
|
|
19740
|
+
});
|
|
19741
|
+
const updateAvailable = isNewerVersion(currentVersion, latestVersion);
|
|
19742
|
+
return {
|
|
19743
|
+
info: {
|
|
19744
|
+
currentVersion,
|
|
19745
|
+
latestVersion,
|
|
19746
|
+
updateAvailable
|
|
19747
|
+
},
|
|
19748
|
+
error: false
|
|
19749
|
+
};
|
|
19750
|
+
}
|
|
19751
|
+
function printUpdateNotification(info) {
|
|
19752
|
+
console.log(
|
|
19753
|
+
`Update available: ${info.currentVersion} \u2192 ${info.latestVersion} (npm i -g @promptscript/cli)`
|
|
19754
|
+
);
|
|
19755
|
+
}
|
|
19756
|
+
|
|
19757
|
+
// packages/cli/src/commands/update-check.ts
|
|
18232
19758
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
18233
|
-
var __dirname2 =
|
|
19759
|
+
var __dirname2 = dirname8(__filename2);
|
|
19760
|
+
async function updateCheckCommand() {
|
|
19761
|
+
const currentVersion = getPackageVersion(__dirname2, "../../package.json");
|
|
19762
|
+
console.log(`@promptscript/cli v${currentVersion}`);
|
|
19763
|
+
const { info, error } = await forceCheckForUpdates(currentVersion);
|
|
19764
|
+
if (error) {
|
|
19765
|
+
ConsoleOutput.error("Could not check for updates");
|
|
19766
|
+
process.exitCode = 1;
|
|
19767
|
+
return;
|
|
19768
|
+
}
|
|
19769
|
+
if (info?.updateAvailable) {
|
|
19770
|
+
console.log(
|
|
19771
|
+
`Update available: ${info.currentVersion} \u2192 ${info.latestVersion} (npm i -g @promptscript/cli)`
|
|
19772
|
+
);
|
|
19773
|
+
} else {
|
|
19774
|
+
ConsoleOutput.success("Up to date");
|
|
19775
|
+
}
|
|
19776
|
+
}
|
|
19777
|
+
|
|
19778
|
+
// packages/cli/src/cli.ts
|
|
19779
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
19780
|
+
var __dirname3 = dirname9(__filename3);
|
|
18234
19781
|
var program = new Command();
|
|
18235
|
-
program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(
|
|
19782
|
+
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) => {
|
|
18236
19783
|
const opts = thisCommand.opts();
|
|
18237
19784
|
if (opts["quiet"]) {
|
|
18238
19785
|
setContext({ logLevel: 0 /* Quiet */ });
|
|
@@ -18246,13 +19793,22 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
|
|
|
18246
19793
|
} else if (process.env["PROMPTSCRIPT_VERBOSE"] === "1" || process.env["PROMPTSCRIPT_VERBOSE"] === "true") {
|
|
18247
19794
|
setContext({ logLevel: 2 /* Verbose */ });
|
|
18248
19795
|
}
|
|
19796
|
+
if (actionCommand.name() === "update-check") {
|
|
19797
|
+
return;
|
|
19798
|
+
}
|
|
19799
|
+
const currentVersion = getPackageVersion(__dirname3, "../package.json");
|
|
19800
|
+
const updateInfo = await checkForUpdates(currentVersion);
|
|
19801
|
+
if (updateInfo) {
|
|
19802
|
+
printUpdateNotification(updateInfo);
|
|
19803
|
+
}
|
|
18249
19804
|
});
|
|
18250
|
-
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));
|
|
19805
|
+
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));
|
|
18251
19806
|
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));
|
|
18252
19807
|
program.command("validate").description("Validate PromptScript files").option("--strict", "Treat warnings as errors").option("--format <format>", "Output format (text, json)", "text").action(validateCommand);
|
|
18253
19808
|
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);
|
|
18254
19809
|
program.command("diff").description("Show diff for compiled output").option("-t, --target <target>", "Specific target").option("-a, --all", "Show diff for all targets at once").option("--full", "Show full diff without truncation").option("--no-pager", "Disable pager output").option("--color", "Force colored output").option("--no-color", "Disable colored output").action(diffCommand);
|
|
18255
|
-
program.command("check").description("Check configuration and dependencies health").
|
|
19810
|
+
program.command("check").description("Check configuration and dependencies health").action(checkCommand);
|
|
19811
|
+
program.command("update-check").description("Check for CLI updates").action(updateCheckCommand);
|
|
18256
19812
|
function run(args = process.argv) {
|
|
18257
19813
|
program.parse(args);
|
|
18258
19814
|
}
|
|
@@ -18262,18 +19818,25 @@ export {
|
|
|
18262
19818
|
ConsoleOutput,
|
|
18263
19819
|
LogLevel,
|
|
18264
19820
|
checkCommand,
|
|
19821
|
+
checkForUpdates,
|
|
18265
19822
|
compileCommand,
|
|
18266
19823
|
createSpinner,
|
|
18267
19824
|
diffCommand,
|
|
19825
|
+
fetchLatestVersion,
|
|
18268
19826
|
findConfigFile,
|
|
19827
|
+
forceCheckForUpdates,
|
|
19828
|
+
getCacheDir,
|
|
19829
|
+
getCachePath,
|
|
18269
19830
|
getContext,
|
|
18270
19831
|
initCommand,
|
|
18271
19832
|
isQuiet,
|
|
18272
19833
|
isVerbose,
|
|
18273
19834
|
loadConfig,
|
|
19835
|
+
printUpdateNotification,
|
|
18274
19836
|
pullCommand,
|
|
18275
19837
|
run,
|
|
18276
19838
|
setContext,
|
|
19839
|
+
updateCheckCommand,
|
|
18277
19840
|
validateCommand
|
|
18278
19841
|
};
|
|
18279
19842
|
/*! Bundled license information:
|