@constraint/cli 0.3.4 → 0.3.6

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.4",
3
+ "version": "0.3.6",
4
4
  "description": "Install, publish, and report Agent Skills with Constraint",
5
5
  "type": "module",
6
6
  "bin": {
@@ -49,11 +49,16 @@ constraint skills run complete <run-id> <actual-transcript-path>
49
49
  constraint skills run list
50
50
  constraint skills run list --team <domain> --user <workos-user-id> --skill <skill>
51
51
  constraint skills run get <run-id>
52
- constraint skills run get <run-id> --raw --output transcript.jsonl
52
+ constraint skills run get <run-id> --raw
53
53
  ```
54
54
 
55
55
  Repeat `--team`, `--user`, or `--skill` to request subsets. Review visibility is permission-controlled.
56
56
 
57
+ `run get` never prints a transcript to stdout: it writes the run JSON (or the
58
+ original transcript file with `--raw`) under the system temp directory and
59
+ prints a one-object summary containing the file `path`. Read the file from
60
+ that path. Pass `--output <path>` to choose the destination (single run only).
61
+
57
62
  ## Automation and recovery
58
63
 
59
64
  Global flags: `--json`, `--quiet`, `--no-color`, `--help`, and `--version`. Installer workflows also accept `--yes`.
package/src/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import fs from 'node:fs/promises';
2
+ import os from 'node:os';
2
3
  import path from 'node:path';
3
4
  import readline from 'node:readline/promises';
4
5
 
@@ -35,7 +36,7 @@ import {
35
36
  uploadSkill,
36
37
  } from './skills.js';
37
38
 
38
- const VERSION = '0.3.4';
39
+ const VERSION = '0.3.6';
39
40
 
40
41
  const HELP = `Constraint Skills CLI
41
42
 
@@ -56,7 +57,7 @@ Usage:
56
57
  constraint skills run start <skill> --client <client> --session <session-id>
57
58
  constraint skills run complete <run-id> <transcript-path>
58
59
  constraint skills run list [--team <team>]... [--user <user>]... [--skill <skill>]...
59
- constraint skills run get <run-id>... [--raw] [--output <path>]
60
+ constraint skills run get <run-id>... [--raw] [--output <path>] (always writes a file, prints summary + path)
60
61
 
61
62
  Global options: --json --quiet --no-color --help --version`;
62
63
 
@@ -511,23 +512,50 @@ async function runList(args, config, out) {
511
512
  out.data(await apiRequest(config, `/skill-runs?${query.toString()}`));
512
513
  }
513
514
 
515
+ // Run payloads are whole session transcripts — megabytes of JSON that would
516
+ // flood a terminal or an agent's context. runGet therefore always writes to a
517
+ // file and prints only a summary with the path; there is no stdout mode.
518
+ async function runGetDestination(output, runId, extension) {
519
+ if (output) return path.resolve(output);
520
+ const directory = path.join(os.tmpdir(), 'constraint', 'runs');
521
+ await fs.mkdir(directory, { recursive: true });
522
+ return path.join(directory, `${runId}${extension}`);
523
+ }
524
+
514
525
  async function runGet(args, config, out) {
515
526
  const raw = takeFlag(args, '--raw');
516
527
  const output = takeOption(args, '--output');
517
528
  rejectUnknown(args);
518
529
  if (!args.length) throw new Error('Usage: constraint skills run get <run-id>...');
530
+ if (output && args.length !== 1) throw new Error('--output accepts exactly one run ID');
519
531
  if (raw && args.length !== 1) throw new Error('--raw accepts exactly one run ID');
520
532
  if (raw) {
521
533
  const response = await apiRequest(config, `/skill-runs/${encodeURIComponent(args[0])}/transcript`, { rawResponse: true });
522
534
  const content = Buffer.from(await response.arrayBuffer());
523
- if (output) await fs.writeFile(path.resolve(output), content);
524
- else process.stdout.write(content);
535
+ const destination = await runGetDestination(output, args[0], '.transcript');
536
+ await fs.writeFile(destination, content);
537
+ out.data({ run_id: args[0], bytes: content.length, path: destination });
525
538
  return;
526
539
  }
527
- const runs = await Promise.all(args.map((runId) => apiRequest(config, `/skill-runs/${encodeURIComponent(runId)}`)));
528
- const value = runs.length === 1 ? runs[0] : runs;
529
- if (output) await fs.writeFile(path.resolve(output), `${JSON.stringify(value, null, 2)}\n`);
530
- else out.data(value);
540
+ const summaries = [];
541
+ for (const runId of args) {
542
+ const run = await apiRequest(config, `/skill-runs/${encodeURIComponent(runId)}`);
543
+ const destination = await runGetDestination(output, runId, '.json');
544
+ await fs.writeFile(destination, `${JSON.stringify(run, null, 2)}\n`);
545
+ const events = Array.isArray(run.normalized_events) ? run.normalized_events : [];
546
+ summaries.push({
547
+ run_id: run.run_id,
548
+ skill: run.skill_slug,
549
+ version: run.skill_version,
550
+ state: run.state,
551
+ client: run.client,
552
+ completed_at: run.completed_at,
553
+ events: events.length,
554
+ events_with_text: events.filter((event) => event.content).length,
555
+ path: destination,
556
+ });
557
+ }
558
+ out.data(summaries.length === 1 ? summaries[0] : summaries);
531
559
  }
532
560
 
533
561
  async function runCommand(args, config, out) {
package/src/config.js CHANGED
@@ -28,6 +28,9 @@ export async function loadConfig() {
28
28
  return {
29
29
  apiUrl: (process.env.CONSTRAINT_API_URL || saved.apiUrl || DEFAULT_API_URL).replace(/\/$/, ''),
30
30
  agents: Array.isArray(saved.agents) ? saved.agents : Array.isArray(saved.clients) ? saved.clients : [],
31
+ companion_hashes: saved.companion_hashes && typeof saved.companion_hashes === 'object'
32
+ ? saved.companion_hashes
33
+ : {},
31
34
  };
32
35
  }
33
36
 
package/src/setup.js CHANGED
@@ -42,6 +42,13 @@ async function companionFiles() {
42
42
  return readSkillDirectory(path.resolve(here, '..', 'skills', 'constraint-skills'));
43
43
  }
44
44
 
45
+ export function expectedCompanionHash(config, selected, currentHash) {
46
+ const direct = config.companion_hashes?.[selected] || null;
47
+ if (direct) return direct;
48
+ const recorded = Object.values(config.companion_hashes || {});
49
+ return currentHash && recorded.includes(currentHash) ? currentHash : null;
50
+ }
51
+
45
52
  async function instructionPlan(selected, paths) {
46
53
  let existing = '';
47
54
  let mode = 0o644;
@@ -59,7 +66,6 @@ async function skillPlan(selected, paths) {
59
66
  const files = await companionFiles();
60
67
  const bundledHash = packageDigest(files);
61
68
  const config = await loadConfig();
62
- const expectedHash = config.companion_hashes?.[selected] || null;
63
69
  const target = path.join(paths.skills, 'constraint-skills');
64
70
  let currentHash = null;
65
71
  try {
@@ -67,6 +73,7 @@ async function skillPlan(selected, paths) {
67
73
  } catch (error) {
68
74
  if (error && error.code !== 'ENOENT') throw error;
69
75
  }
76
+ const expectedHash = expectedCompanionHash(config, selected, currentHash);
70
77
  const action = currentHash === bundledHash
71
78
  ? 'current'
72
79
  : currentHash === null
@@ -109,8 +116,9 @@ export async function installCompanionSkill(agent) {
109
116
  const config = await loadConfig();
110
117
  config.companion_hashes ||= {};
111
118
  const target = path.join(paths.skills, 'constraint-skills');
119
+ const plan = await skillPlan(selected, paths);
112
120
  const result = await writeSkillDirectory(target, files, {
113
- expectedHash: config.companion_hashes[selected] || null,
121
+ expectedHash: plan.expectedHash,
114
122
  });
115
123
  config.companion_hashes[selected] = result.hash;
116
124
  await recordConfiguredAgent(selected, config);