@mc-and-his-agents/loom-installer 0.1.53 → 0.1.54

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 (45) hide show
  1. package/dist/src/codex.js +9 -77
  2. package/dist/src/index.js +1 -1
  3. package/dist/test/installer.test.js +15 -28
  4. package/package.json +1 -1
  5. package/payload/manifest.json +82 -82
  6. package/payload/plugin/loom/skills/README.md +3 -1
  7. package/payload/plugin/loom/skills/README.zh-CN.md +3 -1
  8. package/payload/plugin/loom/skills/distribution-and-adapter-contract.md +2 -2
  9. package/payload/plugin/loom/skills/shared/scripts/loom_check.py +16 -8
  10. package/payload/skills/loom-adopt/.loom-runtime/README.md +3 -1
  11. package/payload/skills/loom-adopt/.loom-runtime/README.zh-CN.md +3 -1
  12. package/payload/skills/loom-adopt/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  13. package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py +16 -8
  14. package/payload/skills/loom-handoff/.loom-runtime/README.md +3 -1
  15. package/payload/skills/loom-handoff/.loom-runtime/README.zh-CN.md +3 -1
  16. package/payload/skills/loom-handoff/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  17. package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py +16 -8
  18. package/payload/skills/loom-init/.loom-runtime/README.md +3 -1
  19. package/payload/skills/loom-init/.loom-runtime/README.zh-CN.md +3 -1
  20. package/payload/skills/loom-init/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  21. package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_check.py +16 -8
  22. package/payload/skills/loom-merge-ready/.loom-runtime/README.md +3 -1
  23. package/payload/skills/loom-merge-ready/.loom-runtime/README.zh-CN.md +3 -1
  24. package/payload/skills/loom-merge-ready/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  25. package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py +16 -8
  26. package/payload/skills/loom-pre-review/.loom-runtime/README.md +3 -1
  27. package/payload/skills/loom-pre-review/.loom-runtime/README.zh-CN.md +3 -1
  28. package/payload/skills/loom-pre-review/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  29. package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py +16 -8
  30. package/payload/skills/loom-resume/.loom-runtime/README.md +3 -1
  31. package/payload/skills/loom-resume/.loom-runtime/README.zh-CN.md +3 -1
  32. package/payload/skills/loom-resume/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  33. package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py +16 -8
  34. package/payload/skills/loom-retire/.loom-runtime/README.md +3 -1
  35. package/payload/skills/loom-retire/.loom-runtime/README.zh-CN.md +3 -1
  36. package/payload/skills/loom-retire/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  37. package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py +16 -8
  38. package/payload/skills/loom-review/.loom-runtime/README.md +3 -1
  39. package/payload/skills/loom-review/.loom-runtime/README.zh-CN.md +3 -1
  40. package/payload/skills/loom-review/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  41. package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_check.py +16 -8
  42. package/payload/skills/loom-spec-review/.loom-runtime/README.md +3 -1
  43. package/payload/skills/loom-spec-review/.loom-runtime/README.zh-CN.md +3 -1
  44. package/payload/skills/loom-spec-review/.loom-runtime/distribution-and-adapter-contract.md +2 -2
  45. package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py +16 -8
package/dist/src/codex.js CHANGED
@@ -1,4 +1,3 @@
1
- import { readFileSync, writeFileSync } from 'node:fs';
2
1
  import { join } from 'node:path';
3
2
  import { InstallerError, copyTree, dirExists, ensureDirectory, fileExists, readJson, replaceTree, writeJson } from './utils.js';
