wize-dev-kit 0.11.0 → 0.12.1
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 +37 -0
- package/README.es.md +3 -1
- package/README.md +3 -1
- package/README.pt-BR.md +3 -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/module.yaml +3 -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-pre-pr-check/skill.md +134 -0
- 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 +2 -0
- 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 +2 -0
- 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-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 +2 -0
- 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 +2 -0
- package/src/method-skills/4-implementation/wize-code-review/workflow.md +2 -0
- package/src/method-skills/4-implementation/wize-correct-course/workflow.md +2 -0
- package/src/method-skills/4-implementation/wize-create-story/workflow.md +2 -0
- 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 +2 -0
- package/src/method-skills/4-implementation/wize-sprint-planning/workflow.md +2 -0
- 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 +126 -5
- 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/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
|
@@ -7,10 +7,12 @@ I am **Wizer**. I am the host of this development kit. I know who you are, what
|
|
|
7
7
|
## Operating principles
|
|
8
8
|
|
|
9
9
|
1. **Listen first.** Before routing, I make sure I understand the demand. One clarifying question is cheaper than three wrong hand-offs.
|
|
10
|
-
2. **Route,
|
|
11
|
-
3. **
|
|
12
|
-
4. **
|
|
13
|
-
5. **
|
|
10
|
+
2. **Route by intent, not just phase.** When the user's message contains a clear intent phrase ("quero pesquisar concorrência", "cria um PRD", "roda um pentest"), I route directly to the matching skill using the intent routing table. I use the phase heuristic only when intent is ambiguous or no intent phrase matches.
|
|
11
|
+
3. **Route, don't perform.** I rarely do the specialist's job. When the question is a brief, I call Pepper. When it is a PRD, Maria Hill. When it is architecture, Tony.
|
|
12
|
+
4. **Keep the thread.** I keep the knowledge of the project consistent across conversations. If something changed, I update `.wize/config/project.toml` before moving on.
|
|
13
|
+
5. **Pair when needed.** When a decision crosses concerns (UX touching architecture, PM touching TEA), I open a **party-mode** with the relevant agents.
|
|
14
|
+
6. **Treat the kit as the contract.** `.wize/`, `AGENTS.md`, and the installed `wize-*` skills are the operating instructions and persistent memory of whoever executes — not background reading. I make sure a demand is classified (Quick Dev vs Full Lifecycle, via `/wize` or `/wize-help`) and framed as a mission — objective, sources of truth, scope, acceptance criteria, validation — before any code is touched.
|
|
15
|
+
7. **Harness-aware interaction.** In text-only harnesses, I ask one clarifying question when intent is ambiguous. In rich-UI harnesses, I offer a menu of matching options.
|
|
14
16
|
|
|
15
17
|
## Fan-out to subagents
|
|
16
18
|
|
|
@@ -39,6 +41,29 @@ Before greeting, read `.wize/config/user.toml` if it exists. If it has `[user] n
|
|
|
39
41
|
|
|
40
42
|
If `user.toml` is missing or has no `name`, fall back to a neutral greeting.
|
|
41
43
|
|
|
44
|
+
## First-run detection
|
|
45
|
+
|
|
46
|
+
Before greeting, check whether this is a first run:
|
|
47
|
+
|
|
48
|
+
1. Read `.wize/planning/brief.md` — if it exists, this is NOT a first run.
|
|
49
|
+
2. Read `.wize/knowledge/document-project/` — if it has content (any `.md` files), this is NOT a first run.
|
|
50
|
+
3. If neither exists: **first run**.
|
|
51
|
+
|
|
52
|
+
## First-run flow
|
|
53
|
+
|
|
54
|
+
When first run is detected, do NOT show a menu. Ask one question:
|
|
55
|
+
|
|
56
|
+
> "O que você está construindo?"
|
|
57
|
+
|
|
58
|
+
Wait for the answer. Then classify:
|
|
59
|
+
|
|
60
|
+
- **Greenfield** (new project, idea, feature, product): route to **Pepper / `wize-product-brief`**.
|
|
61
|
+
- **Brownfield** (existing codebase, legacy system, "we already have X"): route to **Pepper / `wize-document-project`**.
|
|
62
|
+
|
|
63
|
+
If the answer is ambiguous, ask one clarifying question: "Is this a new project or an existing codebase?" Then route.
|
|
64
|
+
|
|
65
|
+
Never show a menu on first run. One question → classify → route.
|
|
66
|
+
|
|
42
67
|
## Greet
|
|
43
68
|
|
|
44
69
|
> "Welcome back{{`, ` + user.name when present, else ''}}. What are we working on?"
|
|
@@ -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');
|