create-promptex-js 1.0.1 → 1.1.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,38 +1,124 @@
1
1
  # create-promptex-js
2
2
 
3
- Scaffold a [promptex](https://github.com/promptex-ai/promptex) TypeScript
4
- project, plugin or adapter from the
5
- starter templates bundled with this package.
3
+ Start a [promptex](https://github.com/promptex-ai/promptex) TypeScript project,
4
+ plugin or adapter from the starter templates bundled with this package, or add
5
+ promptex to a project you already have.
6
6
 
7
7
  ```console
8
- npm create promptex-js
8
+ pnpm create promptex-js my-prompts # new project in ./my-prompts
9
+ pnpm create promptex-js . # add promptex to the current directory
9
10
  ```
10
11
 
11
- Interactive by default; every answer has a flag for non-interactive use:
12
+ `npx create-promptex-js <dir>` does the same. With `npm create promptex-js`, put the
13
+ flags after `--`, for example `npm create promptex-js . -- --dry-run`.
14
+
15
+ After that, declare and install the SDK, then build:
16
+
17
+ ```console
18
+ pnpm add promptex-js
19
+ pnpm exec promptex build --install
20
+ ```
21
+
22
+ ## 建立模式與初始化模式
23
+
24
+ create 依目標目錄自動選模式,輸出的第一行說明選了哪一種:
25
+
26
+ - 目標不存在,或第一層只有下表的項目時,走建立模式,開一個新專案。
27
+ - 其他情況走初始化模式,在原資料夾加入 promptex。初始化模式只支援 `project` 範本,
28
+ `plugin` 與 `adapter` 遇到這種資料夾會以結束碼 2 拒絕。
29
+
30
+ | 分組 | 不影響模式判斷的項目 |
31
+ | :--- | :--- |
32
+ | macOS(不分大小寫) | `.DS_Store`、`._*`、`.localized`、`.Spotlight-V100`、`.Trashes`、`.fseventsd`、`.TemporaryItems` |
33
+ | Windows(不分大小寫) | `Thumbs.db`、`desktop.ini`、`ehthumbs.db`、`$RECYCLE.BIN`、`System Volume Information` |
34
+ | Linux | `.directory`、`.Trash-*`、`.fuse_hidden*`、`.nfs*`、`lost+found` |
35
+ | 版本控制與倉庫 | `.git`、`.gitignore`、`.gitattributes`、`.gitkeep`、`.hg`、`.hgignore`、`.svn`、`LICENSE`、`README.md`、`.github` |
36
+ | 編輯器與 AI 工具 | `.vscode`、`.idea`、`*.iml`、`.zed`、`.cursor`、`.claude` |
37
+
38
+ 兩種模式寫入時都逐檔判斷,既有檔一律不覆寫:
39
+
40
+ | 情況 | 處理 |
41
+ | :--- | :--- |
42
+ | 已有 `promptex.config.ts`、`.py` 或 `.rs` | 判定已初始化,什麼都不寫,結束碼 0 |
43
+ | 已有 `package.json` | 內容不動,`promptex-js` 依賴由你用 `pnpm add promptex-js` 加 |
44
+ | 其他同名的範本檔 | 跳過並列出。`prompts/` 已存在時整個跳過 |
45
+ | `.gitignore` | 不存在就建立。存在就在尾端追加 promptex 區塊,已有區塊的標記行就跳過 |
46
+ | `README.md` | 只在建立模式寫出,已存在仍跳過 |
47
+ | git | 目標在任何 git 工作樹內(含上層資料夾)就不 `git init`。兩種模式都不自動 commit |
48
+ | 寫到一半失敗 | 只移除這次新建的檔案與資料夾,既有檔不動 |
49
+
50
+ 目標本身是符號連結或檔案時,以結束碼 2 拒絕。目標裡的符號連結不跟隨,連到外部的
51
+ 檔案不會被寫到。
52
+
53
+ ## 旗標
54
+
55
+ 每個問題都有對應的旗標,可以不互動執行:
12
56
 
13
57
  ```console
14
- npm create promptex-js my-prompts -- --template project --name my-prompts --no-git --yes
58
+ pnpm create promptex-js my-prompts --template project --name my-prompts --no-git --yes
15
59
  ```
16
60
 
17
61
  | Flag | Meaning |
18
62
  | :--- | :--- |
19
- | `--template <t>` | `project`(消費專案)、`plugin`、`adapter` |
63
+ | `--template <t>` | `project`(消費專案)、`plugin`、`adapter`。初始化模式只收 `project` |
20
64
  | `--name <n>` | kebab-case 名稱;預設取目標目錄名正規化 |
21
65
  | `--source <p>` | 改讀本地模板目錄(含各 `*-starter` 的那一層),開發模板時用 |
22
- | `--git` / `--no-git` | 是否 `git init`;互動預設是 |
23
- | `--install` | 鷹架後直接 `npm install`;預設只印下一步 |
24
- | `--yes` | 跳過互動,全部取預設或旗標值 |
66
+ | `--git` / `--no-git` | 是否 `git init`;預設是。在既有 git 工作樹內一律略過。給 `--git` 但沒裝 git 時報錯,沒給時靜默略過 |
67
+ | `--install` | 鷹架後直接安裝依賴,用下一段偵測到的套件管理器;預設只印下一步 |
68
+ | `--force` | 覆寫與範本同名的既有檔。不清空資料夾,也不動 `package.json`、`.gitignore` 與設定檔 |
69
+ | `--dry-run` | 列出會建立、覆寫、追加、跳過的檔,不寫入 |
70
+ | `--json` | 搭配 `--dry-run`,在 stdout 輸出一份 JSON |
71
+ | `--yes` | 跳過互動,全部取預設值;不代表同意覆寫 |
25
72
  | `--locale <l>` | 訊息與範例資源的語系:`en`(預設)或 `zh-Hant` |
26
73
 
74
+ 沒有終端機、帶 `--yes`,或環境變數 `CI` 為真時不提問,全部取預設值。
75
+
76
+ 下一步依偵測到的套件管理器提示:目標或上層有鎖定檔就用它的工具
77
+ (`pnpm-lock.yaml`、`yarn.lock`、`bun.lock`、`package-lock.json`),否則裝了 pnpm 就用
78
+ pnpm,再退回 npm。已有 `package.json` 時下一步改成先加依賴,因為既有的
79
+ `package.json` 沒有範本的 `install:prompts` script。
80
+
81
+ ## 機器可讀的試跑結果
82
+
83
+ `--dry-run --json` 在 stdout 輸出一行 JSON,欄位與另兩個 create 套件
84
+ (create-promptex-py、create-promptex-rs)相同,只有 `package` 與 `language` 不同:
85
+
86
+ ```json
87
+ {"formatVersion":"1.1","package":{"name":"create-promptex-js","version":"1.0.1"},"language":"ts",
88
+ "mode":"init","alreadyInitialized":false,"existingConfig":null,"target":"/work/app",
89
+ "template":"project","name":"app",
90
+ "files":{"create":["promptex.config.ts","prompts/example-skill.ts","prompts/example.ts"],
91
+ "overwrite":[],"append":[".gitignore"],
92
+ "skip":[{"path":"README.md","reason":"init-mode"},{"path":"package.json","reason":"manifest-kept"}]},
93
+ "git":{"init":false,"reason":"inside-worktree"}}
94
+ ```
95
+
96
+ | 欄位 | 內容 |
97
+ | :--- | :--- |
98
+ | `mode` | `create` 或 `init` |
99
+ | `alreadyInitialized`、`existingConfig` | 已有 promptex 設定檔時為 `true` 與那個檔名,`files` 四個清單都是空的 |
100
+ | `files.create`、`files.overwrite`、`files.append` | 會建立、覆寫(只在 `--force` 時出現)、追加的相對路徑 |
101
+ | `files.skip` | 跳過的路徑與原因:`exists`、`manifest-kept`、`example-dir-exists`、`init-mode`、`marker-present`、`symlink`、`blocked` |
102
+ | `git` | `init` 是否會執行 `git init`;不執行時 `reason` 是 `inside-worktree`、`disabled`、`unavailable` 或 `already-initialized` |
103
+
104
+ 失敗時 stdout 是一份錯誤 JSON,stderr 另寫一行同文的訊息:
105
+
106
+ ```json
107
+ {"formatVersion":"1.1","error":{"code":"USAGE_TEMPLATE_NEEDS_EMPTY_DIR","message":"…","details":{"template":"plugin","path":"/work/app","entries":["src"]}}}
108
+ ```
109
+
110
+ 結束碼是 0 成功(含已初始化)、1 執行失敗、2 用法錯誤。程式只比對 `error.code`:
111
+ `USAGE_` 開頭是呼叫本身不對,`RUN_` 開頭是執行失敗。
112
+
113
+ ## 語系與模板
114
+
27
115
  語系未以 `--locale` 指定時,依序讀 `PROMPTEX_LOCALE`、`LANGUAGE`、`LC_ALL`、
28
116
  `LC_MESSAGES`、`LANG`,都沒有就用英文;`LC_ALL=C` 意為關掉在地化。`zh-TW`、
29
117
  `zh-HK`、`zh-MO` 與 `zh-Hant-*` 都對到 `zh-Hant`。它同時決定鋪出來的範例資源
30
118
  用哪個語系寫([ADR-030](https://github.com/promptex-ai/ssot)):那份內容從產出
31
119
  當下起就歸你,框架不再改寫它。plugin 與 adapter 骨架裡教你怎麼填的註解維持英文。
32
120
 
33
- 目標目錄非空時列出既有項目、零寫入退出。模板隨本套件一起發布,
34
- 鷹架時不需要網路,每個版本具現化的都是它發布時的模板。`{{promptexVersion}}`
35
- 於鷹架當下以本套件版本填入,三套件與 SDK 同版發布。
121
+ 模板隨本套件一起發布,鷹架時不需要網路,每個版本具現化的都是它發布時的模板。
122
+ `{{promptexVersion}}` 於鷹架當下以本套件版本填入,三套件與 SDK 同版發布。
36
123
 
37
- `promptex init` 與本工具的分工:init 在既有專案就地補上 promptex(非互動、
38
- 極簡骨架);本工具從零開一個 repo(互動、三種模板、git init)。
124
+ `promptex init` 已棄用,2.0 移除。它能做的事改由本工具的初始化模式處理。
@@ -0,0 +1,24 @@
1
+ /** 機器可讀輸出的格式版本(ADR-041)。 */
2
+ export declare const FORMAT_VERSION = "1.1";
3
+ /** `.gitignore` 區塊的標記行。已有這一行就不再追加。 */
4
+ export declare const GITIGNORE_MARKER = "# === promptex init \u7522\u751F\u7684\u5FFD\u7565\u898F\u5247\uFF08\u91CD\u8DD1\u4EE5\u672C\u884C\u5224\u5B9A\uFF0C\u52FF\u6539\u5BEB\uFF09===";
5
+ /** 任一存在就判定目標已初始化。 */
6
+ export declare const CONFIG_FILES: readonly string[];
7
+ /** 只決定模式的允許清單:第一層只有這些項目時走建立模式。 */
8
+ export declare const ALLOWLIST: readonly {
9
+ group: string;
10
+ caseInsensitive: boolean;
11
+ entries: readonly string[];
12
+ }[];
13
+ /** 錯誤代碼。`exitCode` 由前綴決定:`USAGE_` 為 2,`RUN_` 為 1。 */
14
+ export interface ErrorCode {
15
+ code: string;
16
+ exitCode: number;
17
+ messageKey: string;
18
+ details: readonly string[];
19
+ }
20
+ export declare const ERRORS: readonly ErrorCode[];
21
+ /** 依代碼查錯誤記錄。代碼不在表內是呼叫端的程式錯誤。 */
22
+ export declare function errorCode(code: string): ErrorCode;
23
+ /** 第一層項目名稱是否在允許清單內。 */
24
+ export declare function isAllowlisted(name: string): boolean;
@@ -0,0 +1,59 @@
1
+ // 本檔由 packages/promptex-js/scripts/gen-messages.mjs 自動產生,請勿手改。
2
+ //
3
+ // 來源是 crates/promptex-core/cli/create.json(允許清單、配置檔名、標記行與錯誤代碼),
4
+ // 格式版本取自 crates/promptex-core/src/graph.rs 的 FORMAT_VERSION。要改內容就改來源,
5
+ // 再跑 `pnpm --filter promptex-js run gen:messages` 重新生成;直接改本檔會被核對閘擋下。
6
+ /** 機器可讀輸出的格式版本(ADR-041)。 */
7
+ export const FORMAT_VERSION = "1.1";
8
+ /** `.gitignore` 區塊的標記行。已有這一行就不再追加。 */
9
+ export const GITIGNORE_MARKER = "# === promptex init 產生的忽略規則(重跑以本行判定,勿改寫)===";
10
+ /** 任一存在就判定目標已初始化。 */
11
+ export const CONFIG_FILES = ["promptex.config.ts", "promptex.config.py", "promptex.config.rs"];
12
+ /** 只決定模式的允許清單:第一層只有這些項目時走建立模式。 */
13
+ export const ALLOWLIST = [
14
+ { group: "macos", caseInsensitive: true, entries: [".DS_Store", "._*", ".localized", ".Spotlight-V100", ".Trashes", ".fseventsd", ".TemporaryItems"] },
15
+ { group: "windows", caseInsensitive: true, entries: ["Thumbs.db", "desktop.ini", "ehthumbs.db", "$RECYCLE.BIN", "System Volume Information"] },
16
+ { group: "linux", caseInsensitive: false, entries: [".directory", ".Trash-*", ".fuse_hidden*", ".nfs*", "lost+found"] },
17
+ { group: "vcs", caseInsensitive: false, entries: [".git", ".gitignore", ".gitattributes", ".gitkeep", ".hg", ".hgignore", ".svn", "LICENSE", "README.md", ".github"] },
18
+ { group: "editor", caseInsensitive: false, entries: [".vscode", ".idea", "*.iml", ".zed", ".cursor", ".claude"] },
19
+ ];
20
+ export const ERRORS = [
21
+ { code: "USAGE_UNKNOWN_ARGUMENT", exitCode: 2, messageKey: "scaffold.error.unknown_argument", details: ["arg"] },
22
+ { code: "USAGE_FLAG_MISSING_VALUE", exitCode: 2, messageKey: "scaffold.error.flag_missing_value", details: ["flag"] },
23
+ { code: "USAGE_TEMPLATE_UNSUPPORTED", exitCode: 2, messageKey: "scaffold.error.template_unsupported", details: ["template", "choices"] },
24
+ { code: "USAGE_JSON_REQUIRES_DRY_RUN", exitCode: 2, messageKey: "scaffold.error.json_requires_dry_run", details: [] },
25
+ { code: "USAGE_TARGET_IS_FILE", exitCode: 2, messageKey: "scaffold.error.target_is_file", details: ["path"] },
26
+ { code: "USAGE_TARGET_IS_SYMLINK", exitCode: 2, messageKey: "scaffold.error.target_is_symlink", details: ["path"] },
27
+ { code: "USAGE_TEMPLATE_NEEDS_EMPTY_DIR", exitCode: 2, messageKey: "scaffold.error.template_needs_empty_dir", details: ["template", "path", "entries"] },
28
+ { code: "USAGE_MISSING_INPUTS", exitCode: 2, messageKey: "scaffold.error.missing_inputs", details: ["flags"] },
29
+ { code: "RUN_GIT_UNAVAILABLE", exitCode: 1, messageKey: "scaffold.error.git_unavailable", details: [] },
30
+ { code: "RUN_STARTER_MISSING", exitCode: 1, messageKey: "scaffold.error.starter_missing", details: ["template", "path"] },
31
+ { code: "RUN_READ_FAILED", exitCode: 1, messageKey: "scaffold.error.read_failed", details: ["path", "detail"] },
32
+ { code: "RUN_WRITE_FAILED", exitCode: 1, messageKey: "scaffold.error.write_failed", details: ["path", "detail"] },
33
+ { code: "RUN_INSTALL_TOOL_MISSING", exitCode: 1, messageKey: "scaffold.error.install_tool_missing", details: ["command"] },
34
+ { code: "RUN_INSTALL_FAILED", exitCode: 1, messageKey: "scaffold.error.install_failed", details: ["command"] },
35
+ { code: "RUN_CANCELLED", exitCode: 1, messageKey: "scaffold.cancelled", details: [] },
36
+ ];
37
+ /** 依代碼查錯誤記錄。代碼不在表內是呼叫端的程式錯誤。 */
38
+ export function errorCode(code) {
39
+ const found = ERRORS.find((e) => e.code === code);
40
+ if (found === undefined)
41
+ throw new Error(`create.json 沒有錯誤代碼 ${code}`);
42
+ return found;
43
+ }
44
+ /** 單一樣式比對:`*` 只會出現在頭或尾。 */
45
+ function matchesPattern(name, pattern) {
46
+ if (pattern.startsWith('*'))
47
+ return name.endsWith(pattern.slice(1));
48
+ if (pattern.endsWith('*'))
49
+ return name.startsWith(pattern.slice(0, -1));
50
+ return name === pattern;
51
+ }
52
+ /** 只轉 ASCII 字母:三種語言的小寫轉換對非 ASCII 字元結果不同,而清單本身全是 ASCII。 */
53
+ function asciiLower(text) {
54
+ return text.replace(/[A-Z]/g, (c) => c.toLowerCase());
55
+ }
56
+ /** 第一層項目名稱是否在允許清單內。 */
57
+ export function isAllowlisted(name) {
58
+ return ALLOWLIST.some((group) => group.entries.some((pattern) => group.caseInsensitive ? matchesPattern(asciiLower(name), asciiLower(pattern)) : matchesPattern(name, pattern)));
59
+ }