wize-dev-kit 0.10.1 → 0.12.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/AGENTS.md +3 -3
- package/CHANGELOG.md +48 -0
- package/README.es.md +1 -1
- package/README.md +1 -1
- package/README.pt-BR.md +1 -1
- package/package.json +1 -1
- package/src/app-overlay/wize-app-release-channels/workflow.md +2 -0
- package/src/app-overlay/wize-app-scaffold/workflow.md +2 -0
- package/src/app-overlay/wize-app-store-listing/workflow.md +2 -0
- package/src/builder-skills/wize-create-agent/workflow.md +2 -0
- package/src/builder-skills/wize-create-skill/workflow.md +2 -0
- package/src/builder-skills/wize-create-workflow/workflow.md +2 -0
- package/src/core-skills/wize-advanced-elicitation/skill.md +2 -0
- package/src/core-skills/wize-brainstorming/skill.md +2 -0
- package/src/core-skills/wize-customize/skill.md +2 -0
- package/src/core-skills/wize-editorial-review-prose/skill.md +2 -0
- package/src/core-skills/wize-editorial-review-structure/skill.md +2 -0
- package/src/core-skills/wize-grill/skill.md +3 -1
- package/src/core-skills/wize-index-docs/skill.md +3 -1
- package/src/core-skills/wize-review-adversarial/skill.md +2 -0
- package/src/core-skills/wize-review-edge-case-hunter/skill.md +2 -0
- package/src/core-skills/wize-shard-doc/skill.md +2 -0
- package/src/core-skills/wize-spec/skill.md +2 -0
- package/src/method-skills/1-analysis/wize-agent-analyst/agent.yaml +3 -0
- package/src/method-skills/1-analysis/wize-document-project/workflow.md +2 -0
- package/src/method-skills/1-analysis/wize-domain-research/workflow.md +7 -0
- package/src/method-skills/1-analysis/wize-market-research/workflow.md +7 -0
- package/src/method-skills/1-analysis/wize-prfaq/workflow.md +2 -0
- package/src/method-skills/1-analysis/wize-product-brief/workflow.md +2 -0
- package/src/method-skills/1-analysis/wize-refresh-knowledge/workflow.md +3 -1
- package/src/method-skills/1-analysis/wize-research/workflow.md +24 -89
- package/src/method-skills/1-analysis/wize-technical-research/workflow.md +7 -0
- package/src/method-skills/1-analysis/wize-trigger-map/workflow.md +2 -0
- package/src/method-skills/2-plan-workflows/wize-create-prd/workflow.md +3 -2
- package/src/method-skills/2-plan-workflows/wize-edit-prd/workflow.md +3 -1
- package/src/method-skills/2-plan-workflows/wize-ux-design/workflow.md +2 -0
- package/src/method-skills/2-plan-workflows/wize-ux-scenarios/workflow.md +2 -0
- package/src/method-skills/2-plan-workflows/wize-validate-prd/workflow.md +3 -1
- package/src/method-skills/3-solutioning/wize-check-implementation-readiness/workflow.md +2 -0
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-02-context.md +1 -1
- package/src/method-skills/3-solutioning/wize-create-architecture/steps/step-08-complete.md +1 -1
- package/src/method-skills/3-solutioning/wize-create-architecture/workflow.md +2 -0
- package/src/method-skills/3-solutioning/wize-create-epics-and-stories/workflow.md +7 -6
- package/src/method-skills/3-solutioning/wize-design-system/workflow.md +2 -0
- package/src/method-skills/3-solutioning/wize-nfr-principles/workflow.md +3 -1
- package/src/method-skills/3-solutioning/wize-project-context/workflow.md +3 -1
- package/src/method-skills/3-solutioning/wize-tech-vision/workflow.md +3 -1
- package/src/method-skills/4-implementation/wize-changelog/workflow.md +184 -0
- package/src/method-skills/4-implementation/wize-checkpoint-preview/workflow.md +8 -6
- package/src/method-skills/4-implementation/wize-code-review/workflow.md +2 -0
- package/src/method-skills/4-implementation/wize-correct-course/workflow.md +3 -1
- package/src/method-skills/4-implementation/wize-create-story/workflow.md +7 -13
- package/src/method-skills/4-implementation/wize-dev-story/workflow.md +3 -1
- package/src/method-skills/4-implementation/wize-investigate/workflow.md +2 -0
- package/src/method-skills/4-implementation/wize-quick-dev/workflow.md +2 -0
- package/src/method-skills/4-implementation/wize-release/workflow.md +162 -0
- package/src/method-skills/4-implementation/wize-retrospective/workflow.md +7 -5
- package/src/method-skills/4-implementation/wize-sprint-planning/workflow.md +11 -6
- package/src/method-skills/4-implementation/wize-sprint-status/workflow.md +2 -0
- package/src/method-skills/module.yaml +12 -3
- package/src/orchestrator-skills/module.yaml +4 -1
- package/src/orchestrator-skills/wize/skill.md +164 -0
- package/src/orchestrator-skills/wize-help/skill.md +127 -6
- package/src/orchestrator-skills/wize-onboarding/workflow.md +3 -1
- package/src/orchestrator-skills/wize-orchestrator/agent.yaml +1 -0
- package/src/orchestrator-skills/wize-orchestrator/persona.md +29 -4
- package/src/orchestrator-skills/wize-party-mode/workflow.md +2 -0
- package/src/security-overlay/_shared/backlog.js +3 -14
- package/src/security-overlay/_shared/scope-parser.js +27 -0
- package/src/security-overlay/agents/red-teamer/agent.yaml +2 -0
- package/src/security-overlay/agents/red-teamer/persona.md +6 -0
- package/src/security-overlay/skills/wize-sec-enumerate/skill.md +2 -0
- package/src/security-overlay/skills/wize-sec-pentest/scripts/run-pipeline.js +18 -1
- package/src/security-overlay/skills/wize-sec-pentest/skill.md +2 -0
- package/src/security-overlay/skills/wize-sec-recon/skill.md +2 -0
- package/src/security-overlay/skills/wize-sec-scope/scripts/generate-scope.js +106 -0
- package/src/security-overlay/skills/wize-sec-scope/skill.md +52 -0
- package/src/tea-skills/wize-qa-generate-e2e-tests/workflow.md +2 -0
- package/src/tea-skills/wize-tea-design/workflow.md +3 -0
- package/src/tea-skills/wize-tea-gate/workflow.md +3 -0
- package/src/tea-skills/wize-tea-nfr/workflow.md +3 -0
- package/src/tea-skills/wize-tea-review/workflow.md +3 -0
- package/src/tea-skills/wize-tea-risk/workflow.md +3 -0
- package/src/tea-skills/wize-tea-trace/workflow.md +3 -0
- package/src/web-overlay/wize-web-deploy/workflow.md +2 -0
- package/src/web-overlay/wize-web-scaffold/workflow.md +2 -0
- package/src/web-overlay/wize-web-seo-audit/workflow.md +2 -0
- package/tools/installer/commands/doctor.js +2 -2
- package/tools/installer/onboarding.js +14 -21
- package/tools/installer/render-shared.js +50 -28
- package/tools/installer/validators/run-all.js +3 -2
- package/tools/installer/wize-cli.js +323 -77
|
@@ -111,10 +111,37 @@ function loadScope(scopePath) {
|
|
|
111
111
|
return scope;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// signScope(scopePath) — read, parse, recompute SHA-256 of the body, and
|
|
115
|
+
// rewrite the scope_sha256 field in the frontmatter. Returns metadata about
|
|
116
|
+
// the signed scope. Throws ScopeError on missing file or invalid format.
|
|
117
|
+
// Idempotent: running twice on the same body produces the same hash.
|
|
118
|
+
function signScope(scopePath) {
|
|
119
|
+
if (!fs.existsSync(scopePath)) {
|
|
120
|
+
throw new ScopeError('MISSING_FILE', null,
|
|
121
|
+
`scope.md ausente em ${scopePath} — crie com wize-sec-scope antes de assinar`);
|
|
122
|
+
}
|
|
123
|
+
const text = fs.readFileSync(scopePath, 'utf8');
|
|
124
|
+
const scope = parseScope(text);
|
|
125
|
+
|
|
126
|
+
const newHash = computeScopeSha256(scope.body);
|
|
127
|
+
|
|
128
|
+
// Replace scope_sha256 within the frontmatter block only (avoid
|
|
129
|
+
// accidental matches in the body).
|
|
130
|
+
const fmMatch = text.match(/^---\n([\s\S]*?)\n---\n/);
|
|
131
|
+
const fmBlock = fmMatch[0];
|
|
132
|
+
const newFmBlock = fmBlock.replace(/^(scope_sha256:\s*).*$/m, `$1${newHash}`);
|
|
133
|
+
const newText = text.replace(fmBlock, newFmBlock);
|
|
134
|
+
|
|
135
|
+
fs.writeFileSync(scopePath, newText, 'utf8');
|
|
136
|
+
|
|
137
|
+
return { path: scopePath, sha256: newHash, accepted_by: scope.frontmatter.accepted_by };
|
|
138
|
+
}
|
|
139
|
+
|
|
114
140
|
module.exports = {
|
|
115
141
|
parseScope,
|
|
116
142
|
validateScope,
|
|
117
143
|
computeScopeSha256,
|
|
118
144
|
loadScope,
|
|
145
|
+
signScope,
|
|
119
146
|
ScopeError
|
|
120
147
|
};
|
|
@@ -25,6 +25,12 @@ Each phase is a standalone skill; the orchestrator `wize-sec-pentest` chains the
|
|
|
25
25
|
- **Missing tools degrade, they don't abort.** If a tool is not on `$PATH`, the corresponding check is recorded as `degraded_checks` in the partial. The pipeline continues.
|
|
26
26
|
- **Flags via allowlist.** Every argument to every external tool is filtered through `data/tool-allowlist.json`. I never pass user-supplied flags directly to `execFile`.
|
|
27
27
|
|
|
28
|
+
## Scope management
|
|
29
|
+
|
|
30
|
+
- **Create scope:** `/wize-sec-scope` — guided flow that asks for target URL, hosts, paths, and acceptance name. Generates `.wize/security/scope.md` with a valid `scope_sha256`.
|
|
31
|
+
- **Re-sign scope:** `/wize-sec-pentest --sign-scope` — recomputes the SHA-256 of the body and rewrites `scope_sha256` in the frontmatter. Use after editing the allowlist manually.
|
|
32
|
+
- **Validate scope:** `loadScope()` is called before any offensive action. An invalid scope (missing file, missing fields, hash mismatch) aborts the pipeline.
|
|
33
|
+
|
|
28
34
|
## Limits
|
|
29
35
|
|
|
30
36
|
- I do NOT attack hosts, URLs, or paths outside the `scope.md` allowlist. Even with `--active`.
|
|
@@ -26,10 +26,11 @@ const PHASE_SCRIPTS = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
function parseArgv(argv) {
|
|
29
|
-
const out = { active: false, scopePath: null, kitRoot: DEFAULT_KIT_ROOT };
|
|
29
|
+
const out = { active: false, scopePath: null, kitRoot: DEFAULT_KIT_ROOT, signScope: false };
|
|
30
30
|
for (let i = 0; i < (argv || []).length; i++) {
|
|
31
31
|
const a = argv[i];
|
|
32
32
|
if (a === '--active') out.active = true;
|
|
33
|
+
else if (a === '--sign-scope') out.signScope = true;
|
|
33
34
|
else if (a === '--scope' && argv[i + 1]) { out.scopePath = argv[i + 1]; i++; }
|
|
34
35
|
else if (a.startsWith('--scope=')) out.scopePath = a.slice('--scope='.length);
|
|
35
36
|
}
|
|
@@ -120,6 +121,22 @@ async function runPipeline(opts = {}) {
|
|
|
120
121
|
// user invokes `/wize-sec-pentest`.
|
|
121
122
|
async function main() {
|
|
122
123
|
const args = parseArgv(process.argv.slice(2));
|
|
124
|
+
|
|
125
|
+
// --sign-scope: recompute SHA-256 and exit (no pipeline run).
|
|
126
|
+
if (args.signScope) {
|
|
127
|
+
try {
|
|
128
|
+
const { signScope } = require('../../../_shared/scope-parser.js');
|
|
129
|
+
const result = signScope(args.scopePath);
|
|
130
|
+
console.log(`✓ Scope re-assinado. SHA-256: ${result.sha256}`);
|
|
131
|
+
console.log(` aceite por: ${result.accepted_by}`);
|
|
132
|
+
console.log(` arquivo: ${result.path}`);
|
|
133
|
+
process.exit(0);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error('✖ sign-scope falhou:', err && err.message ? err.message : err);
|
|
136
|
+
process.exit(2);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
123
140
|
let result;
|
|
124
141
|
try {
|
|
125
142
|
// Derive the securityDir from the scopePath's parent (the .wize/security
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// generate-scope.js — creates a valid .wize/security/scope.md from CLI args.
|
|
4
|
+
// Zero-dep. Computes scope_sha256 from the body (including the leading newline
|
|
5
|
+
// separator, matching parseScope's convention).
|
|
6
|
+
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const crypto = require('node:crypto');
|
|
10
|
+
|
|
11
|
+
function parseArgv(argv) {
|
|
12
|
+
const out = { url: null, hosts: [], paths: [], acceptedBy: null, outPath: null };
|
|
13
|
+
for (let i = 0; i < (argv || []).length; i++) {
|
|
14
|
+
const a = argv[i];
|
|
15
|
+
if (a === '--url' && argv[i + 1]) { out.url = argv[i + 1]; i++; }
|
|
16
|
+
else if (a.startsWith('--url=')) out.url = a.slice('--url='.length);
|
|
17
|
+
else if (a === '--hosts' && argv[i + 1]) { out.hosts = argv[i + 1].split(',').map(s => s.trim()).filter(Boolean); i++; }
|
|
18
|
+
else if (a.startsWith('--hosts=')) out.hosts = a.slice('--hosts='.length).split(',').map(s => s.trim()).filter(Boolean);
|
|
19
|
+
else if (a === '--paths' && argv[i + 1]) { out.paths = argv[i + 1].split(',').map(s => s.trim()).filter(Boolean); i++; }
|
|
20
|
+
else if (a.startsWith('--paths=')) out.paths = a.slice('--paths='.length).split(',').map(s => s.trim()).filter(Boolean);
|
|
21
|
+
else if (a === '--accepted-by' && argv[i + 1]) { out.acceptedBy = argv[i + 1]; i++; }
|
|
22
|
+
else if (a.startsWith('--accepted-by=')) out.acceptedBy = a.slice('--accepted-by='.length);
|
|
23
|
+
else if (a === '--out' && argv[i + 1]) { out.outPath = argv[i + 1]; i++; }
|
|
24
|
+
else if (a.startsWith('--out=')) out.outPath = a.slice('--out='.length);
|
|
25
|
+
}
|
|
26
|
+
if (!out.outPath) out.outPath = path.join(process.cwd(), '.wize', 'security', 'scope.md');
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildBody({ url, hosts, paths }) {
|
|
31
|
+
const lines = [];
|
|
32
|
+
|
|
33
|
+
lines.push('## allowlist');
|
|
34
|
+
|
|
35
|
+
lines.push('hosts:');
|
|
36
|
+
for (const h of hosts) lines.push(` - ${h}`);
|
|
37
|
+
|
|
38
|
+
lines.push('urls:');
|
|
39
|
+
if (url) lines.push(` - ${url.replace(/\/+$/, '')}/`);
|
|
40
|
+
|
|
41
|
+
lines.push('paths:');
|
|
42
|
+
if (paths.length > 0) {
|
|
43
|
+
for (const p of paths) lines.push(` - ${p}`);
|
|
44
|
+
} else {
|
|
45
|
+
lines.push(' - /');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
lines.push('');
|
|
49
|
+
lines.push('## dast_target');
|
|
50
|
+
if (url) lines.push(`url: ${url}`);
|
|
51
|
+
lines.push('');
|
|
52
|
+
lines.push('## notes');
|
|
53
|
+
lines.push(`Scope gerado por wize-sec-scope em ${new Date().toISOString()}.`);
|
|
54
|
+
lines.push('');
|
|
55
|
+
|
|
56
|
+
return lines.join('\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function generateScope({ url, hosts, paths, acceptedBy, outPath }) {
|
|
60
|
+
const body = buildBody({ url, hosts, paths });
|
|
61
|
+
|
|
62
|
+
// The hash is computed on the body as parseScope returns it: with the
|
|
63
|
+
// leading newline separator between the closing --- and the body.
|
|
64
|
+
const signedBody = '\n' + body;
|
|
65
|
+
const scopeSha256 = crypto.createHash('sha256').update(signedBody, 'utf8').digest('hex');
|
|
66
|
+
const acceptedAt = new Date().toISOString();
|
|
67
|
+
|
|
68
|
+
const fm = [
|
|
69
|
+
`accepted_by: ${acceptedBy || 'unknown'}`,
|
|
70
|
+
`accepted_at: ${acceptedAt}`,
|
|
71
|
+
`scope_sha256: ${scopeSha256}`
|
|
72
|
+
].join('\n');
|
|
73
|
+
|
|
74
|
+
const content = `---\n${fm}\n---\n${signedBody}`;
|
|
75
|
+
|
|
76
|
+
const dir = path.dirname(outPath);
|
|
77
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
78
|
+
fs.writeFileSync(outPath, content, 'utf8');
|
|
79
|
+
|
|
80
|
+
return { path: outPath, sha256: scopeSha256, acceptedBy, acceptedAt };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function main() {
|
|
84
|
+
const args = parseArgv(process.argv.slice(2));
|
|
85
|
+
|
|
86
|
+
if (!args.url) {
|
|
87
|
+
console.error('✖ --url é obrigatório (ex: --url=http://localhost:8080)');
|
|
88
|
+
process.exit(2);
|
|
89
|
+
}
|
|
90
|
+
if (!args.acceptedBy) {
|
|
91
|
+
console.error('✖ --accepted-by é obrigatório (ex: --accepted-by="Seu Nome")');
|
|
92
|
+
process.exit(2);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const result = generateScope(args);
|
|
96
|
+
console.log(`✓ scope.md gerado em ${result.path}`);
|
|
97
|
+
console.log(` SHA-256: ${result.sha256}`);
|
|
98
|
+
console.log(` aceite por: ${result.acceptedBy}`);
|
|
99
|
+
console.log(` aceite em: ${result.acceptedAt}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (require.main === module) {
|
|
103
|
+
main();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = { generateScope, buildBody, parseArgv };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
code: wize-sec-scope
|
|
4
|
+
description: "Use para criar ou re-assinar o scope.md de segurança. Guia o usuário com perguntas e gera o arquivo com hash SHA-256."
|
|
5
|
+
name: wize-sec-scope
|
|
6
|
+
overlay: security
|
|
7
|
+
module: security-overlay
|
|
8
|
+
owner: wize-sec-red-teamer # Natasha Romanoff
|
|
9
|
+
status: ready
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# wize-sec-scope — Criação guiada de scope.md
|
|
13
|
+
|
|
14
|
+
Cria o arquivo `.wize/security/scope.md` a partir de perguntas interativas. Gera o frontmatter com `accepted_by`, `accepted_at`, e `scope_sha256` (SHA-256 do corpo). O arquivo gerado passa na validação do `loadScope()` sem intervenção manual.
|
|
15
|
+
|
|
16
|
+
## Uso
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
/wize-sec-scope
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Fluxo guiado
|
|
23
|
+
|
|
24
|
+
O agente faz uma pergunta por vez:
|
|
25
|
+
|
|
26
|
+
1. **URL base do alvo** — ex: `http://localhost:8080`
|
|
27
|
+
2. **Hosts adicionais no escopo** — um por linha, Enter para pular
|
|
28
|
+
3. **Paths no escopo** — ex: `/api/*`, Enter para todos (`/`)
|
|
29
|
+
4. **Nome para o aceite** — quem está autorizando o pentest
|
|
30
|
+
|
|
31
|
+
Ao final, gera `scope.md` com:
|
|
32
|
+
|
|
33
|
+
- Frontmatter: `accepted_by`, `accepted_at` (ISO-8601), `scope_sha256`
|
|
34
|
+
- Corpo: `## allowlist` com hosts, urls e paths
|
|
35
|
+
- `## dast_target` com a URL base
|
|
36
|
+
- `## notes` com o timestamp de criação
|
|
37
|
+
|
|
38
|
+
## Re-assinatura
|
|
39
|
+
|
|
40
|
+
Se o `scope.md` já existe e foi editado, use `--sign-scope` para recomputar o hash:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
/wize-sec-pentest --sign-scope
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Script
|
|
47
|
+
|
|
48
|
+
`scripts/generate-scope.js` — gera o arquivo programaticamente (usado pelo agente ou chamado diretamente).
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
node scripts/generate-scope.js --url=http://localhost:8080 --hosts=localhost,127.0.0.1 --paths=/api,/admin --accepted-by="Seu Nome"
|
|
52
|
+
```
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
+
|
|
2
3
|
code: wize-tea-design
|
|
4
|
+
description: "Use quando uma story entrar em desenvolvimento e precisar do contrato de testes (split, fixtures, mocks, assertions) que a Shuri implementará."
|
|
3
5
|
name: TEA Test Design
|
|
6
|
+
phase: 4-implementation
|
|
4
7
|
gate: design
|
|
5
8
|
owner: wize-agent-test-architect # Hawkeye
|
|
6
9
|
when: per-story-start
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
+
|
|
2
3
|
code: wize-tea-gate
|
|
4
|
+
description: "Use quando uma story chegar ao final e precisar da decisão de gate (PASS/CONCERNS/FAIL/WAIVED) com rationale documentado."
|
|
3
5
|
name: TEA Gate Decision
|
|
6
|
+
phase: 4-implementation
|
|
4
7
|
gate: gate
|
|
5
8
|
owner: wize-agent-test-architect # Hawkeye
|
|
6
9
|
when: story-final
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
+
|
|
2
3
|
code: wize-tea-nfr
|
|
4
|
+
description: "Use quando um epic estiver próximo do merge e precisar verificar se atende aos princípios de NFR definidos pelo Fury."
|
|
3
5
|
name: TEA NFR Assessment
|
|
6
|
+
phase: 4-implementation
|
|
4
7
|
gate: nfr
|
|
5
8
|
owner: wize-agent-test-architect # Hawkeye
|
|
6
9
|
when: pre-merge-per-epic
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
+
|
|
2
3
|
code: wize-tea-review
|
|
4
|
+
description: "Use quando uma story terminar implementação e precisar de revisão estruturada de AC fulfillment, disciplina de teste e cobertura de risco."
|
|
3
5
|
name: TEA Story Review
|
|
6
|
+
phase: 4-implementation
|
|
4
7
|
gate: review
|
|
5
8
|
owner: wize-agent-test-architect # Hawkeye
|
|
6
9
|
when: story-end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
+
|
|
2
3
|
code: wize-tea-risk
|
|
4
|
+
description: "Use quando a arquitetura for assinada e precisar construir a matriz probabilidade × impacto que prioriza todo o trabalho de TEA."
|
|
3
5
|
name: TEA Risk Profile
|
|
6
|
+
phase: 4-implementation
|
|
4
7
|
gate: risk
|
|
5
8
|
owner: wize-agent-test-architect # Hawkeye
|
|
6
9
|
when: once-after-architecture
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
+
|
|
2
3
|
code: wize-tea-trace
|
|
4
|
+
description: "Use quando uma story estiver em implementação e precisar mapear cada AC para testes concretos no repositório."
|
|
3
5
|
name: TEA Traceability
|
|
6
|
+
phase: 4-implementation
|
|
4
7
|
gate: trace
|
|
5
8
|
owner: wize-agent-test-architect # Hawkeye
|
|
6
9
|
when: during-or-after-implementation
|
|
@@ -252,13 +252,13 @@ async function cmdDoctor({ kitRoot, projectRoot, opts = {} } = {}) {
|
|
|
252
252
|
const age = f.days == null ? 'no last_refreshed' : `${f.days}d ago`;
|
|
253
253
|
log(` ${f.name.padEnd(28)} ${age}`);
|
|
254
254
|
if (f.days != null && f.days > 60) {
|
|
255
|
-
suggestions.push({ level: 'warn', text: `\`${f.name}\` last refreshed ${f.days}d ago. Run
|
|
255
|
+
suggestions.push({ level: 'warn', text: `\`${f.name}\` last refreshed ${f.days}d ago. Run \`/wize-refresh-knowledge\` after current sprint.` });
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
if (knowledge.pendingLines > 0) {
|
|
259
259
|
log(` _pending.md: ${knowledge.pendingLines} inline note(s) waiting consolidation`);
|
|
260
260
|
if (knowledge.pendingLines >= 5) {
|
|
261
|
-
suggestions.push({ level: 'info', text: `${knowledge.pendingLines} notes piled up in _pending.md. Time to run
|
|
261
|
+
suggestions.push({ level: 'info', text: `${knowledge.pendingLines} notes piled up in _pending.md. Time to run \`/wize-refresh-knowledge\`.` });
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
const markerText = knowledge.toBeGeneratedMarkers > 0 ? `${knowledge.toBeGeneratedMarkers} marker(s)` : 'none';
|
|
@@ -1,32 +1,25 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Onboarding handoff — Wizer-driven triage after install.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Returns a clean, minimal message (≤3 lines) that always ends with "→ /wize".
|
|
5
|
+
* The message adapts to brownfield detection and active profiles without
|
|
6
|
+
* turning into a wall of text.
|
|
6
7
|
*/
|
|
7
8
|
'use strict';
|
|
8
9
|
|
|
9
10
|
function compose(detection, profiles) {
|
|
10
|
-
const lines = [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
'
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
lines.push('
|
|
19
|
-
lines.push(' → /wize-document-project');
|
|
11
|
+
const lines = [];
|
|
12
|
+
const profileCodes = new Set(profiles.map(p => p.code));
|
|
13
|
+
|
|
14
|
+
if (detection && detection.brownfield) {
|
|
15
|
+
lines.push('Brownfield detected — baseline ready.');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (profileCodes.has('security-overlay')) {
|
|
19
|
+
lines.push('Security pentest available (/wize-sec-recon).');
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
lines.push('
|
|
23
|
-
lines.push(' → /wize-product-brief (Pepper)');
|
|
24
|
-
lines.push(' → /wize-create-prd (Maria Hill, if brief exists)');
|
|
25
|
-
lines.push(' → /wize-create-architecture (Tony, if PRD exists)');
|
|
26
|
-
lines.push(' → /wize-quick-dev (Shuri, for small fixes)');
|
|
27
|
-
if (profiles.find(p => p.code === 'web-overlay')) lines.push(' → /wize-web-scaffold (overlay)');
|
|
28
|
-
if (profiles.find(p => p.code === 'app-overlay')) lines.push(' → /wize-app-scaffold (overlay)');
|
|
29
|
-
if (profiles.find(p => p.code === 'security-overlay')) lines.push(' → /wize-sec-pentest (overlay)');
|
|
21
|
+
|
|
22
|
+
lines.push('→ /wize');
|
|
30
23
|
return lines.join('\n');
|
|
31
24
|
}
|
|
32
25
|
|
|
@@ -12,6 +12,32 @@ const fs = require('node:fs');
|
|
|
12
12
|
const path = require('node:path');
|
|
13
13
|
const { walkAgents, walkWorkflows, walkSkills } = require('./validators/walk.js');
|
|
14
14
|
|
|
15
|
+
// Shared block-scalar reader used by both readYamlField (agent.yaml) and
|
|
16
|
+
// readFrontmatter (skill.md / workflow.md). Returns { value, nextIndex }.
|
|
17
|
+
function readBlockScalar(lines, startIndex, folded) {
|
|
18
|
+
const collected = [];
|
|
19
|
+
let baseIndent = null;
|
|
20
|
+
let i = startIndex;
|
|
21
|
+
for (; i < lines.length; i++) {
|
|
22
|
+
const line = lines[i];
|
|
23
|
+
if (line.trim() === '') { collected.push(''); continue; }
|
|
24
|
+
const indent = line.match(/^(\s*)/)[1].length;
|
|
25
|
+
if (indent === 0) break; // dedent to a sibling top-level key ends the block
|
|
26
|
+
if (baseIndent === null) baseIndent = indent;
|
|
27
|
+
collected.push(line.slice(baseIndent));
|
|
28
|
+
}
|
|
29
|
+
while (collected.length && collected[collected.length - 1] === '') collected.pop();
|
|
30
|
+
if (folded) {
|
|
31
|
+
const value = collected.reduce((acc, ln, idx) => {
|
|
32
|
+
if (idx === 0) return ln;
|
|
33
|
+
if (ln === '' || collected[idx - 1] === '') return acc + '\n' + ln;
|
|
34
|
+
return acc + ' ' + ln;
|
|
35
|
+
}, '').trim();
|
|
36
|
+
return { value, nextIndex: i };
|
|
37
|
+
}
|
|
38
|
+
return { value: collected.join('\n').trim(), nextIndex: i };
|
|
39
|
+
}
|
|
40
|
+
|
|
15
41
|
function readYamlField(content, field) {
|
|
16
42
|
const lines = content.split('\n');
|
|
17
43
|
const keyRe = new RegExp('^' + field + ':\\s*(.*)$');
|
|
@@ -23,27 +49,7 @@ function readYamlField(content, field) {
|
|
|
23
49
|
// Block scalar (| literal, > folded) with optional chomp/indent indicator.
|
|
24
50
|
const block = inline.match(/^([|>])[+-]?\d*\s*$/);
|
|
25
51
|
if (block) {
|
|
26
|
-
|
|
27
|
-
const collected = [];
|
|
28
|
-
let baseIndent = null;
|
|
29
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
30
|
-
const line = lines[j];
|
|
31
|
-
if (line.trim() === '') { collected.push(''); continue; }
|
|
32
|
-
const indent = line.match(/^(\s*)/)[1].length;
|
|
33
|
-
if (indent === 0) break; // dedent to a sibling top-level key ends the block
|
|
34
|
-
if (baseIndent === null) baseIndent = indent;
|
|
35
|
-
collected.push(line.slice(baseIndent));
|
|
36
|
-
}
|
|
37
|
-
while (collected.length && collected[collected.length - 1] === '') collected.pop();
|
|
38
|
-
if (folded) {
|
|
39
|
-
// Folded: blank line -> newline, otherwise join lines with a space.
|
|
40
|
-
return collected.reduce((acc, ln, idx) => {
|
|
41
|
-
if (idx === 0) return ln;
|
|
42
|
-
if (ln === '' || collected[idx - 1] === '') return acc + '\n' + ln;
|
|
43
|
-
return acc + ' ' + ln;
|
|
44
|
-
}, '').trim();
|
|
45
|
-
}
|
|
46
|
-
return collected.join('\n').trim();
|
|
52
|
+
return readBlockScalar(lines, i + 1, block[1] === '>').value;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
// Inline scalar: quoted or plain.
|
|
@@ -59,9 +65,25 @@ function readFrontmatter(content) {
|
|
|
59
65
|
if (end === -1) return {};
|
|
60
66
|
const lines = content.slice(3, end).split('\n');
|
|
61
67
|
const out = {};
|
|
62
|
-
for (
|
|
68
|
+
for (let i = 0; i < lines.length; i++) {
|
|
69
|
+
const line = lines[i];
|
|
63
70
|
const m = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\s*(.*?)\s*$/);
|
|
64
|
-
if (m)
|
|
71
|
+
if (!m) continue;
|
|
72
|
+
const key = m[1];
|
|
73
|
+
const inline = m[2].trim();
|
|
74
|
+
|
|
75
|
+
// Block scalar (| literal, > folded) with optional chomp/indent indicator.
|
|
76
|
+
const block = inline.match(/^([|>])[+-]?\d*\s*$/);
|
|
77
|
+
if (block) {
|
|
78
|
+
const result = readBlockScalar(lines, i + 1, block[1] === '>');
|
|
79
|
+
out[key] = result.value;
|
|
80
|
+
i = result.nextIndex - 1; // -1 because the for loop will increment
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Inline scalar: quoted or plain.
|
|
85
|
+
const q = inline.match(/^"([^"]*)"$/) || inline.match(/^'([^']*)'$/);
|
|
86
|
+
out[key] = (q ? q[1] : inline).trim();
|
|
65
87
|
}
|
|
66
88
|
return out;
|
|
67
89
|
}
|
|
@@ -154,7 +176,7 @@ function collectAssets(kitRoot, { profiles = ['core'] } = {}) {
|
|
|
154
176
|
code: fm.code,
|
|
155
177
|
name: fm.name || fm.code,
|
|
156
178
|
title: fm.phase || fm.gate || '',
|
|
157
|
-
description: `${fm.phase || fm.gate || 'workflow'}: ${fm.name || fm.code}`,
|
|
179
|
+
description: fm.description || `${fm.phase || fm.gate || 'workflow'}: ${fm.name || fm.code}`,
|
|
158
180
|
body: bodyAfterFrontmatter(content),
|
|
159
181
|
overlay: fm.overlay || null,
|
|
160
182
|
owner: fm.owner || null,
|
|
@@ -175,7 +197,7 @@ function collectAssets(kitRoot, { profiles = ['core'] } = {}) {
|
|
|
175
197
|
code: fm.code,
|
|
176
198
|
name: fm.name || fm.code,
|
|
177
199
|
title: fm.module || '',
|
|
178
|
-
description: fm.module ? `${fm.module} skill: ${fm.name || fm.code}` : (fm.name || fm.code),
|
|
200
|
+
description: fm.description || (fm.module ? `${fm.module} skill: ${fm.name || fm.code}` : (fm.name || fm.code)),
|
|
179
201
|
body: bodyAfterFrontmatter(content),
|
|
180
202
|
overlay: fm.overlay || null,
|
|
181
203
|
owner: fm.owner || null,
|
|
@@ -272,8 +294,8 @@ function renderAgentsMd(kitRoot, projectRoot, opts = {}) {
|
|
|
272
294
|
'instructions and persistent memory — not background reading. Read project state before',
|
|
273
295
|
'acting; write what you change back into `.wize/` so the next session inherits it.',
|
|
274
296
|
'',
|
|
275
|
-
'Before editing, classify the demand via `/wize-help
|
|
276
|
-
'
|
|
297
|
+
'Before editing, classify the demand via `/wize` (or `/wize-help`): **Quick Dev** (small, predictable,',
|
|
298
|
+
'trivially scoped, no new feature / architecture / UX / security) or **Full Lifecycle**. Never pick',
|
|
277
299
|
'Quick Dev just to skip artifacts.',
|
|
278
300
|
'',
|
|
279
301
|
'When a skill fans out to subagents, match the model tier to the task: a lightweight tier',
|
|
@@ -288,7 +310,7 @@ function renderAgentsMd(kitRoot, projectRoot, opts = {}) {
|
|
|
288
310
|
const optIn = a.code.startsWith('wize-sec-') ? ' _(security-overlay, opt-in)_' : '';
|
|
289
311
|
lines.push(`- **${a.name}** (\`${a.code}\`)${optIn} — ${a.title}. ${clipOneLine(a.description, 180)}`);
|
|
290
312
|
}
|
|
291
|
-
lines.push('', '## Where to start', '', 'Activate the orchestrator: `wize-
|
|
313
|
+
lines.push('', '## Where to start', '', 'Activate the orchestrator: `/wize` (or `/wize-help`). Wizer will guide you.', '');
|
|
292
314
|
|
|
293
315
|
if (opts.dryRun) return { written: ['[dry-run] AGENTS.md'], skipped: [] };
|
|
294
316
|
fs.writeFileSync(file, lines.join('\n'), 'utf-8');
|
|
@@ -13,7 +13,7 @@ const { walkAgents, walkWorkflows, walkSkills } = require('./walk.js');
|
|
|
13
13
|
function checkAgent(file) {
|
|
14
14
|
const content = fs.readFileSync(file, 'utf-8');
|
|
15
15
|
const errs = [];
|
|
16
|
-
if (!/^code:\s+wize
|
|
16
|
+
if (!/^code:\s+wize(-|$)/m.test(content)) errs.push('missing or invalid "code:" prefix');
|
|
17
17
|
if (!/^name:/m.test(content)) errs.push('missing "name:"');
|
|
18
18
|
if (!/^title:/m.test(content)) errs.push('missing "title:"');
|
|
19
19
|
if (!/^description:/m.test(content)) errs.push('missing "description:"');
|
|
@@ -24,9 +24,10 @@ function checkWorkflow(file) {
|
|
|
24
24
|
const content = fs.readFileSync(file, 'utf-8');
|
|
25
25
|
const errs = [];
|
|
26
26
|
if (!/^---\s*$/m.test(content)) errs.push('missing YAML frontmatter');
|
|
27
|
-
if (!/^code:\s+wize
|
|
27
|
+
if (!/^code:\s+wize(-|$)/m.test(content)) errs.push('missing or invalid "code:" prefix');
|
|
28
28
|
if (!/^name:/m.test(content)) errs.push('missing "name:"');
|
|
29
29
|
if (!/^owner:/m.test(content) && !/^module:/m.test(content)) errs.push('missing "owner:" or "module:"');
|
|
30
|
+
if (!/^description:/m.test(content)) errs.push('missing "description:"');
|
|
30
31
|
return errs;
|
|
31
32
|
}
|
|
32
33
|
|