codiedev 0.10.0-beta.1 → 0.10.0-beta.11

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 (155) hide show
  1. package/README.md +11 -5
  2. package/assets/opencode-plugin.mjs +6 -0
  3. package/assets/session-key.mjs +48 -0
  4. package/assets/tui-brand-plugin.tsx +73 -0
  5. package/dist/backup/cache.js +1 -5
  6. package/dist/backup/match.js +0 -6
  7. package/dist/backup/run.js +3 -35
  8. package/dist/captureExclusions.js +0 -12
  9. package/dist/captureLog.js +0 -28
  10. package/dist/captureReadiness.js +0 -58
  11. package/dist/captureSignals.js +184 -0
  12. package/dist/cli.js +135 -72
  13. package/dist/cliSession.js +456 -0
  14. package/dist/codexHookTrust.js +125 -6
  15. package/dist/codexLimitScan.js +359 -0
  16. package/dist/codie.js +77 -130
  17. package/dist/codieBrandInstall.js +80 -0
  18. package/dist/codieLauncher.js +76 -79
  19. package/dist/codieRuntime.js +19 -23
  20. package/dist/codieRuntimeInstall.js +108 -23
  21. package/dist/codieUsage.js +181 -0
  22. package/dist/commands/agentTemplates.generated.js +136 -0
  23. package/dist/commands/agents.js +1101 -0
  24. package/dist/commands/agentsSdk.generated.js +7 -0
  25. package/dist/commands/ask.js +0 -3
  26. package/dist/commands/capture.js +0 -2
  27. package/dist/commands/createTicket.js +0 -10
  28. package/dist/commands/doctor.js +53 -120
  29. package/dist/commands/evals.js +187 -0
  30. package/dist/commands/inbox.js +0 -2
  31. package/dist/commands/library.js +0 -3
  32. package/dist/commands/pack.js +573 -0
  33. package/dist/commands/ping.js +0 -2
  34. package/dist/commands/pull.js +6 -10
  35. package/dist/commands/push.js +2 -12
  36. package/dist/commands/search.js +0 -4
  37. package/dist/commands/send.js +0 -5
  38. package/dist/commands/share.js +0 -4
  39. package/dist/commands/shared.js +0 -35
  40. package/dist/commands/traces.js +344 -0
  41. package/dist/commands/tracesLabel.js +348 -0
  42. package/dist/configDir.js +18 -11
  43. package/dist/connect.js +55 -118
  44. package/dist/connectFlow.js +35 -9
  45. package/dist/copilotHooks.js +391 -0
  46. package/dist/copilotInstallation.js +64 -0
  47. package/dist/cursorHooks.js +38 -30
  48. package/dist/cursorLifecycle.js +109 -0
  49. package/dist/detection.js +0 -30
  50. package/dist/harnessDirs.js +77 -0
  51. package/dist/hook.js +256 -445
  52. package/dist/inboxFormat.js +0 -24
  53. package/dist/launchContext.js +150 -0
  54. package/dist/launcher.js +168 -0
  55. package/dist/limitEvent.js +44 -13
  56. package/dist/limitEventReporter.js +1 -1
  57. package/dist/managedPi.js +256 -0
  58. package/dist/mcp.js +5 -384
  59. package/dist/nativeSteering.js +24 -0
  60. package/dist/packs/codie.js +182 -0
  61. package/dist/packs/harnesses.js +411 -0
  62. package/dist/packs/hookGate.js +256 -0
  63. package/dist/packs/install.js +286 -0
  64. package/dist/packs/manifest.js +126 -0
  65. package/dist/packs/paths.js +81 -0
  66. package/dist/packs/shim.js +214 -0
  67. package/dist/packs/store.js +155 -0
  68. package/dist/packs/sync.js +170 -0
  69. package/dist/piHooks.js +216 -0
  70. package/dist/piRuntime.js +120 -0
  71. package/dist/repoResolver.js +0 -19
  72. package/dist/secret.js +0 -3
  73. package/dist/selfUpdate.js +376 -0
  74. package/dist/skillMarker.js +23 -0
  75. package/dist/subagents.js +135 -0
  76. package/dist/teamSkillContext.js +10 -0
  77. package/dist/transcriptModels.js +205 -0
  78. package/dist/upgradeNudge.js +0 -30
  79. package/dist/utils.js +502 -828
  80. package/dist/version.js +0 -10
  81. package/dist/versionCheck.js +0 -33
  82. package/package.json +8 -9
  83. package/dist/backup/cache.d.ts +0 -14
  84. package/dist/backup/match.d.ts +0 -2
  85. package/dist/backup/run.d.ts +0 -25
  86. package/dist/captureExclusions.d.ts +0 -30
  87. package/dist/captureLog.d.ts +0 -20
  88. package/dist/captureReadiness.d.ts +0 -65
  89. package/dist/cli.d.ts +0 -16
  90. package/dist/codexHookTrust.d.ts +0 -34
  91. package/dist/codie.d.ts +0 -2
  92. package/dist/codieLauncher.d.ts +0 -53
  93. package/dist/codieRuntime.d.ts +0 -29
  94. package/dist/codieRuntimeInstall.d.ts +0 -28
  95. package/dist/codieStartup.d.ts +0 -26
  96. package/dist/codieStartup.js +0 -30
  97. package/dist/commands/ask.d.ts +0 -1
  98. package/dist/commands/capture.d.ts +0 -14
  99. package/dist/commands/createTicket.d.ts +0 -27
  100. package/dist/commands/delete.d.ts +0 -1
  101. package/dist/commands/doctor.d.ts +0 -1
  102. package/dist/commands/inbox.d.ts +0 -3
  103. package/dist/commands/library.d.ts +0 -2
  104. package/dist/commands/note.d.ts +0 -1
  105. package/dist/commands/ping.d.ts +0 -1
  106. package/dist/commands/promote.d.ts +0 -1
  107. package/dist/commands/promote.js +0 -53
  108. package/dist/commands/pull.d.ts +0 -2
  109. package/dist/commands/push.d.ts +0 -33
  110. package/dist/commands/react.d.ts +0 -2
  111. package/dist/commands/react.js +0 -40
  112. package/dist/commands/reverseTicket.d.ts +0 -2
  113. package/dist/commands/reverseTicket.js +0 -477
  114. package/dist/commands/search.d.ts +0 -2
  115. package/dist/commands/security.d.ts +0 -1
  116. package/dist/commands/security.js +0 -156
  117. package/dist/commands/send.d.ts +0 -2
  118. package/dist/commands/share.d.ts +0 -2
  119. package/dist/commands/shared.d.ts +0 -64
  120. package/dist/configDir.d.ts +0 -1
  121. package/dist/connect.d.ts +0 -1
  122. package/dist/connectFlow.d.ts +0 -13
  123. package/dist/cursorHooks.d.ts +0 -23
  124. package/dist/detection.d.ts +0 -30
  125. package/dist/hook.d.ts +0 -6
  126. package/dist/inboxFormat.d.ts +0 -38
  127. package/dist/limitEvent.d.ts +0 -16
  128. package/dist/limitEventReporter.d.ts +0 -21
  129. package/dist/mcp.d.ts +0 -12
  130. package/dist/prUrl.d.ts +0 -9
  131. package/dist/prUrl.js +0 -33
  132. package/dist/repoResolver.d.ts +0 -17
  133. package/dist/secret.d.ts +0 -1
  134. package/dist/security.d.ts +0 -102
  135. package/dist/security.js +0 -437
  136. package/dist/securityDeepScan.d.ts +0 -41
  137. package/dist/securityDeepScan.js +0 -248
  138. package/dist/securityDoctor.d.ts +0 -32
  139. package/dist/securityDoctor.js +0 -63
  140. package/dist/securityFeature.d.ts +0 -5
  141. package/dist/securityFeature.js +0 -7
  142. package/dist/securityInstall.d.ts +0 -63
  143. package/dist/securityInstall.js +0 -239
  144. package/dist/securityInventory.d.ts +0 -54
  145. package/dist/securityInventory.js +0 -556
  146. package/dist/securityPilot.d.ts +0 -19
  147. package/dist/securityPilot.js +0 -61
  148. package/dist/securityPolicy.d.ts +0 -33
  149. package/dist/securityPolicy.js +0 -188
  150. package/dist/securitySession.d.ts +0 -6
  151. package/dist/securitySession.js +0 -72
  152. package/dist/upgradeNudge.d.ts +0 -25
  153. package/dist/utils.d.ts +0 -342
  154. package/dist/version.d.ts +0 -1
  155. package/dist/versionCheck.d.ts +0 -68
