codiedev 0.10.0-beta.4 → 0.10.0-beta.5
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 +7 -1
- package/assets/tui-brand-plugin.tsx +14 -2
- package/dist/backup/cache.js +1 -5
- package/dist/backup/match.js +0 -6
- package/dist/backup/run.js +3 -35
- package/dist/captureExclusions.js +0 -12
- package/dist/captureLog.js +0 -28
- package/dist/captureReadiness.js +0 -58
- package/dist/captureSignals.js +66 -3
- package/dist/cli.js +41 -18
- package/dist/codexHookTrust.js +61 -5
- package/dist/codexLimitScan.js +2 -109
- package/dist/codie.js +27 -40
- package/dist/codieBrandInstall.js +0 -18
- package/dist/codieLauncher.js +2 -70
- package/dist/codieRuntime.js +19 -23
- package/dist/codieRuntimeInstall.js +108 -23
- package/dist/codieStartup.js +0 -3
- package/dist/codieUsage.js +38 -41
- package/dist/commands/ask.js +0 -3
- package/dist/commands/capture.js +0 -2
- package/dist/commands/createTicket.js +0 -10
- package/dist/commands/doctor.js +41 -82
- package/dist/commands/inbox.js +0 -2
- package/dist/commands/library.js +0 -3
- package/dist/commands/pack.js +573 -0
- package/dist/commands/ping.js +0 -2
- package/dist/commands/pull.js +6 -10
- package/dist/commands/push.js +2 -12
- package/dist/commands/react.js +0 -2
- package/dist/commands/reverseTicket.js +0 -33
- package/dist/commands/search.js +0 -4
- package/dist/commands/send.js +0 -5
- package/dist/commands/share.js +0 -4
- package/dist/commands/shared.js +0 -35
- package/dist/commands/traces.js +280 -0
- package/dist/configDir.js +0 -10
- package/dist/connect.js +21 -77
- package/dist/connectFlow.js +0 -9
- package/dist/copilotHooks.js +395 -0
- package/dist/copilotInstallation.js +64 -0
- package/dist/cursorHooks.js +40 -17
- package/dist/cursorLifecycle.js +109 -0
- package/dist/detection.js +0 -30
- package/dist/harnessDirs.js +73 -0
- package/dist/hook.js +193 -316
- package/dist/inboxFormat.js +0 -24
- package/dist/launchContext.js +150 -0
- package/dist/launcher.js +140 -0
- package/dist/limitEvent.js +41 -35
- package/dist/limitEventReporter.js +1 -1
- package/dist/mcp.js +3 -77
- package/dist/nativeSteering.js +24 -0
- package/dist/packs/codie.js +182 -0
- package/dist/packs/harnesses.js +374 -0
- package/dist/packs/hookGate.js +256 -0
- package/dist/packs/install.js +285 -0
- package/dist/packs/manifest.js +126 -0
- package/dist/packs/paths.js +81 -0
- package/dist/packs/shim.js +214 -0
- package/dist/packs/store.js +155 -0
- package/dist/packs/sync.js +170 -0
- package/dist/prUrl.js +0 -5
- package/dist/repoResolver.js +0 -19
- package/dist/secret.js +0 -3
- package/dist/security.js +0 -20
- package/dist/securityDeepScan.js +1 -14
- package/dist/securityDoctor.js +0 -2
- package/dist/securityFeature.js +0 -1
- package/dist/securityInstall.js +6 -13
- package/dist/securityInventory.js +23 -13
- package/dist/securityPilot.js +0 -2
- package/dist/securityPolicy.js +0 -7
- package/dist/selfUpdate.js +331 -0
- package/dist/skillMarker.js +23 -0
- package/dist/subagents.js +135 -0
- package/dist/teamSkillContext.js +10 -0
- package/dist/transcriptModels.js +6 -8
- package/dist/upgradeNudge.js +0 -30
- package/dist/utils.js +286 -733
- package/dist/version.js +0 -10
- package/dist/versionCheck.js +0 -33
- package/package.json +7 -6
- package/dist/backup/cache.d.ts +0 -14
- package/dist/backup/match.d.ts +0 -2
- package/dist/backup/run.d.ts +0 -25
- package/dist/captureExclusions.d.ts +0 -30
- package/dist/captureLog.d.ts +0 -20
- package/dist/captureReadiness.d.ts +0 -65
- package/dist/captureSignals.d.ts +0 -19
- package/dist/cli.d.ts +0 -16
- package/dist/codexHookTrust.d.ts +0 -34
- package/dist/codexLimitScan.d.ts +0 -64
- package/dist/codie.d.ts +0 -2
- package/dist/codieBrandInstall.d.ts +0 -11
- package/dist/codieLauncher.d.ts +0 -86
- package/dist/codieRuntime.d.ts +0 -29
- package/dist/codieRuntimeInstall.d.ts +0 -28
- package/dist/codieStartup.d.ts +0 -26
- package/dist/codieUsage.d.ts +0 -57
- package/dist/commands/ask.d.ts +0 -1
- package/dist/commands/capture.d.ts +0 -14
- package/dist/commands/createTicket.d.ts +0 -27
- package/dist/commands/delete.d.ts +0 -1
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/inbox.d.ts +0 -3
- package/dist/commands/library.d.ts +0 -2
- package/dist/commands/note.d.ts +0 -1
- package/dist/commands/ping.d.ts +0 -1
- package/dist/commands/promote.d.ts +0 -1
- package/dist/commands/pull.d.ts +0 -2
- package/dist/commands/push.d.ts +0 -33
- package/dist/commands/react.d.ts +0 -2
- package/dist/commands/reverseTicket.d.ts +0 -2
- package/dist/commands/search.d.ts +0 -2
- package/dist/commands/security.d.ts +0 -1
- package/dist/commands/send.d.ts +0 -2
- package/dist/commands/share.d.ts +0 -2
- package/dist/commands/shared.d.ts +0 -64
- package/dist/configDir.d.ts +0 -1
- package/dist/connect.d.ts +0 -1
- package/dist/connectFlow.d.ts +0 -13
- package/dist/cursorHooks.d.ts +0 -23
- package/dist/detection.d.ts +0 -30
- package/dist/hook.d.ts +0 -6
- package/dist/inboxFormat.d.ts +0 -38
- package/dist/limitEvent.d.ts +0 -16
- package/dist/limitEventReporter.d.ts +0 -21
- package/dist/mcp.d.ts +0 -12
- package/dist/prUrl.d.ts +0 -9
- package/dist/repoResolver.d.ts +0 -17
- package/dist/secret.d.ts +0 -1
- package/dist/security.d.ts +0 -102
- package/dist/securityDeepScan.d.ts +0 -41
- package/dist/securityDoctor.d.ts +0 -32
- package/dist/securityFeature.d.ts +0 -5
- package/dist/securityInstall.d.ts +0 -63
- package/dist/securityInventory.d.ts +0 -54
- package/dist/securityPilot.d.ts +0 -19
- package/dist/securityPolicy.d.ts +0 -33
- package/dist/securitySession.d.ts +0 -6
- package/dist/transcriptModels.d.ts +0 -18
- package/dist/upgradeNudge.d.ts +0 -25
- package/dist/utils.d.ts +0 -346
- package/dist/version.d.ts +0 -1
- package/dist/versionCheck.d.ts +0 -68
package/dist/codieUsage.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// The Codie harness status line.
|
|
3
|
-
//
|
|
4
|
-
// The gateway already knows what a developer has spent, what caps them, and
|
|
5
|
-
// what they have in flight. Nothing ever asked it. This module asks, and turns
|
|
6
|
-
// the answer into one line the harness can paint next to the prompt.
|
|
7
|
-
//
|
|
8
|
-
// Every failure here is silent by design: a status poll must never interrupt,
|
|
9
|
-
// delay, or break a coding session. No snapshot means no line.
|
|
10
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
3
|
exports.formatUsageLine = formatUsageLine;
|
|
4
|
+
exports.usageTone = usageTone;
|
|
12
5
|
exports.formatSessionCostLine = formatSessionCostLine;
|
|
13
6
|
exports.fetchCodieUsage = fetchCodieUsage;
|
|
14
7
|
const USAGE_TIMEOUT_MS = 4000;
|
|
@@ -18,8 +11,6 @@ function cleanBaseUrl(value) {
|
|
|
18
11
|
function money(value) {
|
|
19
12
|
return `$${value.toFixed(2)}`;
|
|
20
13
|
}
|
|
21
|
-
/** Budgets are set in whole dollars far more often than not, so $150 reads
|
|
22
|
-
* better than $150.00 next to a spend figure that does need its cents. */
|
|
23
14
|
function budgetMoney(value) {
|
|
24
15
|
return Number.isInteger(value) ? `$${value}` : money(value);
|
|
25
16
|
}
|
|
@@ -27,16 +18,10 @@ function percent(spent, budget) {
|
|
|
27
18
|
if (!(budget > 0))
|
|
28
19
|
return null;
|
|
29
20
|
const share = (spent / budget) * 100;
|
|
30
|
-
// A first request against a large budget rounds to 0%, which reads as
|
|
31
|
-
// "nothing spent". Say "under one percent" instead.
|
|
32
21
|
if (share > 0 && share < 1)
|
|
33
22
|
return "<1%";
|
|
34
23
|
return `${Math.round(share)}%`;
|
|
35
24
|
}
|
|
36
|
-
/**
|
|
37
|
-
* One line: who is spending, how much, against what, and what is running.
|
|
38
|
-
* Returns "" when there is nothing honest to say.
|
|
39
|
-
*/
|
|
40
25
|
function formatUsageLine(usage) {
|
|
41
26
|
if (!usage || !usage.enabled)
|
|
42
27
|
return "";
|
|
@@ -44,61 +29,78 @@ function formatUsageLine(usage) {
|
|
|
44
29
|
const segments = [];
|
|
45
30
|
if (usage.user?.name)
|
|
46
31
|
segments.push(usage.user.name);
|
|
47
|
-
// The developer's own month-to-date spend is always the headline. Only the
|
|
48
|
-
// budget that actually caps them earns a percentage next to it.
|
|
49
32
|
if (typeof budget.userBudgetUsd === "number") {
|
|
50
33
|
segments.push(`${money(budget.userSpentUsd)} / ${budgetMoney(budget.userBudgetUsd)}`);
|
|
51
34
|
const share = percent(budget.userSpentUsd, budget.userBudgetUsd);
|
|
52
35
|
if (share)
|
|
53
36
|
segments.push(share);
|
|
37
|
+
if (poolBindsFirst(budget) && typeof budget.companyBudgetUsd === "number"
|
|
38
|
+
&& budget.companyBudgetUsd > 0
|
|
39
|
+
&& budget.companySpentUsd / budget.companyBudgetUsd >= 0.75) {
|
|
40
|
+
const share = percent(budget.companySpentUsd, budget.companyBudgetUsd);
|
|
41
|
+
if (share)
|
|
42
|
+
segments.push(`team pool ${share}`);
|
|
43
|
+
}
|
|
54
44
|
}
|
|
55
45
|
else {
|
|
56
46
|
segments.push(money(budget.userSpentUsd));
|
|
57
47
|
if (typeof budget.companyBudgetUsd === "number") {
|
|
58
48
|
const share = percent(budget.companySpentUsd, budget.companyBudgetUsd);
|
|
59
|
-
// Labelled, because a team percentage beside a personal dollar figure
|
|
60
|
-
// otherwise reads as the developer's own share.
|
|
61
49
|
if (share)
|
|
62
50
|
segments.push(`team ${share}`);
|
|
63
51
|
}
|
|
64
52
|
}
|
|
53
|
+
if (typeof budget.companyBudgetUsd === "number" && budget.companyBudgetUsd > 0
|
|
54
|
+
&& budget.companySpentUsd >= budget.companyBudgetUsd) {
|
|
55
|
+
segments.push("team pool used up");
|
|
56
|
+
}
|
|
65
57
|
if (inFlight.requests > 0)
|
|
66
58
|
segments.push(`${inFlight.requests} in flight`);
|
|
67
|
-
// Stale holds cap a budget while doing no work. Naming them is the
|
|
68
|
-
// difference between "I am blocked" and "I am blocked by nothing".
|
|
69
59
|
if (inFlight.staleHeldUsd > 0)
|
|
70
60
|
segments.push(`${money(inFlight.staleHeldUsd)} held stale`);
|
|
71
61
|
return segments.join(" · ");
|
|
72
62
|
}
|
|
63
|
+
function poolBindsFirst(budget) {
|
|
64
|
+
if (typeof budget.companyBudgetUsd !== "number" || typeof budget.userBudgetUsd !== "number")
|
|
65
|
+
return false;
|
|
66
|
+
const poolLeft = Math.max(0, budget.companyBudgetUsd - budget.companySpentUsd);
|
|
67
|
+
const userLeft = Math.max(0, budget.userBudgetUsd - budget.userSpentUsd);
|
|
68
|
+
return budget.userBudgetUsd > 0 && poolLeft < userLeft;
|
|
69
|
+
}
|
|
70
|
+
function usageTone(usage) {
|
|
71
|
+
if (!usage || !usage.enabled)
|
|
72
|
+
return "normal";
|
|
73
|
+
const { budget } = usage;
|
|
74
|
+
const ratios = [];
|
|
75
|
+
if (typeof budget.userBudgetUsd === "number" && budget.userBudgetUsd > 0) {
|
|
76
|
+
ratios.push(budget.userSpentUsd / budget.userBudgetUsd);
|
|
77
|
+
}
|
|
78
|
+
if (typeof budget.companyBudgetUsd === "number" && budget.companyBudgetUsd > 0
|
|
79
|
+
&& (typeof budget.userBudgetUsd !== "number" || poolBindsFirst(budget))) {
|
|
80
|
+
ratios.push(budget.companySpentUsd / budget.companyBudgetUsd);
|
|
81
|
+
}
|
|
82
|
+
const worst = ratios.length ? Math.max(...ratios) : 0;
|
|
83
|
+
if (worst >= 0.9)
|
|
84
|
+
return "critical";
|
|
85
|
+
if (worst >= 0.75)
|
|
86
|
+
return "warn";
|
|
87
|
+
return "normal";
|
|
88
|
+
}
|
|
73
89
|
function tokens(total) {
|
|
74
|
-
// Rounded by arithmetic, not toFixed: 2.55 is not exactly representable, so
|
|
75
|
-
// toFixed(1) rounds it down and the same count can read two ways.
|
|
76
90
|
if (total >= 1_000_000)
|
|
77
91
|
return `${Math.round(total / 100_000) / 10}M`;
|
|
78
92
|
if (total >= 1_000)
|
|
79
93
|
return `${Math.round(total / 1_000)}k`;
|
|
80
94
|
return String(total);
|
|
81
95
|
}
|
|
82
|
-
/**
|
|
83
|
-
* What this session just cost. Returns "" when the session billed nothing,
|
|
84
|
-
* because a developer who spent nothing does not need to be told a number.
|
|
85
|
-
*/
|
|
86
96
|
function formatSessionCostLine(usage) {
|
|
87
97
|
const since = readSince(usage?.since);
|
|
88
98
|
if (!since || since.requests <= 0)
|
|
89
99
|
return "";
|
|
90
|
-
// A real charge that rounds to $0.00 reads as free. Say it is under a cent
|
|
91
|
-
// instead — the month-to-date figure is where it really lands.
|
|
92
100
|
const cost = since.costUsd > 0 && since.costUsd < 0.005 ? "<$0.01" : money(since.costUsd);
|
|
93
101
|
const requests = `${since.requests} ${since.requests === 1 ? "request" : "requests"}`;
|
|
94
102
|
return `This session: ${cost} · ${requests} · ${tokens(since.promptTokens + since.completionTokens)} tokens`;
|
|
95
103
|
}
|
|
96
|
-
/**
|
|
97
|
-
* A usage window is reported only when every field of it is a real number.
|
|
98
|
-
* A partial block is worse than none: it reads as a cheap session, and the
|
|
99
|
-
* arithmetic on a missing field throws out of a call site whose whole
|
|
100
|
-
* contract is that it cannot break the run that just finished.
|
|
101
|
-
*/
|
|
102
104
|
function readSince(value) {
|
|
103
105
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
104
106
|
return undefined;
|
|
@@ -127,11 +129,6 @@ function readUser(value) {
|
|
|
127
129
|
role: typeof role === "string" ? role : "",
|
|
128
130
|
};
|
|
129
131
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Read the developer's budget snapshot. Returns null on any refusal, network
|
|
132
|
-
* failure, timeout, or malformed answer — a status poll is never worth an
|
|
133
|
-
* error in front of a developer who asked for none.
|
|
134
|
-
*/
|
|
135
132
|
async function fetchCodieUsage({ baseUrl, key, sinceMs, fetchImpl = fetch, }) {
|
|
136
133
|
const controller = new AbortController();
|
|
137
134
|
const timeout = setTimeout(() => controller.abort(), USAGE_TIMEOUT_MS);
|
package/dist/commands/ask.js
CHANGED
|
@@ -3,9 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runAsk = runAsk;
|
|
4
4
|
const shared_1 = require("./shared");
|
|
5
5
|
function parseArgs(args) {
|
|
6
|
-
// Everything after the command, joined back into a question string. Allows
|
|
7
|
-
// both quoted and unquoted forms: `codiedev ask "why pg-bouncer?"` or
|
|
8
|
-
// `codiedev ask why did we drop pg-bouncer`.
|
|
9
6
|
const question = args
|
|
10
7
|
.filter((a) => !a.startsWith("--"))
|
|
11
8
|
.join(" ")
|
package/dist/commands/capture.js
CHANGED
|
@@ -123,7 +123,6 @@ async function runCaptureCommand(args, depsOverride = {}) {
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
if (subcommand === "ignored") {
|
|
126
|
-
// `ignored` lists everything; `ignored <path>` answers "is this ignored?".
|
|
127
126
|
const query = args.slice(1).join(" ").trim();
|
|
128
127
|
if (query) {
|
|
129
128
|
reportCheck(query, current, deps);
|
|
@@ -139,7 +138,6 @@ async function runCaptureCommand(args, depsOverride = {}) {
|
|
|
139
138
|
deps.log(` ${value}`);
|
|
140
139
|
return;
|
|
141
140
|
}
|
|
142
|
-
// Back-compat: the earlier `codiedev capture check <path>` form.
|
|
143
141
|
if (subcommand === "check") {
|
|
144
142
|
reportCheck(requirePath(args), current, deps);
|
|
145
143
|
return;
|
|
@@ -100,7 +100,6 @@ async function runCreateTicket(args) {
|
|
|
100
100
|
await runBare(config, parsed.prompt);
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
|
-
// submit mode
|
|
104
103
|
if (!parsed.submitSource) {
|
|
105
104
|
console.error("--submit requires a path or '-' for stdin");
|
|
106
105
|
process.exit(1);
|
|
@@ -114,7 +113,6 @@ async function runCreateTicket(args) {
|
|
|
114
113
|
console.error(`Failed to read plan: ${err.message}`);
|
|
115
114
|
process.exit(1);
|
|
116
115
|
}
|
|
117
|
-
// Translate fullNames → IDs before submitting
|
|
118
116
|
let parsedPlan;
|
|
119
117
|
try {
|
|
120
118
|
parsedPlan = JSON.parse(raw);
|
|
@@ -141,14 +139,6 @@ async function runCreateTicket(args) {
|
|
|
141
139
|
}
|
|
142
140
|
await runSubmit(config, JSON.stringify(parsedPlan));
|
|
143
141
|
}
|
|
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
142
|
async function runBare(config, prompt) {
|
|
153
143
|
let res;
|
|
154
144
|
try {
|
package/dist/commands/doctor.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.configDirInUseCheck = configDirInUseCheck;
|
|
36
37
|
exports.runDoctor = runDoctor;
|
|
37
38
|
const fs = __importStar(require("fs"));
|
|
38
39
|
const path = __importStar(require("path"));
|
|
@@ -47,12 +48,27 @@ const captureReadiness_1 = require("../captureReadiness");
|
|
|
47
48
|
const captureLog_1 = require("../captureLog");
|
|
48
49
|
const securityDoctor_1 = require("../securityDoctor");
|
|
49
50
|
const securityFeature_1 = require("../securityFeature");
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
const
|
|
51
|
+
const copilotInstallation_1 = require("../copilotInstallation");
|
|
52
|
+
const harnessDirs_1 = require("../harnessDirs");
|
|
53
|
+
const CLAUDE_DIR = (0, harnessDirs_1.claudeConfigDir)();
|
|
54
|
+
const CLAUDE_SETTINGS_PATH = path.join(CLAUDE_DIR, "settings.json");
|
|
55
|
+
const CLAUDE_INSTRUCTIONS_PATH = path.join(CLAUDE_DIR, "CLAUDE.md");
|
|
56
|
+
const CODEX_DIR = (0, harnessDirs_1.codexHome)();
|
|
57
|
+
const CODEX_HOOKS_PATH = path.join(CODEX_DIR, "hooks.json");
|
|
58
|
+
const CODEX_INSTRUCTIONS_PATH = path.join(CODEX_DIR, "AGENTS.md");
|
|
54
59
|
const CURSOR_HOOKS_PATH = path.join(os.homedir(), ".cursor", "hooks.json");
|
|
55
60
|
const CURSOR_INSTRUCTIONS_PATH = path.join(os.homedir(), ".cursor", "rules", "codiedev.mdc");
|
|
61
|
+
function configDirInUseCheck(agent, dir, inUse, envVar) {
|
|
62
|
+
return {
|
|
63
|
+
name: `${agent} reads this config dir`,
|
|
64
|
+
status: inUse ? "pass" : "warn",
|
|
65
|
+
detail: inUse
|
|
66
|
+
? (0, harnessDirs_1.displayPath)(dir)
|
|
67
|
+
: `no ${agent} activity in ${(0, harnessDirs_1.displayPath)(dir)} — if you've run ${agent} here, it reads its config from ` +
|
|
68
|
+
`somewhere else, so the hooks below never fire. Set ${envVar} to that dir (or run as the user that runs ` +
|
|
69
|
+
`${agent}) and re-run \`codiedev connect\``,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
56
72
|
function symbol(status) {
|
|
57
73
|
if (status === "pass")
|
|
58
74
|
return "✓";
|
|
@@ -61,17 +77,11 @@ function symbol(status) {
|
|
|
61
77
|
return "✗";
|
|
62
78
|
}
|
|
63
79
|
function codexInstalled() {
|
|
64
|
-
return fs.existsSync(
|
|
80
|
+
return fs.existsSync(CODEX_DIR);
|
|
65
81
|
}
|
|
66
82
|
function cursorInstalled() {
|
|
67
83
|
return fs.existsSync(path.join(os.homedir(), ".cursor"));
|
|
68
84
|
}
|
|
69
|
-
// `isCodiedevHookCommand` lives in ../detection alongside detectClaudeCode
|
|
70
|
-
// so utils.ts and doctor.ts share one definition (they used to drift —
|
|
71
|
-
// each had its own copy).
|
|
72
|
-
// Cursor's hooks.json schema is { hooks: { sessionEnd: [{ command, ... }] } }
|
|
73
|
-
// — flat array of objects with `command`, not the Claude/Codex nested
|
|
74
|
-
// `{ hooks: [{ command }] }` wrapper.
|
|
75
85
|
function hasCursorHook() {
|
|
76
86
|
try {
|
|
77
87
|
if (!fs.existsSync(CURSOR_HOOKS_PATH))
|
|
@@ -140,7 +150,6 @@ async function runDoctor(_args) {
|
|
|
140
150
|
const checks = [];
|
|
141
151
|
console.log("\nCodiedev doctor — checking your setup…\n");
|
|
142
152
|
await (0, upgradeNudge_1.maybePrintUpgradeNudge)();
|
|
143
|
-
// 1. Config file
|
|
144
153
|
const config = (0, utils_1.readConfig)();
|
|
145
154
|
if (!config) {
|
|
146
155
|
checks.push({
|
|
@@ -156,19 +165,12 @@ async function runDoctor(_args) {
|
|
|
156
165
|
status: "pass",
|
|
157
166
|
detail: `connected to ${config.companyName}`,
|
|
158
167
|
});
|
|
159
|
-
// 2. Token still valid + backend reachable (re-validate). The detail names
|
|
160
|
-
// the backend host so users (and their IT) can confirm which domain the CLI
|
|
161
|
-
// talks to — the same host capture uploads use, so this check exercises the
|
|
162
|
-
// exact TLS path a corporate proxy would intercept.
|
|
163
168
|
let backendHost = "the capture host";
|
|
164
169
|
try {
|
|
165
170
|
backendHost = new URL(config.backendUrl).host;
|
|
166
171
|
}
|
|
167
172
|
catch {
|
|
168
|
-
// malformed backendUrl in config — keep the generic placeholder
|
|
169
173
|
}
|
|
170
|
-
// Live tracked-repo list fetched from validateToken below. Null until (and
|
|
171
|
-
// unless) the backend answers; the checks fall back to `config.repos`.
|
|
172
174
|
let liveRepos = null;
|
|
173
175
|
try {
|
|
174
176
|
const res = await fetch(`${config.backendUrl}/api/cli/validateToken`, {
|
|
@@ -185,20 +187,13 @@ async function runDoctor(_args) {
|
|
|
185
187
|
status: "pass",
|
|
186
188
|
detail: backendHost,
|
|
187
189
|
});
|
|
188
|
-
// validateToken returns the company's CURRENT tracked repos. Capture
|
|
189
|
-
// them: the repo-resolution checks below must reflect what the SERVER
|
|
190
|
-
// will resolve at capture time (repoResolver.ts resolves against the
|
|
191
|
-
// live list), not the possibly-stale `config.repos` frozen at the last
|
|
192
|
-
// `connect`. A repo linked in the portal after that connect otherwise
|
|
193
|
-
// reports a false "isn't a tracked repo — link it in the portal" here
|
|
194
|
-
// even though sessions/MRs for it attribute fine server-side.
|
|
195
190
|
try {
|
|
196
191
|
const data = (await res.json());
|
|
197
192
|
if (Array.isArray(data.repos))
|
|
198
193
|
liveRepos = data.repos;
|
|
199
194
|
}
|
|
200
195
|
catch {
|
|
201
|
-
liveRepos = null;
|
|
196
|
+
liveRepos = null;
|
|
202
197
|
}
|
|
203
198
|
}
|
|
204
199
|
else {
|
|
@@ -210,9 +205,6 @@ async function runDoctor(_args) {
|
|
|
210
205
|
}
|
|
211
206
|
}
|
|
212
207
|
catch (err) {
|
|
213
|
-
// fetch buries the real TLS/socket error in .cause ("fetch failed" alone
|
|
214
|
-
// is undiagnosable); unwrap it and, when it's an SSL-inspection signature,
|
|
215
|
-
// say what actually fixes it.
|
|
216
208
|
const desc = (0, captureReadiness_1.describeFetchError)(err);
|
|
217
209
|
const tlsHint = (0, captureReadiness_1.tlsInterceptionHint)(desc, backendHost);
|
|
218
210
|
checks.push({
|
|
@@ -221,7 +213,6 @@ async function runDoctor(_args) {
|
|
|
221
213
|
detail: tlsHint ? `${desc} — ${tlsHint}` : `${desc} (host: ${backendHost})`,
|
|
222
214
|
});
|
|
223
215
|
}
|
|
224
|
-
// 3. Authenticated endpoint smoke test (inbox query)
|
|
225
216
|
try {
|
|
226
217
|
await (0, shared_1.apiRequest)("GET", "/api/cli/inbox", {
|
|
227
218
|
config,
|
|
@@ -239,38 +230,16 @@ async function runDoctor(_args) {
|
|
|
239
230
|
detail: err.message,
|
|
240
231
|
});
|
|
241
232
|
}
|
|
242
|
-
// 4. Tracked repos. Prefer the live list from validateToken over the cache:
|
|
243
|
-
// a repo linked after the last `connect` is tracked server-side but absent
|
|
244
|
-
// from `config.repos`, and resolving against the stale cache is exactly what
|
|
245
|
-
// produced the false "isn't a tracked repo — link it in the portal" warning.
|
|
246
|
-
// Treat an EMPTY live list as "no usable live data", NOT an authoritative
|
|
247
|
-
// zero: a transient permission glitch / backend hiccup can return `repos: []`,
|
|
248
|
-
// and both driving the checks from it AND healing the cache to empty would be
|
|
249
|
-
// wrong — the write would durably break every offline doctor run (and the
|
|
250
|
-
// capture hook's providedRepoId) until the next successful connect. So when
|
|
251
|
-
// live is empty, fall back to the cache and skip the write, exactly like
|
|
252
|
-
// backend-unreachable. A genuinely repo-less company also has an empty cache,
|
|
253
|
-
// so it still reports "0 repos" correctly.
|
|
254
233
|
const trackedRepos = liveRepos !== null && liveRepos.length > 0 ? liveRepos : (config.repos ?? []);
|
|
255
|
-
// Detect drift by CONTENT, not count: a same-size swap (one repo removed +
|
|
256
|
-
// another added between connects) or a repo whose path/URL changed while its
|
|
257
|
-
// id stayed stable (GitLab move/rename — exactly the SONIFI migration case)
|
|
258
|
-
// both leave the count unchanged. A length-only check would skip the heal and
|
|
259
|
-
// leave the capture hook's cache — and its advisory providedRepoId — stale.
|
|
260
234
|
const canonRepos = (rs) => JSON.stringify([...rs].map((r) => [r.repoId, r.url, r.fullName]).sort());
|
|
261
235
|
const cacheRefreshed = liveRepos !== null &&
|
|
262
236
|
liveRepos.length > 0 &&
|
|
263
237
|
canonRepos(liveRepos) !== canonRepos(config.repos ?? []);
|
|
264
|
-
// Heal the on-disk cache so the capture hook (which reads `config.repos` for
|
|
265
|
-
// the advisory providedRepoId) stops trailing the portal too. Best-effort:
|
|
266
|
-
// doctor must never fail because the config couldn't be rewritten.
|
|
267
238
|
if (cacheRefreshed && liveRepos) {
|
|
268
239
|
try {
|
|
269
240
|
(0, utils_1.writeConfig)({ ...config, repos: liveRepos, lastSynced: new Date().toISOString() });
|
|
270
241
|
}
|
|
271
242
|
catch {
|
|
272
|
-
// read-only home / permission issue — the in-memory list still drives
|
|
273
|
-
// the checks below; the user just re-syncs on the next `connect`.
|
|
274
243
|
}
|
|
275
244
|
}
|
|
276
245
|
const repoCount = trackedRepos.length;
|
|
@@ -281,11 +250,6 @@ async function runDoctor(_args) {
|
|
|
281
250
|
? `${repoCount} repo${repoCount === 1 ? "" : "s"}${cacheRefreshed ? " (refreshed from portal)" : ""}`
|
|
282
251
|
: "0 — link repos in the portal so sessions get captured",
|
|
283
252
|
});
|
|
284
|
-
// 4b. Capture readiness — the part that actually decides whether a session
|
|
285
|
-
// lands. doctor used to verify only that the hook was *installed*; these
|
|
286
|
-
// checks answer "from this directory, would a session capture, and did my
|
|
287
|
-
// last one?" — the blind spot behind lean-container / submodule / no-remote
|
|
288
|
-
// zeros.
|
|
289
253
|
const cwd = process.cwd();
|
|
290
254
|
const readiness = (0, captureReadiness_1.assessCaptureReadiness)(cwd, trackedRepos, {
|
|
291
255
|
exec: (cmd, dir) => (0, child_process_1.execSync)(cmd, { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }).toString(),
|
|
@@ -294,11 +258,6 @@ async function runDoctor(_args) {
|
|
|
294
258
|
getBranch: utils_1.getGitBranch,
|
|
295
259
|
getHeadSha: utils_1.getGitHeadSha,
|
|
296
260
|
});
|
|
297
|
-
// Workspace-root mode: the cwd isn't a repo itself, but git repos sit
|
|
298
|
-
// directly under it (one `.devcontainer/` at a parent folder of several
|
|
299
|
-
// cloned repos). Capture tags the sub-repo a session edits, so report that
|
|
300
|
-
// instead of flatly warning "not a work tree / no remote" — the exact output
|
|
301
|
-
// that made working workspace-root setups read as broken.
|
|
302
261
|
const workspaceScan = readiness.gitOnPath && !readiness.insideWorkTree
|
|
303
262
|
? (0, captureReadiness_1.assessWorkspaceRepos)(cwd, trackedRepos, {
|
|
304
263
|
listChildDirs: (dir) => fs
|
|
@@ -349,9 +308,6 @@ async function runDoctor(_args) {
|
|
|
349
308
|
? "branch/sha resolve from the sub-repo each session writes to"
|
|
350
309
|
: "no branch/sha resolved — session→merge attribution will be weaker",
|
|
351
310
|
});
|
|
352
|
-
// 4c. The payoff: did the last session actually land? Ask the server for
|
|
353
|
-
// portal truth (does the row exist for this user?), falling back to the local
|
|
354
|
-
// 2xx breadcrumb if the endpoint is unreachable or predates this CLI/backend.
|
|
355
311
|
const localMarker = (0, utils_1.readLastCapture)();
|
|
356
312
|
let server = null;
|
|
357
313
|
try {
|
|
@@ -372,7 +328,7 @@ async function runDoctor(_args) {
|
|
|
372
328
|
server = (await res.json());
|
|
373
329
|
}
|
|
374
330
|
catch {
|
|
375
|
-
server = null;
|
|
331
|
+
server = null;
|
|
376
332
|
}
|
|
377
333
|
const lastCapture = (0, captureReadiness_1.summarizeServerCapture)(server, localMarker, Date.now());
|
|
378
334
|
checks.push({
|
|
@@ -380,7 +336,6 @@ async function runDoctor(_args) {
|
|
|
380
336
|
status: lastCapture.status,
|
|
381
337
|
detail: lastCapture.detail,
|
|
382
338
|
});
|
|
383
|
-
// 4d. Capture-log activity: did recent uploads succeed? Are there pending jobs?
|
|
384
339
|
const logTail = (0, captureLog_1.readCaptureLogTail)(40);
|
|
385
340
|
let pendingJobCount = 0;
|
|
386
341
|
try {
|
|
@@ -397,25 +352,25 @@ async function runDoctor(_args) {
|
|
|
397
352
|
status: captureActivity.status,
|
|
398
353
|
detail: captureActivity.detail,
|
|
399
354
|
});
|
|
400
|
-
// 5. Claude Code setup (if present)
|
|
401
355
|
if ((0, utils_1.claudeCodeInstalled)()) {
|
|
402
356
|
checks.push({
|
|
403
357
|
name: "Claude Code detected",
|
|
404
358
|
status: "pass",
|
|
405
|
-
detail:
|
|
359
|
+
detail: (0, harnessDirs_1.displayPath)(CLAUDE_DIR),
|
|
406
360
|
});
|
|
361
|
+
checks.push(configDirInUseCheck("Claude Code", CLAUDE_DIR, (0, harnessDirs_1.claudeConfigDirInUse)(CLAUDE_DIR), "CLAUDE_CONFIG_DIR"));
|
|
407
362
|
checks.push({
|
|
408
363
|
name: "Claude Code SessionEnd hook",
|
|
409
364
|
status: hasCodiedevHook(CLAUDE_SETTINGS_PATH, "SessionEnd") ? "pass" : "fail",
|
|
410
365
|
detail: hasCodiedevHook(CLAUDE_SETTINGS_PATH, "SessionEnd")
|
|
411
|
-
?
|
|
366
|
+
? (0, harnessDirs_1.displayPath)(CLAUDE_SETTINGS_PATH)
|
|
412
367
|
: "missing — re-run `codiedev connect`",
|
|
413
368
|
});
|
|
414
369
|
checks.push({
|
|
415
370
|
name: "Claude Code agent instructions",
|
|
416
371
|
status: hasCodiedevInstructions(CLAUDE_INSTRUCTIONS_PATH) ? "pass" : "fail",
|
|
417
372
|
detail: hasCodiedevInstructions(CLAUDE_INSTRUCTIONS_PATH)
|
|
418
|
-
?
|
|
373
|
+
? (0, harnessDirs_1.displayPath)(CLAUDE_INSTRUCTIONS_PATH)
|
|
419
374
|
: "missing — re-run `codiedev connect`",
|
|
420
375
|
});
|
|
421
376
|
}
|
|
@@ -426,25 +381,25 @@ async function runDoctor(_args) {
|
|
|
426
381
|
detail: "not installed on this machine",
|
|
427
382
|
});
|
|
428
383
|
}
|
|
429
|
-
// 6. Codex setup (if present)
|
|
430
384
|
if (codexInstalled()) {
|
|
431
385
|
checks.push({
|
|
432
386
|
name: "Codex detected",
|
|
433
387
|
status: "pass",
|
|
434
|
-
detail:
|
|
388
|
+
detail: (0, harnessDirs_1.displayPath)(CODEX_DIR),
|
|
435
389
|
});
|
|
390
|
+
checks.push(configDirInUseCheck("Codex", CODEX_DIR, (0, harnessDirs_1.codexHomeInUse)(CODEX_DIR), "CODEX_HOME"));
|
|
436
391
|
checks.push({
|
|
437
392
|
name: "Codex Stop hook",
|
|
438
393
|
status: hasCodiedevHook(CODEX_HOOKS_PATH, "Stop") ? "pass" : "fail",
|
|
439
394
|
detail: hasCodiedevHook(CODEX_HOOKS_PATH, "Stop")
|
|
440
|
-
?
|
|
395
|
+
? (0, harnessDirs_1.displayPath)(CODEX_HOOKS_PATH)
|
|
441
396
|
: "missing — re-run `codiedev connect`",
|
|
442
397
|
});
|
|
443
398
|
checks.push({
|
|
444
399
|
name: "Codex agent instructions",
|
|
445
400
|
status: hasCodiedevInstructions(CODEX_INSTRUCTIONS_PATH) ? "pass" : "fail",
|
|
446
401
|
detail: hasCodiedevInstructions(CODEX_INSTRUCTIONS_PATH)
|
|
447
|
-
?
|
|
402
|
+
? (0, harnessDirs_1.displayPath)(CODEX_INSTRUCTIONS_PATH)
|
|
448
403
|
: "missing — re-run `codiedev connect`",
|
|
449
404
|
});
|
|
450
405
|
}
|
|
@@ -455,7 +410,6 @@ async function runDoctor(_args) {
|
|
|
455
410
|
detail: "not installed on this machine",
|
|
456
411
|
});
|
|
457
412
|
}
|
|
458
|
-
// 7. Cursor setup (if present)
|
|
459
413
|
if (cursorInstalled()) {
|
|
460
414
|
checks.push({
|
|
461
415
|
name: "Cursor detected",
|
|
@@ -484,14 +438,19 @@ async function runDoctor(_args) {
|
|
|
484
438
|
detail: "not installed on this machine",
|
|
485
439
|
});
|
|
486
440
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
441
|
+
if ((0, utils_1.copilotInstalled)()) {
|
|
442
|
+
const hooks = (0, copilotInstallation_1.inspectCopilotHooks)();
|
|
443
|
+
checks.push({ name: "GitHub Copilot capture hooks", status: hooks.status === "pass" ? "pass" : "fail", detail: hooks.detail });
|
|
444
|
+
checks.push({ name: "GitHub Copilot CLI instructions",
|
|
445
|
+
status: hasCodiedevInstructions((0, utils_1.copilotInstructionsPath)()) ? "pass" : "fail",
|
|
446
|
+
detail: hasCodiedevInstructions((0, utils_1.copilotInstructionsPath)()) ? (0, utils_1.copilotInstructionsPath)() : "missing — re-run `codiedev connect`" });
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
checks.push({ name: "GitHub Copilot", status: "warn", detail: "not installed on this machine" });
|
|
450
|
+
}
|
|
490
451
|
if ((0, securityFeature_1.isAgentSecurityEnabled)(config)) {
|
|
491
452
|
checks.push(...(0, securityDoctor_1.securityDoctorChecks)((0, securityDoctor_1.collectSecurityDoctorReport)(config.companyId)));
|
|
492
453
|
}
|
|
493
|
-
// 8. gh / glab CLIs (optional — only needed for reverse-ticket from a
|
|
494
|
-
// specific PR/MR URL; branch mode works without either).
|
|
495
454
|
checks.push({
|
|
496
455
|
name: "GitHub CLI (`gh`) for reverse-ticket from a PR url",
|
|
497
456
|
status: hasGhCli() ? "pass" : "warn",
|
package/dist/commands/inbox.js
CHANGED
|
@@ -70,8 +70,6 @@ async function runRead(args) {
|
|
|
70
70
|
const config = (0, shared_1.requireConfig)();
|
|
71
71
|
const start = Date.now();
|
|
72
72
|
try {
|
|
73
|
-
// First, fetch inbox so we can show the full body (markRead endpoint
|
|
74
|
-
// doesn't return content).
|
|
75
73
|
const listRes = await (0, shared_1.apiRequest)("GET", "/api/cli/inbox", {
|
|
76
74
|
config,
|
|
77
75
|
query: { limit: 100 },
|
package/dist/commands/library.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// `codiedev library [--scope mine|shared|all] [--kind doc|skill] [--tag X]
|
|
3
|
-
// [--type spec|...] [--limit N]` — list artifacts in your Codiedev workspace.
|
|
4
|
-
// Server applies companyId scoping; --scope filters within that.
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.runLibrary = runLibrary;
|
|
7
4
|
const shared_1 = require("./shared");
|