@urateam/cli 0.1.39 → 0.1.41

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.
Files changed (61) hide show
  1. package/dist/__tests__/init.test.d.ts +2 -0
  2. package/dist/__tests__/init.test.d.ts.map +1 -0
  3. package/dist/__tests__/init.test.js +65 -0
  4. package/dist/__tests__/init.test.js.map +1 -0
  5. package/dist/__tests__/repo.test.d.ts +2 -0
  6. package/dist/__tests__/repo.test.d.ts.map +1 -0
  7. package/dist/__tests__/repo.test.js +191 -0
  8. package/dist/__tests__/repo.test.js.map +1 -0
  9. package/dist/__tests__/service-unit.test.d.ts +2 -0
  10. package/dist/__tests__/service-unit.test.d.ts.map +1 -0
  11. package/dist/__tests__/service-unit.test.js +65 -0
  12. package/dist/__tests__/service-unit.test.js.map +1 -0
  13. package/dist/__tests__/service.test.d.ts +2 -0
  14. package/dist/__tests__/service.test.d.ts.map +1 -0
  15. package/dist/__tests__/service.test.js +193 -0
  16. package/dist/__tests__/service.test.js.map +1 -0
  17. package/dist/__tests__/start-user-level-fallback.test.d.ts +2 -0
  18. package/dist/__tests__/start-user-level-fallback.test.d.ts.map +1 -0
  19. package/dist/__tests__/start-user-level-fallback.test.js +191 -0
  20. package/dist/__tests__/start-user-level-fallback.test.js.map +1 -0
  21. package/dist/__tests__/uninstall.test.d.ts +2 -0
  22. package/dist/__tests__/uninstall.test.d.ts.map +1 -0
  23. package/dist/__tests__/uninstall.test.js +51 -0
  24. package/dist/__tests__/uninstall.test.js.map +1 -0
  25. package/dist/__tests__/user-level-config.test.d.ts +2 -0
  26. package/dist/__tests__/user-level-config.test.d.ts.map +1 -0
  27. package/dist/__tests__/user-level-config.test.js +92 -0
  28. package/dist/__tests__/user-level-config.test.js.map +1 -0
  29. package/dist/commands/init.d.ts +11 -0
  30. package/dist/commands/init.d.ts.map +1 -0
  31. package/dist/commands/init.js +27 -0
  32. package/dist/commands/init.js.map +1 -0
  33. package/dist/commands/repo.d.ts +7 -0
  34. package/dist/commands/repo.d.ts.map +1 -0
  35. package/dist/commands/repo.js +126 -0
  36. package/dist/commands/repo.js.map +1 -0
  37. package/dist/commands/service.d.ts +13 -0
  38. package/dist/commands/service.d.ts.map +1 -0
  39. package/dist/commands/service.js +164 -0
  40. package/dist/commands/service.js.map +1 -0
  41. package/dist/commands/start.d.ts.map +1 -1
  42. package/dist/commands/start.js +25 -0
  43. package/dist/commands/start.js.map +1 -1
  44. package/dist/commands/uninstall.d.ts +17 -0
  45. package/dist/commands/uninstall.d.ts.map +1 -0
  46. package/dist/commands/uninstall.js +38 -0
  47. package/dist/commands/uninstall.js.map +1 -0
  48. package/dist/index.js +8 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/lib/build-repo-configs.d.ts.map +1 -1
  51. package/dist/lib/build-repo-configs.js +85 -27
  52. package/dist/lib/build-repo-configs.js.map +1 -1
  53. package/dist/lib/service-unit.d.ts +25 -0
  54. package/dist/lib/service-unit.d.ts.map +1 -0
  55. package/dist/lib/service-unit.js +67 -0
  56. package/dist/lib/service-unit.js.map +1 -0
  57. package/dist/lib/user-level-config.d.ts +70 -0
  58. package/dist/lib/user-level-config.d.ts.map +1 -0
  59. package/dist/lib/user-level-config.js +91 -0
  60. package/dist/lib/user-level-config.js.map +1 -0
  61. package/package.json +4 -3
