atlas-workflow 0.9.3 → 0.9.4
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 +16 -10
- package/VERSION +1 -1
- package/build/tests/etapa3.test.mjs +36 -6
- package/hosts/opencode/.opencode/atlas/VERSION +1 -1
- package/hosts/opencode/.opencode/atlas/orchestrator/README.md +15 -2
- package/hosts/opencode/.opencode/atlas/orchestrator/commands/workflow.md +7 -5
- package/hosts/opencode/.opencode/atlas/orchestrator/references/subagent_dispatch.md +11 -1
- package/hosts/opencode/.opencode/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +30 -10
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/README.md +2 -2
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/package.json +1 -1
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/server.js +80 -14
- package/hosts/opencode/.opencode/skills/_shared/references/stack-profiles.md +36 -0
- package/hosts/opencode/.opencode/skills/_shared/scripts/document_quality.mjs +37 -1
- package/hosts/opencode/.opencode/skills/atlas-audit/SKILL.md +201 -0
- package/hosts/opencode/.opencode/skills/atlas-audit/agents/openai.yaml +7 -0
- package/hosts/opencode/.opencode/skills/atlas-task-validator/SKILL.md +6 -0
- package/hosts/opencode/.opencode/skills/atlas-workflow-orchestrator/SKILL.md +30 -10
- package/hosts/pi/atlas/VERSION +1 -1
- package/hosts/pi/atlas/orchestrator/README.md +15 -2
- package/hosts/pi/atlas/orchestrator/commands/workflow.md +7 -5
- package/hosts/pi/atlas/orchestrator/references/subagent_dispatch.md +11 -1
- package/hosts/pi/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +30 -10
- package/hosts/pi/atlas/packages/mcp-server/README.md +2 -2
- package/hosts/pi/atlas/packages/mcp-server/package.json +1 -1
- package/hosts/pi/atlas/packages/mcp-server/server.js +80 -14
- package/hosts/pi/skills/_shared/references/stack-profiles.md +36 -0
- package/hosts/pi/skills/_shared/scripts/document_quality.mjs +37 -1
- package/hosts/pi/skills/atlas-audit/SKILL.md +201 -0
- package/hosts/pi/skills/atlas-audit/agents/openai.yaml +7 -0
- package/hosts/pi/skills/atlas-task-validator/SKILL.md +6 -0
- package/hosts/pi/skills/atlas-workflow-orchestrator/SKILL.md +30 -10
- package/hosts/zcode/.zcode-plugin/plugin.json +2 -2
- package/hosts/zcode/packages/mcp-server/README.md +2 -2
- package/hosts/zcode/packages/mcp-server/VERSION +1 -1
- package/hosts/zcode/packages/mcp-server/package.json +1 -1
- package/hosts/zcode/packages/mcp-server/server.js +80 -14
- package/hosts/zcode/packages/orchestrator/README.md +15 -2
- package/hosts/zcode/packages/orchestrator/commands/workflow.md +7 -5
- package/hosts/zcode/packages/orchestrator/references/subagent_dispatch.md +11 -1
- package/hosts/zcode/packages/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +30 -10
- package/hosts/zcode/skills/_shared/references/stack-profiles.md +36 -0
- package/hosts/zcode/skills/_shared/scripts/document_quality.mjs +37 -1
- package/hosts/zcode/skills/atlas-audit/SKILL.md +201 -0
- package/hosts/zcode/skills/atlas-audit/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-task-validator/SKILL.md +6 -0
- package/hosts/zcode/skills/atlas-workflow-orchestrator/SKILL.md +30 -10
- package/package.json +1 -1
- package/plugins/atlas-workflow-orchestrator/.codex-plugin/plugin.json +5 -4
- package/plugins/atlas-workflow-orchestrator/VERSION +1 -1
- package/plugins/atlas-workflow-orchestrator/orchestrator/README.md +15 -2
- package/plugins/atlas-workflow-orchestrator/orchestrator/commands/workflow.md +7 -5
- package/plugins/atlas-workflow-orchestrator/orchestrator/references/subagent_dispatch.md +11 -1
- package/plugins/atlas-workflow-orchestrator/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +30 -10
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/README.md +2 -2
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/package.json +1 -1
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/server.js +80 -14
- package/plugins/atlas-workflow-orchestrator/packages/skills/_shared/references/stack-profiles.md +36 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/_shared/scripts/document_quality.mjs +37 -1
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-audit/SKILL.md +201 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-audit/agents/openai.yaml +7 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-task-validator/SKILL.md +6 -0
- package/plugins/atlas-workflow-orchestrator/skills/_shared/references/stack-profiles.md +36 -0
- package/plugins/atlas-workflow-orchestrator/skills/_shared/scripts/document_quality.mjs +37 -1
- package/plugins/atlas-workflow-orchestrator/skills/atlas-audit/SKILL.md +201 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-audit/agents/openai.yaml +7 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-task-validator/SKILL.md +6 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +30 -10
|
@@ -64,11 +64,12 @@ const WORKFLOW_CONFIG = {
|
|
|
64
64
|
plan_handoff: 'atlas-plan-handoff',
|
|
65
65
|
plan_execute: 'atlas-plan-execute',
|
|
66
66
|
direct_execute: 'atlas-direct-execute',
|
|
67
|
+
audit: 'atlas-audit',
|
|
67
68
|
findings_repair: 'atlas-findings-repair',
|
|
68
69
|
slice_review: 'atlas-slice-review',
|
|
69
70
|
task_validator: 'atlas-task-validator',
|
|
70
71
|
},
|
|
71
|
-
modes: ['full', 'direct', 'execute', 'interview-only', 'interview_only'],
|
|
72
|
+
modes: ['full', 'direct', 'execute', 'interview-only', 'interview_only', 'audit'],
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
const VALIDATOR_MAX_ATTEMPTS = 2;
|
|
@@ -104,7 +105,7 @@ function repairRunId(runId, attempt, timestamp) {
|
|
|
104
105
|
// pipelines completas (full/direct/execute) declaram full_pipeline; uso avulso
|
|
105
106
|
// documental/leitura declara reduced_standalone (fora do escopo desta camada).
|
|
106
107
|
// Data-driven: rota → nível, sem ramo solto. Modos sem execução de código
|
|
107
|
-
// (interview-only) NÃO declaram guarantee_level (não há execução a garantir):
|
|
108
|
+
// (interview-only/audit) NÃO declaram guarantee_level (não há execução a garantir):
|
|
108
109
|
// guaranteeLevelForMode devolve null e o campo é OMITIDO do output (PRD D2/D12).
|
|
109
110
|
const GUARANTEE_LEVELS = ['full_pipeline', 'reduced_standalone'];
|
|
110
111
|
const MODE_GUARANTEE_LEVEL = {
|
|
@@ -1160,6 +1161,16 @@ function runState(args = {}) {
|
|
|
1160
1161
|
throw rpcError(-32602, `Ação inválida para atlas_run_state: ${action}`);
|
|
1161
1162
|
}
|
|
1162
1163
|
|
|
1164
|
+
function validateJsonArtifactFile(absolutePath) {
|
|
1165
|
+
try {
|
|
1166
|
+
const content = fs.readFileSync(absolutePath, 'utf8');
|
|
1167
|
+
const parsed = JSON.parse(content);
|
|
1168
|
+
return { ok: true, parsed_type: Array.isArray(parsed) ? 'array' : typeof parsed };
|
|
1169
|
+
} catch (error) {
|
|
1170
|
+
return { ok: false, error: error.message };
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1163
1174
|
function verifyArtifact(args = {}) {
|
|
1164
1175
|
const runId = validateRunId(args.run_id);
|
|
1165
1176
|
const artifactPath = requiredString(args, 'artifact_path');
|
|
@@ -1172,7 +1183,9 @@ function verifyArtifact(args = {}) {
|
|
|
1172
1183
|
const artifactKind = optionalString(args, 'artifact_kind');
|
|
1173
1184
|
const okBanner = artifactKind === 'prd'
|
|
1174
1185
|
? renderBanner('prd_ok', {})
|
|
1175
|
-
:
|
|
1186
|
+
: artifactKind === 'json'
|
|
1187
|
+
? renderBanner('validacao', { status: 'json_ok' })
|
|
1188
|
+
: renderBanner('plano', {});
|
|
1176
1189
|
let result;
|
|
1177
1190
|
|
|
1178
1191
|
try {
|
|
@@ -1189,15 +1202,43 @@ function verifyArtifact(args = {}) {
|
|
|
1189
1202
|
};
|
|
1190
1203
|
} else {
|
|
1191
1204
|
fs.accessSync(absolutePath, fs.constants.R_OK);
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1205
|
+
if (artifactKind === 'json') {
|
|
1206
|
+
const jsonCheck = validateJsonArtifactFile(absolutePath);
|
|
1207
|
+
if (!jsonCheck.ok) {
|
|
1208
|
+
result = {
|
|
1209
|
+
gate: 'G1',
|
|
1210
|
+
status: 'blocked',
|
|
1211
|
+
artifact_path: artifactPath,
|
|
1212
|
+
bytes: stat.size,
|
|
1213
|
+
timestamp,
|
|
1214
|
+
banner: renderBanner('preflight_fail', { motivo: `json inválido: ${artifactPath}` }),
|
|
1215
|
+
error: `Artefato JSON inválido: ${artifactPath}`,
|
|
1216
|
+
cause: jsonCheck.error,
|
|
1217
|
+
next_action: 'corrigir_json_ou_regenerar_por_serializer',
|
|
1218
|
+
};
|
|
1219
|
+
} else {
|
|
1220
|
+
result = {
|
|
1221
|
+
gate: 'G1',
|
|
1222
|
+
status: 'passed',
|
|
1223
|
+
artifact_path: artifactPath,
|
|
1224
|
+
bytes: stat.size,
|
|
1225
|
+
parsed_type: jsonCheck.parsed_type,
|
|
1226
|
+
timestamp,
|
|
1227
|
+
banner: okBanner,
|
|
1228
|
+
next_action: 'avançar',
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
} else {
|
|
1232
|
+
result = {
|
|
1233
|
+
gate: 'G1',
|
|
1234
|
+
status: 'passed',
|
|
1235
|
+
artifact_path: artifactPath,
|
|
1236
|
+
bytes: stat.size,
|
|
1237
|
+
timestamp,
|
|
1238
|
+
banner: okBanner,
|
|
1239
|
+
next_action: 'avançar',
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1201
1242
|
}
|
|
1202
1243
|
} catch (error) {
|
|
1203
1244
|
result = {
|
|
@@ -1819,6 +1860,7 @@ function expectedNextPhase(routing, dispatch) {
|
|
|
1819
1860
|
if (routing.mode === 'full') return 'plan_handoff';
|
|
1820
1861
|
if (routing.mode === 'direct') return 'plan_execute';
|
|
1821
1862
|
if (routing.mode === 'execute') return 'plan_execute';
|
|
1863
|
+
if (routing.mode === 'audit') return 'audit_report';
|
|
1822
1864
|
return 'prd_interview';
|
|
1823
1865
|
}
|
|
1824
1866
|
|
|
@@ -2764,6 +2806,7 @@ function validatorComplete(args, context) {
|
|
|
2764
2806
|
// validator. Sem token não existe garantia anti-stale completa.
|
|
2765
2807
|
const dispatchToken = optionalInteger(args, 'dispatch_token');
|
|
2766
2808
|
const challengeResponse = optionalString(args, 'challenge_response');
|
|
2809
|
+
const validatorOutputPath = optionalString(args, 'validator_output_path');
|
|
2767
2810
|
|
|
2768
2811
|
if (!cycle.active) {
|
|
2769
2812
|
// S10: slot já fechado. Distinguir retorno duplicado já aplicado (idempotente
|
|
@@ -2934,6 +2977,28 @@ function validatorComplete(args, context) {
|
|
|
2934
2977
|
}
|
|
2935
2978
|
const challengeVerified = !cycle.active.challenge ? 'no_challenge' : 'verified';
|
|
2936
2979
|
|
|
2980
|
+
if (validatorOutputPath) {
|
|
2981
|
+
const outputPath = resolveConsumerPath(validatorOutputPath, args);
|
|
2982
|
+
const jsonCheck = validateJsonArtifactFile(outputPath);
|
|
2983
|
+
if (!jsonCheck.ok) {
|
|
2984
|
+
return {
|
|
2985
|
+
gate: 'G4',
|
|
2986
|
+
action: 'complete',
|
|
2987
|
+
status: 'blocked',
|
|
2988
|
+
timestamp,
|
|
2989
|
+
validator_attempt: cycle.active.attempt,
|
|
2990
|
+
validator_run_id: activeValidatorRunId,
|
|
2991
|
+
state_path: statePathValue,
|
|
2992
|
+
validator_output_path: validatorOutputPath,
|
|
2993
|
+
validator_status: 'invalid_validator_output_json',
|
|
2994
|
+
error: `Output JSON do validator inválido: ${validatorOutputPath}`,
|
|
2995
|
+
cause: jsonCheck.error,
|
|
2996
|
+
impact: 'relatorio_do_validador_nao_e_parseavel_como_json_confiavel',
|
|
2997
|
+
next_action: 'regenerar_validator_output_json_por_serializer_e_reenviar_complete',
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
|
|
2937
3002
|
if (packetResult.violations.length > 0) {
|
|
2938
3003
|
return {
|
|
2939
3004
|
gate: 'G4', action: 'complete', status: 'blocked', timestamp,
|
|
@@ -3605,7 +3670,7 @@ function toolsList() {
|
|
|
3605
3670
|
run_id: { type: 'string', minLength: 1 },
|
|
3606
3671
|
project_root: { type: 'string', minLength: 1 },
|
|
3607
3672
|
artifact_path: { type: 'string', minLength: 1 },
|
|
3608
|
-
artifact_kind: { enum: ['prd', 'plan'] },
|
|
3673
|
+
artifact_kind: { enum: ['prd', 'plan', 'json'] },
|
|
3609
3674
|
},
|
|
3610
3675
|
},
|
|
3611
3676
|
},
|
|
@@ -3655,7 +3720,7 @@ function toolsList() {
|
|
|
3655
3720
|
},
|
|
3656
3721
|
{
|
|
3657
3722
|
name: 'atlas_preflight',
|
|
3658
|
-
description: 'Gate PREREQ+G10: hard-fail de pré-requisitos de determinismo (subagente/MCP do host, DEC-004), depois valida modo, versão e lock ativo, travando a rota da run. Output declara guarantee_level
|
|
3723
|
+
description: 'Gate PREREQ+G10: hard-fail de pré-requisitos de determinismo (subagente/MCP do host, DEC-004), depois valida modo, versão e lock ativo, travando a rota da run. Output declara guarantee_level só em modos com execução.',
|
|
3659
3724
|
inputSchema: {
|
|
3660
3725
|
type: 'object',
|
|
3661
3726
|
additionalProperties: false,
|
|
@@ -3724,6 +3789,7 @@ function toolsList() {
|
|
|
3724
3789
|
repair_run_id: { type: 'string' },
|
|
3725
3790
|
dispatch_token: { type: 'integer' },
|
|
3726
3791
|
challenge_response: { type: 'string' },
|
|
3792
|
+
validator_output_path: { type: 'string' },
|
|
3727
3793
|
verdict: { type: 'string', enum: ['pass', 'pass_with_observations', 'fail'] },
|
|
3728
3794
|
data: { type: 'object', additionalProperties: true },
|
|
3729
3795
|
host: { type: 'string', enum: HOST_NAMES },
|
package/plugins/atlas-workflow-orchestrator/packages/skills/_shared/references/stack-profiles.md
CHANGED
|
@@ -33,4 +33,40 @@ Ativação é determinística: inspecione manifests no boundary e comandos realm
|
|
|
33
33
|
- parsing/typing nas fronteiras e mutabilidade de defaults;
|
|
34
34
|
- `pytest`, `ruff`, `mypy` ou equivalente apenas se declarados.
|
|
35
35
|
|
|
36
|
+
## Go — `go.mod` ou comando Go real
|
|
37
|
+
|
|
38
|
+
- context propagation/cancelamento, goroutine leak, data race e cleanup;
|
|
39
|
+
- erros retornados sem swallow, wrapping útil e validação em fronteiras;
|
|
40
|
+
- `go test`, `go vet`, `go test -race` e linters somente quando declarados/aplicáveis.
|
|
41
|
+
|
|
42
|
+
## Rust — `Cargo.toml` ou comando Cargo real
|
|
43
|
+
|
|
44
|
+
- ownership/lifetime usados para segurança real, não wrappers desnecessários;
|
|
45
|
+
- `Result`/`Option` tratados sem `unwrap`/`expect` em fronteiras recuperáveis;
|
|
46
|
+
- `cargo test`, `cargo check`, `cargo clippy` e `cargo fmt` somente quando declarados/aplicáveis.
|
|
47
|
+
|
|
48
|
+
## Java/Kotlin — Maven/Gradle ou comando Java/Kotlin real
|
|
49
|
+
|
|
50
|
+
- nullability, exceptions, resource cleanup e lifecycle de threads/coroutines;
|
|
51
|
+
- boundaries de DTO/entity, serialização e validação de input;
|
|
52
|
+
- `mvn test`, `gradle test`, linters/typecheck somente quando declarados.
|
|
53
|
+
|
|
54
|
+
## Firebase — `firebase.json`, `.firebaserc` ou dependência Firebase real
|
|
55
|
+
|
|
56
|
+
- regras/claims/authz, paths e ownership de dados;
|
|
57
|
+
- falhas offline/retry, listeners/subscriptions e cleanup;
|
|
58
|
+
- emuladores/deploy/checks somente quando declarados.
|
|
59
|
+
|
|
60
|
+
## Supabase — dependência Supabase real
|
|
61
|
+
|
|
62
|
+
- RLS/auth claims, schema/migrations, RPC/Edge Functions e storage policies;
|
|
63
|
+
- sessão/token refresh, SSR/cookies quando aplicável e boundaries de dados;
|
|
64
|
+
- CLI/migration/testes somente quando declarados.
|
|
65
|
+
|
|
66
|
+
## REST/OpenAPI — OpenAPI/Swagger ou servidor/cliente HTTP real
|
|
67
|
+
|
|
68
|
+
- compatibilidade request/response, status codes, paginação, erros e idempotência;
|
|
69
|
+
- validação runtime nas bordas e divergência entre contrato e implementação;
|
|
70
|
+
- geração/validação OpenAPI somente quando declarada.
|
|
71
|
+
|
|
36
72
|
Perfis podem coexistir em monorepo. Regra de perfil nunca vira finding fora do boundary onde seu sinal foi ativado.
|
package/plugins/atlas-workflow-orchestrator/packages/skills/_shared/scripts/document_quality.mjs
CHANGED
|
@@ -10,7 +10,11 @@ const VALID = Object.freeze({
|
|
|
10
10
|
state: new Set(['backlog', 'ready', 'doing', 'review', 'done', 'blocked']),
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
const STACK_MANIFESTS = [
|
|
13
|
+
const STACK_MANIFESTS = [
|
|
14
|
+
'package.json', 'tsconfig.json', 'pubspec.yaml', 'pyproject.toml', 'requirements.txt', 'setup.py',
|
|
15
|
+
'go.mod', 'Cargo.toml', 'pom.xml', 'build.gradle', 'build.gradle.kts', 'settings.gradle', 'settings.gradle.kts',
|
|
16
|
+
'firebase.json', '.firebaserc', 'openapi.yaml', 'openapi.yml', 'openapi.json', 'swagger.yaml', 'swagger.yml', 'swagger.json',
|
|
17
|
+
];
|
|
14
18
|
|
|
15
19
|
function boundaryRoot(projectRoot, boundary) {
|
|
16
20
|
const project = path.resolve(projectRoot);
|
|
@@ -47,6 +51,9 @@ function containsGetxImport(root) {
|
|
|
47
51
|
|
|
48
52
|
function detectBoundaryProfile(root, declaredCommands) {
|
|
49
53
|
const exists = (name) => fs.existsSync(path.join(root, name));
|
|
54
|
+
const readIfExists = (name) => {
|
|
55
|
+
try { return exists(name) ? fs.readFileSync(path.join(root, name), 'utf8') : ''; } catch { return ''; }
|
|
56
|
+
};
|
|
50
57
|
const commands = declaredCommands.filter((v) => typeof v === 'string');
|
|
51
58
|
let packageJson = null;
|
|
52
59
|
if (exists('package.json')) {
|
|
@@ -57,13 +64,36 @@ function detectBoundaryProfile(root, declaredCommands) {
|
|
|
57
64
|
try { pubspec = fs.readFileSync(path.join(root, 'pubspec.yaml'), 'utf8'); } catch {}
|
|
58
65
|
}
|
|
59
66
|
const packageCommands = Object.values(packageJson?.scripts ?? {}).filter((v) => typeof v === 'string');
|
|
67
|
+
const packageDeps = Object.keys({
|
|
68
|
+
...(packageJson?.dependencies ?? {}),
|
|
69
|
+
...(packageJson?.devDependencies ?? {}),
|
|
70
|
+
...(packageJson?.peerDependencies ?? {}),
|
|
71
|
+
...(packageJson?.optionalDependencies ?? {}),
|
|
72
|
+
});
|
|
73
|
+
const cargo = readIfExists('Cargo.toml');
|
|
74
|
+
const pom = readIfExists('pom.xml');
|
|
75
|
+
const gradle = `${readIfExists('build.gradle')}\n${readIfExists('build.gradle.kts')}\n${readIfExists('settings.gradle')}\n${readIfExists('settings.gradle.kts')}`;
|
|
60
76
|
const allCommands = [...commands, ...packageCommands];
|
|
61
77
|
const hasCommand = (re) => allCommands.some((command) => re.test(command));
|
|
78
|
+
const hasPackageDep = (re) => packageDeps.some((dep) => re.test(dep));
|
|
79
|
+
const javaKotlinSignal = exists('pom.xml') || exists('build.gradle') || exists('build.gradle.kts')
|
|
80
|
+
|| exists('settings.gradle') || exists('settings.gradle.kts') || hasCommand(/\b(gradle|mvn|java|javac|kotlinc)\b/);
|
|
81
|
+
const restSignal = exists('openapi.yaml') || exists('openapi.yml') || exists('openapi.json')
|
|
82
|
+
|| exists('swagger.yaml') || exists('swagger.yml') || exists('swagger.json')
|
|
83
|
+
|| hasPackageDep(/\b(openapi|swagger|express|fastify|koa|hono|axios|ky)\b/i)
|
|
84
|
+
|| /openapi|swagger|spring-boot-starter-web|ktor|retrofit/i.test(`${pom}\n${gradle}\n${pubspec}`);
|
|
62
85
|
return {
|
|
63
86
|
universal: true,
|
|
64
87
|
flutter_dart: exists('pubspec.yaml') || hasCommand(/\b(flutter|dart)\b/),
|
|
65
88
|
node_typescript: exists('package.json') || exists('tsconfig.json') || hasCommand(/\b(node|npm|pnpm|yarn|bun|tsc)\b/),
|
|
66
89
|
python: exists('pyproject.toml') || exists('requirements.txt') || exists('setup.py') || hasCommand(/\b(python3?|pytest|ruff|mypy)\b/),
|
|
90
|
+
go: exists('go.mod') || hasCommand(/\bgo\s+(test|build|run|vet|fmt)\b/),
|
|
91
|
+
rust: exists('Cargo.toml') || hasCommand(/\bcargo\s+(test|build|run|check|clippy|fmt)\b/) || /^\s*\[package\]/m.test(cargo),
|
|
92
|
+
java_kotlin: javaKotlinSignal,
|
|
93
|
+
firebase: exists('firebase.json') || exists('.firebaserc') || hasPackageDep(/^firebase$|^@firebase\/|firebase-admin/i)
|
|
94
|
+
|| /firebase_core|cloud_firestore|firebase_auth|firebase_messaging|firebase_storage/i.test(pubspec),
|
|
95
|
+
supabase: hasPackageDep(/^@supabase\/|supabase-js/i) || /supabase_flutter|supabase|postgrest/i.test(pubspec),
|
|
96
|
+
rest_openapi: restSignal,
|
|
67
97
|
getx: /^\s{0,4}get\s*:/m.test(pubspec) || containsGetxImport(root),
|
|
68
98
|
};
|
|
69
99
|
}
|
|
@@ -80,6 +110,12 @@ export function detectStackProfiles(root, declaredCommands = [], boundaryPaths =
|
|
|
80
110
|
flutter_dart: boundaries.some((profile) => profile.flutter_dart),
|
|
81
111
|
node_typescript: boundaries.some((profile) => profile.node_typescript),
|
|
82
112
|
python: boundaries.some((profile) => profile.python),
|
|
113
|
+
go: boundaries.some((profile) => profile.go),
|
|
114
|
+
rust: boundaries.some((profile) => profile.rust),
|
|
115
|
+
java_kotlin: boundaries.some((profile) => profile.java_kotlin),
|
|
116
|
+
firebase: boundaries.some((profile) => profile.firebase),
|
|
117
|
+
supabase: boundaries.some((profile) => profile.supabase),
|
|
118
|
+
rest_openapi: boundaries.some((profile) => profile.rest_openapi),
|
|
83
119
|
getx: boundaries.some((profile) => profile.getx),
|
|
84
120
|
boundaries,
|
|
85
121
|
};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atlas-audit
|
|
3
|
+
description: Skill/mode universal de auditoria Atlas. Use para `/workflow audit <target>` com flags opcionais `--handoff` e `--scope <descrição>`. Audita código contra regras locais, boas práticas da stack detectada e complexidade acidental estilo Ponytail, sem corrigir código nem executar plano.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Atlas Audit
|
|
7
|
+
|
|
8
|
+
Auditoria universal, framework-agnóstica. Esta skill lê o repositório real, audita o `target` informado e entrega relatório de achados. Opcionalmente gera handoff Atlas-style para correção futura. **Nunca altera código. Nunca executa plano.**
|
|
9
|
+
|
|
10
|
+
## Sintaxe
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
/workflow audit <target>
|
|
14
|
+
/workflow audit <target> --handoff
|
|
15
|
+
/workflow audit <target> --scope <descrição>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`target` pode ser arquivo, diretório, pacote, módulo, feature, PRD/plano como referência ou descrição que aponte para um boundary localizável. Se não for possível resolver o boundary em disco, pare e peça um target mais preciso.
|
|
19
|
+
|
|
20
|
+
## Contrato duro
|
|
21
|
+
|
|
22
|
+
- Auditar o target informado, não o repo inteiro por padrão.
|
|
23
|
+
- Diagnóstico factual vem antes de proposta de correção.
|
|
24
|
+
- Todo achado precisa de evidência concreta `arquivo:linha`.
|
|
25
|
+
- Não criar finding por preferência genérica sem evidência local.
|
|
26
|
+
- Regras locais reais prevalecem sobre boas práticas genéricas.
|
|
27
|
+
- Não migrar lógica especialista de Flutter/Dart para o core universal.
|
|
28
|
+
- Adapters por stack/backend são opcionais; fallback universal deve funcionar.
|
|
29
|
+
- Não inventar comandos de validação: usar apenas manifests/configs/scripts reais.
|
|
30
|
+
- `--handoff` gera artefato de plano em `.atlas/plans/`, mas não chama executor.
|
|
31
|
+
|
|
32
|
+
## Fluxo obrigatório
|
|
33
|
+
|
|
34
|
+
1. **Resolver boundary**
|
|
35
|
+
- Identificar `project_root`, `target`, paths reais e exclusões óbvias (`node_modules`, `build`, `dist`, `.git`, caches).
|
|
36
|
+
- Se `--scope` existir, tratar como recorte adicional, não expansão automática para repo inteiro.
|
|
37
|
+
- Registrar limitações se parte do target estiver ausente, gerada ou ilegível.
|
|
38
|
+
|
|
39
|
+
2. **Ler regras locais reais**
|
|
40
|
+
- Procurar e consultar, quando existirem e forem relevantes ao boundary: `AGENTS.md`, `CLAUDE.md`, `README.md`, docs de rules, `project-rules/`, configs de lint/format/typecheck/test, manifests e arquivos de workspace.
|
|
41
|
+
- Não copiar regras integralmente no relatório; listar só fontes consultadas e regras aplicáveis.
|
|
42
|
+
|
|
43
|
+
3. **Detectar stack/perfis**
|
|
44
|
+
- Usar `../_shared/references/stack-profiles.md` como baseline.
|
|
45
|
+
- Detectar por manifests/configs/comandos reais, não só por extensão.
|
|
46
|
+
- Perfis esperados quando houver sinal: Dart/Flutter, TypeScript/Node, Python, Go, Rust, Java/Kotlin, Firebase, Supabase, REST/OpenAPI.
|
|
47
|
+
- Se múltiplos perfis coexistirem, aplicar cada regra só ao boundary onde o sinal foi ativado.
|
|
48
|
+
|
|
49
|
+
4. **Entender arquitetura antes de julgar**
|
|
50
|
+
- Mapear camadas reais, fluxo de dados, contratos, entrypoints, DI, estado, side effects, testes e consumidores afetados.
|
|
51
|
+
- Comparar com padrões existentes do repo antes de classificar algo como violação.
|
|
52
|
+
|
|
53
|
+
5. **Auditar por lentes**
|
|
54
|
+
- Arquitetura e ownership.
|
|
55
|
+
- Contrato/dados/schemas/mappers/DTOs.
|
|
56
|
+
- Erros, falhas parciais, retries, cleanup.
|
|
57
|
+
- Segurança, authz/authn, segredos, trust boundaries.
|
|
58
|
+
- Estado, concorrência, lifecycle, idempotência.
|
|
59
|
+
- Fluxos previstos e imprevistos.
|
|
60
|
+
- Testes e validação declarada.
|
|
61
|
+
- Observabilidade, logs e diagnóstico.
|
|
62
|
+
- Manutenção/DX.
|
|
63
|
+
|
|
64
|
+
6. **Ponytail pass final**
|
|
65
|
+
- Procurar abstração inútil, wrapper sem valor, código morto, duplicação simples, dependência excessiva, branching/config acidental e complexidade que não protege segurança/contrato/dados.
|
|
66
|
+
- Só registrar se houver simplificação clara e segura, com evidência local.
|
|
67
|
+
|
|
68
|
+
## Severidade
|
|
69
|
+
|
|
70
|
+
- `P0`: quebra crítica, perda/corrupção de dados, bypass de segurança, execução impossível.
|
|
71
|
+
- `P1`: bug provável em fluxo principal, contrato errado, regressão relevante, risco alto.
|
|
72
|
+
- `P2`: gap importante, cenário não coberto, arquitetura frágil, teste/validação faltante com risco real.
|
|
73
|
+
- `P3`: melhoria localizada, simplificação Ponytail, DX/manutenção, observação de baixo risco.
|
|
74
|
+
|
|
75
|
+
## Formato do achado
|
|
76
|
+
|
|
77
|
+
```md
|
|
78
|
+
### AUDIT-001 — P1 — <categoria>
|
|
79
|
+
- Arquivo: `path/to/file.ext:123`
|
|
80
|
+
- Evidência: <fato observado no código>
|
|
81
|
+
- Impacto: <falha/risco concreto>
|
|
82
|
+
- Correção proposta: <direção, sem implementar>
|
|
83
|
+
- Dependências/bloqueios: <se houver>
|
|
84
|
+
- Status: `open`
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Se não houver linha precisa, não promova a finding; registre em limitações ou perguntas.
|
|
88
|
+
|
|
89
|
+
## Relatório de auditoria
|
|
90
|
+
|
|
91
|
+
Responder em pt-BR com:
|
|
92
|
+
|
|
93
|
+
```md
|
|
94
|
+
# Auditoria Atlas — <target>
|
|
95
|
+
|
|
96
|
+
## Stack detectada
|
|
97
|
+
...
|
|
98
|
+
|
|
99
|
+
## Regras locais consultadas
|
|
100
|
+
...
|
|
101
|
+
|
|
102
|
+
## Boundary auditado
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
## Achados
|
|
106
|
+
### P0
|
|
107
|
+
...
|
|
108
|
+
### P1
|
|
109
|
+
...
|
|
110
|
+
### P2
|
|
111
|
+
...
|
|
112
|
+
### P3
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
## Gaps por área
|
|
116
|
+
- Contrato/dados:
|
|
117
|
+
- Segurança:
|
|
118
|
+
- Testes:
|
|
119
|
+
- Arquitetura:
|
|
120
|
+
|
|
121
|
+
## Limitações
|
|
122
|
+
...
|
|
123
|
+
|
|
124
|
+
## Próximo passo seguro
|
|
125
|
+
...
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Se zero achados, diga explicitamente `Nenhum achado P0/P1/P2/P3 com evidência suficiente no boundary auditado` e liste risco residual/limitações.
|
|
129
|
+
|
|
130
|
+
## `--handoff`
|
|
131
|
+
|
|
132
|
+
Quando `--handoff` estiver presente, escrever em `.atlas/plans/PLAN_AUDIT_<slug>.md` um plano **conforme ao `PLAN_TEMPLATE.md` canônico** (resolver em `<raiz-do-plugin>/packages/templates/`), para que passe no gate TC (`atlas_verify_template_conformance`) e seja consumível por `/workflow execute plan <path>` ou por `atlas-plan-execute`. O plano só pode conter tasks derivadas dos achados evidenciados. Se a escrita falhar, reportar bloqueio e não fingir que há handoff executável.
|
|
133
|
+
|
|
134
|
+
A auditoria **não tem PRD**: a fonte de verdade é o relatório de auditoria + achados + regras locais reais. O plano espelha o template, mas reancorado na auditoria — **sem inventar decisões `D*` nem referências PRD inexistentes**.
|
|
135
|
+
|
|
136
|
+
### Cabeçalho obrigatório (gate TC)
|
|
137
|
+
|
|
138
|
+
O gate TC exige literalmente a linha `| **PRD** |`, referência a `BOUNDARY_PRD_PLAN.md` e tarefas `#### T01.`. Em `execution_mode: sequencial`, a §7 Slices é dispensável.
|
|
139
|
+
|
|
140
|
+
```md
|
|
141
|
+
# PLAN AUDIT — <target> (correção de auditoria)
|
|
142
|
+
|
|
143
|
+
| Campo | Valor |
|
|
144
|
+
|-------|-------|
|
|
145
|
+
| **PRD** | N/A — origem auditoria; ver **Source audit** abaixo |
|
|
146
|
+
| **Package / app** | `<boundary auditado>` |
|
|
147
|
+
| **Tipo** | `audit-fix` |
|
|
148
|
+
| **execution_mode** | `sequencial (T01→TN)` |
|
|
149
|
+
| **Data** | <YYYY-MM-DD> |
|
|
150
|
+
|
|
151
|
+
**Escopo técnico:** boundary da auditoria. **Fora:** <o que ficou fora do target/scope>.
|
|
152
|
+
|
|
153
|
+
Política: [BOUNDARY_PRD_PLAN.md](./BOUNDARY_PRD_PLAN.md).
|
|
154
|
+
|
|
155
|
+
## Metadados de execução
|
|
156
|
+
- Plan prefix: `atlas`
|
|
157
|
+
- Execution mode: `sequencial (T01→TN)`
|
|
158
|
+
- Executor skill: `atlas-plan-execute`
|
|
159
|
+
- Internal validator: `atlas-task-validator`
|
|
160
|
+
- Source audit: `<título/path/data do relatório>`
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
> `N/A — origem auditoria` satisfaz o regex do TC declarando a proveniência sem inventar PRD.
|
|
164
|
+
|
|
165
|
+
### Seções obrigatórias (§1–§6, §8)
|
|
166
|
+
|
|
167
|
+
Sem PRD, as âncoras "derivados do PRD" do template passam a apontar para achados/regras locais:
|
|
168
|
+
|
|
169
|
+
- **§1 Tradução executiva** — 1 parágrafo do que será corrigido + resultado observável; referência ao relatório de auditoria.
|
|
170
|
+
- **§2 Invariantes de execução** — invariantes derivados das regras locais reais e do boundary (não de `D*`).
|
|
171
|
+
- **§3 Pitfalls** — anti-padrões evidenciados na auditoria → correção canônica.
|
|
172
|
+
- **§4 Estado na abertura da sprint** — 3–6 bullets do que está quebrado hoje, ancorados nos achados.
|
|
173
|
+
- **§5 Tarefas de execução** — `#### T01.`…`#### TNN.`, uma por achado evidenciado (schema abaixo).
|
|
174
|
+
- **§6 Contratos técnicos** — só se a auditoria revelou contrato/dados/schema afetado; senão `Não aplicável`.
|
|
175
|
+
- **§8 Validação e checklist (validator)** — comandos reais detectados (manifests/scripts) + checkboxes derivados dos achados. Nunca inventar `flutter`/`npm`/`pytest`.
|
|
176
|
+
|
|
177
|
+
### Schema de cada task (§5)
|
|
178
|
+
|
|
179
|
+
```md
|
|
180
|
+
#### T01. <correção curta>
|
|
181
|
+
- **Objetivo:** <resultado observável>
|
|
182
|
+
- **Referência ao achado:** `AUDIT-NNN` — `arquivo:linha`
|
|
183
|
+
- **Mudança esperada:** <o que muda concretamente>
|
|
184
|
+
- **Invariantes preservados:** <§2 / regra local>
|
|
185
|
+
- **Não mudar:** <…>
|
|
186
|
+
- **Dependências:** <nenhuma | T0X>
|
|
187
|
+
- **Riscos:** <se relevante>
|
|
188
|
+
- **Critério de done:** <sinal objetivo>
|
|
189
|
+
- **Validação local:**
|
|
190
|
+
```bash
|
|
191
|
+
cd <package> && <comando real>
|
|
192
|
+
```
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Regras do handoff
|
|
196
|
+
|
|
197
|
+
- Incluir apenas validações derivadas de manifests/configs reais.
|
|
198
|
+
- Não usar task vaga como "refatorar geral".
|
|
199
|
+
- Não incluir achado sem `arquivo:linha`; toda task aponta para um `AUDIT-NNN`.
|
|
200
|
+
- Não chamar executor automaticamente.
|
|
201
|
+
- Reportar o path final do plano e deixar claro que ele não foi executado.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Atlas Audit"
|
|
3
|
+
short_description: "Auditoria universal sem correção de código"
|
|
4
|
+
default_prompt: "Use $atlas-audit para auditar o target informado contra regras locais, stack detectada e Ponytail pass, sem alterar código."
|
|
5
|
+
|
|
6
|
+
policy:
|
|
7
|
+
allow_implicit_invocation: true
|
|
@@ -102,6 +102,12 @@ Ative regras específicas somente quando o perfil retornar `true`:
|
|
|
102
102
|
- `flutter_dart`: lifecycle Flutter, rotas/args, null-safety/casts, l10n, analyze/test; GetX somente se dependência/import/regra real confirmar GetX.
|
|
103
103
|
- `node_typescript`: handles/promises, validação runtime, ESM/CJS/exports/tipos e scripts Node reais.
|
|
104
104
|
- `python`: context managers/cleanup, exceções/async, typing/parsing e ferramentas Python declaradas.
|
|
105
|
+
- `go`: context/cancelamento, goroutines, erros retornados, data race e comandos Go declarados.
|
|
106
|
+
- `rust`: `Result`/`Option`, ownership/lifetime, unwrap em fronteiras recuperáveis e comandos Cargo declarados.
|
|
107
|
+
- `java_kotlin`: nullability, exceptions, resource cleanup, threads/coroutines e Maven/Gradle declarados.
|
|
108
|
+
- `firebase`: rules/claims/authz, paths/ownership, listeners e emuladores/checks declarados.
|
|
109
|
+
- `supabase`: RLS/auth claims, schema/migrations, RPC/Edge Functions, storage policies e checks declarados.
|
|
110
|
+
- `rest_openapi`: request/response, status codes, paginação, erros, idempotência e contrato OpenAPI quando presente.
|
|
105
111
|
|
|
106
112
|
Monorepo pode ativar múltiplos perfis, sempre restritos ao boundary correspondente. Fixture Node sem sinal Flutter não recebe regra Flutter/GetX.
|
|
107
113
|
|
|
@@ -33,4 +33,40 @@ Ativação é determinística: inspecione manifests no boundary e comandos realm
|
|
|
33
33
|
- parsing/typing nas fronteiras e mutabilidade de defaults;
|
|
34
34
|
- `pytest`, `ruff`, `mypy` ou equivalente apenas se declarados.
|
|
35
35
|
|
|
36
|
+
## Go — `go.mod` ou comando Go real
|
|
37
|
+
|
|
38
|
+
- context propagation/cancelamento, goroutine leak, data race e cleanup;
|
|
39
|
+
- erros retornados sem swallow, wrapping útil e validação em fronteiras;
|
|
40
|
+
- `go test`, `go vet`, `go test -race` e linters somente quando declarados/aplicáveis.
|
|
41
|
+
|
|
42
|
+
## Rust — `Cargo.toml` ou comando Cargo real
|
|
43
|
+
|
|
44
|
+
- ownership/lifetime usados para segurança real, não wrappers desnecessários;
|
|
45
|
+
- `Result`/`Option` tratados sem `unwrap`/`expect` em fronteiras recuperáveis;
|
|
46
|
+
- `cargo test`, `cargo check`, `cargo clippy` e `cargo fmt` somente quando declarados/aplicáveis.
|
|
47
|
+
|
|
48
|
+
## Java/Kotlin — Maven/Gradle ou comando Java/Kotlin real
|
|
49
|
+
|
|
50
|
+
- nullability, exceptions, resource cleanup e lifecycle de threads/coroutines;
|
|
51
|
+
- boundaries de DTO/entity, serialização e validação de input;
|
|
52
|
+
- `mvn test`, `gradle test`, linters/typecheck somente quando declarados.
|
|
53
|
+
|
|
54
|
+
## Firebase — `firebase.json`, `.firebaserc` ou dependência Firebase real
|
|
55
|
+
|
|
56
|
+
- regras/claims/authz, paths e ownership de dados;
|
|
57
|
+
- falhas offline/retry, listeners/subscriptions e cleanup;
|
|
58
|
+
- emuladores/deploy/checks somente quando declarados.
|
|
59
|
+
|
|
60
|
+
## Supabase — dependência Supabase real
|
|
61
|
+
|
|
62
|
+
- RLS/auth claims, schema/migrations, RPC/Edge Functions e storage policies;
|
|
63
|
+
- sessão/token refresh, SSR/cookies quando aplicável e boundaries de dados;
|
|
64
|
+
- CLI/migration/testes somente quando declarados.
|
|
65
|
+
|
|
66
|
+
## REST/OpenAPI — OpenAPI/Swagger ou servidor/cliente HTTP real
|
|
67
|
+
|
|
68
|
+
- compatibilidade request/response, status codes, paginação, erros e idempotência;
|
|
69
|
+
- validação runtime nas bordas e divergência entre contrato e implementação;
|
|
70
|
+
- geração/validação OpenAPI somente quando declarada.
|
|
71
|
+
|
|
36
72
|
Perfis podem coexistir em monorepo. Regra de perfil nunca vira finding fora do boundary onde seu sinal foi ativado.
|
|
@@ -10,7 +10,11 @@ const VALID = Object.freeze({
|
|
|
10
10
|
state: new Set(['backlog', 'ready', 'doing', 'review', 'done', 'blocked']),
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
const STACK_MANIFESTS = [
|
|
13
|
+
const STACK_MANIFESTS = [
|
|
14
|
+
'package.json', 'tsconfig.json', 'pubspec.yaml', 'pyproject.toml', 'requirements.txt', 'setup.py',
|
|
15
|
+
'go.mod', 'Cargo.toml', 'pom.xml', 'build.gradle', 'build.gradle.kts', 'settings.gradle', 'settings.gradle.kts',
|
|
16
|
+
'firebase.json', '.firebaserc', 'openapi.yaml', 'openapi.yml', 'openapi.json', 'swagger.yaml', 'swagger.yml', 'swagger.json',
|
|
17
|
+
];
|
|
14
18
|
|
|
15
19
|
function boundaryRoot(projectRoot, boundary) {
|
|
16
20
|
const project = path.resolve(projectRoot);
|
|
@@ -47,6 +51,9 @@ function containsGetxImport(root) {
|
|
|
47
51
|
|
|
48
52
|
function detectBoundaryProfile(root, declaredCommands) {
|
|
49
53
|
const exists = (name) => fs.existsSync(path.join(root, name));
|
|
54
|
+
const readIfExists = (name) => {
|
|
55
|
+
try { return exists(name) ? fs.readFileSync(path.join(root, name), 'utf8') : ''; } catch { return ''; }
|
|
56
|
+
};
|
|
50
57
|
const commands = declaredCommands.filter((v) => typeof v === 'string');
|
|
51
58
|
let packageJson = null;
|
|
52
59
|
if (exists('package.json')) {
|
|
@@ -57,13 +64,36 @@ function detectBoundaryProfile(root, declaredCommands) {
|
|
|
57
64
|
try { pubspec = fs.readFileSync(path.join(root, 'pubspec.yaml'), 'utf8'); } catch {}
|
|
58
65
|
}
|
|
59
66
|
const packageCommands = Object.values(packageJson?.scripts ?? {}).filter((v) => typeof v === 'string');
|
|
67
|
+
const packageDeps = Object.keys({
|
|
68
|
+
...(packageJson?.dependencies ?? {}),
|
|
69
|
+
...(packageJson?.devDependencies ?? {}),
|
|
70
|
+
...(packageJson?.peerDependencies ?? {}),
|
|
71
|
+
...(packageJson?.optionalDependencies ?? {}),
|
|
72
|
+
});
|
|
73
|
+
const cargo = readIfExists('Cargo.toml');
|
|
74
|
+
const pom = readIfExists('pom.xml');
|
|
75
|
+
const gradle = `${readIfExists('build.gradle')}\n${readIfExists('build.gradle.kts')}\n${readIfExists('settings.gradle')}\n${readIfExists('settings.gradle.kts')}`;
|
|
60
76
|
const allCommands = [...commands, ...packageCommands];
|
|
61
77
|
const hasCommand = (re) => allCommands.some((command) => re.test(command));
|
|
78
|
+
const hasPackageDep = (re) => packageDeps.some((dep) => re.test(dep));
|
|
79
|
+
const javaKotlinSignal = exists('pom.xml') || exists('build.gradle') || exists('build.gradle.kts')
|
|
80
|
+
|| exists('settings.gradle') || exists('settings.gradle.kts') || hasCommand(/\b(gradle|mvn|java|javac|kotlinc)\b/);
|
|
81
|
+
const restSignal = exists('openapi.yaml') || exists('openapi.yml') || exists('openapi.json')
|
|
82
|
+
|| exists('swagger.yaml') || exists('swagger.yml') || exists('swagger.json')
|
|
83
|
+
|| hasPackageDep(/\b(openapi|swagger|express|fastify|koa|hono|axios|ky)\b/i)
|
|
84
|
+
|| /openapi|swagger|spring-boot-starter-web|ktor|retrofit/i.test(`${pom}\n${gradle}\n${pubspec}`);
|
|
62
85
|
return {
|
|
63
86
|
universal: true,
|
|
64
87
|
flutter_dart: exists('pubspec.yaml') || hasCommand(/\b(flutter|dart)\b/),
|
|
65
88
|
node_typescript: exists('package.json') || exists('tsconfig.json') || hasCommand(/\b(node|npm|pnpm|yarn|bun|tsc)\b/),
|
|
66
89
|
python: exists('pyproject.toml') || exists('requirements.txt') || exists('setup.py') || hasCommand(/\b(python3?|pytest|ruff|mypy)\b/),
|
|
90
|
+
go: exists('go.mod') || hasCommand(/\bgo\s+(test|build|run|vet|fmt)\b/),
|
|
91
|
+
rust: exists('Cargo.toml') || hasCommand(/\bcargo\s+(test|build|run|check|clippy|fmt)\b/) || /^\s*\[package\]/m.test(cargo),
|
|
92
|
+
java_kotlin: javaKotlinSignal,
|
|
93
|
+
firebase: exists('firebase.json') || exists('.firebaserc') || hasPackageDep(/^firebase$|^@firebase\/|firebase-admin/i)
|
|
94
|
+
|| /firebase_core|cloud_firestore|firebase_auth|firebase_messaging|firebase_storage/i.test(pubspec),
|
|
95
|
+
supabase: hasPackageDep(/^@supabase\/|supabase-js/i) || /supabase_flutter|supabase|postgrest/i.test(pubspec),
|
|
96
|
+
rest_openapi: restSignal,
|
|
67
97
|
getx: /^\s{0,4}get\s*:/m.test(pubspec) || containsGetxImport(root),
|
|
68
98
|
};
|
|
69
99
|
}
|
|
@@ -80,6 +110,12 @@ export function detectStackProfiles(root, declaredCommands = [], boundaryPaths =
|
|
|
80
110
|
flutter_dart: boundaries.some((profile) => profile.flutter_dart),
|
|
81
111
|
node_typescript: boundaries.some((profile) => profile.node_typescript),
|
|
82
112
|
python: boundaries.some((profile) => profile.python),
|
|
113
|
+
go: boundaries.some((profile) => profile.go),
|
|
114
|
+
rust: boundaries.some((profile) => profile.rust),
|
|
115
|
+
java_kotlin: boundaries.some((profile) => profile.java_kotlin),
|
|
116
|
+
firebase: boundaries.some((profile) => profile.firebase),
|
|
117
|
+
supabase: boundaries.some((profile) => profile.supabase),
|
|
118
|
+
rest_openapi: boundaries.some((profile) => profile.rest_openapi),
|
|
83
119
|
getx: boundaries.some((profile) => profile.getx),
|
|
84
120
|
boundaries,
|
|
85
121
|
};
|