@iodes/releasekit 0.1.7 → 0.2.1
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/README.md +71 -5
- package/dist/assets.d.ts +4 -3
- package/dist/assets.js +4 -2
- package/dist/cli.js +115 -27
- package/dist/content.d.ts +7 -5
- package/dist/content.js +3 -1
- package/dist/export.d.ts +1 -0
- package/dist/export.js +33 -17
- package/dist/files.js +7 -2
- package/dist/images.d.ts +5 -3
- package/dist/images.js +3 -1
- package/dist/install.d.ts +36 -3
- package/dist/install.js +33 -5
- package/dist/migrate-config.d.ts +10 -0
- package/dist/migrate-config.js +44 -0
- package/dist/model.d.ts +69 -7
- package/dist/model.js +28 -5
- package/dist/move.d.ts +24 -0
- package/dist/move.js +292 -0
- package/dist/project.d.ts +13 -5
- package/dist/project.js +187 -33
- package/dist/refs.d.ts +6 -0
- package/dist/refs.js +29 -0
- package/dist/setup.d.ts +18 -0
- package/dist/setup.js +55 -0
- package/dist/status.d.ts +31 -0
- package/dist/status.js +52 -0
- package/dist/validate.d.ts +5 -2
- package/dist/validate.js +31 -17
- package/kit/references/adoption.md +3 -1
- package/kit/references/channels.md +88 -0
- package/kit/references/format.md +15 -5
- package/kit/references/workflow.md +4 -4
- package/kit/skills/releasekit-draft/SKILL.md +3 -1
- package/kit/skills/releasekit-finalize/SKILL.md +4 -2
- package/kit/skills/releasekit-image/SKILL.md +2 -0
- package/package.json +2 -1
- package/schemas/bundle.schema.json +54 -5
- package/schemas/config.schema.json +125 -9
- package/schemas/release.schema.json +46 -5
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { checkbox, select } from '@inquirer/prompts';
|
|
2
|
+
import { type ProjectConfig } from './model.js';
|
|
3
|
+
import type { InitOptions } from './install.js';
|
|
4
|
+
export declare const commaList: (value: string) => string[];
|
|
5
|
+
export declare function parseTools(value: string): ("claude" | "codex" | "cursor")[];
|
|
6
|
+
type Theme = ProjectConfig['visuals']['themes'];
|
|
7
|
+
export declare function selectAgentTools(context?: Parameters<typeof checkbox>[1]): Promise<("claude" | "codex" | "cursor")[]> & {
|
|
8
|
+
cancel: () => void;
|
|
9
|
+
};
|
|
10
|
+
interface SetupPrompts {
|
|
11
|
+
tools?: () => Promise<ProjectConfig['tools']>;
|
|
12
|
+
theme?: () => Promise<Theme>;
|
|
13
|
+
}
|
|
14
|
+
export declare function selectImageThemes(context?: Parameters<typeof select>[1]): Promise<"both" | "dark" | "light"> & {
|
|
15
|
+
cancel: () => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function interactiveSetup(options: InitOptions, prompts?: SetupPrompts): Promise<InitOptions>;
|
|
18
|
+
export {};
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { checkbox, select } from '@inquirer/prompts';
|
|
2
|
+
import { configSchema } from './model.js';
|
|
3
|
+
export const commaList = (value) => value.split(',').map(s => s.trim()).filter(Boolean);
|
|
4
|
+
export function parseTools(value) {
|
|
5
|
+
if (value === 'none')
|
|
6
|
+
return [];
|
|
7
|
+
const values = commaList(value);
|
|
8
|
+
if (!values.length)
|
|
9
|
+
throw new Error('Choose codex, claude, cursor, or none.');
|
|
10
|
+
return configSchema.shape.tools.parse(values);
|
|
11
|
+
}
|
|
12
|
+
export function selectAgentTools(context) {
|
|
13
|
+
(context?.output ?? process.stdout).write('Choose at least one coding agent to receive ReleaseKit skills.\nUse Space to select or clear tools, then Enter to continue.\n\n');
|
|
14
|
+
return checkbox({
|
|
15
|
+
message: 'Which agent tools do you use?',
|
|
16
|
+
choices: [
|
|
17
|
+
{ name: 'Codex', value: 'codex', checked: false },
|
|
18
|
+
{ name: 'Claude Code', value: 'claude', checked: false },
|
|
19
|
+
{ name: 'Cursor', value: 'cursor', checked: false },
|
|
20
|
+
],
|
|
21
|
+
required: true,
|
|
22
|
+
loop: false,
|
|
23
|
+
shortcuts: { all: 'a', invert: 'i' },
|
|
24
|
+
}, context);
|
|
25
|
+
}
|
|
26
|
+
export function selectImageThemes(context) {
|
|
27
|
+
(context?.output ?? process.stdout).write('Choose the theme variants for generated release-note illustrations.\nThis default applies to new drafts and can be changed later.\n\n');
|
|
28
|
+
return select({
|
|
29
|
+
message: 'Which image themes should new drafts use?',
|
|
30
|
+
choices: [
|
|
31
|
+
{ name: 'Both dark and light (recommended)', value: 'both',
|
|
32
|
+
description: 'Two matching versions of each illustration, one for dark backgrounds and one for light. Choose this when your product supports both themes.' },
|
|
33
|
+
{ name: 'Dark only', value: 'dark',
|
|
34
|
+
description: 'One version of each illustration for dark backgrounds. Choose this when your release notes are always shown in a dark theme.' },
|
|
35
|
+
{ name: 'Light only', value: 'light',
|
|
36
|
+
description: 'One version of each illustration for light backgrounds. Choose this when your release notes are always shown in a light theme.' },
|
|
37
|
+
],
|
|
38
|
+
default: 'both',
|
|
39
|
+
loop: false,
|
|
40
|
+
}, context);
|
|
41
|
+
}
|
|
42
|
+
// All other settings retain their explicit values or initProject defaults.
|
|
43
|
+
export async function interactiveSetup(options, prompts = {}) {
|
|
44
|
+
try {
|
|
45
|
+
const tools = options.tools ?? await (prompts.tools ?? selectAgentTools)();
|
|
46
|
+
const themes = options.themes ?? await (prompts.theme ?? selectImageThemes)();
|
|
47
|
+
return { ...options, tools, themes };
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error instanceof Error && ['ExitPromptError', 'AbortPromptError'].includes(error.name)) {
|
|
51
|
+
throw new Error('Setup cancelled. No configuration was written.');
|
|
52
|
+
}
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Project } from './project.js';
|
|
2
|
+
import type { ReleaseRef } from './model.js';
|
|
3
|
+
export declare function listReleases(project: Project, channel?: string): Promise<{
|
|
4
|
+
version: string;
|
|
5
|
+
channel?: string;
|
|
6
|
+
status: "draft" | "ready";
|
|
7
|
+
releasedAt: string;
|
|
8
|
+
notes: number;
|
|
9
|
+
locales: string[];
|
|
10
|
+
}[]>;
|
|
11
|
+
export declare function projectStatus(project: Project, selected?: ReleaseRef, channel?: string): Promise<{
|
|
12
|
+
initialized: boolean;
|
|
13
|
+
releases: never[];
|
|
14
|
+
next: string[];
|
|
15
|
+
product?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
initialized: boolean;
|
|
18
|
+
product: string;
|
|
19
|
+
releases: {
|
|
20
|
+
version: string;
|
|
21
|
+
channel?: string | undefined;
|
|
22
|
+
status: "draft" | "ready";
|
|
23
|
+
notes: number;
|
|
24
|
+
valid: boolean;
|
|
25
|
+
errors: string[];
|
|
26
|
+
warnings: string[];
|
|
27
|
+
next: string;
|
|
28
|
+
}[];
|
|
29
|
+
next: string[];
|
|
30
|
+
}>;
|
|
31
|
+
export declare function formatStatus(result: Awaited<ReturnType<typeof projectStatus>>): string;
|
package/dist/status.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Project } from './project.js';
|
|
2
|
+
import { exists } from './files.js';
|
|
3
|
+
import { refKey } from './refs.js';
|
|
4
|
+
import { validate } from './validate.js';
|
|
5
|
+
export async function listReleases(project, channel) {
|
|
6
|
+
await project.config();
|
|
7
|
+
if (channel !== undefined)
|
|
8
|
+
await project.requireChannel(channel);
|
|
9
|
+
const refs = (await project.allRefs()).filter(value => channel === undefined || value.channel === channel);
|
|
10
|
+
return Promise.all(refs.map(async (value) => {
|
|
11
|
+
const release = await project.release(value);
|
|
12
|
+
return { ...value, status: release.status, releasedAt: release.releasedAt, notes: release.notes.length, locales: release.locales };
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
export async function projectStatus(project, selected, channel) {
|
|
16
|
+
if (!(await exists(await project.content('config.yaml')))) {
|
|
17
|
+
return { initialized: false, releases: [], next: ['releasekit init'] };
|
|
18
|
+
}
|
|
19
|
+
const config = await project.config();
|
|
20
|
+
const releases = selected ? [await project.release(selected)] : await listReleases(project, channel);
|
|
21
|
+
const results = await Promise.all(releases.map(async (release) => {
|
|
22
|
+
const validation = await validate(project, release);
|
|
23
|
+
const identity = refKey(release);
|
|
24
|
+
const args = `${release.version}${release.channel === undefined ? '' : ` --channel ${release.channel}`}`;
|
|
25
|
+
let next;
|
|
26
|
+
if (!validation.valid)
|
|
27
|
+
next = `Use releasekit-draft or releasekit-image for ${identity} to resolve the reported issues, then run releasekit validate ${args}.`;
|
|
28
|
+
else if (release.status === 'ready')
|
|
29
|
+
next = `releasekit export --current ${args} --out <new-directory>`;
|
|
30
|
+
else {
|
|
31
|
+
const predecessors = (await project.history(release)).slice(1).filter(item => item.status !== 'ready');
|
|
32
|
+
next = release.channel === undefined && predecessors.length
|
|
33
|
+
? `Finalize earlier releases first: ${predecessors.reverse().map(refKey).join(', ')}.`
|
|
34
|
+
: `releasekit finalize ${args}`;
|
|
35
|
+
}
|
|
36
|
+
return { version: release.version, ...(release.channel === undefined ? {} : { channel: release.channel }), status: release.status,
|
|
37
|
+
notes: typeof release.notes === 'number' ? release.notes : release.notes.length,
|
|
38
|
+
valid: validation.valid, errors: validation.errors, warnings: validation.warnings, next };
|
|
39
|
+
}));
|
|
40
|
+
return { initialized: true, product: config.product, releases: results,
|
|
41
|
+
next: results.length ? [] : ['Use releasekit-draft with a version to select the Git range and write the first draft.'] };
|
|
42
|
+
}
|
|
43
|
+
export function formatStatus(result) {
|
|
44
|
+
const lines = [result.initialized ? `${result.product} — release status` : 'ReleaseKit is not initialized.'];
|
|
45
|
+
for (const release of result.releases) {
|
|
46
|
+
lines.push(`\n${refKey(release)} ${release.status} ${release.notes} notes ${release.valid ? 'validation passed' : 'needs attention'}`);
|
|
47
|
+
lines.push(...release.errors.map(error => ` - ${error}`), ...release.warnings.map(warning => ` Warning: ${warning}`));
|
|
48
|
+
lines.push(` Next: ${release.next}`);
|
|
49
|
+
}
|
|
50
|
+
lines.push(...result.next.map(next => `Next: ${next}`));
|
|
51
|
+
return lines.join('\n');
|
|
52
|
+
}
|
package/dist/validate.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { type ReleaseId } from './model.js';
|
|
1
2
|
import { type Release } from './model.js';
|
|
2
3
|
import { Project } from './project.js';
|
|
3
4
|
export interface Validation {
|
|
4
5
|
version: string;
|
|
6
|
+
channel?: string;
|
|
5
7
|
valid: boolean;
|
|
6
8
|
errors: string[];
|
|
7
9
|
warnings: string[];
|
|
8
10
|
contentHash: string | null;
|
|
9
11
|
}
|
|
10
12
|
export declare function contentHash(project: Project, release: Release): Promise<string>;
|
|
11
|
-
export declare function validate(project: Project, version:
|
|
12
|
-
export declare function
|
|
13
|
+
export declare function validate(project: Project, version: ReleaseId): Promise<Validation>;
|
|
14
|
+
export declare function validateMany(project: Project, versions: ReleaseId[]): Promise<Validation[]>;
|
|
15
|
+
export declare function finalize(project: Project, version: ReleaseId): Promise<Validation>;
|
package/dist/validate.js
CHANGED
|
@@ -1,27 +1,47 @@
|
|
|
1
|
+
import {} from './model.js';
|
|
2
|
+
import { ref, refKey } from './refs.js';
|
|
1
3
|
import { imageSource } from './model.js';
|
|
2
4
|
import { Project, editable } from './project.js';
|
|
3
5
|
import { canonical, digest, readNote, noteHash, identifier } from './files.js';
|
|
4
6
|
import { readVisual, checkReferenceFiles } from './content.js';
|
|
5
7
|
import { validateImages } from './images.js';
|
|
6
|
-
import {
|
|
8
|
+
import { collect, collectSnapshot } from './git.js';
|
|
7
9
|
export async function contentHash(project, release) {
|
|
8
10
|
const { status: _status, contentHash: _hash, ...metadata } = release;
|
|
9
11
|
const parts = [metadata];
|
|
10
12
|
for (const note of release.notes) {
|
|
11
13
|
for (const language of release.locales)
|
|
12
|
-
parts.push(await readNote(await project.releaseFile(release
|
|
14
|
+
parts.push(await readNote(await project.releaseFile(release, `notes/${note.id}/${language}.md`)));
|
|
13
15
|
if (note.image)
|
|
14
|
-
parts.push(await readVisual(project, release
|
|
16
|
+
parts.push(await readVisual(project, release, note.id));
|
|
15
17
|
}
|
|
16
18
|
return digest(canonical(parts));
|
|
17
19
|
}
|
|
18
20
|
export async function validate(project, version) {
|
|
21
|
+
return validateOne(project, version);
|
|
22
|
+
}
|
|
23
|
+
// Reuse structural validation within an export or move, without caching across mutations.
|
|
24
|
+
export async function validateMany(project, versions) {
|
|
25
|
+
const checkedHistory = new Set();
|
|
26
|
+
if (versions.some(v => ref(v).channel !== undefined)) {
|
|
27
|
+
for (const release of await project.channelHistory())
|
|
28
|
+
checkedHistory.add(refKey(release));
|
|
29
|
+
}
|
|
30
|
+
for (const version of versions) {
|
|
31
|
+
if (!checkedHistory.has(refKey(version))) {
|
|
32
|
+
for (const release of await project.history(version))
|
|
33
|
+
checkedHistory.add(refKey(release));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return Promise.all(versions.map(version => validateOne(project, version, checkedHistory)));
|
|
37
|
+
}
|
|
38
|
+
async function validateOne(project, version, checkedHistory) {
|
|
19
39
|
const errors = [], warnings = [];
|
|
20
40
|
let hash = null;
|
|
21
41
|
try {
|
|
22
42
|
const release = await project.release(version);
|
|
23
|
-
if (release.initialContent && (release.source.fromSha !== null || release.source.fromRef !== null
|
|
24
|
-
errors.push('Initial content requires a root baseline
|
|
43
|
+
if (release.initialContent && (release.source.fromSha !== null || release.source.fromRef !== null)) {
|
|
44
|
+
errors.push('Initial content requires a root baseline Git range.');
|
|
25
45
|
}
|
|
26
46
|
// Summaries inspect only the baseline snapshot; ready releases use their finalized fingerprint.
|
|
27
47
|
const summary = release.initialContent === 'summary';
|
|
@@ -74,19 +94,13 @@ export async function validate(project, version) {
|
|
|
74
94
|
}
|
|
75
95
|
}
|
|
76
96
|
try {
|
|
77
|
-
|
|
97
|
+
if (!checkedHistory?.has(refKey(version)))
|
|
98
|
+
await project.history(version, 1);
|
|
78
99
|
}
|
|
79
100
|
catch (error) {
|
|
80
101
|
errors.push(error instanceof Error ? error.message : String(error));
|
|
81
102
|
}
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
checkPrevious(project.root, await project.release(release.previous), release.source);
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
errors.push(error instanceof Error ? error.message : String(error));
|
|
88
|
-
}
|
|
89
|
-
}
|
|
103
|
+
// Git scope was pinned independently; display links can change during moves.
|
|
90
104
|
if (!errors.length)
|
|
91
105
|
hash = await contentHash(project, release);
|
|
92
106
|
if (release.status === 'ready' && release.contentHash !== hash && !errors.length)
|
|
@@ -95,7 +109,7 @@ export async function validate(project, version) {
|
|
|
95
109
|
catch (error) {
|
|
96
110
|
errors.push(error instanceof Error ? error.message : String(error));
|
|
97
111
|
}
|
|
98
|
-
return { version, valid: errors.length === 0, errors, warnings, contentHash: hash };
|
|
112
|
+
return { ...ref(version), valid: errors.length === 0, errors, warnings, contentHash: hash };
|
|
99
113
|
}
|
|
100
114
|
export async function finalize(project, version) {
|
|
101
115
|
const release = await project.release(version);
|
|
@@ -103,8 +117,8 @@ export async function finalize(project, version) {
|
|
|
103
117
|
const result = await validate(project, version);
|
|
104
118
|
if (!result.valid || !result.contentHash)
|
|
105
119
|
throw new Error(result.errors.join('\n'));
|
|
106
|
-
const chain = await project.history(version
|
|
107
|
-
if (chain.slice(1).some(r => r.status !== 'ready'))
|
|
120
|
+
const chain = await project.history(version);
|
|
121
|
+
if (release.channel === undefined && chain.slice(1).some(r => r.status !== 'ready'))
|
|
108
122
|
throw new Error('Finalize the previous releases before finalizing this release.');
|
|
109
123
|
release.status = 'ready';
|
|
110
124
|
release.contentHash = result.contentHash;
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Read this when a product has no ReleaseKit releases and the author is choosing where to start. Resolve the likely baseline through [repository inspection](workflow.md#resolve-release-scope-from-the-repository) before asking; a clear inferred tag interval does not require confirmation. Setup has two separate decisions: the baseline commit, and how to present the product's earlier history. A baseline is the end of the earlier period; regular change notes begin **after** it. Choosing a tag does not mean starting at the commit that created that tag's features.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
For channel-aware first drafts, choose use/non-use and the target channel through [the channel workflow](channels.md#first-draft-and-later-drafts) before applying this guide. Use `--channel` on start and prepare, scope existing-release discovery to that channel, and read its `channels.<name>.history.start`. Its display predecessor can be another channel; Git boundaries remain separate.
|
|
6
|
+
|
|
7
|
+
For an unchanneled history, read existing releases and `history.start` in the project config first. Reuse a saved selection when continuing setup. Existing releases use their explicit `previous` links. An explicitly limited Git interval or a requested full-history release already establishes the work's scope; do not add retrospective work or repeat that choice. For first-use requests with unresolved scope, gather the missing decisions using the workflow's [native question UI](workflow.md#ask-with-the-native-question-ui). Follow [the answer-waiting procedure](workflow.md#wait-for-the-users-answer) after asking. Independent inspection may continue while answers are pending, but do not save an unconfirmed choice or draft dependent copy.
|
|
6
8
|
|
|
7
9
|
## Choose the baseline
|
|
8
10
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Channels and whole-release moves
|
|
2
|
+
|
|
3
|
+
## First draft and later drafts
|
|
4
|
+
|
|
5
|
+
Read the request, conversation, config, and existing releases before choosing anything. Channel setup is editorial work performed by the skill; the CLI never prompts.
|
|
6
|
+
|
|
7
|
+
- With no saved releases and no intentional channel setting, ask whether to use channels. Offer a single history or the initial map below, and accept custom channel names and direct include combinations through free text.
|
|
8
|
+
- If language choices are also unresolved, bundle language and channel-use questions in one native question request when supported. After channel use is decided, ask the first draft's target channel only if still unresolved. Keep at most one unanswered request and wait for its answers as described in [the shared question workflow](workflow.md#ask-with-the-native-question-ui).
|
|
9
|
+
- Save non-use as `channels: false`, or save the chosen map in `releasekit/config.yaml`, before `prepare`. Preserve unrelated settings. Do not add a separate save-confirmation question.
|
|
10
|
+
- Initialization intentionally leaves `channels` absent. Existing projects with saved unchanneled releases and no setting continue their single history without a new first-use prompt. Reuse explicit choices already made in the request, conversation, or config.
|
|
11
|
+
- For later new drafts, `channels: false` means no channel question. With a configured map, reuse an explicit target such as “Draft 2.1 for dev”; use the only configured channel when exactly one exists. Otherwise ask which configured channel to write for. Do not infer it merely from the most recent release, version spelling, or Git branch name.
|
|
12
|
+
- Existing drafts retain their saved channel, source languages, and Git boundaries. If the same version exists in several channels and the request does not resolve the intended release, ask which release to edit. A request to change the existing channel uses the move workflow below.
|
|
13
|
+
- Once a choice is saved, do not ask whether to use channels on every draft. A requested configuration change affects future work and exports; it does not relocate saved releases.
|
|
14
|
+
- While a necessary channel answer is pending, do not prepare folders, scaffold notes, or write channel-specific copy. Independent repository inspection may continue. A preselected choice or elapsed wait is not an answer.
|
|
15
|
+
|
|
16
|
+
Initial channel map, used only when the user selects it:
|
|
17
|
+
|
|
18
|
+
```yaml
|
|
19
|
+
channels:
|
|
20
|
+
prod:
|
|
21
|
+
include: [prod]
|
|
22
|
+
dev:
|
|
23
|
+
include: [dev, prod]
|
|
24
|
+
stage:
|
|
25
|
+
include: [prod]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`include` controls viewing, not where a draft belongs. In this example stage can own releases, but the stage view displays prod only. Never automatically add a channel to its own include list. The CLI rejects empty lists, duplicate includes, and unknown referenced names.
|
|
29
|
+
|
|
30
|
+
Keep the resolved `{ channel, version }` identity through note edits, translations, images, validation, and finalization. Pass `--channel <name>` to each command. Omitting it always addresses the unchanneled history, even when channel settings exist. Avoid presenting CLI argument questions when the actual product choice is already clear.
|
|
31
|
+
|
|
32
|
+
## Display history and export
|
|
33
|
+
|
|
34
|
+
All channel releases share one chain of `{ channel, version }` previous references. Channel-free releases retain a separate chain of plain version references. The source tree is `releases/<channel>/<version>/` or `releases/<version>/` respectively. Duplicate versions across channels remain distinct; never deduplicate their notes by title or version.
|
|
35
|
+
|
|
36
|
+
For example, newest to oldest:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
dev/2.1 -> prod/1.1 -> dev/2.0 -> prod/1.0 -> null
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Export walks that chain without date or version sorting. The dev view above displays all ready entries; prod and stage display only ready prod entries. Excluded channels and unfinished drafts are skipped without stopping traversal. No branch/end-point question is needed: malformed, disconnected, or branching channel histories are errors to repair.
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
releasekit export --channel dev --out ./output/dev
|
|
46
|
+
releasekit export --channel prod --limit 3 --out ./output/prod
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Omitting `--limit` exports all matching releases. `--limit N` counts only entries surviving channel/status filtering and limits the combined result, not each channel separately. `history.limit` is removed; if encountered, replace it with an explicit export option only when that limit is requested. `--current` remains available only for unchanneled exports.
|
|
50
|
+
|
|
51
|
+
The first displayed entry supplies default locales and the current identity. Missing or stale content in selected releases blocks output before files are created. Exported channel `previous` links connect the next included entry and end at null; source references remain unchanged. Files retain `release-notes.<locale>.json`, with channel assets under `assets/<channel>/<version>/`. Re-export to a new directory when rules or membership change.
|
|
52
|
+
|
|
53
|
+
## Git boundaries
|
|
54
|
+
|
|
55
|
+
Display ancestry and Git comparison scope are independent for channel releases. `prepare` links to the current global head, even if that head is another channel or a draft. Do not use that entry's commit as the analysis boundary merely because it is the display predecessor.
|
|
56
|
+
|
|
57
|
+
Choose comparison start from explicit `--from`, the latest saved same-channel release's `source.toSha`, or that channel's saved `history.start`, in that order. Honor a deliberately selected `--from-root`. The CLI pins and checks actual Git ancestry for the comparison interval. If no valid interval is established, inspect the repository and ask only when materially different scopes remain. A different channel's draft does not block finalization.
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
releasekit start --channel prod --at v1.0.0 --past summary --baseline-version 1.0.0
|
|
61
|
+
releasekit prepare 1.0.0 --channel prod
|
|
62
|
+
releasekit prepare 2.1 --channel dev --from <start-ref> --to <end-ref>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Channel start settings live at `channels.<name>.history.start`; unchanneled starts remain at `history.start`. A channel baseline can follow another channel in display history. Language defaults still come from current project config for every new draft, not from either predecessor.
|
|
66
|
+
|
|
67
|
+
`releasedAt` accepts a date or a timestamp including seconds and a timezone (`Z` or UTC offset), with optional milliseconds. New drafts default to current UTC time. Preserve explicitly supplied dates/times and saved values during revisions, finalization, and moves.
|
|
68
|
+
|
|
69
|
+
## Move whole releases
|
|
70
|
+
|
|
71
|
+
Use this workflow for requests such as “Move 1.4.0 to dev”, “Move dev 2.0 and 2.1 to prod”, or “Return these releases to the unchanneled history.” Move the complete release, including all notes, languages, briefs, and assets. Do not rewrite copy, regenerate images, duplicate releases, or reinterpret the request as moving individual feature notes.
|
|
72
|
+
|
|
73
|
+
1. Resolve the source identities and destination from the request and saved files. Ask only if the intended source or destination remains ambiguous. One command selects versions in one source channel (or the unchanneled history) and one destination.
|
|
74
|
+
2. Run the move command with `--dry-run`. Inspect paths, affected references, and collisions. Channel-to-channel moves retain global positions automatically. Crossing between channel and unchanneled histories preserves selected relative order and appends at the newest end by default. Use `--after <version-or-channel/version>` or `--at-start` only for a requested alternative insertion point.
|
|
75
|
+
3. When the request and placement are clear and preflight succeeds, execute the same command without `--dry-run`; do not add another permission question. A missing destination channel needs explicit configuration. Existing destination versions/files stop the batch; do not overwrite, rename, or merge automatically.
|
|
76
|
+
4. Report moved source/destination identities, retained ready/draft status, and updated links/references. Validate the affected ready releases if additional edits occurred. Do not promise that an existing export has changed; export again only when requested.
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
releasekit release move 1.4.0 --to-channel dev --dry-run
|
|
80
|
+
releasekit release move 1.4.0 --to-channel dev
|
|
81
|
+
releasekit release move 2.0 2.1 --from-channel dev --to-channel prod --dry-run
|
|
82
|
+
releasekit release move 2.0 2.1 --from-channel dev --to-channel prod
|
|
83
|
+
releasekit release move 1.4.0 --from-channel prod --to-unchanneled
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use the CLI for the move, not shell folder moves or direct channel-field edits. It preserves original Git SHA boundaries, timestamps, note/translation text, image bytes, and ready/draft status. It checks existing ready fingerprints before updating relocation-related hashes, updates incoming links and managed visual reference paths, preserves stale-image state, and updates existing generated prompts through their generator. Referenced baseline settings move with their baseline unless the destination already has a conflicting start.
|
|
87
|
+
|
|
88
|
+
The batch preflights before mutation and rolls back on write failures. If rollback itself fails, the error names preserved recovery data; report that exact state and resolve it before attempting another move. Do not mark a partial move successful or blanket-rehash previously modified ready content.
|
package/kit/references/format.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Content contract
|
|
2
2
|
|
|
3
|
-
Project configuration is `releasekit/config.yaml`.
|
|
3
|
+
Project configuration is `releasekit/config.yaml`. Channel-free releases live under `releasekit/releases/<version>/`; channel releases use `releasekit/releases/<channel>/<version>/`. See [channels](channels.md) for optional config, qualified identities, and cross-channel display links. Version IDs are filesystem-safe strings, not necessarily semantic versions. A release stores its configured locales and visual policy so future project-default changes do not rewrite past releases. New projects default to English originals (`sourceLocale: en-US`, `locales: [en-US]`). On first use, the agent asks about optional translations only when language choices remain unresolved, then saves the selected source and translations in `releasekit/config.yaml` before preparation. Each new release copies the current project `sourceLocale`, `locales`, and visual policy. Later drafts use those configured languages without asking again, including a single-language choice. Previous releases' language selections do not override config. Explicit release-only language changes are saved in the affected `release.yaml`, preserving project defaults and other releases.
|
|
4
4
|
|
|
5
5
|
Optional `history.start` in the project config records first-use setup: `ref` is the original commit/tag label, `sha` is its immutable commit, `past` is `summary`, `history`, or `skip`, and `version` is the baseline release ID for summary/history or null for skip. `releasekit start` saves this once before any release exists. It creates no content. Existing configs without this field retain the explicit-range workflow. See [first-use setup](adoption.md).
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ Within one release:
|
|
|
18
18
|
|
|
19
19
|
`visuals.accent` makes a project color available for generated images; it does not require that color in every image. In the shared scene's `composition`, record no accent or the exact colored element and its supported state, action, or information meaning. Keep that assignment in `preserve`; neutral scenes remain neutral in both themes.
|
|
20
20
|
|
|
21
|
-
Each `(version, note.id, variant)` has one selected image. Importing replaces the selected slot and then removes unused managed images belonging to this note, including obsolete shared or themed imports. Files referenced by any visual variant or scene in the project are retained, as are other notes' files and source originals outside the note's managed assets. Reimporting identical content reuses its file. Keep existing variant entries until the replacement import succeeds.
|
|
21
|
+
Each `(channel?, version, note.id, variant)` has one selected image. Importing replaces the selected slot and then removes unused managed images belonging to this note, including obsolete shared or themed imports. Files referenced by any visual variant or scene in the project are retained, as are other notes' files and source originals outside the note's managed assets. Reimporting identical content reuses its file. Keep existing variant entries until the replacement import succeeds.
|
|
22
22
|
|
|
23
23
|
Importing `--theme shared` replaces the note's dark/light entries with one shared entry. Importing `--theme dark` or `light` replaces a shared entry and keeps compatible themed entries. Optional `--source generated|provided` updates `scene.source` in the same save as the imported selection; omission preserves the current source. Shared imports require supplied media, and supplied-only subjects still reject generated media. The CLI validates the file before saving, so decoding or metadata-save failure preserves the previous source and selections. A missing configured counterpart stays pending after the first themed import and blocks finalization. See [image transitions](theme-pairing.md#switch-between-shared-and-themed-images).
|
|
24
24
|
|
|
@@ -28,11 +28,11 @@ Adding a note creates the release's saved locale files and clears `emptyReason`.
|
|
|
28
28
|
|
|
29
29
|
Preparation writes only `release.yaml`: `source` records the immutable Git boundaries, and each note later records its relevant commits or paths. No full patch or separate changed-file index is stored. Draft validation checks note references against the pinned Git range or the baseline snapshot for a summary; finalization fingerprints the metadata, note text, and visual briefs. Ready content can be validated and exported without Git history.
|
|
30
30
|
|
|
31
|
-
Optional `initialContent` in a baseline release snapshots the selected `summary` or `history` mode. Either mode requires `source.fromRef: null
|
|
31
|
+
Optional `initialContent` in a baseline release snapshots the selected `summary` or `history` mode. Either mode requires `source.fromRef: null` and `source.fromSha: null` (a root Git range). Display predecessors are independent and may change during a whole-release move. A summary describes the product at `source.toSha`; its evidence may reference only that SHA or tracked paths in that snapshot. History mode analyzes the full history through that SHA with normal range evidence. This distinction is preserved in the content fingerprint but excluded from consumer JSON. An absent field keeps the existing range semantics, including full history when `fromSha` is null; loading old files adds no defaults or changes to their fingerprints.
|
|
32
32
|
|
|
33
33
|
The next release begins after the baseline SHA and links to its version with `previous`. A skipped past creates no baseline release: the first regular draft uses the saved start, and later drafts use explicit boundaries or previous links. Baseline entries count toward the export limit just like other releases.
|
|
34
34
|
|
|
35
|
-
Notes are ordered by their entries in `release.yaml`. Note IDs are unique within a version and shared across locales. Their consumer identity is
|
|
35
|
+
Notes are ordered by their entries in `release.yaml`. Note IDs are unique within a version and shared across locales. Their consumer identity is `(channel?, version, note.id)`; never deduplicate different releases by note ID or title alone.
|
|
36
36
|
|
|
37
37
|
A grouped minor-change note uses the same contract: one note ID, category `fix` or `improvement`, a localized summary title, and an unordered Markdown list in the body. Its metadata carries the evidence paths or commits covering all bullets. Bullets have no separate note records or images; the group uses the normal image and translation policy and exports as one note with its list in `bodyMarkdown`. See [grouping guidance](writing.md#group-minor-changes) for selection and titles.
|
|
38
38
|
|
|
@@ -46,6 +46,16 @@ The `releasekit-finalize` skill reviews the release and runs `releasekit finaliz
|
|
|
46
46
|
|
|
47
47
|
The generated JSON schemas shipped with the package are the structural source of truth. `releasekit export --out <directory>` produces one `release-notes.<locale>.json` per locale saved in the current release, with shared relative image assets in `assets/`. `--locale <locale>` selects one language and keeps the same filename pattern. Each JSON file uses the existing bundle schema and includes only display fields, configured image variants, its locale, and explicit version groups. Source patches, prompts, internal paths, and Git evidence are not included. Consumers should safely render `bodyMarkdown` and use image `variants[theme]` or `variants[fallbackTheme]` without recoloring the raster.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
For unchanneled exports, only `--out` is required. Without `--current`, export selects the unique release that no other saved release names as `previous`, regardless of version spelling, date, or directory order. No releases or cyclic links prevent automatic selection; multiple endpoints require `--current`. A draft endpoint must be finalized before export. Without `--limit`, export follows the entire `previous` chain. `--limit N` accepts any positive safe integer; there is no fixed 100-release cap. Remove obsolete `history.limit` settings and pass an export option instead. The locales saved in the current release determine the default language set, independently of later project configuration changes. Every selected release must contain each requested locale; missing or stale translations fail the whole export before creating output. The command result contains a `files` array of JSON paths, a `releases` count of version groups, and an `assets` count of image files copied once across locales.
|
|
50
50
|
|
|
51
51
|
An export destination must not already exist. This avoids overwriting content or mixing assets from separate builds. Validation completes before the destination is created.
|
|
52
|
+
|
|
53
|
+
## Optional channel fields and timestamps
|
|
54
|
+
|
|
55
|
+
`channels` is absent before the first choice, `false` for explicit non-use, or a nonempty map of channel names to `{ include, history? }`. Each include list is nonempty, unique, and references configured names directly. Channel names use lowercase letters, digits, and hyphens, beginning with a letter; filesystem-reserved names are rejected. `history` is optional and contains only `start`; `history.limit` is no longer supported.
|
|
56
|
+
|
|
57
|
+
Release `channel` is optional. A channel release has `previous: { channel, version }` or null; an unchanneled release retains a version string or null. The global channel chain must contain every channel release exactly once. Unchanneled releases are not members of that chain. New channel drafts link to its latest member; their Git comparison start instead comes from explicit `--from`, the most recent release of the same channel, or that channel's saved start. Existing schemaVersion values and absent fields are preserved.
|
|
58
|
+
|
|
59
|
+
Channel export adds `viewChannel` and `currentChannel` at the top level and `channel` on every release entry. `currentVersion`/`currentChannel` identify the first displayed entry. Exported channel `previous` points to the next entry included in the output, ending with null; the source links remain unchanged. Images use `assets/<channel>/<version>/...`. Channel-free output retains its existing fields and paths.
|
|
60
|
+
|
|
61
|
+
`releasedAt` accepts a valid `YYYY-MM-DD` date or an ISO timestamp with seconds, optional one-to-three fractional digits, and `Z` or an explicit UTC offset. Timezone-less times are rejected. Preparation defaults to `new Date().toISOString()`; explicit input strings and existing date-only values are preserved. The display chain, not timestamps or semantic version comparison, determines order.
|
|
@@ -6,14 +6,14 @@ Use the installed `releasekit` CLI, or the repository's compiled CLI when develo
|
|
|
6
6
|
|
|
7
7
|
The normal skill flow is `releasekit-draft` (source and selected translations), `releasekit-image` (required assets), then `releasekit-finalize` (review, validation, and local confirmation). Translation-only edits also belong to `releasekit-draft`. Skip image work for a release the user explicitly chose to keep text-only; export follows finalization only when requested.
|
|
8
8
|
|
|
9
|
-
1. Read `releasekit/config.yaml` and any existing `release.yaml`. [Resolve languages](#choose-languages) from current project settings for a new draft or the saved selection for an existing draft. Ask only when a first-use choice or requested language change remains unresolved. Save the first-use selection in project config before preparation. Honor the theme policy and product context.
|
|
9
|
+
1. Read `releasekit/config.yaml` and any existing `release.yaml`. [Choose channels](channels.md#first-draft-and-later-drafts) before preparing a new draft, alongside unresolved language choices. Keep the resolved channel in every subsequent command. [Resolve languages](#choose-languages) from current project settings for a new draft or the saved selection for an existing draft. Ask only when a first-use choice or requested language change remains unresolved. Save the first-use selection in project config before preparation. Honor the theme policy and product context.
|
|
10
10
|
2. For a new release, [resolve the version and Git boundaries from the repository](#resolve-release-scope-from-the-repository). Reuse explicit choices and saved boundaries, inspect the relevant release line, and supply the CLI arguments yourself. Briefly state the selected scope and continue when the evidence is clear. For first-use requests with unresolved earlier-history scope, follow [the adoption guide](adoption.md) to summarize, analyze, or skip the period through the baseline.
|
|
11
11
|
3. Run `releasekit prepare`. It creates only `release.yaml`, including the pinned comparison start and end SHAs. Inspect the pinned Git evidence as described below: baseline summaries read the snapshot; other drafts read history, changed paths, and relevant diffs. Do not save a full patch or a separate changed-file index.
|
|
12
12
|
4. Save the selected `sourceLocale` and `locales` in this release. Use [Group minor changes](writing.md#group-minor-changes) to select standalone notes and separate minor-fix and minor-improvement groups before adding notes with `releasekit note add <version> <id>`. Give each group the corresponding `--category fix` or `--category improvement`; its bullet items do not get separate notes. Fill the source Markdown and attach evidence paths or commit SHAs to `release.yaml`, using snapshot evidence for a baseline summary. Keep every note image-enabled by default; use `--no-image` only for the user's explicit text-only choice, never to select just the important notes.
|
|
13
13
|
5. As part of `releasekit-draft`, [translate the selected locales](#translate-selected-locales) and mark reviewed translations current. Finish the source and selected translations before recommending image work, unless the user explicitly limited the draft scope.
|
|
14
14
|
6. Use `releasekit-image` to cover all current drafted notes, including later additions, under [the coverage policy](theme-pairing.md#coverage-and-repeat-runs). For minor groups, [reuse common originals](common-images.md) before generating new images. Choose generated or supplied media and complete each missing visual brief, reusing existing valid images on repeat runs. Run `releasekit image plan`, then handle each request by its action: generate configured variants for `generate`, or find/request an approved capture/image for `provide`. Review and import selected files, using one shared supplied asset when appropriate. Preserve unrelated accepted images and manual edits. For a replacement or regeneration, import into the same note with the intended theme; follow [image transitions](theme-pairing.md#switch-between-shared-and-themed-images) when changing shared/themed usage. Unused managed files are removed after the new selection is saved.
|
|
15
15
|
7. Use `releasekit-finalize` to review factual and visual accuracy, run `releasekit validate <version>`, resolve errors and review warnings, then run `releasekit finalize <version>`. Confirm `status: ready` and a recorded content fingerprint. Review is part of finalization; a validation report alone does not complete this step.
|
|
16
|
-
8. If requested, run `releasekit export --out <directory>` to export recent history to a new output directory.
|
|
16
|
+
8. If requested, run `releasekit export --out <directory>` to export recent history to a new output directory. For unchanneled history, omit `--current` to use the unique release with no successor in the saved `previous` links; pass an explicitly requested version or resolve multiple release endpoints with `--current <version>`. Omit `--limit` to export all matching releases; specify it only for a requested count limit. Omit `--locale` to export all locales saved in the current release as separate `release-notes.<locale>.json` files sharing one `assets/` directory; pass it only when the user requests a specific output language. Every selected version must contain those locales. For channel output, pass the requested `--channel`; the global channel chain is filtered to ready entries in its include list. Do not choose channel-specific endpoints or sort by timestamps. A finalized release is a complete local result even without an export. Finalization does not tag, commit, push, deploy, or publish anything.
|
|
17
17
|
|
|
18
18
|
For an existing draft, read and edit the existing content. `prepare` never overwrites a release. Do not recreate a folder as a shortcut for refreshing one note. Reopen a ready release by setting `status: draft` and `contentHash: null`, then make the targeted change and finalize again.
|
|
19
19
|
|
|
@@ -30,7 +30,7 @@ After additions or removals, validate the release and report outstanding copy, t
|
|
|
30
30
|
|
|
31
31
|
## Resolve release scope from the repository
|
|
32
32
|
|
|
33
|
-
Treat versions, tags, commit SHAs, and CLI arguments as repository discovery work. Missing flags in the user's request are not by themselves a reason to open the question UI. Read the request, saved releases, applicable history-start settings, local tags, and release metadata before deciding that scope is missing.
|
|
33
|
+
For channel drafts, resolve the target channel through [the channel workflow](channels.md#first-draft-and-later-drafts). Its `previous` is the latest entry across channels and is independent from the same-channel Git comparison boundary; use [channel Git boundaries](channels.md#git-boundaries). The following predecessor-based rules otherwise describe unchanneled history. Treat versions, tags, commit SHAs, and CLI arguments as repository discovery work. Missing flags in the user's request are not by themselves a reason to open the question UI. Read the request, saved releases, applicable history-start settings, local tags, and release metadata before deciding that scope is missing.
|
|
34
34
|
|
|
35
35
|
- Reuse the user's explicit version and refs. For an existing draft, keep its pinned `source.fromSha` and `source.toSha` and edit in place; a moved tag does not change that draft's scope.
|
|
36
36
|
- Identify the target on the requested product and release line. For a named released version, use its matching tag according to the repository's naming convention. For current unreleased work, use `HEAD`. For the latest released version, inspect the relevant release tags. Infer an omitted version only from an unambiguous tag or release metadata at the selected target; do not invent a version increment.
|
|
@@ -81,7 +81,7 @@ Questions should address an actual unresolved decision, for example:
|
|
|
81
81
|
|
|
82
82
|
| Skill | Ask when needed | Reuse or decide without another question |
|
|
83
83
|
| --- | --- | --- |
|
|
84
|
-
| `releasekit-draft` | Unchosen first-use translation languages, an ambiguous requested language change, unresolved translation scope or product terminology, earlier-history treatment for first use, or materially different release scopes that repository inspection cannot resolve. | Current project languages for new drafts, saved selections for existing drafts, including a single-language choice; intentional first-use language settings; established terminology, current translations, saved boundaries, and versions or Git ranges resolved from release metadata, tags, and ancestry. |
|
|
84
|
+
| `releasekit-draft` | Unchosen first-use channel use or translation languages, an unspecified new-draft channel when several are configured, an ambiguous requested language change, unresolved translation scope or product terminology, earlier-history treatment for first use, or materially different release scopes that repository inspection cannot resolve. | Current project languages for new drafts, saved selections for existing drafts, including a single-language choice; intentional first-use language settings; established terminology, current translations, saved boundaries, and versions or Git ranges resolved from release metadata, tags, and ancestry. |
|
|
85
85
|
| `releasekit-image` | Ambiguous target notes or a meaningful choice among suitable approved reference images. | Captured theme policy, selected assets, and media-source requirements. Required supplied media must stay supplied; do not offer generation as an alternative. |
|
|
86
86
|
| `releasekit-finalize` | Ambiguous target release or requested export choices that neither the request nor established settings resolves. | Requested fixes and local finalization, completed review when content is unchanged, valid export defaults, and already requested export. |
|
|
87
87
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: releasekit-draft
|
|
3
|
-
description: Create or revise ReleaseKit release notes and their selected translations, including translation-only refreshes. Resolve release scope from the repository and guide first-use setup for an existing product. Use for release copy, not general code implementation.
|
|
3
|
+
description: Create or revise ReleaseKit release notes and their selected translations, including translation-only refreshes and moving whole releases between channels. Resolve release scope from the repository and guide first-use setup for an existing product. Use for release copy, not general code implementation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Before asking anything, check for an unanswered question request already in this conversation. Keep that request pending across skill transitions and queue every new question until it is resolved; follow [the shared question guidance](references/workflow.md#ask-with-the-native-question-ui).
|
|
7
7
|
|
|
8
|
+
For first-use channel decisions, new-draft channel selection, or whole-release channel moves, read [channels and moves](references/channels.md). Save first-use use/non-use alongside unresolved language choices before preparation; later drafts ask for a channel only when the request does not identify one and several are configured. Existing drafts retain their channel. A channel-move request is a whole-release operation through the CLI, not a copy rewrite, translation refresh, or image-regeneration task. Follow the reference's preflight and state-preserving move workflow.
|
|
9
|
+
|
|
8
10
|
Read the project's ReleaseKit config and the existing release before writing. For translation-only requests, preserve the source copy, pinned scope, and accepted images; follow [Translate selected locales](references/workflow.md#translate-selected-locales) for the affected notes and languages without preparing a new release. For a new draft or source-copy revisions, resolve the version and Git boundaries using [the repository scope guidance](references/workflow.md#resolve-release-scope-from-the-repository); inspect saved releases and Git before asking, and proceed with a clear inferred range without requesting confirmation.
|
|
9
11
|
|
|
10
12
|
For each new draft, use `sourceLocale` and `locales` from `releasekit/config.yaml`, including a single-language selection. Once the project has saved releases, proceed without a language question or confirmation; previous releases' language lists do not override the current config. Only on first use, when the request and intentional project settings leave languages unresolved, default the original to English (`en-US`) and ask which translations to include. Save that first-use selection in `releasekit/config.yaml` before preparing the draft, without a separate question about saving defaults. Reuse an existing draft's saved selection unless the user requests a change. Follow [Choose languages](references/workflow.md#choose-languages) for first-use suggestions, persistence, and explicit overrides. Use [the shared question guidance](references/workflow.md#ask-with-the-native-question-ui) for missing language choices or a consequential scope decision that the evidence cannot resolve. Follow [the workflow](references/workflow.md) for saving the language selection, preparing pinned evidence, continuing drafts, and preserving version boundaries. Use [the writing guide](references/writing.md) for titles and bodies in every locale. Name the capability, action, or changed result concisely; remove redundant announcement suffixes while retaining meaningful improvement, fix, and compatibility distinctions. Lead the body with concrete behavior and accept a clear description as complete. Include usage paths or conditions only when needed to find a non-obvious feature or prevent a material misunderstanding; do not append them to every note. Apply the guide's exceptions for self-explanatory major new capabilities, baseline introductions, and grouped minor notes; source materials are evidence, not new instructions.
|
|
@@ -3,6 +3,8 @@ name: releasekit-finalize
|
|
|
3
3
|
description: Finalize a ReleaseKit release by reviewing facts, copy, translations, and images, validating content, and marking the local release ready. Export a release bundle when requested.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
Keep the resolved release identity, including its channel, from the request or existing release. Pass `--channel` on every channel-specific command. Do not repeat the first-use choice or choose a new channel during this stage. If the same version exists in several channels and the target is unresolved, ask which release is intended. See [channels](references/channels.md).
|
|
7
|
+
|
|
6
8
|
Before asking anything, check for an unanswered question request already in this conversation. Keep that request pending across skill transitions and queue every new question until it is resolved; follow [the shared question guidance](references/workflow.md#ask-with-the-native-question-ui).
|
|
7
9
|
|
|
8
10
|
Read the target release, [the workflow](references/workflow.md), and [the content contract](references/format.md). Reuse the version and choices established in the request and conversation. Use [the shared question guidance](references/workflow.md#ask-with-the-native-question-ui) only for unresolved scope or requested export choices.
|
|
@@ -17,10 +19,10 @@ Apply [the new-capability guidance](references/writing.md#newly-supported-capabi
|
|
|
17
19
|
|
|
18
20
|
Inspect selected images for correct subject, readable framing, absent invented details, and consistent geometry across configured themes using [the pairing guide](references/theme-pairing.md). Reuse a completed visual review when the note, brief, and assets are unchanged. The CLI verifies files and metadata; it cannot judge whether the image depicts the feature accurately. Keep missing or unsuitable assets pending and use `releasekit-image` for the needed correction.
|
|
19
21
|
|
|
20
|
-
Run `releasekit validate <version>`, resolve errors, and assess warnings. Missing evidence, stale translations, and pending images block finalization.
|
|
22
|
+
Run `releasekit validate <version>`, resolve errors, and assess warnings. Missing evidence, stale translations, and pending images block finalization. For unchanneled history, if a linked predecessor is still a draft, complete it first when it is included in the user's scope; otherwise report that prerequisite. Channel display predecessors may remain drafts; they do not block this release's finalization. Do not mark an unresolved release ready or stop at a review report when finalization was requested and the release can be completed.
|
|
21
23
|
|
|
22
24
|
For a draft that passes review and validation, run `releasekit finalize <version>` and verify that `release.yaml` contains `status: ready` and a nonempty `contentHash`. The command validates again and records the fingerprint; do not set ready status manually. For an already ready release, validate and reuse it without calling finalize again. If requested corrections require changes, reopen it with `status: draft` and `contentHash: null`, apply the corrections, and finalize again.
|
|
23
25
|
|
|
24
|
-
If export was requested, run `releasekit export --out <directory>`, using [the workflow](references/workflow.md) for defaults.
|
|
26
|
+
If export was requested, run `releasekit export --out <directory>`, using [the workflow](references/workflow.md) for defaults. For channel output, pass the requested `--channel`; it uses the global chain and skips excluded channels and drafts. `--current` is only for unchanneled output. Add `--limit` or `--locale` only for requested overrides; no limit means all matching releases. With no language override, export all locales saved in the current release to separate JSON files that share image assets. Preserve individual release boundaries and configured fallback themes. A successful finalization is a complete local result even when no export was requested. Finalization does not commit, tag, push, deploy, or publish.
|
|
25
27
|
|
|
26
28
|
When finalized, report the version and link to its release file, adding bundle links only for a completed requested export. If blocked, state the remaining work and the actual saved status. Follow [the next-step workflow](references/workflow.md#continue-to-the-next-step) without introducing a separate review stage.
|
|
@@ -3,6 +3,8 @@ name: releasekit-image
|
|
|
3
3
|
description: Create, revise, or import ReleaseKit release illustrations with consistent composition and project-configured dark and light variants. Use for visual release-note assets and generation prompts, not general UI implementation or arbitrary image work.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
Keep the resolved release identity, including its channel, from the request or existing release. Pass `--channel` on every channel-specific command. Do not repeat the first-use choice or choose a new channel during this stage. If the same version exists in several channels and the target is unresolved, ask which release is intended. See [channels](references/channels.md).
|
|
7
|
+
|
|
6
8
|
Before asking anything, check for an unanswered question request already in this conversation. Keep that request pending across skill transitions and queue every new question until it is resolved; follow [the shared question guidance](references/workflow.md#ask-with-the-native-question-ui).
|
|
7
9
|
|
|
8
10
|
Read the saved release's complete current note list and captured visual policy on every invocation. Default to covering every drafted note, including notes added since earlier image work; honor only the user's explicit text-only choices or explicitly limited request. Follow [coverage and repeat runs](references/theme-pairing.md#coverage-and-repeat-runs) to reuse accepted images and fill missing ones. For grouped minor fixes and improvements, follow [common images](references/common-images.md) first: reuse reviewed project originals before generating anything, and create only missing originals or themes. Choose the [media source](references/media-sources.md), then read [the visual language](references/visual-language.md) and applicable [composition recipe](references/composition-recipes.md). Use [the pairing guide](references/theme-pairing.md) for configured themes, cost-aware reuse, and importing images. Consult [the file contract](references/format.md) when editing a brief.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iodes/releasekit",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Git-based visual release notes and portable agent skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"prepack": "npm run build"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"@inquirer/prompts": "^7.10.1",
|
|
43
44
|
"commander": "^15.0.0",
|
|
44
45
|
"sharp": "^0.35.4",
|
|
45
46
|
"yaml": "^2.9.0",
|