brainclaw 0.19.2

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 (128) hide show
  1. package/LICENSE +74 -0
  2. package/README.md +226 -0
  3. package/dist/cli.js +1037 -0
  4. package/dist/commands/accept.js +149 -0
  5. package/dist/commands/adapter-openclaw-import.js +75 -0
  6. package/dist/commands/add-step.js +35 -0
  7. package/dist/commands/agent-board.js +106 -0
  8. package/dist/commands/audit.js +35 -0
  9. package/dist/commands/bootstrap.js +34 -0
  10. package/dist/commands/capability.js +104 -0
  11. package/dist/commands/changes.js +112 -0
  12. package/dist/commands/check-constraints.js +63 -0
  13. package/dist/commands/claim-resource.js +54 -0
  14. package/dist/commands/claim.js +92 -0
  15. package/dist/commands/complete-step.js +34 -0
  16. package/dist/commands/constraint.js +44 -0
  17. package/dist/commands/context-diff.js +32 -0
  18. package/dist/commands/context.js +63 -0
  19. package/dist/commands/decision.js +45 -0
  20. package/dist/commands/delete-plan.js +20 -0
  21. package/dist/commands/diff.js +99 -0
  22. package/dist/commands/doctor.js +1275 -0
  23. package/dist/commands/enable-agent.js +63 -0
  24. package/dist/commands/env.js +46 -0
  25. package/dist/commands/estimation-report.js +167 -0
  26. package/dist/commands/explore.js +47 -0
  27. package/dist/commands/export.js +381 -0
  28. package/dist/commands/handoff.js +63 -0
  29. package/dist/commands/history.js +22 -0
  30. package/dist/commands/hooks.js +123 -0
  31. package/dist/commands/init.js +356 -0
  32. package/dist/commands/install-hooks.js +115 -0
  33. package/dist/commands/instruction.js +56 -0
  34. package/dist/commands/list-agents.js +44 -0
  35. package/dist/commands/list-claims.js +45 -0
  36. package/dist/commands/list-instructions.js +50 -0
  37. package/dist/commands/list-plans.js +48 -0
  38. package/dist/commands/mcp-worker.js +12 -0
  39. package/dist/commands/mcp.js +2272 -0
  40. package/dist/commands/memory.js +283 -0
  41. package/dist/commands/metrics.js +175 -0
  42. package/dist/commands/plan-resource.js +62 -0
  43. package/dist/commands/plan.js +76 -0
  44. package/dist/commands/prune-candidates.js +36 -0
  45. package/dist/commands/prune.js +48 -0
  46. package/dist/commands/pull.js +25 -0
  47. package/dist/commands/push.js +28 -0
  48. package/dist/commands/rebuild.js +14 -0
  49. package/dist/commands/reflect-runtime-note.js +74 -0
  50. package/dist/commands/reflect.js +286 -0
  51. package/dist/commands/register-agent.js +29 -0
  52. package/dist/commands/reject.js +52 -0
  53. package/dist/commands/release-claim.js +41 -0
  54. package/dist/commands/release-claims.js +67 -0
  55. package/dist/commands/review.js +242 -0
  56. package/dist/commands/rollback.js +156 -0
  57. package/dist/commands/runtime-note.js +144 -0
  58. package/dist/commands/runtime-status.js +49 -0
  59. package/dist/commands/search.js +36 -0
  60. package/dist/commands/session-end.js +187 -0
  61. package/dist/commands/session-start.js +147 -0
  62. package/dist/commands/set-trust.js +92 -0
  63. package/dist/commands/setup.js +446 -0
  64. package/dist/commands/show-candidate.js +31 -0
  65. package/dist/commands/star-candidate.js +28 -0
  66. package/dist/commands/status.js +133 -0
  67. package/dist/commands/sync.js +159 -0
  68. package/dist/commands/tool.js +126 -0
  69. package/dist/commands/trap.js +74 -0
  70. package/dist/commands/update-handoff.js +23 -0
  71. package/dist/commands/update-plan.js +37 -0
  72. package/dist/commands/upgrade.js +382 -0
  73. package/dist/commands/use-candidate.js +35 -0
  74. package/dist/commands/version.js +96 -0
  75. package/dist/commands/watch.js +215 -0
  76. package/dist/commands/whoami.js +104 -0
  77. package/dist/core/agent-context.js +340 -0
  78. package/dist/core/agent-files.js +874 -0
  79. package/dist/core/agent-integrations.js +135 -0
  80. package/dist/core/agent-inventory.js +401 -0
  81. package/dist/core/agent-registry.js +420 -0
  82. package/dist/core/ai-agent-detection.js +140 -0
  83. package/dist/core/audit.js +85 -0
  84. package/dist/core/bootstrap.js +658 -0
  85. package/dist/core/brainclaw-version.js +433 -0
  86. package/dist/core/candidates.js +137 -0
  87. package/dist/core/circuit-breaker.js +118 -0
  88. package/dist/core/claims.js +72 -0
  89. package/dist/core/config.js +86 -0
  90. package/dist/core/context-diff.js +122 -0
  91. package/dist/core/context.js +1212 -0
  92. package/dist/core/contradictions.js +270 -0
  93. package/dist/core/coordination.js +86 -0
  94. package/dist/core/cross-project.js +99 -0
  95. package/dist/core/duplicates.js +72 -0
  96. package/dist/core/event-log.js +152 -0
  97. package/dist/core/events.js +56 -0
  98. package/dist/core/execution-context.js +204 -0
  99. package/dist/core/freshness.js +87 -0
  100. package/dist/core/global-registry.js +182 -0
  101. package/dist/core/host.js +10 -0
  102. package/dist/core/identity.js +151 -0
  103. package/dist/core/ids.js +56 -0
  104. package/dist/core/input-validation.js +81 -0
  105. package/dist/core/instructions.js +117 -0
  106. package/dist/core/io.js +191 -0
  107. package/dist/core/json-store.js +63 -0
  108. package/dist/core/lifecycle.js +45 -0
  109. package/dist/core/lock.js +129 -0
  110. package/dist/core/logger.js +49 -0
  111. package/dist/core/machine-profile.js +332 -0
  112. package/dist/core/markdown.js +120 -0
  113. package/dist/core/memory-git.js +133 -0
  114. package/dist/core/migration.js +247 -0
  115. package/dist/core/project-registry.js +64 -0
  116. package/dist/core/reflection-safety.js +21 -0
  117. package/dist/core/repo-analysis.js +133 -0
  118. package/dist/core/reputation.js +409 -0
  119. package/dist/core/runtime.js +134 -0
  120. package/dist/core/schema.js +580 -0
  121. package/dist/core/search.js +115 -0
  122. package/dist/core/security.js +66 -0
  123. package/dist/core/setup-state.js +50 -0
  124. package/dist/core/state.js +83 -0
  125. package/dist/core/store-resolution.js +119 -0
  126. package/dist/core/sync-remote.js +83 -0
  127. package/dist/core/traps.js +86 -0
  128. package/package.json +60 -0