@@ -1,53 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runPromote = runPromote;
4
- const shared_1 = require("./shared");
5
- function parseArgs(args) {
6
- let artifactId;
7
- let keyOverride;
8
- for (let i = 0; i < args.length; i++) {
9
- const a = args[i];
10
- if ((a === "--key" || a === "-k") && i + 1 < args.length) {
11
- keyOverride = args[++i];
12
- }
13
- else if (a.startsWith("--key=")) {
14
- keyOverride = a.slice("--key=".length);
15
- }
16
- else if (!a.startsWith("--") && !artifactId) {
17
- artifactId = a;
18
- }
19
- }
20
- if (!artifactId) {
21
- console.error("Usage: codiedev promote <extracted-artifact-id> [--key spec-foo.md]");
22
- process.exit(1);
23
- }
24
- return { artifactId, keyOverride };
25
- }
26
- async function runPromote(args) {
27
- const { artifactId, keyOverride } = parseArgs(args);
28
- const config = (0, shared_1.requireConfig)();
29
- const start = Date.now();
30
- try {
31
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/promote", {
32
- config,
33
- body: { artifactId, keyOverride },
34
- });
35
- (0, shared_1.recordAgentEvent)(config, {
36
- tool: "codiedev_promote",
37
- ok: true,
38
- latencyMs: Date.now() - start,
39
- });
40
- console.log(`✓ Promoted to authored artifact ${res.key}`);
41
- console.log(` id: ${res.artifactId}`);
42
- }
43
- catch (err) {
44
- (0, shared_1.recordAgentEvent)(config, {
45
- tool: "codiedev_promote",
46
- ok: false,
47
- latencyMs: Date.now() - start,
48
- error: err.message,
49
- });
50
- console.error(`Promote failed: ${err.message}`);
51
- process.exit(1);
52
- }
53
- }
@@ -1,2 +0,0 @@
1
- export declare function candidateKeys(key: string): string[];
2
- export declare function runPull(args: string[]): Promise<void>;
@@ -1,33 +0,0 @@
1
- import { requireConfig } from "./shared";
2
- interface PushResponse {
3
- ok: boolean;
4
- artifactId: string;
5
- key: string;
6
- version: number;
7
- type: string;
8
- kind: "doc" | "skill";
9
- tags: string[];
10
- destination: string;
11
- }
12
- export interface ParsedArgs {
13
- file: string;
14
- type?: string;
15
- kind?: string;
16
- tags: string[];
17
- force: boolean;
18
- as?: string;
19
- private?: boolean;
20
- }
21
- export declare function parseArgs(args: string[]): ParsedArgs;
22
- export declare function pushFile(config: ReturnType<typeof requireConfig>, opts: {
23
- absPath: string;
24
- key: string;
25
- visibility?: "private" | "team";
26
- contentHash?: string;
27
- type?: string;
28
- kind?: string;
29
- tags?: string[];
30
- force?: boolean;
31
- }): Promise<PushResponse>;
32
- export declare function runPush(args: string[]): Promise<void>;
33
- export {};
@@ -1,2 +0,0 @@
1
- import type { CodiedevConfig } from "../utils";
2
- export declare function runReact(args: string[], configOverride?: CodiedevConfig): Promise<void>;
@@ -1,40 +0,0 @@
1
- "use strict";
2
- // `codiedev react <postId> <emoji>` — toggle a reaction on a feed post.
3
- // Calling twice with the same emoji removes it.
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.runReact = runReact;
6
- const shared_1 = require("./shared");
7
- async function runReact(args, configOverride) {
8
- const positionals = args.filter((a) => !a.startsWith("--"));
9
- const [postId, emoji] = positionals;
10
- if (!postId || !emoji) {
11
- console.error("Usage: codiedev react <postId> <emoji>");
12
- process.exit(1);
13
- }
14
- const config = configOverride ?? (0, shared_1.requireConfig)();
15
- const start = Date.now();
16
- try {
17
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/react", {
18
- config,
19
- body: { postId, emoji },
20
- });
21
- (0, shared_1.recordAgentEvent)(config, {
22
- tool: "codiedev_react",
23
- ok: true,
24
- latencyMs: Date.now() - start,
25
- });
26
- console.log(res.action === "removed"
27
- ? `Removed ${res.emoji ?? emoji} from post ${postId}.`
28
- : `Reacted to post ${postId} with ${res.emoji ?? emoji}.`);
29
- }
30
- catch (err) {
31
- (0, shared_1.recordAgentEvent)(config, {
32
- tool: "codiedev_react",
33
- ok: false,
34
- latencyMs: Date.now() - start,
35
- error: err.message,
36
- });
37
- console.error(`React failed: ${err.message}`);
38
- process.exit(1);
39
- }
40
- }
@@ -1,2 +0,0 @@
1
- export declare function isSafeRef(ref: string): boolean;
2
- export declare function runReverseTicket(args: string[]): Promise<void>;
@@ -1,477 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isSafeRef = isSafeRef;
4
- exports.runReverseTicket = runReverseTicket;
5
- const child_process_1 = require("child_process");
6
- const shared_1 = require("./shared");
7
- const prUrl_1 = require("../prUrl");
8
- /**
9
- * Run a binary with an explicit argv, never a shell.
10
- *
11
- * The `gh`/`glab` API paths are built from an owner/repo parsed out of a URL the
12
- * caller pastes in. `parsePrOrMrUrl` only bounds those segments to "no slash",
13
- * so they can still carry `$(…)`, backticks or `;` — which a `shell: true`
14
- * invocation would execute. Passing argv directly means the shell never sees
15
- * them at all.
16
- */
17
- function runArgv(bin, argv, opts = {}) {
18
- const res = (0, child_process_1.spawnSync)(bin, argv, {
19
- stdio: ["pipe", "pipe", "pipe"],
20
- encoding: "utf8",
21
- maxBuffer: opts.maxBuffer ?? 50 * 1024 * 1024,
22
- shell: false,
23
- });
24
- if (res.error)
25
- throw res.error;
26
- if (res.status !== 0) {
27
- throw new Error(`${bin} ${argv.join(" ")} failed (exit ${res.status}): ${(res.stderr || "").trim().slice(0, 500)}`);
28
- }
29
- return res.stdout ?? "";
30
- }
31
- // Git ref names legally contain `$()`, `;`, `|` and backticks, all of which
32
- // break out of the `shell: true` git calls in branch mode below. A ref supplied
33
- // via `--base` is caller-controlled, so bound it to the safe subset before it
34
- // reaches a command line. Mirrors `validate_ref` in modal/runner.py.
35
- const SAFE_REF_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,255}$/;
36
- function isSafeRef(ref) {
37
- if (!ref || ref.startsWith("-"))
38
- return false;
39
- if (ref.includes("..") || ref.includes("@{"))
40
- return false;
41
- if (ref.endsWith("/") || ref.endsWith("."))
42
- return false;
43
- return SAFE_REF_RE.test(ref);
44
- }
45
- async function fetchPrOrMrBundle(parsed, rawUrl, noTruncate) {
46
- console.log(`Fetching ${parsed.provider === "gitlab" ? "MR" : "PR"} ${parsed.owner}/${parsed.repo}#${parsed.number}…`);
47
- if (parsed.provider === "github") {
48
- let prJson;
49
- let filesJson;
50
- let diff;
51
- try {
52
- prJson = ghApi(`/repos/${parsed.owner}/${parsed.repo}/pulls/${parsed.number}`);
53
- filesJson = ghApi(`/repos/${parsed.owner}/${parsed.repo}/pulls/${parsed.number}/files`);
54
- try {
55
- const rawDiff = ghRaw(`/repos/${parsed.owner}/${parsed.repo}/pulls/${parsed.number}`);
56
- diff = noTruncate ? rawDiff : rawDiff.slice(0, 12_000);
57
- }
58
- catch {
59
- // diff is nice-to-have
60
- }
61
- }
62
- catch (err) {
63
- console.error(`Failed to fetch PR via gh api: ${err.message}`);
64
- process.exit(1);
65
- }
66
- return {
67
- repo: `${parsed.owner}/${parsed.repo}`,
68
- number: parsed.number,
69
- title: prJson.title ?? "",
70
- body: prJson.body ?? "",
71
- author: prJson.user?.login ?? "unknown",
72
- authorEmail: prJson.user?.email ?? undefined,
73
- mergedAt: prJson.merged_at
74
- ? Date.parse(prJson.merged_at)
75
- : prJson.closed_at
76
- ? Date.parse(prJson.closed_at)
77
- : Date.now(),
78
- filesChanged: (filesJson ?? []).map((f) => f.filename),
79
- diff,
80
- htmlUrl: prJson.html_url ?? rawUrl,
81
- };
82
- }
83
- // GitLab path. `glab api projects/<encoded-path>/...` returns the same
84
- // MR shape as the REST API. We pull the diff from `merge_requests/<n>/changes`
85
- // and reassemble a unified diff out of each file's `diff` blob.
86
- const projectPath = `${parsed.owner}/${parsed.repo}`;
87
- let mrJson;
88
- let changesJson;
89
- try {
90
- mrJson = glabApi(projectPath, `merge_requests/${parsed.number}`);
91
- changesJson = glabApi(projectPath, `merge_requests/${parsed.number}/changes`);
92
- }
93
- catch (err) {
94
- console.error(`Failed to fetch MR via glab api: ${err.message}`);
95
- process.exit(1);
96
- }
97
- const changes = (changesJson?.changes ?? []);
98
- const filesChanged = changes
99
- .map((c) => c.new_path ?? c.old_path)
100
- .filter((p) => Boolean(p));
101
- const rawDiff = changes
102
- .map((c) => {
103
- const header = `diff --git a/${c.old_path ?? c.new_path} b/${c.new_path ?? c.old_path}`;
104
- return [header, c.diff ?? ""].join("\n");
105
- })
106
- .join("\n");
107
- const diff = noTruncate ? rawDiff : rawDiff.slice(0, 12_000);
108
- return {
109
- repo: projectPath,
110
- number: parsed.number,
111
- title: mrJson.title ?? "",
112
- body: mrJson.description ?? "",
113
- author: mrJson.author?.username ?? "unknown",
114
- authorEmail: undefined,
115
- mergedAt: mrJson.merged_at
116
- ? Date.parse(mrJson.merged_at)
117
- : mrJson.closed_at
118
- ? Date.parse(mrJson.closed_at)
119
- : Date.now(),
120
- filesChanged,
121
- diff,
122
- htmlUrl: mrJson.web_url ?? rawUrl,
123
- };
124
- }
125
- function checkCli(bin) {
126
- try {
127
- runArgv(bin, ["--version"], { maxBuffer: 1024 * 1024 });
128
- return true;
129
- }
130
- catch {
131
- return false;
132
- }
133
- }
134
- function ghApi(path) {
135
- return JSON.parse(runArgv("gh", ["api", path]));
136
- }
137
- function ghRaw(path) {
138
- return runArgv("gh", [
139
- "api",
140
- "-H",
141
- "Accept: application/vnd.github.v3.diff",
142
- path,
143
- ]);
144
- }
145
- // glab api takes a project path (URL-encoded) or numeric id and a sub-path.
146
- // For self-hosted GitLab, glab respects `GITLAB_HOST` env if the user has
147
- // configured glab against their internal instance.
148
- function glabApi(projectPath, subpath) {
149
- const encoded = encodeURIComponent(projectPath);
150
- return JSON.parse(runArgv("glab", ["api", `projects/${encoded}/${subpath}`]));
151
- }
152
- function parseArgs(args) {
153
- let prUrl;
154
- let base;
155
- let forcedKey;
156
- let noTruncate = false;
157
- for (let i = 0; i < args.length; i++) {
158
- const a = args[i];
159
- if ((a === "--with" || a === "-w") && i + 1 < args.length) {
160
- forcedKey = args[++i];
161
- }
162
- else if (a.startsWith("--with=")) {
163
- forcedKey = a.slice("--with=".length);
164
- }
165
- else if ((a === "--base" || a === "-b") && i + 1 < args.length) {
166
- base = args[++i];
167
- }
168
- else if (a.startsWith("--base=")) {
169
- base = a.slice("--base=".length);
170
- }
171
- else if (a === "--full") {
172
- noTruncate = true;
173
- }
174
- else if (!a.startsWith("--") && !prUrl) {
175
- prUrl = a;
176
- }
177
- }
178
- return { prUrl, base, forcedKey, noTruncate };
179
- }
180
- async function runReverseTicket(args) {
181
- const { prUrl, base, forcedKey, noTruncate } = parseArgs(args);
182
- // No PR URL → branch mode (mid-session, no PR exists yet).
183
- if (!prUrl) {
184
- return runBranchMode({ explicitBase: base, forcedKey });
185
- }
186
- const parsed = (0, prUrl_1.parsePrOrMrUrl)(prUrl);
187
- if (!parsed) {
188
- console.error("Couldn't parse PR/MR URL. Expected formats:");
189
- console.error(" https://github.com/<owner>/<repo>/pull/<number>");
190
- console.error(" https://gitlab.com/<group>/<repo>/-/merge_requests/<number>");
191
- console.error(" (self-hosted GitLab works too — same shape, your host)");
192
- console.error("");
193
- console.error("Run with no arguments for branch mode (uses your current local branch).");
194
- process.exit(1);
195
- }
196
- const requiredCli = parsed.provider === "gitlab" ? "glab" : "gh";
197
- if (!checkCli(requiredCli)) {
198
- console.error(`This command needs the \`${requiredCli}\` CLI installed and authenticated.`);
199
- if (requiredCli === "gh") {
200
- console.error(" brew install gh (macOS)");
201
- console.error(" gh auth login");
202
- }
203
- else {
204
- console.error(" brew install glab (macOS)");
205
- console.error(" glab auth login");
206
- }
207
- process.exit(1);
208
- }
209
- const config = (0, shared_1.requireConfig)();
210
- const bundle = await fetchPrOrMrBundle(parsed, prUrl, noTruncate);
211
- console.log(` title: ${bundle.title}`);
212
- console.log(` author: ${bundle.author}`);
213
- console.log(` files: ${bundle.filesChanged.length}`);
214
- console.log(` merged: ${new Date(bundle.mergedAt).toISOString()}`);
215
- console.log("");
216
- console.log("Generating ticket…");
217
- console.log("");
218
- const start = Date.now();
219
- try {
220
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/reverseTicket", {
221
- config,
222
- body: {
223
- pr: bundle,
224
- forcedArtifactKey: forcedKey,
225
- },
226
- });
227
- (0, shared_1.recordAgentEvent)(config, {
228
- tool: "codiedev_reverse_ticket",
229
- ok: true,
230
- latencyMs: Date.now() - start,
231
- });
232
- if (res.match) {
233
- console.log(`✓ Matched artifact: ${res.match.key} (score=${res.match.score.toFixed(2)})`);
234
- console.log(` signals · body=${res.match.signals.bodyMention} · files=${res.match.signals.fileOverlap.toFixed(2)} · transcript=${res.match.signals.transcriptEdit}`);
235
- }
236
- else if (res.candidates.length > 0) {
237
- console.log(`? Ambiguous / low confidence. Candidates:`);
238
- for (const c of res.candidates.slice(0, 3)) {
239
- console.log(` - ${c.key} (score=${c.score.toFixed(2)})`);
240
- }
241
- console.log(` Re-run with --with <key> to force a specific artifact.`);
242
- }
243
- else {
244
- console.log("· No matched artifact. Ticket generated from PR diff alone.");
245
- }
246
- console.log(` prompt tokens (est): ${res.tokensInEstimate}`);
247
- console.log("");
248
- console.log("─────────── Generated ticket ───────────");
249
- console.log("");
250
- console.log(res.ticket);
251
- }
252
- catch (err) {
253
- (0, shared_1.recordAgentEvent)(config, {
254
- tool: "codiedev_reverse_ticket",
255
- ok: false,
256
- latencyMs: Date.now() - start,
257
- error: err.message,
258
- });
259
- console.error(`Reverse-ticket failed: ${err.message}`);
260
- process.exit(1);
261
- }
262
- }
263
- function git(cmd) {
264
- try {
265
- return (0, child_process_1.execSync)(`git ${cmd}`, {
266
- stdio: ["pipe", "pipe", "pipe"],
267
- maxBuffer: 32 * 1024 * 1024,
268
- })
269
- .toString("utf8")
270
- .trim();
271
- }
272
- catch (err) {
273
- throw new Error(`git ${cmd} failed: ${err.message}`);
274
- }
275
- }
276
- function safeGit(fn) {
277
- try {
278
- return fn() || undefined;
279
- }
280
- catch {
281
- return undefined;
282
- }
283
- }
284
- async function runBranchMode(opts) {
285
- // Confirm we're in a git repo.
286
- try {
287
- git("rev-parse --is-inside-work-tree");
288
- }
289
- catch {
290
- console.error("Not inside a git repository. Run this from a checked-out repo.");
291
- process.exit(1);
292
- }
293
- // `--base` lands unquoted on a `git` command line below, so reject anything
294
- // outside the safe-ref subset instead of letting the shell interpret it.
295
- if (opts.explicitBase !== undefined && !isSafeRef(opts.explicitBase)) {
296
- console.error(`Invalid --base ref: ${JSON.stringify(opts.explicitBase)}. Expected a plain branch or tag name.`);
297
- process.exit(1);
298
- }
299
- // Resolve base ref — explicit, then origin/main, then origin/master.
300
- const candidates = opts.explicitBase
301
- ? [opts.explicitBase]
302
- : ["origin/main", "origin/master"];
303
- let baseRef = null;
304
- for (const c of candidates) {
305
- try {
306
- git(`rev-parse --verify ${c}`);
307
- baseRef = c;
308
- break;
309
- }
310
- catch {
311
- // try next
312
- }
313
- }
314
- if (!baseRef) {
315
- console.error(`Couldn't find a base ref (tried ${candidates.join(", ")}). Pass --base <ref>.`);
316
- process.exit(1);
317
- }
318
- const branch = git("rev-parse --abbrev-ref HEAD");
319
- if (branch === "HEAD") {
320
- console.error("Detached HEAD — checkout a branch first.");
321
- process.exit(1);
322
- }
323
- const baseSha = git(`merge-base HEAD ${baseRef}`);
324
- const headSha = git("rev-parse HEAD");
325
- // Filter generated/lockfile noise from the diff body. The --stat (collected
326
- // separately) still includes every file untouched, so excluded files still
327
- // surface in the ticket's "Other files touched" section.
328
- const isNoise = (p) => /(^|\/)(node_modules|dist|build|\.next|\.turbo|coverage|out)\//.test(p) ||
329
- /(^|\/)package-lock\.json$/.test(p) ||
330
- /(^|\/)yarn\.lock$/.test(p) ||
331
- /(^|\/)pnpm-lock\.yaml$/.test(p) ||
332
- /(^|\/)bun\.lock(b)?$/.test(p) ||
333
- /(^|\/)Cargo\.lock$/.test(p) ||
334
- /(^|\/)Gemfile\.lock$/.test(p) ||
335
- /(^|\/)poetry\.lock$/.test(p) ||
336
- /(^|\/)go\.sum$/.test(p);
337
- const shellTolerant = (cmd) => {
338
- try {
339
- return (0, child_process_1.execSync)(`${cmd} || true`, {
340
- stdio: ["pipe", "pipe", "pipe"],
341
- maxBuffer: 32 * 1024 * 1024,
342
- })
343
- .toString("utf8")
344
- .trim();
345
- }
346
- catch {
347
- return "";
348
- }
349
- };
350
- const shQuote = (s) => `'${s.replace(/'/g, "'\\''")}'`;
351
- // Capture all work relative to the base ref: committed + uncommitted-tracked
352
- // + new untracked. Matches "ticket my current changes" intent.
353
- const trackedRaw = git(`diff --name-only ${baseSha}`);
354
- const trackedFiles = trackedRaw ? trackedRaw.split("\n").filter(Boolean) : [];
355
- const untrackedRaw = safeGit(() => git("ls-files --others --exclude-standard"));
356
- const untrackedFiles = untrackedRaw
357
- ? untrackedRaw.split("\n").filter(Boolean)
358
- : [];
359
- const filesChanged = Array.from(new Set([...trackedFiles, ...untrackedFiles]));
360
- if (filesChanged.length === 0) {
361
- console.error(`No changes detected between ${baseRef} and your working tree on ${branch}.`);
362
- process.exit(1);
363
- }
364
- let stat = "";
365
- try {
366
- const trackedStat = git(`diff --stat=200 ${baseSha}`);
367
- const untrackedStatLines = untrackedFiles.map((f) => {
368
- const wc = shellTolerant(`wc -l < ${shQuote(f)}`);
369
- const n = parseInt(wc.split(/\s+/)[0] || "0", 10) || 0;
370
- return ` ${f} | ${n} ++++++++++ (new file)`;
371
- });
372
- stat = [trackedStat, ...untrackedStatLines]
373
- .filter(Boolean)
374
- .join("\n")
375
- .slice(0, 8_000);
376
- }
377
- catch {
378
- // best effort
379
- }
380
- let diff = "";
381
- try {
382
- const sourceTracked = trackedFiles.filter((f) => !isNoise(f));
383
- const trackedDiff = sourceTracked.length > 0
384
- ? git(`diff ${baseSha} -- ${sourceTracked.map(shQuote).join(" ")}`)
385
- : git(`diff ${baseSha}`);
386
- const sourceUntracked = untrackedFiles.filter((f) => !isNoise(f));
387
- const untrackedDiffs = sourceUntracked.map((f) => shellTolerant(`git diff --no-index --no-color -- /dev/null ${shQuote(f)}`));
388
- diff = [trackedDiff, ...untrackedDiffs]
389
- .filter(Boolean)
390
- .join("\n")
391
- .slice(0, 40_000);
392
- }
393
- catch {
394
- // best effort
395
- }
396
- let repo;
397
- try {
398
- const remoteUrl = git("config --get remote.origin.url");
399
- const m = remoteUrl.match(/[:/]([^/:]+)\/([^/:]+?)(?:\.git)?$/);
400
- if (!m) {
401
- throw new Error(`Couldn't parse owner/repo from remote: ${remoteUrl}`);
402
- }
403
- repo = `${m[1]}/${m[2]}`;
404
- }
405
- catch (err) {
406
- console.error(`Couldn't read git remote.origin.url: ${err.message}`);
407
- process.exit(1);
408
- }
409
- const title = safeGit(() => git("log -1 --format=%s")) ?? `WIP: ${branch}`;
410
- const author = safeGit(() => git("config --get user.name"));
411
- const authorEmail = safeGit(() => git("config --get user.email"));
412
- console.log(`Branch: ${branch} (${repo})`);
413
- console.log(`Base: ${baseRef} @ ${baseSha.slice(0, 12)}`);
414
- console.log(`Head: ${headSha.slice(0, 12)}`);
415
- console.log(`Files: ${filesChanged.length}`);
416
- console.log("");
417
- console.log("Generating ticket draft…");
418
- console.log("");
419
- const config = (0, shared_1.requireConfig)();
420
- const start = Date.now();
421
- try {
422
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/reverseTicketFromBranch", {
423
- config,
424
- body: {
425
- branch: {
426
- repo,
427
- branch,
428
- baseSha,
429
- headSha,
430
- title,
431
- author,
432
- authorEmail,
433
- filesChanged,
434
- diff: diff || undefined,
435
- stat: stat || undefined,
436
- },
437
- forcedArtifactKey: opts.forcedKey,
438
- },
439
- });
440
- if (!res.ok || !res.artifactId) {
441
- (0, shared_1.recordAgentEvent)(config, {
442
- tool: "codiedev_reverse_ticket",
443
- ok: false,
444
- latencyMs: Date.now() - start,
445
- error: "no result",
446
- });
447
- console.error("Reverse-ticket generation returned no result. The writer may have skipped due to missing context.");
448
- process.exit(1);
449
- }
450
- (0, shared_1.recordAgentEvent)(config, {
451
- tool: "codiedev_reverse_ticket",
452
- ok: true,
453
- latencyMs: Date.now() - start,
454
- });
455
- const portalHost = (process.env.CODIEDEV_PORTAL_URL || "https://codiedev.com").replace(/\/$/, "");
456
- const portalLink = res.portalUrl.startsWith("http")
457
- ? res.portalUrl
458
- : `${portalHost}${res.portalUrl.startsWith("/") ? "" : "/"}${res.portalUrl}`;
459
- const verb = res.action === "overwritten" ? "Updated" : "Created";
460
- console.log(`✓ ${verb} reverse-ticket draft.`);
461
- console.log(` artifact: ${res.artifactKey ?? res.artifactId}`);
462
- console.log(` portal: ${portalLink}`);
463
- console.log("");
464
- console.log("Open in your portal to review, edit, and push to Jira.");
465
- console.log("Each call creates a fresh draft — clean up duplicates from the portal if needed.");
466
- }
467
- catch (err) {
468
- (0, shared_1.recordAgentEvent)(config, {
469
- tool: "codiedev_reverse_ticket",
470
- ok: false,
471
- latencyMs: Date.now() - start,
472
- error: err.message,
473
- });
474
- console.error(`Reverse-ticket failed: ${err.message}`);
475
- process.exit(1);
476
- }
477
- }
@@ -1,2 +0,0 @@
1
- import type { CodiedevConfig } from "../utils";
2
- export declare function runSearch(args: string[], configOverride?: CodiedevConfig): Promise<void>;
@@ -1 +0,0 @@
1
- export declare function runSecurityCommand(args: string[]): Promise<void>;