@constraint/cli 0.3.6 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constraint/cli",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Install, publish, and report Agent Skills with Constraint",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  uploadSkill,
37
37
  } from './skills.js';
38
38
 
39
- const VERSION = '0.3.6';
39
+ const VERSION = '0.3.7';
40
40
 
41
41
  const HELP = `Constraint Skills CLI
42
42
 
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({