@thiagodiogo/pscode 2.2.2 → 2.4.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/dist/core/init.d.ts +0 -2
- package/dist/core/init.js +7 -77
- package/dist/core/profile-sync-drift.js +2 -15
- package/dist/core/profiles.d.ts +3 -3
- package/dist/core/profiles.js +3 -15
- package/dist/core/shared/index.d.ts +1 -0
- package/dist/core/shared/index.js +1 -0
- package/dist/core/shared/prune-orphans.d.ts +39 -0
- package/dist/core/shared/prune-orphans.js +149 -0
- package/dist/core/shared/skill-generation.js +4 -14
- package/dist/core/shared/tool-detection.d.ts +2 -2
- package/dist/core/shared/tool-detection.js +1 -13
- package/dist/core/templates/skill-templates.d.ts +2 -7
- package/dist/core/templates/skill-templates.js +2 -7
- package/dist/core/templates/workflows/{archive-change.d.ts → complete-change.d.ts} +1 -1
- package/dist/core/templates/workflows/{archive-change.js → complete-change.js} +2 -2
- package/dist/core/templates/workflows/grill-me.d.ts +13 -0
- package/dist/core/templates/workflows/grill-me.js +64 -0
- package/dist/core/templates/workflows/propose.js +12 -0
- package/dist/core/update.d.ts +0 -20
- package/dist/core/update.js +29 -115
- package/package.json +1 -1
- package/dist/core/templates/workflows/bulk-archive-change.d.ts +0 -10
- package/dist/core/templates/workflows/bulk-archive-change.js +0 -491
- package/dist/core/templates/workflows/continue-change.d.ts +0 -10
- package/dist/core/templates/workflows/continue-change.js +0 -233
- package/dist/core/templates/workflows/ff-change.d.ts +0 -10
- package/dist/core/templates/workflows/ff-change.js +0 -199
- package/dist/core/templates/workflows/new-change.d.ts +0 -10
- package/dist/core/templates/workflows/new-change.js +0 -142
- package/dist/core/templates/workflows/onboard.d.ts +0 -10
- package/dist/core/templates/workflows/onboard.js +0 -606
- package/dist/core/templates/workflows/verify-change.d.ts +0 -10
- package/dist/core/templates/workflows/verify-change.js +0 -337
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export function getGrillMeSkillTemplate() {
|
|
2
|
+
return {
|
|
3
|
+
name: 'pscode-grill-me',
|
|
4
|
+
description: 'Interroga um plano antes da implementação — uma pergunta por vez, com resposta recomendada, explorando o código quando há evidência, até entendimento compartilhado. Use quando o usuário quer validar e refinar um plano antes de escrever artefatos ou código.',
|
|
5
|
+
instructions: getGrillMeInstructions(),
|
|
6
|
+
compatibility: 'Works with any pscode project.',
|
|
7
|
+
metadata: { author: 'pscode', version: '1.0' },
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function getGrillMeInstructions() {
|
|
11
|
+
return `Interrogue o plano do usuário até atingir entendimento compartilhado sobre o que realmente deve existir — não apenas sobre o que foi descrito. O objetivo é pressionar cada decisão e ambiguidade do plano **antes** de escrever qualquer artefato ou código.
|
|
12
|
+
|
|
13
|
+
**Princípio central**: faça **uma pergunta por vez**. Nunca despeje uma lista de perguntas de uma só vez. Aguarde a resposta, incorpore-a ao seu entendimento e só então formule a próxima.
|
|
14
|
+
|
|
15
|
+
**Steps**
|
|
16
|
+
|
|
17
|
+
1. **Entenda o plano inicial**
|
|
18
|
+
|
|
19
|
+
Leia a descrição do usuário (e qualquer artefato/contexto já disponível). Identifique a árvore de decisões: requisitos, escopo, abordagem técnica, trade-offs e os pontos onde o plano está vago, ambíguo ou assume algo não dito.
|
|
20
|
+
|
|
21
|
+
2. **Explore o código antes de perguntar**
|
|
22
|
+
|
|
23
|
+
Se uma pergunta pode ser respondida com evidência presente no próprio repositório (convenções existentes, padrões de arquitetura, como features semelhantes já foram feitas), **investigue o código** em vez de perguntar ao usuário. Use o resultado da investigação para formular perguntas melhores ou para já resolver a decisão.
|
|
24
|
+
|
|
25
|
+
Só pergunte ao usuário o que o código não responde — decisões de produto, prioridades, trade-offs e intenção.
|
|
26
|
+
|
|
27
|
+
3. **Conduza a interrogação — uma pergunta por vez**
|
|
28
|
+
|
|
29
|
+
Para cada ponto não resolvido, na ordem em que as dependências entre decisões exigem (resolva primeiro o que destrava as demais):
|
|
30
|
+
|
|
31
|
+
- Faça **uma** pergunta clara e específica.
|
|
32
|
+
- Sempre acompanhe a pergunta com a **sua resposta recomendada** e um motivo curto — para orientar a decisão e permitir que o usuário apenas concorde e siga em frente quando a recomendação fizer sentido.
|
|
33
|
+
- Quando houver opções discretas, use a **ferramenta AskUserQuestion** com a recomendação como primeira opção (marcada como "(Recomendada)"). Para perguntas abertas, formule a recomendação no texto.
|
|
34
|
+
- Aguarde a resposta antes de prosseguir. Incorpore-a e reavalie a árvore de decisão: a resposta pode tornar perguntas seguintes desnecessárias ou abrir novas.
|
|
35
|
+
|
|
36
|
+
4. **Navegue a árvore de decisão progressivamente**
|
|
37
|
+
|
|
38
|
+
À medida que cada decisão é resolvida, atualize seu entendimento e siga para a próxima decisão relevante. Não pergunte algo que já foi respondido (direta ou indiretamente) por uma resposta anterior ou pelo código.
|
|
39
|
+
|
|
40
|
+
5. **Encerre por entendimento compartilhado**
|
|
41
|
+
|
|
42
|
+
Quando todos os ramos relevantes da árvore de decisão estiverem resolvidos e não restar ambiguidade material, **encerre a interrogação** e apresente um **resumo do entendimento compartilhado**: o que será construído, as decisões tomadas (com seus motivos) e os pontos explicitamente fora de escopo.
|
|
43
|
+
|
|
44
|
+
Confirme com o usuário que o resumo reflete o que deve existir. A partir daí, o plano está pronto para virar artefatos/implementação.
|
|
45
|
+
|
|
46
|
+
**Guardrails**
|
|
47
|
+
- Uma pergunta por vez — sempre. Nunca despeje várias perguntas juntas.
|
|
48
|
+
- Toda pergunta vem com uma resposta recomendada.
|
|
49
|
+
- Prefira investigar o código a perguntar, quando a resposta está no repositório.
|
|
50
|
+
- Não invente decisões: se algo é genuinamente do usuário, pergunte.
|
|
51
|
+
- Pare assim que houver entendimento compartilhado — não prolongue a interrogação além do necessário.
|
|
52
|
+
- Se o usuário passou argumentos, trate-os como a descrição do plano a ser interrogado.
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
export function getGrillMeCommandTemplate() {
|
|
56
|
+
return {
|
|
57
|
+
name: 'PS: Grill Me',
|
|
58
|
+
description: 'Interroga um plano antes da implementação — uma pergunta por vez, com resposta recomendada, até entendimento compartilhado',
|
|
59
|
+
category: 'Workflow',
|
|
60
|
+
tags: ['grill', 'refinamento', 'plano', 'workflow'],
|
|
61
|
+
content: getGrillMeInstructions(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=grill-me.js.map
|
|
@@ -52,6 +52,18 @@ When ready to implement, run /ps:apply
|
|
|
52
52
|
|
|
53
53
|
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
|
54
54
|
|
|
55
|
+
1b. **Fase de grill — interrogue o plano antes de gerar artefatos**
|
|
56
|
+
|
|
57
|
+
Antes de criar a change e gerar os artefatos, conduza a **fase de grill** para garantir que a proposta reflita o que realmente deve existir — não apenas a descrição inicial. Aplique o mesmo comportamento da skill \`grill-me\` (\`/ps:grill-me\`):
|
|
58
|
+
|
|
59
|
+
- Faça **uma pergunta por vez** — nunca despeje várias perguntas juntas. Aguarde a resposta antes da próxima.
|
|
60
|
+
- Acompanhe **cada** pergunta com a **sua resposta recomendada** e um motivo curto. Quando houver opções discretas, use a **ferramenta AskUserQuestion** com a recomendação como primeira opção ("(Recomendada)").
|
|
61
|
+
- **Explore o código quando há evidência**: se uma pergunta pode ser respondida pelo próprio repositório (convenções, padrões, features semelhantes), investigue o código em vez de perguntar. Só pergunte ao usuário decisões de produto, prioridades, trade-offs e intenção.
|
|
62
|
+
- Navegue a árvore de decisão progressivamente, resolvendo dependências; não repergunte o que já foi respondido pelo código ou por uma resposta anterior.
|
|
63
|
+
- Encerre quando houver **entendimento compartilhado** e apresente um resumo curto (o que será construído, decisões com motivos, fora de escopo) antes de seguir.
|
|
64
|
+
|
|
65
|
+
Os artefatos gerados a partir do Passo 2 SHALL refletir esse entendimento refinado.
|
|
66
|
+
|
|
55
67
|
2. **Create the change directory**
|
|
56
68
|
\`\`\`bash
|
|
57
69
|
pscode new change "<name>"
|
package/dist/core/update.d.ts
CHANGED
|
@@ -38,26 +38,6 @@ export declare class UpdateCommand {
|
|
|
38
38
|
* Displays a note about extra workflows installed that aren't in the active profile.
|
|
39
39
|
*/
|
|
40
40
|
private displayExtraWorkflowsNote;
|
|
41
|
-
/**
|
|
42
|
-
* Removes skill directories for workflows when delivery changed to commands-only.
|
|
43
|
-
* Returns the number of directories removed.
|
|
44
|
-
*/
|
|
45
|
-
private removeSkillDirs;
|
|
46
|
-
/**
|
|
47
|
-
* Removes skill directories for workflows that are no longer selected in the active profile.
|
|
48
|
-
* Returns the number of directories removed.
|
|
49
|
-
*/
|
|
50
|
-
private removeUnselectedSkillDirs;
|
|
51
|
-
/**
|
|
52
|
-
* Removes command files for workflows when delivery changed to skills-only.
|
|
53
|
-
* Returns the number of files removed.
|
|
54
|
-
*/
|
|
55
|
-
private removeCommandFiles;
|
|
56
|
-
/**
|
|
57
|
-
* Removes command files for workflows that are no longer selected in the active profile.
|
|
58
|
-
* Returns the number of files removed.
|
|
59
|
-
*/
|
|
60
|
-
private removeUnselectedCommandFiles;
|
|
61
41
|
/**
|
|
62
42
|
* Detect and handle legacy Pscode artifacts.
|
|
63
43
|
* Unlike init, update warns but continues if legacy files found in non-interactive mode.
|
package/dist/core/update.js
CHANGED
|
@@ -7,19 +7,18 @@
|
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import ora from 'ora';
|
|
10
|
-
import * as fs from 'fs';
|
|
11
10
|
import { createRequire } from 'module';
|
|
12
11
|
import { FileSystemUtils } from '../utils/file-system.js';
|
|
13
12
|
import { transformToHyphenCommands } from '../utils/command-references.js';
|
|
14
13
|
import { AI_TOOLS, PSCODE_DIR_NAME } from './config.js';
|
|
15
14
|
import { generateCommands, CommandAdapterRegistry, } from './command-generation/index.js';
|
|
16
|
-
import { getToolVersionStatus, getSkillTemplates, getCommandContents, generateSkillContent, getToolsWithSkillsDir, } from './shared/index.js';
|
|
15
|
+
import { getToolVersionStatus, getSkillTemplates, getCommandContents, generateSkillContent, getToolsWithSkillsDir, pruneOrphans, pruneOrphansForTool, } from './shared/index.js';
|
|
17
16
|
import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, getToolsFromLegacyArtifacts, } from './legacy-cleanup.js';
|
|
18
17
|
import { isInteractive } from '../utils/interactive.js';
|
|
19
18
|
import { getGlobalConfig } from './global-config.js';
|
|
20
|
-
import { getProfileWorkflows, isValidProfile, DEFAULT_PROFILE
|
|
19
|
+
import { getProfileWorkflows, isValidProfile, DEFAULT_PROFILE } from './profiles.js';
|
|
21
20
|
import { getAvailableTools } from './available-tools.js';
|
|
22
|
-
import {
|
|
21
|
+
import { getCommandConfiguredTools, getConfiguredToolsForProfileSync, getToolsNeedingProfileSync, } from './profile-sync-drift.js';
|
|
23
22
|
import { scanInstalledWorkflows as scanInstalledWorkflowsShared, migrateIfNeeded as migrateIfNeededShared, } from './migration.js';
|
|
24
23
|
const require = createRequire(import.meta.url);
|
|
25
24
|
const { version: PSCODE_VERSION } = require('../../package.json');
|
|
@@ -91,6 +90,15 @@ export class UpdateCommand {
|
|
|
91
90
|
if (!this.force && toolsToUpdateSet.size === 0) {
|
|
92
91
|
// All tools are up to date
|
|
93
92
|
this.displayUpToDateMessage(toolStatuses);
|
|
93
|
+
// Even when up to date, prune orphan artifacts (e.g. skills/commands of
|
|
94
|
+
// workflows deleted or renamed in a previous version) by filesystem scan.
|
|
95
|
+
const pruned = pruneOrphans(resolvedProjectPath, configuredTools, desiredWorkflows, delivery);
|
|
96
|
+
if (pruned.removedSkillDirs > 0) {
|
|
97
|
+
console.log(chalk.dim(`Removed: ${pruned.removedSkillDirs} orphan skill directories`));
|
|
98
|
+
}
|
|
99
|
+
if (pruned.removedCommandFiles > 0) {
|
|
100
|
+
console.log(chalk.dim(`Removed: ${pruned.removedCommandFiles} orphan command files`));
|
|
101
|
+
}
|
|
94
102
|
// Still check for new tool directories and extra workflows
|
|
95
103
|
this.detectNewTools(resolvedProjectPath, configuredTools);
|
|
96
104
|
this.displayExtraWorkflowsNote(resolvedProjectPath, configuredTools, desiredWorkflows);
|
|
@@ -132,11 +140,6 @@ export class UpdateCommand {
|
|
|
132
140
|
const skillContent = generateSkillContent(template, PSCODE_VERSION, transformer);
|
|
133
141
|
await FileSystemUtils.writeFile(skillFile, skillContent);
|
|
134
142
|
}
|
|
135
|
-
removedDeselectedSkillCount += await this.removeUnselectedSkillDirs(skillsDir, desiredWorkflows);
|
|
136
|
-
}
|
|
137
|
-
// Delete skill directories if delivery is commands-only
|
|
138
|
-
if (!shouldGenerateSkills) {
|
|
139
|
-
removedSkillCount += await this.removeSkillDirs(skillsDir);
|
|
140
143
|
}
|
|
141
144
|
// Generate commands if delivery includes commands
|
|
142
145
|
if (shouldGenerateCommands) {
|
|
@@ -147,12 +150,23 @@ export class UpdateCommand {
|
|
|
147
150
|
const commandFile = path.isAbsolute(cmd.path) ? cmd.path : path.join(resolvedProjectPath, cmd.path);
|
|
148
151
|
await FileSystemUtils.writeFile(commandFile, cmd.fileContent);
|
|
149
152
|
}
|
|
150
|
-
removedDeselectedCommandCount += await this.removeUnselectedCommandFiles(resolvedProjectPath, toolId, desiredWorkflows);
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
// Prune by filesystem scan: remove any Pscode-managed skill dir or
|
|
156
|
+
// command file that is not desired for the active profile/delivery —
|
|
157
|
+
// including orphans of workflows deleted or renamed in the enum.
|
|
158
|
+
const pruned = pruneOrphansForTool(resolvedProjectPath, toolId, desiredWorkflows, delivery);
|
|
159
|
+
if (shouldGenerateSkills) {
|
|
160
|
+
removedDeselectedSkillCount += pruned.removedSkillDirs;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
removedSkillCount += pruned.removedSkillDirs;
|
|
164
|
+
}
|
|
165
|
+
if (shouldGenerateCommands) {
|
|
166
|
+
removedDeselectedCommandCount += pruned.removedCommandFiles;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
removedCommandCount += pruned.removedCommandFiles;
|
|
156
170
|
}
|
|
157
171
|
spinner.succeed(`Updated ${tool.name}`);
|
|
158
172
|
updatedTools.push(tool.name);
|
|
@@ -189,9 +203,9 @@ export class UpdateCommand {
|
|
|
189
203
|
if (newlyConfiguredTools.length > 0) {
|
|
190
204
|
console.log();
|
|
191
205
|
console.log(chalk.bold('Getting started:'));
|
|
192
|
-
console.log(' /ps:
|
|
193
|
-
console.log(' /ps:continue Create the next artifact');
|
|
206
|
+
console.log(' /ps:propose Propose a new change');
|
|
194
207
|
console.log(' /ps:apply Implement tasks');
|
|
208
|
+
console.log(' /ps:complete Finalize and archive a change');
|
|
195
209
|
console.log();
|
|
196
210
|
console.log(`Learn more: ${chalk.cyan('https://github.com/thiagodiogo/Pscode')}`);
|
|
197
211
|
}
|
|
@@ -263,106 +277,6 @@ export class UpdateCommand {
|
|
|
263
277
|
console.log(chalk.dim(`Note: ${extraWorkflows.length} extra workflows not in profile (use \`pscode config profile\` to switch profiles)`));
|
|
264
278
|
}
|
|
265
279
|
}
|
|
266
|
-
/**
|
|
267
|
-
* Removes skill directories for workflows when delivery changed to commands-only.
|
|
268
|
-
* Returns the number of directories removed.
|
|
269
|
-
*/
|
|
270
|
-
async removeSkillDirs(skillsDir) {
|
|
271
|
-
let removed = 0;
|
|
272
|
-
for (const workflow of ALL_WORKFLOWS) {
|
|
273
|
-
const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
|
|
274
|
-
if (!dirName)
|
|
275
|
-
continue;
|
|
276
|
-
const skillDir = path.join(skillsDir, dirName);
|
|
277
|
-
try {
|
|
278
|
-
if (fs.existsSync(skillDir)) {
|
|
279
|
-
await fs.promises.rm(skillDir, { recursive: true, force: true });
|
|
280
|
-
removed++;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
284
|
-
// Ignore errors
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return removed;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Removes skill directories for workflows that are no longer selected in the active profile.
|
|
291
|
-
* Returns the number of directories removed.
|
|
292
|
-
*/
|
|
293
|
-
async removeUnselectedSkillDirs(skillsDir, desiredWorkflows) {
|
|
294
|
-
const desiredSet = new Set(desiredWorkflows);
|
|
295
|
-
let removed = 0;
|
|
296
|
-
for (const workflow of ALL_WORKFLOWS) {
|
|
297
|
-
if (desiredSet.has(workflow))
|
|
298
|
-
continue;
|
|
299
|
-
const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
|
|
300
|
-
if (!dirName)
|
|
301
|
-
continue;
|
|
302
|
-
const skillDir = path.join(skillsDir, dirName);
|
|
303
|
-
try {
|
|
304
|
-
if (fs.existsSync(skillDir)) {
|
|
305
|
-
await fs.promises.rm(skillDir, { recursive: true, force: true });
|
|
306
|
-
removed++;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
catch {
|
|
310
|
-
// Ignore errors
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return removed;
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Removes command files for workflows when delivery changed to skills-only.
|
|
317
|
-
* Returns the number of files removed.
|
|
318
|
-
*/
|
|
319
|
-
async removeCommandFiles(projectPath, toolId) {
|
|
320
|
-
let removed = 0;
|
|
321
|
-
const adapter = CommandAdapterRegistry.get(toolId);
|
|
322
|
-
if (!adapter)
|
|
323
|
-
return 0;
|
|
324
|
-
for (const workflow of ALL_WORKFLOWS) {
|
|
325
|
-
const cmdPath = adapter.getFilePath(workflow);
|
|
326
|
-
const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
|
|
327
|
-
try {
|
|
328
|
-
if (fs.existsSync(fullPath)) {
|
|
329
|
-
await fs.promises.unlink(fullPath);
|
|
330
|
-
removed++;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
// Ignore errors
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return removed;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Removes command files for workflows that are no longer selected in the active profile.
|
|
341
|
-
* Returns the number of files removed.
|
|
342
|
-
*/
|
|
343
|
-
async removeUnselectedCommandFiles(projectPath, toolId, desiredWorkflows) {
|
|
344
|
-
let removed = 0;
|
|
345
|
-
const adapter = CommandAdapterRegistry.get(toolId);
|
|
346
|
-
if (!adapter)
|
|
347
|
-
return 0;
|
|
348
|
-
const desiredSet = new Set(desiredWorkflows);
|
|
349
|
-
for (const workflow of ALL_WORKFLOWS) {
|
|
350
|
-
if (desiredSet.has(workflow))
|
|
351
|
-
continue;
|
|
352
|
-
const cmdPath = adapter.getFilePath(workflow);
|
|
353
|
-
const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
|
|
354
|
-
try {
|
|
355
|
-
if (fs.existsSync(fullPath)) {
|
|
356
|
-
await fs.promises.unlink(fullPath);
|
|
357
|
-
removed++;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
catch {
|
|
361
|
-
// Ignore errors
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return removed;
|
|
365
|
-
}
|
|
366
280
|
/**
|
|
367
281
|
* Detect and handle legacy Pscode artifacts.
|
|
368
282
|
* Unlike init, update warns but continues if legacy files found in non-interactive mode.
|
package/package.json
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill Template Workflow Modules
|
|
3
|
-
*
|
|
4
|
-
* This file is generated by splitting the legacy monolithic
|
|
5
|
-
* templates file into workflow-focused modules.
|
|
6
|
-
*/
|
|
7
|
-
import type { SkillTemplate, CommandTemplate } from '../types.js';
|
|
8
|
-
export declare function getBulkArchiveChangeSkillTemplate(): SkillTemplate;
|
|
9
|
-
export declare function getPsBulkArchiveCommandTemplate(): CommandTemplate;
|
|
10
|
-
//# sourceMappingURL=bulk-archive-change.d.ts.map
|