@constraint/cli 0.3.7 → 0.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constraint/cli",
3
- "version": "0.3.7",
3
+ "version": "0.4.1",
4
4
  "description": "Install, publish, and report Agent Skills with Constraint",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,23 +1,25 @@
1
1
  ---
2
2
  name: constraint-skills
3
- description: Operate governed Agent Skills with the Constraint terminal CLI and report their invocations. Use when an agent needs to authenticate or diagnose Constraint access; configure agents; discover upload domains; inspect, install, update, or upload skills; query run history; or invoke any skill installed from or governed by Constraint.
3
+ description: Operate governed Agent Skills with the Constraint terminal CLI. Use when an agent needs to authenticate or diagnose Constraint access; discover upload domains; inspect, install, update, or upload skills; review run history; or report a session transcript when the user asks to put skill work on the record.
4
4
  ---
5
5
 
6
6
  # Constraint Skills
7
7
 
8
- Use the `constraint` command for organization-authorized skills. Do not edit its config, lockfiles, instruction blocks, installation receipts, or authorization tokens directly.
8
+ Use the `constraint` command for organization-authorized skills. Do not edit its
9
+ config, lockfiles, installation receipts, or authorization tokens directly.
9
10
 
10
11
  ## Choose the operation
11
12
 
12
13
  - Authenticate or diagnose: `constraint login`, `constraint whoami`, `constraint doctor`.
13
- - Configure agents: `constraint setup`.
14
14
  - Discover or inspect skills: `constraint skills list`, `constraint skills inspect`.
15
15
  - Install or update: `constraint skills install`, `constraint skills update`.
16
16
  - Discover upload destinations: `constraint skills domains --json`.
17
17
  - Upload a local skill folder: `constraint skills upload`.
18
+ - Report a session: `constraint report`.
18
19
  - Review runs: `constraint skills run list`, `constraint skills run get`.
19
20
 
20
- Read [references/commands.md](references/commands.md) before constructing flags or handling a less common operation.
21
+ Read [references/commands.md](references/commands.md) before constructing flags
22
+ or handling a less common operation.
21
23
 
22
24
  ## Upload skills
23
25
 
@@ -27,7 +29,9 @@ Upload the skill directory, not an individual `SKILL.md`. Before every upload, r
27
29
  constraint skills domains --json
28
30
  ```
29
31
 
30
- Use one returned `slug` exactly as `--domain`. Never guess a slug from a display name or reuse a domain from another organization. If multiple destinations are valid and ownership is unclear, ask the user.
32
+ Use one returned `slug` exactly as `--domain`. Never guess a slug from a display
33
+ name or reuse a domain from another organization. If multiple destinations are
34
+ valid and ownership is unclear, ask the user.
31
35
 
32
36
  ```bash
33
37
  constraint skills upload <skill-directory> --domain <returned-slug>
@@ -35,33 +39,29 @@ constraint skills upload <skill-directory> --domain <returned-slug>
35
39
 
36
40
  ## Install skills
37
41
 
38
- Choose the target agent and scope explicitly. Prefer `--project`; use `--global` only when the user wants the skill across projects. Prefer managed links and use `--copy` only when independent files are required. Never overwrite locally edited installed skills.
42
+ Choose the target agent and scope explicitly. Prefer `--project`; use
43
+ `--global` only when the user wants the skill across projects. Prefer managed
44
+ links and use `--copy` only when independent files are required. Never
45
+ overwrite locally edited installed skills.
39
46
 
40
- ## Start a run
47
+ ## Report a run
41
48
 
42
- Immediately after selecting and loading a governed skill, run:
49
+ A run is the posting of a transcript in which a skill was invoked, and the user
50
+ drives it: report only when the user asks to put a session on the record. Never
51
+ report automatically or unprompted.
43
52
 
44
- ```bash
45
- constraint skills run start <skill-name> --client <client> --session <session-id>
46
- ```
47
-
48
- Use the invoked skill's frontmatter `name`. Normalize the client to `codex` or `claude-code`. Use the current client session identifier. Retain the exact run ID printed by the command. If one session invokes multiple skills, start and retain a distinct run ID for each invocation.
49
-
50
- If the command fails, report the failure briefly and continue the user's work. Do not fabricate a run ID.
51
-
52
- ## Complete a run
53
-
54
- After finishing the governed skill's work and before ending the response, locate the current session's real JSON or JSONL transcript file and run:
53
+ When asked, locate the current session's real JSON or JSONL transcript file and run:
55
54
 
