hivenet 0.4.0
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/README.md +21 -0
- package/dist/chunk-K4DIOYVH.js +16262 -0
- package/dist/cli.js +229 -0
- package/dist/owner-LAL5WKKW.js +235 -0
- package/package.json +39 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
FEEDBACK_CATEGORIES,
|
|
4
|
+
FeedbackError,
|
|
5
|
+
MAX_FEEDBACK_LENGTH,
|
|
6
|
+
__toESM,
|
|
7
|
+
authorSchema,
|
|
8
|
+
flushOutbox,
|
|
9
|
+
require_arg,
|
|
10
|
+
sendFeedback,
|
|
11
|
+
sentimentSchema
|
|
12
|
+
} from "./chunk-K4DIOYVH.js";
|
|
13
|
+
|
|
14
|
+
// src/cli.ts
|
|
15
|
+
var import_arg = __toESM(require_arg(), 1);
|
|
16
|
+
var CLI_NAME = "hivenet";
|
|
17
|
+
var CLI_VERSION = "0.4.0";
|
|
18
|
+
async function main() {
|
|
19
|
+
const subcommand = process.argv[2];
|
|
20
|
+
if (subcommand === "inbox" || subcommand === "triage" || subcommand === "issues" || subcommand === "issue") {
|
|
21
|
+
const owner = await import("./owner-LAL5WKKW.js");
|
|
22
|
+
const run = {
|
|
23
|
+
inbox: owner.runInbox,
|
|
24
|
+
triage: owner.runTriage,
|
|
25
|
+
issues: owner.runIssues,
|
|
26
|
+
issue: owner.runIssue
|
|
27
|
+
}[subcommand];
|
|
28
|
+
await run(process.argv.slice(3));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const args = (0, import_arg.default)(
|
|
32
|
+
{
|
|
33
|
+
"--help": Boolean,
|
|
34
|
+
"--version": Boolean,
|
|
35
|
+
"--to": String,
|
|
36
|
+
"--dsn": String,
|
|
37
|
+
"--category": String,
|
|
38
|
+
"--subject": String,
|
|
39
|
+
"--sentiment": String,
|
|
40
|
+
"--author": String,
|
|
41
|
+
"--meta": [String],
|
|
42
|
+
"--resume": String,
|
|
43
|
+
"--json": Boolean,
|
|
44
|
+
"--dry-run": Boolean,
|
|
45
|
+
"--flush": Boolean,
|
|
46
|
+
"-h": "--help",
|
|
47
|
+
"-v": "--version",
|
|
48
|
+
"-t": "--to",
|
|
49
|
+
"-c": "--category",
|
|
50
|
+
"-s": "--subject"
|
|
51
|
+
},
|
|
52
|
+
{ argv: process.argv.slice(2), permissive: false }
|
|
53
|
+
);
|
|
54
|
+
if (args["--help"]) {
|
|
55
|
+
printHelp();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (args["--version"]) {
|
|
59
|
+
console.log(CLI_VERSION);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (args["--flush"]) {
|
|
63
|
+
const result2 = await flushOutbox();
|
|
64
|
+
console.log(`Outbox: delivered ${result2.delivered}, remaining ${result2.remaining}.`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const feedback = args._.join(" ").trim();
|
|
68
|
+
if (!feedback) {
|
|
69
|
+
throw new FeedbackError(
|
|
70
|
+
`A feedback message is required.
|
|
71
|
+
Usage: npx ${CLI_NAME} --to <slug|domain> "<specific, actionable feedback>"`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const sentiment = parseEnumFlag("--sentiment", args["--sentiment"], sentimentSchema);
|
|
75
|
+
const author = parseEnumFlag("--author", args["--author"], authorSchema);
|
|
76
|
+
const metadata = parseMetaFlags(args["--meta"]);
|
|
77
|
+
const options = {
|
|
78
|
+
feedback,
|
|
79
|
+
...args["--to"] !== void 0 ? { to: args["--to"] } : {},
|
|
80
|
+
...args["--dsn"] !== void 0 ? { dsn: args["--dsn"] } : {},
|
|
81
|
+
...args["--category"] !== void 0 ? { category: args["--category"].toLowerCase() } : {},
|
|
82
|
+
...args["--subject"] !== void 0 ? { subject: args["--subject"] } : {},
|
|
83
|
+
...sentiment !== void 0 ? { sentiment } : {},
|
|
84
|
+
...author !== void 0 ? { author } : {},
|
|
85
|
+
...metadata ? { metadata } : {},
|
|
86
|
+
...args["--resume"] !== void 0 ? { resume: args["--resume"] } : {},
|
|
87
|
+
clientName: CLI_NAME,
|
|
88
|
+
clientVersion: CLI_VERSION
|
|
89
|
+
};
|
|
90
|
+
flushOutbox({ timeoutMs: 3e3 }).catch(() => {
|
|
91
|
+
});
|
|
92
|
+
if (args["--dry-run"]) {
|
|
93
|
+
const preview = await buildDryRun(options);
|
|
94
|
+
console.log(JSON.stringify(preview, null, 2));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const result = await sendFeedback(options);
|
|
98
|
+
if (args["--json"]) {
|
|
99
|
+
console.log(
|
|
100
|
+
JSON.stringify(
|
|
101
|
+
result.delivered ? {
|
|
102
|
+
delivered: true,
|
|
103
|
+
...result.destination.to ? { to: result.destination.to } : {},
|
|
104
|
+
...result.response
|
|
105
|
+
} : { delivered: false, queued: result.queued, error: result.error, thread: result.event.thread.id },
|
|
106
|
+
null,
|
|
107
|
+
2
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const toLabel = result.destination.to ?? result.destination.endpoint;
|
|
113
|
+
if (result.delivered) {
|
|
114
|
+
console.log(`\u2713 Feedback sent to ${toLabel} (${result.response.id})`);
|
|
115
|
+
const resume = result.response.resume ?? `npx ${CLI_NAME}${result.destination.to ? ` --to ${result.destination.to}` : ""} --resume ${result.response.thread}`;
|
|
116
|
+
console.log(` Continue this thread: ${resume}`);
|
|
117
|
+
if (result.response.guidance) {
|
|
118
|
+
console.log(` From ${toLabel}: ${result.response.guidance}`);
|
|
119
|
+
}
|
|
120
|
+
} else if (result.queued) {
|
|
121
|
+
console.log(`\u25CB ${toLabel} is unreachable (${result.error}).`);
|
|
122
|
+
console.log(` Your feedback was queued locally and will be sent on the next run.`);
|
|
123
|
+
} else {
|
|
124
|
+
console.log(`\u2717 Could not deliver feedback to ${toLabel}: ${result.error}`);
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function parseEnumFlag(flag, value, schema) {
|
|
129
|
+
if (value === void 0) {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
const parsed = schema.safeParse(value.toLowerCase());
|
|
133
|
+
if (!parsed.success) {
|
|
134
|
+
throw new FeedbackError(`Invalid ${flag} "${value}". Expected: ${schema.options.join(", ")}.`);
|
|
135
|
+
}
|
|
136
|
+
return parsed.data;
|
|
137
|
+
}
|
|
138
|
+
async function buildDryRun(options) {
|
|
139
|
+
let captured;
|
|
140
|
+
const capture = (async (url, init) => {
|
|
141
|
+
if (init?.method !== "POST") {
|
|
142
|
+
return fetch(url, init);
|
|
143
|
+
}
|
|
144
|
+
captured = JSON.parse(String(init.body));
|
|
145
|
+
return new Response(JSON.stringify({ id: "evt_dry_run", thread: "aaaaaaaaaaaa" }), {
|
|
146
|
+
status: 202,
|
|
147
|
+
headers: { "content-type": "application/json" }
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
const result = await sendFeedback({ ...options, fetchImpl: capture, outbox: false });
|
|
151
|
+
return { destination: { endpoint: result.destination.endpoint, to: result.destination.to }, event: captured };
|
|
152
|
+
}
|
|
153
|
+
function parseMetaFlags(pairs) {
|
|
154
|
+
if (!pairs || pairs.length === 0) {
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
157
|
+
const metadata = {};
|
|
158
|
+
for (const pair of pairs) {
|
|
159
|
+
const separator = pair.indexOf("=");
|
|
160
|
+
if (separator < 1) {
|
|
161
|
+
console.error(`--meta expects key=value, got "${pair}".`);
|
|
162
|
+
process.exit(2);
|
|
163
|
+
}
|
|
164
|
+
const key = pair.slice(0, separator);
|
|
165
|
+
const raw = pair.slice(separator + 1);
|
|
166
|
+
metadata[key] = raw === "true" ? true : raw === "false" ? false : raw !== "" && String(Number(raw)) === raw ? Number(raw) : raw;
|
|
167
|
+
}
|
|
168
|
+
return metadata;
|
|
169
|
+
}
|
|
170
|
+
function printHelp() {
|
|
171
|
+
console.log(`
|
|
172
|
+
Usage
|
|
173
|
+
$ npx ${CLI_NAME} --to <slug|domain> [options] "<feedback>"
|
|
174
|
+
$ npx ${CLI_NAME} inbox --help (owners: list + filter your project's feedback)
|
|
175
|
+
$ npx ${CLI_NAME} triage --help (owners: set status/notes/links on an event)
|
|
176
|
+
$ npx ${CLI_NAME} issues --help (owners: clustered issues, regression counts)
|
|
177
|
+
$ npx ${CLI_NAME} issue --help (owners: manage one issue; cascades to events)
|
|
178
|
+
|
|
179
|
+
Send feedback about an agentic system's surfaces (CLI, docs, MCP, skills, API)
|
|
180
|
+
to its maintainers. Works for AI agents and humans; never prompts, never blocks.
|
|
181
|
+
Feedback can be up to ${MAX_FEEDBACK_LENGTH.toLocaleString("en-US")} characters. Only submit specific, actionable reports.
|
|
182
|
+
|
|
183
|
+
Options
|
|
184
|
+
--to, -t <slug|domain> Destination: registry slug (acme) or domain (acme.dev)
|
|
185
|
+
--dsn <url> Explicit DSN: https://hv_pub_KEY@host/slug
|
|
186
|
+
--category, -c <cat> ${FEEDBACK_CATEGORIES.join(" | ")} | x-<vendor>
|
|
187
|
+
--subject, -s <subject> Exact item the feedback is about (see table below)
|
|
188
|
+
--meta <key=value> Vendor-defined enrichment, repeatable (e.g. --meta sdk_version=1.2.3)
|
|
189
|
+
--sentiment <s> positive | negative | neutral
|
|
190
|
+
--author <a> agent | human | hybrid (auto-detected when omitted)
|
|
191
|
+
--resume <threadId> Continue an earlier feedback thread
|
|
192
|
+
--json Machine-readable result
|
|
193
|
+
--dry-run Print the event and destination without sending
|
|
194
|
+
--flush Retry locally queued (offline) feedback, then exit
|
|
195
|
+
--version, -v Version
|
|
196
|
+
--help, -h This help
|
|
197
|
+
|
|
198
|
+
Subject by category
|
|
199
|
+
tool Exact tool name, e.g. acme_deploy
|
|
200
|
+
skill Exact skill name, e.g. eas-simulator
|
|
201
|
+
prompt Prompt or template name
|
|
202
|
+
docs Full documentation URL
|
|
203
|
+
mcp Exact MCP tool or server name
|
|
204
|
+
cli Full command, e.g. acme deploy --json
|
|
205
|
+
api Endpoint or SDK method
|
|
206
|
+
model Model behavior involved
|
|
207
|
+
eval Capability the failed task involves (include --category eval details)
|
|
208
|
+
ux Flow or surface name
|
|
209
|
+
other Concise topic, or leave empty
|
|
210
|
+
|
|
211
|
+
Data collection
|
|
212
|
+
Submissions include best-effort environment context: agent harness + session ID,
|
|
213
|
+
sandbox, CI, OS/arch/node, and vendor-configured project/session metadata.
|
|
214
|
+
Set DO_NOT_TRACK=1 (or HIVENET_NO_TELEMETRY=1) to omit all of it \u2014
|
|
215
|
+
feedback is still delivered anonymously.
|
|
216
|
+
|
|
217
|
+
Destination resolution
|
|
218
|
+
--dsn \u2192 HIVENET_DSN \u2192 nearest feedback.config.json \u2192 --to slug (registry)
|
|
219
|
+
\u2192 --to domain (https://<domain>/.well-known/agent-feedback.json)
|
|
220
|
+
`);
|
|
221
|
+
}
|
|
222
|
+
main().catch((error) => {
|
|
223
|
+
if (error instanceof FeedbackError) {
|
|
224
|
+
console.error(error.message);
|
|
225
|
+
} else {
|
|
226
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
227
|
+
}
|
|
228
|
+
process.exit(1);
|
|
229
|
+
});
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EVENT_STATUSES,
|
|
3
|
+
FeedbackError,
|
|
4
|
+
ISSUE_STATUSES,
|
|
5
|
+
__toESM,
|
|
6
|
+
ownerApi,
|
|
7
|
+
require_arg,
|
|
8
|
+
resolveApiOrigin
|
|
9
|
+
} from "./chunk-K4DIOYVH.js";
|
|
10
|
+
|
|
11
|
+
// src/owner.ts
|
|
12
|
+
var import_arg = __toESM(require_arg(), 1);
|
|
13
|
+
var SECRET_ENV_VAR = "HIVENET_SECRET_KEY";
|
|
14
|
+
var OWNER_FLAGS = {
|
|
15
|
+
"--to": String,
|
|
16
|
+
"--dsn": String,
|
|
17
|
+
"--api": String,
|
|
18
|
+
"--secret": String,
|
|
19
|
+
"--json": Boolean,
|
|
20
|
+
"--help": Boolean,
|
|
21
|
+
"-h": "--help"
|
|
22
|
+
};
|
|
23
|
+
async function buildApi(args) {
|
|
24
|
+
const secretKey = args["--secret"] ?? process.env[SECRET_ENV_VAR];
|
|
25
|
+
if (!secretKey || !secretKey.startsWith("hv_sec_")) {
|
|
26
|
+
throw new FeedbackError(
|
|
27
|
+
`An owner secret key is required (hv_sec_\u2026, from your project's Settings).
|
|
28
|
+
Pass --secret <key> or set ${SECRET_ENV_VAR}.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const apiOrigin = args["--api"] ?? await resolveApiOrigin({
|
|
32
|
+
...args["--to"] !== void 0 ? { to: args["--to"] } : {},
|
|
33
|
+
...args["--dsn"] !== void 0 ? { dsn: args["--dsn"] } : {}
|
|
34
|
+
});
|
|
35
|
+
return ownerApi({ secretKey, apiOrigin });
|
|
36
|
+
}
|
|
37
|
+
function statusIcon(status) {
|
|
38
|
+
return { new: "\u25CF", seen: "\u25D0", in_progress: "\u25D2", resolved: "\u2713", wont_fix: "\u2717", spam: "\u2298" }[status];
|
|
39
|
+
}
|
|
40
|
+
function printEvent(event, verbose = false) {
|
|
41
|
+
const subject = event.subject ? ` \xB7 ${event.subject}` : "";
|
|
42
|
+
console.log(
|
|
43
|
+
`${statusIcon(event.status)} ${event.id} [${event.status}] ${event.category}${subject} (${event.agent_id ?? event.author ?? "unknown"}, ${event.received_at.slice(0, 16).replace("T", " ")})`
|
|
44
|
+
);
|
|
45
|
+
console.log(` ${event.feedback}`);
|
|
46
|
+
if (event.triage_note) {
|
|
47
|
+
console.log(` \u21B3 note: ${event.triage_note}`);
|
|
48
|
+
}
|
|
49
|
+
if (verbose) {
|
|
50
|
+
console.log(` thread ${event.thread_id} \xB7 telemetry ${event.consent_telemetry ? "on" : "off"}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function runInbox(argv) {
|
|
54
|
+
const args = (0, import_arg.default)(
|
|
55
|
+
{ ...OWNER_FLAGS, "--status": String, "--category": String, "--thread": String, "--limit": Number },
|
|
56
|
+
{ argv }
|
|
57
|
+
);
|
|
58
|
+
if (args["--help"]) {
|
|
59
|
+
console.log(`
|
|
60
|
+
Usage
|
|
61
|
+
$ npx hivenet inbox [--status new] [--category mcp] [--thread <id>] [--limit 50]
|
|
62
|
+
|
|
63
|
+
List feedback for your project (owners only). Auth: --secret <hv_sec_\u2026> or
|
|
64
|
+
${SECRET_ENV_VAR}. Destination: --to <slug|domain>, --dsn, or --api <origin>
|
|
65
|
+
(defaults to the hosted API). Statuses: ${EVENT_STATUSES.join(" | ")}. --json for raw output.
|
|
66
|
+
`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const status = args["--status"];
|
|
70
|
+
if (status && !EVENT_STATUSES.includes(status)) {
|
|
71
|
+
throw new FeedbackError(`Invalid --status "${status}". Expected: ${EVENT_STATUSES.join(", ")}.`);
|
|
72
|
+
}
|
|
73
|
+
const api = await buildApi(args);
|
|
74
|
+
const result = await api.listFeedback({
|
|
75
|
+
...status ? { status } : {},
|
|
76
|
+
...args["--category"] ? { category: args["--category"] } : {},
|
|
77
|
+
...args["--thread"] ? { thread: args["--thread"] } : {},
|
|
78
|
+
...args["--limit"] ? { limit: args["--limit"] } : {}
|
|
79
|
+
});
|
|
80
|
+
if (args["--json"]) {
|
|
81
|
+
console.log(JSON.stringify(result, null, 2));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (result.events.length === 0) {
|
|
85
|
+
console.log(`No matching feedback for ${result.project}.`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(`${result.events.length} event(s) for ${result.project}:
|
|
89
|
+
`);
|
|
90
|
+
for (const event of result.events) {
|
|
91
|
+
printEvent(event);
|
|
92
|
+
}
|
|
93
|
+
console.log(`
|
|
94
|
+
Triage: npx hivenet triage <eventId> --status <${EVENT_STATUSES.join("|")}> [--note "\u2026"]`);
|
|
95
|
+
}
|
|
96
|
+
async function runTriage(argv) {
|
|
97
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, "--status": String, "--note": String, "--link": String }, { argv });
|
|
98
|
+
if (args["--help"] || args._.length === 0) {
|
|
99
|
+
console.log(`
|
|
100
|
+
Usage
|
|
101
|
+
$ npx hivenet triage <eventId> [--status <status>] [--note "why"] [--link <url>]
|
|
102
|
+
|
|
103
|
+
Set triage status and/or attach an external link (Slack, Linear, GitHub, PR) to a
|
|
104
|
+
feedback event (owners only). Statuses: ${EVENT_STATUSES.join(" | ")}.
|
|
105
|
+
Auth: --secret <hv_sec_\u2026> or ${SECRET_ENV_VAR}. Destination: --to/--dsn/--api.
|
|
106
|
+
`);
|
|
107
|
+
if (!args["--help"]) {
|
|
108
|
+
throw new FeedbackError("An event ID is required.");
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const status = args["--status"];
|
|
113
|
+
if (status && !EVENT_STATUSES.includes(status)) {
|
|
114
|
+
throw new FeedbackError(`Invalid --status. Expected: ${EVENT_STATUSES.join(", ")}.`);
|
|
115
|
+
}
|
|
116
|
+
if (!status && !args["--link"]) {
|
|
117
|
+
throw new FeedbackError("Provide --status and/or --link.");
|
|
118
|
+
}
|
|
119
|
+
const api = await buildApi(args);
|
|
120
|
+
const id = args._[0];
|
|
121
|
+
if (args["--link"]) {
|
|
122
|
+
await api.addLink(id, args["--link"]);
|
|
123
|
+
}
|
|
124
|
+
const event = status ? (await api.setFeedbackStatus(id, status, args["--note"] !== void 0 ? args["--note"] : void 0)).event : (await api.getFeedback(id)).event;
|
|
125
|
+
if (args["--json"]) {
|
|
126
|
+
console.log(JSON.stringify({ event }, null, 2));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
console.log("Updated:");
|
|
130
|
+
printEvent(event, true);
|
|
131
|
+
printLinks(event.links_json);
|
|
132
|
+
}
|
|
133
|
+
function printLinks(linksJson) {
|
|
134
|
+
try {
|
|
135
|
+
const links = JSON.parse(linksJson ?? "[]");
|
|
136
|
+
for (const link of links) {
|
|
137
|
+
console.log(` \u21C4 ${link.kind}: ${link.url}`);
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function issueIcon(status) {
|
|
143
|
+
return { open: "\u25CF", in_progress: "\u25D2", resolved: "\u2713", wont_fix: "\u2717", spam: "\u2298" }[status];
|
|
144
|
+
}
|
|
145
|
+
function printIssue(issue) {
|
|
146
|
+
const regressed = issue.reopened_count > 0 ? ` \u21BB regressed \xD7${issue.reopened_count}` : "";
|
|
147
|
+
console.log(
|
|
148
|
+
`${issueIcon(issue.status)} ${issue.id} [${issue.status}] ${issue.title} (${issue.event_count} event${issue.event_count === 1 ? "" : "s"}, last ${issue.last_seen.slice(0, 16).replace("T", " ")})${regressed}`
|
|
149
|
+
);
|
|
150
|
+
printLinks(issue.links_json);
|
|
151
|
+
}
|
|
152
|
+
async function runIssues(argv) {
|
|
153
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, "--status": String, "--limit": Number }, { argv });
|
|
154
|
+
if (args["--help"]) {
|
|
155
|
+
console.log(`
|
|
156
|
+
Usage
|
|
157
|
+
$ npx hivenet issues [--status open] [--limit 50]
|
|
158
|
+
|
|
159
|
+
List issues \u2014 feedback clustered by thread and (category, subject) fingerprint.
|
|
160
|
+
Statuses: ${ISSUE_STATUSES.join(" | ")}. Auth: --secret or ${SECRET_ENV_VAR}.
|
|
161
|
+
Manage one: npx hivenet issue <issueId> --status resolved --note "\u2026" --link <url>
|
|
162
|
+
`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const status = args["--status"];
|
|
166
|
+
if (status && !ISSUE_STATUSES.includes(status)) {
|
|
167
|
+
throw new FeedbackError(`Invalid --status. Expected: ${ISSUE_STATUSES.join(", ")}.`);
|
|
168
|
+
}
|
|
169
|
+
const api = await buildApi(args);
|
|
170
|
+
const result = await api.listIssues({
|
|
171
|
+
...status ? { status } : {},
|
|
172
|
+
...args["--limit"] ? { limit: args["--limit"] } : {}
|
|
173
|
+
});
|
|
174
|
+
if (args["--json"]) {
|
|
175
|
+
console.log(JSON.stringify(result, null, 2));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (result.issues.length === 0) {
|
|
179
|
+
console.log(`No matching issues for ${result.project}.`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
console.log(`${result.issues.length} issue(s) for ${result.project}:
|
|
183
|
+
`);
|
|
184
|
+
for (const issue of result.issues) {
|
|
185
|
+
printIssue(issue);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function runIssue(argv) {
|
|
189
|
+
const args = (0, import_arg.default)({ ...OWNER_FLAGS, "--status": String, "--note": String, "--link": String }, { argv });
|
|
190
|
+
if (args["--help"] || args._.length === 0) {
|
|
191
|
+
console.log(`
|
|
192
|
+
Usage
|
|
193
|
+
$ npx hivenet issue <issueId> (show with events)
|
|
194
|
+
$ npx hivenet issue <issueId> --status resolved --note "\u2026" --link <url>
|
|
195
|
+
|
|
196
|
+
Statuses: ${ISSUE_STATUSES.join(" | ")}. Resolving cascades to member events; new
|
|
197
|
+
feedback on a resolved issue reopens it. Auth: --secret or ${SECRET_ENV_VAR}.
|
|
198
|
+
`);
|
|
199
|
+
if (!args["--help"]) {
|
|
200
|
+
throw new FeedbackError("An issue ID is required.");
|
|
201
|
+
}
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const status = args["--status"];
|
|
205
|
+
if (status && !ISSUE_STATUSES.includes(status)) {
|
|
206
|
+
throw new FeedbackError(`Invalid --status. Expected: ${ISSUE_STATUSES.join(", ")}.`);
|
|
207
|
+
}
|
|
208
|
+
const api = await buildApi(args);
|
|
209
|
+
const id = args._[0];
|
|
210
|
+
if (args["--link"]) {
|
|
211
|
+
await api.addLink(id, args["--link"]);
|
|
212
|
+
}
|
|
213
|
+
if (status) {
|
|
214
|
+
await api.setIssueStatus(id, status, args["--note"] !== void 0 ? args["--note"] : void 0);
|
|
215
|
+
}
|
|
216
|
+
const { issue, events } = await api.getIssue(id);
|
|
217
|
+
if (args["--json"]) {
|
|
218
|
+
console.log(JSON.stringify({ issue, events }, null, 2));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
printIssue(issue);
|
|
222
|
+
if (issue.summary) {
|
|
223
|
+
console.log(` ${issue.summary}`);
|
|
224
|
+
}
|
|
225
|
+
console.log("");
|
|
226
|
+
for (const event of events) {
|
|
227
|
+
printEvent(event);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
export {
|
|
231
|
+
runInbox,
|
|
232
|
+
runIssue,
|
|
233
|
+
runIssues,
|
|
234
|
+
runTriage
|
|
235
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hivenet",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Submit feedback about any agentic system's tools, docs, CLI, or MCP server \u2014 from any AI agent or human",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent",
|
|
7
|
+
"feedback",
|
|
8
|
+
"ai",
|
|
9
|
+
"cli",
|
|
10
|
+
"mcp",
|
|
11
|
+
"agent-experience"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"homepage": "https://hivenet.app",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/zvadaadam/hivenet.git",
|
|
18
|
+
"directory": "packages/cli"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"bin": {
|
|
22
|
+
"hivenet": "dist/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup && node -e \"require('node:fs').chmodSync('dist/cli.js', 0o755)\"",
|
|
29
|
+
"typecheck": "tsc --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@hivenet/core": "workspace:*",
|
|
33
|
+
"@hivenet/spec": "workspace:*",
|
|
34
|
+
"arg": "^5.0.2"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=20.10.0"
|
|
38
|
+
}
|
|
39
|
+
}
|