@intra-mart/accel 0.1.0 → 0.2.0-dev.202605271647

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
@@ -6,7 +6,11 @@ intra-mart Accel Platform (iAP) 開発プロジェクトの作成・管理を行
6
6
 
7
7
  ## 必要環境
8
8
 
9
- - [Bun](https://bun.sh/) v1.0+
9
+ - 以下のいずれかのパッケージマネージャ(`--package-manager` で選択、デフォルト: Bun
10
+ - [Bun](https://bun.sh/) v1.0+
11
+ - [npm](https://www.npmjs.com/)
12
+ - [Yarn](https://classic.yarnpkg.com/) (classic v1)
13
+ - [pnpm](https://pnpm.io/)
10
14
  - Git(`--no-git-init` を指定しない場合)
11
15
 
12
16
  ## インストール
@@ -58,6 +62,7 @@ accel init my-project \
58
62
  | `--database` | string | データベース種別(`postgresql`, `oracle`, `sqlserver`) | `postgresql` |
59
63
  | `--javascript` | boolean | TypeScriptの代わりにJavaScriptを使用 | `false` |
60
64
  | `--juggling-project` | string | IM-Jugglingプロジェクトパス | - |
65
+ | `--package-manager` | string | パッケージマネージャ(`bun`, `npm`, `yarn`, `pnpm`)。対話には現れず、CLIオプションでのみ指定可能 | `bun` |
61
66
  | `--non-interactive` | boolean | 非対話モード(このモードでは `project-name` が必須) | `false` |
62
67
  | `--no-git-init` | boolean | git initをスキップ | `false`(git initする) |
63
68
  | `--skip-install` | boolean | 依存インストールをスキップ | `false` |
@@ -75,7 +80,9 @@ accel attach --non-interactive
75
80
  - ディレクトリを新規作成せず、カレントディレクトリを使用
76
81
  - git initは行わない
77
82
  - プロジェクト名のデフォルトはカレントディレクトリ名
78
- - **既存ファイルがある配備先はスキップ**(上書きしない)。スキップされたファイルは `hashsum.txt` および `deployedAssets` に記録されないため、`detach` の対象外となる
83
+ - **既存ファイルがある配備先はファイル単位で上書き確認**を行う。対話モードでは1ファイルごとに「Yes / No / All (以降全て上書き) / Skip-all (以降全てスキップ)」を選択。`--overwrite` 指定時は確認なしで全て上書き。非対話モードかつ `--overwrite` 未指定の場合は全てスキップ
84
+ - 上書きされたファイルは `hashsum.txt` および `deployedAssets` に記録される(`detach` の対象になる)
85
+ - スキップされたファイルは記録されない(`detach` の対象外)
79
86
 
80
87
  #### オプション一覧
81
88
 
@@ -91,6 +98,8 @@ accel attach --non-interactive
91
98
  | `--database` | string | データベース種別(`postgresql`, `oracle`, `sqlserver`) | `postgresql` |
92
99
  | `--javascript` | boolean | TypeScriptの代わりにJavaScriptを使用 | `false` |
93
100
  | `--juggling-project` | string | IM-Jugglingプロジェクトパス | - |
101
+ | `--package-manager` | string | パッケージマネージャ(`bun`, `npm`, `yarn`, `pnpm`)。対話には現れず、CLIオプションでのみ指定可能 | `bun` |
102
+ | `--overwrite` | boolean | 既存ファイルの上書き確認を行わず、全て上書きする | `false`(対話で確認 / 非対話ではスキップ) |
94
103
  | `--non-interactive` | boolean | 非対話モード | `false` |
95
104
  | `--skip-install` | boolean | 依存インストールをスキップ | `false` |
96
105
 
Binary file
@@ -1,14 +1,27 @@
1
1
  import type { AssetProvider, AccelSettings, HashsumEntry } from "../core/types.js";
2
+ export type OverwriteDecision = "overwrite" | "skip";
3
+ export type ExistingFileHandler = (relativePath: string) => Promise<OverwriteDecision>;
4
+ export type ProgressCallbacks = {
5
+ onFetchStart?: () => void;
6
+ onSectionDeploy?: (section: string, files: string[]) => void;
7
+ onInstallStart?: (command: string) => void;
8
+ onBuildStart?: (command: string) => void;
9
+ };
2
10
  export type DeployOptions = {
3
11
  projectDir: string;
4
12
  settings: AccelSettings;
5
13
  provider: AssetProvider;
6
14
  noInstall: boolean;
7
15
  skipExistingFiles?: boolean;
16
+ onExistingFile?: ExistingFileHandler;
17
+ progress?: ProgressCallbacks;
8
18
  };
9
19
  export type DeployResult = {
10
20
  deployedFiles: string[];
11
21
  hashEntries: HashsumEntry[];
12
22
  skippedFiles: string[];
23
+ overwrittenFiles: string[];
13
24
  };
25
+ export declare const getInstallCmds: (packageManager: string) => string[];
26
+ export declare const ROOT_SECTION = "(root)";
14
27
  export declare const deployAssets: (options: DeployOptions) => Promise<DeployResult>;
@@ -1,5 +1,5 @@
1
1
  import { readFile, writeFile, mkdir, stat } from "node:fs/promises";
2
- import { join, dirname } from "node:path";
2
+ import { join, dirname, resolve, sep } from "node:path";
3
3
  import { labelToSemver } from "../core/version-map.js";
4
4
  import { walkAssetRepo } from "./walker.js";
5
5
  import { processMarkdown } from "../markdown/processor.js";
@@ -16,14 +16,46 @@ const fileExists = async (path) => {
16
16
  return false;
17
17
  }
18
18
  };
19
+ const isWithinDir = (parentDir, candidate) => {
20
+ const parent = resolve(parentDir);
21
+ const target = resolve(candidate);
22
+ if (target === parent)
23
+ return true;
24
+ const prefix = parent.endsWith(sep) ? parent : parent + sep;
25
+ return target.startsWith(prefix);
26
+ };
27
+ export const getInstallCmds = (packageManager) => {
28
+ switch (packageManager) {
29
+ case "npm":
30
+ return ["npm ci", "npm run build"];
31
+ case "yarn":
32
+ return ["yarn install --frozen-lockfile", "yarn run build"];
33
+ case "pnpm":
34
+ return ["pnpm install --frozen-lockfile", "pnpm run build"];
35
+ case "bun":
36
+ default:
37
+ return ["bun ci", "bun run build"];
38
+ }
39
+ };
19
40
  const collectReplacements = (entry) => {
20
41
  if (!entry.meta?.replacements)
21
42
  return [];
22
43
  return entry.meta.replacements;
23
44
  };
45
+ export const ROOT_SECTION = "(root)";
46
+ const sectionOf = (relativePath) => {
47
+ const idx = relativePath.indexOf("/");
48
+ return idx === -1 ? ROOT_SECTION : relativePath.slice(0, idx) + "/";
49
+ };
50
+ const fileLabelOf = (relativePath, section) => {
51
+ if (section === ROOT_SECTION)
52
+ return relativePath;
53
+ return relativePath.slice(section.length);
54
+ };
24
55
  export const deployAssets = async (options) => {
25
- const { projectDir, settings, provider, noInstall, skipExistingFiles } = options;
56
+ const { projectDir, settings, provider, noInstall, skipExistingFiles, onExistingFile, progress, } = options;
26
57
  try {
58
+ progress?.onFetchStart?.();
27
59
  const repoDir = await provider.fetch();
28
60
  const semver = labelToSemver(settings.accelplatformVersion);
29
61
  const context = {
@@ -38,19 +70,52 @@ export const deployAssets = async (options) => {
38
70
  accelplatformVersion: settings.accelplatformVersion,
39
71
  database: settings.database,
40
72
  projectVersion: settings.projectVersion,
73
+ packageManager: settings.packageManager,
74
+ javascript: settings.javascript,
41
75
  };
42
76
  const entries = await walkAssetRepo(repoDir, semver, context);
43
77
  const deployedFiles = [];
44
78
  const hashEntries = [];
45
79
  const deployedAssets = {};
46
80
  const skippedFiles = [];
81
+ const overwrittenFiles = [];
82
+ let currentSection = null;
47
83
  for (const entry of entries) {
48
84
  const targetPath = entry.relativePath;
49
85
  const destPath = join(projectDir, targetPath);
50
- if (skipExistingFiles && (await fileExists(destPath))) {
51
- skippedFiles.push(targetPath);
86
+ const section = sectionOf(targetPath);
87
+ if (section !== currentSection) {
88
+ currentSection = section;
89
+ if (progress?.onSectionDeploy) {
90
+ const sectionFiles = entries
91
+ .filter((e) => sectionOf(e.relativePath) === section)
92
+ .map((e) => fileLabelOf(e.relativePath, section));
93
+ progress.onSectionDeploy(section, sectionFiles);
94
+ }
95
+ }
96
+ if (!isWithinDir(projectDir, destPath)) {
97
+ console.warn(`Warning: skipping asset with path escaping project directory: ${targetPath}`);
98
+ continue;
99
+ }
100
+ if (!isWithinDir(repoDir, entry.sourcePath)) {
101
+ console.warn(`Warning: skipping asset with source path escaping asset repo: ${entry.sourcePath}`);
52
102
  continue;
53
103
  }
104
+ const exists = await fileExists(destPath);
105
+ if (exists) {
106
+ let decision = "overwrite";
107
+ if (skipExistingFiles) {
108
+ decision = "skip";
109
+ }
110
+ else if (onExistingFile) {
111
+ decision = await onExistingFile(targetPath);
112
+ }
113
+ if (decision === "skip") {
114
+ skippedFiles.push(targetPath);
115
+ continue;
116
+ }
117
+ overwrittenFiles.push(targetPath);
118
+ }
54
119
  let content = await readFile(entry.sourcePath, "utf-8");
55
120
  const replacements = collectReplacements(entry);
56
121
  if (replacements.length > 0) {
@@ -79,17 +144,23 @@ export const deployAssets = async (options) => {
79
144
  await writeSettings(projectDir, updatedSettings);
80
145
  await writeHashsum(projectDir, hashEntries);
81
146
  if (!noInstall) {
82
- const installCmds = ["bun ci", "bun run build"];
83
- for (const cmd of installCmds) {
84
- try {
85
- execSync(cmd, { cwd: projectDir, stdio: "pipe" });
86
- }
87
- catch {
88
- console.warn(`Warning: install command failed: ${cmd} in ${projectDir}`);
89
- }
147
+ const [installCmd, buildCmd] = getInstallCmds(settings.packageManager);
148
+ progress?.onInstallStart?.(installCmd);
149
+ try {
150
+ execSync(installCmd, { cwd: projectDir, stdio: "pipe" });
151
+ }
152
+ catch {
153
+ console.warn(`Warning: install command failed: ${installCmd} in ${projectDir}`);
154
+ }
155
+ progress?.onBuildStart?.(buildCmd);
156
+ try {
157
+ execSync(buildCmd, { cwd: projectDir, stdio: "pipe" });
158
+ }
159
+ catch {
160
+ console.warn(`Warning: build command failed: ${buildCmd} in ${projectDir}`);
90
161
  }
91
162
  }
92
- return { deployedFiles, hashEntries, skippedFiles };
163
+ return { deployedFiles, hashEntries, skippedFiles, overwrittenFiles };
93
164
  }
94
165
  finally {
95
166
  await provider.cleanup().catch(() => { });
@@ -53,6 +53,8 @@ const findMatchingVersionDirs = async (repoDir, targetSemver) => {
53
53
  accelplatformVersion: "",
54
54
  database: "",
55
55
  projectVersion: "",
56
+ packageManager: "",
57
+ javascript: false,
56
58
  };
57
59
  if (evaluateCondition(meta.conditions, context)) {
58
60
  matched.push(dirPath);
@@ -47,6 +47,15 @@ export declare const attachCommand: import("citty").CommandDef<{
47
47
  type: "string";
48
48
  description: string;
49
49
  };
50
+ "package-manager": {
51
+ type: "string";
52
+ description: string;
53
+ };
54
+ overwrite: {
55
+ type: "boolean";
56
+ description: string;
57
+ default: false;
58
+ };
50
59
  "skip-install": {
51
60
  type: "boolean";
52
61
  description: string;
@@ -4,12 +4,15 @@ import { stat } from "node:fs/promises";
4
4
  import { basename, join } from "node:path";
5
5
  import { CLI_VERSION } from "../core/constants.js";
6
6
  import { runPrompts } from "../interactive/prompts.js";
7
- import { deployAssets } from "../asset/deployer.js";
7
+ import { deployAssets, } from "../asset/deployer.js";
8
8
  import { createLocalAssetProvider } from "../asset/local-provider.js";
9
9
  import { createFileAssetProvider } from "../asset/file-provider.js";
10
10
  import { defaultAssetSourcePath } from "../asset/default-source.js";
11
11
  import { getMessage } from "../i18n/index.js";
12
12
  import { detectLocale } from "../utils/locale-detect.js";
13
+ import { printSummary } from "../interactive/summary.js";
14
+ import { printNextSteps } from "../interactive/next-steps.js";
15
+ import { buildProgressCallbacks } from "../interactive/progress.js";
13
16
  export const attachCommand = defineCommand({
14
17
  meta: {
15
18
  name: "attach",
@@ -64,6 +67,15 @@ export const attachCommand = defineCommand({
64
67
  type: "string",
65
68
  description: "Maven artifactId (defaults to project name)",
66
69
  },
70
+ "package-manager": {
71
+ type: "string",
72
+ description: "Package manager (bun, npm, yarn, pnpm). Default: bun",
73
+ },
74
+ overwrite: {
75
+ type: "boolean",
76
+ description: "Overwrite existing files without per-file confirmation",
77
+ default: false,
78
+ },
67
79
  "skip-install": {
68
80
  type: "boolean",
69
81
  description: "Skip dependency installation",
@@ -112,6 +124,7 @@ export const attachCommand = defineCommand({
112
124
  agent: agentArg
113
125
  ? agentArg.split(",").map((a) => a.trim())
114
126
  : undefined,
127
+ packageManager: args["package-manager"],
115
128
  locale,
116
129
  noInteraction: args["non-interactive"],
117
130
  isInit: false,
@@ -138,6 +151,7 @@ export const attachCommand = defineCommand({
138
151
  agents: resolved.agents,
139
152
  javascript: resolved.javascript,
140
153
  locale: resolved.locale,
154
+ packageManager: resolved.packageManager,
141
155
  jugglingProject: resolved.jugglingProject,
142
156
  deployedAssets: {},
143
157
  };
@@ -146,22 +160,89 @@ export const attachCommand = defineCommand({
146
160
  const provider = serverUrl
147
161
  ? createLocalAssetProvider(serverUrl)
148
162
  : createFileAssetProvider(assetSource);
149
- p.log.info(getMessage("progress.deploying", locale));
163
+ const isInteractive = !args["non-interactive"];
164
+ const overwriteFlag = args.overwrite;
165
+ if (isInteractive) {
166
+ printSummary(settings, projectDir, locale);
167
+ }
168
+ let skipAll = false;
169
+ let onExistingFile;
170
+ if (!isInteractive) {
171
+ if (!overwriteFlag)
172
+ skipAll = true;
173
+ }
174
+ else if (!overwriteFlag) {
175
+ let sticky;
176
+ onExistingFile = async (path) => {
177
+ if (sticky === "all")
178
+ return "overwrite";
179
+ if (sticky === "skipAll")
180
+ return "skip";
181
+ const choice = await p.select({
182
+ message: getMessage("attach.overwritePrompt", locale, { path }),
183
+ options: [
184
+ {
185
+ value: "yes",
186
+ label: getMessage("attach.overwritePrompt.yes", locale),
187
+ },
188
+ {
189
+ value: "no",
190
+ label: getMessage("attach.overwritePrompt.no", locale),
191
+ },
192
+ {
193
+ value: "all",
194
+ label: getMessage("attach.overwritePrompt.all", locale),
195
+ },
196
+ {
197
+ value: "skipAll",
198
+ label: getMessage("attach.overwritePrompt.skipAll", locale),
199
+ },
200
+ ],
201
+ });
202
+ if (p.isCancel(choice))
203
+ process.exit(0);
204
+ if (choice === "all") {
205
+ sticky = "all";
206
+ return "overwrite";
207
+ }
208
+ if (choice === "skipAll") {
209
+ sticky = "skipAll";
210
+ return "skip";
211
+ }
212
+ return choice === "yes" ? "overwrite" : "skip";
213
+ };
214
+ }
150
215
  const result = await deployAssets({
151
216
  projectDir,
152
217
  settings,
153
218
  provider,
154
219
  noInstall: args["skip-install"],
155
- skipExistingFiles: true,
220
+ skipExistingFiles: skipAll || undefined,
221
+ onExistingFile,
222
+ progress: buildProgressCallbacks(locale),
156
223
  });
224
+ for (const path of result.overwrittenFiles) {
225
+ p.log.info(getMessage("attach.overwritten", locale, { path }));
226
+ }
157
227
  for (const path of result.skippedFiles) {
158
228
  p.log.warn(getMessage("warning.fileExists", locale, { path }));
159
229
  }
230
+ if (result.overwrittenFiles.length > 0) {
231
+ p.log.info(getMessage("attach.overwriteSummary", locale, {
232
+ count: String(result.overwrittenFiles.length),
233
+ }));
234
+ }
160
235
  if (result.skippedFiles.length > 0) {
161
236
  p.log.info(getMessage("attach.skipSummary", locale, {
162
237
  count: String(result.skippedFiles.length),
163
238
  }));
164
239
  }
165
240
  p.log.success(getMessage("progress.complete", locale));
241
+ printNextSteps({
242
+ projectDir,
243
+ agents: resolved.agents,
244
+ isInit: false,
245
+ locale,
246
+ });
166
247
  },
167
248
  });
@@ -48,6 +48,10 @@ export declare const initCommand: import("citty").CommandDef<{
48
48
  type: "string";
49
49
  description: string;
50
50
  };
51
+ "package-manager": {
52
+ type: "string";
53
+ description: string;
54
+ };
51
55
  "git-init": {
52
56
  type: "boolean";
53
57
  description: string;
@@ -12,6 +12,9 @@ import { defaultAssetSourcePath } from "../asset/default-source.js";
12
12
  import { getMessage } from "../i18n/index.js";
13
13
  import { detectLocale } from "../utils/locale-detect.js";
14
14
  import { commandExists } from "../utils/command-exists.js";
15
+ import { printSummary } from "../interactive/summary.js";
16
+ import { printNextSteps } from "../interactive/next-steps.js";
17
+ import { buildProgressCallbacks } from "../interactive/progress.js";
15
18
  export const initCommand = defineCommand({
16
19
  meta: {
17
20
  name: "init",
@@ -67,6 +70,10 @@ export const initCommand = defineCommand({
67
70
  type: "string",
68
71
  description: "Maven artifactId (defaults to project name)",
69
72
  },
73
+ "package-manager": {
74
+ type: "string",
75
+ description: "Package manager (bun, npm, yarn, pnpm). Default: bun",
76
+ },
70
77
  "git-init": {
71
78
  type: "boolean",
72
79
  description: "Initialize git repository (use --no-git-init to disable)",
@@ -113,6 +120,7 @@ export const initCommand = defineCommand({
113
120
  agent: agentArg
114
121
  ? agentArg.split(",").map((a) => a.trim())
115
122
  : undefined,
123
+ packageManager: args["package-manager"],
116
124
  locale,
117
125
  withGit: args["git-init"],
118
126
  noInteraction: args["non-interactive"],
@@ -149,20 +157,25 @@ export const initCommand = defineCommand({
149
157
  agents: resolved.agents,
150
158
  javascript: resolved.javascript,
151
159
  locale: resolved.locale,
160
+ packageManager: resolved.packageManager,
152
161
  jugglingProject: resolved.jugglingProject,
153
162
  deployedAssets: {},
154
163
  };
164
+ const isInteractive = !args["non-interactive"];
165
+ if (isInteractive) {
166
+ printSummary(settings, projectDir, locale);
167
+ }
155
168
  const serverUrl = args["asset-server-url"];
156
169
  const assetSource = args["asset-source"] ?? defaultAssetSourcePath();
157
170
  const provider = serverUrl
158
171
  ? createLocalAssetProvider(serverUrl)
159
172
  : createFileAssetProvider(assetSource);
160
- p.log.info(getMessage("progress.deploying", locale));
161
173
  await deployAssets({
162
174
  projectDir,
163
175
  settings,
164
176
  provider,
165
177
  noInstall: args["skip-install"],
178
+ progress: buildProgressCallbacks(locale),
166
179
  });
167
180
  if (resolved.withGit) {
168
181
  if (commandExists("git")) {
@@ -179,5 +192,11 @@ export const initCommand = defineCommand({
179
192
  }
180
193
  }
181
194
  p.log.success(getMessage("progress.complete", locale));
195
+ printNextSteps({
196
+ projectDir,
197
+ agents: resolved.agents,
198
+ isInit: true,
199
+ locale,
200
+ });
182
201
  },
183
202
  });
@@ -1,5 +1,5 @@
1
1
  import { satisfies } from "semver";
2
- import { isAndCondition, isOrCondition, isVersionCondition, isModuleCondition, isLocaleCondition, isAgentCondition, } from "./types.js";
2
+ import { isAndCondition, isOrCondition, isVersionCondition, isModuleCondition, isLocaleCondition, isAgentCondition, isPackageManagerCondition, isJavascriptCondition, } from "./types.js";
3
3
  export const evaluateCondition = (condition, context) => {
4
4
  if (condition === undefined) {
5
5
  return true;
@@ -22,5 +22,11 @@ export const evaluateCondition = (condition, context) => {
22
22
  if (isAgentCondition(condition)) {
23
23
  return context.agents.includes(condition.agent);
24
24
  }
25
+ if (isPackageManagerCondition(condition)) {
26
+ return context.packageManager === condition.packageManager;
27
+ }
28
+ if (isJavascriptCondition(condition)) {
29
+ return context.javascript === condition.javascript;
30
+ }
25
31
  return false;
26
32
  };
@@ -8,4 +8,6 @@ export declare const MODULE_OPTIONS: readonly ["workflow", "bpm", "copilot", "im
8
8
  export type ModuleOption = (typeof MODULE_OPTIONS)[number];
9
9
  export declare const LOCALE_OPTIONS: readonly ["ja", "en", "zh_CN"];
10
10
  export type LocaleOption = (typeof LOCALE_OPTIONS)[number];
11
+ export declare const PACKAGE_MANAGER_OPTIONS: readonly ["bun", "npm", "yarn", "pnpm"];
12
+ export type PackageManagerOption = (typeof PACKAGE_MANAGER_OPTIONS)[number];
11
13
  export declare const DEFAULT_SETTINGS: Omit<AccelSettings, "cliVersion" | "createdAt" | "deployedAssets">;
@@ -10,6 +10,12 @@ export const MODULE_OPTIONS = [
10
10
  "kaiden",
11
11
  ];
12
12
  export const LOCALE_OPTIONS = ["ja", "en", "zh_CN"];
13
+ export const PACKAGE_MANAGER_OPTIONS = [
14
+ "bun",
15
+ "npm",
16
+ "yarn",
17
+ "pnpm",
18
+ ];
13
19
  export const DEFAULT_SETTINGS = {
14
20
  name: "my-accel-project",
15
21
  artifactId: "",
@@ -22,5 +28,6 @@ export const DEFAULT_SETTINGS = {
22
28
  agents: ["claude-code", "github-copilot"],
23
29
  javascript: false,
24
30
  locale: "ja",
31
+ packageManager: "bun",
25
32
  jugglingProject: null,
26
33
  };
@@ -16,7 +16,13 @@ export type LocaleCondition = {
16
16
  export type AgentCondition = {
17
17
  agent: string;
18
18
  };
19
- export type LeafCondition = VersionCondition | ModuleCondition | LocaleCondition | AgentCondition;
19
+ export type PackageManagerCondition = {
20
+ packageManager: string;
21
+ };
22
+ export type JavascriptCondition = {
23
+ javascript: boolean;
24
+ };
25
+ export type LeafCondition = VersionCondition | ModuleCondition | LocaleCondition | AgentCondition | PackageManagerCondition | JavascriptCondition;
20
26
  export type Condition = AndCondition | OrCondition | LeafCondition;
21
27
  export type TextReplacement = {
22
28
  type: "text";
@@ -69,6 +75,7 @@ export type AccelSettings = {
69
75
  agents: string[];
70
76
  javascript: boolean;
71
77
  locale: string;
78
+ packageManager: string;
72
79
  jugglingProject: string | null;
73
80
  deployedAssets: Record<string, string>;
74
81
  };
@@ -93,6 +100,8 @@ export type EvalContext = {
93
100
  accelplatformVersion: string;
94
101
  database: string;
95
102
  projectVersion: string;
103
+ packageManager: string;
104
+ javascript: boolean;
96
105
  };
97
106
  export declare const isAndCondition: (c: Condition) => c is AndCondition;
98
107
  export declare const isOrCondition: (c: Condition) => c is OrCondition;
@@ -100,3 +109,5 @@ export declare const isVersionCondition: (c: Condition) => c is VersionCondition
100
109
  export declare const isModuleCondition: (c: Condition) => c is ModuleCondition;
101
110
  export declare const isLocaleCondition: (c: Condition) => c is LocaleCondition;
102
111
  export declare const isAgentCondition: (c: Condition) => c is AgentCondition;
112
+ export declare const isPackageManagerCondition: (c: Condition) => c is PackageManagerCondition;
113
+ export declare const isJavascriptCondition: (c: Condition) => c is JavascriptCondition;
@@ -4,3 +4,5 @@ export const isVersionCondition = (c) => "version" in c && !("and" in c) && !("o
4
4
  export const isModuleCondition = (c) => "module" in c;
5
5
  export const isLocaleCondition = (c) => "locale" in c;
6
6
  export const isAgentCondition = (c) => "agent" in c;
7
+ export const isPackageManagerCondition = (c) => "packageManager" in c;
8
+ export const isJavascriptCondition = (c) => "javascript" in c;
@@ -1,4 +1,4 @@
1
- export type Validator = (value: string) => string | undefined;
1
+ export type Validator = (value: string | undefined) => string | undefined;
2
2
  export type Validators = {
3
3
  name: Validator;
4
4
  artifactId: Validator;
@@ -8,5 +8,6 @@ export type Validators = {
8
8
  module: Validator;
9
9
  database: Validator;
10
10
  agent: Validator;
11
+ packageManager: Validator;
11
12
  };
12
13
  export declare const createValidators: (locale: string) => Validators;
@@ -1,5 +1,5 @@
1
1
  import { getMessage } from "../i18n/index.js";
2
- import { AGENT_OPTIONS, DATABASE_OPTIONS, MODULE_OPTIONS, } from "./constants.js";
2
+ import { AGENT_OPTIONS, DATABASE_OPTIONS, MODULE_OPTIONS, PACKAGE_MANAGER_OPTIONS, } from "./constants.js";
3
3
  import { SELECTABLE_VERSIONS } from "./version-map.js";
4
4
  const ARTIFACT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
5
5
  const GROUP_PATTERN = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/;
@@ -23,29 +23,62 @@ const isValidVersionLabel = (value) => SELECTABLE_VERSIONS.some((v) => v.label =
23
23
  const isValidModule = (value) => MODULE_OPTIONS.includes(value);
24
24
  const isValidDatabase = (value) => DATABASE_OPTIONS.includes(value);
25
25
  const isValidAgent = (value) => AGENT_OPTIONS.includes(value);
26
+ const isValidPackageManager = (value) => PACKAGE_MANAGER_OPTIONS.includes(value);
26
27
  export const createValidators = (locale) => ({
27
- name: (value) => isValidName(value)
28
- ? undefined
29
- : getMessage("error.invalidName", locale, { value }),
30
- artifactId: (value) => isValidArtifactId(value)
31
- ? undefined
32
- : getMessage("error.invalidArtifactId", locale, { value }),
33
- group: (value) => isValidGroup(value)
34
- ? undefined
35
- : getMessage("error.invalidGroup", locale, { value }),
36
- projectVersion: (value) => isValidProjectVersion(value)
37
- ? undefined
38
- : getMessage("error.invalidProjectVersion", locale, { value }),
39
- versionLabel: (value) => isValidVersionLabel(value)
40
- ? undefined
41
- : getMessage("error.invalidVersion", locale, { version: value }),
42
- module: (value) => isValidModule(value)
43
- ? undefined
44
- : getMessage("error.invalidModule", locale, { module: value }),
45
- database: (value) => isValidDatabase(value)
46
- ? undefined
47
- : getMessage("error.invalidDatabase", locale, { database: value }),
48
- agent: (value) => isValidAgent(value)
49
- ? undefined
50
- : getMessage("error.invalidAgent", locale, { agent: value }),
28
+ name: (value) => {
29
+ const v = value ?? "";
30
+ return isValidName(v)
31
+ ? undefined
32
+ : getMessage("error.invalidName", locale, { value: v });
33
+ },
34
+ artifactId: (value) => {
35
+ const v = value ?? "";
36
+ return isValidArtifactId(v)
37
+ ? undefined
38
+ : getMessage("error.invalidArtifactId", locale, { value: v });
39
+ },
40
+ group: (value) => {
41
+ const v = value ?? "";
42
+ return isValidGroup(v)
43
+ ? undefined
44
+ : getMessage("error.invalidGroup", locale, { value: v });
45
+ },
46
+ projectVersion: (value) => {
47
+ const v = value ?? "";
48
+ return isValidProjectVersion(v)
49
+ ? undefined
50
+ : getMessage("error.invalidProjectVersion", locale, { value: v });
51
+ },
52
+ versionLabel: (value) => {
53
+ const v = value ?? "";
54
+ return isValidVersionLabel(v)
55
+ ? undefined
56
+ : getMessage("error.invalidVersion", locale, { version: v });
57
+ },
58
+ module: (value) => {
59
+ const v = value ?? "";
60
+ return isValidModule(v)
61
+ ? undefined
62
+ : getMessage("error.invalidModule", locale, { module: v });
63
+ },
64
+ database: (value) => {
65
+ const v = value ?? "";
66
+ return isValidDatabase(v)
67
+ ? undefined
68
+ : getMessage("error.invalidDatabase", locale, { database: v });
69
+ },
70
+ agent: (value) => {
71
+ const v = value ?? "";
72
+ return isValidAgent(v)
73
+ ? undefined
74
+ : getMessage("error.invalidAgent", locale, { agent: v });
75
+ },
76
+ packageManager: (value) => {
77
+ const v = value ?? "";
78
+ return isValidPackageManager(v)
79
+ ? undefined
80
+ : getMessage("error.invalidPackageManager", locale, {
81
+ packageManager: v,
82
+ });
83
+ },
51
84
  });
@@ -8,6 +8,7 @@ const VARIABLE_MAP = new Map([
8
8
  ["locale", "locale"],
9
9
  ["database", "database"],
10
10
  ["projectVersion", "projectVersion"],
11
+ ["packageManager", "packageManager"],
11
12
  ]);
12
13
  const INTERPOLATION_PATTERN = /\{\{\$(\w+)\}\}/g;
13
14
  export const interpolateVariables = (template, context) => {
package/dist/i18n/en.js CHANGED
@@ -1,16 +1,30 @@
1
1
  export const messages = {
2
+ "intro.init": "Generate an iAP (intra-mart Accel Platform) project and\nassets for Claude Code / GitHub Copilot.\nA few questions follow; everything is then deployed in one go.",
3
+ "intro.attach": "Mark the current directory as an iAP project and\ndeploy assets for Claude Code / GitHub Copilot.\nA few questions follow; everything is then deployed in one go.",
2
4
  "prompt.name": "Enter project name",
5
+ "prompt.name.hint": "Used as the directory name and recorded in pom.xml <name> and package.json name\nProvide a concise name that represents the system or feature you are creating (e.g. customer-portal, invoice-management)",
3
6
  "prompt.artifactId": "Enter artifact ID (Maven artifactId)",
7
+ "prompt.artifactId.hint": "Recorded in pom.xml as the Maven artifactId (the artifact's identifier)\nThe identifier that forms the built artifact's file name (jar/war) and Maven coordinates. Defaults to the project name; change only if you want a different identifier",
4
8
  "prompt.jugglingProject": "Enter IM-Juggling project path (optional)",
9
+ "prompt.jugglingProject.hint": "If set, the iAP version and modules are auto-detected from juggling.im\nPath to an existing IM-Juggling project directory you want to link; leave empty to skip (e.g. ../customer-portal-juggling)",
5
10
  "prompt.accelplatformVersion": "Select iAP version",
11
+ "prompt.accelplatformVersion.hint": "iAP release version. The deployed assets (CLAUDE.md, etc.) adapt to this version",
6
12
  "prompt.modules": "Select modules to use",
13
+ "prompt.modules.hint": "iAP optional product modules. Each selection deploys matching skills, config, and CLAUDE.md sections",
7
14
  "prompt.group": "Enter group name",
15
+ "prompt.group.hint": "Recorded in pom.xml as the Maven groupId (dot-separated organization identifier)\nAn identifier for your organization or team; reverse-domain notation reduces collisions (e.g. com.example, jp.co.intra_mart.app)",
8
16
  "prompt.projectVersion": "Enter project version",
17
+ "prompt.projectVersion.hint": "Recorded in pom.xml <version> and package.json version\nThe release version of your project; Semantic Versioning (MAJOR.MINOR.PATCH) is common (e.g. 0.1.0, 1.0.0-SNAPSHOT)",
9
18
  "prompt.description": "Enter project description",
19
+ "prompt.description.hint": "Recorded in pom.xml <description> and used in headers of CLAUDE.md and similar files\nA short description of what this system does; being specific helps future readers and search (e.g. Portal to manage customer information)",
10
20
  "prompt.database": "Select database",
21
+ "prompt.database.hint": "The DB iAP connects to. Used to generate sample config and example connection strings in assets",
11
22
  "prompt.javascript": "Use JavaScript instead of TypeScript?",
23
+ "prompt.javascript.hint": "Affects the whole project setup (presence of tsconfig.json, pom.xml / package.json dependencies and scripts, agent asset language guidance)",
12
24
  "prompt.agent": "Select agent",
25
+ "prompt.agent.hint": "claude-code deploys under .claude/, github-copilot deploys under .github/ (instructions, skills, etc.)",
13
26
  "prompt.withGit": "Initialize Git repository?",
27
+ "prompt.withGit.hint": "Runs git init inside the created project directory",
14
28
  "juggling.detected.version": "Detected version from juggling.im: {version}",
15
29
  "juggling.detected.modules": "Detected modules from juggling.im: {modules}",
16
30
  "juggling.notFound": "juggling.im not found at specified path: {path}",
@@ -19,6 +33,13 @@ export const messages = {
19
33
  "warning.gitNotFound": "git command not found. Skipping Git initialization.",
20
34
  "warning.fileExists": "File already exists, skipping: {path}",
21
35
  "attach.skipSummary": "{count} file(s) were skipped because they already exist.",
36
+ "attach.overwritePrompt": "{path} already exists. Overwrite?",
37
+ "attach.overwritePrompt.yes": "Yes (overwrite this file)",
38
+ "attach.overwritePrompt.no": "No (skip this file)",
39
+ "attach.overwritePrompt.all": "All (overwrite all remaining)",
40
+ "attach.overwritePrompt.skipAll": "Skip-all (skip all remaining)",
41
+ "attach.overwritten": "Overwritten: {path}",
42
+ "attach.overwriteSummary": "{count} file(s) overwritten.",
22
43
  "detach.confirm": "Detach Accel CLI from this project. Are you sure?",
23
44
  "detach.deleted": "Deleted: {path}",
24
45
  "detach.skipped": "Skipped: {path} (modifications detected)",
@@ -33,13 +54,36 @@ export const messages = {
33
54
  "error.invalidModule": "Invalid module: {module}",
34
55
  "error.invalidDatabase": "Invalid database: {database}",
35
56
  "error.invalidAgent": "Invalid agent: {agent}",
57
+ "error.invalidPackageManager": "Invalid package manager (expected one of bun, npm, yarn, pnpm): {packageManager}",
36
58
  "error.invalidName": "Project name contains invalid characters for a directory name, or is empty: {value}",
37
59
  "error.invalidArtifactId": "Artifact ID violates Maven naming rules (^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
38
60
  "error.invalidGroup": "Group name violates Maven groupId naming rules (dot-separated Java identifiers): {value}",
39
61
  "error.invalidProjectVersion": "Project version violates Maven version naming rules (^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
40
- "progress.downloading": "Downloading assets...",
41
- "progress.deploying": "Deploying assets...",
42
- "progress.installing": "Installing dependencies...",
43
- "progress.gitInit": "Initializing Git repository...",
62
+ "summary.heading": "Deployment settings",
63
+ "summary.label.name": "Project name",
64
+ "summary.label.artifactId": "Artifact ID",
65
+ "summary.label.group": "Group",
66
+ "summary.label.projectVersion": "Project version",
67
+ "summary.label.description": "Description",
68
+ "summary.label.accelplatformVersion": "iAP version",
69
+ "summary.label.modules": "Modules",
70
+ "summary.label.database": "Database",
71
+ "summary.label.javascript": "JavaScript",
72
+ "summary.label.agents": "Agents",
73
+ "summary.label.locale": "Locale",
74
+ "summary.label.packageManager": "Package manager",
75
+ "summary.label.jugglingProject": "IM-Juggling project",
76
+ "summary.outputDir": "Output directory",
77
+ "summary.empty": "(none)",
78
+ "progress.fetching": "Fetching asset repository...",
79
+ "progress.deploying.section": "Deploying under {section} ({files})",
80
+ "progress.deploying.root": "Deploying root-level files ({files})",
81
+ "progress.installing": "Running {command}...",
82
+ "progress.building": "Running {command}...",
83
+ "progress.gitInit": "Running git init...",
44
84
  "progress.complete": "Project creation complete.",
85
+ "nextSteps.heading": "Next steps",
86
+ "nextSteps.cd": "cd {path}",
87
+ "nextSteps.agent.claudeCode": "Run claude to start agent-driven development",
88
+ "nextSteps.agent.githubCopilot": "Run gh copilot to start agent-driven development",
45
89
  };
package/dist/i18n/ja.js CHANGED
@@ -1,16 +1,30 @@
1
1
  export const messages = {
2
+ "intro.init": "iAP(intra-mart Accel Platform)プロジェクトと、\nClaude Code / GitHub Copilot 向けの資材を生成します。\nいくつか質問した後、最後に一括で配備します。",
3
+ "intro.attach": "カレントディレクトリを iAP プロジェクトとして認識させ、\nClaude Code / GitHub Copilot 向けの資材を配備します。\nいくつか質問した後、最後に一括で配備します。",
2
4
  "prompt.name": "プロジェクト名を入力してください",
5
+ "prompt.name.hint": "ディレクトリ名、pom.xml の <name>、package.json の name に反映されます\n作成するシステムや機能を端的に表す名称を指定してください(例: customer-portal, invoice-management)",
3
6
  "prompt.artifactId": "アーティファクトID(pomのartifactId)を入力してください",
7
+ "prompt.artifactId.hint": "Maven の artifactId として pom.xml に記録されます(成果物のID)\n成果物 (jar/war 等) のファイル名や、Maven で依存関係を識別するための ID の一部として使われます。デフォルトはプロジェクト名と同じ値で、別の識別子を使いたい場合のみ変更してください",
4
8
  "prompt.jugglingProject": "IM-Jugglingプロジェクトのパスを入力してください(省略可)",
9
+ "prompt.jugglingProject.hint": "指定すると juggling.im から iAPバージョンとモジュールを自動検出します\n連携したい既存の IM-Juggling プロジェクトのディレクトリパスを指定してください(例: ../customer-portal-juggling)",
5
10
  "prompt.accelplatformVersion": "iAPバージョンを選択してください",
11
+ "prompt.accelplatformVersion.hint": "iAP本体のリリース版。配備される資材(CLAUDE.md 等)の内容がこのバージョンに合わせて変化します",
6
12
  "prompt.modules": "利用するモジュールを選択してください",
13
+ "prompt.modules.hint": "iAP のオプション製品群。選択したモジュール向けの Skill や設定ファイル、CLAUDE.md 内のセクションが配備されます",
7
14
  "prompt.group": "グループ名を入力してください",
15
+ "prompt.group.hint": "Maven の groupId として pom.xml に記録されます(組織を表すドット区切り名)\n所属する組織やチームを表す識別子。逆ドメイン記法で書くと他者と衝突しにくいです(例: com.example, jp.co.intra_mart.app)",
8
16
  "prompt.projectVersion": "プロジェクトバージョンを入力してください",
17
+ "prompt.projectVersion.hint": "pom.xml の <version> と package.json の version に反映されます\nプロジェクトのリリースバージョン。セマンティックバージョニング(MAJOR.MINOR.PATCH)が一般的です(例: 0.1.0, 1.0.0-SNAPSHOT)",
9
18
  "prompt.description": "プロジェクトの説明を入力してください",
19
+ "prompt.description.hint": "pom.xml の <description> や CLAUDE.md 等のヘッダに反映されます\nこのシステムが何をするものかの簡潔な説明。具体的に書くと後で読む人や検索の助けになります(例: 顧客情報を管理するポータルサイト)",
10
20
  "prompt.database": "データベースを選択してください",
21
+ "prompt.database.hint": "iAP が接続する DB。資材内のサンプル設定や接続文字列の例の生成に使われます",
11
22
  "prompt.javascript": "TypeScriptの代わりにJavaScriptを利用しますか?",
23
+ "prompt.javascript.hint": "プロジェクト全体の構成(tsconfig.json の有無、pom.xml / package.json の依存・スクリプト、エージェント資材内の言語ガイド)に影響します",
12
24
  "prompt.agent": "エージェントを選択してください",
25
+ "prompt.agent.hint": "claude-code → .claude/ 配下、github-copilot → .github/ 配下に、指示書や skills などを配備します",
13
26
  "prompt.withGit": "Gitリポジトリを初期化しますか?",
27
+ "prompt.withGit.hint": "作成したプロジェクトディレクトリで git init を実行します",
14
28
  "juggling.detected.version": "juggling.imからバージョンを検出: {version}",
15
29
  "juggling.detected.modules": "juggling.imからモジュールを検出: {modules}",
16
30
  "juggling.notFound": "指定されたパスにjuggling.imが見つかりません: {path}",
@@ -19,6 +33,13 @@ export const messages = {
19
33
  "warning.gitNotFound": "gitコマンドが見つかりません。Git初期化をスキップします。",
20
34
  "warning.fileExists": "既存ファイルのためスキップ: {path}",
21
35
  "attach.skipSummary": "{count}件のファイルは既に存在したためスキップしました。",
36
+ "attach.overwritePrompt": "{path} は既に存在します。上書きしますか?",
37
+ "attach.overwritePrompt.yes": "Yes (このファイルを上書き)",
38
+ "attach.overwritePrompt.no": "No (このファイルをスキップ)",
39
+ "attach.overwritePrompt.all": "All (以降全て上書き)",
40
+ "attach.overwritePrompt.skipAll": "Skip-all (以降全てスキップ)",
41
+ "attach.overwritten": "上書き: {path}",
42
+ "attach.overwriteSummary": "{count}件のファイルを上書きしました。",
22
43
  "detach.confirm": "このプロジェクトからAccel CLIを解除します。よろしいですか?",
23
44
  "detach.deleted": "削除: {path}",
24
45
  "detach.skipped": "スキップ: {path} (変更が検出されました)",
@@ -33,13 +54,36 @@ export const messages = {
33
54
  "error.invalidModule": "無効なモジュールです: {module}",
34
55
  "error.invalidDatabase": "無効なデータベースです: {database}",
35
56
  "error.invalidAgent": "無効なエージェントです: {agent}",
57
+ "error.invalidPackageManager": "無効なパッケージマネージャです(bun, npm, yarn, pnpm のいずれかを指定してください): {packageManager}",
36
58
  "error.invalidName": "プロジェクト名にディレクトリ名として使用できない文字が含まれています、または空です: {value}",
37
- "error.invalidArtifactId": "アーティファクトIDが Maven の規約に違反しています(^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
38
- "error.invalidGroup": "グループ名が Maven groupId の規約に違反しています(ドット区切りのJava識別子): {value}",
39
- "error.invalidProjectVersion": "プロジェクトバージョンが Maven version の規約に違反しています(^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
40
- "progress.downloading": "資材をダウンロードしています...",
41
- "progress.deploying": "資材を配備しています...",
42
- "progress.installing": "依存関係をインストールしています...",
43
- "progress.gitInit": "Gitリポジトリを初期化しています...",
59
+ "error.invalidArtifactId": "アーティファクトIDが Maven の規約に違反しています(^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
60
+ "error.invalidGroup": "グループ名が Maven groupId の規約に違反しています(ドット区切りのJava識別子): {value}",
61
+ "error.invalidProjectVersion": "プロジェクトバージョンが Maven version の規約に違反しています(^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
62
+ "summary.heading": "配備設定",
63
+ "summary.label.name": "プロジェクト名",
64
+ "summary.label.artifactId": "アーティファクトID",
65
+ "summary.label.group": "グループ",
66
+ "summary.label.projectVersion": "プロジェクトバージョン",
67
+ "summary.label.description": "説明",
68
+ "summary.label.accelplatformVersion": "iAPバージョン",
69
+ "summary.label.modules": "モジュール",
70
+ "summary.label.database": "データベース",
71
+ "summary.label.javascript": "JavaScript",
72
+ "summary.label.agents": "エージェント",
73
+ "summary.label.locale": "ロケール",
74
+ "summary.label.packageManager": "パッケージマネージャ",
75
+ "summary.label.jugglingProject": "IM-Jugglingプロジェクト",
76
+ "summary.outputDir": "出力先",
77
+ "summary.empty": "(なし)",
78
+ "progress.fetching": "資材リポジトリを取得しています...",
79
+ "progress.deploying.section": "{section} 配下を配備中 ({files})",
80
+ "progress.deploying.root": "ルート直下を配備中 ({files})",
81
+ "progress.installing": "{command} を実行しています...",
82
+ "progress.building": "{command} を実行しています...",
83
+ "progress.gitInit": "git init を実行しています...",
44
84
  "progress.complete": "プロジェクトの作成が完了しました。",
85
+ "nextSteps.heading": "次の手順",
86
+ "nextSteps.cd": "cd {path}",
87
+ "nextSteps.agent.claudeCode": "claude を起動してエージェント開発を始めてください",
88
+ "nextSteps.agent.githubCopilot": "gh copilot を起動してエージェント開発を始めてください",
45
89
  };
@@ -1,16 +1,30 @@
1
1
  export const messages = {
2
+ "intro.init": "生成 iAP(intra-mart Accel Platform)项目,\n以及面向 Claude Code / GitHub Copilot 的资源。\n将提出若干问题,最后统一进行部署。",
3
+ "intro.attach": "将当前目录识别为 iAP 项目,\n并部署面向 Claude Code / GitHub Copilot 的资源。\n将提出若干问题,最后统一进行部署。",
2
4
  "prompt.name": "请输入项目名称",
5
+ "prompt.name.hint": "用作目录名称,并写入 pom.xml 的 <name> 和 package.json 的 name\n请提供能简洁表达所创建系统或功能的名称(例如: customer-portal, invoice-management)",
3
6
  "prompt.artifactId": "请输入构件ID(Maven artifactId)",
7
+ "prompt.artifactId.hint": "作为 Maven artifactId 写入 pom.xml(构件的标识符)\n构成成品 (jar/war 等) 文件名和 Maven 坐标的标识符。默认与项目名称相同;如需使用其他标识符请修改",
4
8
  "prompt.jugglingProject": "请输入IM-Juggling项目路径(可选)",
9
+ "prompt.jugglingProject.hint": "若指定,则从 juggling.im 自动检测 iAP 版本和模块\n指定要联动的已有 IM-Juggling 项目目录路径;不需要时留空(例如: ../customer-portal-juggling)",
5
10
  "prompt.accelplatformVersion": "请选择iAP版本",
11
+ "prompt.accelplatformVersion.hint": "iAP 本体的发行版。部署的资源(CLAUDE.md 等)会根据该版本变化",
6
12
  "prompt.modules": "请选择要使用的模块",
13
+ "prompt.modules.hint": "iAP 的可选产品模块。每选一项会部署对应的 Skill、配置文件以及 CLAUDE.md 中的相应章节",
7
14
  "prompt.group": "请输入组名",
15
+ "prompt.group.hint": "作为 Maven groupId 写入 pom.xml(表示组织的点分标识符)\n表示所属组织或团队的标识符;按反向域名记法书写可减少与他人的冲突(例如: com.example, jp.co.intra_mart.app)",
8
16
  "prompt.projectVersion": "请输入项目版本",
17
+ "prompt.projectVersion.hint": "写入 pom.xml 的 <version> 和 package.json 的 version\n项目的发布版本;常用语义化版本(MAJOR.MINOR.PATCH)(例如: 0.1.0, 1.0.0-SNAPSHOT)",
9
18
  "prompt.description": "请输入项目描述",
19
+ "prompt.description.hint": "写入 pom.xml 的 <description>,并用于 CLAUDE.md 等文件的标题部分\n对该系统功能的简要说明;写得具体有助于日后阅读和检索(例如: 用于管理客户信息的门户网站)",
10
20
  "prompt.database": "请选择数据库",
21
+ "prompt.database.hint": "iAP 连接的数据库。用于生成资源中的示例配置和连接字符串样例",
11
22
  "prompt.javascript": "是否使用JavaScript代替TypeScript?",
23
+ "prompt.javascript.hint": "影响整个项目结构(是否存在 tsconfig.json、pom.xml / package.json 的依赖与脚本、代理资源中的语言指引)",
12
24
  "prompt.agent": "请选择代理",
25
+ "prompt.agent.hint": "claude-code 部署到 .claude/ 下,github-copilot 部署到 .github/ 下(指令书、skills 等)",
13
26
  "prompt.withGit": "是否初始化Git仓库?",
27
+ "prompt.withGit.hint": "在创建的项目目录中执行 git init",
14
28
  "juggling.detected.version": "从juggling.im检测到版本: {version}",
15
29
  "juggling.detected.modules": "从juggling.im检测到模块: {modules}",
16
30
  "juggling.notFound": "在指定路径未找到juggling.im: {path}",
@@ -19,6 +33,13 @@ export const messages = {
19
33
  "warning.gitNotFound": "未找到git命令。跳过Git初始化。",
20
34
  "warning.fileExists": "文件已存在,跳过: {path}",
21
35
  "attach.skipSummary": "{count}个文件已存在,已跳过。",
36
+ "attach.overwritePrompt": "{path} 已存在。要覆盖吗?",
37
+ "attach.overwritePrompt.yes": "Yes (覆盖此文件)",
38
+ "attach.overwritePrompt.no": "No (跳过此文件)",
39
+ "attach.overwritePrompt.all": "All (覆盖所有剩余文件)",
40
+ "attach.overwritePrompt.skipAll": "Skip-all (跳过所有剩余文件)",
41
+ "attach.overwritten": "已覆盖: {path}",
42
+ "attach.overwriteSummary": "已覆盖 {count} 个文件。",
22
43
  "detach.confirm": "从此项目解除Accel CLI。确定吗?",
23
44
  "detach.deleted": "已删除: {path}",
24
45
  "detach.skipped": "已跳过: {path} (检测到修改)",
@@ -33,13 +54,36 @@ export const messages = {
33
54
  "error.invalidModule": "无效的模块: {module}",
34
55
  "error.invalidDatabase": "无效的数据库: {database}",
35
56
  "error.invalidAgent": "无效的代理: {agent}",
57
+ "error.invalidPackageManager": "无效的包管理器(必须是 bun、npm、yarn、pnpm 之一): {packageManager}",
36
58
  "error.invalidName": "项目名称包含不能用作目录名的字符或为空: {value}",
37
- "error.invalidArtifactId": "构件ID违反Maven命名规则(^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
38
- "error.invalidGroup": "组名违反Maven groupId命名规则(点分隔的Java标识符): {value}",
39
- "error.invalidProjectVersion": "项目版本违反Maven version命名规则(^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
40
- "progress.downloading": "正在下载资源...",
41
- "progress.deploying": "正在部署资源...",
42
- "progress.installing": "正在安装依赖...",
43
- "progress.gitInit": "正在初始化Git仓库...",
59
+ "error.invalidArtifactId": "构件ID违反Maven命名规则(^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
60
+ "error.invalidGroup": "组名违反Maven groupId命名规则(点分隔的Java标识符): {value}",
61
+ "error.invalidProjectVersion": "项目版本违反Maven version命名规则(^[A-Za-z0-9][A-Za-z0-9._-]*$): {value}",
62
+ "summary.heading": "部署设置",
63
+ "summary.label.name": "项目名称",
64
+ "summary.label.artifactId": "构件ID",
65
+ "summary.label.group": "",
66
+ "summary.label.projectVersion": "项目版本",
67
+ "summary.label.description": "描述",
68
+ "summary.label.accelplatformVersion": "iAP版本",
69
+ "summary.label.modules": "模块",
70
+ "summary.label.database": "数据库",
71
+ "summary.label.javascript": "JavaScript",
72
+ "summary.label.agents": "代理",
73
+ "summary.label.locale": "语言",
74
+ "summary.label.packageManager": "包管理器",
75
+ "summary.label.jugglingProject": "IM-Juggling项目",
76
+ "summary.outputDir": "输出目录",
77
+ "summary.empty": "(无)",
78
+ "progress.fetching": "正在获取资源仓库...",
79
+ "progress.deploying.section": "正在部署 {section} 下 ({files})",
80
+ "progress.deploying.root": "正在部署根目录文件 ({files})",
81
+ "progress.installing": "正在执行 {command}...",
82
+ "progress.building": "正在执行 {command}...",
83
+ "progress.gitInit": "正在执行 git init...",
44
84
  "progress.complete": "项目创建完成。",
85
+ "nextSteps.heading": "下一步",
86
+ "nextSteps.cd": "cd {path}",
87
+ "nextSteps.agent.claudeCode": "执行 claude 开始基于代理的开发",
88
+ "nextSteps.agent.githubCopilot": "执行 gh copilot 开始基于代理的开发",
45
89
  };
@@ -0,0 +1,2 @@
1
+ export declare const withHint: (baseKey: string, locale: string) => string;
2
+ export declare const displayWidth: (s: string) => number;
@@ -0,0 +1,33 @@
1
+ import { getMessage } from "../i18n/index.js";
2
+ export const withHint = (baseKey, locale) => {
3
+ const main = getMessage(baseKey, locale);
4
+ const hintKey = `${baseKey}.hint`;
5
+ const hint = getMessage(hintKey, locale);
6
+ if (hint === hintKey)
7
+ return main;
8
+ return `${main}\n${hint}`;
9
+ };
10
+ const isWide = (ch) => {
11
+ const code = ch.codePointAt(0) ?? 0;
12
+ if (code >= 0x1100 && code <= 0x115f)
13
+ return true;
14
+ if (code >= 0x2e80 && code <= 0x9fff)
15
+ return true;
16
+ if (code >= 0xa000 && code <= 0xa4cf)
17
+ return true;
18
+ if (code >= 0xac00 && code <= 0xd7a3)
19
+ return true;
20
+ if (code >= 0xf900 && code <= 0xfaff)
21
+ return true;
22
+ if (code >= 0xff00 && code <= 0xff60)
23
+ return true;
24
+ if (code >= 0xffe0 && code <= 0xffe6)
25
+ return true;
26
+ return false;
27
+ };
28
+ export const displayWidth = (s) => {
29
+ let w = 0;
30
+ for (const ch of s)
31
+ w += isWide(ch) ? 2 : 1;
32
+ return w;
33
+ };
@@ -0,0 +1,8 @@
1
+ export type NextStepsOptions = {
2
+ projectDir: string;
3
+ agents: string[];
4
+ isInit: boolean;
5
+ locale: string;
6
+ };
7
+ export declare const buildNextStepsBody: (opts: NextStepsOptions) => string;
8
+ export declare const printNextSteps: (opts: NextStepsOptions) => void;
@@ -0,0 +1,22 @@
1
+ import * as p from "@clack/prompts";
2
+ import { basename } from "node:path";
3
+ import { getMessage } from "../i18n/index.js";
4
+ export const buildNextStepsBody = (opts) => {
5
+ const steps = [];
6
+ if (opts.isInit) {
7
+ steps.push(getMessage("nextSteps.cd", opts.locale, { path: basename(opts.projectDir) }));
8
+ }
9
+ if (opts.agents.includes("claude-code")) {
10
+ steps.push(getMessage("nextSteps.agent.claudeCode", opts.locale));
11
+ }
12
+ if (opts.agents.includes("github-copilot")) {
13
+ steps.push(getMessage("nextSteps.agent.githubCopilot", opts.locale));
14
+ }
15
+ return steps.map((s, i) => `${i + 1}. ${s}`).join("\n");
16
+ };
17
+ export const printNextSteps = (opts) => {
18
+ const body = buildNextStepsBody(opts);
19
+ if (body.length === 0)
20
+ return;
21
+ p.note(body, getMessage("nextSteps.heading", opts.locale));
22
+ };
@@ -0,0 +1,2 @@
1
+ import { type ProgressCallbacks } from "../asset/deployer.js";
2
+ export declare const buildProgressCallbacks: (locale: string) => ProgressCallbacks;
@@ -0,0 +1,21 @@
1
+ import * as p from "@clack/prompts";
2
+ import { ROOT_SECTION } from "../asset/deployer.js";
3
+ import { getMessage } from "../i18n/index.js";
4
+ const MAX_FILES_SHOWN = 3;
5
+ const formatFileList = (files) => {
6
+ if (files.length <= MAX_FILES_SHOWN)
7
+ return files.join(", ");
8
+ return `${files.slice(0, MAX_FILES_SHOWN).join(", ")} +${files.length - MAX_FILES_SHOWN}`;
9
+ };
10
+ export const buildProgressCallbacks = (locale) => ({
11
+ onFetchStart: () => p.log.info(getMessage("progress.fetching", locale)),
12
+ onSectionDeploy: (section, files) => {
13
+ const filesStr = formatFileList(files);
14
+ const key = section === ROOT_SECTION
15
+ ? "progress.deploying.root"
16
+ : "progress.deploying.section";
17
+ p.log.info(getMessage(key, locale, { section, files: filesStr }));
18
+ },
19
+ onInstallStart: (command) => p.log.info(getMessage("progress.installing", locale, { command })),
20
+ onBuildStart: (command) => p.log.info(getMessage("progress.building", locale, { command })),
21
+ });
@@ -12,6 +12,7 @@ export type PromptOptions = {
12
12
  database?: string;
13
13
  javascript?: boolean;
14
14
  agent?: string[];
15
+ packageManager?: string;
15
16
  locale: string;
16
17
  withGit?: boolean;
17
18
  noInteraction?: boolean;
@@ -2,6 +2,7 @@ import * as p from "@clack/prompts";
2
2
  import { DATABASE_OPTIONS, AGENT_OPTIONS, DEFAULT_SETTINGS, } from "../core/constants.js";
3
3
  import { createValidators } from "../core/validators.js";
4
4
  import { getMessage } from "../i18n/index.js";
5
+ import { withHint } from "./format.js";
5
6
  import { detectAgents, detectDefaultAgents } from "./agent-detect.js";
6
7
  import { parseJugglingFile } from "../juggling/parser.js";
7
8
  import { extractVersionLabel, extractModules, } from "../juggling/extractor.js";
@@ -71,6 +72,11 @@ export const validateCliValues = (opts, validators) => {
71
72
  throw new Error(err);
72
73
  }
73
74
  }
75
+ if (opts.packageManager !== undefined) {
76
+ const err = validators.packageManager(opts.packageManager);
77
+ if (err)
78
+ throw new Error(err);
79
+ }
74
80
  };
75
81
  export const runPrompts = async (opts) => {
76
82
  const locale = opts.locale;
@@ -103,13 +109,15 @@ export const runPrompts = async (opts) => {
103
109
  agents: opts.agent ?? detectDefaultAgents(),
104
110
  javascript: opts.javascript ?? DEFAULT_SETTINGS.javascript,
105
111
  locale,
112
+ packageManager: opts.packageManager ?? DEFAULT_SETTINGS.packageManager,
106
113
  jugglingProject: opts.jugglingProject ?? null,
107
114
  withGit: opts.withGit ?? true,
108
115
  };
109
116
  }
110
117
  p.intro("Accel CLI");
118
+ p.note(getMessage(opts.isInit ? "intro.init" : "intro.attach", locale));
111
119
  const name = (await p.text({
112
- message: getMessage("prompt.name", locale),
120
+ message: withHint("prompt.name", locale),
113
121
  defaultValue: opts.name ?? DEFAULT_SETTINGS.name,
114
122
  initialValue: opts.name ?? DEFAULT_SETTINGS.name,
115
123
  validate: validators.name,
@@ -118,7 +126,7 @@ export const runPrompts = async (opts) => {
118
126
  process.exit(0);
119
127
  const artifactIdInitial = opts.artifactId ?? name;
120
128
  const artifactId = (await p.text({
121
- message: getMessage("prompt.artifactId", locale),
129
+ message: withHint("prompt.artifactId", locale),
122
130
  defaultValue: artifactIdInitial,
123
131
  initialValue: artifactIdInitial,
124
132
  validate: validators.artifactId,
@@ -126,7 +134,7 @@ export const runPrompts = async (opts) => {
126
134
  if (p.isCancel(artifactId))
127
135
  process.exit(0);
128
136
  const jugglingInput = (await p.text({
129
- message: getMessage("prompt.jugglingProject", locale),
137
+ message: withHint("prompt.jugglingProject", locale),
130
138
  defaultValue: opts.jugglingProject ?? "",
131
139
  initialValue: opts.jugglingProject ?? "",
132
140
  }));
@@ -151,7 +159,7 @@ export const runPrompts = async (opts) => {
151
159
  const accelplatformVersion = "2025-Autumn";
152
160
  const modules = ["workflow", "bpm"];
153
161
  const group = (await p.text({
154
- message: getMessage("prompt.group", locale),
162
+ message: withHint("prompt.group", locale),
155
163
  defaultValue: opts.group ?? DEFAULT_SETTINGS.group,
156
164
  initialValue: opts.group ?? DEFAULT_SETTINGS.group,
157
165
  validate: validators.group,
@@ -159,7 +167,7 @@ export const runPrompts = async (opts) => {
159
167
  if (p.isCancel(group))
160
168
  process.exit(0);
161
169
  const projectVersion = (await p.text({
162
- message: getMessage("prompt.projectVersion", locale),
170
+ message: withHint("prompt.projectVersion", locale),
163
171
  defaultValue: opts.projectVersion ?? DEFAULT_SETTINGS.projectVersion,
164
172
  initialValue: opts.projectVersion ?? DEFAULT_SETTINGS.projectVersion,
165
173
  validate: validators.projectVersion,
@@ -167,21 +175,21 @@ export const runPrompts = async (opts) => {
167
175
  if (p.isCancel(projectVersion))
168
176
  process.exit(0);
169
177
  const description = (await p.text({
170
- message: getMessage("prompt.description", locale),
178
+ message: withHint("prompt.description", locale),
171
179
  defaultValue: opts.description ?? DEFAULT_SETTINGS.description,
172
180
  initialValue: opts.description ?? DEFAULT_SETTINGS.description,
173
181
  }));
174
182
  if (p.isCancel(description))
175
183
  process.exit(0);
176
184
  const database = (await p.select({
177
- message: getMessage("prompt.database", locale),
185
+ message: withHint("prompt.database", locale),
178
186
  options: DATABASE_OPTIONS.map((d) => ({ value: d, label: d })),
179
187
  initialValue: opts.database ?? DEFAULT_SETTINGS.database,
180
188
  }));
181
189
  if (p.isCancel(database))
182
190
  process.exit(0);
183
191
  const javascript = (await p.confirm({
184
- message: getMessage("prompt.javascript", locale),
192
+ message: withHint("prompt.javascript", locale),
185
193
  initialValue: opts.javascript ?? DEFAULT_SETTINGS.javascript,
186
194
  }));
187
195
  if (p.isCancel(javascript))
@@ -193,7 +201,7 @@ export const runPrompts = async (opts) => {
193
201
  ? detected.map((a) => a.name)
194
202
  : [...AGENT_OPTIONS]);
195
203
  const agents = (await p.multiselect({
196
- message: getMessage("prompt.agent", locale),
204
+ message: withHint("prompt.agent", locale),
197
205
  options: agentOptions,
198
206
  initialValues: defaultAgents,
199
207
  }));
@@ -202,7 +210,7 @@ export const runPrompts = async (opts) => {
202
210
  let withGit = opts.withGit ?? true;
203
211
  if (opts.isInit) {
204
212
  withGit = (await p.confirm({
205
- message: getMessage("prompt.withGit", locale),
213
+ message: withHint("prompt.withGit", locale),
206
214
  initialValue: opts.withGit ?? true,
207
215
  }));
208
216
  if (p.isCancel(withGit))
@@ -220,6 +228,7 @@ export const runPrompts = async (opts) => {
220
228
  agents,
221
229
  javascript,
222
230
  locale,
231
+ packageManager: opts.packageManager ?? DEFAULT_SETTINGS.packageManager,
223
232
  jugglingProject,
224
233
  withGit,
225
234
  };
@@ -0,0 +1,3 @@
1
+ import type { AccelSettings } from "../core/types.js";
2
+ export declare const buildSummaryBody: (settings: AccelSettings, outputDir: string, locale: string) => string;
3
+ export declare const printSummary: (settings: AccelSettings, outputDir: string, locale: string) => void;
@@ -0,0 +1,50 @@
1
+ import * as p from "@clack/prompts";
2
+ import { getMessage } from "../i18n/index.js";
3
+ import { displayWidth } from "./format.js";
4
+ const formatList = (values, emptyLabel) => values.length > 0 ? values.join(", ") : emptyLabel;
5
+ const formatString = (value, emptyLabel) => value.length > 0 ? value : emptyLabel;
6
+ const formatBoolean = (value) => (value ? "true" : "false");
7
+ export const buildSummaryBody = (settings, outputDir, locale) => {
8
+ const empty = getMessage("summary.empty", locale);
9
+ const rows = [
10
+ { labelKey: "summary.label.name", value: settings.name },
11
+ { labelKey: "summary.label.artifactId", value: settings.artifactId },
12
+ { labelKey: "summary.label.group", value: settings.group },
13
+ { labelKey: "summary.label.projectVersion", value: settings.projectVersion },
14
+ {
15
+ labelKey: "summary.label.description",
16
+ value: formatString(settings.description, empty),
17
+ },
18
+ { labelKey: "summary.label.database", value: settings.database },
19
+ {
20
+ labelKey: "summary.label.javascript",
21
+ value: formatBoolean(settings.javascript),
22
+ },
23
+ {
24
+ labelKey: "summary.label.agents",
25
+ value: formatList(settings.agents, empty),
26
+ },
27
+ { labelKey: "summary.label.locale", value: settings.locale },
28
+ {
29
+ labelKey: "summary.label.packageManager",
30
+ value: settings.packageManager,
31
+ },
32
+ {
33
+ labelKey: "summary.label.jugglingProject",
34
+ value: settings.jugglingProject ?? empty,
35
+ },
36
+ { labelKey: "summary.outputDir", value: outputDir },
37
+ ];
38
+ const labels = rows.map((r) => getMessage(r.labelKey, locale));
39
+ const maxLabelWidth = Math.max(...labels.map((l) => displayWidth(l)));
40
+ return rows
41
+ .map((row, i) => {
42
+ const label = labels[i];
43
+ const pad = " ".repeat(Math.max(0, maxLabelWidth - displayWidth(label)));
44
+ return `${label}${pad} : ${row.value}`;
45
+ })
46
+ .join("\n");
47
+ };
48
+ export const printSummary = (settings, outputDir, locale) => {
49
+ p.note(buildSummaryBody(settings, outputDir, locale), getMessage("summary.heading", locale));
50
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intra-mart/accel",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-dev.202605271647",
4
4
  "type": "module",
5
5
  "description": "CLI tool for intra-mart Accel Platform development",
6
6
  "author": "NTT DATA INTRAMART",
@@ -39,7 +39,7 @@
39
39
  "prepublishOnly": "tsc"
40
40
  },
41
41
  "dependencies": {
42
- "@clack/prompts": "^0.10.0",
42
+ "@clack/prompts": "^1.4.0",
43
43
  "citty": "^0.1.6",
44
44
  "fast-xml-parser": "^5.2.0",
45
45
  "remark": "^15.0.1",