mano-coding 0.1.18 → 0.1.20

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/dist/bin.js CHANGED
@@ -31879,6 +31879,15 @@ var init_execute = __esm({
31879
31879
 
31880
31880
  // packages/core/src/execute/task-runner.ts
31881
31881
  import { spawn } from "node:child_process";
31882
+ import { existsSync as existsSync4 } from "node:fs";
31883
+ import { delimiter, join as join11 } from "node:path";
31884
+ function filterTasksByHook(items, hook) {
31885
+ return items.filter((item) => {
31886
+ const task = "task" in item ? item.task : item;
31887
+ const hooks = task.on && task.on.length > 0 ? task.on : ["after:create"];
31888
+ return hooks.includes(hook);
31889
+ });
31890
+ }
31882
31891
  function parseSetInputs(rawSetFlags = []) {
31883
31892
  const values = {};
31884
31893
  const warnings = [];
@@ -31973,6 +31982,33 @@ function generateTaskSummary(tasks, ctx) {
31973
31982
  return `[${idx + 1}/${tasks.length}] ${task.id}: ${fullCmd}`;
31974
31983
  });
31975
31984
  }
31985
+ function resolveTaskExecutable(command, platform = process.platform, envPath) {
31986
+ if (platform !== "win32") {
31987
+ return command;
31988
+ }
31989
+ if (/\.(exe|cmd|bat|com)$/i.test(command)) {
31990
+ return command;
31991
+ }
31992
+ const knownCmds = /* @__PURE__ */ new Set(["npx", "npm", "pnpm", "yarn", "corepack", "bun", "deno"]);
31993
+ if (knownCmds.has(command.toLowerCase())) {
31994
+ return `${command}.cmd`;
31995
+ }
31996
+ const pathValue = envPath ?? process.env.PATH ?? "";
31997
+ const pathext = (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((ext) => ext.toLowerCase());
31998
+ const dirs = pathValue.split(delimiter).filter(Boolean);
31999
+ for (const dir of dirs) {
32000
+ for (const ext of pathext) {
32001
+ const target = join11(dir, `${command}${ext}`);
32002
+ try {
32003
+ if (existsSync4(target)) {
32004
+ return `${command}${ext}`;
32005
+ }
32006
+ } catch {
32007
+ }
32008
+ }
32009
+ }
32010
+ return command;
32011
+ }
31976
32012
  async function executeTasks(tasks, ctx, options = {}) {
31977
32013
  if (tasks.length === 0) return;
31978
32014
  const stdio = options.stdio ?? "inherit";
@@ -31990,8 +32026,9 @@ async function executeTasks(tasks, ctx, options = {}) {
31990
32026
  if (options.onProgress) {
31991
32027
  options.onProgress(`\u6B63\u5728\u6267\u884C\u4EFB\u52A1 [${i + 1}/${tasks.length}] (${task.id}): ${command} ${args.join(" ")}`);
31992
32028
  }
32029
+ const executable = resolveTaskExecutable(command, process.platform, env.PATH);
31993
32030
  await new Promise((resolve22, reject) => {
31994
- const child = spawn(command, args, {
32031
+ const child = spawn(executable, args, {
31995
32032
  cwd,
31996
32033
  env,
31997
32034
  stdio,
@@ -32043,7 +32080,7 @@ var init_task_runner = __esm({
32043
32080
 
32044
32081
  // packages/core/src/execute/template-contract.ts
32045
32082
  import { readFile as readFile17, readdir as readdir4 } from "node:fs/promises";
32046
- import { join as join11, relative as relative4 } from "node:path";
32083
+ import { join as join12, relative as relative4 } from "node:path";
32047
32084
  function validateMcpConfig(rawConfig) {
32048
32085
  if (typeof rawConfig !== "object" || rawConfig === null) {
32049
32086
  throw new TemplateContractError("mcp/mcp.json \u5FC5\u987B\u662F JSON \u5BF9\u8C61", "MCP_CONFIG_INVALID");
@@ -32079,7 +32116,7 @@ function validateMcpConfig(rawConfig) {
32079
32116
  async function scanTemplateDirectoryContract(options) {
32080
32117
  const { templateDir, targets } = options;
32081
32118
  const intents = [];
32082
- const agentsPath = join11(templateDir, "AGENTS.md");
32119
+ const agentsPath = join12(templateDir, "AGENTS.md");
32083
32120
  try {
32084
32121
  const agentsContent = await readFile17(agentsPath);
32085
32122
  intents.push({
@@ -32090,16 +32127,16 @@ async function scanTemplateDirectoryContract(options) {
32090
32127
  });
32091
32128
  } catch {
32092
32129
  }
32093
- const skillsDir = join11(templateDir, "skills");
32130
+ const skillsDir = join12(templateDir, "skills");
32094
32131
  try {
32095
32132
  const skillEntries = await readdir4(skillsDir, { withFileTypes: true });
32096
32133
  for (const skillEntry of skillEntries) {
32097
32134
  if (!skillEntry.isDirectory()) continue;
32098
32135
  const skillId = skillEntry.name;
32099
- const singleSkillDir = join11(skillsDir, skillId);
32136
+ const singleSkillDir = join12(skillsDir, skillId);
32100
32137
  const allSkillFiles = await listAllFilesRecursive(singleSkillDir);
32101
32138
  for (const relFile of allSkillFiles) {
32102
- const fileContent = await readFile17(join11(singleSkillDir, relFile));
32139
+ const fileContent = await readFile17(join12(singleSkillDir, relFile));
32103
32140
  intents.push({
32104
32141
  action: "upsert",
32105
32142
  type: "file",
@@ -32120,17 +32157,17 @@ async function scanTemplateDirectoryContract(options) {
32120
32157
  }
32121
32158
  } catch {
32122
32159
  }
32123
- const rulesDir = join11(templateDir, "rules");
32160
+ const rulesDir = join12(templateDir, "rules");
32124
32161
  try {
32125
32162
  const ruleIdeEntries = await readdir4(rulesDir, { withFileTypes: true });
32126
32163
  for (const ideEntry of ruleIdeEntries) {
32127
32164
  if (!ideEntry.isDirectory()) continue;
32128
32165
  const ide = ideEntry.name;
32129
32166
  if (!targets.includes(ide)) continue;
32130
- const singleRuleDir = join11(rulesDir, ide);
32167
+ const singleRuleDir = join12(rulesDir, ide);
32131
32168
  const allRuleFiles = await listAllFilesRecursive(singleRuleDir);
32132
32169
  for (const relFile of allRuleFiles) {
32133
- const fileContent = await readFile17(join11(singleRuleDir, relFile));
32170
+ const fileContent = await readFile17(join12(singleRuleDir, relFile));
32134
32171
  const normalizedRel = relFile.replaceAll("\\", "/");
32135
32172
  let targetDestPath;
32136
32173
  switch (ide) {
@@ -32161,7 +32198,7 @@ async function scanTemplateDirectoryContract(options) {
32161
32198
  }
32162
32199
  } catch {
32163
32200
  }
32164
- const mcpPath = join11(templateDir, "mcp", "mcp.json");
32201
+ const mcpPath = join12(templateDir, "mcp", "mcp.json");
32165
32202
  try {
32166
32203
  const mcpRaw = await readFile17(mcpPath, "utf-8");
32167
32204
  const mcpServers = validateMcpConfig(JSON.parse(mcpRaw));
@@ -32205,7 +32242,7 @@ async function listAllFilesRecursive(dir, baseDir = dir) {
32205
32242
  try {
32206
32243
  const entries = await readdir4(dir, { withFileTypes: true });
32207
32244
  for (const entry of entries) {
32208
- const full = join11(dir, entry.name);
32245
+ const full = join12(dir, entry.name);
32209
32246
  if (entry.isDirectory()) {
32210
32247
  const subFiles = await listAllFilesRecursive(full, baseDir);
32211
32248
  files.push(...subFiles);
@@ -32233,7 +32270,7 @@ var init_template_contract = __esm({
32233
32270
 
32234
32271
  // packages/core/src/plugin/plugin-delivery.ts
32235
32272
  import { readFile as readFile18, stat as stat4, readdir as readdir5 } from "node:fs/promises";
32236
- import { join as join12, resolve as resolve8 } from "node:path";
32273
+ import { join as join13, resolve as resolve8 } from "node:path";
32237
32274
  import { createHash as createHash8 } from "node:crypto";
32238
32275
  function isPluginTargetSupported(target) {
32239
32276
  return target in PLUGIN_SUPPORT_MATRIX;
@@ -32287,7 +32324,7 @@ async function validatePlugin(request) {
32287
32324
  return diagnostics;
32288
32325
  }
32289
32326
  const entryFile = getPluginEntryFile(plugin.target);
32290
- const entryPath = join12(pluginPath, entryFile);
32327
+ const entryPath = join13(pluginPath, entryFile);
32291
32328
  try {
32292
32329
  await stat4(entryPath);
32293
32330
  } catch {
@@ -32548,7 +32585,7 @@ function getHostVersionCommand(target) {
32548
32585
  }
32549
32586
  async function readNativePluginMeta(pluginDir, target) {
32550
32587
  const entryFile = getPluginEntryFile(target);
32551
- const entryPath = join12(pluginDir, entryFile);
32588
+ const entryPath = join13(pluginDir, entryFile);
32552
32589
  try {
32553
32590
  const raw = await readFile18(entryPath, "utf-8");
32554
32591
  const parsed = JSON.parse(raw);
@@ -32592,7 +32629,7 @@ async function collectDirectoryCopyIntents(sourceDir, sourceRelPath, destDir, pa
32592
32629
  const intents = [];
32593
32630
  const entries = await readdir5(sourceDir, { withFileTypes: true });
32594
32631
  for (const entry of entries) {
32595
- const entrySourcePath = join12(sourceDir, entry.name);
32632
+ const entrySourcePath = join13(sourceDir, entry.name);
32596
32633
  const entryDestPath = `${destDir}/${entry.name}`;
32597
32634
  if (!isPathInside(packageRoot, entrySourcePath)) {
32598
32635
  continue;
@@ -32600,7 +32637,7 @@ async function collectDirectoryCopyIntents(sourceDir, sourceRelPath, destDir, pa
32600
32637
  if (entry.isDirectory()) {
32601
32638
  const subIntents = await collectDirectoryCopyIntents(
32602
32639
  entrySourcePath,
32603
- join12(sourceRelPath, entry.name),
32640
+ join13(sourceRelPath, entry.name),
32604
32641
  entryDestPath,
32605
32642
  packageRoot,
32606
32643
  metadata
@@ -32710,7 +32747,7 @@ var init_plugin = __esm({
32710
32747
  // packages/core/src/journal.ts
32711
32748
  import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
32712
32749
  import { chmod as chmod2, cp, mkdir as mkdir11, open as open3, readdir as readdir6, readFile as readFile19, rename as rename5, rm as rm4, stat as stat5, writeFile as writeFile7 } from "node:fs/promises";
32713
- import { dirname as dirname11, join as join13, resolve as resolve9 } from "node:path";
32750
+ import { dirname as dirname11, join as join14, resolve as resolve9 } from "node:path";
32714
32751
  function projectHash(projectRoot) {
32715
32752
  return createHash9("sha256").update(resolve9(projectRoot)).digest("hex").slice(0, 32);
32716
32753
  }
@@ -32721,15 +32758,15 @@ function isUuid(value) {
32721
32758
  return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
32722
32759
  }
32723
32760
  function transactionPathOf(userDataDir, projectRoot, operationId) {
32724
- return join13(resolve9(userDataDir), "transactions", projectHash(projectRoot), operationId);
32761
+ return join14(resolve9(userDataDir), "transactions", projectHash(projectRoot), operationId);
32725
32762
  }
32726
32763
  function pathsFor(userDataDir, projectRoot, operationId) {
32727
32764
  const directory = transactionPathOf(userDataDir, projectRoot, operationId);
32728
32765
  return {
32729
- journalPath: join13(directory, "journal.json"),
32766
+ journalPath: join14(directory, "journal.json"),
32730
32767
  transactionPath: directory,
32731
- backupPath: join13(directory, "backup"),
32732
- recoveryPath: join13(directory, "recovery.json")
32768
+ backupPath: join14(directory, "backup"),
32769
+ recoveryPath: join14(directory, "recovery.json")
32733
32770
  };
32734
32771
  }
32735
32772
  function documentOf(journal) {
@@ -32854,7 +32891,7 @@ async function updateJournal(journal, state) {
32854
32891
  return updated;
32855
32892
  }
32856
32893
  async function scanJournals(userDataDir, projectRoot) {
32857
- const directory = join13(resolve9(userDataDir), "transactions", projectHash(projectRoot));
32894
+ const directory = join14(resolve9(userDataDir), "transactions", projectHash(projectRoot));
32858
32895
  let entries;
32859
32896
  try {
32860
32897
  entries = await readdir6(directory, { withFileTypes: true });
@@ -32865,7 +32902,7 @@ async function scanJournals(userDataDir, projectRoot) {
32865
32902
  const journals = [];
32866
32903
  for (const entry of entries) {
32867
32904
  if (!entry.isDirectory() || !isUuid(entry.name)) continue;
32868
- const path2 = join13(directory, entry.name, "journal.json");
32905
+ const path2 = join14(directory, entry.name, "journal.json");
32869
32906
  try {
32870
32907
  journals.push(await readJournal(path2, userDataDir, projectRoot));
32871
32908
  } catch (error) {
@@ -32903,12 +32940,12 @@ async function prepareRecoverySnapshots(journal, scope, root, paths) {
32903
32940
  try {
32904
32941
  const info = await stat5(absolutePath);
32905
32942
  if (info.isDirectory()) {
32906
- await cp(absolutePath, join13(journal.backupPath, backupName), { recursive: true, force: false });
32943
+ await cp(absolutePath, join14(journal.backupPath, backupName), { recursive: true, force: false });
32907
32944
  snapshots.push({ scope, path: path2, existed: true, backup: backupName, kind: "directory", mode: info.mode & 4095 });
32908
32945
  continue;
32909
32946
  }
32910
32947
  if (!info.isFile()) throw new JournalError(`Recovery \u76EE\u6807\u4E0D\u662F\u6587\u4EF6\u6216\u76EE\u5F55: ${path2}`, "JOURNAL_INVALID");
32911
- await writeFile7(join13(journal.backupPath, backupName), await readFile19(absolutePath));
32948
+ await writeFile7(join14(journal.backupPath, backupName), await readFile19(absolutePath));
32912
32949
  snapshots.push({ scope, path: path2, existed: true, backup: backupName, mode: info.mode & 4095, kind: "file" });
32913
32950
  } catch (error) {
32914
32951
  if (error.code === "ENOENT") {
@@ -32934,12 +32971,12 @@ async function restoreRecovery(journal, projectRoot, recovery) {
32934
32971
  }
32935
32972
  if (snapshot2.kind === "directory") {
32936
32973
  await rm4(absolutePath, { recursive: true, force: true });
32937
- await cp(join13(journal.backupPath, snapshot2.backup), absolutePath, { recursive: true, force: false });
32974
+ await cp(join14(journal.backupPath, snapshot2.backup), absolutePath, { recursive: true, force: false });
32938
32975
  if (snapshot2.mode !== void 0 && process.platform !== "win32") await chmod2(absolutePath, snapshot2.mode);
32939
32976
  continue;
32940
32977
  }
32941
32978
  await mkdir11(dirname11(absolutePath), { recursive: true });
32942
- await writeFile7(absolutePath, await readFile19(join13(journal.backupPath, snapshot2.backup)));
32979
+ await writeFile7(absolutePath, await readFile19(join14(journal.backupPath, snapshot2.backup)));
32943
32980
  if (snapshot2.mode !== void 0 && process.platform !== "win32") await chmod2(absolutePath, snapshot2.mode);
32944
32981
  }
32945
32982
  }
@@ -33477,6 +33514,7 @@ __export(src_exports, {
33477
33514
  executeRollbackActions: () => executeRollbackActions,
33478
33515
  executeTasks: () => executeTasks,
33479
33516
  extractRequiredInputs: () => extractRequiredInputs,
33517
+ filterTasksByHook: () => filterTasksByHook,
33480
33518
  findInstallationForLauncher: () => findInstallationForLauncher,
33481
33519
  generateLauncherConfig: () => generateLauncherConfig,
33482
33520
  generateOperationId: () => generateOperationId,
@@ -33543,6 +33581,7 @@ __export(src_exports, {
33543
33581
  resolveAdapter: () => resolveAdapter,
33544
33582
  resolveCanonicalSchemaPath: () => resolveCanonicalSchemaPath3,
33545
33583
  resolveLauncher: () => resolveLauncher,
33584
+ resolveTaskExecutable: () => resolveTaskExecutable,
33546
33585
  sanitizeCommand: () => sanitizeCommand,
33547
33586
  sanitizeEnvValue: () => sanitizeEnvValue,
33548
33587
  sanitizeUrl: () => sanitizeUrl,
@@ -36689,7 +36728,7 @@ var init_shared_options = __esm({
36689
36728
  // packages/cli/src/commands/extension-dev.ts
36690
36729
  import { mkdtemp as mkdtemp4, readFile as readFile21 } from "node:fs/promises";
36691
36730
  import { tmpdir as tmpdir4 } from "node:os";
36692
- import { join as join14, resolve as resolvePath9, relative as relative5, isAbsolute as isAbsolute5, sep as sep3 } from "node:path";
36731
+ import { join as join15, resolve as resolvePath9, relative as relative5, isAbsolute as isAbsolute5, sep as sep3 } from "node:path";
36693
36732
  function createExtensionDevCommand() {
36694
36733
  const cmd = new Command("dev");
36695
36734
  cmd.description(t("extension.dev.description")).argument("[package-directory]", t("extension.dev.argPackageDirectory"), ".").requiredOption("--project <fixture-directory>", t("extension.dev.optionProject")).option("--operation <create|apply|sync|remove>", t("extension.dev.optionOperation"), "apply").option("--target <id>", t("mutation.target"), collectString2, []).option("--set <name=value>", t("mutation.set"), collectString2, []).option("--dry-run", t("mutation.dryRun")).option("--yes", t("mutation.yes")).option("--allow <permission>", t("mutation.allow"), collectString2, []).option("--watch", t("extension.dev.optionWatch")).action(async (packageDirectory, opts) => {
@@ -36743,7 +36782,7 @@ async function executeExtensionDev(packageDirectory, options) {
36743
36782
  return ExitCode.SchemaInvalid;
36744
36783
  }
36745
36784
  const effectiveDryRun = options.dryRun || options.watch;
36746
- const isolatedUserData = await mkdtemp4(join14(tmpdir4(), "aicoding-dev-userdata-"));
36785
+ const isolatedUserData = await mkdtemp4(join15(tmpdir4(), "aicoding-dev-userdata-"));
36747
36786
  const prevUserData = process.env["AICODING_USER_DATA"];
36748
36787
  process.env["AICODING_USER_DATA"] = isolatedUserData;
36749
36788
  try {
@@ -36984,7 +37023,7 @@ var init_extension_test = __esm({
36984
37023
 
36985
37024
  // packages/cli/src/commands/extension-pack.ts
36986
37025
  import { readFile as readFile23, mkdir as mkdir14 } from "node:fs/promises";
36987
- import { resolve as resolve13, join as join15, dirname as dirname13 } from "node:path";
37026
+ import { resolve as resolve13, join as join16, dirname as dirname13 } from "node:path";
36988
37027
  import { execFile as execFile5 } from "node:child_process";
36989
37028
  import { promisify as promisify5 } from "node:util";
36990
37029
  import { createHash as createHash10 } from "node:crypto";
@@ -37137,7 +37176,7 @@ async function executeExtensionPack(packageDirectory, options, cliVersion) {
37137
37176
  let tarballName;
37138
37177
  let tarballPath;
37139
37178
  tarballName = `${pkgName.replace(/^@/, "").replace(/\//g, "-")}-${pkgVersion}.tgz`;
37140
- tarballPath = join15(outputDir, tarballName);
37179
+ tarballPath = join16(outputDir, tarballName);
37141
37180
  try {
37142
37181
  await packWithNpm(pkgDir, outputDir);
37143
37182
  } catch (err) {
@@ -37904,7 +37943,7 @@ var init_journal_helper = __esm({
37904
37943
 
37905
37944
  // packages/cli/src/commands/service-locator.ts
37906
37945
  import { homedir as homedir2 } from "node:os";
37907
- import { join as join16, resolve as resolve16 } from "node:path";
37946
+ import { join as join17, resolve as resolve16 } from "node:path";
37908
37947
  var ServiceLocator;
37909
37948
  var init_service_locator = __esm({
37910
37949
  "packages/cli/src/commands/service-locator.ts"() {
@@ -37938,13 +37977,13 @@ var init_service_locator = __esm({
37938
37977
  return `mano-coding@${this.config.cliVersion}`;
37939
37978
  }
37940
37979
  getUserDataDir() {
37941
- return this.config.userDataDir ?? process.env["MANO_USER_DATA"] ?? process.env["AICODING_USER_DATA"] ?? join16(homedir2(), ".mano-coding");
37980
+ return this.config.userDataDir ?? process.env["MANO_USER_DATA"] ?? process.env["AICODING_USER_DATA"] ?? join17(homedir2(), ".mano-coding");
37942
37981
  }
37943
37982
  getRegistryUrl() {
37944
37983
  return this.config.registryUrl || process.env["MANO_REGISTRY"] || process.env["AICODING_REGISTRY"] || DEFAULT_PUBLIC_REGISTRY_URL;
37945
37984
  }
37946
37985
  getCacheRoot() {
37947
- return this.config.cacheRoot ?? join16(this.getUserDataDir(), "cache");
37986
+ return this.config.cacheRoot ?? join17(this.getUserDataDir(), "cache");
37948
37987
  }
37949
37988
  getRegistryClient() {
37950
37989
  if (!this._registryClient) {
@@ -38124,7 +38163,7 @@ var init_service_locator = __esm({
38124
38163
 
38125
38164
  // packages/cli/src/commands/plan-execution.ts
38126
38165
  import { mkdir as mkdir16, readFile as readFile26, readdir as readdir8, stat as stat6 } from "node:fs/promises";
38127
- import { join as join17, relative as relative6, resolve as resolve17 } from "node:path";
38166
+ import { join as join18, relative as relative6, resolve as resolve17 } from "node:path";
38128
38167
  import { createHash as createHash12 } from "node:crypto";
38129
38168
  async function buildIntentsFromPlan(_plan, resolutions, service, projectTargets, userInstallationKeys) {
38130
38169
  const adapterRegistry = service.getAdapterRegistry();
@@ -38426,7 +38465,7 @@ async function collectAssetIntents(packageRoot, source, destination, metadata) {
38426
38465
  if (!sourceStat.isDirectory()) throw new Error(`Asset '${source}' \u65E2\u4E0D\u662F\u6587\u4EF6\u4E5F\u4E0D\u662F\u76EE\u5F55`);
38427
38466
  const intents = [];
38428
38467
  for (const entry of await readdir8(sourcePath, { withFileTypes: true })) {
38429
- intents.push(...await collectAssetIntents(packageRoot, join17(source, entry.name), join17(destination, entry.name).replaceAll("\\", "/"), metadata));
38468
+ intents.push(...await collectAssetIntents(packageRoot, join18(source, entry.name), join18(destination, entry.name).replaceAll("\\", "/"), metadata));
38430
38469
  }
38431
38470
  return intents;
38432
38471
  }
@@ -39139,6 +39178,47 @@ ${taskValidation.missingInputs.map((k2) => `--set ${k2}=<value>`).join("\n")}`,
39139
39178
  }
39140
39179
  return ExitCode.Success;
39141
39180
  }
39181
+ const allTasksWithContext = allResolutions.flatMap((resolution) => {
39182
+ const tTasks = resolution.manifest.tasks ?? [];
39183
+ return tTasks.map((task) => ({
39184
+ task,
39185
+ templateDir: service.getPackageRoot(resolution) ?? service.projectRoot
39186
+ }));
39187
+ });
39188
+ const beforeCreateTasks = allTasksWithContext.filter(({ task }) => {
39189
+ const hooks = task.on && task.on.length > 0 ? task.on : ["after:create"];
39190
+ return hooks.includes("before:create");
39191
+ });
39192
+ if (beforeCreateTasks.length > 0) {
39193
+ const { basename: basename4, dirname: dirname15 } = await import("node:path");
39194
+ for (let i = 0; i < beforeCreateTasks.length; i++) {
39195
+ const { task, templateDir } = beforeCreateTasks[i];
39196
+ const taskContext = {
39197
+ projectName: basename4(service.projectRoot),
39198
+ projectDir: service.projectRoot,
39199
+ projectParentDir: dirname15(service.projectRoot),
39200
+ templateDir,
39201
+ inputs: inputValues
39202
+ };
39203
+ if (!output.json && i === 0) {
39204
+ writeHumanOutput("\n\u6B63\u5728\u6267\u884C\u524D\u7F6E\u521B\u5EFA\u4EFB\u52A1 (before:create):\n");
39205
+ }
39206
+ try {
39207
+ await executeTasks2([task], taskContext, {
39208
+ onProgress: (msg) => {
39209
+ if (!output.json) writeHumanOutput(`> ${msg}
39210
+ `);
39211
+ },
39212
+ stdio: output.json ? "ignore" : "inherit"
39213
+ });
39214
+ } catch (err) {
39215
+ if (err instanceof Error) {
39216
+ throw new CliError(err.message, ExitCode.ExecuteFailed, "TASK_EXECUTION_FAILED");
39217
+ }
39218
+ throw err;
39219
+ }
39220
+ }
39221
+ }
39142
39222
  const executor = service.getExecutor();
39143
39223
  const executeStartedAt = Date.now();
39144
39224
  if (!output.json) writeHumanOutput(t("create.writingResources", intents.length));
@@ -39219,17 +39299,14 @@ ${taskValidation.missingInputs.map((k2) => `--set ${k2}=<value>`).join("\n")}`,
39219
39299
  await finishJournal(journal);
39220
39300
  });
39221
39301
  events.report("record", "success", Date.now() - recordStartedAt, { planDigest: planResult.plan.planDigest });
39222
- const allTasksWithContext = allResolutions.flatMap((resolution) => {
39223
- const tTasks = resolution.manifest.tasks ?? [];
39224
- return tTasks.map((task) => ({
39225
- task,
39226
- templateDir: service.getPackageRoot(resolution) ?? service.projectRoot
39227
- }));
39302
+ const afterCreateTasks = allTasksWithContext.filter(({ task }) => {
39303
+ const hooks = task.on && task.on.length > 0 ? task.on : ["after:create"];
39304
+ return hooks.includes("after:create");
39228
39305
  });
39229
- if (allTasksWithContext.length > 0) {
39306
+ if (afterCreateTasks.length > 0) {
39230
39307
  const { basename: basename4, dirname: dirname15 } = await import("node:path");
39231
- for (let i = 0; i < allTasksWithContext.length; i++) {
39232
- const { task, templateDir } = allTasksWithContext[i];
39308
+ for (let i = 0; i < afterCreateTasks.length; i++) {
39309
+ const { task, templateDir } = afterCreateTasks[i];
39233
39310
  const taskContext = {
39234
39311
  projectName: basename4(service.projectRoot),
39235
39312
  projectDir: service.projectRoot,
@@ -39238,7 +39315,7 @@ ${taskValidation.missingInputs.map((k2) => `--set ${k2}=<value>`).join("\n")}`,
39238
39315
  inputs: inputValues
39239
39316
  };
39240
39317
  if (!output.json && i === 0) {
39241
- writeHumanOutput("\n\u5C06\u6309\u987A\u5E8F\u6267\u884C\u5DE5\u7A0B\u521D\u59CB\u5316\u4EFB\u52A1:\n");
39318
+ writeHumanOutput("\n\u6B63\u5728\u6267\u884C\u540E\u7F6E\u521D\u59CB\u5316\u4EFB\u52A1 (after:create):\n");
39242
39319
  }
39243
39320
  try {
39244
39321
  await executeTasks2([task], taskContext, {
@@ -39527,6 +39604,47 @@ async function executeApply(packageSpec, options, cliVersion, events) {
39527
39604
  }
39528
39605
  return ExitCode.Success;
39529
39606
  }
39607
+ const allTasksWithContext = allResolutions.flatMap((resolution) => {
39608
+ const tTasks = resolution.manifest.tasks ?? [];
39609
+ return tTasks.map((task) => ({
39610
+ task,
39611
+ templateDir: service.getPackageRoot(resolution) ?? service.projectRoot
39612
+ }));
39613
+ });
39614
+ const beforeApplyTasks = allTasksWithContext.filter(({ task }) => {
39615
+ const hooks = task.on && task.on.length > 0 ? task.on : ["after:create"];
39616
+ return hooks.includes("before:apply");
39617
+ });
39618
+ if (beforeApplyTasks.length > 0) {
39619
+ const { basename: basename4, dirname: dirname15 } = await import("node:path");
39620
+ for (let i = 0; i < beforeApplyTasks.length; i++) {
39621
+ const { task, templateDir } = beforeApplyTasks[i];
39622
+ const taskContext = {
39623
+ projectName: basename4(service.projectRoot),
39624
+ projectDir: service.projectRoot,
39625
+ projectParentDir: dirname15(service.projectRoot),
39626
+ templateDir,
39627
+ inputs: inputValues
39628
+ };
39629
+ if (!output.json && i === 0) {
39630
+ writeHumanOutput("\n\u6B63\u5728\u6267\u884C\u524D\u7F6E\u5E94\u7528\u4EFB\u52A1 (before:apply):\n");
39631
+ }
39632
+ try {
39633
+ await executeTasks([task], taskContext, {
39634
+ onProgress: (msg) => {
39635
+ if (!output.json) writeHumanOutput(`> ${msg}
39636
+ `);
39637
+ },
39638
+ stdio: output.json ? "ignore" : "inherit"
39639
+ });
39640
+ } catch (err) {
39641
+ if (err instanceof Error) {
39642
+ throw new CliError(err.message, ExitCode.ExecuteFailed, "TASK_EXECUTION_FAILED");
39643
+ }
39644
+ throw err;
39645
+ }
39646
+ }
39647
+ }
39530
39648
  const executor = service.getExecutor();
39531
39649
  const executeStartedAt = Date.now();
39532
39650
  const { journal, result: executeResult } = await withJournalTransaction(service, async (journal2) => {
@@ -39582,6 +39700,40 @@ async function executeApply(packageSpec, options, cliVersion, events) {
39582
39700
  await finishJournal(journal);
39583
39701
  });
39584
39702
  events.report("record", "success", Date.now() - recordStartedAt, { planDigest: planResult.plan.planDigest });
39703
+ const afterApplyTasks = allTasksWithContext.filter(({ task }) => {
39704
+ const hooks = task.on && task.on.length > 0 ? task.on : ["after:create"];
39705
+ return hooks.includes("after:apply");
39706
+ });
39707
+ if (afterApplyTasks.length > 0) {
39708
+ const { basename: basename4, dirname: dirname15 } = await import("node:path");
39709
+ for (let i = 0; i < afterApplyTasks.length; i++) {
39710
+ const { task, templateDir } = afterApplyTasks[i];
39711
+ const taskContext = {
39712
+ projectName: basename4(service.projectRoot),
39713
+ projectDir: service.projectRoot,
39714
+ projectParentDir: dirname15(service.projectRoot),
39715
+ templateDir,
39716
+ inputs: inputValues
39717
+ };
39718
+ if (!output.json && i === 0) {
39719
+ writeHumanOutput("\n\u6B63\u5728\u6267\u884C\u540E\u7F6E\u5E94\u7528\u4EFB\u52A1 (after:apply):\n");
39720
+ }
39721
+ try {
39722
+ await executeTasks([task], taskContext, {
39723
+ onProgress: (msg) => {
39724
+ if (!output.json) writeHumanOutput(`> ${msg}
39725
+ `);
39726
+ },
39727
+ stdio: output.json ? "ignore" : "inherit"
39728
+ });
39729
+ } catch (err) {
39730
+ if (err instanceof Error) {
39731
+ throw new CliError(err.message, ExitCode.ExecuteFailed, "TASK_EXECUTION_FAILED");
39732
+ }
39733
+ throw err;
39734
+ }
39735
+ }
39736
+ }
39585
39737
  const summary = summarizePlan(planResult.plan);
39586
39738
  if (output.json) {
39587
39739
  emitSuccessJson("apply", { dryRun: false, ...summary, lockPath: lockRepo.path, operationId: journal.operationId }, [], [], jsonEvents());
@@ -41116,7 +41268,7 @@ var init_upgrade = __esm({
41116
41268
 
41117
41269
  // packages/cli/src/commands/cache.ts
41118
41270
  import { homedir as homedir3 } from "node:os";
41119
- import { join as join18, resolve as resolve20 } from "node:path";
41271
+ import { join as join19, resolve as resolve20 } from "node:path";
41120
41272
  import { rm as rm5, stat as stat7 } from "node:fs/promises";
41121
41273
  function createCacheCommand(cliVersion) {
41122
41274
  const cmd = new Command("cache");
@@ -41158,7 +41310,7 @@ function createCacheCommand(cliVersion) {
41158
41310
  return cmd;
41159
41311
  }
41160
41312
  function getUserDataDir() {
41161
- return process.env["AICODING_USER_DATA"] ?? join18(homedir3(), ".aicoding");
41313
+ return process.env["AICODING_USER_DATA"] ?? join19(homedir3(), ".aicoding");
41162
41314
  }
41163
41315
  function parsePositiveInt(value, name) {
41164
41316
  if (value === void 0) return void 0;
@@ -41360,7 +41512,7 @@ async function executeClean(opts, cliVersion) {
41360
41512
  throw new CliError(t("cache.cleanRequiresYes"), ExitCode.NeedsInputOrDenied, "NEEDS_CONFIRMATION");
41361
41513
  }
41362
41514
  if (cleanRegistry && !dryRun) await updateCache.clear();
41363
- if (cleanAudit && !dryRun) await rm5(join18(userDataDir, "audit"), { recursive: true, force: true });
41515
+ if (cleanAudit && !dryRun) await rm5(join19(userDataDir, "audit"), { recursive: true, force: true });
41364
41516
  if (json) {
41365
41517
  emitSuccessJson("cache clean", { dryRun, cleanedLeases: 0, clearedUpdateCache: cleanRegistry, clearedAudit: cleanAudit, message: t("cache.noInstallationsClean") });
41366
41518
  } else {
@@ -41375,7 +41527,7 @@ async function executeClean(opts, cliVersion) {
41375
41527
  throw new CliError(t("cache.cleanRequiresYes"), ExitCode.NeedsInputOrDenied, "NEEDS_CONFIRMATION");
41376
41528
  }
41377
41529
  if (cleanRegistry && !dryRun) await updateCache.clear();
41378
- if (cleanAudit && !dryRun) await rm5(join18(userDataDir, "audit"), { recursive: true, force: true });
41530
+ if (cleanAudit && !dryRun) await rm5(join19(userDataDir, "audit"), { recursive: true, force: true });
41379
41531
  if (json) {
41380
41532
  emitSuccessJson("cache clean", { dryRun, cleanedLeases: 0, clearedUpdateCache: cleanRegistry, clearedAudit: cleanAudit, message: t("cache.noInstallationsClean") });
41381
41533
  } else {
@@ -41420,7 +41572,7 @@ async function executeClean(opts, cliVersion) {
41420
41572
  totalCleaned += cleaned;
41421
41573
  }
41422
41574
  if (cleanRegistry) await updateCache.clear();
41423
- if (cleanAudit) await rm5(join18(userDataDir, "audit"), { recursive: true, force: true });
41575
+ if (cleanAudit) await rm5(join19(userDataDir, "audit"), { recursive: true, force: true });
41424
41576
  if (json) {
41425
41577
  emitSuccessJson("cache clean", { dryRun: false, cleanedLeases: totalCleaned, clearedUpdateCache: cleanRegistry, clearedAudit: cleanAudit });
41426
41578
  } else {
@@ -41442,7 +41594,7 @@ var init_cache = __esm({
41442
41594
  // packages/cli/src/commands/launch.ts
41443
41595
  import { spawn as spawn2 } from "node:child_process";
41444
41596
  import { homedir as homedir4 } from "node:os";
41445
- import { join as join19 } from "node:path";
41597
+ import { join as join20 } from "node:path";
41446
41598
  function createLaunchCommand() {
41447
41599
  const cmd = new Command("_launch");
41448
41600
  cmd.description(t("launch.description")).option("--kind <kind>", t("launch.optionKind")).option("--package <package-id>", t("launch.optionPackage")).option("--id <id>", t("launch.optionId")).option("--version <version>", t("launch.optionVersion")).option("--command <name>", t("launch.optionCommand")).allowUnknownOption(true).allowExcessArguments(true).action(async (_opts, command) => {
@@ -41475,7 +41627,7 @@ function createLaunchCommand() {
41475
41627
  return cmd;
41476
41628
  }
41477
41629
  function getUserDataDir2() {
41478
- return process.env["AICODING_USER_DATA"] ?? join19(homedir4(), ".aicoding");
41630
+ return process.env["AICODING_USER_DATA"] ?? join20(homedir4(), ".aicoding");
41479
41631
  }
41480
41632
  async function executeLaunch(launcherArgs) {
41481
41633
  const userDataDir = getUserDataDir2();
@@ -41523,7 +41675,7 @@ var init_launch = __esm({
41523
41675
  import { spawn as spawn3 } from "node:child_process";
41524
41676
  import { readFile as readFile28, realpath as realpath2 } from "node:fs/promises";
41525
41677
  import { homedir as homedir5 } from "node:os";
41526
- import { extname, join as join20, resolve as resolve21 } from "node:path";
41678
+ import { extname, join as join21, resolve as resolve21 } from "node:path";
41527
41679
  import { createInterface as createInterface2 } from "node:readline/promises";
41528
41680
  import { stdin, stderr } from "node:process";
41529
41681
  function createSelfUpgradeCommand(cliVersion) {
@@ -41585,7 +41737,7 @@ async function executeSelfUpgrade(options, cliVersion) {
41585
41737
  });
41586
41738
  await auditLog.prune().catch(() => void 0);
41587
41739
  try {
41588
- const result = await withStateLock(join20(userDataDir, "cli-update"), async () => {
41740
+ const result = await withStateLock(join21(userDataDir, "cli-update"), async () => {
41589
41741
  const target = await resolveTarget(options, cacheRepository, registry);
41590
41742
  ensureTargetAllowed(target.version, installation.currentVersion, options.allowDowngrade);
41591
41743
  if (!isCliVersionCompatible(process.version, target.enginesNode)) {
@@ -41637,7 +41789,7 @@ function validateOptions(options) {
41637
41789
  }
41638
41790
  }
41639
41791
  function getUserDataDir3() {
41640
- return process.env["AICODING_USER_DATA"] ?? join20(homedir5(), ".aicoding");
41792
+ return process.env["AICODING_USER_DATA"] ?? join21(homedir5(), ".aicoding");
41641
41793
  }
41642
41794
  function getRegistryUrl() {
41643
41795
  const raw = process.env["AICODING_NPM_REGISTRY"] || DEFAULT_NPM_REGISTRY;
@@ -41756,7 +41908,7 @@ async function isWindowsCmdShimFor(shimPath, expectedBin) {
41756
41908
  }
41757
41909
  async function readPackageJson(root) {
41758
41910
  try {
41759
- const value = JSON.parse(await readFile28(join20(root, "package.json"), "utf8"));
41911
+ const value = JSON.parse(await readFile28(join21(root, "package.json"), "utf8"));
41760
41912
  if (!value || typeof value !== "object") return void 0;
41761
41913
  const item = value;
41762
41914
  return typeof item["name"] === "string" && typeof item["version"] === "string" ? { name: item["name"], version: item["version"], bin: item["bin"] } : void 0;
@@ -41821,11 +41973,11 @@ var init_self_upgrade = __esm({
41821
41973
 
41822
41974
  // packages/cli/src/commands/update-notice.ts
41823
41975
  import { homedir as homedir6 } from "node:os";
41824
- import { join as join21 } from "node:path";
41976
+ import { join as join22 } from "node:path";
41825
41977
  async function getCachedUpdateNotice(argv, currentVersion) {
41826
41978
  const json = argv.includes("--json");
41827
41979
  const disabled = argv.includes("--offline") || argv[2] === "self-upgrade" || Boolean(process.env["AICODING_NO_UPDATE_CHECK"]) || Boolean(process.env["CI"]);
41828
- const repository = new CliUpdateCacheRepository(process.env["AICODING_USER_DATA"] ?? join21(homedir6(), ".aicoding"));
41980
+ const repository = new CliUpdateCacheRepository(process.env["AICODING_USER_DATA"] ?? join22(homedir6(), ".aicoding"));
41829
41981
  if (disabled || !json && !process.stdout.isTTY) return { json, markNotified: async () => void 0 };
41830
41982
  const cache = await repository.read();
41831
41983
  const notifiedAt = cache?.lastNotifiedAt ? Date.parse(cache.lastNotifiedAt) : Number.NaN;
@@ -41857,7 +42009,7 @@ import { createRequire as createRequire2 } from "node:module";
41857
42009
  import { readFileSync as readFileSync4 } from "node:fs";
41858
42010
  import { fileURLToPath as fileURLToPath5 } from "node:url";
41859
42011
  function resolveVersion() {
41860
- if (true) return "0.1.18";
42012
+ if (true) return "0.1.20";
41861
42013
  const candidates = [
41862
42014
  new URL("../package.json", import.meta.url),
41863
42015
  new URL("../../package.json", import.meta.url),
@@ -55,6 +55,13 @@
55
55
  "additionalProperties": {
56
56
  "type": "string"
57
57
  }
58
+ },
59
+ "on": {
60
+ "type": "array",
61
+ "items": {
62
+ "enum": ["before:create", "after:create", "before:apply", "after:apply"]
63
+ },
64
+ "uniqueItems": true
58
65
  }
59
66
  },
60
67
  "additionalProperties": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mano-coding",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "Mano Coding CLI and toolchain",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -55,6 +55,13 @@
55
55
  "additionalProperties": {
56
56
  "type": "string"
57
57
  }
58
+ },
59
+ "on": {
60
+ "type": "array",
61
+ "items": {
62
+ "enum": ["before:create", "after:create", "before:apply", "after:apply"]
63
+ },
64
+ "uniqueItems": true
58
65
  }
59
66
  },
60
67
  "additionalProperties": false