56
55
  ```bash
57
- constraint skills run complete <run-id> <transcript-path>
56
+ constraint report <transcript-path> --skill <skill-name>
58
57
  ```
59
58
 
60
- Pass the explicit local path. The CLI uploads the whole transcript file as it exists at that moment and completes only that run. Never create a replacement transcript, summarize it, pass a temporary file, or guess a path.
61
-
62
- For multiple invocations, complete each retained run separately. Overlapping full-session transcript prefixes are expected. If the transcript path cannot be determined, leave the run started and tell the user that reporting could not be completed.
63
-
64
- Do not run completion hooks, background flushes, or pending-run scans.
59
+ Pass the explicit local path to the transcript as it exists at that moment.
60
+ Use the invoked skill's frontmatter `name`. Client and session id are read
61
+ from the transcript path; re-reporting the same session later supersedes the
62
+ earlier report. Never create a replacement transcript, summarize it, copy it
63
+ elsewhere first, or pass a temporary file. If the transcript path cannot be
64
+ determined, say so instead of guessing.
65
65
 
66
66
  ## Work safely
67
67
 
@@ -12,7 +12,7 @@ constraint setup
12
12
  constraint doctor
13
13
  ```
14
14
 
15
- `login` uses WorkOS device authorization. Never request or embed an API key or client secret. `setup` detects Codex and Claude Code, then separately presents the managed global-instruction change and this skill's global installation destinations. It never replaces unrelated instructions.
15
+ `login` uses WorkOS device authorization. Never request or embed an API key or client secret. `setup` detects Codex and Claude Code and installs this skill globally. It never touches instruction files.
16
16
 
17
17
  ## Discover, inspect, install, and update
18
18
 
@@ -40,11 +40,11 @@ constraint skills upload <skill-directory> --domain <returned-slug>
40
40
 
41
41
  `skills domains` returns only domains where the authenticated user can manage skills. JSON objects contain the exact `slug`, display `name`, and `description`. Discover first and pass one returned slug. Ask the user when multiple domains fit and ownership is unclear.
42
42
 
43
- ## Start, complete, and review runs
43
+ ## Report and review runs
44
44
 
45
45
  ```bash
46
- constraint skills run start <skill> --client codex --session <stable-session-id>
47
- constraint skills run complete <run-id> <actual-transcript-path>
46
+ constraint report <actual-transcript-path> --skill <skill-name>
47
+ constraint report <actual-transcript-path> --skill <skill-name> --client codex --session <id>
48
48
 
49
49
  constraint skills run list
50
50
  constraint skills run list --team <domain> --user <workos-user-id> --skill <skill>
@@ -52,6 +52,12 @@ constraint skills run get <run-id>
52
52
  constraint skills run get <run-id> --raw
53
53
  ```
54
54
 
55
+ `report` is user-driven: run it only when the user asks to put a session on the
56
+ record. Client and session id are read from the transcript path itself (Claude
57
+ Code project transcripts and Codex rollouts are named by session id);
58
+ `--client`/`--session` are only needed for files outside those locations.
59
+ Re-reporting the same session supersedes the earlier report.
60
+
55
61
  Repeat `--team`, `--user`, or `--skill` to request subsets. Review visibility is permission-controlled.
56
62
 
57
63
  `run get` never prints a transcript to stdout: it writes the run JSON (or the
@@ -67,4 +73,4 @@ Global flags: `--json`, `--quiet`, `--no-color`, `--help`, and `--version`. Inst
67
73
  - Setup uncertainty: run `constraint doctor`.
68
74
  - Upload denied: rerun `constraint skills domains --json` and respect the returned scope.
69
75
  - Install conflict: preserve local edits.
70
- - Missing transcript: locate the real client JSON or JSONL file before completion.
76
+ - Missing transcript: locate the real client JSON or JSONL file before reporting; if it cannot be found, tell the user instead of guessing.
package/src/cli.js CHANGED
@@ -20,7 +20,6 @@ import {
20
20
  taskSpinner,
21
21
  } from './prompts.js';
22
22
  import {
23
- applyInstructionSetup,
24
23
  installCompanionSkill,
25
24
  setupPlan,
26
25
  setupStatus,
@@ -36,7 +35,7 @@ import {
36
35
  uploadSkill,
37
36
  } from './skills.js';
38
37
 
39
- const VERSION = '0.3.7';
38
+ const VERSION = '0.4.1';
40
39
 
41
40
  const HELP = `Constraint Skills CLI