@@ -0,0 +1,191 @@
1
+ /**
2
+ * `buildRepoConfigsFromEnv` — user-level config fallback.
3
+ *
4
+ * When no `REPO_*` env vars produce a RepoConfig, the function should fall
5
+ * back to reading `~/.urateam/config.json` (or `$URATEAM_HOME/config.json`).
6
+ * Env vars win when both are present so the existing project-level
7
+ * `ura start` deploys are unaffected.
8
+ */
9
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
10
+ import { mkdtempSync, rmSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+ import { writeUserLevelConfig } from "../lib/user-level-config.js";
14
+ import { buildRepoConfigsFromEnv } from "../lib/build-repo-configs.js";
15
+ describe("buildRepoConfigsFromEnv — user-level fallback", () => {
16
+ let tmp;
17
+ const envBackup = {};
18
+ const ENV_KEYS_TO_STRIP = [
19
+ "REPO_TEAM_ID",
20
+ "REPO_URL",
21
+ "REPO_DEFAULT_BRANCH",
22
+ "REPO_TEST_CMD",
23
+ "REPO_BUILD_CMD",
24
+ ];
25
+ beforeEach(() => {
26
+ tmp = mkdtempSync(join(tmpdir(), "ura-fallback-"));
27
+ process.env.URATEAM_HOME = tmp;
28
+ for (const k of ENV_KEYS_TO_STRIP) {
29
+ envBackup[k] = process.env[k];
30
+ delete process.env[k];
31
+ }
32
+ });
33
+ afterEach(() => {
34
+ delete process.env.URATEAM_HOME;
35
+ for (const [k, v] of Object.entries(envBackup)) {
36
+ if (v === undefined)
37
+ delete process.env[k];
38
+ else
39
+ process.env[k] = v;
40
+ delete envBackup[k];
41
+ }
42
+ rmSync(tmp, { recursive: true, force: true });
43
+ });
44
+ it("returns an empty map when no env vars and no user-level config", () => {
45
+ expect(buildRepoConfigsFromEnv()).toEqual({});
46
+ });
47
+ it("loads from ~/.urateam/config.json when no REPO_* env vars are set", () => {
48
+ writeUserLevelConfig({
49
+ version: 1,
50
+ repos: [
51
+ {
52
+ url: "https://github.com/o/r.git",
53
+ path: "/tmp/r",
54
+ defaultBranch: "main",
55
+ testCommand: "pnpm test",
56
+ buildCommand: "pnpm build",
57
+ teamId: "team-1",
58
+ },
59
+ ],
60
+ });
61
+ const configs = buildRepoConfigsFromEnv();
62
+ expect(configs["team-1"]).toBeDefined();
63
+ expect(configs["team-1"].url).toBe("https://github.com/o/r.git");
64
+ expect(configs["team-1"].defaultBranch).toBe("main");
65
+ });
66
+ it("propagates labelPattern when present (BEC-177 routing)", () => {
67
+ writeUserLevelConfig({
68
+ version: 1,
69
+ repos: [
70
+ {
71
+ url: "https://github.com/o/r.git",
72
+ path: "/tmp/r",
73
+ defaultBranch: "main",
74
+ testCommand: "pnpm test",
75
+ buildCommand: "pnpm build",
76
+ teamId: "team-1",
77
+ labelPattern: "auto-implement",
78
+ },
79
+ ],
80
+ });
81
+ const configs = buildRepoConfigsFromEnv();
82
+ expect(configs["team-1"].labelPattern).toBe("auto-implement");
83
+ });
84
+ it("keys repos by url-slug when teamId is omitted", () => {
85
+ writeUserLevelConfig({
86
+ version: 1,
87
+ repos: [
88
+ {
89
+ url: "https://github.com/o/no-team.git",
90
+ path: "/tmp/no-team",
91
+ defaultBranch: "main",
92
+ testCommand: "pnpm test",
93
+ buildCommand: "pnpm build",
94
+ },
95
+ ],
96
+ });
97
+ const configs = buildRepoConfigsFromEnv();
98
+ expect(Object.keys(configs)).toHaveLength(1);
99
+ const key = Object.keys(configs)[0];
100
+ expect(key).toContain("no-team");
101
+ });
102
+ it("env vars win over user-level config (project-level back-compat)", () => {
103
+ writeUserLevelConfig({
104
+ version: 1,
105
+ repos: [
106
+ {
107
+ url: "https://github.com/user-level/r.git",
108
+ path: "/p",
109
+ defaultBranch: "main",
110
+ testCommand: "pnpm test",
111
+ buildCommand: "pnpm build",
112
+ teamId: "team-userlevel",
113
+ },
114
+ ],
115
+ });
116
+ process.env.REPO_TEAM_ID = "team-env";
117
+ process.env.REPO_URL = "https://github.com/env-var/r.git";
118
+ const configs = buildRepoConfigsFromEnv();
119
+ expect(configs["team-env"]).toBeDefined();
120
+ expect(configs["team-env"].url).toBe("https://github.com/env-var/r.git");
121
+ expect(configs["team-userlevel"]).toBeUndefined();
122
+ });
123
+ it("handles multiple repos in the user-level config", () => {
124
+ writeUserLevelConfig({
125
+ version: 1,
126
+ repos: [
127
+ {
128
+ url: "https://github.com/o/a.git",
129
+ path: "/tmp/a",
130
+ defaultBranch: "main",
131
+ testCommand: "pnpm test",
132
+ buildCommand: "pnpm build",
133
+ teamId: "team-a",
134
+ },
135
+ {
136
+ url: "https://github.com/o/b.git",
137
+ path: "/tmp/b",
138
+ defaultBranch: "main",
139
+ testCommand: "pnpm test",
140
+ buildCommand: "pnpm build",
141
+ teamId: "team-b",
142
+ },
143
+ ],
144
+ });
145
+ const configs = buildRepoConfigsFromEnv();
146
+ expect(Object.keys(configs).sort()).toEqual(["team-a", "team-b"]);
147
+ });
148
+ });
149
+ describe("requireRepoConfigs — branched error messages", () => {
150
+ let tmp;
151
+ beforeEach(() => {
152
+ tmp = mkdtempSync(join(tmpdir(), "ura-req-"));
153
+ process.env.URATEAM_HOME = tmp;
154
+ });
155
+ afterEach(() => {
156
+ delete process.env.URATEAM_HOME;
157
+ rmSync(tmp, { recursive: true, force: true });
158
+ });
159
+ it("prints a 'ura repo add' hint when ~/.urateam/config.json exists but has no repos", async () => {
160
+ writeUserLevelConfig({ version: 1, repos: [] });
161
+ const errSpy = vi.spyOn(console, "error").mockImplementation(() => { });
162
+ const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {
163
+ throw new Error("exit");
164
+ }));
165
+ const { requireRepoConfigs } = await import("../lib/build-repo-configs.js");
166
+ expect(() => requireRepoConfigs({}, "ura start")).toThrow();
167
+ const msg = errSpy.mock.calls.flat().join("\n");
168
+ expect(msg).toContain("ura repo add");
169
+ expect(msg).not.toContain("REPO_TEAM_ID and REPO_URL in .urateam/.env"); // user-level branch
170
+ errSpy.mockRestore();
171
+ exitSpy.mockRestore();
172
+ });
173
+ it("prints both install paths when no ~/.urateam/config.json exists yet", async () => {
174
+ // Don't write a config — exercises the `userConfig === null` branch
175
+ const errSpy = vi.spyOn(console, "error").mockImplementation(() => { });
176
+ const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {
177
+ throw new Error("exit");
178
+ }));
179
+ const { requireRepoConfigs } = await import("../lib/build-repo-configs.js");
180
+ expect(() => requireRepoConfigs({}, "ura start")).toThrow();
181
+ const msg = errSpy.mock.calls.flat().join("\n");
182
+ expect(msg).toContain("REPO_TEAM_ID");
183
+ expect(msg).toContain("ura init");
184
+ expect(msg).toContain("ura repo add");
185
+ errSpy.mockRestore();
186
+ exitSpy.mockRestore();
187
+ });
188
+ });
189
+ // vi needs to be available — re-import at top-level for the new tests
190
+ import { vi } from "vitest";
191
+ //# sourceMappingURL=start-user-level-fallback.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-user-level-fallback.test.js","sourceRoot":"","sources":["../../src/__tests__/start-user-level-fallback.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=uninstall.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/uninstall.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `ura uninstall` — remove the user-level install at ~/.urateam (or
3
+ * $URATEAM_HOME).
4
+ *
5
+ * Destructive, so gated on `--yes`. Without `--yes`, prints what would be
6
+ * deleted and exits 0. With `--yes`, recursively removes the directory.
7
+ * No-op if the directory doesn't exist (so re-running after a manual
8
+ * `rm -rf ~/.urateam` doesn't error).
9
+ */
10
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
11
+ import { mkdtempSync, mkdirSync, existsSync, writeFileSync, rmSync, } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { tmpdir } from "node:os";
14
+ import { uninstallCommand } from "../commands/uninstall.js";
15
+ describe("ura uninstall", () => {
16
+ let tmp;
17
+ beforeEach(() => {
18
+ tmp = mkdtempSync(join(tmpdir(), "ura-uninst-"));
19
+ process.env.URATEAM_HOME = tmp;
20
+ mkdirSync(join(tmp, "data"), { recursive: true });
21
+ mkdirSync(join(tmp, "repos"), { recursive: true });
22
+ writeFileSync(join(tmp, "config.json"), '{"version":1,"repos":[]}');
23
+ });
24
+ afterEach(() => {
25
+ delete process.env.URATEAM_HOME;
26
+ });
27
+ it("removes URATEAM_HOME when --yes is passed", async () => {
28
+ await uninstallCommand.parseAsync(["--yes"], { from: "user" });
29
+ expect(existsSync(tmp)).toBe(false);
30
+ });
31
+ it("aborts without --yes (no destructive action)", async () => {
32
+ const spy = vi.spyOn(console, "log").mockImplementation(() => { });
33
+ await uninstallCommand.parseAsync([], { from: "user" });
34
+ expect(existsSync(tmp)).toBe(true);
35
+ expect(spy.mock.calls.flat().join("\n")).toMatch(/--yes/i);
36
+ spy.mockRestore();
37
+ // Test-cleanup: remove the directory manually since uninstall didn't.
38
+ rmSync(tmp, { recursive: true, force: true });
39
+ });
40
+ it("is a no-op when URATEAM_HOME does not exist", async () => {
41
+ rmSync(tmp, { recursive: true, force: true });
42
+ await expect(uninstallCommand.parseAsync(["--yes"], { from: "user" })).resolves.not.toThrow();
43
+ });
44
+ it("prints an npm-uninstall hint after a successful removal", async () => {
45
+ const spy = vi.spyOn(console, "log").mockImplementation(() => { });
46
+ await uninstallCommand.parseAsync(["--yes"], { from: "user" });
47
+ expect(spy.mock.calls.flat().join("\n")).toContain("npm uninstall -g @urateam/cli");
48
+ spy.mockRestore();
49
+ });
50
+ });
51
+ //# sourceMappingURL=uninstall.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.test.js","sourceRoot":"","sources":["../../src/__tests__/uninstall.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=user-level-config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-level-config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/user-level-config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * User-level config — schema + read/write/path helpers.
3
+ *
4
+ * The user-level install path stores its state at `~/.urateam/` (or
5
+ * `$URATEAM_HOME` when set). This module is the source of truth for the
6
+ * schema, the on-disk locations, and the round-trip read/write contract.
7
+ *
8
+ * Tests use a temp-dir override via `URATEAM_HOME` to keep them hermetic.
9
+ */
10
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
11
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { tmpdir } from "node:os";
14
+ import { resolveUserLevelHome, readUserLevelConfig, writeUserLevelConfig, UserLevelConfigSchema, } from "../lib/user-level-config.js";
15
+ describe("resolveUserLevelHome", () => {
16
+ const envBefore = process.env.URATEAM_HOME;
17
+ afterEach(() => {
18
+ if (envBefore === undefined)
19
+ delete process.env.URATEAM_HOME;
20
+ else
21
+ process.env.URATEAM_HOME = envBefore;
22
+ });
23
+ it("returns ~/.urateam by default", () => {
24
+ delete process.env.URATEAM_HOME;
25
+ const home = resolveUserLevelHome();
26
+ expect(home.endsWith("/.urateam")).toBe(true);
27
+ });
28
+ it("honors URATEAM_HOME override", () => {
29
+ process.env.URATEAM_HOME = "/tmp/test-urateam";
30
+ expect(resolveUserLevelHome()).toBe("/tmp/test-urateam");
31
+ });
32
+ });
33
+ describe("readUserLevelConfig / writeUserLevelConfig", () => {
34
+ let tmp;
35
+ beforeEach(() => {
36
+ tmp = mkdtempSync(join(tmpdir(), "ura-test-"));
37
+ process.env.URATEAM_HOME = tmp;
38
+ });
39
+ afterEach(() => {
40
+ delete process.env.URATEAM_HOME;
41
+ rmSync(tmp, { recursive: true, force: true });
42
+ });
43
+ it("round-trips an empty config", () => {
44
+ const config = { version: 1, repos: [] };
45
+ writeUserLevelConfig(config);
46
+ expect(readUserLevelConfig()).toEqual(config);
47
+ });
48
+ it("returns null when the config file does not exist", () => {
49
+ expect(readUserLevelConfig()).toBeNull();
50
+ });
51
+ it("validates repo entries against the zod schema (rejects malformed)", () => {
52
+ writeFileSync(join(tmp, "config.json"), JSON.stringify({ version: 1, repos: [{ url: 42 }] }));
53
+ expect(() => readUserLevelConfig()).toThrow();
54
+ });
55
+ it("schema accepts minimum repo fields (url + defaultBranch + paths + commands)", () => {
56
+ const result = UserLevelConfigSchema.safeParse({
57
+ version: 1,
58
+ repos: [
59
+ {
60
+ url: "https://github.com/o/r.git",
61
+ path: "/tmp/r",
62
+ defaultBranch: "main",
63
+ testCommand: "pnpm test",
64
+ buildCommand: "pnpm build",
65
+ },
66
+ ],
67
+ });
68
+ expect(result.success).toBe(true);
69
+ });
70
+ it("schema accepts optional teamId and labelPattern", () => {
71
+ const result = UserLevelConfigSchema.safeParse({
72
+ version: 1,
73
+ repos: [
74
+ {
75
+ url: "https://github.com/o/r.git",
76
+ path: "/tmp/r",
77
+ defaultBranch: "main",
78
+ testCommand: "pnpm test",
79
+ buildCommand: "pnpm build",
80
+ teamId: "team-abc",
81
+ labelPattern: "auto-implement",
82
+ },
83
+ ],
84
+ });
85
+ expect(result.success).toBe(true);
86
+ if (result.success) {
87
+ expect(result.data.repos[0].teamId).toBe("team-abc");
88
+ expect(result.data.repos[0].labelPattern).toBe("auto-implement");
89
+ }
90
+ });
91
+ });
92
+ //# sourceMappingURL=user-level-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-level-config.test.js","sourceRoot":"","sources":["../../src/__tests__/user-level-config.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { Command } from "commander";
2
+ /**
3
+ * Bootstrap a user-level urateam install at `~/.urateam/` (or `$URATEAM_HOME`).
4
+ *
5
+ * Creates the directory skeleton (`config.json`, `data/`, `repos/`) and
6
+ * prints a one-line next-step hint. Idempotent: if `config.json` already
7
+ * exists, the existing config is preserved — only missing subdirectories
8
+ * are created.
9
+ */
10
+ export declare const initCommand: Command;
11
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,SAmBpB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { Command } from "commander";
2
+ import { mkdirSync } from "node:fs";
3
+ import { resolveUserLevelHome, userLevelConfigPath, userLevelDataDir, userLevelReposDir, writeUserLevelConfig, readUserLevelConfig, } from "../lib/user-level-config.js";
4
+ /**
5
+ * Bootstrap a user-level urateam install at `~/.urateam/` (or `$URATEAM_HOME`).
6
+ *
7
+ * Creates the directory skeleton (`config.json`, `data/`, `repos/`) and
8
+ * prints a one-line next-step hint. Idempotent: if `config.json` already
9
+ * exists, the existing config is preserved — only missing subdirectories
10
+ * are created.
11
+ */
12
+ export const initCommand = new Command("init")
13
+ .description("Bootstrap a user-level urateam install at ~/.urateam (or $URATEAM_HOME)")
14
+ .action(() => {
15
+ const home = resolveUserLevelHome();
16
+ mkdirSync(home, { recursive: true });
17
+ mkdirSync(userLevelDataDir(), { recursive: true });
18
+ mkdirSync(userLevelReposDir(), { recursive: true });
19
+ if (readUserLevelConfig() !== null) {
20
+ console.log(`ura init: ${userLevelConfigPath()} already exists — leaving it untouched.`);
21
+ return;
22
+ }
23
+ writeUserLevelConfig({ version: 1, repos: [] });
24
+ console.log(`ura init: created ${home}`);
25
+ console.log("Next: ura repo add <url>");
26
+ });
27
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AAErC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CACV,yEAAyE,CAC1E;KACA,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,SAAS,CAAC,gBAAgB,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,SAAS,CAAC,iBAAiB,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpD,IAAI,mBAAmB,EAAE,KAAK,IAAI,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CACT,aAAa,mBAAmB,EAAE,yCAAyC,CAC5E,CAAC;QACF,OAAO;IACT,CAAC;IACD,oBAAoB,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { Command } from "commander";
2
+ /**
3
+ * `ura repo` — the parent command. The subcommands below carry the
4
+ * actual behavior.
5
+ */
6
+ export declare const repoCommand: Command;
7
+ //# sourceMappingURL=repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/commands/repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyCpC;;;GAGG;AACH,eAAO,MAAM,WAAW,SAEvB,CAAC"}
@@ -0,0 +1,126 @@
1
+ import { Command } from "commander";
2
+ import { rmSync } from "node:fs";
3
+ import { join, basename } from "node:path";
4
+ import { cloneRepo } from "@urateam/core";
5
+ import { readUserLevelConfig, writeUserLevelConfig, resolveUserLevelHome, userLevelReposDir, } from "../lib/user-level-config.js";
6
+ /**
7
+ * Derive a filesystem-safe slug from a repo URL. Handles:
8
+ * https://github.com/org/name.git → "name"
9
+ * git@github.com:org/name.git → "name"
10
+ * ssh://git@host/path/to/name → "name"
11
+ *
12
+ * Characters outside `[A-Za-z0-9._-]` are replaced with `-` so the slug
13
+ * survives use as a directory name on every platform we target.
14
+ */
15
+ function deriveSlug(url) {
16
+ const stripped = url.replace(/\.git$/, "");
17
+ const last = stripped.split(/[/:]/).filter(Boolean).pop() ?? "repo";
18
+ return last.replace(/[^A-Za-z0-9._-]/g, "-");
19
+ }
20
+ /**
21
+ * Load the user-level config, or throw a helpful "run `ura init` first"
22
+ * error so the operator isn't left guessing why the command failed.
23
+ */
24
+ function loadOrThrow() {
25
+ const cfg = readUserLevelConfig();
26
+ if (!cfg) {
27
+ throw new Error(`ura: no user-level config found. Run 'ura init' first.`);
28
+ }
29
+ return cfg;
30
+ }
31
+ /**
32
+ * `ura repo` — the parent command. The subcommands below carry the
33
+ * actual behavior.
34
+ */
35
+ export const repoCommand = new Command("repo").description("Manage repos in the user-level config (~/.urateam/config.json)");
36
+ /**
37
+ * `ura repo add <url> [--branch] [--test-command] [--build-command]
38
+ * [--team] [--label-pattern]`
39
+ *
40
+ * Clones the repo into `~/.urateam/repos/<slug>` and appends to
41
+ * `config.json`. Rejects duplicate URLs so re-running by accident doesn't
42
+ * produce two near-identical entries for the same upstream.
43
+ */
44
+ repoCommand
45
+ .command("add <url>")
46
+ .description("Clone <url> into ~/.urateam/repos/<slug> and register it")
47
+ .option("--branch <name>", "Default branch (defaults to 'main')", "main")
48
+ .option("--test-command <cmd>", "Test command", "pnpm test")
49
+ .option("--build-command <cmd>", "Build command", "pnpm build")
50
+ .option("--team <id>", "Linear team ID (optional)")
51
+ .option("--label-pattern <pattern>", "Pipeline label pattern (BEC-177 routing)")
52
+ .action(async (url, opts) => {
53
+ const cfg = loadOrThrow();
54
+ if (cfg.repos.some((r) => r.url === url)) {
55
+ throw new Error(`ura: ${url} is already configured.`);
56
+ }
57
+ const slug = deriveSlug(url);
58
+ const path = join(userLevelReposDir(), slug);
59
+ await cloneRepo(url, path);
60
+ const repo = {
61
+ url,
62
+ path,
63
+ defaultBranch: opts.branch,
64
+ testCommand: opts.testCommand,
65
+ buildCommand: opts.buildCommand,
66
+ ...(opts.team && { teamId: opts.team }),
67
+ ...(opts.labelPattern && { labelPattern: opts.labelPattern }),
68
+ };
69
+ cfg.repos.push(repo);
70
+ writeUserLevelConfig(cfg);
71
+ console.log(`ura repo add: cloned ${url} → ${path}`);
72
+ });
73
+ /**
74
+ * `ura repo list` — print the configured repos.
75
+ *
76
+ * Output is plain text; one repo per line. When no repos are configured,
77
+ * prints a hint pointing at `ura repo add`.
78
+ */
79
+ repoCommand
80
+ .command("list")
81
+ .description("List configured repos")
82
+ .action(() => {
83
+ const cfg = loadOrThrow();
84
+ if (cfg.repos.length === 0) {
85
+ console.log("ura repo list: no repos configured. Run 'ura repo add <url>'.");
86
+ return;
87
+ }
88
+ for (const r of cfg.repos) {
89
+ console.log(` ${basename(r.path)}\t${r.url}\t(${r.defaultBranch})`);
90
+ }
91
+ });
92
+ /**
93
+ * `ura repo remove <slug> [--purge]`
94
+ *
95
+ * Removes the repo from `config.json`. The clone on disk is preserved by
96
+ * default so operators don't lose uncommitted work; `--purge` deletes the
97
+ * directory after the config update.
98
+ */
99
+ repoCommand
100
+ .command("remove <slug>")
101
+ .description("Remove a repo from config (use --purge to delete the clone)")
102
+ .option("--purge", "Also delete the cloned directory on disk")
103
+ .action((slug, opts) => {
104
+ const cfg = loadOrThrow();
105
+ const idx = cfg.repos.findIndex((r) => basename(r.path) === slug);
106
+ if (idx === -1) {
107
+ throw new Error(`ura repo remove: slug '${slug}' not found.`);
108
+ }
109
+ const [removed] = cfg.repos.splice(idx, 1);
110
+ writeUserLevelConfig(cfg);
111
+ if (opts.purge && removed) {
112
+ // Path-safety guard: `config.json` is a plain file an operator can
113
+ // hand-edit. Without this check, a config entry with `"path": "/"` or
114
+ // `"path": "/home/operator"` would silently `rm -rf` that path on
115
+ // `--purge`. Restrict purges to descendants of URATEAM_HOME (the
116
+ // directory `ura repo add` writes into by construction).
117
+ const home = resolveUserLevelHome();
118
+ if (!removed.path.startsWith(home + "/")) {
119
+ throw new Error(`ura repo remove --purge: refusing to delete '${removed.path}' — ` +
120
+ `path is not under ${home}. (Config entry may have been hand-edited.)`);
121
+ }
122
+ rmSync(removed.path, { recursive: true, force: true });
123
+ }
124
+ console.log(`ura repo remove: removed '${slug}'${opts.purge ? " and deleted the clone" : ""}`);
125
+ });
126
+ //# sourceMappingURL=repo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/commands/repo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `ura service install` / `ura service uninstall`
3
+ *
4
+ * Generates a platform-appropriate service unit (launchd plist on macOS,
5
+ * systemd-user unit on Linux) so the user-level daemon auto-starts on login.
6
+ * Mirrors what `deploy/USER_LEVEL_INSTALL.md` previously documented as
7
+ * copy-paste blocks. Idempotent: refuses to overwrite an existing unit
8
+ * (operator must uninstall first). `--dry-run` prints the unit content
9
+ * without touching the filesystem.
10
+ */
11
+ import { Command } from "commander";
12
+ export declare const serviceCommand: Command;
13
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/commands/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwGpC,eAAO,MAAM,cAAc,SAE1B,CAAC"}