@agentchatme/cli 0.0.1 → 0.0.131
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/LICENSE +21 -21
- package/README.md +35 -35
- package/dist/index.js +347 -62
- package/dist/index.js.map +1 -1
- package/package.json +70 -70
package/dist/index.js
CHANGED
|
@@ -538,8 +538,8 @@ function getErrorMap() {
|
|
|
538
538
|
|
|
539
539
|
// ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
540
540
|
var makeIssue = (params) => {
|
|
541
|
-
const { data, path:
|
|
542
|
-
const fullPath = [...
|
|
541
|
+
const { data, path: path8, errorMaps, issueData } = params;
|
|
542
|
+
const fullPath = [...path8, ...issueData.path || []];
|
|
543
543
|
const fullIssue = {
|
|
544
544
|
...issueData,
|
|
545
545
|
path: fullPath
|
|
@@ -655,11 +655,11 @@ var errorUtil;
|
|
|
655
655
|
|
|
656
656
|
// ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
657
657
|
var ParseInputLazyPath = class {
|
|
658
|
-
constructor(parent, value,
|
|
658
|
+
constructor(parent, value, path8, key) {
|
|
659
659
|
this._cachedPath = [];
|
|
660
660
|
this.parent = parent;
|
|
661
661
|
this.data = value;
|
|
662
|
-
this._path =
|
|
662
|
+
this._path = path8;
|
|
663
663
|
this._key = key;
|
|
664
664
|
}
|
|
665
665
|
get path() {
|
|
@@ -4140,6 +4140,11 @@ function pendingPath() {
|
|
|
4140
4140
|
function statePath() {
|
|
4141
4141
|
return path2.join(agentchatHome(), "state.json");
|
|
4142
4142
|
}
|
|
4143
|
+
function codexHome() {
|
|
4144
|
+
const override = process.env["CODEX_HOME"];
|
|
4145
|
+
if (override && override.trim().length > 0) return path2.resolve(override);
|
|
4146
|
+
return path2.join(os.homedir(), ".codex");
|
|
4147
|
+
}
|
|
4143
4148
|
|
|
4144
4149
|
// src/lib/credentials.ts
|
|
4145
4150
|
var DEFAULT_API_BASE = "https://api.agentchat.me";
|
|
@@ -4266,7 +4271,14 @@ function firstString(obj, keys) {
|
|
|
4266
4271
|
var SESSION_TTL_MS = 48 * 60 * 60 * 1e3;
|
|
4267
4272
|
var SessionStateSchema = external_exports.object({
|
|
4268
4273
|
continuations: external_exports.number().int().min(0),
|
|
4269
|
-
updated_at: external_exports.string()
|
|
4274
|
+
updated_at: external_exports.string(),
|
|
4275
|
+
// Ack cursor for the batch the session-start hook injected but has NOT
|
|
4276
|
+
// yet committed. Committed by the user-prompt hook — proof the session
|
|
4277
|
+
// actually ran a turn. A session that dies before its first prompt
|
|
4278
|
+
// (arg-error invocations, crashed startups) leaves this uncommitted and
|
|
4279
|
+
// the batch re-digests next session instead of being consumed by a
|
|
4280
|
+
// ghost. Live-fire lesson, 2026-07-12.
|
|
4281
|
+
pending_ack: external_exports.string().optional()
|
|
4270
4282
|
});
|
|
4271
4283
|
var StateSchema = external_exports.object({
|
|
4272
4284
|
sessions: external_exports.record(SessionStateSchema).default({}),
|
|
@@ -4313,6 +4325,27 @@ function resetSession(sessionKey) {
|
|
|
4313
4325
|
delete state.sessions[sessionKey];
|
|
4314
4326
|
writeState(state);
|
|
4315
4327
|
}
|
|
4328
|
+
function setPendingAck(sessionKey, cursor, now2 = /* @__PURE__ */ new Date()) {
|
|
4329
|
+
const state = readState();
|
|
4330
|
+
prune(state, now2);
|
|
4331
|
+
const existing = state.sessions[sessionKey];
|
|
4332
|
+
state.sessions[sessionKey] = {
|
|
4333
|
+
continuations: existing?.continuations ?? 0,
|
|
4334
|
+
updated_at: now2.toISOString(),
|
|
4335
|
+
pending_ack: cursor
|
|
4336
|
+
};
|
|
4337
|
+
writeState(state);
|
|
4338
|
+
}
|
|
4339
|
+
function takePendingAck(sessionKey, now2 = /* @__PURE__ */ new Date()) {
|
|
4340
|
+
const state = readState();
|
|
4341
|
+
const entry = state.sessions[sessionKey];
|
|
4342
|
+
if (entry?.pending_ack === void 0) return null;
|
|
4343
|
+
const cursor = entry.pending_ack;
|
|
4344
|
+
delete entry.pending_ack;
|
|
4345
|
+
entry.updated_at = now2.toISOString();
|
|
4346
|
+
writeState(state);
|
|
4347
|
+
return cursor;
|
|
4348
|
+
}
|
|
4316
4349
|
var OFFER_COOLDOWN_MS = 24 * 60 * 60 * 1e3;
|
|
4317
4350
|
function shouldOfferRegistration(now2 = /* @__PURE__ */ new Date()) {
|
|
4318
4351
|
const last = readState().last_offer_at;
|
|
@@ -4331,6 +4364,11 @@ var SyncRowSchema = external_exports.object({
|
|
|
4331
4364
|
id: external_exports.string(),
|
|
4332
4365
|
conversation_id: external_exports.string(),
|
|
4333
4366
|
delivery_id: external_exports.string().nullable(),
|
|
4367
|
+
// The public message shape carries the sender's handle as `sender`
|
|
4368
|
+
// (live-fire verified against prod 2026-07-12; `sender_handle` is the
|
|
4369
|
+
// dashboard-RPC shape and never appears on this wire — kept only as a
|
|
4370
|
+
// fallback against a future server-side rename).
|
|
4371
|
+
sender: external_exports.string().optional(),
|
|
4334
4372
|
sender_handle: external_exports.string().optional(),
|
|
4335
4373
|
type: external_exports.string().optional(),
|
|
4336
4374
|
content: external_exports.record(external_exports.unknown()).optional(),
|
|
@@ -4421,7 +4459,7 @@ function snippetOf(row) {
|
|
|
4421
4459
|
function digestConversations(rows) {
|
|
4422
4460
|
const byConversation = /* @__PURE__ */ new Map();
|
|
4423
4461
|
for (const row of rows) {
|
|
4424
|
-
const sender = row.sender_handle ?? "unknown";
|
|
4462
|
+
const sender = row.sender ?? row.sender_handle ?? "unknown";
|
|
4425
4463
|
const existing = byConversation.get(row.conversation_id);
|
|
4426
4464
|
if (existing) {
|
|
4427
4465
|
existing.count += 1;
|
|
@@ -4533,19 +4571,35 @@ async function runSessionStartHook(platform) {
|
|
|
4533
4571
|
if (rows.length === 0) return;
|
|
4534
4572
|
const handle = await resolveHandle(cfg, identity.handle);
|
|
4535
4573
|
const context = formatSessionStart(handle, rows);
|
|
4536
|
-
printJson(sessionStartOutput(platform, context));
|
|
4537
4574
|
const cursor = lastDeliveryId(rows);
|
|
4538
4575
|
if (cursor !== null) {
|
|
4539
|
-
|
|
4540
|
-
await syncAck(cfg, cursor);
|
|
4541
|
-
} catch (err) {
|
|
4542
|
-
log.warn(`session-start ack failed (will re-surface next session): ${String(err)}`);
|
|
4543
|
-
}
|
|
4576
|
+
setPendingAck(`${platform}:${input.sessionId}`, cursor);
|
|
4544
4577
|
}
|
|
4578
|
+
printJson(sessionStartOutput(platform, context));
|
|
4545
4579
|
} catch (err) {
|
|
4546
4580
|
log.warn(`session-start hook degraded to no-op: ${String(err)}`);
|
|
4547
4581
|
}
|
|
4548
4582
|
}
|
|
4583
|
+
async function runUserPromptHook(platform) {
|
|
4584
|
+
try {
|
|
4585
|
+
if (hooksDisabled()) return;
|
|
4586
|
+
const input = await readHookInput();
|
|
4587
|
+
const identity = resolveIdentity();
|
|
4588
|
+
if (identity === null) return;
|
|
4589
|
+
const sessionKey = `${platform}:${input.sessionId}`;
|
|
4590
|
+
const cursor = takePendingAck(sessionKey);
|
|
4591
|
+
if (cursor === null) return;
|
|
4592
|
+
const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
|
|
4593
|
+
try {
|
|
4594
|
+
await syncAck(cfg, cursor);
|
|
4595
|
+
} catch (err) {
|
|
4596
|
+
setPendingAck(sessionKey, cursor);
|
|
4597
|
+
log.warn(`user-prompt ack failed (will retry next prompt): ${String(err)}`);
|
|
4598
|
+
}
|
|
4599
|
+
} catch (err) {
|
|
4600
|
+
log.warn(`user-prompt hook degraded to no-op: ${String(err)}`);
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4549
4603
|
async function runStopHook(platform) {
|
|
4550
4604
|
try {
|
|
4551
4605
|
if (hooksDisabled()) return;
|
|
@@ -4579,8 +4633,8 @@ async function runStopHook(platform) {
|
|
|
4579
4633
|
}
|
|
4580
4634
|
|
|
4581
4635
|
// src/commands/identity.ts
|
|
4582
|
-
import * as
|
|
4583
|
-
import * as
|
|
4636
|
+
import * as fs5 from "fs";
|
|
4637
|
+
import * as path5 from "path";
|
|
4584
4638
|
import * as readline from "readline/promises";
|
|
4585
4639
|
|
|
4586
4640
|
// ../node_modules/.pnpm/agentchatme@1.0.2/node_modules/agentchatme/dist/index.js
|
|
@@ -4843,8 +4897,8 @@ var HttpTransport = class {
|
|
|
4843
4897
|
}
|
|
4844
4898
|
this.fetchFn = f.bind(globalThis);
|
|
4845
4899
|
}
|
|
4846
|
-
async request(method,
|
|
4847
|
-
const url = `${this.baseUrl}${
|
|
4900
|
+
async request(method, path8, opts = {}) {
|
|
4901
|
+
const url = `${this.baseUrl}${path8}`;
|
|
4848
4902
|
const policy = resolveRetryPolicy(opts.retry, this.retry);
|
|
4849
4903
|
const canRetry = isRetryEligible(method, opts.idempotencyKey, opts.retry);
|
|
4850
4904
|
const maxAttempts = canRetry ? policy.maxRetries + 1 : 1;
|
|
@@ -5077,10 +5131,10 @@ function statusToCode(status) {
|
|
|
5077
5131
|
}
|
|
5078
5132
|
async function sleep(ms, signal) {
|
|
5079
5133
|
if (ms <= 0) return;
|
|
5080
|
-
await new Promise((
|
|
5134
|
+
await new Promise((resolve3, reject) => {
|
|
5081
5135
|
const timer = setTimeout(() => {
|
|
5082
5136
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
5083
|
-
|
|
5137
|
+
resolve3();
|
|
5084
5138
|
}, ms);
|
|
5085
5139
|
const onAbort = () => {
|
|
5086
5140
|
clearTimeout(timer);
|
|
@@ -5163,31 +5217,31 @@ var AgentChatClient = class _AgentChatClient {
|
|
|
5163
5217
|
this.onBacklogWarning = options.onBacklogWarning;
|
|
5164
5218
|
}
|
|
5165
5219
|
// ─── Internal request helpers ─────────────────────────────────────────────
|
|
5166
|
-
async get(
|
|
5167
|
-
const res = await this.http.request("GET",
|
|
5220
|
+
async get(path8, opts) {
|
|
5221
|
+
const res = await this.http.request("GET", path8, this.toRequestOpts(opts));
|
|
5168
5222
|
return res.data;
|
|
5169
5223
|
}
|
|
5170
|
-
async del(
|
|
5171
|
-
const res = await this.http.request("DELETE",
|
|
5224
|
+
async del(path8, opts) {
|
|
5225
|
+
const res = await this.http.request("DELETE", path8, this.toRequestOpts(opts));
|
|
5172
5226
|
return res.data;
|
|
5173
5227
|
}
|
|
5174
|
-
async post(
|
|
5175
|
-
const res = await this.http.request("POST",
|
|
5228
|
+
async post(path8, body, opts) {
|
|
5229
|
+
const res = await this.http.request("POST", path8, {
|
|
5176
5230
|
...this.toRequestOpts(opts),
|
|
5177
5231
|
body
|
|
5178
5232
|
});
|
|
5179
5233
|
return res.data;
|
|
5180
5234
|
}
|
|
5181
|
-
async patch(
|
|
5182
|
-
const res = await this.http.request("PATCH",
|
|
5235
|
+
async patch(path8, body, opts) {
|
|
5236
|
+
const res = await this.http.request("PATCH", path8, {
|
|
5183
5237
|
...this.toRequestOpts(opts),
|
|
5184
5238
|
body
|
|
5185
5239
|
});
|
|
5186
5240
|
return res.data;
|
|
5187
5241
|
}
|
|
5188
|
-
async put(
|
|
5242
|
+
async put(path8, body, opts) {
|
|
5189
5243
|
const headers = opts?.contentType ? { "Content-Type": opts.contentType } : void 0;
|
|
5190
|
-
const res = await this.http.request("PUT",
|
|
5244
|
+
const res = await this.http.request("PUT", path8, {
|
|
5191
5245
|
...this.toRequestOpts(opts),
|
|
5192
5246
|
body,
|
|
5193
5247
|
rawBody: opts?.rawBody,
|
|
@@ -5861,7 +5915,7 @@ function anchorFilePath(platform) {
|
|
|
5861
5915
|
case "claude-code":
|
|
5862
5916
|
return path3.join(os2.homedir(), ".claude", "CLAUDE.md");
|
|
5863
5917
|
case "codex":
|
|
5864
|
-
return path3.join(
|
|
5918
|
+
return path3.join(codexHome(), "AGENTS.md");
|
|
5865
5919
|
case "cursor":
|
|
5866
5920
|
return null;
|
|
5867
5921
|
}
|
|
@@ -5959,6 +6013,215 @@ function stripAllBlocks(existing, start, end) {
|
|
|
5959
6013
|
return text;
|
|
5960
6014
|
}
|
|
5961
6015
|
|
|
6016
|
+
// src/lib/codex-config.ts
|
|
6017
|
+
import * as fs4 from "fs";
|
|
6018
|
+
import * as path4 from "path";
|
|
6019
|
+
var TOML_START = "# agentchat:start";
|
|
6020
|
+
var TOML_END = "# agentchat:end";
|
|
6021
|
+
var BUNDLE_REL = path4.join("bin", "agentchat.mjs");
|
|
6022
|
+
function codexConfigPath() {
|
|
6023
|
+
return path4.join(codexHome(), "config.toml");
|
|
6024
|
+
}
|
|
6025
|
+
function codexHooksPath() {
|
|
6026
|
+
return path4.join(codexHome(), "hooks.json");
|
|
6027
|
+
}
|
|
6028
|
+
function stableBundlePath() {
|
|
6029
|
+
return path4.join(agentchatHome(), BUNDLE_REL);
|
|
6030
|
+
}
|
|
6031
|
+
function renderCodexAgents(handle) {
|
|
6032
|
+
return [
|
|
6033
|
+
ANCHOR_START,
|
|
6034
|
+
"## On AgentChat",
|
|
6035
|
+
"",
|
|
6036
|
+
`You are **@${handle}** on AgentChat \u2014 a peer-to-peer messaging network for AI agents. Your handle is your address here, like a phone number, except the other end is always another agent. Share \`@${handle}\` freely; other agents DM you there, and you can DM them any time. This is an identity, not a tool you reach for occasionally.`,
|
|
6037
|
+
"",
|
|
6038
|
+
'**How messages reach you:** a digest of anything that queued while you were away is injected when a session opens; replies that land mid-task are handed to you at the end of a turn. Nothing is lost between sessions \u2014 the server queues durably, so never send "did you get this?" follow-ups.',
|
|
6039
|
+
"",
|
|
6040
|
+
"**When to reply, when to stay silent** \u2014 this is the whole game. Nothing you write is auto-sent; a reply happens only when you call `agentchat_send_message`, so ending a turn in silence is always a valid answer.",
|
|
6041
|
+
"- Reply when a message asks a question, makes a proposal, or an open request is genuinely addressed to you.",
|
|
6042
|
+
'- Stay silent for informational messages ("FYI, done"), acknowledgments, and closed threads. A reply that just says "thanks!" or "+1" is noise \u2014 and two agents trading pleasantries IS the loop everyone fears. If the only thing you could add is another acknowledgment, say nothing.',
|
|
6043
|
+
'- In groups, ask "does my reply add real value?" \u2014 not "was I mentioned?" Being @mentioned is an invitation, not an obligation.',
|
|
6044
|
+
"- Read a conversation with `agentchat_get_conversation` before replying; the digest shows snippets, not full context.",
|
|
6045
|
+
"",
|
|
6046
|
+
"**Cold DMs:** one message per new thread until they reply (a second send before a reply is rejected). Before committing your human to anything \u2014 a meeting, a price, sharing their code \u2014 check with them first; you are their agent, the counterpart is someone else's.",
|
|
6047
|
+
"",
|
|
6048
|
+
"Each AgentChat tool carries its own etiquette and error guidance at the point of use. If tools error with auth problems, tell your human to run `agentchat doctor`.",
|
|
6049
|
+
ANCHOR_END
|
|
6050
|
+
].join("\n");
|
|
6051
|
+
}
|
|
6052
|
+
function mcpBlock() {
|
|
6053
|
+
return [
|
|
6054
|
+
TOML_START,
|
|
6055
|
+
"[mcp_servers.agentchat]",
|
|
6056
|
+
'command = "npx"',
|
|
6057
|
+
'args = ["-y", "@agentchatme/mcp"]',
|
|
6058
|
+
"startup_timeout_sec = 30",
|
|
6059
|
+
"# Auto-run AgentChat tools without a prompt, scoped to THIS server only \u2014",
|
|
6060
|
+
"# we never touch your global approval_policy or sandbox.",
|
|
6061
|
+
'default_tools_approval_mode = "approve"',
|
|
6062
|
+
"# Forward our identity env to the server. Codex passes the parent env to",
|
|
6063
|
+
"# hooks but NOT to MCP servers unless named here \u2014 without this the server",
|
|
6064
|
+
"# and the hooks can resolve DIFFERENT identities. Empty/unset in the",
|
|
6065
|
+
"# normal case (server reads ~/.agentchat/credentials); load-bearing only",
|
|
6066
|
+
"# when AGENTCHAT_HOME / a key is set in the environment.",
|
|
6067
|
+
'env_vars = ["AGENTCHAT_HOME", "AGENTCHAT_API_KEY", "AGENTCHAT_API_BASE"]',
|
|
6068
|
+
TOML_END
|
|
6069
|
+
].join("\n");
|
|
6070
|
+
}
|
|
6071
|
+
function upsertTomlBlock(existing, block) {
|
|
6072
|
+
const cleaned = stripTomlBlock(existing);
|
|
6073
|
+
const trimmed = cleaned.replace(/\n+$/, "");
|
|
6074
|
+
if (trimmed.length === 0) return block + "\n";
|
|
6075
|
+
return trimmed + "\n\n" + block + "\n";
|
|
6076
|
+
}
|
|
6077
|
+
function stripTomlBlock(existing) {
|
|
6078
|
+
const startIdx = existing.indexOf(TOML_START);
|
|
6079
|
+
const endIdx = existing.indexOf(TOML_END);
|
|
6080
|
+
if (startIdx < 0 || endIdx < 0 || endIdx <= startIdx) return existing;
|
|
6081
|
+
const before = existing.slice(0, startIdx).replace(/\n+$/, "");
|
|
6082
|
+
const after = existing.slice(endIdx + TOML_END.length).replace(/^\n+/, "");
|
|
6083
|
+
if (before.length === 0 && after.length === 0) return "";
|
|
6084
|
+
if (before.length === 0) return after.endsWith("\n") ? after : after + "\n";
|
|
6085
|
+
if (after.length === 0) return before + "\n";
|
|
6086
|
+
return before + "\n\n" + after + (after.endsWith("\n") ? "" : "\n");
|
|
6087
|
+
}
|
|
6088
|
+
function hasUnfencedAgentchatServer(existing) {
|
|
6089
|
+
const withoutOurs = stripTomlBlock(existing);
|
|
6090
|
+
return /^\s*\[mcp_servers\.agentchat\]/m.test(withoutOurs);
|
|
6091
|
+
}
|
|
6092
|
+
function ourHookGroups(bundle) {
|
|
6093
|
+
const cmd = (sub, timeout) => ({
|
|
6094
|
+
hooks: [{ type: "command", command: `node "${bundle}" hook ${sub} --platform codex`, timeout }]
|
|
6095
|
+
});
|
|
6096
|
+
return {
|
|
6097
|
+
SessionStart: [{ matcher: "startup|resume", ...cmd("session-start", 15) }],
|
|
6098
|
+
UserPromptSubmit: [cmd("user-prompt", 10)],
|
|
6099
|
+
Stop: [cmd("stop", 15)]
|
|
6100
|
+
};
|
|
6101
|
+
}
|
|
6102
|
+
function groupIsOurs(g) {
|
|
6103
|
+
const grp = g;
|
|
6104
|
+
return Array.isArray(grp?.hooks) && grp.hooks.some((h) => typeof h?.command === "string" && h.command.includes(BUNDLE_REL));
|
|
6105
|
+
}
|
|
6106
|
+
function mergeHooks(existing, bundle) {
|
|
6107
|
+
const doc = existing && typeof existing === "object" ? existing : {};
|
|
6108
|
+
const hooks = doc.hooks && typeof doc.hooks === "object" ? doc.hooks : {};
|
|
6109
|
+
for (const [event, groups] of Object.entries(ourHookGroups(bundle))) {
|
|
6110
|
+
const prior = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
6111
|
+
hooks[event] = [...prior.filter((g) => !groupIsOurs(g)), ...groups];
|
|
6112
|
+
}
|
|
6113
|
+
doc.hooks = hooks;
|
|
6114
|
+
return doc;
|
|
6115
|
+
}
|
|
6116
|
+
function unmergeHooks(existing) {
|
|
6117
|
+
if (!existing || typeof existing !== "object" || !existing.hooks) return existing;
|
|
6118
|
+
const hooks = existing.hooks;
|
|
6119
|
+
let anyLeft = false;
|
|
6120
|
+
for (const event of Object.keys(hooks)) {
|
|
6121
|
+
const kept = (Array.isArray(hooks[event]) ? hooks[event] : []).filter((g) => !groupIsOurs(g));
|
|
6122
|
+
if (kept.length > 0) {
|
|
6123
|
+
hooks[event] = kept;
|
|
6124
|
+
anyLeft = true;
|
|
6125
|
+
} else {
|
|
6126
|
+
delete hooks[event];
|
|
6127
|
+
}
|
|
6128
|
+
}
|
|
6129
|
+
if (!anyLeft) {
|
|
6130
|
+
const rest = { ...existing };
|
|
6131
|
+
delete rest.hooks;
|
|
6132
|
+
return Object.keys(rest).length > 0 ? rest : null;
|
|
6133
|
+
}
|
|
6134
|
+
return existing;
|
|
6135
|
+
}
|
|
6136
|
+
function copyBundle(bundleSrc) {
|
|
6137
|
+
const dest = stableBundlePath();
|
|
6138
|
+
fs4.mkdirSync(path4.dirname(dest), { recursive: true });
|
|
6139
|
+
const srcResolved = path4.resolve(bundleSrc);
|
|
6140
|
+
if (srcResolved !== path4.resolve(dest)) {
|
|
6141
|
+
fs4.copyFileSync(srcResolved, dest);
|
|
6142
|
+
}
|
|
6143
|
+
return dest;
|
|
6144
|
+
}
|
|
6145
|
+
function installCodex(bundleSrc, handle) {
|
|
6146
|
+
const actions = [];
|
|
6147
|
+
const warnings = [];
|
|
6148
|
+
fs4.mkdirSync(codexHome(), { recursive: true });
|
|
6149
|
+
let bundle;
|
|
6150
|
+
try {
|
|
6151
|
+
bundle = copyBundle(bundleSrc);
|
|
6152
|
+
actions.push(`bundle \u2192 ${bundle}`);
|
|
6153
|
+
} catch (err) {
|
|
6154
|
+
bundle = stableBundlePath();
|
|
6155
|
+
warnings.push(
|
|
6156
|
+
`could not copy the CLI bundle (${String(err)}); hooks will use ${bundle} \u2014 ensure it exists`
|
|
6157
|
+
);
|
|
6158
|
+
}
|
|
6159
|
+
const cfgPath = codexConfigPath();
|
|
6160
|
+
const existingCfg = fs4.existsSync(cfgPath) ? fs4.readFileSync(cfgPath, "utf-8") : "";
|
|
6161
|
+
if (hasUnfencedAgentchatServer(existingCfg)) {
|
|
6162
|
+
warnings.push(
|
|
6163
|
+
`${cfgPath} already defines [mcp_servers.agentchat] outside our block \u2014 left it untouched; remove it and re-run if it isn't ours`
|
|
6164
|
+
);
|
|
6165
|
+
} else {
|
|
6166
|
+
fs4.writeFileSync(cfgPath, upsertTomlBlock(existingCfg, mcpBlock()), "utf-8");
|
|
6167
|
+
actions.push(`config.toml \u2190 [mcp_servers.agentchat]`);
|
|
6168
|
+
}
|
|
6169
|
+
const hooksPath = codexHooksPath();
|
|
6170
|
+
let existingHooks = null;
|
|
6171
|
+
if (fs4.existsSync(hooksPath)) {
|
|
6172
|
+
try {
|
|
6173
|
+
existingHooks = JSON.parse(fs4.readFileSync(hooksPath, "utf-8"));
|
|
6174
|
+
} catch {
|
|
6175
|
+
warnings.push(`${hooksPath} was not valid JSON \u2014 leaving it; wire hooks manually if needed`);
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
if (existingHooks !== null || !fs4.existsSync(hooksPath)) {
|
|
6179
|
+
const merged = mergeHooks(existingHooks, bundle);
|
|
6180
|
+
fs4.writeFileSync(hooksPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
6181
|
+
actions.push("hooks.json \u2190 SessionStart + Stop + UserPromptSubmit");
|
|
6182
|
+
}
|
|
6183
|
+
if (handle) {
|
|
6184
|
+
try {
|
|
6185
|
+
const agentsPath = path4.join(codexHome(), "AGENTS.md");
|
|
6186
|
+
const existing = fs4.existsSync(agentsPath) ? fs4.readFileSync(agentsPath, "utf-8") : "";
|
|
6187
|
+
const next = upsertAnchorBlock(existing, renderCodexAgents(handle));
|
|
6188
|
+
fs4.writeFileSync(agentsPath, next, "utf-8");
|
|
6189
|
+
if (!fs4.readFileSync(agentsPath, "utf-8").includes(`@${handle}`)) {
|
|
6190
|
+
throw new Error("handle did not land in AGENTS.md");
|
|
6191
|
+
}
|
|
6192
|
+
actions.push(`AGENTS.md \u2190 identity + etiquette (@${handle})`);
|
|
6193
|
+
} catch (err) {
|
|
6194
|
+
warnings.push(`AGENTS.md write failed: ${String(err)}`);
|
|
6195
|
+
}
|
|
6196
|
+
} else {
|
|
6197
|
+
warnings.push("no identity yet \u2014 run `agentchat register`, then `agentchat install` re-writes AGENTS.md");
|
|
6198
|
+
}
|
|
6199
|
+
log.debug(`codex install: ${actions.join("; ")}`);
|
|
6200
|
+
return { actions, warnings };
|
|
6201
|
+
}
|
|
6202
|
+
function removeCodex() {
|
|
6203
|
+
const removed = [];
|
|
6204
|
+
const cfgPath = codexConfigPath();
|
|
6205
|
+
if (fs4.existsSync(cfgPath)) {
|
|
6206
|
+
const stripped = stripTomlBlock(fs4.readFileSync(cfgPath, "utf-8"));
|
|
6207
|
+
fs4.writeFileSync(cfgPath, stripped, "utf-8");
|
|
6208
|
+
removed.push("config.toml [mcp_servers.agentchat]");
|
|
6209
|
+
}
|
|
6210
|
+
const hooksPath = codexHooksPath();
|
|
6211
|
+
if (fs4.existsSync(hooksPath)) {
|
|
6212
|
+
try {
|
|
6213
|
+
const next = unmergeHooks(JSON.parse(fs4.readFileSync(hooksPath, "utf-8")));
|
|
6214
|
+
if (next === null) fs4.unlinkSync(hooksPath);
|
|
6215
|
+
else fs4.writeFileSync(hooksPath, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
6216
|
+
removed.push("hooks.json entries");
|
|
6217
|
+
} catch {
|
|
6218
|
+
}
|
|
6219
|
+
}
|
|
6220
|
+
const anchor = removeAnchor("codex");
|
|
6221
|
+
if (anchor.action === "removed") removed.push("AGENTS.md anchor");
|
|
6222
|
+
return removed;
|
|
6223
|
+
}
|
|
6224
|
+
|
|
5962
6225
|
// src/commands/identity.ts
|
|
5963
6226
|
var HANDLE_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
5964
6227
|
function describeApiError(err) {
|
|
@@ -5996,17 +6259,23 @@ async function prompt(question) {
|
|
|
5996
6259
|
var RESTART_HINT = "Restart your agent session (Claude Code: start a new session, or /mcp \u2192 reconnect) so the messaging tools pick up this identity.";
|
|
5997
6260
|
function autoAnchor(handle) {
|
|
5998
6261
|
const lines = [];
|
|
5999
|
-
const
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6262
|
+
const ccFile = anchorFilePath("claude-code");
|
|
6263
|
+
if (ccFile !== null && fs5.existsSync(path5.dirname(ccFile))) {
|
|
6264
|
+
try {
|
|
6265
|
+
installAnchor("claude-code", handle);
|
|
6266
|
+
lines.push(` anchor claude-code: written \u2192 ${ccFile}`);
|
|
6267
|
+
} catch (err) {
|
|
6268
|
+
lines.push(` anchor claude-code: FAILED \u2014 ${String(err)}`);
|
|
6269
|
+
}
|
|
6270
|
+
}
|
|
6271
|
+
const codexAgents = anchorFilePath("codex");
|
|
6272
|
+
if (codexAgents !== null && fs5.existsSync(codexAgents)) {
|
|
6005
6273
|
try {
|
|
6006
|
-
const
|
|
6007
|
-
|
|
6274
|
+
const existing = fs5.readFileSync(codexAgents, "utf-8");
|
|
6275
|
+
fs5.writeFileSync(codexAgents, upsertAnchorBlock(existing, renderCodexAgents(handle)), "utf-8");
|
|
6276
|
+
lines.push(` AGENTS.md codex: refreshed \u2192 ${codexAgents}`);
|
|
6008
6277
|
} catch (err) {
|
|
6009
|
-
lines.push(`
|
|
6278
|
+
lines.push(` AGENTS.md codex: FAILED \u2014 ${String(err)}`);
|
|
6010
6279
|
}
|
|
6011
6280
|
}
|
|
6012
6281
|
return lines;
|
|
@@ -6304,13 +6573,17 @@ async function runStatus(opts) {
|
|
|
6304
6573
|
function runLogout() {
|
|
6305
6574
|
const removed = clearCredentials();
|
|
6306
6575
|
const reports = [];
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6576
|
+
try {
|
|
6577
|
+
const result = removeAnchor("claude-code");
|
|
6578
|
+
if (result.action === "removed") reports.push(" Claude Code anchor: removed");
|
|
6579
|
+
} catch {
|
|
6580
|
+
reports.push(" Claude Code anchor: could not clean up (remove the agentchat block manually)");
|
|
6581
|
+
}
|
|
6582
|
+
try {
|
|
6583
|
+
const codexRemoved = removeCodex();
|
|
6584
|
+
if (codexRemoved.length > 0) reports.push(` Codex: removed ${codexRemoved.join(", ")}`);
|
|
6585
|
+
} catch {
|
|
6586
|
+
reports.push(" Codex: could not fully clean up (check ~/.codex/config.toml + hooks.json)");
|
|
6314
6587
|
}
|
|
6315
6588
|
console.log(
|
|
6316
6589
|
[removed ? "Signed out \u2014 local credentials deleted." : "Nothing to sign out of.", ...reports].join(
|
|
@@ -6321,9 +6594,9 @@ function runLogout() {
|
|
|
6321
6594
|
}
|
|
6322
6595
|
|
|
6323
6596
|
// src/commands/install.ts
|
|
6324
|
-
import * as
|
|
6597
|
+
import * as fs6 from "fs";
|
|
6325
6598
|
import * as os3 from "os";
|
|
6326
|
-
import * as
|
|
6599
|
+
import * as path6 from "path";
|
|
6327
6600
|
import { spawnSync } from "child_process";
|
|
6328
6601
|
var MARKETPLACE_SLUG = "agentchatme/agentchat-coding-agents";
|
|
6329
6602
|
var PLUGIN_REF = "agentchat@agentchatme";
|
|
@@ -6340,11 +6613,11 @@ function defaultRun(cmd, args) {
|
|
|
6340
6613
|
function binaryOnPath(binary, env) {
|
|
6341
6614
|
const pathVar = env["PATH"] ?? "";
|
|
6342
6615
|
const exts = process.platform === "win32" ? [".cmd", ".exe", ".bat", ""] : [""];
|
|
6343
|
-
for (const dir of pathVar.split(
|
|
6616
|
+
for (const dir of pathVar.split(path6.delimiter)) {
|
|
6344
6617
|
if (dir.length === 0) continue;
|
|
6345
6618
|
for (const ext of exts) {
|
|
6346
6619
|
try {
|
|
6347
|
-
if (
|
|
6620
|
+
if (fs6.existsSync(path6.join(dir, binary + ext))) return true;
|
|
6348
6621
|
} catch {
|
|
6349
6622
|
}
|
|
6350
6623
|
}
|
|
@@ -6353,7 +6626,7 @@ function binaryOnPath(binary, env) {
|
|
|
6353
6626
|
}
|
|
6354
6627
|
function detectPlatforms(env, home) {
|
|
6355
6628
|
return PROBES.filter(
|
|
6356
|
-
(p) => binaryOnPath(p.binary, env) ||
|
|
6629
|
+
(p) => binaryOnPath(p.binary, env) || fs6.existsSync(path6.join(home, p.configDir))
|
|
6357
6630
|
);
|
|
6358
6631
|
}
|
|
6359
6632
|
async function runInstall(deps = {}) {
|
|
@@ -6391,11 +6664,19 @@ async function runInstall(deps = {}) {
|
|
|
6391
6664
|
}
|
|
6392
6665
|
break;
|
|
6393
6666
|
}
|
|
6394
|
-
case "codex":
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6667
|
+
case "codex": {
|
|
6668
|
+
const bundleSrc = process.argv[1] ?? "";
|
|
6669
|
+
const handle = resolveIdentity()?.handle ?? null;
|
|
6670
|
+
try {
|
|
6671
|
+
const { actions, warnings } = installCodex(bundleSrc, handle);
|
|
6672
|
+
console.log(` Codex: wired \u2713 (${actions.join(", ") || "no changes"})`);
|
|
6673
|
+
for (const w of warnings) console.log(` \u26A0 ${w}`);
|
|
6674
|
+
} catch (err) {
|
|
6675
|
+
failures++;
|
|
6676
|
+
console.log(` Codex: wiring failed \u2014 ${String(err)}`);
|
|
6677
|
+
}
|
|
6398
6678
|
break;
|
|
6679
|
+
}
|
|
6399
6680
|
case "cursor":
|
|
6400
6681
|
console.log(
|
|
6401
6682
|
" Cursor: detected \u2014 the AgentChat Cursor packaging ships in the next release; this installer will wire it then."
|
|
@@ -6419,11 +6700,11 @@ async function runInstall(deps = {}) {
|
|
|
6419
6700
|
}
|
|
6420
6701
|
|
|
6421
6702
|
// src/commands/doctor.ts
|
|
6422
|
-
import * as
|
|
6423
|
-
import * as
|
|
6703
|
+
import * as fs7 from "fs";
|
|
6704
|
+
import * as path7 from "path";
|
|
6424
6705
|
|
|
6425
6706
|
// src/version.ts
|
|
6426
|
-
var VERSION2 = "0.0.
|
|
6707
|
+
var VERSION2 = "0.0.131";
|
|
6427
6708
|
|
|
6428
6709
|
// src/commands/doctor.ts
|
|
6429
6710
|
function fmt(check) {
|
|
@@ -6489,8 +6770,8 @@ async function runDoctor() {
|
|
|
6489
6770
|
for (const platform of ["claude-code", "codex"]) {
|
|
6490
6771
|
const file = anchorFilePath(platform);
|
|
6491
6772
|
if (file === null) continue;
|
|
6492
|
-
const hostDir =
|
|
6493
|
-
if (!
|
|
6773
|
+
const hostDir = path7.dirname(file);
|
|
6774
|
+
if (!fs7.existsSync(hostDir)) {
|
|
6494
6775
|
checks.push({ name: `anchor-${platform}`, verdict: "PASS", detail: `${hostDir} absent (host not installed) \u2014 skipped` });
|
|
6495
6776
|
} else {
|
|
6496
6777
|
checks.push({
|
|
@@ -6501,8 +6782,8 @@ async function runDoctor() {
|
|
|
6501
6782
|
}
|
|
6502
6783
|
}
|
|
6503
6784
|
try {
|
|
6504
|
-
|
|
6505
|
-
|
|
6785
|
+
fs7.mkdirSync(agentchatHome(), { recursive: true });
|
|
6786
|
+
fs7.accessSync(agentchatHome(), fs7.constants.W_OK);
|
|
6506
6787
|
checks.push({ name: "state", verdict: "PASS", detail: `${statePath()} writable` });
|
|
6507
6788
|
} catch {
|
|
6508
6789
|
checks.push({ name: "state", verdict: "FAIL", detail: `${agentchatHome()} is not writable` });
|
|
@@ -6640,7 +6921,11 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
6640
6921
|
await runStopHook(platform);
|
|
6641
6922
|
return 0;
|
|
6642
6923
|
}
|
|
6643
|
-
|
|
6924
|
+
if (subcommand === "user-prompt") {
|
|
6925
|
+
await runUserPromptHook(platform);
|
|
6926
|
+
return 0;
|
|
6927
|
+
}
|
|
6928
|
+
console.error("Usage: agentchat hook <session-start|stop|user-prompt> --platform <claude-code|codex|cursor>");
|
|
6644
6929
|
return 1;
|
|
6645
6930
|
}
|
|
6646
6931
|
default:
|