@nalvietnam/avatar-cli 1.2.8 → 1.2.10

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/index.js CHANGED
@@ -1865,6 +1865,40 @@ async function ensureGitHubReady(remoteUrl) {
1865
1865
  }
1866
1866
 
1867
1867
  // src/lib/create-workspace-remote-via-gh.ts
1868
+ var CreateWorkspaceRemoteError = class extends Error {
1869
+ reason;
1870
+ fullName;
1871
+ stderr;
1872
+ constructor(reason, fullName, message, stderr) {
1873
+ super(message);
1874
+ this.name = "CreateWorkspaceRemoteError";
1875
+ this.reason = reason;
1876
+ this.fullName = fullName;
1877
+ this.stderr = stderr;
1878
+ }
1879
+ };
1880
+ function classifyGhCreateError(stderr) {
1881
+ const text = stderr.toLowerCase();
1882
+ if (text.includes("name already exists") || text.includes("already exists on this account") || text.includes("repository already exists")) {
1883
+ return "repo-exists";
1884
+ }
1885
+ if (text.includes("403") || text.includes("permission") || text.includes("not authorized") || text.includes("forbidden")) {
1886
+ return "no-permission";
1887
+ }
1888
+ if (text.includes("invalid") && text.includes("name")) {
1889
+ return "name-invalid";
1890
+ }
1891
+ if (text.includes("could not resolve") || text.includes("network") || text.includes("connection refused")) {
1892
+ return "network";
1893
+ }
1894
+ return "unknown";
1895
+ }
1896
+ function repoExistsOnGitHub(fullName) {
1897
+ const r = spawnSync16("gh", ["repo", "view", fullName, "--json", "name"], {
1898
+ stdio: "ignore"
1899
+ });
1900
+ return r.status === 0;
1901
+ }
1868
1902
  function canCreateInNamespace(org, ghUser) {
1869
1903
  if (org.toLowerCase() === ghUser.toLowerCase()) return { ok: true };
1870
1904
  const r = spawnSync16("gh", ["api", `orgs/${org}/members/${ghUser}`, "--silent"], {
@@ -1901,6 +1935,13 @@ async function createWorkspaceRemoteViaGh(input5) {
1901
1935
  throw new Error(`Kh\xF4ng th\u1EC3 t\u1EA1o repo d\u01B0\u1EDBi '${org}/': ${namespaceCheck.reason}`);
1902
1936
  }
1903
1937
  const fullName = `${org}/${input5.workspaceName}`;
1938
+ if (repoExistsOnGitHub(fullName)) {
1939
+ throw new CreateWorkspaceRemoteError(
1940
+ "repo-exists",
1941
+ fullName,
1942
+ `Repo '${fullName}' \u0111\xE3 t\u1ED3n t\u1EA1i tr\xEAn GitHub. C\xF3 th\u1EC3 b\u1EA1n \u0111\xE3 init workspace n\xE0y tr\u01B0\u1EDBc \u0111\xF3.`
1943
+ );
1944
+ }
1904
1945
  log.info(`T\u1EA1o GitHub repo cho workspace: ${fullName} (${input5.visibility})...`);
1905
1946
  const r = spawnSync16(
1906
1947
  "gh",
@@ -1915,14 +1956,24 @@ async function createWorkspaceRemoteViaGh(input5) {
1915
1956
  "origin",
1916
1957
  "--push"
1917
1958
  ],
1918
- { stdio: ["inherit", "inherit", "pipe"], encoding: "utf8" }
1959
+ { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" }
1919
1960
  );
1920
1961
  if (r.status !== 0) {
1921
1962
  const stderr = (r.stderr || "").trim();
1922
- if (stderr) process.stderr.write(`${stderr}
1963
+ const stdout = (r.stdout || "").trim();
1964
+ const combined = [stderr, stdout].filter(Boolean).join("\n");
1965
+ const reason = classifyGhCreateError(combined);
1966
+ if (combined) {
1967
+ process.stderr.write(`
1968
+ ${combined}
1969
+
1923
1970
  `);
1924
- throw new Error(
1925
- `T\u1EA1o workspace remote th\u1EA5t b\u1EA1i (exit ${r.status}). Workspace v\u1EABn d\xF9ng \u0111\u01B0\u1EE3c local. Setup remote sau qua: gh repo create ${fullName} --${input5.visibility} --source=. --remote=origin --push`
1971
+ }
1972
+ throw new CreateWorkspaceRemoteError(
1973
+ reason,
1974
+ fullName,
1975
+ `T\u1EA1o workspace remote th\u1EA5t b\u1EA1i (exit ${r.status}): ${reason}.`,
1976
+ combined
1926
1977
  );
1927
1978
  }
1928
1979
  const sshUrl = `git@github.com:${fullName}.git`;
@@ -1930,6 +1981,25 @@ async function createWorkspaceRemoteViaGh(input5) {
1930
1981
  log.success(`Workspace remote: ${sshUrl}`);
1931
1982
  return { sshUrl, httpsUrl };
1932
1983
  }
1984
+ function linkExistingRemoteToWorkspace(args) {
1985
+ const sshUrl = `git@github.com:${args.fullName}.git`;
1986
+ const httpsUrl = `https://github.com/${args.fullName}.git`;
1987
+ const addResult = spawnSync16(
1988
+ "git",
1989
+ ["-C", args.workspacePath, "remote", "add", "origin", sshUrl],
1990
+ {
1991
+ encoding: "utf8",
1992
+ stdio: ["ignore", "pipe", "pipe"]
1993
+ }
1994
+ );
1995
+ if (addResult.status !== 0) {
1996
+ spawnSync16("git", ["-C", args.workspacePath, "remote", "set-url", "origin", sshUrl], {
1997
+ stdio: "ignore"
1998
+ });
1999
+ }
2000
+ log.success(`Linked existing remote: ${sshUrl}`);
2001
+ return { sshUrl, httpsUrl };
2002
+ }
1933
2003
 
1934
2004
  // src/lib/safe-bootstrap-for-dirty-folder.ts
1935
2005
  import { readdirSync } from "fs";
@@ -1997,8 +2067,14 @@ import { dirname as dirname3, join as join13 } from "path";
1997
2067
  import { fileURLToPath as fileURLToPath2 } from "url";
1998
2068
  var __dirname = dirname3(fileURLToPath2(import.meta.url));
1999
2069
  var CANDIDATE_DIRS = [
2070
+ // Bundled production: dist/index.js → __dirname = .../dist/, sibling dist/templates
2071
+ join13(__dirname, "templates", "gitignore"),
2072
+ // Legacy bundled: nếu file là dist/lib/*.js (sub-bundle), templates ở dist/templates
2000
2073
  join13(__dirname, "..", "templates", "gitignore"),
2001
- join13(__dirname, "..", "..", "src", "templates", "gitignore")
2074
+ // Dev mode (vitest/tsx run src/ trực tiếp): __dirname = src/lib/
2075
+ join13(__dirname, "..", "..", "src", "templates", "gitignore"),
2076
+ // npm-installed alt: __dirname = .../dist/ → package_root/src/templates
2077
+ join13(__dirname, "..", "src", "templates", "gitignore")
2002
2078
  ];
2003
2079
  var AVATAR_MARKER_START = "# === avatar ===";
2004
2080
  var AVATAR_MARKER_END = "# === /avatar ===";
@@ -2787,6 +2863,44 @@ async function maybeCreateWorkspaceRemote(args) {
2787
2863
  });
2788
2864
  return;
2789
2865
  } catch (err) {
2866
+ if (err instanceof CreateWorkspaceRemoteError && err.reason === "repo-exists") {
2867
+ const fullName = err.fullName;
2868
+ const reuseAction = await select7({
2869
+ message: `Repo '${fullName}' \u0111\xE3 t\u1ED3n t\u1EA1i tr\xEAn GitHub. C\xE1ch x\u1EED l\xFD?`,
2870
+ choices: [
2871
+ {
2872
+ name: "D\xF9ng remote \u0111\xE3 c\xF3 (link workspace local v\xE0o repo n\xE0y)",
2873
+ value: "reuse"
2874
+ },
2875
+ {
2876
+ name: "Nh\u1EADp t\xEAn workspace kh\xE1c (t\u1EA1o repo m\u1EDBi)",
2877
+ value: "rename"
2878
+ },
2879
+ { name: "B\u1ECF qua (workspace local-only)", value: "skip" },
2880
+ { name: "T\u1EA1m ng\u01B0ng init", value: "abort" }
2881
+ ]
2882
+ });
2883
+ if (reuseAction === "abort") {
2884
+ throw new UserAbortedRecoveryError("User abort t\u1EA1i b\u01B0\u1EDBc t\u1EA1o workspace remote.");
2885
+ }
2886
+ if (reuseAction === "skip") {
2887
+ log.warn("Workspace v\u1EABn s\u1EB5n s\xE0ng local-only. Setup remote sau khi c\u1EA7n.");
2888
+ return;
2889
+ }
2890
+ if (reuseAction === "reuse") {
2891
+ linkExistingRemoteToWorkspace({
2892
+ workspacePath: args.workspacePath,
2893
+ fullName
2894
+ });
2895
+ return;
2896
+ }
2897
+ const newName = await input4({
2898
+ message: "T\xEAn workspace m\u1EDBi (s\u1EBD t\u1EA1o repo new):",
2899
+ validate: (v) => v.trim().length > 0 ? true : "T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng"
2900
+ });
2901
+ args.workspaceName = newName.trim();
2902
+ continue;
2903
+ }
2790
2904
  const action = await promptRetryOrSkip({
2791
2905
  taskName: "T\u1EA1o workspace remote tr\xEAn GitHub",
2792
2906
  reason: err instanceof Error ? err.message : String(err),
@@ -3167,7 +3281,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
3167
3281
  }
3168
3282
 
3169
3283
  // src/commands/uninstall.ts
3170
- var CLI_VERSION = "1.2.8";
3284
+ var CLI_VERSION = "1.2.10";
3171
3285
  function registerUninstallCommand(program2) {
3172
3286
  program2.command("uninstall").description("G\u1EE1 Avatar kh\u1ECFi project \u2014 backup t\u1EF1 \u0111\u1ED9ng (M11)").option("--yes", "Skip confirm prompt").option("--no-backup", "Kh\xF4ng t\u1EA1o backup tr\u01B0\u1EDBc khi x\xF3a (nguy hi\u1EC3m)").option("--keep-submodule", "Gi\u1EEF submodule .claude/pack/").option("--keep-hooks", "Gi\u1EEF git hooks post-merge, pre-push").option("--dry-run", "Hi\u1EC3n th\u1ECB danh s\xE1ch s\u1EBD x\xF3a, kh\xF4ng th\u1EF1c thi").action(async (opts) => {
3173
3287
  try {
@@ -3249,7 +3363,7 @@ function printUninstallSuccessBox(backupPath) {
3249
3363
  }
3250
3364
 
3251
3365
  // src/index.ts
3252
- var CLI_VERSION2 = "1.2.8";
3366
+ var CLI_VERSION2 = "1.2.10";
3253
3367
  var program = new Command();
3254
3368
  program.name("avatar").description("AI harness CLI for NAL Vietnam engineering").version(CLI_VERSION2, "-v, --version", "Hi\u1EC3n th\u1ECB phi\xEAn b\u1EA3n Avatar CLI").addHelpText(
3255
3369
  "beforeAll",