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.
Files changed (146) hide show
  1. package/README.md +7 -1
  2. package/assets/tui-brand-plugin.tsx +14 -2
  3. package/dist/backup/cache.js +1 -5
  4. package/dist/backup/match.js +0 -6
  5. package/dist/backup/run.js +3 -35
  6. package/dist/captureExclusions.js +0 -12
  7. package/dist/captureLog.js +0 -28
  8. package/dist/captureReadiness.js +0 -58
  9. package/dist/captureSignals.js +66 -3
  10. package/dist/cli.js +41 -18
  11. package/dist/codexHookTrust.js +61 -5
  12. package/dist/codexLimitScan.js +2 -109
  13. package/dist/codie.js +27 -40
  14. package/dist/codieBrandInstall.js +0 -18
  15. package/dist/codieLauncher.js +2 -70
  16. package/dist/codieRuntime.js +19 -23
  17. package/dist/codieRuntimeInstall.js +108 -23
  18. package/dist/codieStartup.js +0 -3
  19. package/dist/codieUsage.js +38 -41
  20. package/dist/commands/ask.js +0 -3
  21. package/dist/commands/capture.js +0 -2
  22. package/dist/commands/createTicket.js +0 -10
  23. package/dist/commands/doctor.js +41 -82
  24. package/dist/commands/inbox.js +0 -2
  25. package/dist/commands/library.js +0 -3
  26. package/dist/commands/pack.js +573 -0
  27. package/dist/commands/ping.js +0 -2
  28. package/dist/commands/pull.js +6 -10
  29. package/dist/commands/push.js +2 -12
  30. package/dist/commands/react.js +0 -2
  31. package/dist/commands/reverseTicket.js +0 -33
  32. package/dist/commands/search.js +0 -4
  33. package/dist/commands/send.js +0 -5
  34. package/dist/commands/share.js +0 -4
  35. package/dist/commands/shared.js +0 -35
  36. package/dist/commands/traces.js +280 -0
  37. package/dist/configDir.js +0 -10
  38. package/dist/connect.js +21 -77
  39. package/dist/connectFlow.js +0 -9
  40. package/dist/copilotHooks.js +395 -0
  41. package/dist/copilotInstallation.js +64 -0
  42. package/dist/cursorHooks.js +40 -17
  43. package/dist/cursorLifecycle.js +109 -0
  44. package/dist/detection.js +0 -30
  45. package/dist/harnessDirs.js +73 -0
  46. package/dist/hook.js +193 -316
  47. package/dist/inboxFormat.js +0 -24
  48. package/dist/launchContext.js +150 -0
  49. package/dist/launcher.js +140 -0
  50. package/dist/limitEvent.js +41 -35
  51. package/dist/limitEventReporter.js +1 -1
  52. package/dist/mcp.js +3 -77
  53. package/dist/nativeSteering.js +24 -0
  54. package/dist/packs/codie.js +182 -0
  55. package/dist/packs/harnesses.js +374 -0
  56. package/dist/packs/hookGate.js +256 -0
  57. package/dist/packs/install.js +285 -0
  58. package/dist/packs/manifest.js +126 -0
  59. package/dist/packs/paths.js +81 -0
  60. package/dist/packs/shim.js +214 -0
  61. package/dist/packs/store.js +155 -0
  62. package/dist/packs/sync.js +170 -0
  63. package/dist/prUrl.js +0 -5
  64. package/dist/repoResolver.js +0 -19
  65. package/dist/secret.js +0 -3
  66. package/dist/security.js +0 -20
  67. package/dist/securityDeepScan.js +1 -14
  68. package/dist/securityDoctor.js +0 -2
  69. package/dist/securityFeature.js +0 -1
  70. package/dist/securityInstall.js +6 -13
  71. package/dist/securityInventory.js +23 -13
  72. package/dist/securityPilot.js +0 -2
  73. package/dist/securityPolicy.js +0 -7
  74. package/dist/selfUpdate.js +331 -0
  75. package/dist/skillMarker.js +23 -0
  76. package/dist/subagents.js +135 -0
  77. package/dist/teamSkillContext.js +10 -0
  78. package/dist/transcriptModels.js +6 -8
  79. package/dist/upgradeNudge.js +0 -30
  80. package/dist/utils.js +286 -733
  81. package/dist/version.js +0 -10
  82. package/dist/versionCheck.js +0 -33
  83. package/package.json +7 -6
  84. package/dist/backup/cache.d.ts +0 -14
  85. package/dist/backup/match.d.ts +0 -2
  86. package/dist/backup/run.d.ts +0 -25
  87. package/dist/captureExclusions.d.ts +0 -30
  88. package/dist/captureLog.d.ts +0 -20
  89. package/dist/captureReadiness.d.ts +0 -65
  90. package/dist/captureSignals.d.ts +0 -19
  91. package/dist/cli.d.ts +0 -16
  92. package/dist/codexHookTrust.d.ts +0 -34
  93. package/dist/codexLimitScan.d.ts +0 -64
  94. package/dist/codie.d.ts +0 -2
  95. package/dist/codieBrandInstall.d.ts +0 -11
  96. package/dist/codieLauncher.d.ts +0 -86
  97. package/dist/codieRuntime.d.ts +0 -29
  98. package/dist/codieRuntimeInstall.d.ts +0 -28
  99. package/dist/codieStartup.d.ts +0 -26
  100. package/dist/codieUsage.d.ts +0 -57
  101. package/dist/commands/ask.d.ts +0 -1
  102. package/dist/commands/capture.d.ts +0 -14
  103. package/dist/commands/createTicket.d.ts +0 -27
  104. package/dist/commands/delete.d.ts +0 -1
  105. package/dist/commands/doctor.d.ts +0 -1
  106. package/dist/commands/inbox.d.ts +0 -3
  107. package/dist/commands/library.d.ts +0 -2
  108. package/dist/commands/note.d.ts +0 -1
  109. package/dist/commands/ping.d.ts +0 -1
  110. package/dist/commands/promote.d.ts +0 -1
  111. package/dist/commands/pull.d.ts +0 -2
  112. package/dist/commands/push.d.ts +0 -33
  113. package/dist/commands/react.d.ts +0 -2
  114. package/dist/commands/reverseTicket.d.ts +0 -2
  115. package/dist/commands/search.d.ts +0 -2
  116. package/dist/commands/security.d.ts +0 -1
  117. package/dist/commands/send.d.ts +0 -2
  118. package/dist/commands/share.d.ts +0 -2
  119. package/dist/commands/shared.d.ts +0 -64
  120. package/dist/configDir.d.ts +0 -1
  121. package/dist/connect.d.ts +0 -1
  122. package/dist/connectFlow.d.ts +0 -13
  123. package/dist/cursorHooks.d.ts +0 -23
  124. package/dist/detection.d.ts +0 -30
  125. package/dist/hook.d.ts +0 -6
  126. package/dist/inboxFormat.d.ts +0 -38
  127. package/dist/limitEvent.d.ts +0 -16
  128. package/dist/limitEventReporter.d.ts +0 -21
  129. package/dist/mcp.d.ts +0 -12
  130. package/dist/prUrl.d.ts +0 -9
  131. package/dist/repoResolver.d.ts +0 -17
  132. package/dist/secret.d.ts +0 -1
  133. package/dist/security.d.ts +0 -102
  134. package/dist/securityDeepScan.d.ts +0 -41
  135. package/dist/securityDoctor.d.ts +0 -32
  136. package/dist/securityFeature.d.ts +0 -5
  137. package/dist/securityInstall.d.ts +0 -63
  138. package/dist/securityInventory.d.ts +0 -54
  139. package/dist/securityPilot.d.ts +0 -19
  140. package/dist/securityPolicy.d.ts +0 -33
  141. package/dist/securitySession.d.ts +0 -6
  142. package/dist/transcriptModels.d.ts +0 -18
  143. package/dist/upgradeNudge.d.ts +0 -25
  144. package/dist/utils.d.ts +0 -346
  145. package/dist/version.d.ts +0 -1
  146. package/dist/versionCheck.d.ts +0 -68
@@ -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);
@@ -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(" ")
@@ -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 {
@@ -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 CLAUDE_SETTINGS_PATH = path.join(os.homedir(), ".claude", "settings.json");
51
- const CLAUDE_INSTRUCTIONS_PATH = path.join(os.homedir(), ".claude", "CLAUDE.md");
52
- const CODEX_HOOKS_PATH = path.join(os.homedir(), ".codex", "hooks.json");
53
- const CODEX_INSTRUCTIONS_PATH = path.join(os.homedir(), ".codex", "AGENTS.md");
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(path.join(os.homedir(), ".codex"));
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; // malformed body → fall back to the cached list
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; // unreachable / old backend → fall back to the local marker
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: "~/.claude",
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
- ? "~/.claude/settings.json"
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
- ? "~/.claude/CLAUDE.md"
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: "~/.codex",
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
- ? "~/.codex/hooks.json"
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
- ? "~/.codex/AGENTS.md"
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
- // Agent Security is part of installation health, not a separate setup
488
- // concept. Keep `codiedev security doctor` as an alias for focused output,
489
- // while the main doctor remains the one command users need to remember.
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",
@@ -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 },
@@ -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");