Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

## Changed

- Markdown is now writable as markdown wherever it is written, and not on standards and commands alone. Editing a file of a skill, and writing a package description from the new-package page, the package edit form or either of the Context drawers, all carried the WYSIWYG editor by itself: what it would actually save could only be read back by saving it, and content it lays out differently from how it was pasted had nowhere to be corrected. Those five editors now carry the WYSIWYG and Raw tabs the standard and command forms already had. They open on WYSIWYG as before, switching between the two keeps whatever is unsaved, and Save reads the pane you are in, so a keystroke made the instant before clicking it is included either way
- Skill files and package descriptions (new-package page, package edit form, Context drawers) now offer the WYSIWYG and Raw tabs that standards and commands already had

## Fixed

- `playbook submit` now resolves a standard's rule ids before submitting, so removing or editing a rule is applied rather than silently dropped when submitted with `--no-review`

## Removed

# [1.18.0] - 2026-09-22
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { commandFactory } from '@packmind/commands/test';
import { BadRequestException, NotFoundException } from '@nestjs/common';
import { BadRequestException } from '@nestjs/common';
import {
CommandNotFoundError,
CommandSlugAlreadyExistsError,
} from '@packmind/commands';
import { PackmindLogger } from '@packmind/logger';
import { AuthenticatedRequest } from '@packmind/node-utils';
import { stubLogger, createMockInstance } from '@packmind/test-utils';
Expand Down Expand Up @@ -197,7 +201,7 @@ describe('OrganizationsSpacesRecipesController', () => {
});
});

it('throws NotFoundException for non-existent recipe', async () => {
it('throws CommandNotFoundError for non-existent recipe', async () => {
const orgId = createOrganizationId('org-123');
const spaceId = createSpaceId('space-456');
const recipeId = createCommandId('recipe-1');
Expand All @@ -219,7 +223,7 @@ describe('OrganizationsSpacesRecipesController', () => {

await expect(
controller.getCommandById(orgId, spaceId, recipeId, request),
).rejects.toThrow(NotFoundException);
).rejects.toThrow(CommandNotFoundError);
});

it('propagates errors from service', async () => {
Expand Down Expand Up @@ -301,7 +305,7 @@ describe('OrganizationsSpacesRecipesController', () => {
});
});

it('throws NotFoundException for empty versions list', async () => {
it('throws CommandNotFoundError for empty versions list', async () => {
const orgId = createOrganizationId('org-123');
const spaceId = createSpaceId('space-456');
const recipeId = createCommandId('recipe-1');
Expand All @@ -310,7 +314,7 @@ describe('OrganizationsSpacesRecipesController', () => {

await expect(
controller.getCommandVersionsById(orgId, spaceId, recipeId),
).rejects.toThrow(NotFoundException);
).rejects.toThrow(CommandNotFoundError);
});

it('propagates errors from service', async () => {
Expand All @@ -327,6 +331,35 @@ describe('OrganizationsSpacesRecipesController', () => {
});
});

describe('createRecipe', () => {
describe('when the slug already exists in the space', () => {
it('propagates CommandSlugAlreadyExistsError', async () => {
const orgId = createOrganizationId('org-123');
const spaceId = createSpaceId('space-456');
const request = {
user: { userId: createUserId('user-1') },
} as unknown as AuthenticatedRequest;

commandsService.addCommand.mockRejectedValue(
new CommandSlugAlreadyExistsError('my-command', spaceId),
);

await expect(
controller.createCommand(
orgId,
spaceId,
{
name: 'My command',
content: 'content',
slug: 'my-command',
} as Parameters<typeof controller.createCommand>[2],
request,
),
).rejects.toThrow(CommandSlugAlreadyExistsError);
});
});
});

describe('updateRecipe', () => {
describe('when update is successful', () => {
const orgId = createOrganizationId('org-123');
Expand Down Expand Up @@ -442,17 +475,13 @@ describe('OrganizationsSpacesRecipesController', () => {
name: 'Test User',
},
} as unknown as AuthenticatedRequest;
const error = new Error(
`Recipe ${recipeId} does not belong to space ${spaceId}`,
);
const error = new CommandNotFoundError(recipeId, spaceId);

commandsService.updateCommandFromUI.mockRejectedValue(error);

await expect(
controller.updateCommand(orgId, spaceId, recipeId, updateData, request),
).rejects.toThrow(
`Recipe ${recipeId} does not belong to space ${spaceId}`,
);
).rejects.toThrow(CommandNotFoundError);
});
});

Expand Down Expand Up @@ -533,17 +562,13 @@ describe('OrganizationsSpacesRecipesController', () => {
name: 'Test User',
},
} as unknown as AuthenticatedRequest;
const error = new Error(
`Recipe ${recipeId} does not belong to space ${spaceId}`,
);
const error = new CommandNotFoundError(recipeId, spaceId);

commandsService.deleteCommand.mockRejectedValue(error);

await expect(
controller.deleteCommand(orgId, spaceId, recipeId, request),
).rejects.toThrow(
`Recipe ${recipeId} does not belong to space ${spaceId}`,
);
).rejects.toThrow(CommandNotFoundError);
});
});

Expand Down Expand Up @@ -689,12 +714,12 @@ describe('OrganizationsSpacesRecipesController', () => {
});

describe('when service returns null', () => {
it('throws NotFoundException', async () => {
it('throws CommandNotFoundError', async () => {
commandsService.getLatestVersionNumber.mockResolvedValue(null);

await expect(
controller.getCommandLatestVersion(orgId, spaceId, recipeId, request),
).rejects.toThrow(NotFoundException);
).rejects.toThrow(CommandNotFoundError);
});
});
});
Expand Down
Loading