@clawplays/ospec-cli 0.3.4 → 0.3.5

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 CHANGED
@@ -37,6 +37,12 @@ OSpec is a document-driven workflow for AI-assisted development, helping you def
37
37
  npm install -g @clawplays/ospec-cli
38
38
  ```
39
39
 
40
+ Update inside your project directory:
41
+
42
+ ```bash
43
+ ospec update
44
+ ```
45
+
40
46
  ## Recommended Prompts
41
47
 
42
48
  Most teams only need 3 steps to use OSpec:
@@ -141,6 +147,7 @@ Archive notes:
141
147
  - run your project-specific deploy, test, and QA flow first
142
148
  - use `ospec verify` to confirm the active change is ready
143
149
  - use `ospec finalize` to rebuild indexes and archive the accepted change
150
+ - new projects archive under `changes/archived/YYYY-MM/YYYY-MM-DD/<change-name>`; existing flat archives are reorganized by `ospec update`
144
151
 
145
152
  </details>
146
153
 
package/dist/cli.js CHANGED
@@ -224,7 +224,7 @@ const services_1 = require("./services");
224
224
 
225
225
 
226
226
 
227
- const CLI_VERSION = '0.3.4';
227
+ const CLI_VERSION = '0.3.5';
228
228
 
229
229
  function showInitUsage() {
230
230
  console.log('Usage: ospec init [root-dir] [--summary "..."] [--tech-stack node,react] [--architecture "..."] [--document-language en-US|zh-CN|ja-JP|ar]');
@@ -180,7 +180,7 @@ class ArchiveCommand extends BaseCommand_1.BaseCommand {
180
180
  this.success('Change is ready to archive');
181
181
  return;
182
182
  }
183
- const archivePath = await this.performArchive(targetPath, projectRoot, featureState);
183
+ const archivePath = await this.performArchive(targetPath, projectRoot, featureState, config);
184
184
  this.success(`Change archived to ${archivePath}`);
185
185
  return archivePath;
186
186
  }
@@ -194,11 +194,10 @@ class ArchiveCommand extends BaseCommand_1.BaseCommand {
194
194
  throw error;
195
195
  }
196
196
  }
197
- async performArchive(targetPath, projectRoot, featureState) {
197
+ async performArchive(targetPath, projectRoot, featureState, config) {
198
198
  const archivedRoot = path.join(projectRoot, constants_1.DIR_NAMES.CHANGES, constants_1.DIR_NAMES.ARCHIVED);
199
199
  await services_1.services.fileService.ensureDir(archivedRoot);
200
- const archiveDirName = await this.resolveArchiveDirName(archivedRoot, featureState.feature);
201
- const archivePath = path.join(archivedRoot, archiveDirName);
200
+ const archivePath = await this.resolveArchivePath(archivedRoot, featureState.feature, config);
202
201
  const nextState = {
203
202
  ...featureState,
204
203
  status: 'archived',
@@ -222,9 +221,29 @@ class ArchiveCommand extends BaseCommand_1.BaseCommand {
222
221
  proposal.data.status = status;
223
222
  await services_1.services.fileService.writeFile(proposalPath, gray_matter_1.default.stringify(proposal.content, proposal.data));
224
223
  }
225
- async resolveArchiveDirName(archivedRoot, featureName) {
226
- const datePrefix = new Date().toISOString().slice(0, 10);
227
- const baseName = `${datePrefix}-${featureName}`;
224
+ async resolveArchivePath(archivedRoot, featureName, config) {
225
+ const archiveLayout = config?.archive?.layout === 'month-day' ? 'month-day' : 'flat';
226
+ const archiveDate = this.getLocalArchiveDateParts();
227
+ if (archiveLayout === 'month-day') {
228
+ const archiveDayRoot = path.join(archivedRoot, archiveDate.month, archiveDate.day);
229
+ await services_1.services.fileService.ensureDir(archiveDayRoot);
230
+ const archiveLeafName = await this.resolveArchiveLeafName(archiveDayRoot, featureName);
231
+ return path.join(archiveDayRoot, archiveLeafName);
232
+ }
233
+ const archiveDirName = await this.resolveLegacyArchiveDirName(archivedRoot, archiveDate.day, featureName);
234
+ return path.join(archivedRoot, archiveDirName);
235
+ }
236
+ async resolveArchiveLeafName(archiveDayRoot, featureName) {
237
+ let candidate = featureName;
238
+ let index = 2;
239
+ while (await services_1.services.fileService.exists(path.join(archiveDayRoot, candidate))) {
240
+ candidate = `${featureName}-${index}`;
241
+ index += 1;
242
+ }
243
+ return candidate;
244
+ }
245
+ async resolveLegacyArchiveDirName(archivedRoot, archiveDay, featureName) {
246
+ const baseName = `${archiveDay}-${featureName}`;
228
247
  let candidate = baseName;
229
248
  let index = 2;
230
249
  while (await services_1.services.fileService.exists(path.join(archivedRoot, candidate))) {
@@ -233,6 +252,16 @@ class ArchiveCommand extends BaseCommand_1.BaseCommand {
233
252
  }
234
253
  return candidate;
235
254
  }
255
+ getLocalArchiveDateParts() {
256
+ const now = new Date();
257
+ const year = String(now.getFullYear());
258
+ const monthNumber = String(now.getMonth() + 1).padStart(2, '0');
259
+ const dayNumber = String(now.getDate()).padStart(2, '0');
260
+ return {
261
+ month: `${year}-${monthNumber}`,
262
+ day: `${year}-${monthNumber}-${dayNumber}`,
263
+ };
264
+ }
236
265
  toRelativePath(rootDir, targetPath) {
237
266
  return path.relative(rootDir, targetPath).replace(/\\/g, '/');
238
267
  }
@@ -18,8 +18,14 @@ class UpdateCommand extends BaseCommand_1.BaseCommand {
18
18
  const protocolResult = await services_1.services.projectService.syncProtocolGuidance(targetPath);
19
19
  const toolingResult = await this.syncProjectTooling(targetPath, protocolResult.documentLanguage);
20
20
  const pluginResult = await this.syncEnabledPluginAssets(targetPath);
21
+ const archiveResult = await this.syncArchiveLayout(targetPath);
21
22
  const skillResult = await this.syncInstalledSkills();
22
- const refreshedFiles = [...protocolResult.refreshedFiles, ...toolingResult.refreshedFiles, ...pluginResult.refreshedFiles];
23
+ const refreshedFiles = Array.from(new Set([
24
+ ...protocolResult.refreshedFiles,
25
+ ...toolingResult.refreshedFiles,
26
+ ...pluginResult.refreshedFiles,
27
+ ...(archiveResult.configSaved ? ['.skillrc'] : []),
28
+ ]));
23
29
  const createdFiles = [...protocolResult.createdFiles, ...toolingResult.createdFiles, ...pluginResult.createdFiles];
24
30
  const skippedFiles = [...protocolResult.skippedFiles, ...toolingResult.skippedFiles, ...pluginResult.skippedFiles];
25
31
  this.success(`Updated OSpec assets for ${protocolResult.projectName}`);
@@ -40,8 +46,14 @@ class UpdateCommand extends BaseCommand_1.BaseCommand {
40
46
  if (pluginResult.configSaved) {
41
47
  this.info(' plugin config normalized: .skillrc');
42
48
  }
43
- this.info(' note: update refreshes protocol docs, tooling, hooks, managed skills, and managed assets for already-enabled plugins');
44
- this.info(' note: it does not enable, disable, or migrate existing changes automatically');
49
+ if (archiveResult.configSaved) {
50
+ this.info(' archive layout normalized: .skillrc');
51
+ }
52
+ if (archiveResult.migratedChanges.length > 0) {
53
+ this.info(` archived changes migrated: ${archiveResult.migratedChanges.length}`);
54
+ }
55
+ this.info(' note: update refreshes protocol docs, tooling, hooks, managed skills, managed assets for already-enabled plugins, and the archive layout when needed');
56
+ this.info(' note: it does not enable, disable, or migrate active or queued changes automatically');
45
57
  }
46
58
  async syncProjectTooling(rootDir, documentLanguage) {
47
59
  const toolingPaths = [
@@ -222,6 +234,100 @@ class UpdateCommand extends BaseCommand_1.BaseCommand {
222
234
  }
223
235
  return { createdFiles, skippedFiles };
224
236
  }
237
+ async syncArchiveLayout(rootDir) {
238
+ const rawConfig = await services_1.services.fileService.readJSON((0, path_1.join)(rootDir, '.skillrc'));
239
+ const config = await services_1.services.configManager.loadConfig(rootDir);
240
+ const nextConfig = JSON.parse(JSON.stringify(config));
241
+ const archivedRoot = (0, path_1.join)(rootDir, 'changes', 'archived');
242
+ const migratedChanges = [];
243
+ if (await services_1.services.fileService.exists(archivedRoot)) {
244
+ const entryNames = (await services_1.services.fileService.readDir(archivedRoot)).sort((left, right) => left.localeCompare(right));
245
+ for (const entryName of entryNames) {
246
+ const entryPath = (0, path_1.join)(archivedRoot, entryName);
247
+ const stat = await services_1.services.fileService.stat(entryPath);
248
+ if (!stat.isDirectory()) {
249
+ continue;
250
+ }
251
+ const parsed = this.parseLegacyArchiveDirName(entryName);
252
+ if (!parsed) {
253
+ continue;
254
+ }
255
+ const archivedState = await this.readArchivedChangeState(entryPath);
256
+ if (!archivedState) {
257
+ continue;
258
+ }
259
+ const archiveDayRoot = (0, path_1.join)(archivedRoot, parsed.month, parsed.day);
260
+ await services_1.services.fileService.ensureDir(archiveDayRoot);
261
+ const targetPath = await this.resolveArchiveMigrationTarget(archiveDayRoot, archivedState.feature);
262
+ await services_1.services.fileService.move(entryPath, targetPath);
263
+ migratedChanges.push({
264
+ from: `changes/archived/${entryName}`,
265
+ to: this.toRelativePath(rootDir, targetPath),
266
+ });
267
+ }
268
+ }
269
+ let configSaved = false;
270
+ if (nextConfig.archive?.layout !== 'month-day') {
271
+ nextConfig.archive = {
272
+ ...(nextConfig.archive || {}),
273
+ layout: 'month-day',
274
+ };
275
+ await services_1.services.configManager.saveConfig(rootDir, nextConfig);
276
+ configSaved = true;
277
+ }
278
+ else if (!rawConfig?.archive || rawConfig.archive.layout !== 'month-day') {
279
+ await services_1.services.configManager.saveConfig(rootDir, nextConfig);
280
+ configSaved = true;
281
+ }
282
+ return {
283
+ configSaved,
284
+ migratedChanges,
285
+ };
286
+ }
287
+ parseLegacyArchiveDirName(entryName) {
288
+ const match = /^(\d{4}-\d{2}-\d{2})-(.+)$/.exec(entryName);
289
+ if (!match) {
290
+ return null;
291
+ }
292
+ return {
293
+ month: match[1].slice(0, 7),
294
+ day: match[1],
295
+ leafName: match[2],
296
+ };
297
+ }
298
+ async resolveArchiveMigrationTarget(archiveDayRoot, leafName) {
299
+ let candidate = leafName;
300
+ let index = 2;
301
+ while (await services_1.services.fileService.exists((0, path_1.join)(archiveDayRoot, candidate))) {
302
+ candidate = `${leafName}-${index}`;
303
+ index += 1;
304
+ }
305
+ return (0, path_1.join)(archiveDayRoot, candidate);
306
+ }
307
+ async readArchivedChangeState(entryPath) {
308
+ const statePath = (0, path_1.join)(entryPath, 'state.json');
309
+ if (!(await services_1.services.fileService.exists(statePath))) {
310
+ return null;
311
+ }
312
+ try {
313
+ const state = await services_1.services.fileService.readJSON(statePath);
314
+ if (typeof state?.feature !== 'string' || state.feature.trim().length === 0) {
315
+ return null;
316
+ }
317
+ if (state.status !== 'archived') {
318
+ return null;
319
+ }
320
+ return {
321
+ feature: state.feature.trim(),
322
+ };
323
+ }
324
+ catch {
325
+ return null;
326
+ }
327
+ }
328
+ toRelativePath(rootDir, targetPath) {
329
+ return (0, path_1.relative)(rootDir, targetPath).replace(/\\/g, '/');
330
+ }
225
331
  getManagedSkillNames() {
226
332
  return ['ospec', 'ospec-change'];
227
333
  }
@@ -89,6 +89,9 @@ export interface CheckpointPluginConfig {
89
89
  auto_pass_stitch_review: boolean;
90
90
  };
91
91
  }
92
+ export interface ArchiveConfig {
93
+ layout: 'flat' | 'month-day';
94
+ }
92
95
  export interface SkillrcConfig {
93
96
  version: string;
94
97
  mode: ProjectMode;
@@ -104,6 +107,7 @@ export interface SkillrcConfig {
104
107
  include?: string[];
105
108
  exclude?: string[];
106
109
  };
110
+ archive?: ArchiveConfig;
107
111
  plugins?: {
108
112
  stitch?: StitchPluginConfig;
109
113
  checkpoint?: CheckpointPluginConfig;
@@ -81,6 +81,9 @@ class ConfigManager {
81
81
  index: {
82
82
  exclude: ['node_modules/**', 'dist/**', '*.test.*'],
83
83
  },
84
+ archive: {
85
+ layout: 'month-day',
86
+ },
84
87
  plugins: this.createDefaultPluginsConfig(),
85
88
  workflow,
86
89
  };
@@ -345,6 +348,7 @@ class ConfigManager {
345
348
  }
346
349
  normalizeConfig(config) {
347
350
  const mode = ['lite', 'standard', 'full'].includes(config.mode) ? config.mode : 'full';
351
+ const archive = config.archive && typeof config.archive === 'object' ? config.archive : {};
348
352
  const hooks = config.hooks || {
349
353
  'pre-commit': true,
350
354
  'post-merge': true,
@@ -370,6 +374,9 @@ class ConfigManager {
370
374
  version: config.version === '3.0' ? '4.0' : config.version,
371
375
  mode,
372
376
  documentLanguage: this.normalizeDocumentLanguage(config.documentLanguage),
377
+ archive: {
378
+ layout: archive.layout === 'month-day' ? 'month-day' : 'flat',
379
+ },
373
380
  hooks: {
374
381
  ...normalizedHooks,
375
382
  ...(legacyWarnDefaults
@@ -4385,71 +4385,15 @@ class ProjectService {
4385
4385
 
4386
4386
 
4387
4387
 
4388
- const archivedRoot = path_1.default.join(projectRoot, constants_1.DIR_NAMES.CHANGES, constants_1.DIR_NAMES.ARCHIVED);
4389
-
4390
-
4391
-
4392
-
4393
-
4394
-
4395
-
4396
- await this.fileService.ensureDir(archivedRoot);
4397
-
4398
-
4399
-
4400
-
4401
-
4402
-
4403
-
4404
- const datePrefix = new Date().toISOString().slice(0, 10);
4405
-
4406
-
4388
+ const config = await this.configManager.loadConfig(projectRoot);
4407
4389
 
4408
4390
 
4409
4391
 
4410
4392
 
4411
4393
 
4412
- const baseName = `${datePrefix}-${featureState.feature}`;
4413
-
4414
-
4415
-
4416
-
4417
-
4418
-
4419
-
4420
- let archiveDirName = baseName;
4421
-
4422
-
4423
-
4424
-
4425
-
4426
-
4427
-
4428
- let archiveIndex = 2;
4429
-
4430
-
4431
4394
 
4432
4395
 
4433
-
4434
-
4435
-
4436
- while (await this.fileService.exists(path_1.default.join(archivedRoot, archiveDirName))) {
4437
-
4438
-
4439
-
4440
-
4441
-
4442
-
4443
-
4444
- archiveDirName = `${baseName}-${archiveIndex}`;
4445
-
4446
-
4447
-
4448
-
4449
-
4450
-
4451
-
4452
- archiveIndex += 1;
4396
+ const archivedRoot = path_1.default.join(projectRoot, constants_1.DIR_NAMES.CHANGES, constants_1.DIR_NAMES.ARCHIVED);
4453
4397
 
4454
4398
 
4455
4399
 
@@ -4457,7 +4401,7 @@ class ProjectService {
4457
4401
 
4458
4402
 
4459
4403
 
4460
- }
4404
+ await this.fileService.ensureDir(archivedRoot);
4461
4405
 
4462
4406
 
4463
4407
 
@@ -4465,7 +4409,7 @@ class ProjectService {
4465
4409
 
4466
4410
 
4467
4411
 
4468
- const archivePath = path_1.default.join(archivedRoot, archiveDirName);
4412
+ const archivePath = await this.resolveArchivePath(archivedRoot, featureState.feature, config);
4469
4413
 
4470
4414
 
4471
4415
 
@@ -12449,6 +12393,252 @@ ${formatSuggestion()}
12449
12393
 
12450
12394
 
12451
12395
 
12396
+ async resolveArchivePath(archivedRoot, featureName, config) {
12397
+
12398
+
12399
+
12400
+
12401
+
12402
+ const archiveLayout = config?.archive?.layout === 'month-day' ? 'month-day' : 'flat';
12403
+
12404
+
12405
+
12406
+
12407
+
12408
+ const archiveDate = this.getLocalArchiveDateParts();
12409
+
12410
+
12411
+
12412
+
12413
+
12414
+ if (archiveLayout === 'month-day') {
12415
+
12416
+
12417
+
12418
+
12419
+
12420
+ const archiveDayRoot = path_1.default.join(archivedRoot, archiveDate.month, archiveDate.day);
12421
+
12422
+
12423
+
12424
+
12425
+
12426
+ await this.fileService.ensureDir(archiveDayRoot);
12427
+
12428
+
12429
+
12430
+
12431
+
12432
+ const archiveLeafName = await this.resolveArchiveLeafName(archiveDayRoot, featureName);
12433
+
12434
+
12435
+
12436
+
12437
+
12438
+ return path_1.default.join(archiveDayRoot, archiveLeafName);
12439
+
12440
+
12441
+
12442
+
12443
+
12444
+ }
12445
+
12446
+
12447
+
12448
+
12449
+
12450
+ const archiveDirName = await this.resolveLegacyArchiveDirName(archivedRoot, archiveDate.day, featureName);
12451
+
12452
+
12453
+
12454
+
12455
+
12456
+ return path_1.default.join(archivedRoot, archiveDirName);
12457
+
12458
+
12459
+
12460
+
12461
+
12462
+ }
12463
+
12464
+
12465
+
12466
+
12467
+
12468
+ async resolveArchiveLeafName(archiveDayRoot, featureName) {
12469
+
12470
+
12471
+
12472
+
12473
+
12474
+ let candidate = featureName;
12475
+
12476
+
12477
+
12478
+
12479
+
12480
+ let archiveIndex = 2;
12481
+
12482
+
12483
+
12484
+
12485
+
12486
+ while (await this.fileService.exists(path_1.default.join(archiveDayRoot, candidate))) {
12487
+
12488
+
12489
+
12490
+
12491
+
12492
+ candidate = `${featureName}-${archiveIndex}`;
12493
+
12494
+
12495
+
12496
+
12497
+
12498
+ archiveIndex += 1;
12499
+
12500
+
12501
+
12502
+
12503
+
12504
+ }
12505
+
12506
+
12507
+
12508
+
12509
+
12510
+ return candidate;
12511
+
12512
+
12513
+
12514
+
12515
+
12516
+ }
12517
+
12518
+
12519
+
12520
+
12521
+
12522
+ async resolveLegacyArchiveDirName(archivedRoot, archiveDay, featureName) {
12523
+
12524
+
12525
+
12526
+
12527
+
12528
+ const baseName = `${archiveDay}-${featureName}`;
12529
+
12530
+
12531
+
12532
+
12533
+
12534
+ let candidate = baseName;
12535
+
12536
+
12537
+
12538
+
12539
+
12540
+ let archiveIndex = 2;
12541
+
12542
+
12543
+
12544
+
12545
+
12546
+ while (await this.fileService.exists(path_1.default.join(archivedRoot, candidate))) {
12547
+
12548
+
12549
+
12550
+
12551
+
12552
+ candidate = `${baseName}-${archiveIndex}`;
12553
+
12554
+
12555
+
12556
+
12557
+
12558
+ archiveIndex += 1;
12559
+
12560
+
12561
+
12562
+
12563
+
12564
+ }
12565
+
12566
+
12567
+
12568
+
12569
+
12570
+ return candidate;
12571
+
12572
+
12573
+
12574
+
12575
+
12576
+ }
12577
+
12578
+
12579
+
12580
+
12581
+
12582
+ getLocalArchiveDateParts() {
12583
+
12584
+
12585
+
12586
+
12587
+
12588
+ const now = new Date();
12589
+
12590
+
12591
+
12592
+
12593
+
12594
+ const year = String(now.getFullYear());
12595
+
12596
+
12597
+
12598
+
12599
+
12600
+ const monthNumber = String(now.getMonth() + 1).padStart(2, '0');
12601
+
12602
+
12603
+
12604
+
12605
+
12606
+ const dayNumber = String(now.getDate()).padStart(2, '0');
12607
+
12608
+
12609
+
12610
+
12611
+
12612
+ return {
12613
+
12614
+
12615
+
12616
+
12617
+
12618
+ month: `${year}-${monthNumber}`,
12619
+
12620
+
12621
+
12622
+
12623
+
12624
+ day: `${year}-${monthNumber}-${dayNumber}`,
12625
+
12626
+
12627
+
12628
+
12629
+
12630
+ };
12631
+
12632
+
12633
+
12634
+
12635
+
12636
+ }
12637
+
12638
+
12639
+
12640
+
12641
+
12452
12642
  toRelativePath(rootDir, filePath) {
12453
12643
 
12454
12644
 
@@ -350,13 +350,59 @@ class RunService {
350
350
  if (!(await this.fileService.exists(archivedDir))) {
351
351
  return null;
352
352
  }
353
+ const candidatePaths = await this.listArchivedChangeDirectories(archivedDir);
354
+ const matches = [];
355
+ for (const candidatePath of candidatePaths) {
356
+ const statePath = path_1.default.join(candidatePath, constants_1.FILE_NAMES.STATE);
357
+ if (!(await this.fileService.exists(statePath))) {
358
+ continue;
359
+ }
360
+ try {
361
+ const state = await this.fileService.readJSON(statePath);
362
+ if (state?.feature === changeName && state?.status === 'archived') {
363
+ matches.push(this.toArchivedRelativePath(archivedDir, candidatePath));
364
+ }
365
+ }
366
+ catch {
367
+ continue;
368
+ }
369
+ }
370
+ return matches.sort().at(-1) || null;
371
+ }
372
+ async listArchivedChangeDirectories(archivedDir) {
353
373
  const entries = await fs_extra_1.default.readdir(archivedDir, { withFileTypes: true });
354
- const matched = entries
355
- .filter(entry => entry.isDirectory() && entry.name.endsWith(`-${changeName}`))
356
- .map(entry => entry.name)
357
- .sort()
358
- .at(-1);
359
- return matched ? `changes/archived/${matched}` : null;
374
+ const candidates = [];
375
+ for (const entry of entries) {
376
+ if (!entry.isDirectory()) {
377
+ continue;
378
+ }
379
+ const entryPath = path_1.default.join(archivedDir, entry.name);
380
+ if (/^\d{4}-\d{2}-\d{2}-.+/.test(entry.name)) {
381
+ candidates.push(entryPath);
382
+ continue;
383
+ }
384
+ if (!/^\d{4}-\d{2}$/.test(entry.name)) {
385
+ continue;
386
+ }
387
+ const dayEntries = await fs_extra_1.default.readdir(entryPath, { withFileTypes: true });
388
+ for (const dayEntry of dayEntries) {
389
+ if (!dayEntry.isDirectory() || !/^\d{4}-\d{2}-\d{2}$/.test(dayEntry.name)) {
390
+ continue;
391
+ }
392
+ const dayPath = path_1.default.join(entryPath, dayEntry.name);
393
+ const changeEntries = await fs_extra_1.default.readdir(dayPath, { withFileTypes: true });
394
+ for (const changeEntry of changeEntries) {
395
+ if (changeEntry.isDirectory()) {
396
+ candidates.push(path_1.default.join(dayPath, changeEntry.name));
397
+ }
398
+ }
399
+ }
400
+ }
401
+ return candidates;
402
+ }
403
+ toArchivedRelativePath(archivedDir, candidatePath) {
404
+ const relativePath = path_1.default.relative(archivedDir, candidatePath).replace(/\\/g, '/');
405
+ return `changes/archived/${relativePath}`;
360
406
  }
361
407
  async ensureRunDirectories(rootDir) {
362
408
  await Promise.all([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawplays/ospec-cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "CLI tool for enforcing ospec workflow",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",