create-promptex-js 0.0.1-alpha.1 → 1.0.0-alpha.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 +33 -48
- package/bin/create-promptex-js.mjs +5 -61
- package/dist/default-ref.d.ts +6 -0
- package/dist/default-ref.js +14 -0
- package/dist/index.d.ts +2 -22
- package/dist/index.js +213 -161
- package/dist/messages.generated.d.ts +9 -0
- package/dist/messages.generated.js +296 -0
- package/package.json +18 -16
- package/LICENSE-APACHE +0 -204
- package/LICENSE-MIT +0 -21
package/README.md
CHANGED
|
@@ -1,55 +1,40 @@
|
|
|
1
|
-
# create-promptex-js
|
|
1
|
+
# create-promptex-js
|
|
2
2
|
|
|
3
|
-
promptex
|
|
4
|
-
|
|
3
|
+
Scaffold a [promptex](https://github.com/promptex-ai/promptex) TypeScript
|
|
4
|
+
project, plugin or adapter from the
|
|
5
|
+
[promptex-templates](https://github.com/promptex-ai/promptex-templates)
|
|
6
|
+
starters.
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
本套件是早期 alpha。骨架只有四個檔案、一份範例宣告,沒有多單元配置、沒有
|
|
9
|
-
多平台選擇、沒有外掛與轉接器。正式版把骨架收在核心(`promptex init`),
|
|
10
|
-
本原型自帶一份副本,正式版發布時會被整份取代。
|
|
11
|
-
|
|
12
|
-
介面與骨架內容在正式版之前都會變,不保證任何相容性。
|
|
13
|
-
|
|
14
|
-
## 用法
|
|
15
|
-
|
|
16
|
-
不需要事先安裝,`npm create` 會取得後直接執行。需要 Node.js 22.14 以上。
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
npm create promptex-js my-prompts
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
產出:
|
|
23
|
-
|
|
24
|
-
```text
|
|
25
|
-
my-prompts/
|
|
26
|
-
├── .gitignore
|
|
27
|
-
├── README.md
|
|
28
|
-
├── package.json
|
|
29
|
-
└── prompts/skill.ts
|
|
8
|
+
```console
|
|
9
|
+
npm create promptex-js
|
|
30
10
|
```
|
|
31
11
|
|
|
32
|
-
|
|
33
|
-
`prompts/skill.py`,`--lang rust` 產出 `Cargo.toml` 與 `src/main.rs`。)
|
|
34
|
-
|
|
35
|
-
### 選項
|
|
12
|
+
Interactive by default; every answer has a flag for non-interactive use:
|
|
36
13
|
|
|
37
|
-
```
|
|
38
|
-
npm create promptex-js
|
|
39
|
-
|
|
40
|
-
--lang <語言> 骨架語言,預設 ts
|
|
41
|
-
--yes 目標目錄已有同名檔案時仍覆寫
|
|
14
|
+
```console
|
|
15
|
+
npm create promptex-js my-prompts -- --template project --name my-prompts --no-git --yes
|
|
42
16
|
```
|
|
43
17
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
18
|
+
| Flag | Meaning |
|
|
19
|
+
| :--- | :--- |
|
|
20
|
+
| `--template <t>` | `project`(消費專案)、`plugin`、`adapter` |
|
|
21
|
+
| `--name <n>` | kebab-case 名稱;預設取目標目錄名正規化 |
|
|
22
|
+
| `--ref <r>` | 抓 promptex-templates 的指定 branch/tag;預設 `v<本套件版本>` |
|
|
23
|
+
| `--source <p>` | 改用本地 promptex-templates checkout(離線與開發用) |
|
|
24
|
+
| `--git` / `--no-git` | 是否 `git init`;互動預設是 |
|
|
25
|
+
| `--install` | 鷹架後直接 `npm install`;預設只印下一步 |
|
|
26
|
+
| `--yes` | 跳過互動,全部取預設或旗標值 |
|
|
27
|
+
| `--locale <l>` | 訊息與範例資源的語系:`en`(預設)或 `zh-Hant` |
|
|
28
|
+
|
|
29
|
+
語系未以 `--locale` 指定時,依序讀 `PROMPTEX_LOCALE`、`LANGUAGE`、`LC_ALL`、
|
|
30
|
+
`LC_MESSAGES`、`LANG`,都沒有就用英文;`LC_ALL=C` 意為關掉在地化。`zh-TW`、
|
|
31
|
+
`zh-HK`、`zh-MO` 與 `zh-Hant-*` 都對到 `zh-Hant`。它同時決定鋪出來的範例資源
|
|
32
|
+
用哪個語系寫([ADR-030](https://github.com/promptex-ai/ssot)):那份內容從產出
|
|
33
|
+
當下起就歸你,框架不再改寫它。plugin 與 adapter 骨架裡教你怎麼填的註解維持英文。
|
|
34
|
+
|
|
35
|
+
目標目錄非空時列出既有項目、零寫入退出。模板不內嵌:單一來源住
|
|
36
|
+
promptex-templates 倉庫,鷹架時以 git 取得對應版本 tag(需要 git;
|
|
37
|
+
`{{promptexVersion}}` 於鷹架當下以本套件版本填入,三套件與 SDK 同版發布)。
|
|
38
|
+
|
|
39
|
+
`promptex init` 與本工具的分工:init 在既有專案就地補上 promptex(非互動、
|
|
40
|
+
極簡骨架);本工具從零開一個 repo(互動、三種模板、git init)。
|
|
@@ -1,63 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// create-promptex-js
|
|
2
|
+
// create-promptex-js 的轉發殼。
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
const USAGE = `create-promptex-js(alpha 原型)
|
|
10
|
-
|
|
11
|
-
用法:
|
|
12
|
-
npm create promptex-js <目錄> -- [--lang ts|python|rust] [--yes]
|
|
13
|
-
|
|
14
|
-
選項:
|
|
15
|
-
--lang <語言> 骨架語言,預設 ts
|
|
16
|
-
--yes 目標目錄已有同名檔案時仍覆寫
|
|
17
|
-
--help 顯示本說明
|
|
18
|
-
`
|
|
19
|
-
|
|
20
|
-
function parseArgs(argv) {
|
|
21
|
-
const args = { dir: null, lang: 'ts', assumeYes: false }
|
|
22
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
23
|
-
const token = argv[i]
|
|
24
|
-
if (token === '--yes' || token === '-y') args.assumeYes = true
|
|
25
|
-
else if (token === '--lang') {
|
|
26
|
-
const value = argv[i + 1]
|
|
27
|
-
if (value === undefined) throw new Error('--lang 後面缺少值')
|
|
28
|
-
if (!LANGS.includes(value)) throw new Error(`不支援的語言:${value}(可用:${LANGS.join('、')})`)
|
|
29
|
-
args.lang = value
|
|
30
|
-
i += 1
|
|
31
|
-
} else if (token.startsWith('-')) throw new Error(`未知的選項:${token}`)
|
|
32
|
-
else if (args.dir === null) args.dir = token
|
|
33
|
-
else throw new Error(`多餘的位置參數:${token}`)
|
|
34
|
-
}
|
|
35
|
-
if (args.dir === null) throw new Error('缺少目標目錄。用法:npm create promptex-js <目錄>')
|
|
36
|
-
return args
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function main() {
|
|
40
|
-
const argv = process.argv.slice(2)
|
|
41
|
-
if (argv.includes('--help') || argv.includes('-h')) {
|
|
42
|
-
process.stdout.write(USAGE)
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const args = parseArgs(argv)
|
|
47
|
-
const outcome = await scaffold(args.dir, args.lang, args.assumeYes)
|
|
48
|
-
|
|
49
|
-
if (outcome.aborted) {
|
|
50
|
-
process.stderr.write(`目標目錄已有同名檔案,未寫入任何檔案:\n`)
|
|
51
|
-
for (const path of outcome.conflicts) process.stderr.write(` ${path}\n`)
|
|
52
|
-
process.stderr.write(`確定要覆寫請加 --yes。\n`)
|
|
53
|
-
process.exitCode = 1
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
for (const path of outcome.written) process.stdout.write(`${path}\n`)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
await main().catch((error) => {
|
|
61
|
-
process.stderr.write(`${error.message}\n`)
|
|
62
|
-
process.exitCode = 1
|
|
63
|
-
})
|
|
4
|
+
// 邏輯住 `src/index.ts`(發布物為 `dist/index.js`),本檔只負責被 npm 當成
|
|
5
|
+
// bin 執行:鷹架的流程是頂層的一段腳本而非可呼叫的函式(它從 argv 讀到底、
|
|
6
|
+
// 逐步問完就結束),因此這裡 import 即執行,不另外呼叫誰。
|
|
7
|
+
import '../dist/index.js'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// 預設 templates ref 的判定(決策 d006)。三個 create-* 鷹架規則一致:
|
|
2
|
+
// GA 版號(X.Y.Z)抓對應 tag `v<版本>`;帶預發布後綴(-alpha.N、-beta.N、-rc.N 等)
|
|
3
|
+
// 的版本,tag 不一定存在,預設抓 `main`;使用者給 --ref 一律照用。
|
|
4
|
+
const GA_VERSION = /^\d+\.\d+\.\d+$/;
|
|
5
|
+
export function defaultRef(version) {
|
|
6
|
+
return GA_VERSION.test(version) ? `v${version}` : 'main';
|
|
7
|
+
}
|
|
8
|
+
/** `showCloneHint`:clone 失敗時是否附上「tag 可能尚未建立」提示,只在 GA 預設 ref 時為真。 */
|
|
9
|
+
export function resolveRef(explicit, version) {
|
|
10
|
+
if (explicit)
|
|
11
|
+
return { ref: explicit, showCloneHint: false };
|
|
12
|
+
const ref = defaultRef(version);
|
|
13
|
+
return { ref, showCloneHint: ref !== 'main' };
|
|
14
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export declare const LANGS: readonly Lang[];
|
|
4
|
-
/**
|
|
5
|
-
* 一份骨架的全部檔案,鍵為相對路徑。
|
|
6
|
-
*
|
|
7
|
-
* 純計算、不碰檔案系統:讓「骨架長什麼樣」可被直接比對,不必先寫到磁碟。
|
|
8
|
-
*/
|
|
9
|
-
export declare function skeleton(name: string, lang: Lang): Record<string, string>;
|
|
10
|
-
/** 一次初始化的處置結果。`aborted` 為真時 `written` 必為空。 */
|
|
11
|
-
export interface Outcome {
|
|
12
|
-
aborted: boolean;
|
|
13
|
-
conflicts: string[];
|
|
14
|
-
written: string[];
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* 在目標目錄產出骨架。
|
|
18
|
-
*
|
|
19
|
-
* 全有全無:有同名檔案且未帶 assumeYes 時一個檔案都不寫,避免留下半套骨架。
|
|
20
|
-
* 這與正式版 `promptex init` 的衝突處置一致。
|
|
21
|
-
*/
|
|
22
|
-
export declare function scaffold(targetDir: string, lang: Lang, assumeYes: boolean): Promise<Outcome>;
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export declare function normalizeName(raw: string): string;
|
package/dist/index.js
CHANGED
|
@@ -1,177 +1,229 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// 鷹架 CLI:從 promptex-templates 倉庫開出 TypeScript 的 project/plugin/
|
|
3
|
+
// adapter 專案。六階段:環境檢查 → 互動收集 → 模板取得 → 具現化 → 後處理 →
|
|
4
|
+
// 稽核輸出;非互動時(--yes 或無 TTY)全部取預設或旗標值。
|
|
2
5
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
''
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
''
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
scripts: { build: 'promptex build' },
|
|
46
|
-
dependencies: { 'promptex-js': '0.0.1-alpha.1' },
|
|
47
|
-
}, null, 2) + '\n',
|
|
48
|
-
'prompts/skill.ts': [
|
|
49
|
-
"import { defineSkill } from 'promptex-js'",
|
|
50
|
-
'',
|
|
51
|
-
'export const commitHelper = defineSkill({',
|
|
52
|
-
" id: '" + SKILL_ID + "',",
|
|
53
|
-
" name: '" + SKILL_NAME + "',",
|
|
54
|
-
" config: { whenToUse: '" + SKILL_WHEN + "' },",
|
|
55
|
-
' content: [',
|
|
56
|
-
" '" + SKILL_P1 + "',",
|
|
57
|
-
" '" + SKILL_P2 + "',",
|
|
58
|
-
' ],',
|
|
59
|
-
'})',
|
|
60
|
-
'',
|
|
61
|
-
].join('\n'),
|
|
62
|
-
'.gitignore': 'node_modules/\n',
|
|
63
|
-
'README.md': readme(name, 'ts'),
|
|
64
|
-
});
|
|
65
|
-
const pythonSkeleton = (name) => ({
|
|
66
|
-
'pyproject.toml': [
|
|
67
|
-
'[project]',
|
|
68
|
-
'name = "' + name + '"',
|
|
69
|
-
'version = "0.0.0"',
|
|
70
|
-
'requires-python = ">=3.10"',
|
|
71
|
-
'dependencies = ["promptex-py==0.0.1a1"]',
|
|
72
|
-
'',
|
|
73
|
-
].join('\n'),
|
|
74
|
-
'prompts/skill.py': [
|
|
75
|
-
'from promptex import config, define_skill',
|
|
76
|
-
'',
|
|
77
|
-
'commit_helper = define_skill(',
|
|
78
|
-
' "' + SKILL_ID + '",',
|
|
79
|
-
' "' + SKILL_NAME + '",',
|
|
80
|
-
' config=config(when_to_use="' + SKILL_WHEN + '"),',
|
|
81
|
-
' content=[',
|
|
82
|
-
' "' + SKILL_P1 + '",',
|
|
83
|
-
' "' + SKILL_P2 + '",',
|
|
84
|
-
' ],',
|
|
85
|
-
')',
|
|
86
|
-
'',
|
|
87
|
-
].join('\n'),
|
|
88
|
-
'.gitignore': '__pycache__/\n',
|
|
89
|
-
'README.md': readme(name, 'python'),
|
|
90
|
-
});
|
|
91
|
-
const rustSkeleton = (name) => ({
|
|
92
|
-
'Cargo.toml': [
|
|
93
|
-
'[package]',
|
|
94
|
-
'name = "' + name + '"',
|
|
95
|
-
'version = "0.0.0"',
|
|
96
|
-
'edition = "2021"',
|
|
97
|
-
'',
|
|
98
|
-
'[dependencies]',
|
|
99
|
-
'promptex-rs = "0.0.1-alpha.1"',
|
|
100
|
-
'',
|
|
101
|
-
].join('\n'),
|
|
102
|
-
'src/main.rs': [
|
|
103
|
-
'use promptex_rs::{build, define_skill, CapabilityConfig, SkillDef};',
|
|
104
|
-
'',
|
|
105
|
-
'fn main() -> Result<(), Box<dyn std::error::Error>> {',
|
|
106
|
-
' define_skill(SkillDef {',
|
|
107
|
-
' id: "' + SKILL_ID + '".into(),',
|
|
108
|
-
' name: "' + SKILL_NAME + '".into(),',
|
|
109
|
-
' config: CapabilityConfig::new().when_to_use("' + SKILL_WHEN + '"),',
|
|
110
|
-
' content: vec![',
|
|
111
|
-
' "' + SKILL_P1 + '".into(),',
|
|
112
|
-
' "' + SKILL_P2 + '".into(),',
|
|
113
|
-
' ],',
|
|
114
|
-
' });',
|
|
115
|
-
'',
|
|
116
|
-
' for path in build(".")? {',
|
|
117
|
-
' println!("{}", path.display());',
|
|
118
|
-
' }',
|
|
119
|
-
' Ok(())',
|
|
120
|
-
'}',
|
|
121
|
-
'',
|
|
122
|
-
].join('\n'),
|
|
123
|
-
'.gitignore': 'target/\n',
|
|
124
|
-
'README.md': readme(name, 'rust'),
|
|
6
|
+
// 模板不內嵌:單一來源住 promptex-templates,GA 版預設抓與本套件版本對應的
|
|
7
|
+
// tag(v<版本>),預發布版預設抓 main,--ref 覆寫、--source 改用本地 checkout
|
|
8
|
+
// (離線與開發用)。
|
|
9
|
+
//
|
|
10
|
+
// 用法:
|
|
11
|
+
// npm create promptex-js [目標目錄] [-- --template project|plugin|adapter \
|
|
12
|
+
// --name <kebab 名> --ref <branch|tag> --source <本地路徑> \
|
|
13
|
+
// --git|--no-git --install --yes --locale <語系>]
|
|
14
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
15
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import * as p from '@clack/prompts';
|
|
20
|
+
import mri from 'mri';
|
|
21
|
+
import { resolveRef } from './default-ref.js';
|
|
22
|
+
import { resolveLocale, t } from './messages.generated.js';
|
|
23
|
+
const here = fileURLToPath(new URL('.', import.meta.url));
|
|
24
|
+
const ownVersion = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8')).version;
|
|
25
|
+
const TEMPLATES = ['project', 'plugin', 'adapter'];
|
|
26
|
+
// 與 promptex init 的單元名正規化同一套規則(crates/promptex-core/src/init.rs)。
|
|
27
|
+
const RESERVED_UNIT_NAMES = ['cache', 'preview', 'content', 'content-index', 'graph', 'main'];
|
|
28
|
+
export function normalizeName(raw) {
|
|
29
|
+
let out = '';
|
|
30
|
+
for (const ch of raw.toLowerCase()) {
|
|
31
|
+
if (/[a-z0-9]/.test(ch))
|
|
32
|
+
out += ch;
|
|
33
|
+
else if (!out.endsWith('-'))
|
|
34
|
+
out += '-';
|
|
35
|
+
}
|
|
36
|
+
out = out.replace(/^-+|-+$/g, '');
|
|
37
|
+
return out === '' || RESERVED_UNIT_NAMES.includes(out) ? 'main' : out;
|
|
38
|
+
}
|
|
39
|
+
function bail(message) {
|
|
40
|
+
console.error(message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const TEMPLATES_REPO = 'https://github.com/promptex-ai/promptex-templates.git';
|
|
44
|
+
const args = mri(process.argv.slice(2), {
|
|
45
|
+
boolean: ['install', 'yes', 'git', 'help'],
|
|
46
|
+
string: ['template', 'name', 'ref', 'source', 'locale'],
|
|
47
|
+
alias: { h: 'help' },
|
|
125
48
|
});
|
|
49
|
+
const locale = resolveLocale(args.locale);
|
|
50
|
+
const separator = t('common.list_separator', locale);
|
|
51
|
+
if (args.help) {
|
|
52
|
+
console.log(t('scaffold.usage.js', locale));
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
const interactive = process.stdin.isTTY && !args.yes;
|
|
56
|
+
p.intro(`create-promptex-js v${ownVersion}`);
|
|
57
|
+
// ── 互動收集(缺什麼問什麼;非互動時取預設) ──
|
|
126
58
|
/**
|
|
127
|
-
*
|
|
59
|
+
* 缺什麼問什麼:旗標已給就用它,非互動(`--yes` 或無 TTY)取預設,其餘才問。
|
|
128
60
|
*
|
|
129
|
-
*
|
|
61
|
+
* 泛型鎖住「問到的答案與預設值同型」:`p.text` 回字串、`p.confirm` 回布林,
|
|
62
|
+
* 兩者的呼叫點接著就把值餵給只收該型別的下游。
|
|
130
63
|
*/
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
|
|
64
|
+
async function ask(value, prompt) {
|
|
65
|
+
if (value !== undefined)
|
|
66
|
+
return value;
|
|
67
|
+
if (!interactive)
|
|
68
|
+
return prompt.fallback;
|
|
69
|
+
const answer = await prompt.run();
|
|
70
|
+
if (p.isCancel(answer)) {
|
|
71
|
+
p.cancel(t('scaffold.cancelled', locale));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
return answer;
|
|
75
|
+
}
|
|
76
|
+
const targetDirRaw = await ask(args._[0], {
|
|
77
|
+
fallback: 'promptex-project',
|
|
78
|
+
run: () => p.text({
|
|
79
|
+
message: t('scaffold.prompt.target_dir', locale),
|
|
80
|
+
placeholder: 'promptex-project',
|
|
81
|
+
defaultValue: 'promptex-project',
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
const targetDir = resolve(String(targetDirRaw));
|
|
85
|
+
const template = await ask(args.template, {
|
|
86
|
+
fallback: 'project',
|
|
87
|
+
run: () => p.select({
|
|
88
|
+
message: t('scaffold.prompt.template', locale),
|
|
89
|
+
options: [
|
|
90
|
+
{ value: 'project', label: t('scaffold.template_option.project', locale) },
|
|
91
|
+
{ value: 'plugin', label: t('scaffold.template_option.plugin', locale) },
|
|
92
|
+
{ value: 'adapter', label: t('scaffold.template_option.adapter', locale) },
|
|
93
|
+
],
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
if (!TEMPLATES.includes(template)) {
|
|
97
|
+
bail(t('scaffold.error.template_unsupported', locale, { template, choices: TEMPLATES.join(separator) }));
|
|
98
|
+
}
|
|
99
|
+
const defaultName = normalizeName(basename(targetDir));
|
|
100
|
+
const name = normalizeName(String(await ask(args.name, {
|
|
101
|
+
fallback: defaultName,
|
|
102
|
+
run: () => p.text({ message: t('scaffold.prompt.name', locale), defaultValue: defaultName, placeholder: defaultName }),
|
|
103
|
+
})));
|
|
104
|
+
const wantGit = args.git !== undefined
|
|
105
|
+
? args.git
|
|
106
|
+
: await ask(undefined, {
|
|
107
|
+
fallback: true,
|
|
108
|
+
run: () => p.confirm({ message: t('scaffold.prompt.git_init', locale), initialValue: true }),
|
|
109
|
+
});
|
|
110
|
+
// ── 環境檢查:目標目錄非空即列衝突、零寫入退出(比照 promptex init)。 ──
|
|
111
|
+
if (existsSync(targetDir)) {
|
|
112
|
+
const entries = readdirSync(targetDir);
|
|
113
|
+
if (entries.length > 0) {
|
|
114
|
+
const listing = `${entries.slice(0, 10).join(separator)}${entries.length > 10 ? '…' : ''}`;
|
|
115
|
+
bail(t('scaffold.error.dir_not_empty', locale, { path: targetDir, entries: listing }));
|
|
134
116
|
}
|
|
135
|
-
if (lang === 'ts')
|
|
136
|
-
return tsSkeleton(name);
|
|
137
|
-
if (lang === 'python')
|
|
138
|
-
return pythonSkeleton(name);
|
|
139
|
-
if (lang === 'rust')
|
|
140
|
-
return rustSkeleton(name);
|
|
141
|
-
throw new Error(`不支援的語言:${lang}(可用:${LANGS.join('、')})`);
|
|
142
117
|
}
|
|
143
|
-
const
|
|
118
|
+
const gitAvailable = spawnSync('git', ['--version'], { stdio: 'ignore' }).status === 0;
|
|
119
|
+
if (!args.source && !gitAvailable)
|
|
120
|
+
bail(t('scaffold.error.git_required', locale));
|
|
121
|
+
// ── 模板取得:--source 讀本地 checkout;否則淺 clone,GA 版預設 ref 綁本套件版本、預發布版取 main。 ──
|
|
122
|
+
let sourceRoot;
|
|
123
|
+
let sourceLabel;
|
|
124
|
+
let cloneDir;
|
|
125
|
+
if (args.source) {
|
|
126
|
+
sourceRoot = join(resolve(args.source), 'typescript');
|
|
127
|
+
sourceLabel = t('scaffold.source.local', locale, { path: resolve(args.source) });
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const { ref, showCloneHint } = resolveRef(args.ref, ownVersion);
|
|
131
|
+
cloneDir = mkdtempSync(join(tmpdir(), 'promptex-templates-'));
|
|
144
132
|
try {
|
|
145
|
-
|
|
146
|
-
|
|
133
|
+
execFileSync('git', ['clone', '--depth', '1', '--branch', ref, TEMPLATES_REPO, cloneDir], {
|
|
134
|
+
stdio: ['ignore', 'ignore', 'inherit'],
|
|
135
|
+
});
|
|
147
136
|
}
|
|
148
137
|
catch {
|
|
149
|
-
|
|
138
|
+
const hint = showCloneHint ? t('scaffold.error.clone_hint', locale) : '';
|
|
139
|
+
bail(t('scaffold.error.clone_failed', locale, { ref, hint }));
|
|
150
140
|
}
|
|
151
|
-
|
|
141
|
+
sourceRoot = join(cloneDir, 'typescript');
|
|
142
|
+
sourceLabel = `promptex-templates@${ref}`;
|
|
143
|
+
}
|
|
144
|
+
const starterDir = join(sourceRoot, `${template}-starter`);
|
|
145
|
+
if (!existsSync(starterDir))
|
|
146
|
+
bail(t('scaffold.error.starter_missing', locale, { template, path: starterDir }));
|
|
147
|
+
// ── 具現化:複製、佔位替換、_gitignore 改名。 ──
|
|
152
148
|
/**
|
|
153
|
-
*
|
|
149
|
+
* 把一則訊息文字變成可直接嵌進單引號字串字面量的形態。
|
|
150
|
+
*
|
|
151
|
+
* 本套件只鋪 TypeScript 模板,而模板一律以單引號包住文字佔位符,因此界定符固定
|
|
152
|
+
* 是單引號。反斜線必須第一個處理:先跳脫界定符會讓後補的反斜線又被自己跳脫一次。
|
|
154
153
|
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
154
|
+
* 此前不跳脫,於是英文的 `this skill's content` 一進模板就提前關閉字串,
|
|
155
|
+
* `npm create promptex-js` 鋪出來的 plugin 與 adapter 專案在第一次建置即失敗
|
|
156
|
+
* (中文的對應文字沒有 ASCII 引號,因此只有英文使用者踩得到)。核心的 `init`
|
|
157
|
+
* 骨架走另一條路徑,同一個缺陷在那裡另行修正(`promptex_core::init`)。
|
|
157
158
|
*/
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
159
|
+
function escapeForLiteral(text) {
|
|
160
|
+
return text.replaceAll('\\', '\\\\').replaceAll('\n', '\\n').replaceAll("'", "\\'");
|
|
161
|
+
}
|
|
162
|
+
const tokens = {
|
|
163
|
+
'{{name}}': name,
|
|
164
|
+
'{{snakeName}}': name.replaceAll('-', '_'),
|
|
165
|
+
'{{unitName}}': name,
|
|
166
|
+
'{{promptexVersion}}': ownVersion,
|
|
167
|
+
// 模板的範例資源文字(ADR-030「一次性的起手內容」、AC-523):模板檔以佔位符留位,
|
|
168
|
+
// 寫檔當下代入本次語系的那一份。實作指引註解不在此列,它們在模板裡就是英文。
|
|
169
|
+
'{{adapterDemoRuleContent}}': escapeForLiteral(t('template.adapter_demo.rule_content', locale)),
|
|
170
|
+
'{{adapterDemoSkillContent}}': escapeForLiteral(t('template.adapter_demo.skill_content', locale)),
|
|
171
|
+
'{{adapterDemoSkillDescription}}': escapeForLiteral(t('template.adapter_demo.skill_description', locale)),
|
|
172
|
+
'{{adapterDemoSkillWhenToUse}}': escapeForLiteral(t('template.adapter_demo.skill_when_to_use', locale)),
|
|
173
|
+
'{{demoRuleName}}': escapeForLiteral(t('template.demo.rule_name', locale)),
|
|
174
|
+
'{{demoSkillName}}': escapeForLiteral(t('template.demo.skill_name', locale)),
|
|
175
|
+
'{{exampleRuleIntro}}': escapeForLiteral(t('template.example_rule.intro', locale)),
|
|
176
|
+
'{{exampleRuleName}}': escapeForLiteral(t('template.example_rule.name', locale)),
|
|
177
|
+
'{{exampleRuleOtherStates}}': escapeForLiteral(t('template.example_rule.other_states', locale)),
|
|
178
|
+
'{{exampleRuleScoped}}': escapeForLiteral(t('template.example_rule.scoped', locale)),
|
|
179
|
+
'{{exampleSkillDescription}}': escapeForLiteral(t('template.example_skill.description', locale)),
|
|
180
|
+
'{{exampleSkillIntro}}': escapeForLiteral(t('template.example_skill.intro', locale)),
|
|
181
|
+
'{{exampleSkillInvoked}}': escapeForLiteral(t('template.example_skill.invoked', locale)),
|
|
182
|
+
'{{exampleSkillInvokedRs}}': escapeForLiteral(t('template.example_skill.invoked_rs', locale)),
|
|
183
|
+
'{{exampleSkillName}}': escapeForLiteral(t('template.example_skill.name', locale)),
|
|
184
|
+
'{{exampleSkillWhenToUse}}': escapeForLiteral(t('template.example_skill.when_to_use', locale)),
|
|
185
|
+
'{{pluginDemoRuleContent}}': escapeForLiteral(t('template.plugin_demo.rule_content', locale)),
|
|
186
|
+
'{{pluginDemoSkillContent}}': escapeForLiteral(t('template.plugin_demo.skill_content', locale)),
|
|
187
|
+
'{{pluginDemoSkillDescription}}': escapeForLiteral(t('template.plugin_demo.skill_description', locale)),
|
|
188
|
+
'{{pluginDemoSkillWhenToUse}}': escapeForLiteral(t('template.plugin_demo.skill_when_to_use', locale)),
|
|
189
|
+
};
|
|
190
|
+
const substitute = (text) => Object.entries(tokens).reduce((acc, [token, value]) => acc.replaceAll(token, value), text);
|
|
191
|
+
function walk(dir, rel = '') {
|
|
192
|
+
const out = [];
|
|
193
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
194
|
+
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
195
|
+
if (entry.isDirectory())
|
|
196
|
+
out.push(...walk(join(dir, entry.name), relPath));
|
|
197
|
+
else
|
|
198
|
+
out.push(relPath);
|
|
175
199
|
}
|
|
176
|
-
return
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
const written = [];
|
|
203
|
+
for (const rel of walk(starterDir)) {
|
|
204
|
+
let destRel = substitute(rel);
|
|
205
|
+
if (destRel === '_gitignore')
|
|
206
|
+
destRel = '.gitignore';
|
|
207
|
+
const destPath = join(targetDir, destRel);
|
|
208
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
209
|
+
writeFileSync(destPath, substitute(readFileSync(join(starterDir, rel), 'utf8')));
|
|
210
|
+
written.push(destRel);
|
|
211
|
+
}
|
|
212
|
+
if (cloneDir)
|
|
213
|
+
rmSync(cloneDir, { recursive: true, force: true });
|
|
214
|
+
// ── 後處理:git init(可選)、--install 裝依賴。 ──
|
|
215
|
+
if (wantGit && gitAvailable)
|
|
216
|
+
spawnSync('git', ['init', '--quiet'], { cwd: targetDir, stdio: 'inherit' });
|
|
217
|
+
if (args.install) {
|
|
218
|
+
const r = spawnSync('npm', ['install'], { cwd: targetDir, stdio: 'inherit', shell: process.platform === 'win32' });
|
|
219
|
+
if (r.status !== 0)
|
|
220
|
+
bail(t('scaffold.error.install_failed.js', locale));
|
|
177
221
|
}
|
|
222
|
+
// ── 稽核輸出:寫了什麼、來源為何、下一步。 ──
|
|
223
|
+
p.log.success(t('scaffold.done.written', locale, { count: written.length, path: targetDir, source: sourceLabel }));
|
|
224
|
+
for (const rel of written)
|
|
225
|
+
p.log.message(` ${rel}`);
|
|
226
|
+
const nextSteps = template === 'project'
|
|
227
|
+
? [`cd ${targetDirRaw}`, ...(args.install ? [] : ['npm install']), 'npm run install:prompts']
|
|
228
|
+
: [`cd ${targetDirRaw}`, ...(args.install ? [] : ['npm install']), 'npm run build', 'cd example && npx promptex build --install'];
|
|
229
|
+
p.outro(`${t('scaffold.done.next_steps', locale)}\n${nextSteps.map((s, i) => ` ${i + 1}. ${s}`).join('\n')}`);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const MESSAGES: Record<string, Record<string, string>>;
|
|
2
|
+
/** 目錄認得的語系標籤。 */
|
|
3
|
+
export type LocaleTag = 'en' | 'zh-Hant';
|
|
4
|
+
/** POSIX 形態轉 BCP 47 並收斂為目錄認得的標籤;認不出來回 undefined。 */
|
|
5
|
+
export declare function parseLocale(tag: string | undefined): LocaleTag | undefined;
|
|
6
|
+
/** 依旗標與環境算出本次的語系。三項都認不出時回退路。 */
|
|
7
|
+
export declare function resolveLocale(flag?: string): LocaleTag;
|
|
8
|
+
/** 依語系取一則訊息,並以 `%{name}` 形態代入具名參數。 */
|
|
9
|
+
export declare function t(key: string, locale: LocaleTag, params?: Record<string, string | number>): string;
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// 本檔由 scripts/gen-messages.mjs 自動產生,請勿手改。
|
|
2
|
+
//
|
|
3
|
+
// 來源是核心的訊息目錄 crates/promptex-core/locales/(ADR-030)。要改文字就改那裡,
|
|
4
|
+
// 再跑 `npm run gen:messages` 重新生成;直接改本檔會被核對閘擋下。
|
|
5
|
+
export const MESSAGES = {
|
|
6
|
+
"common.list_separator": {
|
|
7
|
+
"en": ", ",
|
|
8
|
+
"zh-Hant": "、",
|
|
9
|
+
},
|
|
10
|
+
"scaffold.cancelled": {
|
|
11
|
+
"en": "Cancelled, no file was written.",
|
|
12
|
+
"zh-Hant": "已取消,未寫入任何檔案。",
|
|
13
|
+
},
|
|
14
|
+
"scaffold.done.next_steps": {
|
|
15
|
+
"en": "Next steps:",
|
|
16
|
+
"zh-Hant": "下一步:",
|
|
17
|
+
},
|
|
18
|
+
"scaffold.done.written": {
|
|
19
|
+
"en": "Wrote %{count} files → %{path} (template source: %{source})",
|
|
20
|
+
"zh-Hant": "已寫入 %{count} 個檔案 → %{path}(模板來源:%{source})",
|
|
21
|
+
},
|
|
22
|
+
"scaffold.error.clone_failed": {
|
|
23
|
+
"en": "git clone promptex-templates@%{ref} failed. %{hint}",
|
|
24
|
+
"zh-Hant": "git clone promptex-templates@%{ref} 失敗。%{hint}",
|
|
25
|
+
},
|
|
26
|
+
"scaffold.error.clone_hint": {
|
|
27
|
+
"en": "The tag for this version may not have been created yet: use --ref main to get the latest, or --source <local-path> to take it offline.",
|
|
28
|
+
"zh-Hant": "此版本的 tag 可能尚未建立:用 --ref main 抓最新、或 --source <本地路徑> 離線取用。",
|
|
29
|
+
},
|
|
30
|
+
"scaffold.error.dir_create": {
|
|
31
|
+
"en": "Creating %{path} failed: %{reason}",
|
|
32
|
+
"zh-Hant": "建立 %{path} 失敗:%{reason}",
|
|
33
|
+
},
|
|
34
|
+
"scaffold.error.dir_not_empty": {
|
|
35
|
+
"en": "Target directory is not empty: %{path}\nExisting entries: %{entries}\nNo file was written.",
|
|
36
|
+
"zh-Hant": "目標目錄非空:%{path}\n既有項目:%{entries}\n未寫入任何檔案。",
|
|
37
|
+
},
|
|
38
|
+
"scaffold.error.file_read": {
|
|
39
|
+
"en": "Reading %{path} failed: %{reason}",
|
|
40
|
+
"zh-Hant": "讀取 %{path} 失敗:%{reason}",
|
|
41
|
+
},
|
|
42
|
+
"scaffold.error.file_write": {
|
|
43
|
+
"en": "Writing %{path} failed: %{reason}",
|
|
44
|
+
"zh-Hant": "寫入 %{path} 失敗:%{reason}",
|
|
45
|
+
},
|
|
46
|
+
"scaffold.error.flag_missing_value": {
|
|
47
|
+
"en": "%{flag} is missing its value",
|
|
48
|
+
"zh-Hant": "%{flag} 缺值",
|
|
49
|
+
},
|
|
50
|
+
"scaffold.error.git_required": {
|
|
51
|
+
"en": "Getting the template needs git (or give a local promptex-templates path with --source).",
|
|
52
|
+
"zh-Hant": "取得模板需要 git(或用 --source 指定本地 promptex-templates 路徑)。",
|
|
53
|
+
},
|
|
54
|
+
"scaffold.error.install_failed.js": {
|
|
55
|
+
"en": "npm install failed; the scaffold files were written, the dependencies were not installed.",
|
|
56
|
+
"zh-Hant": "npm install 失敗,鷹架檔案已寫入、依賴未裝。",
|
|
57
|
+
},
|
|
58
|
+
"scaffold.error.install_failed.py": {
|
|
59
|
+
"en": "uv sync failed; the scaffold files were written, the dependencies were not installed.",
|
|
60
|
+
"zh-Hant": "uv sync 失敗,鷹架檔案已寫入、依賴未裝。",
|
|
61
|
+
},
|
|
62
|
+
"scaffold.error.install_failed.rs": {
|
|
63
|
+
"en": "cargo fetch failed; the scaffold files were written, the dependencies were not fetched.",
|
|
64
|
+
"zh-Hant": "cargo fetch 失敗,鷹架檔案已寫入、依賴未取。",
|
|
65
|
+
},
|
|
66
|
+
"scaffold.error.starter_missing": {
|
|
67
|
+
"en": "The template source has no %{template}-starter: %{path}",
|
|
68
|
+
"zh-Hant": "模板來源缺 %{template}-starter:%{path}",
|
|
69
|
+
},
|
|
70
|
+
"scaffold.error.template_dir_read": {
|
|
71
|
+
"en": "Reading the template directory failed: %{reason}",
|
|
72
|
+
"zh-Hant": "讀取模板目錄失敗:%{reason}",
|
|
73
|
+
},
|
|
74
|
+
"scaffold.error.template_unsupported": {
|
|
75
|
+
"en": "Unsupported template: %{template} (available: %{choices})",
|
|
76
|
+
"zh-Hant": "不支援的模板:%{template}(可用:%{choices})",
|
|
77
|
+
},
|
|
78
|
+
"scaffold.error.unknown_argument": {
|
|
79
|
+
"en": "Unrecognized argument: %{arg}",
|
|
80
|
+
"zh-Hant": "不認得的引數:%{arg}",
|
|
81
|
+
},
|
|
82
|
+
"scaffold.error.uv_missing": {
|
|
83
|
+
"en": "--install needs uv, but no uv executable was found; the scaffold files were written, the dependencies were not installed.",
|
|
84
|
+
"zh-Hant": "--install 需要 uv,但找不到 uv 執行檔;鷹架檔案已寫入、依賴未裝。",
|
|
85
|
+
},
|
|
86
|
+
"scaffold.help.install": {
|
|
87
|
+
"en": "run uv sync right after scaffolding",
|
|
88
|
+
"zh-Hant": "鷹架後直接 uv sync",
|
|
89
|
+
},
|
|
90
|
+
"scaffold.help.locale": {
|
|
91
|
+
"en": "message locale (en|zh-Hant), defaults to the environment variables then English",
|
|
92
|
+
"zh-Hant": "訊息語系(en|zh-Hant),預設依環境變數,再退回英文",
|
|
93
|
+
},
|
|
94
|
+
"scaffold.help.name": {
|
|
95
|
+
"en": "kebab-case name, defaults to the normalized target directory name",
|
|
96
|
+
"zh-Hant": "kebab-case 名稱,預設取目標目錄名正規化",
|
|
97
|
+
},
|
|
98
|
+
"scaffold.help.ref": {
|
|
99
|
+
"en": "take the given branch/tag of promptex-templates (defaults to v<this package's version>; pre-release versions default to main)",
|
|
100
|
+
"zh-Hant": "抓 promptex-templates 的指定 branch/tag(預設 v<本套件版本>;預發布版預設 main)",
|
|
101
|
+
},
|
|
102
|
+
"scaffold.help.source": {
|
|
103
|
+
"en": "use a local promptex-templates checkout instead (for offline and development use)",
|
|
104
|
+
"zh-Hant": "改用本地 promptex-templates checkout(離線與開發用)",
|
|
105
|
+
},
|
|
106
|
+
"scaffold.help.target_dir": {
|
|
107
|
+
"en": "target directory",
|
|
108
|
+
"zh-Hant": "目標目錄",
|
|
109
|
+
},
|
|
110
|
+
"scaffold.help.template": {
|
|
111
|
+
"en": "project|plugin|adapter",
|
|
112
|
+
"zh-Hant": "project|plugin|adapter",
|
|
113
|
+
},
|
|
114
|
+
"scaffold.help.yes": {
|
|
115
|
+
"en": "skip the interactive prompts, take defaults or flag values throughout",
|
|
116
|
+
"zh-Hant": "跳過互動,全部取預設或旗標值",
|
|
117
|
+
},
|
|
118
|
+
"scaffold.prompt.git_init": {
|
|
119
|
+
"en": "Initialize a git repository?",
|
|
120
|
+
"zh-Hant": "初始化 git 倉庫?",
|
|
121
|
+
},
|
|
122
|
+
"scaffold.prompt.git_init_yn": {
|
|
123
|
+
"en": "Initialize a git repository? (y/n)",
|
|
124
|
+
"zh-Hant": "初始化 git 倉庫?(y/n)",
|
|
125
|
+
},
|
|
126
|
+
"scaffold.prompt.name": {
|
|
127
|
+
"en": "Name (kebab-case)",
|
|
128
|
+
"zh-Hant": "名稱(kebab-case)",
|
|
129
|
+
},
|
|
130
|
+
"scaffold.prompt.target_dir": {
|
|
131
|
+
"en": "Target directory",
|
|
132
|
+
"zh-Hant": "目標目錄",
|
|
133
|
+
},
|
|
134
|
+
"scaffold.prompt.template": {
|
|
135
|
+
"en": "Template",
|
|
136
|
+
"zh-Hant": "模板",
|
|
137
|
+
},
|
|
138
|
+
"scaffold.prompt.template_choices": {
|
|
139
|
+
"en": "Template (%{choices})",
|
|
140
|
+
"zh-Hant": "模板(%{choices})",
|
|
141
|
+
},
|
|
142
|
+
"scaffold.source.local": {
|
|
143
|
+
"en": "local template (%{path})",
|
|
144
|
+
"zh-Hant": "本地模板(%{path})",
|
|
145
|
+
},
|
|
146
|
+
"scaffold.template_option.adapter": {
|
|
147
|
+
"en": "adapter: a publishable third-party platform adapter package",
|
|
148
|
+
"zh-Hant": "adapter:可發布的第三方平台 adapter 套件",
|
|
149
|
+
},
|
|
150
|
+
"scaffold.template_option.plugin": {
|
|
151
|
+
"en": "plugin: a publishable plugin package",
|
|
152
|
+
"zh-Hant": "plugin:可發布的 plugin 套件",
|
|
153
|
+
},
|
|
154
|
+
"scaffold.template_option.project": {
|
|
155
|
+
"en": "project: prompt source tree (a consuming project)",
|
|
156
|
+
"zh-Hant": "project:提示詞源碼樹(消費專案)",
|
|
157
|
+
},
|
|
158
|
+
"scaffold.usage.js": {
|
|
159
|
+
"en": "create-promptex-js [target-dir] --template project|plugin|adapter --name <kebab-name> [--ref <branch|tag>] [--source <local-path>] [--git|--no-git] [--install] [--yes] [--locale <locale>]",
|
|
160
|
+
"zh-Hant": "create-promptex-js [目標目錄] --template project|plugin|adapter --name <kebab 名> [--ref <branch|tag>] [--source <本地路徑>] [--git|--no-git] [--install] [--yes] [--locale <語系>]",
|
|
161
|
+
},
|
|
162
|
+
"scaffold.usage.py": {
|
|
163
|
+
"en": "uvx create-promptex-py [target-dir] [--template project|plugin|adapter] [--name <kebab-name>] [--ref <branch|tag>] [--source <local-path>] [--git|--no-git] [--install] [--yes] [--locale <locale>]",
|
|
164
|
+
"zh-Hant": "uvx create-promptex-py [目標目錄] [--template project|plugin|adapter] [--name <kebab 名>] [--ref <branch|tag>] [--source <本地路徑>] [--git|--no-git] [--install] [--yes] [--locale <語系>]",
|
|
165
|
+
},
|
|
166
|
+
"scaffold.usage.rs": {
|
|
167
|
+
"en": "cargo create-promptex-rs [target-dir] --template project|plugin|adapter --name <kebab-name> [--ref <branch|tag>] [--source <local-path>] [--git|--no-git] [--install] [--yes] [--locale <locale>]",
|
|
168
|
+
"zh-Hant": "cargo create-promptex-rs [目標目錄] --template project|plugin|adapter --name <kebab 名> [--ref <branch|tag>] [--source <本地路徑>] [--git|--no-git] [--install] [--yes] [--locale <語系>]",
|
|
169
|
+
},
|
|
170
|
+
"template.adapter_demo.rule_content": {
|
|
171
|
+
"en": "On the custom platform this rule degrades to always-on, with its scope noted in the body.",
|
|
172
|
+
"zh-Hant": "本規則在自訂平台會降級為常駐,並於內文標註適用範圍。",
|
|
173
|
+
},
|
|
174
|
+
"template.adapter_demo.skill_content": {
|
|
175
|
+
"en": "This skill is projected onto two placements at once: claude and the custom platform.",
|
|
176
|
+
"zh-Hant": "本技能會同時投影到 claude 與自訂平台兩個落點。",
|
|
177
|
+
},
|
|
178
|
+
"template.adapter_demo.skill_description": {
|
|
179
|
+
"en": "A demo skill the adapter example projects",
|
|
180
|
+
"zh-Hant": "adapter 範例用來輸出的示範技能",
|
|
181
|
+
},
|
|
182
|
+
"template.adapter_demo.skill_when_to_use": {
|
|
183
|
+
"en": "When checking what the custom adapter emits",
|
|
184
|
+
"zh-Hant": "驗證自訂 adapter 的產出時",
|
|
185
|
+
},
|
|
186
|
+
"template.demo.rule_name": {
|
|
187
|
+
"en": "Demo rule",
|
|
188
|
+
"zh-Hant": "示範規則",
|
|
189
|
+
},
|
|
190
|
+
"template.demo.skill_name": {
|
|
191
|
+
"en": "Demo skill",
|
|
192
|
+
"zh-Hant": "示範技能",
|
|
193
|
+
},
|
|
194
|
+
"template.example_rule.intro": {
|
|
195
|
+
"en": "This is the example resource produced by promptex init; change it or delete it as your project needs.",
|
|
196
|
+
"zh-Hant": "這是 promptex init 產出的範例資源,可依專案需求修改或刪除。",
|
|
197
|
+
},
|
|
198
|
+
"template.example_rule.name": {
|
|
199
|
+
"en": "Example rule",
|
|
200
|
+
"zh-Hant": "範例規則",
|
|
201
|
+
},
|
|
202
|
+
"template.example_rule.other_states": {
|
|
203
|
+
"en": "The other two states: declare the always-on flag instead and it loads unconditionally, regardless of what you are doing; declare neither and you get the default third state, where the resource is still installed on the target but never enters context on its own, appearing only when another resource references it explicitly. A skeleton that stopped at the third state would hand the author a rule that compiles and does nothing.",
|
|
204
|
+
"zh-Hant": "另兩態:改宣告常駐旗標即無條件載入,不看當下在做什麼;兩者都不宣告則是預設的第三態,資源照樣安裝到目標端,卻不會自動進入上下文,只在被其他資源顯式引用時登場。骨架若停在第三態,作者拿到的會是一份編得動、卻不會有任何作用的規則。",
|
|
205
|
+
},
|
|
206
|
+
"template.example_rule.scoped": {
|
|
207
|
+
"en": "This rule declares a scope (every file under the prompts source directory), which puts it in the scoped-loading state, one of the three loading states. The scope declaration appears in the artifact as that platform's own loading declaration, and the platform loads the rule into context only while working on a matching file.",
|
|
208
|
+
"zh-Hant": "本規則宣告了適用範圍(源碼目錄 prompts 下的全部檔案),因此屬載入宣告三態中的範圍載入態:範圍宣告在產物裡呈現為該平台的載入宣告,平台於符合的檔案上工作時才把規則載入上下文。",
|
|
209
|
+
},
|
|
210
|
+
"template.example_skill.description": {
|
|
211
|
+
"en": "Shows how an invoked resource is declared: a description of what it does and trigger text for when to use it",
|
|
212
|
+
"zh-Hant": "示範被喚起型資源的宣告:描述它做什麼、觸發語料說何時用",
|
|
213
|
+
},
|
|
214
|
+
"template.example_skill.intro": {
|
|
215
|
+
"en": "This is the example skill that ships with the project starter; change it or delete it as your project needs.",
|
|
216
|
+
"zh-Hant": "這是 project starter 附帶的範例技能,可依專案需求修改或刪除。",
|
|
217
|
+
},
|
|
218
|
+
"template.example_skill.invoked": {
|
|
219
|
+
"en": "A skill is an invoked resource: config.whenToUse is its trigger text (required), and the platform decides from it when to load the skill into context. Set against the rule next to it, which uses scoped loading, the two together show the most common pair of loading declarations.",
|
|
220
|
+
"zh-Hant": "技能屬被喚起型資源:config.whenToUse 是觸發語料(必填),平台依它判斷何時把本技能載入上下文。與相鄰的規則(範圍載入)對照,兩者合起來展示了載入宣告最常用的兩種形態。",
|
|
221
|
+
},
|
|
222
|
+
"template.example_skill.invoked_rs": {
|
|
223
|
+
"en": "A skill is an invoked resource: whenToUse on config is its trigger text (required, enforced by the typestate parameter), and the platform decides from it when to load the skill into context. Set against the rule next to it, which uses scoped loading, the two together show the most common pair of loading declarations.",
|
|
224
|
+
"zh-Hant": "技能屬被喚起型資源:config 的 whenToUse 是觸發語料(必填,型別狀態參數把關),平台依它判斷何時把本技能載入上下文。與相鄰的規則(範圍載入)對照,兩者合起來展示了載入宣告最常用的兩種形態。",
|
|
225
|
+
},
|
|
226
|
+
"template.example_skill.name": {
|
|
227
|
+
"en": "Example skill",
|
|
228
|
+
"zh-Hant": "範例技能",
|
|
229
|
+
},
|
|
230
|
+
"template.example_skill.when_to_use": {
|
|
231
|
+
"en": "When demonstrating how an invoked resource is declared",
|
|
232
|
+
"zh-Hant": "示範被喚起型資源的宣告形態時",
|
|
233
|
+
},
|
|
234
|
+
"template.plugin_demo.rule_content": {
|
|
235
|
+
"en": "The plugin appends a line to this rule's content during the transform stage.",
|
|
236
|
+
"zh-Hant": "本規則的內容會在 transform 階段被 plugin 追加一行。",
|
|
237
|
+
},
|
|
238
|
+
"template.plugin_demo.skill_content": {
|
|
239
|
+
"en": "The plugin appends a line to this skill's content during the transform stage.",
|
|
240
|
+
"zh-Hant": "本技能的內容會在 transform 階段被 plugin 追加一行。",
|
|
241
|
+
},
|
|
242
|
+
"template.plugin_demo.skill_description": {
|
|
243
|
+
"en": "A demo skill the plugin example builds against",
|
|
244
|
+
"zh-Hant": "plugin 範例用來建置的示範技能",
|
|
245
|
+
},
|
|
246
|
+
"template.plugin_demo.skill_when_to_use": {
|
|
247
|
+
"en": "When checking how the plugin rewrites a skill",
|
|
248
|
+
"zh-Hant": "驗證 plugin 對技能的改寫時",
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
const FALLBACK = 'en';
|
|
252
|
+
const LOCALE_ENV_ORDER = ["PROMPTEX_LOCALE", "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"];
|
|
253
|
+
/** POSIX 形態轉 BCP 47 並收斂為目錄認得的標籤;認不出來回 undefined。 */
|
|
254
|
+
export function parseLocale(tag) {
|
|
255
|
+
const normalized = String(tag ?? '').trim().split(/[.@]/)[0].replaceAll('_', '-');
|
|
256
|
+
if (normalized === '')
|
|
257
|
+
return undefined;
|
|
258
|
+
const upper = normalized.toUpperCase();
|
|
259
|
+
if (upper === 'C' || upper === 'POSIX')
|
|
260
|
+
return 'en';
|
|
261
|
+
const [language, ...rest] = normalized.toLowerCase().split('-');
|
|
262
|
+
if (language === 'en')
|
|
263
|
+
return 'en';
|
|
264
|
+
if (language === 'zh' && rest.some((p) => ['hant', 'tw', 'hk', 'mo'].includes(p)))
|
|
265
|
+
return 'zh-Hant';
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
/** 依旗標與環境算出本次的語系。三項都認不出時回退路。 */
|
|
269
|
+
export function resolveLocale(flag) {
|
|
270
|
+
const fromFlag = parseLocale(flag);
|
|
271
|
+
if (fromFlag !== undefined)
|
|
272
|
+
return fromFlag;
|
|
273
|
+
for (const key of LOCALE_ENV_ORDER) {
|
|
274
|
+
const raw = process.env[key];
|
|
275
|
+
if (raw === undefined || raw.trim() === '')
|
|
276
|
+
continue;
|
|
277
|
+
for (const part of raw.split(':')) {
|
|
278
|
+
const found = parseLocale(part);
|
|
279
|
+
if (found !== undefined)
|
|
280
|
+
return found;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return FALLBACK;
|
|
284
|
+
}
|
|
285
|
+
/** 依語系取一則訊息,並以 `%{name}` 形態代入具名參數。 */
|
|
286
|
+
export function t(key, locale, params) {
|
|
287
|
+
const entry = MESSAGES[key];
|
|
288
|
+
if (entry === undefined)
|
|
289
|
+
return key;
|
|
290
|
+
const template = entry[locale] ?? entry[FALLBACK];
|
|
291
|
+
if (template === undefined)
|
|
292
|
+
return key;
|
|
293
|
+
if (params === undefined)
|
|
294
|
+
return template;
|
|
295
|
+
return template.replace(/%\{([^}]+)\}/g, (whole, name) => (name in params ? String(params[name]) : whole));
|
|
296
|
+
}
|
package/package.json
CHANGED
|
@@ -1,35 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-promptex-js",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "promptex
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
|
+
"description": "Scaffold a promptex TypeScript project, plugin or adapter.",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
|
-
"author": "promptex-ai",
|
|
7
|
-
"homepage": "https://github.com/promptex-ai/promptex#readme",
|
|
8
6
|
"repository": {
|
|
9
7
|
"type": "git",
|
|
10
|
-
"url": "
|
|
8
|
+
"url": "https://github.com/promptex-ai/promptex.git",
|
|
9
|
+
"directory": "packages/create-promptex-js"
|
|
11
10
|
},
|
|
12
|
-
"bugs": {
|
|
13
|
-
"url": "https://github.com/promptex-ai/promptex/issues"
|
|
14
|
-
},
|
|
15
|
-
"keywords": ["prompt", "prompt-engineering", "ai", "llm", "agent", "claude", "skills", "scaffold"],
|
|
16
11
|
"type": "module",
|
|
17
|
-
"main": "./dist/index.js",
|
|
18
|
-
"types": "./dist/index.d.ts",
|
|
19
12
|
"bin": {
|
|
20
13
|
"create-promptex-js": "./bin/create-promptex-js.mjs"
|
|
21
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin",
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
22
19
|
"engines": {
|
|
23
|
-
"node": ">=
|
|
20
|
+
"node": ">=18"
|
|
24
21
|
},
|
|
25
|
-
"files": ["dist", "bin", "README.md", "LICENSE-MIT", "LICENSE-APACHE"],
|
|
26
22
|
"scripts": {
|
|
27
23
|
"build": "tsc -p tsconfig.json",
|
|
28
|
-
"
|
|
29
|
-
"test": "node --test
|
|
24
|
+
"pretest": "npm run build",
|
|
25
|
+
"test": "node --experimental-strip-types --test test/smoke.test.ts && npm run test:types",
|
|
26
|
+
"test:types": "tsc -p tsconfig.typelevel.json",
|
|
27
|
+
"prepack": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@clack/prompts": "^0.10.0",
|
|
31
|
+
"mri": "^1.2.0"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|
|
32
34
|
"@types/node": "^22.10.0",
|
|
33
|
-
"typescript": "^
|
|
35
|
+
"typescript": "^7.0.2"
|
|
34
36
|
}
|
|
35
37
|
}
|
package/LICENSE-APACHE
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
Apache License
|
|
2
|
-
Version 2.0, January 2004
|
|
3
|
-
http://www.apache.org/licenses/
|
|
4
|
-
|
|
5
|
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
-
|
|
7
|
-
1. Definitions.
|
|
8
|
-
|
|
9
|
-
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
-
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
-
|
|
12
|
-
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
-
the copyright owner that is granting the License.
|
|
14
|
-
|
|
15
|
-
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
-
other entities that control, are controlled by, or are under common
|
|
17
|
-
control with that entity. For the purposes of this definition,
|
|
18
|
-
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
-
direction or management of such entity, whether by contract or
|
|
20
|
-
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
-
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
-
|
|
23
|
-
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
-
exercising permissions granted by this License.
|
|
25
|
-
|
|
26
|
-
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
-
including but not limited to software source code, documentation
|
|
28
|
-
source, and configuration files.
|
|
29
|
-
|
|
30
|
-
"Object" form shall mean any form resulting from mechanical
|
|
31
|
-
transformation or translation of a Source form, including but
|
|
32
|
-
not limited to compiled object code, generated documentation,
|
|
33
|
-
and conversions to other media types.
|
|
34
|
-
|
|
35
|
-
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
-
Object form, made available under the License, as indicated by a
|
|
37
|
-
copyright notice that is included in or attached to the work
|
|
38
|
-
(an example is provided in the Appendix below).
|
|
39
|
-
|
|
40
|
-
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
-
form, that is based on (or derived from) the Work and for which the
|
|
42
|
-
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
-
represent, as a whole, an original work of authorship. For the
|
|
44
|
-
purposes of this License, Derivative Works shall not include works
|
|
45
|
-
that remain separable from, or merely link (or bind by name) to the
|
|
46
|
-
interfaces of, the Work and Derivative Works thereof.
|
|
47
|
-
|
|
48
|
-
"Contribution" shall mean any work of authorship, including the
|
|
49
|
-
original version of the Work and any modifications or additions
|
|
50
|
-
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
-
submitted to Licensor for inclusion in the Work by the copyright
|
|
52
|
-
owner or by an individual or Legal Entity authorized to submit on
|
|
53
|
-
behalf of the copyright owner. For the purposes of this definition,
|
|
54
|
-
"submitted" means any form of electronic, verbal, or written
|
|
55
|
-
communication sent to the Licensor or its representatives,
|
|
56
|
-
including but not limited to communication on electronic mailing
|
|
57
|
-
lists, source code control systems, and issue tracking systems that
|
|
58
|
-
are managed by, or on behalf of, the Licensor for the purpose of
|
|
59
|
-
discussing and improving the Work, but excluding communication that
|
|
60
|
-
is conspicuously marked or otherwise designated in writing by the
|
|
61
|
-
copyright owner as "Not a Contribution."
|
|
62
|
-
|
|
63
|
-
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
-
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
-
subsequently incorporated within the Work.
|
|
66
|
-
|
|
67
|
-
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
-
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
-
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
-
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
-
Work and such Derivative Works in Source or Object form.
|
|
73
|
-
|
|
74
|
-
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
-
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
-
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
-
(except as stated in this section) patent license to make, have made,
|
|
78
|
-
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
-
where such license applies only to those patent claims licensable
|
|
80
|
-
by such Contributor that are necessarily infringed by their
|
|
81
|
-
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
-
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
-
institute patent litigation against any entity (including a
|
|
84
|
-
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
-
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
-
or contributory patent infringement, then any patent licenses
|
|
87
|
-
granted to You under this License for that Work shall terminate
|
|
88
|
-
as of the date such litigation is filed.
|
|
89
|
-
|
|
90
|
-
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
-
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
-
modifications, and in Source or Object form, provided that You
|
|
93
|
-
meet the following conditions:
|
|
94
|
-
|
|
95
|
-
(a) You must give any other recipients of the Work or
|
|
96
|
-
Derivative Works a copy of this License; and
|
|
97
|
-
|
|
98
|
-
(b) You must cause any modified files to carry prominent notices
|
|
99
|
-
stating that You changed the files; and
|
|
100
|
-
|
|
101
|
-
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
-
that You distribute, all copyright, patent, trademark, and
|
|
103
|
-
attribution notices from the Source form of the Work,
|
|
104
|
-
excluding those notices that do not pertain to any part of
|
|
105
|
-
the Derivative Works; and
|
|
106
|
-
|
|
107
|
-
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
-
distribution, then any Derivative Works that You distribute must
|
|
109
|
-
include a readable copy of the attribution notices contained
|
|
110
|
-
within such NOTICE file, excluding those notices that do not
|
|
111
|
-
pertain to any part of the Derivative Works, in at least one
|
|
112
|
-
of the following places: within a NOTICE text file distributed
|
|
113
|
-
as part of the Derivative Works; within the Source form or
|
|
114
|
-
documentation, if provided along with the Derivative Works; or,
|
|
115
|
-
within a display generated by the Derivative Works, if and
|
|
116
|
-
wherever such third-party notices normally appear. The contents
|
|
117
|
-
of the NOTICE file are for informational purposes only and
|
|
118
|
-
do not modify the License. You may add Your own attribution
|
|
119
|
-
notices within Derivative Works that You distribute, alongside
|
|
120
|
-
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
-
that such additional attribution notices cannot be construed
|
|
122
|
-
as modifying the License.
|
|
123
|
-
|
|
124
|
-
You may add Your own copyright statement to Your modifications and
|
|
125
|
-
may provide additional or different license terms and conditions
|
|
126
|
-
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
-
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
-
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
-
the conditions stated in this License.
|
|
130
|
-
|
|
131
|
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
-
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
-
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
-
this License, without any additional terms or conditions.
|
|
135
|
-
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
-
the terms of any separate license agreement you may have executed
|
|
137
|
-
with Licensor regarding such Contributions.
|
|
138
|
-
|
|
139
|
-
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
-
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
-
except as required for reasonable and customary use in describing
|
|
142
|
-
the origin of the Work and reproducing the content of the NOTICE
|
|
143
|
-
file.
|
|
144
|
-
|
|
145
|
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
146
|
-
agreed to in writing, Licensor provides the Work (and each
|
|
147
|
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
148
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
149
|
-
implied, including, without limitation, any warranties or conditions
|
|
150
|
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
151
|
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
152
|
-
appropriateness of using or redistributing the Work and assume any
|
|
153
|
-
risks associated with Your exercise of permissions under this
|
|
154
|
-
License.
|
|
155
|
-
|
|
156
|
-
8. Limitation of Liability. In no event and under no legal theory,
|
|
157
|
-
whether in tort (including negligence), contract, or otherwise,
|
|
158
|
-
unless required by applicable law (such as deliberate and grossly
|
|
159
|
-
negligent acts) or agreed to in writing, shall any Contributor be
|
|
160
|
-
liable to You for damages, including any direct, indirect, special,
|
|
161
|
-
incidental, or consequential damages of any character arising as a
|
|
162
|
-
result of this License or out of the use or inability to use the
|
|
163
|
-
Work (including but not limited to damages for loss of goodwill,
|
|
164
|
-
work stoppage, computer failure or malfunction, or any and all
|
|
165
|
-
other commercial damages or losses), even if such Contributor
|
|
166
|
-
has been advised of the possibility of such damages.
|
|
167
|
-
|
|
168
|
-
9. Accepting Warranty or Additional Liability. While redistributing
|
|
169
|
-
the Work or Derivative Works thereof, You may choose to offer,
|
|
170
|
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
171
|
-
or other liability obligations and/or rights consistent with this
|
|
172
|
-
License. However, in accepting such obligations, You may act only
|
|
173
|
-
on Your own behalf and on Your sole responsibility, not on behalf
|
|
174
|
-
of any other Contributor, and only if You agree to indemnify,
|
|
175
|
-
defend, and hold each Contributor harmless for any liability
|
|
176
|
-
incurred by, or claims asserted against, such Contributor by reason
|
|
177
|
-
of your accepting any such warranty or additional liability.
|
|
178
|
-
|
|
179
|
-
END OF TERMS AND CONDITIONS
|
|
180
|
-
|
|
181
|
-
APPENDIX: How to apply the Apache License to your work.
|
|
182
|
-
|
|
183
|
-
To apply the Apache License to your work, attach the following
|
|
184
|
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
185
|
-
replaced with your own identifying information. (Don't include
|
|
186
|
-
the brackets!) The text should be enclosed in the appropriate
|
|
187
|
-
comment syntax for the file format. We also recommend that a
|
|
188
|
-
file or class name and description of purpose be included on the
|
|
189
|
-
same "printed page" as the copyright notice for easier
|
|
190
|
-
identification within third-party archives.
|
|
191
|
-
|
|
192
|
-
Copyright 2026 promptex-ai
|
|
193
|
-
|
|
194
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
195
|
-
you may not use this file except in compliance with the License.
|
|
196
|
-
You may obtain a copy of the License at
|
|
197
|
-
|
|
198
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
199
|
-
|
|
200
|
-
Unless required by applicable law or agreed to in writing, software
|
|
201
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
202
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
203
|
-
See the License for the specific language governing permissions and
|
|
204
|
-
limitations under the License.
|
package/LICENSE-MIT
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 promptex-ai
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|