@@ -0,0 +1,215 @@
1
+ import fs from 'node:fs';
2
+ import { memoryExists, memoryPath } from '../core/io.js';
3
+ import { loadState } from '../core/state.js';
4
+ import { listRuntimeNotes } from '../core/runtime.js';
5
+ import { listCandidates } from '../core/candidates.js';
6
+ import { readAuditLog } from '../core/audit.js';
7
+ import { listClaims, saveClaim, generateClaimId, ensureClaimsDir } from '../core/claims.js';
8
+ import { resolveCurrentAgentName } from '../core/agent-registry.js';
9
+ function emit(event) {
10
+ process.stdout.write(JSON.stringify(event) + '\n');
11
+ }
12
+ export function runWatch(options = {}) {
13
+ if (!memoryExists()) {
14
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
15
+ process.exit(1);
16
+ }
17
+ const intervalMs = (options.interval ?? 2) * 1000;
18
+ const dir = memoryPath('');
19
+ emit({
20
+ event: 'watch_started',
21
+ section: 'system',
22
+ timestamp: new Date().toISOString(),
23
+ });
24
+ // Track known item counts per section for change detection
25
+ const last = {
26
+ constraints: 0,
27
+ decisions: 0,
28
+ traps: 0,
29
+ handoffs: 0,
30
+ plans: 0,
31
+ notes: 0,
32
+ candidates: 0,
33
+ audit: 0,
34
+ };
35
+ // Initialise baseline without emitting
36
+ try {
37
+ const state = loadState();
38
+ last.constraints = state.active_constraints.length;
39
+ last.decisions = state.recent_decisions.length;
40
+ last.traps = state.known_traps.length;
41
+ last.handoffs = state.open_handoffs.length;
42
+ last.plans = state.plan_items.length;
43
+ }
44
+ catch { /* ignore */ }
45
+ try {
46
+ last.notes = listRuntimeNotes().length;
47
+ }
48
+ catch { /* ignore */ }
49
+ try {
50
+ last.candidates = listCandidates().length;
51
+ }
52
+ catch { /* ignore */ }
53
+ try {
54
+ last.audit = readAuditLog().length;
55
+ }
56
+ catch { /* ignore */ }
57
+ const tick = () => {
58
+ try {
59
+ const state = loadState();
60
+ if (state.active_constraints.length !== last.constraints) {
61
+ const delta = state.active_constraints.length - last.constraints;
62
+ last.constraints = state.active_constraints.length;
63
+ if (delta > 0) {
64
+ const added = state.active_constraints.slice(-delta);
65
+ for (const c of added) {
66
+ emit({ event: 'added', section: 'constraint', item_id: c.id, text: c.text, author: c.author, timestamp: c.created_at });
67
+ }
68
+ }
69
+ else {
70
+ emit({ event: 'removed', section: 'constraint', timestamp: new Date().toISOString() });
71
+ }
72
+ }
73
+ if (state.recent_decisions.length !== last.decisions) {
74
+ const delta = state.recent_decisions.length - last.decisions;
75
+ last.decisions = state.recent_decisions.length;
76
+ if (delta > 0) {
77
+ const added = state.recent_decisions.slice(-delta);
78
+ for (const d of added) {
79
+ emit({ event: 'added', section: 'decision', item_id: d.id, text: d.text, author: d.author, timestamp: d.created_at });
80
+ }
81
+ }
82
+ }
83
+ if (state.known_traps.length !== last.traps) {
84
+ const delta = state.known_traps.length - last.traps;
85
+ last.traps = state.known_traps.length;
86
+ if (delta > 0) {
87
+ const added = state.known_traps.slice(-delta);
88
+ for (const t of added) {
89
+ emit({ event: 'added', section: 'trap', item_id: t.id, text: t.text, author: t.author, timestamp: t.created_at });
90
+ }
91
+ }
92
+ }
93
+ if (state.open_handoffs.length !== last.handoffs) {
94
+ last.handoffs = state.open_handoffs.length;
95
+ emit({ event: 'changed', section: 'handoffs', timestamp: new Date().toISOString() });
96
+ }
97
+ if (state.plan_items.length !== last.plans) {
98
+ last.plans = state.plan_items.length;
99
+ emit({ event: 'changed', section: 'plans', timestamp: new Date().toISOString() });
100
+ }
101
+ }
102
+ catch { /* state not available yet */ }
103
+ try {
104
+ const notes = listRuntimeNotes();
105
+ if (notes.length !== last.notes) {
106
+ const delta = notes.length - last.notes;
107
+ last.notes = notes.length;
108
+ if (delta > 0) {
109
+ const added = notes.slice(-delta);
110
+ for (const n of added) {
111
+ emit({ event: 'added', section: 'runtime_note', item_id: n.id, text: n.text, author: n.agent, timestamp: n.created_at });
112
+ }
113
+ }
114
+ }
115
+ }
116
+ catch { /* ignore */ }
117
+ try {
118
+ const candidates = listCandidates();
119
+ if (candidates.length !== last.candidates) {
120
+ const delta = candidates.length - last.candidates;
121
+ last.candidates = candidates.length;
122
+ if (delta > 0) {
123
+ const added = candidates.slice(-delta);
124
+ for (const c of added) {
125
+ emit({ event: 'added', section: 'candidate', item_id: c.id, text: c.text, author: c.author, timestamp: c.created_at });
126
+ }
127
+ }
128
+ }
129
+ }
130
+ catch { /* ignore */ }
131
+ try {
132
+ const auditEntries = readAuditLog();
133
+ if (auditEntries.length !== last.audit) {
134
+ const delta = auditEntries.length - last.audit;
135
+ last.audit = auditEntries.length;
136
+ if (delta > 0) {
137
+ const added = auditEntries.slice(-delta);
138
+ for (const e of added) {
139
+ emit({ event: 'audit', section: 'audit', item_id: e.item_id, timestamp: e.timestamp });
140
+ }
141
+ }
142
+ }
143
+ }
144
+ catch { /* ignore */ }
145
+ };
146
+ // Watch loop — poll on interval
147
+ setInterval(tick, intervalMs);
148
+ // Also watch the memory dir for any file changes
149
+ try {
150
+ fs.watch(dir, { recursive: true }, (_eventType, filename) => {
151
+ if (filename && !filename.endsWith('.lock')) {
152
+ // Just emit a raw file-change event; tick() will handle the details on next poll
153
+ emit({
154
+ event: 'file_changed',
155
+ section: 'fs',
156
+ text: filename,
157
+ timestamp: new Date().toISOString(),
158
+ });
159
+ }
160
+ });
161
+ }
162
+ catch {
163
+ // fs.watch not available (e.g. some Docker environments) — poll only is fine
164
+ }
165
+ // Auto-claim mode: watch workspace for first write on each file
166
+ if (options.autoClaim) {
167
+ const cwd = process.cwd();
168
+ const agentName = options.agent ?? resolveCurrentAgentName();
169
+ const claimedFiles = new Set();
170
+ const IGNORED = new Set(['.brainclaw', '.git', 'node_modules', 'dist', 'dist-test']);
171
+ ensureClaimsDir();
172
+ try {
173
+ fs.watch(cwd, { recursive: true }, (eventType, filename) => {
174
+ if (eventType !== 'change' || !filename)
175
+ return;
176
+ const parts = filename.replace(/\\/g, '/').split('/');
177
+ if (IGNORED.has(parts[0]))
178
+ return;
179
+ if (claimedFiles.has(filename))
180
+ return;
181
+ // Check if this agent already has an active claim covering this file
182
+ const existing = listClaims().filter((c) => c.status === 'active' && c.agent === agentName);
183
+ const alreadyClaimed = existing.some((c) => c.scope.split(/\s+/).some((s) => {
184
+ const sp = s.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '');
185
+ const f = filename.replace(/\\/g, '/').replace(/^\.\//, '');
186
+ return f === sp || f.startsWith(sp + '/');
187
+ }));
188
+ if (alreadyClaimed) {
189
+ claimedFiles.add(filename);
190
+ return;
191
+ }
192
+ claimedFiles.add(filename);
193
+ const claim = {
194
+ id: generateClaimId(),
195
+ agent: agentName,
196
+ scope: filename,
197
+ description: `auto-claim: ${filename}`,
198
+ created_at: new Date().toISOString(),
199
+ status: 'active',
200
+ };
201
+ try {
202
+ saveClaim(claim);
203
+ emit({ event: 'auto_claim_created', section: 'claim', item_id: claim.id, text: filename, timestamp: new Date().toISOString() });
204
+ }
205
+ catch { /* skip if concurrent write fails */ }
206
+ });
207
+ }
208
+ catch { /* fs.watch unavailable */ }
209
+ }
210
+ process.on('SIGINT', () => {
211
+ emit({ event: 'watch_stopped', section: 'system', timestamp: new Date().toISOString() });
212
+ process.exit(0);
213
+ });
214
+ }
215
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1,104 @@
1
+ import { memoryExists } from '../core/io.js';
2
+ import { resolveCurrentAgentIdentity, resolveCurrentModel } from '../core/agent-registry.js';
3
+ import { loadConfig } from '../core/config.js';
4
+ import { resolveCurrentHostId } from '../core/host.js';
5
+ import { buildOperationalIdentity } from '../core/identity.js';
6
+ import { assessAgentIntegrationReadiness } from '../core/agent-integrations.js';
7
+ import { assessBrainclawVersion } from '../core/brainclaw-version.js';
8
+ import { buildExecutionContext, compactExecutionContext } from '../core/execution-context.js';
9
+ import { buildAgentToolingContext } from '../core/agent-context.js';
10
+ export function runWhoami(options = {}) {
11
+ const cwd = options.cwd ?? process.cwd();
12
+ if (!memoryExists(cwd)) {
13
+ console.error('Error: .brainclaw/ not found. Run `brainclaw init` first.');
14
+ process.exit(1);
15
+ }
16
+ const config = loadConfig(cwd);
17
+ const hostId = resolveCurrentHostId();
18
+ const executionContext = compactExecutionContext(buildExecutionContext({ cwd }));
19
+ const integrationReadiness = assessAgentIntegrationReadiness(config, cwd);
20
+ const brainclawVersion = assessBrainclawVersion(config);
21
+ const agentTooling = buildAgentToolingContext({ cwd });
22
+ let identity;
23
+ try {
24
+ identity = buildOperationalIdentity(undefined, cwd);
25
+ }
26
+ catch { /* no agent configured */ }
27
+ const agent = identity
28
+ ? resolveCurrentAgentIdentity(cwd)
29
+ : undefined;
30
+ const model = resolveCurrentModel(cwd);
31
+ const result = {
32
+ resolved_agent: identity?.agent ?? null,
33
+ agent_id: identity?.agent_id ?? null,
34
+ model: model ?? null,
35
+ host_id: hostId,
36
+ session_id: identity?.session_id ?? null,
37
+ project_id: identity?.project_id ?? null,
38
+ project_name: config.project_name,
39
+ storage_dir: config.storage_dir,
40
+ trust_level: agent?.trust_level ?? 'contributor',
41
+ capabilities: agent?.capabilities ?? [],
42
+ kind: agent?.kind ?? 'unknown',
43
+ identity_key: agent?.identity_key ?? null,
44
+ env_agent: process.env.BRAINCLAW_AGENT ?? null,
45
+ env_session: process.env.BRAINCLAW_SESSION_ID ?? null,
46
+ env_host: process.env.BRAINCLAW_HOST_ID ?? null,
47
+ brainclaw_version: brainclawVersion,
48
+ declared_agent_integrations: config.agent_integrations,
49
+ integration_readiness: integrationReadiness,
50
+ execution_context: executionContext,
51
+ agent_tooling: {
52
+ agents_md_present: agentTooling.agents_md_present,
53
+ agents_md_title: agentTooling.agents_md_title,
54
+ agents_rules: agentTooling.agents_rules,
55
+ skills: agentTooling.skills,
56
+ mcp_servers: agentTooling.mcp_servers,
57
+ },
58
+ };
59
+ if (options.json) {
60
+ console.log(JSON.stringify(result, null, 2));
61
+ return;
62
+ }
63
+ console.log(`Identity resolved for: ${result.resolved_agent ?? '(no agent)'}`);
64
+ if (result.agent_id)
65
+ console.log(` Agent ID : ${result.agent_id}`);
66
+ if (result.model)
67
+ console.log(` Model : ${result.model}`);
68
+ console.log(` Trust level: ${result.trust_level}`);
69
+ if (result.capabilities.length > 0)
70
+ console.log(` Capabilities: ${result.capabilities.join(', ')}`);
71
+ if (result.identity_key?.fingerprint)
72
+ console.log(` Fingerprint: ${result.identity_key.fingerprint}`);
73
+ console.log(` Kind : ${result.kind}`);
74
+ console.log(` Host ID : ${result.host_id}`);
75
+ if (result.session_id)
76
+ console.log(` Session ID : ${result.session_id}`);
77
+ console.log(` Project : ${result.project_name} (${result.project_id ?? 'n/a'})`);
78
+ console.log(` Storage : ${result.storage_dir}`);
79
+ console.log(` Brainclaw : ${result.brainclaw_version.cli_version}`);
80
+ if (result.brainclaw_version.status !== 'ok') {
81
+ console.log(` Version : ${result.brainclaw_version.message}`);
82
+ }
83
+ console.log(` Declared integrations: ${result.declared_agent_integrations.declarations.length}`);
84
+ const missingIntegrations = result.integration_readiness.filter((entry) => !entry.ready);
85
+ if (missingIntegrations.length > 0) {
86
+ console.log(` Missing activation: ${missingIntegrations.length}`);
87
+ }
88
+ console.log(` Branch : ${result.execution_context.branch ?? '(none)'}`);
89
+ console.log(` Git status : ${result.execution_context.git_status}`);
90
+ if (result.execution_context.toolchains.length > 0) {
91
+ const primary = result.execution_context.toolchains[0];
92
+ console.log(` Toolchain : ${primary.name}${primary.version ? ` ${primary.version}` : ''}`);
93
+ }
94
+ if (result.agent_tooling.agents_rules.length > 0) {
95
+ console.log(` Agent rule : ${result.agent_tooling.agents_rules[0]}`);
96
+ }
97
+ console.log(` Skills : ${result.agent_tooling.skills.length}`);
98
+ console.log(` MCP servers: ${result.agent_tooling.mcp_servers.length}`);
99
+ const missingServer = result.agent_tooling.mcp_servers.find((server) => server.availability === 'missing_command');
100
+ if (missingServer) {
101
+ console.log(` MCP issue : ${missingServer.name} missing ${missingServer.command ?? 'command'}`);
102
+ }
103
+ }
104
+ //# sourceMappingURL=whoami.js.map
@@ -0,0 +1,340 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ const MAX_AGENT_RULES = 5;
5
+ const MAX_SKILLS = 25;
6
+ const skillsCache = new Map();
7
+ const mcpCache = new Map();
8
+ export function buildAgentToolingContext(options = {}) {
9
+ const cwd = options.cwd ?? process.cwd();
10
+ const env = options.env ?? process.env;
11
+ const agents = readAgentsMarkdown(cwd);
12
+ return {
13
+ agents_md_present: agents.present,
14
+ agents_md_title: agents.title,
15
+ agents_rules: agents.rules,
16
+ skills: listSkills(cwd, env),
17
+ mcp_servers: listMcpServers(cwd, env),
18
+ };
19
+ }
20
+ export function renderAgentToolingSummary(snapshot) {
21
+ const lines = [];
22
+ lines.push(`AGENTS.md: ${snapshot.agents_md_present ? 'present' : 'absent'}`);
23
+ if (snapshot.agents_md_title) {
24
+ lines.push(`AGENTS title: ${snapshot.agents_md_title}`);
25
+ }
26
+ if (snapshot.agents_rules.length > 0) {
27
+ lines.push('Agent rules:');
28
+ for (const rule of snapshot.agents_rules) {
29
+ lines.push(`- ${rule}`);
30
+ }
31
+ }
32
+ lines.push(`Skills: ${snapshot.skills.length}`);
33
+ for (const skill of snapshot.skills.slice(0, 10)) {
34
+ const markers = [];
35
+ if (skill.scripts_present)
36
+ markers.push('scripts');
37
+ if (skill.references_present)
38
+ markers.push('references');
39
+ if (skill.assets_present)
40
+ markers.push('assets');
41
+ const suffix = markers.length > 0 ? ` [${markers.join(', ')}]` : '';
42
+ lines.push(`- ${skill.name}${skill.description ? `: ${skill.description}` : ''}${suffix}`);
43
+ }
44
+ lines.push(`MCP servers: ${snapshot.mcp_servers.length}`);
45
+ for (const server of snapshot.mcp_servers.slice(0, 10)) {
46
+ const availability = server.availability === 'available'
47
+ ? 'available'
48
+ : server.availability === 'missing_command'
49
+ ? 'missing command'
50
+ : server.availability;
51
+ lines.push(`- ${server.name} (${server.transport}, ${availability})${server.command ? ` via ${server.command}` : ''}`);
52
+ }
53
+ return lines.join('\n');
54
+ }
55
+ function readAgentsMarkdown(cwd) {
56
+ const filepath = path.join(cwd, 'AGENTS.md');
57
+ if (!fs.existsSync(filepath)) {
58
+ return { present: false, rules: [] };
59
+ }
60
+ const raw = fs.readFileSync(filepath, 'utf-8');
61
+ const lines = raw.split(/\r?\n/);
62
+ const title = lines.find((line) => line.trim().startsWith('#'))?.replace(/^#+\s*/, '').trim();
63
+ const rules = lines
64
+ .map((line) => line.trim())
65
+ .filter((line) => /^([-*]|\d+\.)\s+/.test(line))
66
+ .map((line) => line.replace(/^([-*]|\d+\.)\s+/, '').trim())
67
+ .filter(Boolean)
68
+ .slice(0, MAX_AGENT_RULES);
69
+ return {
70
+ present: true,
71
+ title,
72
+ rules,
73
+ };
74
+ }
75
+ function listSkills(cwd, env) {
76
+ const cacheKey = skillCacheKey(cwd, env);
77
+ const cached = skillsCache.get(cacheKey);
78
+ if (cached) {
79
+ return cached;
80
+ }
81
+ const items = [];
82
+ const seen = new Set();
83
+ for (const skillsDir of skillDirectories(cwd, env)) {
84
+ if (!fs.existsSync(skillsDir)) {
85
+ continue;
86
+ }
87
+ for (const filepath of findSkillFiles(skillsDir)) {
88
+ if (seen.has(filepath)) {
89
+ continue;
90
+ }
91
+ seen.add(filepath);
92
+ const skill = readSkill(filepath);
93
+ if (skill) {
94
+ items.push(skill);
95
+ }
96
+ }
97
+ }
98
+ const result = items
99
+ .sort((left, right) => left.name.localeCompare(right.name))
100
+ .slice(0, MAX_SKILLS);
101
+ skillsCache.set(cacheKey, result);
102
+ return result;
103
+ }
104
+ function listMcpServers(cwd, env) {
105
+ const cacheKey = mcpCacheKey(cwd, env);
106
+ const cached = mcpCache.get(cacheKey);
107
+ if (cached) {
108
+ return cached;
109
+ }
110
+ const items = [];
111
+ const seen = new Set();
112
+ for (const configPath of configFiles(cwd, env)) {
113
+ if (!fs.existsSync(configPath) || seen.has(configPath)) {
114
+ continue;
115
+ }
116
+ seen.add(configPath);
117
+ items.push(...readMcpConfig(configPath, cwd, env));
118
+ }
119
+ const result = items.sort((left, right) => left.name.localeCompare(right.name));
120
+ mcpCache.set(cacheKey, result);
121
+ return result;
122
+ }
123
+ function readSkill(filepath) {
124
+ try {
125
+ const raw = fs.readFileSync(filepath, 'utf-8');
126
+ const lines = raw.split(/\r?\n/).map((line) => line.trim());
127
+ const description = lines.find((line) => isUsefulDescriptionLine(line));
128
+ const skillDir = path.dirname(filepath);
129
+ return {
130
+ name: path.basename(path.dirname(filepath)),
131
+ description: description && description.length <= 160 ? description : undefined,
132
+ source_path: filepath,
133
+ scripts_present: fs.existsSync(path.join(skillDir, 'scripts')),
134
+ references_present: fs.existsSync(path.join(skillDir, 'references')),
135
+ assets_present: fs.existsSync(path.join(skillDir, 'assets')),
136
+ };
137
+ }
138
+ catch {
139
+ return undefined;
140
+ }
141
+ }
142
+ function findSkillFiles(root) {
143
+ const files = [];
144
+ walkSkillDir(root, files);
145
+ return files;
146
+ }
147
+ function walkSkillDir(dir, files) {
148
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
149
+ const fullPath = path.join(dir, entry.name);
150
+ if (entry.isDirectory()) {
151
+ walkSkillDir(fullPath, files);
152
+ continue;
153
+ }
154
+ if (entry.isFile() && entry.name === 'SKILL.md') {
155
+ files.push(fullPath);
156
+ }
157
+ }
158
+ }
159
+ function readMcpConfig(configPath, cwd, env) {
160
+ const raw = fs.readFileSync(configPath, 'utf-8');
161
+ const lines = raw.split(/\r?\n/);
162
+ const items = [];
163
+ let current;
164
+ for (const line of lines) {
165
+ const trimmed = line.trim();
166
+ const sectionMatch = /^\[mcp_servers\.([A-Za-z0-9_.-]+)\]$/.exec(trimmed);
167
+ if (sectionMatch) {
168
+ if (current) {
169
+ items.push(toMcpServerItem(current, configPath, cwd, env));
170
+ }
171
+ current = { name: sectionMatch[1] };
172
+ continue;
173
+ }
174
+ if (!current) {
175
+ continue;
176
+ }
177
+ if (trimmed.startsWith('[')) {
178
+ items.push(toMcpServerItem(current, configPath, cwd, env));
179
+ current = undefined;
180
+ continue;
181
+ }
182
+ const commandMatch = /^command\s*=\s*"([^"]+)"/.exec(trimmed);
183
+ if (commandMatch) {
184
+ current.command = commandMatch[1];
185
+ continue;
186
+ }
187
+ const urlMatch = /^url\s*=\s*"([^"]+)"/.exec(trimmed);
188
+ if (urlMatch) {
189
+ current.url = urlMatch[1];
190
+ continue;
191
+ }
192
+ const argsMatch = /^args\s*=\s*\[(.+)\]/.exec(trimmed);
193
+ if (argsMatch) {
194
+ current.args = argsMatch[1];
195
+ }
196
+ }
197
+ if (current) {
198
+ items.push(toMcpServerItem(current, configPath, cwd, env));
199
+ }
200
+ return items;
201
+ }
202
+ function toMcpServerItem(record, configPath, cwd, env) {
203
+ const combined = `${record.url ?? ''} ${record.args ?? ''}`.toLowerCase();
204
+ const transport = record.url || combined.includes('http://') || combined.includes('https://')
205
+ ? 'remote'
206
+ : record.command
207
+ ? 'stdio'
208
+ : 'unknown';
209
+ const availability = transport === 'remote'
210
+ ? 'remote'
211
+ : transport === 'stdio'
212
+ ? (commandExists(record.command ?? '', cwd, env) ? 'available' : 'missing_command')
213
+ : 'unknown';
214
+ return {
215
+ name: record.name,
216
+ transport,
217
+ command: record.command,
218
+ config_path: configPath,
219
+ availability,
220
+ source: resolveConfigSource(configPath, cwd, env),
221
+ };
222
+ }
223
+ function skillDirectories(cwd, env) {
224
+ const roots = codexHomes(cwd, env);
225
+ return roots.map((root) => path.join(root, 'skills'));
226
+ }
227
+ function configFiles(cwd, env) {
228
+ return codexHomes(cwd, env).map((root) => path.join(root, 'config.toml'));
229
+ }
230
+ function codexHomes(cwd, env) {
231
+ const explicit = env.CODEX_HOME?.trim();
232
+ if (explicit) {
233
+ return [...new Set([
234
+ explicit,
235
+ path.join(cwd, '.codex'),
236
+ ])];
237
+ }
238
+ return [...new Set([
239
+ path.join(cwd, '.codex'),
240
+ path.join(os.homedir(), '.codex'),
241
+ ])];
242
+ }
243
+ function isUsefulDescriptionLine(line) {
244
+ if (!line) {
245
+ return false;
246
+ }
247
+ if (line.startsWith('#')) {
248
+ return false;
249
+ }
250
+ if (line === '---') {
251
+ return false;
252
+ }
253
+ if (/^[A-Za-z0-9_-]+:\s*$/.test(line)) {
254
+ return false;
255
+ }
256
+ return true;
257
+ }
258
+ function resolveConfigSource(configPath, cwd, env) {
259
+ const normalized = path.normalize(configPath);
260
+ const workspaceCodex = path.normalize(path.join(cwd, '.codex'));
261
+ const explicitCodexHome = env.CODEX_HOME?.trim();
262
+ if (isWithinPath(normalized, workspaceCodex)) {
263
+ return 'workspace';
264
+ }
265
+ if (explicitCodexHome && isWithinPath(normalized, path.normalize(explicitCodexHome))) {
266
+ return 'codex_home';
267
+ }
268
+ return 'home';
269
+ }
270
+ function commandExists(command, cwd, env) {
271
+ const trimmed = command.trim();
272
+ if (!trimmed) {
273
+ return false;
274
+ }
275
+ if (trimmed.includes(path.sep) || trimmed.includes('/')) {
276
+ const candidate = path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed);
277
+ return fileExists(candidate);
278
+ }
279
+ const pathValue = env.PATH ?? env.Path ?? '';
280
+ if (!pathValue) {
281
+ return false;
282
+ }
283
+ const extensions = process.platform === 'win32'
284
+ ? (env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)
285
+ : [''];
286
+ for (const dir of pathValue.split(path.delimiter).filter(Boolean)) {
287
+ if (process.platform === 'win32') {
288
+ const hasKnownExt = /\.[A-Za-z0-9]+$/.test(trimmed);
289
+ const candidates = hasKnownExt ? [trimmed] : extensions.map((ext) => `${trimmed}${ext}`);
290
+ for (const candidate of candidates) {
291
+ if (fileExists(path.join(dir, candidate))) {
292
+ return true;
293
+ }
294
+ }
295
+ continue;
296
+ }
297
+ if (fileExists(path.join(dir, trimmed))) {
298
+ return true;
299
+ }
300
+ }
301
+ return false;
302
+ }
303
+ function fileExists(filepath) {
304
+ try {
305
+ return fs.existsSync(filepath) && fs.statSync(filepath).isFile();
306
+ }
307
+ catch {
308
+ return false;
309
+ }
310
+ }
311
+ function skillCacheKey(cwd, env) {
312
+ const parts = [];
313
+ for (const skillsDir of skillDirectories(cwd, env)) {
314
+ parts.push(skillsDir);
315
+ if (!fs.existsSync(skillsDir)) {
316
+ continue;
317
+ }
318
+ for (const filepath of findSkillFiles(skillsDir)) {
319
+ parts.push(`${filepath}:${safeMtime(filepath)}`);
320
+ }
321
+ }
322
+ return parts.join('|');
323
+ }
324
+ function mcpCacheKey(cwd, env) {
325
+ const parts = configFiles(cwd, env).map((configPath) => `${configPath}:${safeMtime(configPath)}`);
326
+ parts.push(env.PATH ?? '', env.Path ?? '', env.PATHEXT ?? '');
327
+ return parts.join('|');
328
+ }
329
+ function safeMtime(filepath) {
330
+ try {
331
+ return fs.statSync(filepath).mtimeMs;
332
+ }
333
+ catch {
334
+ return 0;
335
+ }
336
+ }
337
+ function isWithinPath(filepath, root) {
338
+ return filepath === root || filepath.startsWith(`${root}${path.sep}`);
339
+ }
340
+ //# sourceMappingURL=agent-context.js.map