42
41
 
@@ -54,8 +53,7 @@ Usage:
54
53
  [--copy] [--yes]
55
54
  constraint skills update --all [--agent <agent>]... [--project|--global] [--yes]
56
55
  constraint skills upload <path> --domain <domain>
57
- constraint skills run start <skill> --client <client> --session <session-id>
58
- constraint skills run complete <run-id> <transcript-path>
56
+ constraint report <transcript-path> --skill <skill> [--client <client>] [--session <id>]
59
57
  constraint skills run list [--team <team>]... [--user <user>]... [--skill <skill>]...
60
58
  constraint skills run get <run-id>... [--raw] [--output <path>] (always writes a file, prints summary + path)
61
59
 
@@ -250,30 +248,17 @@ async function setupCommand(args, out) {
250
248
  if (dryRun) {
251
249
  out.data(plans.map((plan) => ({
252
250
  agent: plan.agent,
253
- instructions: {
254
- path: plan.instructions.path,
255
- action: plan.instructions.changed ? 'update' : 'current',
256
- content: plan.instructions.content,
257
- },
258
251
  skill: { path: plan.skill.path, scope: 'global', action: plan.skill.action },
259
252
  })));
260
253
  return;
261
254
  }
262
- const hasInstructionChanges = plans.some((plan) => plan.instructions.changed);
263
255
  const hasSkillChanges = plans.some((plan) => plan.skill.action !== 'current');
264
- if (!useUi && !yes && (hasInstructionChanges || hasSkillChanges)) {
256
+ if (!useUi && !yes && hasSkillChanges) {
265
257
  throw new Error('Non-interactive setup has pending changes. Pass --yes or use --dry-run.');
266
258
  }
267
259
 
268
- let configureInstructions = true;
269
260
  let installSkill = true;
270
261
  if (useUi) {
271
- note(
272
- plans.map((plan) => `${humanize(plan.agent)}\n${plan.instructions.path}\n${plan.instructions.changed ? 'Managed block will be added or updated' : 'Already configured'}`).join('\n\n'),
273
- '1. Configure agent instructions',
274
- );
275
- configureInstructions = !hasInstructionChanges || yes || await confirmPlan('Apply these instruction changes?');
276
-
277
262
  note(
278
263
  [
279
264
  'Skill: constraint-skills',
@@ -285,20 +270,12 @@ async function setupCommand(args, out) {
285
270
  '',
286
271
  ]),
287
272
  ].join('\n').trim(),
288
- '2. Install Constraint Skills',
273
+ 'Install Constraint Skills',
289
274
  );
290
275
  installSkill = !hasSkillChanges || yes || await confirmPlan(`Install globally for ${agents.map(humanize).join(' and ')}?`);
291
276
  }
292
277
 
293
- const result = { instructions: [], skill: [] };
294
- if (configureInstructions) {
295
- const spinner = taskSpinner(useUi && hasInstructionChanges);
296
- for (const plan of plans) {
297
- spinner.message(`Configuring ${humanize(plan.agent)} instructions`);
298
- result.instructions.push(await applyInstructionSetup(plan.agent));
299
- }
300
- spinner.stop('Agent instructions configured.');
301
- }
278
+ const result = { skill: [] };
302
279
  if (installSkill) {
303
280
  const spinner = taskSpinner(useUi && hasSkillChanges);
304
281
  for (const plan of plans) {
@@ -310,7 +287,6 @@ async function setupCommand(args, out) {
310
287
  if (useUi) {
311
288
  finish([
312
289
  'Setup complete.',
313
- `Instructions: ${configureInstructions ? `${agents.length} configured` : 'skipped'}`,
314
290
  `Skill: ${installSkill ? `installed for ${agents.map(humanize).join(' and ')}` : 'skipped'}`,
315
291
  ].join('\n'));
316
292
  } else out.data(result);
@@ -471,31 +447,55 @@ async function uploadCommand(args, config, out) {
471
447
  out.data({ skill_id: detail.skill_id, slug: detail.slug, domain: detail.domain, version: detail.version.version });
472
448
  }
473
449
 
474
- async function runStart(args, config, out) {
475
- const client = takeOption(args, '--client');
476
- const session = takeOption(args, '--session');
477
- rejectUnknown(args);
478
- if (args.length !== 1 || !client || !session) {
479
- throw new Error('Usage: constraint skills run start <skill> --client <client> --session <session-id>');
450
+ // A run is the posting of a transcript in which a skill was invoked. The user
451
+ // drives reporting: one command, after the session, with the finished
452
+ // transcript. Client and session id come straight from the transcript path —
453
+ // agent clients keep sessions at well-known locations named by session id.
454
+ export function transcriptIdentity(absolute) {
455
+ const basename = path.basename(absolute);
456
+ const stem = basename.replace(/\.(jsonl?|json)$/i, '');
457
+ const uuid = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
458
+ if (absolute.includes(`${path.sep}.codex${path.sep}sessions${path.sep}`) || stem.startsWith('rollout-')) {
459
+ const match = stem.match(uuid);
460
+ return { client: 'codex', session: match ? match[0] : stem };
480
461
  }
481
- const run = await apiRequest(config, '/skill-runs', {
482
- method: 'POST',
483
- ...jsonRequest({ skill: args[0], client, session_id: session }),
484
- });
485
- if (out.json) out.data(run);
486
- else out.line(run.run_id);
462
+ if (absolute.includes(`${path.sep}.claude${path.sep}projects${path.sep}`) || uuid.test(stem)) {
463
+ const match = stem.match(uuid);
464
+ return { client: 'claude-code', session: match ? match[0] : stem };
465
+ }
466
+ return { client: null, session: null };
487
467
  }
488
468
 
489
- async function runComplete(args, config, out) {
469
+ async function reportCommand(args, config, out) {
470
+ const skill = takeOption(args, '--skill');
471
+ const client = takeOption(args, '--client');
472
+ const session = takeOption(args, '--session');
490
473
  rejectUnknown(args);
491
- if (args.length !== 2) throw new Error('Usage: constraint skills run complete <run-id> <transcript-path>');
492
- const transcript = await readTranscript(args[1]);
493
- const run = await apiRequest(config, `/skill-runs/${encodeURIComponent(args[0])}/complete`, {
474
+ if (args.length !== 1 || !skill) {
475
+ throw new Error('Usage: constraint report <transcript-path> --skill <skill> [--client <client>] [--session <session-id>]');
476
+ }
477
+ const transcript = await readTranscript(args[0]);
478
+ const identity = transcriptIdentity(transcript.absolute);
479
+ const resolvedClient = client || identity.client;
480
+ const resolvedSession = session || identity.session;
481
+ if (!resolvedClient || !resolvedSession) {
482
+ throw new Error('Could not tell the client and session from this path. Pass --client and --session.');
483
+ }
484
+ const query = new URLSearchParams({ skill, client: resolvedClient, session: resolvedSession });
485
+ const run = await apiRequest(config, `/skill-reports?${query.toString()}`, {
494
486
  method: 'POST',
495
487
  headers: { 'Content-Type': mediaType(transcript.absolute), 'X-Transcript-Filename': transcript.filename },
496
488
  body: transcript.content,
497
489
  });
498
- out.data(run);
490
+ out.data({
491
+ run_id: run.run_id,
492
+ skill: run.skill_slug,
493
+ version: run.skill_version,
494
+ state: run.state,
495
+ client: run.client,
496
+ session: run.client_session_id,
497
+ transcript_bytes: run.transcript_size,
498
+ });
499
499
  }
500
500
 
501
501
  async function runList(args, config, out) {
@@ -560,11 +560,9 @@ async function runGet(args, config, out) {
560
560
 
561
561
  async function runCommand(args, config, out) {
562
562
  const command = args.shift();
563
- if (command === 'start') return runStart(args, config, out);
564
- if (command === 'complete') return runComplete(args, config, out);
565
563
  if (command === 'list') return runList(args, config, out);
566
564
  if (command === 'get') return runGet(args, config, out);
567
- throw new Error('Expected skills run start, complete, list, or get.');
565
+ throw new Error('Expected skills run list or get. Post a transcript with constraint report.');
568
566
  }
569
567
 
570
568
  async function skillsCommand(args, config, out) {
@@ -597,6 +595,7 @@ export async function main(argv) {
597
595
  if (['login', 'logout', 'whoami'].includes(command)) return authCommand(command, args, config, out);
598
596
  if (command === 'setup') return setupCommand(args, out);
599
597
  if (command === 'doctor') return doctorCommand(args, config, out);
598
+ if (command === 'report') return reportCommand(args, config, out);
600
599
  if (command === 'skills') return skillsCommand(args, config, out);
601
600
  throw new Error(`Unknown command: ${command}`);
602
601
  }
package/src/paths.js CHANGED
@@ -28,18 +28,15 @@ export function agentPaths(agent, { scope = 'global', projectRoot = process.cwd(
28
28
  const selected = normalizeAgent(agent);
29
29
  if (scope === 'project') {
30
30
  if (selected === 'codex') {
31
- return { skills: path.join(projectRoot, '.agents', 'skills'), instructions: null };
31
+ return { skills: path.join(projectRoot, '.agents', 'skills') };
32
32
  }
33
- return { skills: path.join(projectRoot, '.claude', 'skills'), instructions: null };
33
+ return { skills: path.join(projectRoot, '.claude', 'skills') };
34
34
  }
35
35
  if (selected === 'codex') {
36
36
  const root = process.env.CODEX_HOME ? expandPath(process.env.CODEX_HOME) : path.join(os.homedir(), '.codex');
37
- return { instructions: path.join(root, 'AGENTS.md'), skills: path.join(root, 'skills') };
37
+ return { skills: path.join(root, 'skills') };
38
38
  }
39
- return {
40
- instructions: path.join(os.homedir(), '.claude', 'CLAUDE.md'),
41
- skills: path.join(os.homedir(), '.claude', 'skills'),
42
- };
39
+ return { skills: path.join(os.homedir(), '.claude', 'skills') };
43
40
  }
44
41
 
45
42
  async function exists(candidate) {
package/src/setup.js CHANGED
@@ -6,37 +6,6 @@ import { loadConfig, saveConfig } from './config.js';
6
6
  import { packageDigest, readSkillDirectory, writeSkillDirectory } from './files.js';
7
7
  import { agentPaths, normalizeAgent } from './paths.js';
8
8
 
9
- export const BLOCK_START = '<!-- constraint-skills:start -->';
10
- export const BLOCK_END = '<!-- constraint-skills:end -->';
11
-
12
- const BLOCK = `${BLOCK_START}
13
- ## Constraint Skills
14
-
15
- When using the Constraint CLI or invoking any installed Agent Skill, load and
16
- follow the global \`constraint-skills\` companion skill. It contains the CLI
17
- workflow and records governed runs without modifying the invoked skill.
18
- ${BLOCK_END}`;
19
-
20
- export function mergeInstructionBlock(existing) {
21
- const start = existing.indexOf(BLOCK_START);
22
- const end = existing.indexOf(BLOCK_END);
23
- if ((start >= 0) !== (end >= 0)) {
24
- throw new Error('The existing Constraint Skills instruction block is incomplete; fix it before running setup.');
25
- }
26
- if (start >= 0 && end >= start) {
27
- return `${existing.slice(0, start)}${BLOCK}${existing.slice(end + BLOCK_END.length)}`;
28
- }
29
- const separator = existing.length === 0 ? '' : existing.endsWith('\n') ? '\n' : '\n\n';
30
- return `${existing}${separator}${BLOCK}\n`;
31
- }
32
-
33
- async function writeInstructions(file, content, mode) {
34
- await fs.mkdir(path.dirname(file), { recursive: true });
35
- const staging = `${file}.constraint-${process.pid}`;
36
- await fs.writeFile(staging, content, { mode });
37
- await fs.rename(staging, file);
38
- }
39
-
40
9
  async function companionFiles() {
41
10
  const here = path.dirname(fileURLToPath(import.meta.url));
42
11
  return readSkillDirectory(path.resolve(here, '..', 'skills', 'constraint-skills'));
@@ -49,19 +18,6 @@ export function expectedCompanionHash(config, selected, currentHash) {
49
18
  return currentHash && recorded.includes(currentHash) ? currentHash : null;
50
19
  }
51
20
 
52
- async function instructionPlan(selected, paths) {
53
- let existing = '';
54
- let mode = 0o644;
55
- try {
56
- existing = await fs.readFile(paths.instructions, 'utf8');
57
- mode = (await fs.stat(paths.instructions)).mode & 0o777;
58
- } catch (error) {
59
- if (error && error.code !== 'ENOENT') throw error;
60
- }
61
- const content = mergeInstructionBlock(existing);
62
- return { path: paths.instructions, changed: content !== existing, content, mode };
63
- }
64
-
65
21
  async function skillPlan(selected, paths) {
66
22
  const files = await companionFiles();
67
23
  const bundledHash = packageDigest(files);
@@ -89,7 +45,6 @@ export async function setupPlan(agent) {
89
45
  const paths = agentPaths(selected);
90
46
  return {
91
47
  agent: selected,
92
- instructions: await instructionPlan(selected, paths),
93
48
  skill: await skillPlan(selected, paths),
94
49
  };
95
50
  }
@@ -100,15 +55,6 @@ async function recordConfiguredAgent(selected, config = null) {
100
55
  await saveConfig(next);
101
56
  }
102
57
 
103
- export async function applyInstructionSetup(agent) {
104
- const selected = normalizeAgent(agent);
105
- const paths = agentPaths(selected);
106
- const plan = await instructionPlan(selected, paths);
107
- if (plan.changed) await writeInstructions(plan.path, plan.content, plan.mode);
108
- await recordConfiguredAgent(selected);
109
- return { agent: selected, path: plan.path, changed: plan.changed, applied: true };
110
- }
111
-
112
58
  export async function installCompanionSkill(agent) {
113
59
  const selected = normalizeAgent(agent);
114
60
  const paths = agentPaths(selected);
@@ -127,42 +73,25 @@ export async function installCompanionSkill(agent) {
127
73
 
128
74
  export async function setupAgent(agent, {
129
75
  dryRun = false,
130
- yes = false,
131
- confirmChange = async () => false,
132
76
  output = console.log,
133
- instructions = true,
134
- skill = true,
135
77
  } = {}) {
136
78
  const selected = normalizeAgent(agent);
137
79
  const plan = await setupPlan(selected);
138
- output(`${selected}: ${plan.instructions.path}`);
139
80
  if (dryRun) {
140
- if (instructions) output(plan.instructions.content);
141
- if (skill) output(`Install constraint-skills globally at ${plan.skill.path} (${plan.skill.action}).`);
142
- return { agent: selected, instructions: plan.instructions, skill: plan.skill, applied: false };
143
- }
144
- let instructionResult = null;
145
- let skillResult = null;
146
- if (instructions) {
147
- const approved = !plan.instructions.changed || yes || await confirmChange(
148
- `Add the Constraint Skills block to ${plan.instructions.path}?`,
149
- );
150
- if (approved) instructionResult = await applyInstructionSetup(selected);
81
+ output(`Install constraint-skills globally at ${plan.skill.path} (${plan.skill.action}).`);
82
+ return { agent: selected, skill: plan.skill, applied: false };
151
83
  }
152
- if (skill) skillResult = await installCompanionSkill(selected);
153
- return { agent: selected, instructions: instructionResult, skill: skillResult, applied: true };
84
+ const skillResult = await installCompanionSkill(selected);
85
+ return { agent: selected, skill: skillResult, applied: true };
154
86
  }
155
87
 
156
88
  export async function setupStatus(agent) {
157
89
  const selected = normalizeAgent(agent);
158
90
  const paths = agentPaths(selected);
159
91
  try {
160
- const [instructions, skill] = await Promise.all([
161
- fs.readFile(paths.instructions, 'utf8'),
162
- fs.stat(path.join(paths.skills, 'constraint-skills', 'SKILL.md')),
163
- ]);
164
- return { agent: selected, instructions: instructions.includes(BLOCK_START), skill: skill.isFile() };
92
+ const skill = await fs.stat(path.join(paths.skills, 'constraint-skills', 'SKILL.md'));
93
+ return { agent: selected, skill: skill.isFile() };
165
94
  } catch {
166
- return { agent: selected, instructions: false, skill: false };
95
+ return { agent: selected, skill: false };
167
96
  }
168
97
  }