4
3
  function codexMarketplacePath(targetRoot) {
@@ -54,70 +53,6 @@ function ensureMarketplace(targetRoot, force) {
54
53
  writeJson(marketplacePath, marketplace);
55
54
  return [marketplacePath];
56
55
  }
57
- function parseSkillBlocks(content) {
58
- const lines = content.split('\n');
59
- const blocks = [];
60
- for (let index = 0; index < lines.length; index += 1) {
61
- if (lines[index].trim() !== '[[skills.config]]') {
62
- continue;
63
- }
64
- const blockLines = [lines[index]];
65
- index += 1;
66
- while (index < lines.length && !lines[index].trim().startsWith('[[')) {
67
- blockLines.push(lines[index]);
68
- index += 1;
69
- }
70
- index -= 1;
71
- const raw = blockLines.join('\n');
72
- const pathMatch = raw.match(/^path\s*=\s*"([^"]+)"/m);
73
- const enabledMatch = raw.match(/^enabled\s*=\s*(true|false)/m);
74
- blocks.push({
75
- raw,
76
- path: pathMatch?.[1] ?? null,
77
- enabled: enabledMatch ? enabledMatch[1] === 'true' : null,
78
- });
79
- }
80
- return blocks;
81
- }
82
- function upsertSkillBlock(configPath, desiredSkillPath, skillId, force) {
83
- const content = fileExists(configPath) ? readFileSync(configPath, 'utf8') : '';
84
- const blocks = parseSkillBlocks(content);
85
- const skillDir = skillId.startsWith('loom-') ? skillId : `loom-${skillId}`;
86
- const conflicting = blocks.filter((block) => {
87
- if (!block.path) {
88
- return false;
89
- }
90
- return block.path.endsWith(`/${skillDir}/SKILL.md`) && block.path !== desiredSkillPath;
91
- });
92
- if (conflicting.length > 0 && !force) {
93
- throw new InstallerError(`Codex already has ${skillId} from a different path`, `Codex already has ${skillId} from a different path`);
94
- }
95
- let nextContent = content;
96
- for (const block of conflicting) {
97
- nextContent = nextContent.replace(`${block.raw}\n`, '').replace(block.raw, '');
98
- }
99
- const existingBlock = parseSkillBlocks(nextContent).find((block) => block.path === desiredSkillPath);
100
- if (existingBlock) {
101
- const enabledBlock = existingBlock.raw.match(/^enabled\s*=\s*false/m)
102
- ? existingBlock.raw.replace(/^enabled\s*=\s*false/m, 'enabled = true')
103
- : existingBlock.enabled === null
104
- ? `${existingBlock.raw}\nenabled = true`
105
- : existingBlock.raw;
106
- nextContent = nextContent.replace(existingBlock.raw, enabledBlock);
107
- }
108
- else {
109
- if (nextContent && !nextContent.endsWith('\n')) {
110
- nextContent += '\n';
111
- }
112
- nextContent += `\n[[skills.config]]\npath = ${JSON.stringify(desiredSkillPath)}\nenabled = true\n`;
113
- }
114
- ensureDirectory(join(configPath, '..'));
115
- writeFileSync(configPath, nextContent.replace(/^\n+/, ''), 'utf8');
116
- }
117
- function verifySkillEnabled(configPath, desiredSkillPath) {
118
- const content = fileExists(configPath) ? readFileSync(configPath, 'utf8') : '';
119
- return parseSkillBlocks(content).some((block) => block.path === desiredSkillPath && block.enabled !== false);
120
- }
121
56
  export function installCodexPlugin(targetRoot, packageRoot, manifest, force) {
122
57
  const pluginSource = join(packageRoot, 'payload', manifest.plugin.relative_path);
123
58
  const pluginTarget = join(targetRoot, 'plugins', 'loom');
@@ -155,37 +90,34 @@ export function installCodexPlugin(targetRoot, packageRoot, manifest, force) {
155
90
  fail_closed_reason: null,
156
91
  };
157
92
  }
