mustflow 1.18.15 → 1.18.16

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.
@@ -6,7 +6,7 @@ import { MANIFEST_LOCK_RELATIVE_PATH, readManifestLock, sha256File } from '../li
6
6
  import { printUsageError, renderHelp } from '../lib/cli-output.js';
7
7
  import { t } from '../lib/i18n.js';
8
8
  import { resolveMustflowRoot } from '../lib/project-root.js';
9
- import { getDefaultTemplate, getTemplateFiles } from '../lib/templates.js';
9
+ import { getDefaultTemplate, getTemplateFiles, skillNameForTemplatePath } from '../lib/templates.js';
10
10
  import { readTomlFile, stringifyToml } from '../lib/toml.js';
11
11
  const UPDATE_SCHEMA_VERSION = '1';
12
12
  const CUSTOMIZED_LOCK_ACTION = 'customized';
@@ -54,6 +54,20 @@ function sha256Text(content) {
54
54
  function templateFileHash(source) {
55
55
  return source.content === undefined ? sha256File(source.sourcePath) : sha256Text(source.content);
56
56
  }
57
+ function isTemplateManagedSource(source) {
58
+ return source === 'template_locale' || source === 'template_common' || source === 'legacy';
59
+ }
60
+ function lockedTemplateSkillNames(files) {
61
+ return [
62
+ ...new Set(files
63
+ .filter((file) => isTemplateManagedSource(file.source))
64
+ .map((file) => skillNameForTemplatePath(file.relativePath))
65
+ .filter((value) => Boolean(value))),
66
+ ];
67
+ }
68
+ function getInstalledTemplateFiles(projectRoot, template, lock) {
69
+ return getTemplateFiles(template, lock.templateLocale ?? template.manifest.defaultLocale, lock.templateProfile ?? template.manifest.defaultProfile, { extraSkillNames: lockedTemplateSkillNames(lock.files) });
70
+ }
57
71
  function writeTemplateFile(projectRoot, source, targetPath) {
58
72
  if (source.content !== undefined) {
59
73
  writeUtf8FileInsideWithoutSymlinks(projectRoot, targetPath, source.content);
@@ -87,9 +101,7 @@ export function planUpdate(projectRoot) {
87
101
  catch (error) {
88
102
  return { items: [], error: error instanceof Error ? error.message : String(error) };
89
103
  }
90
- const selectedLocale = lockResult.lock.templateLocale ?? template.manifest.defaultLocale;
91
- const selectedProfile = lockResult.lock.templateProfile ?? template.manifest.defaultProfile;
92
- const templateFiles = getTemplateFiles(template, selectedLocale, selectedProfile);
104
+ const templateFiles = getInstalledTemplateFiles(projectRoot, template, lockResult.lock);
93
105
  const lockedFiles = byRelativePath(lockResult.lock.files);
94
106
  const items = [];
95
107
  for (const source of templateFiles) {
@@ -202,16 +214,12 @@ function isMutableTable(value) {
202
214
  function fileActionToLockAction(action) {
203
215
  return action === 'create' ? 'created' : 'updated';
204
216
  }
205
- function readInstalledTemplateSelection(projectRoot) {
206
- const lockResult = readManifestLock(projectRoot);
207
- return lockResult.kind === 'present'
208
- ? { locale: lockResult.lock.templateLocale, profile: lockResult.lock.templateProfile }
209
- : {};
210
- }
211
217
  function copyTemplateFile(projectRoot, relativePath) {
212
218
  const template = getDefaultTemplate();
213
- const selection = readInstalledTemplateSelection(projectRoot);
214
- const source = getTemplateFiles(template, selection.locale ?? template.manifest.defaultLocale, selection.profile ?? template.manifest.defaultProfile).find((file) => file.relativePath === relativePath);
219
+ const lockResult = readManifestLock(projectRoot);
220
+ const source = lockResult.kind === 'present'
221
+ ? getInstalledTemplateFiles(projectRoot, template, lockResult.lock).find((file) => file.relativePath === relativePath)
222
+ : getTemplateFiles(template).find((file) => file.relativePath === relativePath);
215
223
  const targetPath = path.join(projectRoot, relativePath);
216
224
  if (!source) {
217
225
  throw new Error(`Template source missing for ${relativePath}`);
@@ -40,7 +40,7 @@ function readStringArrayTable(raw, label) {
40
40
  function normalizeTemplateTargetPath(relativePath) {
41
41
  return relativePath.replaceAll('\\', '/');
42
42
  }
43
- function skillNameForTemplatePath(relativePath) {
43
+ export function skillNameForTemplatePath(relativePath) {
44
44
  const match = /^\.mustflow\/skills\/([^/]+)\//u.exec(normalizeTemplateTargetPath(relativePath));
45
45
  return match?.[1];
46
46
  }
@@ -58,13 +58,16 @@ function templateSkillNames(creates) {
58
58
  function resolveSkillProfileSkills(manifest, profile) {
59
59
  return manifest.skillProfiles[profile] ?? templateSkillNames(manifest.creates);
60
60
  }
61
- function shouldIncludeTemplatePath(manifest, relativePath, profile) {
61
+ function selectedSkillNames(manifest, profile, options = {}) {
62
+ return [...new Set([...resolveSkillProfileSkills(manifest, profile), ...(options.extraSkillNames ?? [])])];
63
+ }
64
+ function shouldIncludeTemplatePath(relativePath, selectedSkills) {
62
65
  const normalizedPath = normalizeTemplateTargetPath(relativePath);
63
66
  const skillName = skillNameForTemplatePath(normalizedPath);
64
67
  if (!skillName) {
65
68
  return true;
66
69
  }
67
- return resolveSkillProfileSkills(manifest, profile).includes(skillName);
70
+ return selectedSkills.includes(skillName);
68
71
  }
69
72
  function filterSkillIndexContent(content, selectedSkills) {
70
73
  const selectedSkillSet = new Set(selectedSkills);
@@ -157,11 +160,11 @@ export function getDefaultTemplate() {
157
160
  manifest,
158
161
  };
159
162
  }
160
- export function getTemplateFiles(template, locale = template.manifest.defaultLocale, profile = template.manifest.defaultProfile) {
163
+ export function getTemplateFiles(template, locale = template.manifest.defaultLocale, profile = template.manifest.defaultProfile, options = {}) {
161
164
  const commonRoot = path.join(template.templateRoot, template.manifest.commonRoot);
162
165
  const localeRoot = template.manifest.localesRoot ? path.join(template.templateRoot, template.manifest.localesRoot, locale) : undefined;
163
- const selectedSkills = resolveSkillProfileSkills(template.manifest, profile);
164
- return template.manifest.creates.filter((relativePath) => shouldIncludeTemplatePath(template.manifest, relativePath, profile)).map((relativePath) => {
166
+ const selectedSkills = selectedSkillNames(template.manifest, profile, options);
167
+ return template.manifest.creates.filter((relativePath) => shouldIncludeTemplatePath(relativePath, selectedSkills)).map((relativePath) => {
165
168
  const localePath = localeRoot ? path.join(localeRoot, ...relativePath.split('/')) : undefined;
166
169
  const commonPath = path.join(commonRoot, ...relativePath.split('/'));
167
170
  const content = relativePath === '.mustflow/skills/INDEX.md'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mustflow",
3
- "version": "1.18.15",
3
+ "version": "1.18.16",
4
4
  "description": "Agent workflow documents and CLI for mustflow repository roots.",
5
5
  "type": "module",
6
6
  "license": "MIT-0",
@@ -1,6 +1,6 @@
1
1
  id = "default"
2
2
  name = "default"
3
- version = "1.18.15"
3
+ version = "1.18.16"
4
4
  description = "Minimal workflow for LLM agents to read, edit, and verify their work in a repository."
5
5
  common_root = "common"
6
6
  locales_root = "locales"