@fission-ai/openspec 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -4
- package/dist/core/config.js +3 -1
- package/dist/core/configurators/slash/base.d.ts +2 -1
- package/dist/core/configurators/slash/base.js +6 -0
- package/dist/core/configurators/slash/codex.d.ts +14 -0
- package/dist/core/configurators/slash/codex.js +108 -0
- package/dist/core/configurators/slash/github-copilot.d.ts +9 -0
- package/dist/core/configurators/slash/github-copilot.js +34 -0
- package/dist/core/configurators/slash/registry.js +6 -0
- package/dist/core/init.js +10 -2
- package/dist/core/update.js +1 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<a href="https://nodejs.org/"><img alt="node version" src="https://img.shields.io/node/v/@fission-ai/openspec?style=flat-square" /></a>
|
|
16
16
|
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
|
|
17
17
|
<a href="https://conventionalcommits.org"><img alt="Conventional Commits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square" /></a>
|
|
18
|
-
<a href="https://discord.gg/
|
|
18
|
+
<a href="https://discord.gg/YctCnvvshC"><img alt="Discord" src="https://img.shields.io/badge/Discord-Join%20the%20community-5865F2?logo=discord&logoColor=white&style=flat-square" /></a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
<p align="center">
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
25
|
<p align="center">
|
|
26
|
-
Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates · Join the <a href="https://discord.gg/
|
|
26
|
+
Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates · Join the <a href="https://discord.gg/YctCnvvshC">OpenSpec Discord</a> for help and questions.
|
|
27
27
|
</p>
|
|
28
28
|
|
|
29
29
|
# OpenSpec
|
|
@@ -40,6 +40,15 @@ Key outcomes:
|
|
|
40
40
|
- Shared visibility into what's proposed, active, or archived.
|
|
41
41
|
- Works with the AI tools you already use: custom slash commands where supported, context rules everywhere else.
|
|
42
42
|
|
|
43
|
+
## How OpenSpec compares (at a glance)
|
|
44
|
+
|
|
45
|
+
- **Lightweight**: simple workflow, no API keys, minimal setup.
|
|
46
|
+
- **Brownfield-first**: works great beyond 0→1. OpenSpec separates the source of truth from proposals: `openspec/specs/` (current truth) and `openspec/changes/` (proposed updates). This keeps diffs explicit and manageable across features.
|
|
47
|
+
- **Change tracking**: proposals, tasks, and spec deltas live together; archiving merges the approved updates back into specs.
|
|
48
|
+
- **Compared to spec-kit & Kiro**: those shine for brand-new features (0→1). OpenSpec also excels when modifying existing behavior (1→n), especially when updates span multiple specs.
|
|
49
|
+
|
|
50
|
+
See the full comparison in [How OpenSpec Compares](#how-openspec-compares).
|
|
51
|
+
|
|
43
52
|
## How It Works
|
|
44
53
|
|
|
45
54
|
```
|
|
@@ -86,6 +95,8 @@ These tools have built-in OpenSpec commands. Select the OpenSpec integration whe
|
|
|
86
95
|
| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
|
|
87
96
|
| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-archive.md` (`.kilocode/workflows/`) |
|
|
88
97
|
| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.windsurf/workflows/`) |
|
|
98
|
+
| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) |
|
|
99
|
+
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
|
|
89
100
|
|
|
90
101
|
Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/openspec-proposal.md`, `/openspec-apply.md`, or `/openspec-archive.md`.
|
|
91
102
|
|
|
@@ -94,7 +105,7 @@ These tools automatically read workflow instructions from `openspec/AGENTS.md`.
|
|
|
94
105
|
|
|
95
106
|
| Tools |
|
|
96
107
|
|-------|
|
|
97
|
-
|
|
|
108
|
+
| Amp • Jules • Gemini CLI • Others |
|
|
98
109
|
|
|
99
110
|
### Install & Initialize
|
|
100
111
|
|
|
@@ -198,7 +209,7 @@ Or run the command yourself in terminal:
|
|
|
198
209
|
$ openspec archive add-profile-filters --yes # Archive the completed change without prompts
|
|
199
210
|
```
|
|
200
211
|
|
|
201
|
-
**Note:** Tools with native slash commands (Claude Code, Cursor) can use the shortcuts shown. All other tools work with natural language requests to "create an OpenSpec proposal", "apply the OpenSpec change", or "archive the change".
|
|
212
|
+
**Note:** Tools with native slash commands (Claude Code, Cursor, Codex) can use the shortcuts shown. All other tools work with natural language requests to "create an OpenSpec proposal", "apply the OpenSpec change", or "archive the change".
|
|
202
213
|
|
|
203
214
|
## Command Reference
|
|
204
215
|
|
|
@@ -296,6 +307,9 @@ Deltas are "patches" that show how specs change:
|
|
|
296
307
|
|
|
297
308
|
## How OpenSpec Compares
|
|
298
309
|
|
|
310
|
+
### vs. spec-kit
|
|
311
|
+
OpenSpec’s two-folder model (`openspec/specs/` for the current truth, `openspec/changes/` for proposed updates) keeps state and diffs separate. This scales when you modify existing features or touch multiple specs. spec-kit is strong for greenfield/0→1 but provides less structure for cross-spec updates and evolving features.
|
|
312
|
+
|
|
299
313
|
### vs. Kiro.dev
|
|
300
314
|
OpenSpec groups every change for a feature in one folder (`openspec/changes/feature-name/`), making it easy to track related specs, tasks, and designs together. Kiro spreads updates across multiple spec folders, which can make feature tracking harder.
|
|
301
315
|
|
package/dist/core/config.js
CHANGED
|
@@ -9,6 +9,8 @@ export const AI_TOOLS = [
|
|
|
9
9
|
{ name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode' },
|
|
10
10
|
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code' },
|
|
11
11
|
{ name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf' },
|
|
12
|
-
{ name: '
|
|
12
|
+
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex' },
|
|
13
|
+
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot' },
|
|
14
|
+
{ name: 'AGENTS.md (works with Amp, VS Code, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' }
|
|
13
15
|
];
|
|
14
16
|
//# sourceMappingURL=config.js.map
|
|
@@ -12,6 +12,7 @@ export declare abstract class SlashCommandConfigurator {
|
|
|
12
12
|
updateExisting(projectPath: string, _openspecDir: string): Promise<string[]>;
|
|
13
13
|
protected abstract getRelativePath(id: SlashCommandId): string;
|
|
14
14
|
protected abstract getFrontmatter(id: SlashCommandId): string | undefined;
|
|
15
|
-
|
|
15
|
+
resolveAbsolutePath(projectPath: string, id: SlashCommandId): string;
|
|
16
|
+
protected updateBody(filePath: string, body: string): Promise<void>;
|
|
16
17
|
}
|
|
17
18
|
//# sourceMappingURL=base.d.ts.map
|
|
@@ -45,6 +45,12 @@ export class SlashCommandConfigurator {
|
|
|
45
45
|
}
|
|
46
46
|
return updated;
|
|
47
47
|
}
|
|
48
|
+
// Resolve absolute path for a given slash command target. Subclasses may override
|
|
49
|
+
// to redirect to tool-specific locations (e.g., global directories).
|
|
50
|
+
resolveAbsolutePath(projectPath, id) {
|
|
51
|
+
const rel = this.getRelativePath(id);
|
|
52
|
+
return path.join(projectPath, rel);
|
|
53
|
+
}
|
|
48
54
|
async updateBody(filePath, body) {
|
|
49
55
|
const content = await FileSystemUtils.readFile(filePath);
|
|
50
56
|
const startIndex = content.indexOf(OPENSPEC_MARKERS.start);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SlashCommandConfigurator } from "./base.js";
|
|
2
|
+
import { SlashCommandId } from "../../templates/index.js";
|
|
3
|
+
export declare class CodexSlashCommandConfigurator extends SlashCommandConfigurator {
|
|
4
|
+
readonly toolId = "codex";
|
|
5
|
+
readonly isAvailable = true;
|
|
6
|
+
protected getRelativePath(id: SlashCommandId): string;
|
|
7
|
+
protected getFrontmatter(id: SlashCommandId): string | undefined;
|
|
8
|
+
private getGlobalPromptsDir;
|
|
9
|
+
generateAll(projectPath: string, _openspecDir: string): Promise<string[]>;
|
|
10
|
+
updateExisting(projectPath: string, _openspecDir: string): Promise<string[]>;
|
|
11
|
+
private updateFullFile;
|
|
12
|
+
resolveAbsolutePath(_projectPath: string, id: SlashCommandId): string;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import { SlashCommandConfigurator } from "./base.js";
|
|
4
|
+
import { TemplateManager } from "../../templates/index.js";
|
|
5
|
+
import { FileSystemUtils } from "../../../utils/file-system.js";
|
|
6
|
+
import { OPENSPEC_MARKERS } from "../../config.js";
|
|
7
|
+
const FILE_PATHS = {
|
|
8
|
+
proposal: ".codex/prompts/openspec-proposal.md",
|
|
9
|
+
apply: ".codex/prompts/openspec-apply.md",
|
|
10
|
+
archive: ".codex/prompts/openspec-archive.md",
|
|
11
|
+
};
|
|
12
|
+
export class CodexSlashCommandConfigurator extends SlashCommandConfigurator {
|
|
13
|
+
toolId = "codex";
|
|
14
|
+
isAvailable = true;
|
|
15
|
+
getRelativePath(id) {
|
|
16
|
+
return FILE_PATHS[id];
|
|
17
|
+
}
|
|
18
|
+
getFrontmatter(id) {
|
|
19
|
+
// Codex supports YAML frontmatter with description and argument-hint fields,
|
|
20
|
+
// plus $ARGUMENTS to capture all arguments as a single string.
|
|
21
|
+
const frontmatter = {
|
|
22
|
+
proposal: `---
|
|
23
|
+
description: Scaffold a new OpenSpec change and validate strictly.
|
|
24
|
+
argument-hint: request or feature description
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
$ARGUMENTS`,
|
|
28
|
+
apply: `---
|
|
29
|
+
description: Implement an approved OpenSpec change and keep tasks in sync.
|
|
30
|
+
argument-hint: change-id
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
$ARGUMENTS`,
|
|
34
|
+
archive: `---
|
|
35
|
+
description: Archive a deployed OpenSpec change and update specs.
|
|
36
|
+
argument-hint: change-id
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
$ARGUMENTS`,
|
|
40
|
+
};
|
|
41
|
+
return frontmatter[id];
|
|
42
|
+
}
|
|
43
|
+
getGlobalPromptsDir() {
|
|
44
|
+
const home = (process.env.CODEX_HOME && process.env.CODEX_HOME.trim())
|
|
45
|
+
? process.env.CODEX_HOME.trim()
|
|
46
|
+
: path.join(os.homedir(), ".codex");
|
|
47
|
+
return path.join(home, "prompts");
|
|
48
|
+
}
|
|
49
|
+
// Codex discovers prompts globally. Generate directly in the global directory
|
|
50
|
+
// and wrap shared body with markers.
|
|
51
|
+
async generateAll(projectPath, _openspecDir) {
|
|
52
|
+
const createdOrUpdated = [];
|
|
53
|
+
for (const target of this.getTargets()) {
|
|
54
|
+
const body = TemplateManager.getSlashCommandBody(target.id).trim();
|
|
55
|
+
const promptsDir = this.getGlobalPromptsDir();
|
|
56
|
+
const filePath = path.join(promptsDir, path.basename(target.path));
|
|
57
|
+
await FileSystemUtils.createDirectory(path.dirname(filePath));
|
|
58
|
+
if (await FileSystemUtils.fileExists(filePath)) {
|
|
59
|
+
await this.updateFullFile(filePath, target.id, body);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const frontmatter = this.getFrontmatter(target.id);
|
|
63
|
+
const sections = [];
|
|
64
|
+
if (frontmatter)
|
|
65
|
+
sections.push(frontmatter.trim());
|
|
66
|
+
sections.push(`${OPENSPEC_MARKERS.start}\n${body}\n${OPENSPEC_MARKERS.end}`);
|
|
67
|
+
await FileSystemUtils.writeFile(filePath, sections.join("\n") + "\n");
|
|
68
|
+
}
|
|
69
|
+
createdOrUpdated.push(target.path);
|
|
70
|
+
}
|
|
71
|
+
return createdOrUpdated;
|
|
72
|
+
}
|
|
73
|
+
async updateExisting(projectPath, _openspecDir) {
|
|
74
|
+
const updated = [];
|
|
75
|
+
for (const target of this.getTargets()) {
|
|
76
|
+
const promptsDir = this.getGlobalPromptsDir();
|
|
77
|
+
const filePath = path.join(promptsDir, path.basename(target.path));
|
|
78
|
+
if (await FileSystemUtils.fileExists(filePath)) {
|
|
79
|
+
const body = TemplateManager.getSlashCommandBody(target.id).trim();
|
|
80
|
+
await this.updateFullFile(filePath, target.id, body);
|
|
81
|
+
updated.push(target.path);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return updated;
|
|
85
|
+
}
|
|
86
|
+
// Update both frontmatter and body in an existing file
|
|
87
|
+
async updateFullFile(filePath, id, body) {
|
|
88
|
+
const content = await FileSystemUtils.readFile(filePath);
|
|
89
|
+
const startIndex = content.indexOf(OPENSPEC_MARKERS.start);
|
|
90
|
+
if (startIndex === -1) {
|
|
91
|
+
throw new Error(`Missing OpenSpec start marker in ${filePath}`);
|
|
92
|
+
}
|
|
93
|
+
// Replace everything before the start marker with the new frontmatter
|
|
94
|
+
const frontmatter = this.getFrontmatter(id);
|
|
95
|
+
const sections = [];
|
|
96
|
+
if (frontmatter)
|
|
97
|
+
sections.push(frontmatter.trim());
|
|
98
|
+
sections.push(`${OPENSPEC_MARKERS.start}\n${body}\n${OPENSPEC_MARKERS.end}`);
|
|
99
|
+
await FileSystemUtils.writeFile(filePath, sections.join("\n") + "\n");
|
|
100
|
+
}
|
|
101
|
+
// Resolve to the global prompts location for configuration detection
|
|
102
|
+
resolveAbsolutePath(_projectPath, id) {
|
|
103
|
+
const promptsDir = this.getGlobalPromptsDir();
|
|
104
|
+
const fileName = path.basename(FILE_PATHS[id]);
|
|
105
|
+
return path.join(promptsDir, fileName);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SlashCommandConfigurator } from './base.js';
|
|
2
|
+
import { SlashCommandId } from '../../templates/index.js';
|
|
3
|
+
export declare class GitHubCopilotSlashCommandConfigurator extends SlashCommandConfigurator {
|
|
4
|
+
readonly toolId = "github-copilot";
|
|
5
|
+
readonly isAvailable = true;
|
|
6
|
+
protected getRelativePath(id: SlashCommandId): string;
|
|
7
|
+
protected getFrontmatter(id: SlashCommandId): string;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=github-copilot.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { SlashCommandConfigurator } from './base.js';
|
|
2
|
+
const FILE_PATHS = {
|
|
3
|
+
proposal: '.github/prompts/openspec-proposal.prompt.md',
|
|
4
|
+
apply: '.github/prompts/openspec-apply.prompt.md',
|
|
5
|
+
archive: '.github/prompts/openspec-archive.prompt.md'
|
|
6
|
+
};
|
|
7
|
+
const FRONTMATTER = {
|
|
8
|
+
proposal: `---
|
|
9
|
+
description: Scaffold a new OpenSpec change and validate strictly.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
$ARGUMENTS`,
|
|
13
|
+
apply: `---
|
|
14
|
+
description: Implement an approved OpenSpec change and keep tasks in sync.
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
$ARGUMENTS`,
|
|
18
|
+
archive: `---
|
|
19
|
+
description: Archive a deployed OpenSpec change and update specs.
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
$ARGUMENTS`
|
|
23
|
+
};
|
|
24
|
+
export class GitHubCopilotSlashCommandConfigurator extends SlashCommandConfigurator {
|
|
25
|
+
toolId = 'github-copilot';
|
|
26
|
+
isAvailable = true;
|
|
27
|
+
getRelativePath(id) {
|
|
28
|
+
return FILE_PATHS[id];
|
|
29
|
+
}
|
|
30
|
+
getFrontmatter(id) {
|
|
31
|
+
return FRONTMATTER[id];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=github-copilot.js.map
|
|
@@ -3,6 +3,8 @@ import { CursorSlashCommandConfigurator } from './cursor.js';
|
|
|
3
3
|
import { WindsurfSlashCommandConfigurator } from './windsurf.js';
|
|
4
4
|
import { KiloCodeSlashCommandConfigurator } from './kilocode.js';
|
|
5
5
|
import { OpenCodeSlashCommandConfigurator } from './opencode.js';
|
|
6
|
+
import { CodexSlashCommandConfigurator } from './codex.js';
|
|
7
|
+
import { GitHubCopilotSlashCommandConfigurator } from './github-copilot.js';
|
|
6
8
|
export class SlashCommandRegistry {
|
|
7
9
|
static configurators = new Map();
|
|
8
10
|
static {
|
|
@@ -11,11 +13,15 @@ export class SlashCommandRegistry {
|
|
|
11
13
|
const windsurf = new WindsurfSlashCommandConfigurator();
|
|
12
14
|
const kilocode = new KiloCodeSlashCommandConfigurator();
|
|
13
15
|
const opencode = new OpenCodeSlashCommandConfigurator();
|
|
16
|
+
const codex = new CodexSlashCommandConfigurator();
|
|
17
|
+
const githubCopilot = new GitHubCopilotSlashCommandConfigurator();
|
|
14
18
|
this.configurators.set(claude.toolId, claude);
|
|
15
19
|
this.configurators.set(cursor.toolId, cursor);
|
|
16
20
|
this.configurators.set(windsurf.toolId, windsurf);
|
|
17
21
|
this.configurators.set(kilocode.toolId, kilocode);
|
|
18
22
|
this.configurators.set(opencode.toolId, opencode);
|
|
23
|
+
this.configurators.set(codex.toolId, codex);
|
|
24
|
+
this.configurators.set(githubCopilot.toolId, githubCopilot);
|
|
19
25
|
}
|
|
20
26
|
static register(configurator) {
|
|
21
27
|
this.configurators.set(configurator.toolId, configurator);
|
package/dist/core/init.js
CHANGED
|
@@ -331,7 +331,7 @@ export class InitCommand {
|
|
|
331
331
|
kind: 'heading',
|
|
332
332
|
value: OTHER_TOOLS_HEADING_VALUE,
|
|
333
333
|
label: {
|
|
334
|
-
primary: 'Other tools (use Universal AGENTS.md for
|
|
334
|
+
primary: 'Other tools (use Universal AGENTS.md for Amp, VS Code, GitHub Copilot, …)',
|
|
335
335
|
},
|
|
336
336
|
selectable: false,
|
|
337
337
|
},
|
|
@@ -369,7 +369,8 @@ export class InitCommand {
|
|
|
369
369
|
if (!slashConfigurator)
|
|
370
370
|
return false;
|
|
371
371
|
for (const target of slashConfigurator.getTargets()) {
|
|
372
|
-
|
|
372
|
+
const absolute = slashConfigurator.resolveAbsolutePath(projectPath, target.id);
|
|
373
|
+
if (await FileSystemUtils.fileExists(absolute))
|
|
373
374
|
return true;
|
|
374
375
|
}
|
|
375
376
|
return false;
|
|
@@ -470,6 +471,13 @@ export class InitCommand {
|
|
|
470
471
|
console.log(PALETTE.lightGray(' "Please explain the OpenSpec workflow from openspec/AGENTS.md'));
|
|
471
472
|
console.log(PALETTE.lightGray(' and how I should work with you on this project"'));
|
|
472
473
|
console.log(PALETTE.darkGray('────────────────────────────────────────────────────────────\n'));
|
|
474
|
+
// Codex heads-up: prompts installed globally
|
|
475
|
+
const selectedToolIds = new Set(selectedTools.map((t) => t.value));
|
|
476
|
+
if (selectedToolIds.has('codex')) {
|
|
477
|
+
console.log(PALETTE.white('Codex setup note'));
|
|
478
|
+
console.log(PALETTE.midGray('Prompts installed to ~/.codex/prompts (or $CODEX_HOME/prompts).'));
|
|
479
|
+
console.log();
|
|
480
|
+
}
|
|
473
481
|
}
|
|
474
482
|
formatToolNames(tools) {
|
|
475
483
|
const names = tools
|
package/dist/core/update.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fission-ai/openspec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "AI-native system for spec-driven development",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openspec",
|
|
@@ -62,7 +62,10 @@
|
|
|
62
62
|
"test:watch": "vitest",
|
|
63
63
|
"test:ui": "vitest --ui",
|
|
64
64
|
"test:coverage": "vitest --coverage",
|
|
65
|
-
"
|
|
65
|
+
"check:pack-version": "node scripts/pack-version-check.mjs",
|
|
66
|
+
"release": "pnpm run release:ci",
|
|
67
|
+
"release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
|
|
68
|
+
"release:local": "pnpm exec changeset version && pnpm run check:pack-version && pnpm exec changeset publish",
|
|
66
69
|
"changeset": "changeset"
|
|
67
70
|
}
|
|
68
71
|
}
|