158
- export function installCodexSkill(env, packageRoot, skill, force) {
93
+ export function installCodexSkill(_env, targetRoot, packageRoot, skill, force) {
159
94
  const skillDirName = skill.id.startsWith('loom-') ? skill.id : `loom-${skill.id}`;
160
95
  const sourceDir = join(packageRoot, 'payload', skill.relative_path);
161
- const targetDir = join(env.codexHome, 'skills', skillDirName);
96
+ const targetDir = join(targetRoot, '.agents', 'skills', skillDirName);
162
97
  const skillMarkdownPath = join(targetDir, 'SKILL.md');
163
- const configPath = join(env.codexHome, 'config.toml');
164
- ensureDirectory(join(env.codexHome, 'skills'));
98
+ ensureDirectory(join(targetRoot, '.agents', 'skills'));
165
99
  if (dirExists(targetDir)) {
166
100
  if (!force && !fileExists(skillMarkdownPath)) {
167
- throw new InstallerError(`existing Codex skill directory is not Loom-managed: ${targetDir}`, `refusing to take over non-Loom Codex skill directory: ${targetDir}`);
101
+ throw new InstallerError(`target already contains .agents/skills/${skillDirName} but it is not a Loom skill: ${targetDir}`, `refusing to take over non-Loom repo skill directory: ${targetDir}`);
168
102
  }
169
103
  replaceTree(sourceDir, targetDir);
170
104
  }
171
105
  else {
172
106
  copyTree(sourceDir, targetDir, true);
173
107
  }
174
- ensureDirectory(env.codexHome);
175
- upsertSkillBlock(configPath, skillMarkdownPath, skill.id, force);
176
- if (!verifySkillEnabled(configPath, skillMarkdownPath)) {
177
- throw new InstallerError(`Codex config did not enable ${skill.id}`);
108
+ if (!fileExists(skillMarkdownPath)) {
109
+ throw new InstallerError(`Codex repo skill install is missing SKILL.md: ${skillMarkdownPath}`);
178
110
  }
179
111
  return {
180
112
  mode: 'skill',
181
113
  host: 'codex',
182
114
  status: 'installed',
183
- installed_paths: [targetDir, configPath],
115
+ installed_paths: [targetDir],
184
116
  verification: [
185
117
  `verified skill payload at ${targetDir}`,
186
- `verified skills.config entry for ${skill.id}`,
118
+ `verified repo skill discovery path at ${skillMarkdownPath}`,
187
119
  ],
188
- warnings: ['Codex single-skill install exposes only the named skill, not the full Loom plugin surface.'],
120
+ warnings: ['Codex repo-scoped single-skill install exposes only the named skill, not the full Loom plugin surface.'],
189
121
  fail_closed_reason: null,
190
122
  };
191
123
  }
package/dist/src/index.js CHANGED
@@ -170,6 +170,6 @@ function installForHost(input) {
170
170
  }
171
171
  const skill = resolveSkillRecord(manifest, parsed.skillId ?? '');
172
172
  return host === 'codex'
173
- ? installCodexSkill(env, packageRoot, skill, parsed.options.force)
173
+ ? installCodexSkill(env, targetRoot, packageRoot, skill, parsed.options.force)
174
174
  : installClaudeSkill(targetRoot, packageRoot, skill, parsed.options.force);
175
175
  }
@@ -173,13 +173,12 @@ test('codex plugin install lets --force take over conflicting marketplace entry'
173
173
  assert.equal(result.mode, 'plugin');
174
174
  assert.equal(marketplace.plugins[0].source.path, './plugins/loom');
175
175
  });
