codiedev 0.7.11 → 0.8.1

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/cli.js CHANGED
@@ -25,6 +25,7 @@ const inbox_1 = require("./commands/inbox");
25
25
  const note_1 = require("./commands/note");
26
26
  const promote_1 = require("./commands/promote");
27
27
  const reverseTicket_1 = require("./commands/reverseTicket");
28
+ const createTicket_1 = require("./commands/createTicket");
28
29
  const doctor_1 = require("./commands/doctor");
29
30
  const search_1 = require("./commands/search");
30
31
  const library_1 = require("./commands/library");
@@ -32,6 +33,7 @@ const post_1 = require("./commands/post");
32
33
  const share_1 = require("./commands/share");
33
34
  const send_1 = require("./commands/send");
34
35
  const react_1 = require("./commands/react");
36
+ const version_1 = require("./version");
35
37
  const HELP = `
36
38
  CodieDev CLI
37
39
 
@@ -62,6 +64,14 @@ Artifacts:
62
64
  Draft lands in /portal/agentic-ticketing.
63
65
  codiedev reverse-ticket --base <ref> Override the base ref in branch mode
64
66
  (defaults to origin/main).
67
+ codiedev create-ticket [<prompt>] Forward-write a scoped ticket plan.
68
+ Claude investigates the codebase, asks
69
+ "single repo or multiple? where?", then
70
+ emits a plan with N+1 split. Lands in
71
+ /portal/research/<id>; push to Jira from there.
72
+ codiedev create-ticket --submit <path|->
73
+ Internal: Claude calls this itself with
74
+ a plan JSON to submit. Use - for stdin.
65
75
 
66
76
  Messaging:
67
77
  codiedev ping <user> "<msg>" [--with <key>]
@@ -240,6 +250,25 @@ codiedev promote — promote an auto-extracted artifact to authored
240
250
 
241
251
  Find artifact-ids in the portal under Knowledge → Memory, or via
242
252
  the portal search.
253
+ `.trim(),
254
+ "create-ticket": `
255
+ codiedev create-ticket — forward-write a scoped ticket plan
256
+
257
+ Default path:
258
+ codiedev create-ticket "I want to add SSO with Google"
259
+
260
+ Bare mode (no --submit) prints skill instructions for Claude to follow.
261
+ Claude asks where your repos live, investigates them locally, and emits a
262
+ plan as JSON. The CLI then re-invokes itself with --submit to upload it.
263
+
264
+ The plan lands at /portal/research/<id> as a ready-state research session.
265
+ Single ticket renders as one ticket; multi-ticket renders as a folder/group.
266
+ Push to Jira from the portal.
267
+
268
+ Examples:
269
+ codiedev create-ticket
270
+ codiedev create-ticket "I want to add Google SSO"
271
+ codiedev create-ticket --submit /tmp/plan.json # internal, called by Claude
243
272
  `.trim(),
244
273
  };
