@constraint/cli 0.3.5 → 0.3.7
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 +1 -1
- package/skills/constraint-skills/references/commands.md +6 -1
- package/src/cli.js +36 -8
- package/src/skills.js +30 -0
package/package.json
CHANGED
|
@@ -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
|
|
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.
|
|
39
|
+
const VERSION = '0.3.7';
|
|
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
|
-
|
|
524
|
-
|
|
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
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
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/skills.js
CHANGED
|
@@ -45,6 +45,35 @@ async function loadLock(scope, projectRoot) {
|
|
|
45
45
|
return scope === 'global' ? loadInstallations() : loadProjectInstallations(projectRoot);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Installed skill files are per-machine (symlinks into the canonical store);
|
|
49
|
+
// only skills-lock.json belongs in version control. Keep the project's
|
|
50
|
+
// .gitignore covering the generated paths, npm-style.
|
|
51
|
+
const GITIGNORE_ENTRIES = ['.constraint/', '.claude/skills/', '.agents/skills/'];
|
|
52
|
+
|
|
53
|
+
async function ensureProjectGitignore(projectRoot) {
|
|
54
|
+
try {
|
|
55
|
+
await fs.stat(path.join(projectRoot, '.git'));
|
|
56
|
+
} catch {
|
|
57
|
+
return; // not a git repository; nothing to manage
|
|
58
|
+
}
|
|
59
|
+
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
60
|
+
let existing = '';
|
|
61
|
+
try {
|
|
62
|
+
existing = await fs.readFile(gitignorePath, 'utf8');
|
|
63
|
+
} catch {
|
|
64
|
+
existing = '';
|
|
65
|
+
}
|
|
66
|
+
const lines = new Set(existing.split('\n').map((line) => line.trim()));
|
|
67
|
+
const missing = GITIGNORE_ENTRIES.filter(
|
|
68
|
+
(entry) => !lines.has(entry) && !lines.has(entry.slice(0, -1)),
|
|
69
|
+
);
|
|
70
|
+
if (!missing.length) return;
|
|
71
|
+
const block = `${missing.join('\n')}\n`;
|
|
72
|
+
const prefix = existing && !existing.endsWith('\n') ? '\n' : '';
|
|
73
|
+
const header = existing ? '\n# Constraint skill installs (commit skills-lock.json instead)\n' : '# Constraint skill installs (commit skills-lock.json instead)\n';
|
|
74
|
+
await fs.writeFile(gitignorePath, `${existing}${prefix}${header}${block}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
48
77
|
async function saveLock(scope, projectRoot, value) {
|
|
49
78
|
if (scope === 'global') return saveInstallations(value);
|
|
50
79
|
return saveProjectInstallations(projectRoot, value);
|
|
@@ -96,6 +125,7 @@ export async function installSkill(config, identifier, {
|
|
|
96
125
|
const lock = await loadLock(scope, root);
|
|
97
126
|
lock.lockfile_version = 1;
|
|
98
127
|
lock.installations ||= {};
|
|
128
|
+
if (scope === 'project') await ensureProjectGitignore(root);
|
|
99
129
|
const packageFiles = filesFromDetail(detail);
|
|
100
130
|
const contentHash = packageDigest(packageFiles);
|
|
101
131
|
const canonical = copy === true ? null : canonicalSkillPath({
|