@guiho/mirror 3.0.0 → 3.1.0
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/CHANGELOG.md +15 -0
- package/DOCS.md +536 -0
- package/README.md +24 -2
- package/jsr.json +1 -1
- package/library/adapters.d.ts +2 -1
- package/library/adapters.d.ts.map +1 -1
- package/library/adapters.js +65 -14
- package/library/agents.d.ts +27 -0
- package/library/agents.d.ts.map +1 -0
- package/library/agents.js +205 -0
- package/library/cli.d.ts.map +1 -1
- package/library/cli.js +51 -15
- package/library/config.d.ts +1 -1
- package/library/config.d.ts.map +1 -1
- package/library/config.js +41 -3
- package/library/executor.d.ts +1 -1
- package/library/executor.d.ts.map +1 -1
- package/library/executor.js +4 -4
- package/library/flags.d.ts +1 -1
- package/library/flags.d.ts.map +1 -1
- package/library/flags.js +1 -1
- package/library/guiho-mirror-bin.d.ts +1 -1
- package/library/guiho-mirror-bin.js +2 -2
- package/library/guiho-mirror.d.ts +11 -10
- package/library/guiho-mirror.d.ts.map +1 -1
- package/library/guiho-mirror.js +10 -9
- package/library/plan.d.ts +1 -1
- package/library/plan.d.ts.map +1 -1
- package/library/plan.js +4 -4
- package/library/reporter.d.ts +3 -1
- package/library/reporter.d.ts.map +1 -1
- package/library/reporter.js +33 -1
- package/library/types.d.ts +31 -0
- package/library/types.d.ts.map +1 -1
- package/library/version.js +1 -1
- package/package.json +8 -7
- package/skills/guiho-as-mirror/SKILL.md +227 -0
package/library/adapters.js
CHANGED
|
@@ -1,16 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { execFile } from 'node:child_process';
|
|
5
5
|
import { existsSync } from 'node:fs';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
8
|
+
import { MirrorError } from './errors.js';
|
|
9
|
+
import { assertValidSemver, sortSemverDescending } from './version.js';
|
|
10
|
+
import { resolveMirrorPath } from './config.js';
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
let gitChecked = false;
|
|
13
|
+
let gitExists = false;
|
|
14
|
+
const checkGitAvailable = async () => {
|
|
15
|
+
if (gitChecked)
|
|
16
|
+
return gitExists;
|
|
17
|
+
gitChecked = true;
|
|
18
|
+
try {
|
|
19
|
+
await execFileAsync('git', ['--version']);
|
|
20
|
+
gitExists = true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
gitExists = false;
|
|
24
|
+
}
|
|
25
|
+
return gitExists;
|
|
26
|
+
};
|
|
27
|
+
export const ensureGitAvailable = async () => {
|
|
28
|
+
if (!(await checkGitAvailable())) {
|
|
29
|
+
throw new MirrorError('Git executable not found. Git is required when using git as a source or output.');
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const gitNotFoundMessage = 'Git executable not found. Git is required when using git as a source or output.';
|
|
33
|
+
const runGit = async (cwd, args) => {
|
|
34
|
+
if (!(await checkGitAvailable())) {
|
|
35
|
+
throw new MirrorError(gitNotFoundMessage);
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const { stdout } = await execFileAsync('git', args, { cwd });
|
|
39
|
+
return stdout;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
43
|
+
throw new MirrorError(`Git command failed: git ${args.join(' ')}\n${message}`);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
9
46
|
export const supportedGitTagTemplates = ['v{version}', '{name}@{version}', '{name}/v{version}'];
|
|
10
47
|
export const readPackageJson = async (path) => readJsonObject(path, 'package.json');
|
|
11
48
|
export const readJsrJson = async (path) => readJsonObject(path, 'jsr.json');
|
|
12
49
|
export const writeJsonObject = async (path, object) => {
|
|
13
|
-
await
|
|
50
|
+
await writeFile(path, `${JSON.stringify(object, null, 2)}\n`, 'utf8');
|
|
14
51
|
};
|
|
15
52
|
export const readPackageVersion = async (config) => readVersionField(resolveMirrorPath(config.cwd, config.package.path), 'package.json');
|
|
16
53
|
export const readJsrVersion = async (config) => readVersionField(resolveMirrorPath(config.cwd, config.jsr.path), 'jsr.json');
|
|
@@ -43,8 +80,9 @@ export const readCurrentVersion = async (config, projectName) => {
|
|
|
43
80
|
return readGitVersion(config, projectName);
|
|
44
81
|
};
|
|
45
82
|
export const readGitVersion = async (config, projectName) => {
|
|
83
|
+
await ensureGitAvailable();
|
|
46
84
|
await ensureGitRepository(config.cwd);
|
|
47
|
-
const tagsOutput = await
|
|
85
|
+
const tagsOutput = await runGit(config.cwd, ['-C', config.cwd, 'tag', '--list']);
|
|
48
86
|
const versions = tagsOutput
|
|
49
87
|
.split(/\r?\n/)
|
|
50
88
|
.map((line) => line.trim())
|
|
@@ -87,8 +125,10 @@ export const assertSupportedGitTagTemplate = (template) => {
|
|
|
87
125
|
}
|
|
88
126
|
};
|
|
89
127
|
export const isGitRepository = async (cwd) => {
|
|
128
|
+
if (!(await checkGitAvailable()))
|
|
129
|
+
return false;
|
|
90
130
|
try {
|
|
91
|
-
await
|
|
131
|
+
await execFileAsync('git', ['-C', cwd, 'rev-parse', '--is-inside-work-tree']);
|
|
92
132
|
return true;
|
|
93
133
|
}
|
|
94
134
|
catch {
|
|
@@ -96,26 +136,36 @@ export const isGitRepository = async (cwd) => {
|
|
|
96
136
|
}
|
|
97
137
|
};
|
|
98
138
|
export const isGitDirty = async (cwd) => {
|
|
99
|
-
const output = await
|
|
139
|
+
const output = await runGit(cwd, ['-C', cwd, 'status', '--porcelain']);
|
|
100
140
|
return output.trim().length > 0;
|
|
101
141
|
};
|
|
102
142
|
export const createGitCommit = async (cwd, paths, message) => {
|
|
143
|
+
await ensureGitAvailable();
|
|
103
144
|
for (const path of paths)
|
|
104
|
-
await
|
|
105
|
-
await
|
|
145
|
+
await runGit(cwd, ['-C', cwd, 'add', path]);
|
|
146
|
+
await runGit(cwd, ['-C', cwd, 'commit', '-m', message]);
|
|
106
147
|
};
|
|
107
148
|
export const createGitTag = async (cwd, tag) => {
|
|
108
|
-
await
|
|
149
|
+
await ensureGitAvailable();
|
|
150
|
+
await runGit(cwd, ['-C', cwd, 'tag', tag, '-m', `Release ${tag}`]);
|
|
109
151
|
};
|
|
110
152
|
export const pushGitRefs = async (cwd, includeCommit, includeTags) => {
|
|
153
|
+
await ensureGitAvailable();
|
|
111
154
|
if (includeCommit)
|
|
112
|
-
await
|
|
155
|
+
await runGit(cwd, ['-C', cwd, 'push']);
|
|
113
156
|
if (includeTags)
|
|
114
|
-
await
|
|
157
|
+
await runGit(cwd, ['-C', cwd, 'push', '--tags']);
|
|
115
158
|
};
|
|
116
159
|
const readJsonObject = async (path, label) => {
|
|
117
160
|
ensureFile(path, label);
|
|
118
|
-
const
|
|
161
|
+
const content = await readFile(path, 'utf8');
|
|
162
|
+
let json;
|
|
163
|
+
try {
|
|
164
|
+
json = JSON.parse(content);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
throw new MirrorError(`${label} must contain valid JSON: ${path}`);
|
|
168
|
+
}
|
|
119
169
|
if (typeof json !== 'object' || json === null || Array.isArray(json))
|
|
120
170
|
throw new MirrorError(`${label} must contain a JSON object: ${path}`);
|
|
121
171
|
return json;
|
|
@@ -145,6 +195,7 @@ const ensureFile = (path, label) => {
|
|
|
145
195
|
throw new MirrorError(`${label} file not found: ${path}`);
|
|
146
196
|
};
|
|
147
197
|
const ensureGitRepository = async (cwd) => {
|
|
198
|
+
await ensureGitAvailable();
|
|
148
199
|
if (!(await isGitRepository(cwd)))
|
|
149
200
|
throw new MirrorError(`Not a Git repository: ${cwd}`);
|
|
150
201
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
import type { MirrorAgentAutomationResult, MirrorAgentSettings, MirrorAgentsInstructionsResult, MirrorCliOptions, MirrorSkillInstallResult, MirrorSkillInstallScope } from './types.js';
|
|
5
|
+
export declare const mirrorSkillName = "guiho-as-mirror";
|
|
6
|
+
export declare const mirrorAgentsSectionHeading = "## Semantic Project Versioning -- GUIHO Mirror";
|
|
7
|
+
export declare const defaultMirrorAgentSettings: MirrorAgentSettings;
|
|
8
|
+
export declare const mirrorAgentsSection = "## Semantic Project Versioning -- GUIHO Mirror\n\nInvoke the guiho-as-mirror agent skill every time the user wants to bump, tag, release, plan, initialize, configure, or troubleshoot semantic project versioning with GUIHO Mirror.\n\nBefore editing release docs or changelogs, inspect mirror.config.toml. If [agents].write_changelog is false, skip changelog edits. If it is missing or true, changelog edits are allowed when the project has a changelog.\n\nUse [agents].changelog_path as the changelog file path. If it is missing, use CHANGELOG.md in the project root.\n";
|
|
9
|
+
type MirrorSkillPathOptions = {
|
|
10
|
+
cwd?: string;
|
|
11
|
+
homeDirectory?: string;
|
|
12
|
+
};
|
|
13
|
+
type MirrorSkillInstallOptions = MirrorSkillPathOptions & {
|
|
14
|
+
overwrite?: boolean;
|
|
15
|
+
};
|
|
16
|
+
type MirrorAgentAutomationOptions = MirrorCliOptions & {
|
|
17
|
+
homeDirectory?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare const resolveMirrorSkillPath: (scope: MirrorSkillInstallScope, options?: MirrorSkillPathOptions) => string;
|
|
20
|
+
export declare const isMirrorSkillInstalled: (scope: MirrorSkillInstallScope, options?: MirrorSkillPathOptions) => boolean;
|
|
21
|
+
export declare const installMirrorSkill: (scope: MirrorSkillInstallScope, options?: MirrorSkillInstallOptions) => Promise<MirrorSkillInstallResult>;
|
|
22
|
+
export declare const ensureMirrorAgentsInstructions: (cwd: string, create?: boolean) => Promise<MirrorAgentsInstructionsResult>;
|
|
23
|
+
export declare const findAgentsFile: (cwd: string) => string | undefined;
|
|
24
|
+
export declare const resolveMirrorAgentSettings: (options?: MirrorCliOptions) => Promise<MirrorAgentSettings>;
|
|
25
|
+
export declare const runMirrorAgentAutomation: (options?: MirrorAgentAutomationOptions, notify?: (message: string) => void) => Promise<MirrorAgentAutomationResult>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../source/agents.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EACV,2BAA2B,EAC3B,mBAAmB,EACnB,8BAA8B,EAC9B,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,YAAY,CAAA;AAInB,eAAO,MAAM,eAAe,oBAAoB,CAAA;AAChD,eAAO,MAAM,0BAA0B,mDAAmD,CAAA;AAE1F,eAAO,MAAM,0BAA0B,EAAE,mBAKxC,CAAA;AAED,eAAO,MAAM,mBAAmB,6jBAO/B,CAAA;AAED,KAAK,sBAAsB,GAAG;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,KAAK,yBAAyB,GAAG,sBAAsB,GAAG;IACxD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,KAAK,4BAA4B,GAAG,gBAAgB,GAAG;IACrD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,OAAO,uBAAuB,EAAE,UAAS,sBAA2B,WAI1G,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,OAAO,uBAAuB,EAAE,UAAS,sBAA2B,YACvD,CAAA;AAEpD,eAAO,MAAM,kBAAkB,GAC7B,OAAO,uBAAuB,EAC9B,UAAS,yBAA8B,KACtC,OAAO,CAAC,wBAAwB,CAelC,CAAA;AAED,eAAO,MAAM,8BAA8B,GAAU,KAAK,MAAM,EAAE,gBAAc,KAAG,OAAO,CAAC,8BAA8B,CAkBxH,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAWrD,CAAA;AAED,eAAO,MAAM,0BAA0B,GAAU,UAAS,gBAAqB,KAAG,OAAO,CAAC,mBAAmB,CAa5G,CAAA;AAED,eAAO,MAAM,wBAAwB,GACnC,UAAS,4BAAiC,EAC1C,SAAQ,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,KAC3C,OAAO,CAAC,2BAA2B,CAoBrC,CAAA"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { dirname, resolve } from 'node:path';
|
|
8
|
+
import { MirrorError } from './errors.js';
|
|
9
|
+
import { discoverMirrorConfig, resolveMirrorPath } from './config.js';
|
|
10
|
+
export const mirrorSkillName = 'guiho-as-mirror';
|
|
11
|
+
export const mirrorAgentsSectionHeading = '## Semantic Project Versioning -- GUIHO Mirror';
|
|
12
|
+
export const defaultMirrorAgentSettings = {
|
|
13
|
+
writeChangelog: true,
|
|
14
|
+
changelogPath: 'CHANGELOG.md',
|
|
15
|
+
autoAgentsMd: true,
|
|
16
|
+
autoSkillInstall: true,
|
|
17
|
+
};
|
|
18
|
+
export const mirrorAgentsSection = `${mirrorAgentsSectionHeading}
|
|
19
|
+
|
|
20
|
+
Invoke the guiho-as-mirror agent skill every time the user wants to bump, tag, release, plan, initialize, configure, or troubleshoot semantic project versioning with GUIHO Mirror.
|
|
21
|
+
|
|
22
|
+
Before editing release docs or changelogs, inspect mirror.config.toml. If [agents].write_changelog is false, skip changelog edits. If it is missing or true, changelog edits are allowed when the project has a changelog.
|
|
23
|
+
|
|
24
|
+
Use [agents].changelog_path as the changelog file path. If it is missing, use CHANGELOG.md in the project root.
|
|
25
|
+
`;
|
|
26
|
+
export const resolveMirrorSkillPath = (scope, options = {}) => {
|
|
27
|
+
if (scope === 'local')
|
|
28
|
+
return resolve(options.cwd ?? process.cwd(), '.opencode', 'skills', mirrorSkillName, 'SKILL.md');
|
|
29
|
+
return resolve(resolveMirrorAgentHome(options.homeDirectory), '.config', 'opencode', 'skills', mirrorSkillName, 'SKILL.md');
|
|
30
|
+
};
|
|
31
|
+
export const isMirrorSkillInstalled = (scope, options = {}) => existsSync(resolveMirrorSkillPath(scope, options));
|
|
32
|
+
export const installMirrorSkill = async (scope, options = {}) => {
|
|
33
|
+
const path = resolveMirrorSkillPath(scope, options);
|
|
34
|
+
const exists = existsSync(path);
|
|
35
|
+
if (exists && options.overwrite === false)
|
|
36
|
+
return { scope, path, installed: false, updated: false };
|
|
37
|
+
const content = await readBundledMirrorSkill();
|
|
38
|
+
const current = exists ? await readFile(path, 'utf8') : undefined;
|
|
39
|
+
if (current === content)
|
|
40
|
+
return { scope, path, installed: false, updated: false };
|
|
41
|
+
await mkdir(dirname(path), { recursive: true });
|
|
42
|
+
await writeFile(path, content, 'utf8');
|
|
43
|
+
return { scope, path, installed: !exists, updated: exists };
|
|
44
|
+
};
|
|
45
|
+
export const ensureMirrorAgentsInstructions = async (cwd, create = false) => {
|
|
46
|
+
const path = findAgentsFile(cwd) ?? resolve(cwd, 'AGENTS.md');
|
|
47
|
+
const exists = existsSync(path);
|
|
48
|
+
if (!exists && !create)
|
|
49
|
+
return { path, exists: false, changed: false };
|
|
50
|
+
if (!exists) {
|
|
51
|
+
await writeFile(path, `# Project Agents\n\n${mirrorAgentsSection}\n`, 'utf8');
|
|
52
|
+
return { path, exists: true, changed: true };
|
|
53
|
+
}
|
|
54
|
+
const content = await readFile(path, 'utf8');
|
|
55
|
+
if (content.includes(mirrorAgentsSectionHeading))
|
|
56
|
+
return { path, exists: true, changed: false };
|
|
57
|
+
const nextContent = `${content.trimEnd()}\n\n${mirrorAgentsSection}\n`;
|
|
58
|
+
await writeFile(path, nextContent, 'utf8');
|
|
59
|
+
return { path, exists: true, changed: true };
|
|
60
|
+
};
|
|
61
|
+
export const findAgentsFile = (cwd) => {
|
|
62
|
+
let current = resolve(cwd);
|
|
63
|
+
while (true) {
|
|
64
|
+
const path = resolve(current, 'AGENTS.md');
|
|
65
|
+
if (existsSync(path))
|
|
66
|
+
return path;
|
|
67
|
+
const parent = dirname(current);
|
|
68
|
+
if (parent === current)
|
|
69
|
+
return undefined;
|
|
70
|
+
current = parent;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
export const resolveMirrorAgentSettings = async (options = {}) => {
|
|
74
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
75
|
+
const discovered = await discoverMirrorConfig(cwd, options.config);
|
|
76
|
+
if (!discovered.raw)
|
|
77
|
+
return { ...defaultMirrorAgentSettings };
|
|
78
|
+
if (discovered.raw.schema !== 1)
|
|
79
|
+
throw new MirrorError('Unsupported or missing configuration schema. Expected `schema = 1`.');
|
|
80
|
+
return {
|
|
81
|
+
writeChangelog: optionalBoolean(discovered.raw.agents?.write_changelog, 'agents.write_changelog') !== false,
|
|
82
|
+
changelogPath: optionalString(discovered.raw.agents?.changelog_path, 'agents.changelog_path') ?? 'CHANGELOG.md',
|
|
83
|
+
autoAgentsMd: optionalBoolean(discovered.raw.agents?.auto_agents_md, 'agents.auto_agents_md') !== false,
|
|
84
|
+
autoSkillInstall: optionalBoolean(discovered.raw.agents?.auto_skill_install, 'agents.auto_skill_install') !== false,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
export const runMirrorAgentAutomation = async (options = {}, notify = () => { }) => {
|
|
88
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
89
|
+
const settings = await resolveMirrorAgentSettings(options);
|
|
90
|
+
const result = { settings };
|
|
91
|
+
if (settings.autoAgentsMd)
|
|
92
|
+
result.agentsMd = await ensureMirrorAgentsInstructions(cwd, false);
|
|
93
|
+
if (settings.autoSkillInstall) {
|
|
94
|
+
for (const scope of ['local', 'global']) {
|
|
95
|
+
if (isMirrorSkillInstalled(scope, { cwd, homeDirectory: options.homeDirectory }))
|
|
96
|
+
continue;
|
|
97
|
+
const path = resolveMirrorSkillPath(scope, { cwd, homeDirectory: options.homeDirectory });
|
|
98
|
+
notify(`notice: ${mirrorSkillName} skill not found ${scope}; Mirror is installing it at ${path}`);
|
|
99
|
+
const installed = await installMirrorSkill(scope, { cwd, homeDirectory: options.homeDirectory, overwrite: false });
|
|
100
|
+
if (scope === 'local')
|
|
101
|
+
result.localSkill = installed;
|
|
102
|
+
if (scope === 'global')
|
|
103
|
+
result.globalSkill = installed;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
};
|
|
108
|
+
const readBundledMirrorSkill = async () => {
|
|
109
|
+
try {
|
|
110
|
+
return await readFile(new URL('../skills/guiho-as-mirror/SKILL.md', import.meta.url), 'utf8');
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return embeddedMirrorSkillContent;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const resolveMirrorAgentHome = (homeDirectory) => {
|
|
117
|
+
const value = homeDirectory ?? process.env['MIRROR_AGENT_HOME'] ?? homedir();
|
|
118
|
+
return resolveMirrorPath(process.cwd(), value);
|
|
119
|
+
};
|
|
120
|
+
const optionalBoolean = (value, key) => {
|
|
121
|
+
if (value === undefined)
|
|
122
|
+
return undefined;
|
|
123
|
+
if (typeof value !== 'boolean')
|
|
124
|
+
throw new MirrorError(`Invalid ${key}. Expected true or false.`);
|
|
125
|
+
return value;
|
|
126
|
+
};
|
|
127
|
+
const optionalString = (value, key) => {
|
|
128
|
+
if (value === undefined)
|
|
129
|
+
return undefined;
|
|
130
|
+
if (typeof value !== 'string')
|
|
131
|
+
throw new MirrorError(`Invalid ${key}. Expected a string.`);
|
|
132
|
+
return value;
|
|
133
|
+
};
|
|
134
|
+
const embeddedMirrorSkillContent = [
|
|
135
|
+
'---',
|
|
136
|
+
'name: guiho-as-mirror',
|
|
137
|
+
'description: Use this skill whenever the user asks to version, bump, release, tag, initialize, configure, or troubleshoot a project with GUIHO Mirror. This includes Bun, npm, JSR, package.json, jsr.json, Git tag, semantic versioning, changelog, release-plan, prerelease, and what version comes next workflows.',
|
|
138
|
+
'---',
|
|
139
|
+
'',
|
|
140
|
+
'# GUIHO Mirror',
|
|
141
|
+
'',
|
|
142
|
+
'GUIHO Mirror is a deterministic CLI and TypeScript library for semantic project versioning. Use it instead of ad hoc version edits, manual package-manager version commands, or manual release tags.',
|
|
143
|
+
'',
|
|
144
|
+
'## Command Selection',
|
|
145
|
+
'',
|
|
146
|
+
'1. Use `bun @guiho/mirror` when the package is installed locally and Bun is available.',
|
|
147
|
+
'2. Use `mirror` when a global binary is available.',
|
|
148
|
+
'3. Use `bunx @guiho/mirror` when running without installation.',
|
|
149
|
+
'',
|
|
150
|
+
'Run `mirror --help` or `mirror <command> --help` for command-specific details when needed.',
|
|
151
|
+
'',
|
|
152
|
+
'## Release Workflow',
|
|
153
|
+
'',
|
|
154
|
+
'When the user asks to bump, release, tag, or version a project, follow this sequence:',
|
|
155
|
+
'',
|
|
156
|
+
'1. Confirm the target and project root. Supported targets are `major`, `premajor`, `minor`, `preminor`, `patch`, `prepatch`, `prerelease`, or an exact semver like `2.0.0`.',
|
|
157
|
+
'2. Run `<mirror> config show` and read `[git].allow_dirty`, `[agents].write_changelog`, and `[agents].changelog_path`.',
|
|
158
|
+
'3. If `allow_dirty = false` or absent, check `git status --short` and stop if the worktree is dirty.',
|
|
159
|
+
'4. Run the project type checker, commonly `bun run typecheck`.',
|
|
160
|
+
'5. Run the project test suite, commonly `bun test`.',
|
|
161
|
+
'6. Run `<mirror> version plan <target>` and use the planned next version as the source of truth.',
|
|
162
|
+
'7. Update release documentation only when it is part of the project release process.',
|
|
163
|
+
'8. If `[agents].write_changelog = false`, skip changelog edits. Otherwise update `[agents].changelog_path`, defaulting to `CHANGELOG.md` in the project root, and summarize only real changes.',
|
|
164
|
+
'9. Commit release-preparation changes before applying the version bump.',
|
|
165
|
+
'10. Run `<mirror> version apply <target> --yes`. Include `--commit` when file outputs and Git tag output are combined unless config already enables commits or push.',
|
|
166
|
+
'',
|
|
167
|
+
'## Safety Rules',
|
|
168
|
+
'',
|
|
169
|
+
'- Never skip `version plan`.',
|
|
170
|
+
'- Never hand-edit `package.json` or `jsr.json` version fields as a Mirror substitute.',
|
|
171
|
+
'- Never create Git tags manually for a Mirror-managed release unless recovering intentionally.',
|
|
172
|
+
'- Do not apply a version bump after failed typecheck or tests.',
|
|
173
|
+
'- Do not push release refs unless explicitly requested or configured.',
|
|
174
|
+
'- When plan output is surprising, stop and explain the mismatch.',
|
|
175
|
+
'',
|
|
176
|
+
'## Initialization Workflow',
|
|
177
|
+
'',
|
|
178
|
+
'Use `mirror init package.json`, `mirror init jsr.json`, or `mirror init git`, then validate with `mirror config check`, inspect with `mirror config show`, and test with `mirror version current` plus `mirror version plan patch`.',
|
|
179
|
+
'',
|
|
180
|
+
'## Configuration Reference',
|
|
181
|
+
'',
|
|
182
|
+
'Mirror searches for configuration via `--config <path>`, `./mirror.config.toml`, or `./config/mirror.config.toml`.',
|
|
183
|
+
'',
|
|
184
|
+
'Common configuration keys: `[version].source`, `[version].output`, `[version].prerelease_id`, `[git].tag_template`, `[git].commit`, `[git].push`, `[git].allow_dirty`, `[agents].write_changelog`, `[agents].changelog_path`, `[agents].auto_agents_md`, and `[agents].auto_skill_install`.',
|
|
185
|
+
'',
|
|
186
|
+
'Agent automation options default to true. Set `write_changelog = false` to tell agents to skip changelog edits, `changelog_path = "docs/CHANGELOG.md"` to specify the changelog file, `auto_agents_md = false` to stop Mirror from inserting its AGENTS.md section, and `auto_skill_install = false` to stop Mirror from installing `guiho-as-mirror` when missing.',
|
|
187
|
+
'',
|
|
188
|
+
'## CLI Reference',
|
|
189
|
+
'',
|
|
190
|
+
'- `mirror config show`',
|
|
191
|
+
'- `mirror config check`',
|
|
192
|
+
'- `mirror config schema`',
|
|
193
|
+
'- `mirror agents install local`',
|
|
194
|
+
'- `mirror agents install global`',
|
|
195
|
+
'- `mirror agents instructions`',
|
|
196
|
+
'- `mirror version current`',
|
|
197
|
+
'- `mirror version next <target>`',
|
|
198
|
+
'- `mirror version plan <target>`',
|
|
199
|
+
'- `mirror version apply <target> --yes`',
|
|
200
|
+
'',
|
|
201
|
+
'## Response Style',
|
|
202
|
+
'',
|
|
203
|
+
'When reporting a Mirror release result, include the target, current version, planned next version, typecheck/test status, docs or changelog files changed, final apply command, and whether commits, tags, or pushes were created.',
|
|
204
|
+
'',
|
|
205
|
+
].join('\n');
|
package/library/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../source/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../source/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2DH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;EAc5B,CAAA;AAEJ,eAAO,MAAM,YAAY,GAAU,kBAA+B,kBAoEjE,CAAA"}
|
package/library/cli.js
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
import { defineCommand, runMain } from 'citty';
|
|
5
5
|
import { readFileSync } from 'node:fs';
|
|
6
6
|
import { resolve } from 'node:path';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
7
|
+
import { ensureMirrorAgentsInstructions, installMirrorSkill, runMirrorAgentAutomation } from './agents.js';
|
|
8
|
+
import { MirrorError } from './errors.js';
|
|
9
|
+
import { readCurrentVersion, resolveProjectName } from './adapters.js';
|
|
10
|
+
import { configPathForDisplay, discoverMirrorConfig, loadMirrorConfig, relativeFromCwd, writeInitConfig } from './config.js';
|
|
11
|
+
import { executeVersionPlan } from './executor.js';
|
|
12
|
+
import { parseMirrorCliOptions } from './flags.js';
|
|
13
|
+
import { buildVersionPlan, validateMirrorConfig } from './plan.js';
|
|
14
|
+
import { mirrorBanner, reportAgentsInstructions, reportConfig, reportConfigSchema, reportExecution, reportExecutionSummary, reportPlan, reportSkillInstall, reportValue, } from './reporter.js';
|
|
15
|
+
import { resolveNextVersion } from './version.js';
|
|
15
16
|
const mirrorVersion = readInstalledVersion();
|
|
16
17
|
const globalArgs = {
|
|
17
18
|
config: { type: 'string', description: 'Path to mirror.config.toml' },
|
|
@@ -49,6 +50,7 @@ export const createMirrorCommand = () => defineCommand({
|
|
|
49
50
|
subCommands: {
|
|
50
51
|
init: createInitCommand(),
|
|
51
52
|
config: createConfigCommand(),
|
|
53
|
+
agents: createAgentsCommand(),
|
|
52
54
|
version: createVersionCommand(),
|
|
53
55
|
},
|
|
54
56
|
});
|
|
@@ -75,19 +77,16 @@ export const runMirrorCli = async (rawArgs = process.argv.slice(2)) => {
|
|
|
75
77
|
' mirror version plan patch # Preview a patch release plan',
|
|
76
78
|
' mirror version apply minor --commit # Apply a minor release with commit',
|
|
77
79
|
' mirror version plan patch --output=package.json,jsr.json,git # Plan with package, jsr, and git',
|
|
80
|
+
' mirror agents install local # Install guiho-as-mirror in this project',
|
|
81
|
+
' mirror agents instructions # Insert Mirror guidance into AGENTS.md',
|
|
78
82
|
' mirror config schema # Print the configuration file reference',
|
|
79
83
|
'',
|
|
80
84
|
].join('\n') + '\n');
|
|
81
85
|
});
|
|
82
86
|
}
|
|
83
|
-
// Intercept console.error during runMain to suppress citty's default
|
|
84
|
-
// ugly error dump (it does console.error(error, "\n") for non-CLIErrors).
|
|
85
|
-
// We capture the error object and handle it ourselves below.
|
|
86
87
|
let capturedError = undefined;
|
|
87
88
|
const originalConsoleError = console.error;
|
|
88
89
|
console.error = (...args) => {
|
|
89
|
-
// citty prints CLIError messages as strings — let those through
|
|
90
|
-
// citty prints non-CLIError errors as objects — capture those
|
|
91
90
|
if (args.length > 0 && args[0] instanceof Error) {
|
|
92
91
|
capturedError = args[0];
|
|
93
92
|
return;
|
|
@@ -97,8 +96,6 @@ export const runMirrorCli = async (rawArgs = process.argv.slice(2)) => {
|
|
|
97
96
|
const originalProcessExit = process.exit;
|
|
98
97
|
let exitCode;
|
|
99
98
|
process.exit = ((code) => {
|
|
100
|
-
// If runMain calls process.exit(1) after an error, intercept it
|
|
101
|
-
// so we can handle the error ourselves. Let exit(0) through (--help).
|
|
102
99
|
if (code !== 0 && capturedError) {
|
|
103
100
|
exitCode = code;
|
|
104
101
|
return undefined;
|
|
@@ -170,6 +167,7 @@ const createConfigCommand = () => defineCommand({
|
|
|
170
167
|
args: overrideArgs,
|
|
171
168
|
async run(context) {
|
|
172
169
|
const options = cliOptions(context.rawArgs, context.args);
|
|
170
|
+
await prepareAgents(options);
|
|
173
171
|
const config = await loadMirrorConfig(options);
|
|
174
172
|
if (options.format !== 'json')
|
|
175
173
|
process.stdout.write(mirrorBanner(configPathForDisplay(config)));
|
|
@@ -181,6 +179,7 @@ const createConfigCommand = () => defineCommand({
|
|
|
181
179
|
args: overrideArgs,
|
|
182
180
|
async run(context) {
|
|
183
181
|
const options = cliOptions(context.rawArgs, context.args);
|
|
182
|
+
await prepareAgents(options);
|
|
184
183
|
await validateMirrorConfig(options);
|
|
185
184
|
process.stdout.write(reportValue('ok', options.format));
|
|
186
185
|
},
|
|
@@ -197,6 +196,36 @@ const createConfigCommand = () => defineCommand({
|
|
|
197
196
|
}),
|
|
198
197
|
},
|
|
199
198
|
});
|
|
199
|
+
const createAgentsCommand = () => defineCommand({
|
|
200
|
+
meta: { name: 'agents', description: 'Install Mirror agent skills and AGENTS.md instructions.' },
|
|
201
|
+
subCommands: {
|
|
202
|
+
install: defineCommand({
|
|
203
|
+
meta: { name: 'install', description: 'Install the guiho-as-mirror agent skill.' },
|
|
204
|
+
subCommands: {
|
|
205
|
+
local: createInstallSkillCommand('local'),
|
|
206
|
+
global: createInstallSkillCommand('global'),
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
instructions: defineCommand({
|
|
210
|
+
meta: { name: 'instructions', description: 'Insert GUIHO Mirror semantic versioning guidance into AGENTS.md.' },
|
|
211
|
+
args: globalArgs,
|
|
212
|
+
async run(context) {
|
|
213
|
+
const options = cliOptions(context.rawArgs, context.args);
|
|
214
|
+
const result = await ensureMirrorAgentsInstructions(resolve(options.cwd ?? process.cwd()), true);
|
|
215
|
+
process.stdout.write(reportAgentsInstructions(result, options.format));
|
|
216
|
+
},
|
|
217
|
+
}),
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
const createInstallSkillCommand = (scope) => defineCommand({
|
|
221
|
+
meta: { name: scope, description: `Install the guiho-as-mirror skill ${scope}.` },
|
|
222
|
+
args: globalArgs,
|
|
223
|
+
async run(context) {
|
|
224
|
+
const options = cliOptions(context.rawArgs, context.args);
|
|
225
|
+
const result = await installMirrorSkill(scope, { cwd: resolve(options.cwd ?? process.cwd()) });
|
|
226
|
+
process.stdout.write(reportSkillInstall(result, options.format));
|
|
227
|
+
},
|
|
228
|
+
});
|
|
200
229
|
const createVersionCommand = () => defineCommand({
|
|
201
230
|
meta: { name: 'version', description: 'Read, plan, and apply version changes.' },
|
|
202
231
|
subCommands: {
|
|
@@ -205,6 +234,7 @@ const createVersionCommand = () => defineCommand({
|
|
|
205
234
|
args: overrideArgs,
|
|
206
235
|
async run(context) {
|
|
207
236
|
const options = cliOptions(context.rawArgs, context.args);
|
|
237
|
+
await prepareAgents(options);
|
|
208
238
|
const config = await loadMirrorConfig(options);
|
|
209
239
|
const projectName = await resolveProjectName(config);
|
|
210
240
|
process.stdout.write(reportValue(await readCurrentVersion(config, projectName), options.format));
|
|
@@ -215,6 +245,7 @@ const createVersionCommand = () => defineCommand({
|
|
|
215
245
|
args: { ...overrideArgs, ...targetArg },
|
|
216
246
|
async run(context) {
|
|
217
247
|
const options = cliOptions(context.rawArgs, context.args);
|
|
248
|
+
await prepareAgents(options);
|
|
218
249
|
const config = await loadMirrorConfig(options);
|
|
219
250
|
const projectName = await resolveProjectName(config);
|
|
220
251
|
const currentVersion = await readCurrentVersion(config, projectName);
|
|
@@ -226,6 +257,7 @@ const createVersionCommand = () => defineCommand({
|
|
|
226
257
|
args: { ...overrideArgs, ...targetArg },
|
|
227
258
|
async run(context) {
|
|
228
259
|
const options = cliOptions(context.rawArgs, context.args);
|
|
260
|
+
await prepareAgents(options);
|
|
229
261
|
const plan = await buildVersionPlan(String(context.args['target']), options);
|
|
230
262
|
if (options.format !== 'json')
|
|
231
263
|
process.stdout.write(mirrorBanner(plan.configPath ? plan.configPath : ''));
|
|
@@ -237,6 +269,7 @@ const createVersionCommand = () => defineCommand({
|
|
|
237
269
|
args: { ...applyArgs, ...targetArg },
|
|
238
270
|
async run(context) {
|
|
239
271
|
const options = cliOptions(context.rawArgs, context.args);
|
|
272
|
+
await prepareAgents(options);
|
|
240
273
|
const plan = await buildVersionPlan(String(context.args['target']), options);
|
|
241
274
|
if (options.format !== 'json')
|
|
242
275
|
process.stdout.write(mirrorBanner(plan.configPath ? plan.configPath : ''));
|
|
@@ -287,6 +320,9 @@ const outputArg = (value) => {
|
|
|
287
320
|
return adapter;
|
|
288
321
|
});
|
|
289
322
|
};
|
|
323
|
+
const prepareAgents = async (options) => {
|
|
324
|
+
await runMirrorAgentAutomation(options, (message) => console.error(message));
|
|
325
|
+
};
|
|
290
326
|
function readInstalledVersion() {
|
|
291
327
|
try {
|
|
292
328
|
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
package/library/config.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
3
|
*/
|
|
4
|
-
import type { MirrorAdapterName, MirrorCliOptions, MirrorConfig, MirrorConfigDiscovery, MirrorRawConfig } from './types';
|
|
4
|
+
import type { MirrorAdapterName, MirrorCliOptions, MirrorConfig, MirrorConfigDiscovery, MirrorRawConfig } from './types.js';
|
|
5
5
|
export declare const resolveMirrorPath: (cwd: string, path: string) => string;
|
|
6
6
|
export declare const relativeFromCwd: (cwd: string, path: string) => string;
|
|
7
7
|
export declare const discoverMirrorConfig: (cwd: string, explicitPath?: string) => Promise<MirrorConfigDiscovery>;
|
package/library/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../source/config.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../source/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,EAErB,eAAe,EAChB,MAAM,YAAY,CAAA;AAMnB,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,EAAE,MAAM,MAAM,WAAmD,CAAA;AAE9G,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,EAAE,MAAM,MAAM,WAGxD,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAU,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,OAAO,CAAC,qBAAqB,CAa5G,CAAA;AAED,eAAO,MAAM,cAAc,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,eAAe,CAe1E,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAU,UAAS,gBAAqB,KAAG,OAAO,CAAC,YAAY,CAO3F,CAAA;AAED,eAAO,MAAM,qBAAqB,GAChC,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,YAAY,MAAM,GAAG,SAAS,EAC9B,UAAS,gBAAqB,KAC7B,YAuDF,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,iBAAiB,EAAE,KAAK,MAAM,WAuFpE,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,MAAM,iBAAiB,EAAE,KAAK,MAAM,EAAE,mBAAiB,oBAO5F,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,QAAQ,YAAY,WAAoF,CAAA"}
|
package/library/config.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* @copyright Copyright (c) 2026 GUIHO Technologies as represented by Cristóvão GUIHO. All Rights Reserved.
|
|
3
3
|
*/
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
5
6
|
import { basename, isAbsolute, join, relative, resolve } from 'node:path';
|
|
6
|
-
import {
|
|
7
|
+
import { parse as parseToml } from 'smol-toml';
|
|
8
|
+
import { MirrorError } from './errors.js';
|
|
7
9
|
const adapters = new Set(['package.json', 'jsr.json', 'git']);
|
|
8
10
|
const projectNameSources = new Set(['package.json', 'jsr.json']);
|
|
9
11
|
export const resolveMirrorPath = (cwd, path) => (isAbsolute(path) ? path : resolve(cwd, path));
|
|
@@ -27,7 +29,15 @@ export const discoverMirrorConfig = async (cwd, explicitPath) => {
|
|
|
27
29
|
export const readConfigFile = async (path) => {
|
|
28
30
|
if (!existsSync(path))
|
|
29
31
|
throw new MirrorError(`Configuration file not found: ${path}`);
|
|
30
|
-
const
|
|
32
|
+
const content = await readFile(path, 'utf8');
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = parseToml(content);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
throw new MirrorError(`Invalid TOML in configuration file: ${path}\n${message}`);
|
|
40
|
+
}
|
|
31
41
|
if (!isRecord(parsed))
|
|
32
42
|
throw new MirrorError(`Configuration file must contain a TOML object: ${path}`);
|
|
33
43
|
return parsed;
|
|
@@ -57,6 +67,10 @@ export const normalizeMirrorConfig = (raw, cwd, configPath, options = {}) => {
|
|
|
57
67
|
const gitCommit = optionalBoolean(raw.git?.commit, 'git.commit') === true;
|
|
58
68
|
const gitPush = optionalBoolean(raw.git?.push, 'git.push') === true;
|
|
59
69
|
const gitAllowDirty = optionalBoolean(raw.git?.allow_dirty, 'git.allow_dirty') === true;
|
|
70
|
+
const writeChangelog = optionalBoolean(raw.agents?.write_changelog, 'agents.write_changelog') !== false;
|
|
71
|
+
const changelogPath = optionalString(raw.agents?.changelog_path, 'agents.changelog_path') ?? 'CHANGELOG.md';
|
|
72
|
+
const autoAgentsMd = optionalBoolean(raw.agents?.auto_agents_md, 'agents.auto_agents_md') !== false;
|
|
73
|
+
const autoSkillInstall = optionalBoolean(raw.agents?.auto_skill_install, 'agents.auto_skill_install') !== false;
|
|
60
74
|
return {
|
|
61
75
|
schema: 1,
|
|
62
76
|
cwd,
|
|
@@ -83,6 +97,12 @@ export const normalizeMirrorConfig = (raw, cwd, configPath, options = {}) => {
|
|
|
83
97
|
push: options.push === true || gitPush,
|
|
84
98
|
allowDirty: options.allowDirty === true || gitAllowDirty,
|
|
85
99
|
},
|
|
100
|
+
agents: {
|
|
101
|
+
writeChangelog,
|
|
102
|
+
changelogPath,
|
|
103
|
+
autoAgentsMd,
|
|
104
|
+
autoSkillInstall,
|
|
105
|
+
},
|
|
86
106
|
};
|
|
87
107
|
};
|
|
88
108
|
export const createInitConfig = (kind, cwd) => {
|
|
@@ -110,6 +130,12 @@ tag_template = "{name}@{version}"
|
|
|
110
130
|
commit = false
|
|
111
131
|
push = false
|
|
112
132
|
allow_dirty = false
|
|
133
|
+
|
|
134
|
+
[agents]
|
|
135
|
+
write_changelog = true
|
|
136
|
+
changelog_path = "CHANGELOG.md"
|
|
137
|
+
auto_agents_md = true
|
|
138
|
+
auto_skill_install = true
|
|
113
139
|
`;
|
|
114
140
|
}
|
|
115
141
|
if (kind === 'jsr.json') {
|
|
@@ -132,6 +158,12 @@ tag_template = "{name}@{version}"
|
|
|
132
158
|
commit = false
|
|
133
159
|
push = false
|
|
134
160
|
allow_dirty = false
|
|
161
|
+
|
|
162
|
+
[agents]
|
|
163
|
+
write_changelog = true
|
|
164
|
+
changelog_path = "CHANGELOG.md"
|
|
165
|
+
auto_agents_md = true
|
|
166
|
+
auto_skill_install = true
|
|
135
167
|
`;
|
|
136
168
|
}
|
|
137
169
|
return `schema = 1
|
|
@@ -150,13 +182,19 @@ tag_template = "v{version}"
|
|
|
150
182
|
commit = false
|
|
151
183
|
push = false
|
|
152
184
|
allow_dirty = false
|
|
185
|
+
|
|
186
|
+
[agents]
|
|
187
|
+
write_changelog = true
|
|
188
|
+
changelog_path = "CHANGELOG.md"
|
|
189
|
+
auto_agents_md = true
|
|
190
|
+
auto_skill_install = true
|
|
153
191
|
`;
|
|
154
192
|
};
|
|
155
193
|
export const writeInitConfig = async (kind, cwd, overwrite = false) => {
|
|
156
194
|
const path = join(cwd, 'mirror.config.toml');
|
|
157
195
|
if (existsSync(path) && !overwrite)
|
|
158
196
|
throw new MirrorError(`Configuration already exists: ${path}`);
|
|
159
|
-
await
|
|
197
|
+
await writeFile(path, createInitConfig(kind, cwd), 'utf8');
|
|
160
198
|
return path;
|
|
161
199
|
};
|
|
162
200
|
export const configPathForDisplay = (config) => (config.configPath ? relativeFromCwd(config.cwd, config.configPath) : '(none)');
|