Summary
Running --apply prints a batch of Failed to read markdown file ... ENOENT errors for a set of system prompts, e.g.:
Failed to read markdown file ~/.tweakcc/system-prompts/tool-description-sendfile.md: Error: ENOENT: no such file or directory ...
On Claude Code 2.1.212 this is 14 prompts (the ones added in recent versions: memory, code-review, artifacts, cloud-agent, browser navigate, sendfile, etc.). The errors are non-fatal (the prompts are skipped), but those system prompts are then silently never customizable or applied.
Root cause
generateMarkdownFromPrompt and updateVariables in src/systemPromptSync.ts build each prompt's markdown with:
matter.stringify(content, frontmatterData, { delimiters: ['<!--', '-->'] })
gray-matter's matter.stringify re-parses a string argument as front matter first:
matter.stringify = function(file, data, options) {
if (typeof file === 'string') file = matter(file, options); // <-- re-parse
return stringify(file, data, options);
};
tweakcc uses <!--/--> as its front-matter delimiters, so a prompt whose content itself begins with <!-- (an HTML template such as data-plan-artifact-html-template, whose body starts with an HTML comment on Claude Code 2.1.208+) collides with the delimiters. gray-matter hands the enclosed prose to js-yaml, which throws:
YAMLException: bad indentation of a mapping entry
syncSystemPrompts rethrows on any prompt failure, so that single throw aborts the whole sync. Every prompt ordered after the offending one never gets its .md written, and the startup try/catch swallows the error. On the next --apply those files are missing, which is the ENOENT batch above.
(When the mis-parsed YAML happens to be valid, the body is silently corrupted instead of throwing: the content's leading comment is absorbed into the front matter and re-serialized.)
Reproduce
- Claude Code 2.1.208 or later installed
- Run tweakcc so the system-prompt sync runs (or
--apply)
- The sync aborts at
data-plan-artifact-html-template; the prompts ordered after it are left without .md files
--apply prints Failed to read markdown file ... ENOENT for each
Fix
Pass the content as a file object ({ content }) at both call sites so gray-matter skips the string re-parse and stringifies the body verbatim. Output is byte-identical for content that does not begin with <!--.
Summary
Running
--applyprints a batch ofFailed to read markdown file ... ENOENTerrors for a set of system prompts, e.g.:On Claude Code 2.1.212 this is 14 prompts (the ones added in recent versions: memory, code-review, artifacts, cloud-agent, browser
navigate,sendfile, etc.). The errors are non-fatal (the prompts are skipped), but those system prompts are then silently never customizable or applied.Root cause
generateMarkdownFromPromptandupdateVariablesinsrc/systemPromptSync.tsbuild each prompt's markdown with:gray-matter's
matter.stringifyre-parses a string argument as front matter first:tweakcc uses
<!--/-->as its front-matter delimiters, so a prompt whose content itself begins with<!--(an HTML template such asdata-plan-artifact-html-template, whose body starts with an HTML comment on Claude Code 2.1.208+) collides with the delimiters. gray-matter hands the enclosed prose to js-yaml, which throws:syncSystemPromptsrethrows on any prompt failure, so that single throw aborts the whole sync. Every prompt ordered after the offending one never gets its.mdwritten, and the startuptry/catchswallows the error. On the next--applythose files are missing, which is the ENOENT batch above.(When the mis-parsed YAML happens to be valid, the body is silently corrupted instead of throwing: the content's leading comment is absorbed into the front matter and re-serialized.)
Reproduce
--apply)data-plan-artifact-html-template; the prompts ordered after it are left without.mdfiles--applyprintsFailed to read markdown file ... ENOENTfor eachFix
Pass the content as a file object (
{ content }) at both call sites so gray-matter skips the string re-parse and stringifies the body verbatim. Output is byte-identical for content that does not begin with<!--.