245
274
  const DOCS = `
@@ -379,7 +408,7 @@ async function main() {
379
408
  }
380
409
  }
381
410
  if (command === "version" || command === "--version" || command === "-v") {
382
- console.log("codiedev cli");
411
+ console.log(`codiedev v${version_1.CLI_VERSION}`);
383
412
  return;
384
413
  }
385
414
  try {
@@ -430,6 +459,10 @@ async function main() {
430
459
  case "reverseTicket":
431
460
  await (0, reverseTicket_1.runReverseTicket)(rest);
432
461
  return;
462
+ case "create-ticket":
463
+ case "createTicket":
464
+ await (0, createTicket_1.runCreateTicket)(rest);
465
+ return;
433
466
  case "search":
434
467
  await (0, search_1.runSearch)(rest);
435
468
  return;
@@ -0,0 +1,27 @@
1
+ import type { CodiedevConfig } from "../utils";
2
+ export declare function substitutePrompt(protocol: string, prompt: string | undefined): string;
3
+ interface ConnectedRepo {
4
+ repoId: string;
5
+ fullName: string;
6
+ }
7
+ export declare function validatePlanHasRepo(parsedPlan: any): string | null;
8
+ export declare function resolveRepoIds(fullNames: string[], repos: ConnectedRepo[]): string[];
9
+ export interface CreateTicketArgs {
10
+ mode: "bare" | "submit";
11
+ prompt?: string;
12
+ submitSource?: "stdin" | string;
13
+ }
14
+ export declare function parseCreateTicketArgs(args: string[]): CreateTicketArgs;
15
+ export declare function runCreateTicket(args: string[]): Promise<void>;
16
+ /**
17
+ * Bare-mode entrypoint. Fetches the protocol body from the backend so we can
18
+ * iterate on it without shipping a new CLI, substitutes the user's prompt,
19
+ * and writes the result to stdout for the agent to follow. Hard-errors on
20
+ * fetch failure rather than falling back to a stale local copy — the whole
21
+ * point of moving the protocol server-side is that the freshest version is
22
+ * the only version.
23
+ */
24
+ export declare function runBare(config: CodiedevConfig, prompt: string | undefined): Promise<void>;
25
+ export declare function runSubmit(config: CodiedevConfig, rawJson: string): Promise<void>;
26
+ export declare function readSubmitInput(source: "stdin" | string): string;
27
+ export {};
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.substitutePrompt = substitutePrompt;
37
+ exports.validatePlanHasRepo = validatePlanHasRepo;
38
+ exports.resolveRepoIds = resolveRepoIds;
39
+ exports.parseCreateTicketArgs = parseCreateTicketArgs;
40
+ exports.runCreateTicket = runCreateTicket;
41
+ exports.runBare = runBare;
42
+ exports.runSubmit = runSubmit;
43
+ exports.readSubmitInput = readSubmitInput;
44
+ const fs = __importStar(require("node:fs"));
45
+ const shared_1 = require("./shared");
46
+ const PROMPT_PLACEHOLDER = "{{USER_PROMPT}}";
47
+ const MISSING_PROMPT_FALLBACK = "(not yet provided — ask the user a single combined question that covers both the intent AND repo locations: \"What are you working on, and is this in a single repo or multiple? If multiple, give me the paths.\" Skip Step 1 since you've folded it into this question. Then proceed to Step 2.)";
48
+ function substitutePrompt(protocol, prompt) {
49
+ return protocol.replace(PROMPT_PLACEHOLDER, prompt && prompt.length > 0 ? prompt : MISSING_PROMPT_FALLBACK);
50
+ }
51
+ function validatePlanHasRepo(parsedPlan) {
52
+ const fullNames = Array.isArray(parsedPlan?.repoFullNames) ? parsedPlan.repoFullNames : [];
53
+ const explicitIds = Array.isArray(parsedPlan?.repoIds) ? parsedPlan.repoIds : [];
54
+ if (fullNames.length === 0 && explicitIds.length === 0) {
55
+ return "Plan must include at least one repo (repoFullNames: ['owner/name'] or repoIds: [...])";
56
+ }
57
+ return null;
58
+ }
59
+ function resolveRepoIds(fullNames, repos) {
60
+ const lookup = new Map();
61
+ for (const r of repos) {
62
+ lookup.set(r.fullName.toLowerCase(), r.repoId);
63
+ }
64
+ const ids = [];
65
+ for (const fn of fullNames) {
66
+ const id = lookup.get(fn.toLowerCase());
67
+ if (!id) {
68
+ throw new Error(`Repo "${fn}" is not connected to your CodieDev account. Run \`codiedev connect\` to refresh, or push the missing repo through Settings.`);
69
+ }
70
+ ids.push(id);
71
+ }
72
+ return ids;
73
+ }
74
+ function parseCreateTicketArgs(args) {
75
+ let mode = "bare";
76
+ let prompt;
77
+ let submitSource;
78
+ for (let i = 0; i < args.length; i++) {
79
+ const a = args[i];
80
+ if (a === "--submit" && i + 1 < args.length) {
81
+ mode = "submit";
82
+ const next = args[++i];
83
+ submitSource = next === "-" ? "stdin" : next;
84
+ }
85
+ else if (a.startsWith("--submit=")) {
86
+ mode = "submit";
87
+ const v = a.slice("--submit=".length);
88
+ submitSource = v === "-" ? "stdin" : v;
89
+ }
90
+ else if (!a.startsWith("--") && !prompt && mode === "bare") {
91
+ prompt = a;
92
+ }
93
+ }
94
+ return { mode, prompt, submitSource };
95
+ }
96
+ async function runCreateTicket(args) {
97
+ const parsed = parseCreateTicketArgs(args);
98
+ if (parsed.mode === "bare") {
99
+ const config = (0, shared_1.requireConfig)();
100
+ await runBare(config, parsed.prompt);
101
+ return;
102
+ }
103
+ // submit mode
104
+ if (!parsed.submitSource) {
105
+ console.error("--submit requires a path or '-' for stdin");
106
+ process.exit(1);
107
+ }
108
+ const config = (0, shared_1.requireConfig)();
109
+ let raw;
110
+ try {
111
+ raw = readSubmitInput(parsed.submitSource);
112
+ }
113
+ catch (err) {
114
+ console.error(`Failed to read plan: ${err.message}`);
115
+ process.exit(1);
116
+ }
117
+ // Translate fullNames → IDs before submitting
118
+ let parsedPlan;
119
+ try {
120
+ parsedPlan = JSON.parse(raw);
121
+ }
122
+ catch (err) {
123
+ console.error(`Plan JSON did not parse: ${err.message}`);
124
+ process.exit(1);
125
+ }
126
+ const validationError = validatePlanHasRepo(parsedPlan);
127
+ if (validationError) {
128
+ console.error(validationError);
129
+ process.exit(1);
130
+ }
131
+ const fullNames = Array.isArray(parsedPlan.repoFullNames) ? parsedPlan.repoFullNames : [];
132
+ const explicitIds = Array.isArray(parsedPlan.repoIds) ? parsedPlan.repoIds : [];
133
+ if (fullNames.length > 0 && explicitIds.length === 0) {
134
+ parsedPlan.repoIds = resolveRepoIds(fullNames, config.repos);
135
+ }
136
+ for (const t of parsedPlan.plan?.tickets ?? []) {
137
+ if (t.targetRepoFullName && !t.targetRepoId) {
138
+ const ids = resolveRepoIds([t.targetRepoFullName], config.repos);
139
+ t.targetRepoId = ids[0];
140
+ }
141
+ }
142
+ await runSubmit(config, JSON.stringify(parsedPlan));
143
+ }
144
+ /**
145
+ * Bare-mode entrypoint. Fetches the protocol body from the backend so we can
146
+ * iterate on it without shipping a new CLI, substitutes the user's prompt,
147
+ * and writes the result to stdout for the agent to follow. Hard-errors on
148
+ * fetch failure rather than falling back to a stale local copy — the whole
149
+ * point of moving the protocol server-side is that the freshest version is
150
+ * the only version.
151
+ */
152
+ async function runBare(config, prompt) {
153
+ let res;
154
+ try {
155
+ res = await (0, shared_1.apiRequest)("GET", "/api/cli/createTicketProtocol", { config });
156
+ }
157
+ catch (err) {
158
+ console.error(`Failed to fetch create-ticket protocol: ${err.message}. Check your network connection and re-run.`);
159
+ process.exit(1);
160
+ }
161
+ process.stdout.write(substitutePrompt(res.protocol, prompt));
162
+ }
163
+ async function runSubmit(config, rawJson) {
164
+ let payload;
165
+ try {
166
+ payload = JSON.parse(rawJson);
167
+ }
168
+ catch (err) {
169
+ throw new Error(`Plan JSON did not parse — emit valid JSON matching the schema printed by \`codiedev create-ticket\`. Original error: ${err.message}`);
170
+ }
171
+ if (!payload.plan || !payload.prompt || !payload.repoIds) {
172
+ throw new Error("Plan JSON missing one of: plan, prompt, repoIds");
173
+ }
174
+ const res = await (0, shared_1.withTelemetry)(config, "codiedev_create_ticket", () => (0, shared_1.apiRequest)("POST", "/api/cli/createPlan", {
175
+ config,
176
+ body: payload,
177
+ }));
178
+ const portalHost = (process.env.CODIEDEV_PORTAL_URL || "https://codiedev.com").replace(/\/$/, "");
179
+ const portalLink = res.portalUrl.startsWith("http")
180
+ ? res.portalUrl
181
+ : `${portalHost}${res.portalUrl}`;
182
+ console.log(`✓ Created ${res.ticketCount} ticket(s).`);
183
+ console.log(` portal: ${portalLink}`);
184
+ console.log("");
185
+ console.log("Open in your portal to review and push to Jira.");
186
+ }
187
+ function readSubmitInput(source) {
188
+ if (source === "stdin") {
189
+ return fs.readFileSync(0, "utf8");
190
+ }
191
+ return fs.readFileSync(source, "utf8");
192
+ }
@@ -1,4 +1,12 @@
1
1
  import { CodiedevConfig } from "../utils";
2
+ /**
3
+ * Layer per-command env overrides on top of the on-disk config. Useful for
4
+ * ad-hoc testing — point a prod-connected CLI at a dev Convex deployment
5
+ * without re-running `codiedev connect`. Both CODIEDEV_URL and CODIEDEV_TOKEN
6
+ * must be set together (half-overrides would mismatch URL and auth realm).
7
+ * Other fields (companyId, repos[]) come from the file unchanged.
8
+ */
9
+ export declare function applyConfigEnvOverrides(config: CodiedevConfig): CodiedevConfig;
2
10
  /**
3
11
  * Require a connected CodieDev config. Exits the process with a helpful
4
12
  * message if the user hasn't run `codiedev connect` yet.
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.applyConfigEnvOverrides = applyConfigEnvOverrides;
36
37
  exports.requireConfig = requireConfig;
37
38
  exports.apiRequest = apiRequest;
38
39
  exports.recordAgentEvent = recordAgentEvent;
@@ -43,6 +44,21 @@ const https = __importStar(require("https"));
43
44
  const http = __importStar(require("http"));
44
45
  const utils_1 = require("../utils");
45
46
  const version_1 = require("../version");
47
+ /**
48
+ * Layer per-command env overrides on top of the on-disk config. Useful for
49
+ * ad-hoc testing — point a prod-connected CLI at a dev Convex deployment
50
+ * without re-running `codiedev connect`. Both CODIEDEV_URL and CODIEDEV_TOKEN
51
+ * must be set together (half-overrides would mismatch URL and auth realm).
52
+ * Other fields (companyId, repos[]) come from the file unchanged.
53
+ */
54
+ function applyConfigEnvOverrides(config) {
55
+ const url = process.env.CODIEDEV_URL;
56
+ const token = process.env.CODIEDEV_TOKEN;
57
+ if (url && token) {
58
+ return { ...config, backendUrl: url, token };
59
+ }
60
+ return config;
61
+ }
46
62
  /**
47
63
  * Require a connected CodieDev config. Exits the process with a helpful
48
64
  * message if the user hasn't run `codiedev connect` yet.
@@ -53,7 +69,7 @@ function requireConfig() {
53
69
  console.error("Not connected. Run `npx codiedev connect` first and enter your API token.");
54
70
  process.exit(1);
55
71
  }
56
- return config;
72
+ return applyConfigEnvOverrides(config);
57
73
  }
58
74
  /**
59
75
  * Perform an authenticated HTTP request against the CodieDev backend.
package/dist/utils.js CHANGED
@@ -302,6 +302,7 @@ thought, use the \`codiedev\` CLI via Bash:**
302
302
  | "read that reply" / "mark that ping read" | \`codiedev read <ping-id>\` |
303
303
  | "note that X is a follow-up" / "remember X" | \`codiedev note "<text>"\` |
304
304
  | "promote the extracted spec" | \`codiedev promote <artifact-id>\` |
305
+ | "create a ticket" / "scope this into tickets" / "ticket this for me" | \`codiedev create-ticket [optional-inline-prompt]\` |
305
306
  | "post this to the team" / "publish to the feed" / "share with the team" | \`codiedev post --title "<t>" --body "<b>" [--intent <intent>] [--filename <key>] [--mention <name>]\` |
306
307
  | "share with a teammate" / "give someone access" (no notification) | \`codiedev share <key> <name> [--role read\\|edit]\` |
307
308
  | "send this to a teammate" / "ask someone to look at this" | \`codiedev send <key> <name> ["<msg>"]\` |
@@ -369,6 +370,11 @@ iterate rather than duplicating.
369
370
  notification). \`codiedev send\` both grants access AND pings the
370
371
  recipient. Use send when the user is actively looping someone in.
371
372
 
373
+ **\`create-ticket\` flow:** when invoked without \`--submit\`, the CLI prints
374
+ skill instructions you should follow. Step 1 is asking the user about repos.
375
+ Step 4 calls \`codiedev create-ticket --submit <path>\` with the plan JSON.
376
+ Tell the user the portal URL when the submit returns.
377
+
372
378
  **Errors:**
373
379
  - "not connected" → user needs to run \`npx codiedev connect\` with their
374
380
  API token from https://codiedev.com/portal/integrations/claude-code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codiedev",
3
- "version": "0.7.11",
3
+ "version": "0.8.1",
4
4
  "description": "Connect Claude Code, Codex, Cursor, or VS Code Copilot to CodieDev for org-wide session capture and artifact collaboration",
5
5
  "bin": {
6
6
  "codiedev": "dist/cli.js",