@guiho/mirror 3.0.0-alpha.9 → 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.
@@ -1,7 +1,8 @@
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 { MirrorConfig, MirrorJsonObject } from './types';
4
+ import type { MirrorConfig, MirrorJsonObject } from './types.js';
5
+ export declare const ensureGitAvailable: () => Promise<void>;
5
6
  export declare const supportedGitTagTemplates: readonly ["v{version}", "{name}@{version}", "{name}/v{version}"];
6
7
  export declare const readPackageJson: (path: string) => Promise<MirrorJsonObject>;
7
8
  export declare const readJsrJson: (path: string) => Promise<MirrorJsonObject>;
@@ -1 +1 @@
1
- {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../source/adapters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAK7D,eAAO,MAAM,wBAAwB,kEAAmE,CAAA;AAExG,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,gBAAgB,CAAyC,CAAA;AACtH,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,gBAAgB,CAAqC,CAAA;AAE9G,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,EAAE,QAAQ,gBAAgB,kBAE3E,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,oBAAyF,CAAA;AACtJ,eAAO,MAAM,cAAc,GAAU,QAAQ,YAAY,oBAAiF,CAAA;AAC1I,eAAO,MAAM,eAAe,GAAU,QAAQ,YAAY,oBAAsF,CAAA;AAChJ,eAAO,MAAM,WAAW,GAAU,QAAQ,YAAY,oBAA8E,CAAA;AAEpI,eAAO,MAAM,mBAAmB,GAAU,QAAQ,YAAY,EAAE,aAAa,MAAM,kBACiB,CAAA;AAEpG,eAAO,MAAM,eAAe,GAAU,QAAQ,YAAY,EAAE,aAAa,MAAM,kBACa,CAAA;AAE5F,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,kBAI5D,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,gCAK5D,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,EAAE,cAAc,MAAM,oBAIlF,CAAA;AAED,eAAO,MAAM,cAAc,GAAU,QAAQ,YAAY,EAAE,cAAc,MAAM,oBAc9E,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,UAAU,MAAM,EAAE,SAAS,MAAM,EAAE,cAAc,MAAM,WAOnF,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,EAAE,KAAK,MAAM,EAAE,cAAc,MAAM,uBAmBjF,CAAA;AAED,eAAO,MAAM,6BAA6B,GAAI,UAAU,MAAM,SAI7D,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,qBAOhD,CAAA;AAED,eAAO,MAAM,UAAU,GAAU,KAAK,MAAM,qBAG3C,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,EAAE,OAAO,MAAM,EAAE,EAAE,SAAS,MAAM,kBAGlF,CAAA;AAED,eAAO,MAAM,YAAY,GAAU,KAAK,MAAM,EAAE,KAAK,MAAM,kBAE1D,CAAA;AAED,eAAO,MAAM,WAAW,GAAU,KAAK,MAAM,EAAE,eAAe,OAAO,EAAE,aAAa,OAAO,kBAG1F,CAAA"}
1
+ {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../source/adapters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAsBhE,eAAO,MAAM,kBAAkB,qBAI9B,CAAA;AAiBD,eAAO,MAAM,wBAAwB,kEAAmE,CAAA;AAExG,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,gBAAgB,CAAyC,CAAA;AACtH,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,gBAAgB,CAAqC,CAAA;AAE9G,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,EAAE,QAAQ,gBAAgB,kBAE3E,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,oBAAyF,CAAA;AACtJ,eAAO,MAAM,cAAc,GAAU,QAAQ,YAAY,oBAAiF,CAAA;AAC1I,eAAO,MAAM,eAAe,GAAU,QAAQ,YAAY,oBAAsF,CAAA;AAChJ,eAAO,MAAM,WAAW,GAAU,QAAQ,YAAY,oBAA8E,CAAA;AAEpI,eAAO,MAAM,mBAAmB,GAAU,QAAQ,YAAY,EAAE,aAAa,MAAM,kBACiB,CAAA;AAEpG,eAAO,MAAM,eAAe,GAAU,QAAQ,YAAY,EAAE,aAAa,MAAM,kBACa,CAAA;AAE5F,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,kBAI5D,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,gCAK5D,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,QAAQ,YAAY,EAAE,cAAc,MAAM,oBAIlF,CAAA;AAED,eAAO,MAAM,cAAc,GAAU,QAAQ,YAAY,EAAE,cAAc,MAAM,oBAe9E,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,UAAU,MAAM,EAAE,SAAS,MAAM,EAAE,cAAc,MAAM,WAOnF,CAAA;AAED,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,EAAE,KAAK,MAAM,EAAE,cAAc,MAAM,uBAmBjF,CAAA;AAED,eAAO,MAAM,6BAA6B,GAAI,UAAU,MAAM,SAI7D,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,qBAQhD,CAAA;AAED,eAAO,MAAM,UAAU,GAAU,KAAK,MAAM,qBAG3C,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,EAAE,OAAO,MAAM,EAAE,EAAE,SAAS,MAAM,kBAIlF,CAAA;AAED,eAAO,MAAM,YAAY,GAAU,KAAK,MAAM,EAAE,KAAK,MAAM,kBAG1D,CAAA;AAED,eAAO,MAAM,WAAW,GAAU,KAAK,MAAM,EAAE,eAAe,OAAO,EAAE,aAAa,OAAO,kBAI1F,CAAA"}
@@ -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 { $ } from 'bun';
4
+ import { execFile } from 'node:child_process';
5
5
  import { existsSync } from 'node:fs';
6
- import { MirrorError } from './errors';
7
- import { assertValidSemver, sortSemverDescending } from './version';
8
- import { resolveMirrorPath } from './config';
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 Bun.write(path, `${JSON.stringify(object, null, 2)}\n`);
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 $ `git -C ${config.cwd} tag --list`.text();
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 $ `git -C ${cwd} rev-parse --is-inside-work-tree`.quiet();
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 $ `git -C ${cwd} status --porcelain`.quiet().text();
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 $ `git -C ${cwd} add ${path}`.quiet();
105
- await $ `git -C ${cwd} commit -m ${message}`.quiet();
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 $ `git -C ${cwd} tag ${tag} -m ${`Release ${tag}`}`.quiet();
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 $ `git -C ${cwd} push`.quiet();
155
+ await runGit(cwd, ['-C', cwd, 'push']);
113
156
  if (includeTags)
114
- await $ `git -C ${cwd} push --tags`.quiet();
157
+ await runGit(cwd, ['-C', cwd, 'push', '--tags']);
115
158
  };
116
159
  const readJsonObject = async (path, label) => {
117
160
  ensureFile(path, label);
118
- const json = await Bun.file(path).json();
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');
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../source/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgDH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;EAa5B,CAAA;AAEJ,eAAO,MAAM,YAAY,GAAU,kBAA+B,kBAyEjE,CAAA"}
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 { MirrorError } from './errors';
8
- import { readCurrentVersion, resolveProjectName } from './adapters';
9
- import { configPathForDisplay, discoverMirrorConfig, loadMirrorConfig, relativeFromCwd, writeInitConfig } from './config';
10
- import { executeVersionPlan } from './executor';
11
- import { parseMirrorCliOptions } from './flags';
12
- import { buildVersionPlan, validateMirrorConfig } from './plan';
13
- import { mirrorBanner, reportConfig, reportConfigSchema, reportExecution, reportExecutionSummary, reportPlan, reportValue } from './reporter';
14
- import { resolveNextVersion } from './version';
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'));
@@ -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>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../source/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,EAErB,eAAe,EAChB,MAAM,SAAS,CAAA;AAMhB,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,CAQ1E,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,YA6CF,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,iBAAiB,EAAE,KAAK,MAAM,WAqEpE,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,MAAM,iBAAiB,EAAE,KAAK,MAAM,EAAE,mBAAiB,oBAO5F,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,QAAQ,YAAY,WAAoF,CAAA"}
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"}