oxe-cc 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/.cursor/commands/oxe-discuss.md +4 -2
- package/.cursor/commands/oxe-execute.md +6 -2
- package/.cursor/commands/oxe-help.md +10 -2
- package/.cursor/commands/oxe-next.md +10 -2
- package/.cursor/commands/oxe-plan.md +13 -2
- package/.cursor/commands/oxe-quick.md +6 -2
- package/.cursor/commands/oxe-review-pr.md +16 -0
- package/.cursor/commands/oxe-scan.md +13 -2
- package/.cursor/commands/oxe-spec.md +13 -2
- package/.cursor/commands/oxe-verify.md +13 -2
- package/.github/copilot-instructions.md +25 -9
- package/AGENTS.md +3 -1
- package/README.md +310 -258
- package/assets/readme-banner.svg +19 -19
- package/bin/lib/oxe-agent-install.cjs +460 -0
- package/bin/lib/oxe-install-resolve.cjs +93 -0
- package/bin/lib/oxe-manifest.cjs +1 -1
- package/bin/lib/oxe-project-health.cjs +52 -0
- package/bin/lib/oxe-workflows.cjs +145 -0
- package/bin/oxe-cc.js +263 -58
- package/lib/sdk/README.md +54 -0
- package/lib/sdk/index.cjs +241 -0
- package/lib/sdk/index.d.ts +89 -0
- package/oxe/templates/CONFIG.md +32 -17
- package/oxe/templates/WORKFLOW_AUTHORING.md +73 -0
- package/oxe/templates/config.template.json +18 -11
- package/oxe/workflows/discuss.md +31 -31
- package/oxe/workflows/execute.md +36 -36
- package/oxe/workflows/help.md +46 -13
- package/oxe/workflows/next.md +7 -0
- package/oxe/workflows/quick.md +45 -45
- package/oxe/workflows/review-pr.md +2 -2
- package/oxe/workflows/workflow-authoring.md +34 -0
- package/package.json +30 -3
- package/.cursor/rules/oxe-workflow.mdc +0 -15
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resolve o diretório de workflows OXE no projeto (`.oxe/workflows` tem precedência sobre `oxe/workflows`).
|
|
8
|
+
* @param {string} targetProject raiz do repositório alvo
|
|
9
|
+
* @returns {string | null} caminho absoluto ou null
|
|
10
|
+
*/
|
|
11
|
+
function resolveWorkflowsDir(targetProject) {
|
|
12
|
+
const nested = path.join(targetProject, '.oxe', 'workflows');
|
|
13
|
+
const root = path.join(targetProject, 'oxe', 'workflows');
|
|
14
|
+
if (fs.existsSync(nested)) return nested;
|
|
15
|
+
if (fs.existsSync(root)) return root;
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Lista ficheiros `*.md` num diretório de workflows (sem recursão).
|
|
21
|
+
* @param {string} workflowsDir
|
|
22
|
+
* @returns {string[]} nomes ordenados
|
|
23
|
+
*/
|
|
24
|
+
function listWorkflowMdFiles(workflowsDir) {
|
|
25
|
+
if (!fs.existsSync(workflowsDir)) return [];
|
|
26
|
+
return fs
|
|
27
|
+
.readdirSync(workflowsDir)
|
|
28
|
+
.filter((f) => f.endsWith('.md'))
|
|
29
|
+
.sort();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Compara dois conjuntos de workflows (ex.: pacote npm vs projeto).
|
|
34
|
+
* @param {string} expectedDir diretório canónico (ex. `…/oxe/workflows` do pacote)
|
|
35
|
+
* @param {string} actualDir diretório no projeto
|
|
36
|
+
* @returns {{ expected: string[], actual: string[], missing: string[], extra: string[], ok: boolean }}
|
|
37
|
+
*/
|
|
38
|
+
function diffWorkflows(expectedDir, actualDir) {
|
|
39
|
+
const expected = listWorkflowMdFiles(expectedDir);
|
|
40
|
+
const actual = listWorkflowMdFiles(actualDir);
|
|
41
|
+
const actSet = new Set(actual);
|
|
42
|
+
const expSet = new Set(expected);
|
|
43
|
+
const missing = expected.filter((f) => !actSet.has(f));
|
|
44
|
+
const extra = actual.filter((f) => !expSet.has(f));
|
|
45
|
+
return { expected, actual, missing, extra, ok: missing.length === 0 };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Ficheiros onde não exigimos `<success_criteria>` / `<success>` (critérios noutro bloco ou passo minimalista). */
|
|
49
|
+
const SUCCESS_CRITERIA_EXCEPTIONS = new Set(['help.md']);
|
|
50
|
+
|
|
51
|
+
const DEFAULT_MAX_BYTES_SOFT = 45000;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Validação flexível da estrutura dos workflows (avisos, não bloqueante).
|
|
55
|
+
* @param {string} workflowsDir diretório absoluto com `*.md` (sem recursão)
|
|
56
|
+
* @param {{ maxBytesSoft?: number }} [options]
|
|
57
|
+
* @returns {{
|
|
58
|
+
* fileResults: Array<{ file: string, warnings: string[] }>,
|
|
59
|
+
* warnings: Array<{ code: 'WORKFLOW_SHAPE', message: string, detail?: { file: string } }>,
|
|
60
|
+
* }}
|
|
61
|
+
*/
|
|
62
|
+
function validateWorkflowShapes(workflowsDir, options = {}) {
|
|
63
|
+
/** @type {Array<{ file: string, warnings: string[] }>} */
|
|
64
|
+
const fileResults = [];
|
|
65
|
+
/** @type {Array<{ code: 'WORKFLOW_SHAPE', message: string, detail?: { file: string } }>} */
|
|
66
|
+
const warnings = [];
|
|
67
|
+
if (!workflowsDir || !fs.existsSync(workflowsDir)) {
|
|
68
|
+
return { fileResults, warnings };
|
|
69
|
+
}
|
|
70
|
+
const maxBytesSoft = options.maxBytesSoft ?? DEFAULT_MAX_BYTES_SOFT;
|
|
71
|
+
const files = listWorkflowMdFiles(workflowsDir);
|
|
72
|
+
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
const full = path.join(workflowsDir, file);
|
|
75
|
+
let content;
|
|
76
|
+
try {
|
|
77
|
+
content = fs.readFileSync(full, 'utf8');
|
|
78
|
+
} catch {
|
|
79
|
+
warnings.push({
|
|
80
|
+
code: 'WORKFLOW_SHAPE',
|
|
81
|
+
message: `${file}: não foi possível ler o ficheiro`,
|
|
82
|
+
detail: { file },
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const fileWarnings = [];
|
|
88
|
+
|
|
89
|
+
const iObj = content.indexOf('<objective>');
|
|
90
|
+
const iObjEnd = content.indexOf('</objective>');
|
|
91
|
+
if (iObj === -1 && iObjEnd === -1) {
|
|
92
|
+
fileWarnings.push('falta bloco <objective>…</objective>');
|
|
93
|
+
} else {
|
|
94
|
+
if (iObj === -1) fileWarnings.push('falta tag de abertura <objective>');
|
|
95
|
+
if (iObjEnd === -1) fileWarnings.push('falta tag de fecho </objective>');
|
|
96
|
+
if (iObj !== -1 && iObjEnd !== -1 && iObjEnd < iObj) {
|
|
97
|
+
fileWarnings.push('</objective> aparece antes de <objective>');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (file === 'help.md') {
|
|
102
|
+
if (content.indexOf('<output>') === -1 || content.indexOf('</output>') === -1) {
|
|
103
|
+
fileWarnings.push('help.md deveria incluir <output>…</output>');
|
|
104
|
+
}
|
|
105
|
+
} else if (!SUCCESS_CRITERIA_EXCEPTIONS.has(file)) {
|
|
106
|
+
const hasSC =
|
|
107
|
+
content.includes('<success_criteria>') && content.includes('</success_criteria>');
|
|
108
|
+
const hasSuccess = content.includes('<success>') && content.includes('</success>');
|
|
109
|
+
if (!hasSC && !hasSuccess) {
|
|
110
|
+
fileWarnings.push(
|
|
111
|
+
'sem <success_criteria> nem <success>; veja oxe/templates/WORKFLOW_AUTHORING.md'
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const bytes = Buffer.byteLength(content, 'utf8');
|
|
117
|
+
if (bytes > maxBytesSoft) {
|
|
118
|
+
fileWarnings.push(
|
|
119
|
+
`ficheiro grande (${bytes} bytes); considere extrair para oxe/workflows/references/ (limite suave ${maxBytesSoft})`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (fileWarnings.length) {
|
|
124
|
+
fileResults.push({ file, warnings: fileWarnings });
|
|
125
|
+
for (const msg of fileWarnings) {
|
|
126
|
+
warnings.push({
|
|
127
|
+
code: 'WORKFLOW_SHAPE',
|
|
128
|
+
message: `${file}: ${msg}`,
|
|
129
|
+
detail: { file },
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return { fileResults, warnings };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = {
|
|
139
|
+
resolveWorkflowsDir,
|
|
140
|
+
listWorkflowMdFiles,
|
|
141
|
+
diffWorkflows,
|
|
142
|
+
validateWorkflowShapes,
|
|
143
|
+
DEFAULT_MAX_BYTES_SOFT,
|
|
144
|
+
SUCCESS_CRITERIA_EXCEPTIONS,
|
|
145
|
+
};
|