176
- test('codex skill install writes skills.config entry', () => {
176
+ test('codex skill install writes repo-scoped .agents skill', () => {
177
177
  const base = fixtureRoot();
178
178
  const envSource = prepareEnv(base);
179
179
  mkdirSync(envSource.CODEX_HOME, { recursive: true });
180
180
  const repoRoot = join(base, 'repo');
181
181
  mkdirSync(repoRoot, { recursive: true });
182
- writeFileSync(join(envSource.CODEX_HOME, 'config.toml'), 'model = "gpt-5"\n', 'utf8');
183
182
  const parsed = {
184
183
  mode: 'skill',
185
184
  skillId: 'loom-review',
@@ -191,26 +190,19 @@ test('codex skill install writes skills.config entry', () => {
191
190
  },
192
191
  };
193
192
  const result = runInstaller(parsed, envSource, packageRoot());
194
- const config = readFileSync(join(envSource.CODEX_HOME, 'config.toml'), 'utf8');
193
+ const skillPath = join(repoRoot, '.agents', 'skills', 'loom-review', 'SKILL.md');
195
194
  assert.equal(result.mode, 'skill');
196
- assert.match(config, /\[\[skills\.config\]\]/);
197
- assert.match(config, /loom-review\/SKILL\.md/);
198
- assert.match(config, /enabled = true/);
195
+ assert.equal(existsSync(skillPath), true);
196
+ assert.equal(existsSync(join(envSource.CODEX_HOME, 'config.toml')), false);
199
197
  });
200
- test('codex skill install fails closed on conflicting skills.config entry without force', () => {
198
+ test('codex skill install fails closed on conflicting repo skill directory without force', () => {
201
199
  const base = fixtureRoot();
202
200
  const envSource = prepareEnv(base);
203
201
  mkdirSync(envSource.CODEX_HOME, { recursive: true });
204
202
  const repoRoot = join(base, 'repo');
205
203
  mkdirSync(repoRoot, { recursive: true });
206
- writeFileSync(join(envSource.CODEX_HOME, 'config.toml'), [
207
- 'model = "gpt-5"',
208
- '',
209
- '[[skills.config]]',
210
- 'path = "/tmp/other/loom-review/SKILL.md"',
211
- 'enabled = true',
212
- '',
213
- ].join('\n'), 'utf8');
204
+ mkdirSync(join(repoRoot, '.agents', 'skills', 'loom-review'), { recursive: true });
205
+ writeFileSync(join(repoRoot, '.agents', 'skills', 'loom-review', 'README.md'), '# Not a Loom skill\n', 'utf8');
214
206
  const parsed = {
215
207
  mode: 'skill',
216
208
  skillId: 'loom-review',
@@ -221,22 +213,16 @@ test('codex skill install fails closed on conflicting skills.config entry withou
221
213
  json: false,
222
214
  },
223
215
  };
224
- assert.throws(() => runInstaller(parsed, envSource, packageRoot()), /already has loom-review from a different path/);
216
+ assert.throws(() => runInstaller(parsed, envSource, packageRoot()), /not a Loom skill/);
225
217
  });
226
- test('codex skill install lets --force take over conflicting skills.config entry', () => {
218
+ test('codex skill install lets --force take over conflicting repo skill directory', () => {
227
219
  const base = fixtureRoot();
228
220
  const envSource = prepareEnv(base);
229
221
  mkdirSync(envSource.CODEX_HOME, { recursive: true });
230
222
  const repoRoot = join(base, 'repo');
231
223
  mkdirSync(repoRoot, { recursive: true });
232
- writeFileSync(join(envSource.CODEX_HOME, 'config.toml'), [
233
- 'model = "gpt-5"',
234
- '',
235
- '[[skills.config]]',
236
- 'path = "/tmp/other/loom-review/SKILL.md"',
237
- 'enabled = true',
238
- '',
239
- ].join('\n'), 'utf8');
224
+ mkdirSync(join(repoRoot, '.agents', 'skills', 'loom-review'), { recursive: true });
225
+ writeFileSync(join(repoRoot, '.agents', 'skills', 'loom-review', 'README.md'), '# Not a Loom skill\n', 'utf8');
240
226
  const parsed = {
241
227
  mode: 'skill',
242
228
  skillId: 'loom-review',
@@ -248,10 +234,10 @@ test('codex skill install lets --force take over conflicting skills.config entry
248
234
  },
249
235
  };
250
236
  const result = runInstaller(parsed, envSource, packageRoot());
251
- const config = readFileSync(join(envSource.CODEX_HOME, 'config.toml'), 'utf8');
237
+ const skillPath = join(repoRoot, '.agents', 'skills', 'loom-review', 'SKILL.md');
252
238
  assert.equal(result.mode, 'skill');
253
- assert.doesNotMatch(config, /\/tmp\/other\/loom-review\/SKILL\.md/);
254
- assert.match(config, /loom-review\/SKILL\.md/);
239
+ assert.equal(existsSync(skillPath), true);
240
+ assert.equal(existsSync(join(repoRoot, '.agents', 'skills', 'loom-review', 'README.md')), false);
255
241
  });
256
242
  test('single-skill installs stay scoped to the named skill for codex', () => {
257
243
  const base = fixtureRoot();
@@ -272,6 +258,7 @@ test('single-skill installs stay scoped to the named skill for codex', () => {
272
258
  const result = runInstaller(parsed, envSource, packageRoot());
273
259
  assert.equal(result.mode, 'skill');
274
260
  assert.match(result.warnings[0] ?? '', /only the named skill, not the full Loom plugin surface/);
261
+ assert.equal(existsSync(join(repoRoot, '.agents', 'skills', 'loom-init', 'SKILL.md')), true);
275
262
  assert.equal(existsSync(join(repoRoot, 'plugins', 'loom')), false);
276
263
  assert.equal(existsSync(join(repoRoot, '.agents', 'plugins', 'marketplace.json')), false);
277
264
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mc-and-his-agents/loom-installer",
3
- "version": "0.1.53",
3
+ "version": "0.1.54",
4
4
  "description": "Node installer for Loom plugin and single-skill installation surfaces.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,9 +2,9 @@
2
2
  "schema_version": "loom-installer-payload/v1",
3
3
  "loom_version": "0.4.0",
4
4
  "source_repository": "https://github.com/MC-and-his-Agents/Loom",
5
- "source_commit": "544fa797c5cb4ddc5754050239ef3fb22e136726",
5
+ "source_commit": "15fbcf031d7bbd7ec6090ebf7ab1edbcb936d79e",
6
6
  "source_ref": "main",
7
- "built_at": "2026-04-28T16:53:46+08:00",
7
+ "built_at": "2026-04-28T18:00:53+08:00",
8
8
  "runtime": {
9
9
  "python_minimum": "3.10",
10
10
  "python_recommended": "3.11+"
@@ -78,8 +78,8 @@
78
78
  },
79
79
  {
80
80
  "path": "plugin/loom/skills/distribution-and-adapter-contract.md",
81
- "bytes": 15232,
82
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
81
+ "bytes": 15291,
82
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
83
83
  },
84
84
  {
85
85
  "path": "plugin/loom/skills/install-layout.json",
@@ -363,13 +363,13 @@
363
363
  },
364
364
  {
365
365
  "path": "plugin/loom/skills/README.md",
366
- "bytes": 4330,
367
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
366
+ "bytes": 4394,
367
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
368
368
  },
369
369
  {
370
370
  "path": "plugin/loom/skills/README.zh-CN.md",
371
- "bytes": 3986,
372
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
371
+ "bytes": 4050,
372
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
373
373
  },
374
374
  {
375
375
  "path": "plugin/loom/skills/registry.json",
@@ -633,8 +633,8 @@
633
633
  },
634
634
  {
635
635
  "path": "plugin/loom/skills/shared/scripts/loom_check.py",
636
- "bytes": 378826,
637
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
636
+ "bytes": 379162,
637
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
638
638
  },
639
639
  {
640
640
  "path": "plugin/loom/skills/shared/scripts/loom_flow.py",
@@ -668,8 +668,8 @@
668
668
  },
669
669
  {
670
670
  "path": "skills/loom-adopt/.loom-runtime/distribution-and-adapter-contract.md",
671
- "bytes": 15232,
672
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
671
+ "bytes": 15291,
672
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
673
673
  },
674
674
  {
675
675
  "path": "skills/loom-adopt/.loom-runtime/install-layout.json",
@@ -953,13 +953,13 @@
953
953
  },
954
954
  {
955
955
  "path": "skills/loom-adopt/.loom-runtime/README.md",
956
- "bytes": 4330,
957
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
956
+ "bytes": 4394,
957
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
958
958
  },
959
959
  {
960
960
  "path": "skills/loom-adopt/.loom-runtime/README.zh-CN.md",
961
- "bytes": 3986,
962
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
961
+ "bytes": 4050,
962
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
963
963
  },
964
964
  {
965
965
  "path": "skills/loom-adopt/.loom-runtime/registry.json",
@@ -1223,8 +1223,8 @@
1223
1223
  },
1224
1224
  {
1225
1225
  "path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py",
1226
- "bytes": 378826,
1227
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
1226
+ "bytes": 379162,
1227
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
1228
1228
  },
1229
1229
  {
1230
1230
  "path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py",
@@ -1288,8 +1288,8 @@
1288
1288
  },
1289
1289
  {
1290
1290
  "path": "skills/loom-handoff/.loom-runtime/distribution-and-adapter-contract.md",
1291
- "bytes": 15232,
1292
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
1291
+ "bytes": 15291,
1292
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
1293
1293
  },
1294
1294
  {
1295
1295
  "path": "skills/loom-handoff/.loom-runtime/install-layout.json",
@@ -1573,13 +1573,13 @@
1573
1573
  },
1574
1574
  {
1575
1575
  "path": "skills/loom-handoff/.loom-runtime/README.md",
1576
- "bytes": 4330,
1577
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
1576
+ "bytes": 4394,
1577
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
1578
1578
  },
1579
1579
  {
1580
1580
  "path": "skills/loom-handoff/.loom-runtime/README.zh-CN.md",
1581
- "bytes": 3986,
1582
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
1581
+ "bytes": 4050,
1582
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
1583
1583
  },
1584
1584
  {
1585
1585
  "path": "skills/loom-handoff/.loom-runtime/registry.json",
@@ -1843,8 +1843,8 @@
1843
1843
  },
1844
1844
  {
1845
1845
  "path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py",
1846
- "bytes": 378826,
1847
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
1846
+ "bytes": 379162,
1847
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
1848
1848
  },
1849
1849
  {
1850
1850
  "path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py",
@@ -1908,8 +1908,8 @@
1908
1908
  },
1909
1909
  {
1910
1910
  "path": "skills/loom-init/.loom-runtime/distribution-and-adapter-contract.md",
1911
- "bytes": 15232,
1912
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
1911
+ "bytes": 15291,
1912
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
1913
1913
  },
1914
1914
  {
1915
1915
  "path": "skills/loom-init/.loom-runtime/install-layout.json",
@@ -2193,13 +2193,13 @@
2193
2193
  },
2194
2194
  {
2195
2195
  "path": "skills/loom-init/.loom-runtime/README.md",
2196
- "bytes": 4330,
2197
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
2196
+ "bytes": 4394,
2197
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
2198
2198
  },
2199
2199
  {
2200
2200
  "path": "skills/loom-init/.loom-runtime/README.zh-CN.md",
2201
- "bytes": 3986,
2202
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
2201
+ "bytes": 4050,
2202
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
2203
2203
  },
2204
2204
  {
2205
2205
  "path": "skills/loom-init/.loom-runtime/registry.json",
@@ -2463,8 +2463,8 @@
2463
2463
  },
2464
2464
  {
2465
2465
  "path": "skills/loom-init/.loom-runtime/shared/scripts/loom_check.py",
2466
- "bytes": 378826,
2467
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
2466
+ "bytes": 379162,
2467
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
2468
2468
  },
2469
2469
  {
2470
2470
  "path": "skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py",
@@ -2533,8 +2533,8 @@
2533
2533
  },
2534
2534
  {
2535
2535
  "path": "skills/loom-merge-ready/.loom-runtime/distribution-and-adapter-contract.md",
2536
- "bytes": 15232,
2537
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
2536
+ "bytes": 15291,
2537
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
2538
2538
  },
2539
2539
  {
2540
2540
  "path": "skills/loom-merge-ready/.loom-runtime/install-layout.json",
@@ -2818,13 +2818,13 @@
2818
2818
  },
2819
2819
  {
2820
2820
  "path": "skills/loom-merge-ready/.loom-runtime/README.md",
2821
- "bytes": 4330,
2822
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
2821
+ "bytes": 4394,
2822
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
2823
2823
  },
2824
2824
  {
2825
2825
  "path": "skills/loom-merge-ready/.loom-runtime/README.zh-CN.md",
2826
- "bytes": 3986,
2827
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
2826
+ "bytes": 4050,
2827
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
2828
2828
  },
2829
2829
  {
2830
2830
  "path": "skills/loom-merge-ready/.loom-runtime/registry.json",
@@ -3088,8 +3088,8 @@
3088
3088
  },
3089
3089
  {
3090
3090
  "path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py",
3091
- "bytes": 378826,
3092
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
3091
+ "bytes": 379162,
3092
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
3093
3093
  },
3094
3094
  {
3095
3095
  "path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py",
@@ -3153,8 +3153,8 @@
3153
3153
  },
3154
3154
  {
3155
3155
  "path": "skills/loom-pre-review/.loom-runtime/distribution-and-adapter-contract.md",
3156
- "bytes": 15232,
3157
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
3156
+ "bytes": 15291,
3157
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
3158
3158
  },
3159
3159
  {
3160
3160
  "path": "skills/loom-pre-review/.loom-runtime/install-layout.json",
@@ -3438,13 +3438,13 @@
3438
3438
  },
3439
3439
  {
3440
3440
  "path": "skills/loom-pre-review/.loom-runtime/README.md",
3441
- "bytes": 4330,
3442
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
3441
+ "bytes": 4394,
3442
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
3443
3443
  },
3444
3444
  {
3445
3445
  "path": "skills/loom-pre-review/.loom-runtime/README.zh-CN.md",
3446
- "bytes": 3986,
3447
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
3446
+ "bytes": 4050,
3447
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
3448
3448
  },
3449
3449
  {
3450
3450
  "path": "skills/loom-pre-review/.loom-runtime/registry.json",
@@ -3708,8 +3708,8 @@
3708
3708
  },
3709
3709
  {
3710
3710
  "path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py",
3711
- "bytes": 378826,
3712
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
3711
+ "bytes": 379162,
3712
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
3713
3713
  },
3714
3714
  {
3715
3715
  "path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py",
@@ -3773,8 +3773,8 @@
3773
3773
  },
3774
3774
  {
3775
3775
  "path": "skills/loom-resume/.loom-runtime/distribution-and-adapter-contract.md",
3776
- "bytes": 15232,
3777
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
3776
+ "bytes": 15291,
3777
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
3778
3778
  },
3779
3779
  {
3780
3780
  "path": "skills/loom-resume/.loom-runtime/install-layout.json",
@@ -4058,13 +4058,13 @@
4058
4058
  },
4059
4059
  {
4060
4060
  "path": "skills/loom-resume/.loom-runtime/README.md",
4061
- "bytes": 4330,
4062
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
4061
+ "bytes": 4394,
4062
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
4063
4063
  },
4064
4064
  {
4065
4065
  "path": "skills/loom-resume/.loom-runtime/README.zh-CN.md",
4066
- "bytes": 3986,
4067
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
4066
+ "bytes": 4050,
4067
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
4068
4068
  },
4069
4069
  {
4070
4070
  "path": "skills/loom-resume/.loom-runtime/registry.json",
@@ -4328,8 +4328,8 @@
4328
4328
  },
4329
4329
  {
4330
4330
  "path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py",
4331
- "bytes": 378826,
4332
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
4331
+ "bytes": 379162,
4332
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
4333
4333
  },
4334
4334
  {
4335
4335
  "path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py",
@@ -4393,8 +4393,8 @@
4393
4393
  },
4394
4394
  {
4395
4395
  "path": "skills/loom-retire/.loom-runtime/distribution-and-adapter-contract.md",
4396
- "bytes": 15232,
4397
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
4396
+ "bytes": 15291,
4397
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
4398
4398
  },
4399
4399
  {
4400
4400
  "path": "skills/loom-retire/.loom-runtime/install-layout.json",
@@ -4678,13 +4678,13 @@
4678
4678
  },
4679
4679
  {
4680
4680
  "path": "skills/loom-retire/.loom-runtime/README.md",
4681
- "bytes": 4330,
4682
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
4681
+ "bytes": 4394,
4682
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
4683
4683
  },
4684
4684
  {
4685
4685
  "path": "skills/loom-retire/.loom-runtime/README.zh-CN.md",
4686
- "bytes": 3986,
4687
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
4686
+ "bytes": 4050,
4687
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
4688
4688
  },
4689
4689
  {
4690
4690
  "path": "skills/loom-retire/.loom-runtime/registry.json",
@@ -4948,8 +4948,8 @@
4948
4948
  },
4949
4949
  {
4950
4950
  "path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py",
4951
- "bytes": 378826,
4952
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
4951
+ "bytes": 379162,
4952
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
4953
4953
  },
4954
4954
  {
4955
4955
  "path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py",
@@ -5013,8 +5013,8 @@
5013
5013
  },
5014
5014
  {
5015
5015
  "path": "skills/loom-review/.loom-runtime/distribution-and-adapter-contract.md",
5016
- "bytes": 15232,
5017
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
5016
+ "bytes": 15291,
5017
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
5018
5018
  },
5019
5019
  {
5020
5020
  "path": "skills/loom-review/.loom-runtime/install-layout.json",
@@ -5298,13 +5298,13 @@
5298
5298
  },
5299
5299
  {
5300
5300
  "path": "skills/loom-review/.loom-runtime/README.md",
5301
- "bytes": 4330,
5302
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
5301
+ "bytes": 4394,
5302
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
5303
5303
  },
5304
5304
  {
5305
5305
  "path": "skills/loom-review/.loom-runtime/README.zh-CN.md",
5306
- "bytes": 3986,
5307
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
5306
+ "bytes": 4050,
5307
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
5308
5308
  },
5309
5309
  {
5310
5310
  "path": "skills/loom-review/.loom-runtime/registry.json",
@@ -5568,8 +5568,8 @@
5568
5568
  },
5569
5569
  {
5570
5570
  "path": "skills/loom-review/.loom-runtime/shared/scripts/loom_check.py",
5571
- "bytes": 378826,
5572
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
5571
+ "bytes": 379162,
5572
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
5573
5573
  },
5574
5574
  {
5575
5575
  "path": "skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py",
@@ -5633,8 +5633,8 @@
5633
5633
  },
5634
5634
  {
5635
5635
  "path": "skills/loom-spec-review/.loom-runtime/distribution-and-adapter-contract.md",
5636
- "bytes": 15232,
5637
- "sha256": "eb7ccc2c1925099a7508e24eb1be8a833d1c9c97b8c09482848cc43e893b94f5"
5636
+ "bytes": 15291,
5637
+ "sha256": "85f748d598dca87a64fb751d5b50b925ee151204cdf0e908f2db5869bd9f1951"
5638
5638
  },
5639
5639
  {
5640
5640
  "path": "skills/loom-spec-review/.loom-runtime/install-layout.json",
@@ -5918,13 +5918,13 @@
5918
5918
  },
5919
5919
  {
5920
5920
  "path": "skills/loom-spec-review/.loom-runtime/README.md",
5921
- "bytes": 4330,
5922
- "sha256": "907c801ec6d20ee3a4a8d90e137d432e9512aae96c765d28b055e7952e1e0dfc"
5921
+ "bytes": 4394,
5922
+ "sha256": "5a6dd7f9d7b86d4b17af1cec0ca18bd297402423c41f6a0171a65afb0febc08e"
5923
5923
  },
5924
5924
  {
5925
5925
  "path": "skills/loom-spec-review/.loom-runtime/README.zh-CN.md",
5926
- "bytes": 3986,
5927
- "sha256": "90ba899919bf2329098fe72a7993edc6c5a6ba1f04ed820c2a643f89c95f65eb"
5926
+ "bytes": 4050,
5927
+ "sha256": "d30a9934d8c840b3c6b9ecf2013de722e83cc21b76fb35c03216111dfe570c9e"
5928
5928
  },
5929
5929
  {
5930
5930
  "path": "skills/loom-spec-review/.loom-runtime/registry.json",
@@ -6188,8 +6188,8 @@
6188
6188
  },
6189
6189
  {
6190
6190
  "path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py",
6191
- "bytes": 378826,
6192
- "sha256": "89158e15ce752ae1c4a3dc9edeaa2452954346ef8573a75f6e6b56dc5478e924"
6191
+ "bytes": 379162,
6192
+ "sha256": "e3ac079dcea1e0e5b790feaeb447604f7dbd44d31a5631ed82b0bdab9451bb30"
6193
6193
  },
6194
6194
  {
6195
6195
  "path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py",
@@ -59,7 +59,9 @@ The primary install model is the complete Loom skills library:
59
59
  ```bash
60
60
  git clone https://github.com/MC-and-his-Agents/Loom.git ~/.codex/loom
61
61
  mkdir -p ~/.agents/skills
62
- ln -s ~/.codex/loom/skills ~/.agents/skills/loom
62
+ for skill in ~/.codex/loom/skills/loom-*; do
63
+ ln -sfn "$skill" "$HOME/.agents/skills/$(basename "$skill")"
64
+ done
63
65
  ```
64
66
 
65
67
  The npm installer can also install the complete plugin surface: