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 +34 -1
- package/dist/commands/createTicket.d.ts +27 -0
- package/dist/commands/createTicket.js +192 -0
- package/dist/commands/shared.d.ts +8 -0
- package/dist/commands/shared.js +17 -1
- package/dist/utils.js +6 -0
- package/package.json +1 -1
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(
|
|
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.
|
package/dist/commands/shared.js
CHANGED
|
@@ -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