wize-dev-kit 0.1.1 → 0.1.3
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/CHANGELOG.md +45 -1
- package/adapters/antigravity/adapter.yaml +3 -3
- package/adapters/antigravity/render.js +8 -7
- package/adapters/claude-code/adapter.yaml +1 -1
- package/adapters/claude-code/render.js +7 -7
- package/adapters/codex/adapter.yaml +3 -3
- package/adapters/codex/render.js +6 -7
- package/adapters/continue/adapter.yaml +4 -4
- package/adapters/continue/render.js +34 -6
- package/adapters/cursor/adapter.yaml +2 -2
- package/adapters/cursor/render.js +40 -6
- package/adapters/generic/adapter.yaml +2 -2
- package/adapters/generic/render.js +26 -6
- package/adapters/kimi-code/adapter.yaml +3 -3
- package/adapters/kimi-code/render.js +8 -7
- package/adapters/opencode/adapter.yaml +3 -3
- package/adapters/opencode/render.js +61 -6
- package/adapters/windsurf/adapter.yaml +2 -2
- package/adapters/windsurf/render.js +31 -6
- package/package.json +5 -2
- package/src/core-skills/module.yaml +0 -3
- package/src/orchestrator-skills/module.yaml +3 -0
- package/src/orchestrator-skills/wize-help/skill.md +102 -0
- package/tools/installer/render-shared.js +187 -0
- package/tools/installer/wize-cli.js +95 -6
- package/src/core-skills/wize-help/skill.md +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,48 @@ Format inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.1.3] — 2026-06-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **All 9 IDE adapters now ship real renderers** (previously 8 of them were stub printers). Each adapter writes files at the canonical path each harness expects:
|
|
13
|
+
- **Claude Code** → `.claude/skills/wize-{code}/SKILL.md`
|
|
14
|
+
- **Antigravity (Google)** → `.agent/skills/wize-{code}/SKILL.md` (singular `.agent`; `.antigravitycli/` is the CLI's own state and is left untouched)
|
|
15
|
+
- **OpenAI Codex CLI** → `.agents/skills/wize-{code}/SKILL.md`
|
|
16
|
+
- **Moonshot Kimi Code** → `.kimi/skills/wize-{code}/SKILL.md`
|
|
17
|
+
- **Cursor** → `.cursor/rules/wize-{code}.mdc` (with `description`, `globs`, `alwaysApply` frontmatter)
|
|
18
|
+
- **Windsurf (Codeium)** → `.windsurf/rules/wize-{code}.md`
|
|
19
|
+
- **Continue.dev** → `.continue/prompts/wize-{code}.prompt` (`invokable: true` → slash command)
|
|
20
|
+
- **OpenCode (sst/opencode)** → `.opencode/agents/wize-{code}.md` (personas, with `mode: primary` for Wizer and `mode: subagent` for the others) + `.opencode/commands/wize-{code}.md` (workflows/skills as slash commands)
|
|
21
|
+
- **Generic fallback** → `.wize/agents/wize-{code}.md` + a root `AGENTS.md` baseline (read by Codex, Cursor, Windsurf and Antigravity even without their dedicated adapter)
|
|
22
|
+
- New shared module `tools/installer/render-shared.js` centralizes kit traversal, asset parsing, and the Anthropic-style SKILL.md emitter reused by Claude Code, Antigravity, Codex and Kimi adapters.
|
|
23
|
+
- Adapter test suite expanded from 12 → 25 passing tests, with one sanity test per adapter plus format-specific checks (Cursor frontmatter shape, Continue `invokable: true`, OpenCode primary/subagent mode split, generic AGENTS.md emission).
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Antigravity adapter previously created/touched nothing useful. It now emits skills at the correct location (`.agent/skills/`) and explicitly avoids `.antigravitycli/` (CLI state, owned by the Antigravity CLI itself).
|
|
28
|
+
|
|
29
|
+
### Notes
|
|
30
|
+
|
|
31
|
+
- Several harnesses inject custom skills at session startup; you may need to restart the IDE after `wize-dev-kit install` for slash commands to appear.
|
|
32
|
+
|
|
33
|
+
## [0.1.2] — 2026-05-31
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **Claude Code adapter is now functional.** `npx wize-dev-kit install` (when `claude-code` is selected as IDE target) generates `.claude/skills/wize-*/SKILL.md` for every agent, workflow and skill in the kit. Each SKILL.md uses the Claude Code skill frontmatter so the slash menu picks up `/wize-orchestrator`, `/wize-product-brief`, `/wize-create-prd`, `/wize-tea-gate`, etc.
|
|
38
|
+
- **`wize-help` reworked as an orchestrator-aware skill.** Moved from `core-skills/` to `orchestrator-skills/`. The skill instructs Wizer to read `.wize/config/project.toml` plus the state of `.wize/planning/`, `.wize/solutioning/`, `.wize/implementation/` and reply with a phase-aware "what to do next" answer. Four modes: default, `next`, `status`, `personas`.
|
|
39
|
+
- **Interactive prompts in the installer.** Profile and IDE-target selection now use arrow-keys + space (checkbox) via the `prompts` dependency. Non-TTY environments fall back to the previous number-based prompt automatically (preserves CI scriptability).
|
|
40
|
+
- Adapter test suite (`test/adapter-claude-code.test.js`) covering: per-agent SKILL.md emission, overlay-gate behavior (core-only vs full), `wize-help` content checks, dry-run safety.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Profile gating now lives inside the Claude Code adapter: overlay workflows (`wize-web-*`, `wize-app-*`) are only emitted when the corresponding overlay is in the selected profiles.
|
|
45
|
+
|
|
46
|
+
### Dependencies
|
|
47
|
+
|
|
48
|
+
- Added `prompts@^2.4.2` (1 sub-dependency, ~5 KB). Only used by the interactive installer; not bundled into the kit's runtime artifacts.
|
|
49
|
+
|
|
8
50
|
## [0.1.1] — 2026-05-31
|
|
9
51
|
|
|
10
52
|
### Changed
|
|
@@ -41,6 +83,8 @@ Format inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
41
83
|
- Inspired by [BMAD Method v6.8.0](https://github.com/bmad-code-org/BMAD-METHOD).
|
|
42
84
|
- WDS module inspired by [bmad-method-wds-expansion](https://github.com/bmad-code-org/bmad-method-wds-expansion).
|
|
43
85
|
|
|
44
|
-
[Unreleased]: https://github.com/qwize-br/wize-development-kit/compare/v0.1.
|
|
86
|
+
[Unreleased]: https://github.com/qwize-br/wize-development-kit/compare/v0.1.3...HEAD
|
|
87
|
+
[0.1.3]: https://github.com/qwize-br/wize-development-kit/compare/v0.1.2...v0.1.3
|
|
88
|
+
[0.1.2]: https://github.com/qwize-br/wize-development-kit/compare/v0.1.1...v0.1.2
|
|
45
89
|
[0.1.1]: https://github.com/qwize-br/wize-development-kit/compare/v0.1.0...v0.1.1
|
|
46
90
|
[0.1.0]: https://github.com/qwize-br/wize-development-kit/releases/tag/v0.1.0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
code: antigravity
|
|
2
|
-
description: "Antigravity
|
|
3
|
-
target_path: ".
|
|
4
|
-
file_pattern: "wize
|
|
2
|
+
description: "Google Antigravity skills (.agent/skills, NOT .antigravitycli which is CLI state)."
|
|
3
|
+
target_path: ".agent/skills/"
|
|
4
|
+
file_pattern: "wize-*/SKILL.md"
|
|
5
5
|
file_format: markdown
|
|
6
6
|
hooks:
|
|
7
7
|
pre_render: []
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
// Google Antigravity adapter — emits .agent/skills/wize-{code}/SKILL.md per asset.
|
|
2
|
+
// Note: directory is SINGULAR `.agent`. The `.antigravitycli/` directory is
|
|
3
|
+
// Antigravity's own CLI state and must not be touched. Antigravity also reads
|
|
4
|
+
// the root AGENTS.md.
|
|
5
|
+
|
|
2
6
|
'use strict';
|
|
3
7
|
|
|
4
|
-
const fs = require('node:fs');
|
|
5
8
|
const path = require('node:path');
|
|
9
|
+
const { renderAnthropicSkills } = require('../../tools/installer/render-shared.js');
|
|
6
10
|
|
|
7
11
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const targetPath = (cfg.match(/^target_path:\s*"(.+)"$/m) || [])[1] || '';
|
|
11
|
-
const filePattern = (cfg.match(/^file_pattern:\s*"(.+)"$/m) || [])[1] || '';
|
|
12
|
-
console.log(`[adapter:${path.basename(adapterDir)}] would emit ${filePattern} under ${path.join(projectRoot, targetPath)}`);
|
|
12
|
+
const targetDir = path.join(projectRoot, '.agent', 'skills');
|
|
13
|
+
return renderAnthropicSkills(kitRoot, targetDir, opts);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
module.exports = { render };
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
// Claude Code adapter — emits .claude/skills/wize-{code}/SKILL.md per asset.
|
|
2
|
+
// Skill format is identical to the upstream Anthropic spec, so we delegate
|
|
3
|
+
// to the shared renderer.
|
|
4
|
+
|
|
2
5
|
'use strict';
|
|
3
6
|
|
|
4
|
-
const fs = require('node:fs');
|
|
5
7
|
const path = require('node:path');
|
|
8
|
+
const { renderAnthropicSkills } = require('../../tools/installer/render-shared.js');
|
|
6
9
|
|
|
7
10
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const targetPath = (cfg.match(/^target_path:\s*"(.+)"$/m) || [])[1] || '';
|
|
11
|
-
const filePattern = (cfg.match(/^file_pattern:\s*"(.+)"$/m) || [])[1] || '';
|
|
12
|
-
console.log(`[adapter:${path.basename(adapterDir)}] would emit ${filePattern} under ${path.join(projectRoot, targetPath)}`);
|
|
11
|
+
const targetDir = path.join(projectRoot, '.claude', 'skills');
|
|
12
|
+
return renderAnthropicSkills(kitRoot, targetDir, opts);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
module.exports = { render };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
code: codex
|
|
2
|
-
description: "OpenAI Codex CLI agents."
|
|
3
|
-
target_path: ".
|
|
4
|
-
file_pattern: "wize
|
|
2
|
+
description: "OpenAI Codex CLI agent skills (.agents/skills/, Anthropic-compatible)."
|
|
3
|
+
target_path: ".agents/skills/"
|
|
4
|
+
file_pattern: "wize-*/SKILL.md"
|
|
5
5
|
file_format: markdown
|
|
6
6
|
hooks:
|
|
7
7
|
pre_render: []
|
package/adapters/codex/render.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
// OpenAI Codex CLI adapter — emits .agents/skills/wize-{code}/SKILL.md per asset.
|
|
2
|
+
// Codex shares the Anthropic SKILL.md format and also reads the root AGENTS.md.
|
|
3
|
+
|
|
2
4
|
'use strict';
|
|
3
5
|
|
|
4
|
-
const fs = require('node:fs');
|
|
5
6
|
const path = require('node:path');
|
|
7
|
+
const { renderAnthropicSkills } = require('../../tools/installer/render-shared.js');
|
|
6
8
|
|
|
7
9
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const targetPath = (cfg.match(/^target_path:\s*"(.+)"$/m) || [])[1] || '';
|
|
11
|
-
const filePattern = (cfg.match(/^file_pattern:\s*"(.+)"$/m) || [])[1] || '';
|
|
12
|
-
console.log(`[adapter:${path.basename(adapterDir)}] would emit ${filePattern} under ${path.join(projectRoot, targetPath)}`);
|
|
10
|
+
const targetDir = path.join(projectRoot, '.agents', 'skills');
|
|
11
|
+
return renderAnthropicSkills(kitRoot, targetDir, opts);
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
module.exports = { render };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
code: continue
|
|
2
|
-
description: "Continue
|
|
3
|
-
target_path: ".continue/
|
|
4
|
-
file_pattern: "wize-*.
|
|
5
|
-
file_format:
|
|
2
|
+
description: "Continue.dev slash commands (.continue/prompts/, .prompt files)."
|
|
3
|
+
target_path: ".continue/prompts/"
|
|
4
|
+
file_pattern: "wize-*.prompt"
|
|
5
|
+
file_format: prompt
|
|
6
6
|
hooks:
|
|
7
7
|
pre_render: []
|
|
8
8
|
post_render: []
|
|
@@ -1,15 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
// Continue.dev adapter — emits .continue/prompts/wize-{code}.prompt per asset.
|
|
2
|
+
// Prompt files are markdown with YAML frontmatter: name, description, invokable.
|
|
3
|
+
// `invokable: true` registers the prompt as a slash command.
|
|
4
|
+
// Reference: https://docs.continue.dev/customize/deep-dives/prompts
|
|
5
|
+
|
|
2
6
|
'use strict';
|
|
3
7
|
|
|
4
8
|
const fs = require('node:fs');
|
|
5
9
|
const path = require('node:path');
|
|
10
|
+
const { collectAssets, escapeYamlDouble, clipOneLine, ensureDir } = require('../../tools/installer/render-shared.js');
|
|
6
11
|
|
|
7
12
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const targetDir = path.join(projectRoot, '.continue', 'prompts');
|
|
14
|
+
const assets = collectAssets(kitRoot, opts);
|
|
15
|
+
const written = [];
|
|
16
|
+
if (!opts.dryRun) ensureDir(targetDir);
|
|
17
|
+
|
|
18
|
+
for (const a of assets) {
|
|
19
|
+
const desc = clipOneLine(a.description, 180);
|
|
20
|
+
const content = [
|
|
21
|
+
'---',
|
|
22
|
+
`name: ${a.code}`,
|
|
23
|
+
`description: "${escapeYamlDouble(desc)}"`,
|
|
24
|
+
`invokable: true`,
|
|
25
|
+
'---',
|
|
26
|
+
'',
|
|
27
|
+
a.kind === 'agent' ? `# ${a.name} — ${a.title}` : `# ${a.name}`,
|
|
28
|
+
'',
|
|
29
|
+
a.body.trim(),
|
|
30
|
+
''
|
|
31
|
+
].join('\n');
|
|
32
|
+
|
|
33
|
+
if (opts.dryRun) {
|
|
34
|
+
written.push(`[dry-run] ${a.code}.prompt`);
|
|
35
|
+
} else {
|
|
36
|
+
fs.writeFileSync(path.join(targetDir, `${a.code}.prompt`), content, 'utf-8');
|
|
37
|
+
written.push(`${a.code}.prompt`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { written, skipped: [] };
|
|
13
41
|
}
|
|
14
42
|
|
|
15
43
|
module.exports = { render };
|
|
@@ -1,15 +1,49 @@
|
|
|
1
|
-
|
|
1
|
+
// Cursor adapter — emits .cursor/rules/wize-{code}.mdc per asset.
|
|
2
|
+
// Cursor rule frontmatter keys: description (required), globs (comma list),
|
|
3
|
+
// alwaysApply (boolean). Mode is inferred from combinations.
|
|
4
|
+
// Reference: https://cursor.com/docs/context/rules
|
|
5
|
+
|
|
2
6
|
'use strict';
|
|
3
7
|
|
|
4
8
|
const fs = require('node:fs');
|
|
5
9
|
const path = require('node:path');
|
|
10
|
+
const { collectAssets, escapeYamlDouble, clipOneLine, ensureDir } = require('../../tools/installer/render-shared.js');
|
|
6
11
|
|
|
7
12
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const targetDir = path.join(projectRoot, '.cursor', 'rules');
|
|
14
|
+
const assets = collectAssets(kitRoot, opts);
|
|
15
|
+
const written = [];
|
|
16
|
+
if (!opts.dryRun) ensureDir(targetDir);
|
|
17
|
+
|
|
18
|
+
for (const a of assets) {
|
|
19
|
+
const desc = clipOneLine(
|
|
20
|
+
a.kind === 'agent'
|
|
21
|
+
? `${a.name} (${a.title}) — ${a.description}`
|
|
22
|
+
: a.description
|
|
23
|
+
);
|
|
24
|
+
// Agents become alwaysApply=false rules attached on demand;
|
|
25
|
+
// workflows/skills are Agent-attachable via description.
|
|
26
|
+
const content = [
|
|
27
|
+
'---',
|
|
28
|
+
`description: "${escapeYamlDouble(desc)}"`,
|
|
29
|
+
`globs:`,
|
|
30
|
+
`alwaysApply: false`,
|
|
31
|
+
'---',
|
|
32
|
+
'',
|
|
33
|
+
a.kind === 'agent' ? `# ${a.name} — ${a.title}` : `# ${a.name}`,
|
|
34
|
+
'',
|
|
35
|
+
a.body.trim(),
|
|
36
|
+
''
|
|
37
|
+
].join('\n');
|
|
38
|
+
|
|
39
|
+
if (opts.dryRun) {
|
|
40
|
+
written.push(`[dry-run] ${a.code}.mdc`);
|
|
41
|
+
} else {
|
|
42
|
+
fs.writeFileSync(path.join(targetDir, `${a.code}.mdc`), content, 'utf-8');
|
|
43
|
+
written.push(`${a.code}.mdc`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { written, skipped: [] };
|
|
13
47
|
}
|
|
14
48
|
|
|
15
49
|
module.exports = { render };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
code: generic
|
|
2
|
-
description: "Generic markdown fallback
|
|
2
|
+
description: "Generic markdown fallback (.wize/agents/) + AGENTS.md baseline."
|
|
3
3
|
target_path: ".wize/agents/"
|
|
4
|
-
file_pattern: "wize-*.md"
|
|
4
|
+
file_pattern: "wize-*.md + AGENTS.md"
|
|
5
5
|
file_format: markdown
|
|
6
6
|
hooks:
|
|
7
7
|
pre_render: []
|
|
@@ -1,15 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
// Generic fallback adapter — emits plain markdown under .wize/agents/ and
|
|
2
|
+
// writes a root AGENTS.md as a baseline pointer (read by Codex, Cursor,
|
|
3
|
+
// Windsurf and Antigravity even without their dedicated adapter installed).
|
|
4
|
+
|
|
2
5
|
'use strict';
|
|
3
6
|
|
|
4
7
|
const fs = require('node:fs');
|
|
5
8
|
const path = require('node:path');
|
|
9
|
+
const { collectAssets, renderAgentsMd, clipOneLine, ensureDir } = require('../../tools/installer/render-shared.js');
|
|
6
10
|
|
|
7
11
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
const targetDir = path.join(projectRoot, '.wize', 'agents');
|
|
13
|
+
const assets = collectAssets(kitRoot, opts);
|
|
14
|
+
const written = [];
|
|
15
|
+
if (!opts.dryRun) ensureDir(targetDir);
|
|
16
|
+
|
|
17
|
+
for (const a of assets) {
|
|
18
|
+
const summary = clipOneLine(a.description, 180);
|
|
19
|
+
const header = a.kind === 'agent' ? `# ${a.name} — ${a.title}` : `# ${a.name}`;
|
|
20
|
+
const content = [header, '', `> ${summary}`, '', a.body.trim(), ''].join('\n');
|
|
21
|
+
if (opts.dryRun) {
|
|
22
|
+
written.push(`[dry-run] ${a.code}.md`);
|
|
23
|
+
} else {
|
|
24
|
+
fs.writeFileSync(path.join(targetDir, `${a.code}.md`), content, 'utf-8');
|
|
25
|
+
written.push(`${a.code}.md`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Baseline AGENTS.md (not overwritten if user already has one).
|
|
30
|
+
const agentsMd = renderAgentsMd(kitRoot, projectRoot, opts);
|
|
31
|
+
written.push(...agentsMd.written);
|
|
32
|
+
return { written, skipped: agentsMd.skipped };
|
|
13
33
|
}
|
|
14
34
|
|
|
15
35
|
module.exports = { render };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
code: kimi-code
|
|
2
|
-
description: "Moonshot Kimi Code
|
|
3
|
-
target_path: ".kimi/
|
|
4
|
-
file_pattern: "wize
|
|
2
|
+
description: "Moonshot Kimi Code skills (.kimi/skills/, Anthropic-compatible)."
|
|
3
|
+
target_path: ".kimi/skills/"
|
|
4
|
+
file_pattern: "wize-*/SKILL.md"
|
|
5
5
|
file_format: markdown
|
|
6
6
|
hooks:
|
|
7
7
|
pre_render: []
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
// Moonshot Kimi Code adapter — emits .kimi/skills/wize-{code}/SKILL.md per asset.
|
|
2
|
+
// Kimi Code consumes the Anthropic SKILL.md format verbatim and additionally
|
|
3
|
+
// auto-detects .claude/skills/ and .codex/skills/, so installing alongside
|
|
4
|
+
// those adapters is harmless.
|
|
5
|
+
|
|
2
6
|
'use strict';
|
|
3
7
|
|
|
4
|
-
const fs = require('node:fs');
|
|
5
8
|
const path = require('node:path');
|
|
9
|
+
const { renderAnthropicSkills } = require('../../tools/installer/render-shared.js');
|
|
6
10
|
|
|
7
11
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const targetPath = (cfg.match(/^target_path:\s*"(.+)"$/m) || [])[1] || '';
|
|
11
|
-
const filePattern = (cfg.match(/^file_pattern:\s*"(.+)"$/m) || [])[1] || '';
|
|
12
|
-
console.log(`[adapter:${path.basename(adapterDir)}] would emit ${filePattern} under ${path.join(projectRoot, targetPath)}`);
|
|
12
|
+
const targetDir = path.join(projectRoot, '.kimi', 'skills');
|
|
13
|
+
return renderAnthropicSkills(kitRoot, targetDir, opts);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
module.exports = { render };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
code: opencode
|
|
2
|
-
description: "OpenCode
|
|
3
|
-
target_path: ".opencode/
|
|
4
|
-
file_pattern: "wize-*.md"
|
|
2
|
+
description: "OpenCode agents + commands (.opencode/agents/ and .opencode/commands/)."
|
|
3
|
+
target_path: ".opencode/"
|
|
4
|
+
file_pattern: "agents/wize-*.md, commands/wize-*.md"
|
|
5
5
|
file_format: markdown
|
|
6
6
|
hooks:
|
|
7
7
|
pre_render: []
|
|
@@ -1,15 +1,70 @@
|
|
|
1
|
-
|
|
1
|
+
// OpenCode (sst/opencode) adapter — emits two trees:
|
|
2
|
+
// - .opencode/agents/wize-{code}.md (personas → system prompts)
|
|
3
|
+
// - .opencode/commands/wize-{code}.md (workflows/skills → slash commands)
|
|
4
|
+
// Frontmatter (agents): description, mode (primary|subagent|all)
|
|
5
|
+
// Frontmatter (commands): description, template (the prompt body)
|
|
6
|
+
// Reference: https://opencode.ai/docs/agents/ + /docs/commands/
|
|
7
|
+
|
|
2
8
|
'use strict';
|
|
3
9
|
|
|
4
10
|
const fs = require('node:fs');
|
|
5
11
|
const path = require('node:path');
|
|
12
|
+
const { collectAssets, escapeYamlDouble, clipOneLine, ensureDir } = require('../../tools/installer/render-shared.js');
|
|
6
13
|
|
|
7
14
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
15
|
+
const agentsDir = path.join(projectRoot, '.opencode', 'agents');
|
|
16
|
+
const commandsDir = path.join(projectRoot, '.opencode', 'commands');
|
|
17
|
+
const assets = collectAssets(kitRoot, opts);
|
|
18
|
+
const written = [];
|
|
19
|
+
|
|
20
|
+
if (!opts.dryRun) {
|
|
21
|
+
ensureDir(agentsDir);
|
|
22
|
+
ensureDir(commandsDir);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const a of assets) {
|
|
26
|
+
if (a.kind === 'agent') {
|
|
27
|
+
const desc = clipOneLine(`${a.name} (${a.title}) — ${a.description}`);
|
|
28
|
+
const mode = a.code === 'wize-orchestrator' ? 'primary' : 'subagent';
|
|
29
|
+
const content = [
|
|
30
|
+
'---',
|
|
31
|
+
`description: "${escapeYamlDouble(desc)}"`,
|
|
32
|
+
`mode: ${mode}`,
|
|
33
|
+
'---',
|
|
34
|
+
'',
|
|
35
|
+
`# ${a.name} — ${a.title}`,
|
|
36
|
+
'',
|
|
37
|
+
a.body.trim(),
|
|
38
|
+
''
|
|
39
|
+
].join('\n');
|
|
40
|
+
if (opts.dryRun) {
|
|
41
|
+
written.push(`[dry-run] agents/${a.code}.md`);
|
|
42
|
+
} else {
|
|
43
|
+
fs.writeFileSync(path.join(agentsDir, `${a.code}.md`), content, 'utf-8');
|
|
44
|
+
written.push(`agents/${a.code}.md`);
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
// workflow or skill → command
|
|
48
|
+
const desc = clipOneLine(a.description, 180);
|
|
49
|
+
const content = [
|
|
50
|
+
'---',
|
|
51
|
+
`description: "${escapeYamlDouble(desc)}"`,
|
|
52
|
+
'---',
|
|
53
|
+
'',
|
|
54
|
+
`# ${a.name}`,
|
|
55
|
+
'',
|
|
56
|
+
a.body.trim(),
|
|
57
|
+
''
|
|
58
|
+
].join('\n');
|
|
59
|
+
if (opts.dryRun) {
|
|
60
|
+
written.push(`[dry-run] commands/${a.code}.md`);
|
|
61
|
+
} else {
|
|
62
|
+
fs.writeFileSync(path.join(commandsDir, `${a.code}.md`), content, 'utf-8');
|
|
63
|
+
written.push(`commands/${a.code}.md`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { written, skipped: [] };
|
|
13
68
|
}
|
|
14
69
|
|
|
15
70
|
module.exports = { render };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
code: windsurf
|
|
2
|
-
description: "Windsurf Cascade
|
|
3
|
-
target_path: ".windsurf/
|
|
2
|
+
description: "Windsurf Cascade rules (.windsurf/rules/, plain markdown)."
|
|
3
|
+
target_path: ".windsurf/rules/"
|
|
4
4
|
file_pattern: "wize-*.md"
|
|
5
5
|
file_format: markdown
|
|
6
6
|
hooks:
|
|
@@ -1,15 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
// Windsurf (Codeium) adapter — emits .windsurf/rules/wize-{code}.md per asset.
|
|
2
|
+
// Windsurf rules are plain markdown; activation mode is configured inside the
|
|
3
|
+
// IDE Rules panel, not in frontmatter. Cascade picks them up on session start.
|
|
4
|
+
// Reference: https://docs.windsurf.com/windsurf/cascade/memories#rules
|
|
5
|
+
|
|
2
6
|
'use strict';
|
|
3
7
|
|
|
4
8
|
const fs = require('node:fs');
|
|
5
9
|
const path = require('node:path');
|
|
10
|
+
const { collectAssets, clipOneLine, ensureDir } = require('../../tools/installer/render-shared.js');
|
|
6
11
|
|
|
7
12
|
function render(kitRoot, projectRoot, opts = {}) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const targetDir = path.join(projectRoot, '.windsurf', 'rules');
|
|
14
|
+
const assets = collectAssets(kitRoot, opts);
|
|
15
|
+
const written = [];
|
|
16
|
+
if (!opts.dryRun) ensureDir(targetDir);
|
|
17
|
+
|
|
18
|
+
for (const a of assets) {
|
|
19
|
+
const summary = clipOneLine(a.description, 180);
|
|
20
|
+
const header = a.kind === 'agent' ? `# ${a.name} — ${a.title}` : `# ${a.name}`;
|
|
21
|
+
const content = [
|
|
22
|
+
header,
|
|
23
|
+
'',
|
|
24
|
+
`> ${summary}`,
|
|
25
|
+
'',
|
|
26
|
+
a.body.trim(),
|
|
27
|
+
''
|
|
28
|
+
].join('\n');
|
|
29
|
+
|
|
30
|
+
if (opts.dryRun) {
|
|
31
|
+
written.push(`[dry-run] ${a.code}.md`);
|
|
32
|
+
} else {
|
|
33
|
+
fs.writeFileSync(path.join(targetDir, `${a.code}.md`), content, 'utf-8');
|
|
34
|
+
written.push(`${a.code}.md`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { written, skipped: [] };
|
|
13
38
|
}
|
|
14
39
|
|
|
15
40
|
module.exports = { render };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "wize-dev-kit",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"description": "Full-lifecycle AI-assisted development kit with Test Architect and Whiteport Design Studio embedded. Inspired by BMAD Method and WDS.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ai",
|
|
@@ -48,5 +48,8 @@
|
|
|
48
48
|
"ARCH.md",
|
|
49
49
|
"ROSTER.md",
|
|
50
50
|
"DECISIONS.md"
|
|
51
|
-
]
|
|
51
|
+
],
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"prompts": "^2.4.2"
|
|
54
|
+
}
|
|
52
55
|
}
|
|
@@ -16,9 +16,6 @@ skills:
|
|
|
16
16
|
- code: wize-brainstorming
|
|
17
17
|
name: "Brainstorming"
|
|
18
18
|
description: "Divergent-then-convergent ideation; produces idea-pool.md."
|
|
19
|
-
- code: wize-help
|
|
20
|
-
name: "Help"
|
|
21
|
-
description: "Routes user to the right agent/workflow based on intent."
|
|
22
19
|
- code: wize-shard-doc
|
|
23
20
|
name: "Shard Doc"
|
|
24
21
|
description: "Splits large markdown docs (PRD, architecture) into addressable shards."
|
|
@@ -13,6 +13,9 @@ agents:
|
|
|
13
13
|
description: "Knows the user deeply, parses raw demand, routes to the right specialist. Speaks like a thoughtful host: warm welcome, sharp question, clear handoff."
|
|
14
14
|
|
|
15
15
|
skills:
|
|
16
|
+
- code: wize-help
|
|
17
|
+
name: "Help (Wizer)"
|
|
18
|
+
description: "Project-state-aware help. Reads .wize/ and tells the user what to do next. Modes: default, next, status, personas."
|
|
16
19
|
- code: wize-onboarding
|
|
17
20
|
name: "Onboarding"
|
|
18
21
|
description: "Post-install triage: greenfield vs brownfield, profile, objective. Delegates to specialists."
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
code: wize-help
|
|
3
|
+
name: Help (Wizer)
|
|
4
|
+
module: orchestrator
|
|
5
|
+
owner: wize-orchestrator
|
|
6
|
+
status: ready
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Wizer · `/wize-help`
|
|
10
|
+
|
|
11
|
+
You are **Wizer**, the orchestrator. The user invoked `/wize-help`. Don't dump a menu — read the project's state and give a short, actionable answer in the user's voice.
|
|
12
|
+
|
|
13
|
+
## Modes
|
|
14
|
+
|
|
15
|
+
| Invocation | What to do |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `/wize-help` or `/wize` (no argument) | Greeting + project snapshot + the **single best next step**. |
|
|
18
|
+
| `/wize-help next` | Just the next step. Skip the snapshot. |
|
|
19
|
+
| `/wize-help status` | Snapshot only — phase, last TEA gate, in-flight stories. |
|
|
20
|
+
| `/wize-help personas` | List only the personas relevant to the active profiles. |
|
|
21
|
+
|
|
22
|
+
## Step 1 — read project state
|
|
23
|
+
|
|
24
|
+
Read these files if they exist (they may not — that's information too):
|
|
25
|
+
|
|
26
|
+
| Path | Tells you |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `.wize/config/project.toml` | Active profiles, IDE targets, communication & document languages, project name. |
|
|
29
|
+
| `.wize/config/tea.toml` | TEA gate policy (advisory vs enforcing). |
|
|
30
|
+
| `.wize/planning/brief.md` | Whether Phase 1 (Pepper) started. |
|
|
31
|
+
| `.wize/planning/research.md` | Whether research was done. |
|
|
32
|
+
| `.wize/planning/ux/trigger-map.md` | Whether WDS trigger map exists. |
|
|
33
|
+
| `.wize/planning/prd.md` | Whether Phase 2 (Maria Hill) PRD exists. |
|
|
34
|
+
| `.wize/planning/ux/ux-scenarios.md`, `.wize/planning/ux/ux-design/**` | Whether Mantis ran UX. |
|
|
35
|
+
| `.wize/planning/tech-vision.md`, `nfr-principles.md` | Whether Fury set tech strategy. |
|
|
36
|
+
| `.wize/solutioning/architecture.md` | Whether Tony's architecture exists. |
|
|
37
|
+
| `.wize/solutioning/stories/**/*.md` | Whether stories are sliced. |
|
|
38
|
+
| `.wize/implementation/tea/risk-profile.md` | Whether Hawkeye's risk profile is in place. |
|
|
39
|
+
| `.wize/implementation/tea/**/gate.md` | Last gate decisions per story. |
|
|
40
|
+
| `.wize/implementation/sprint-status.md` | Active sprint state. |
|
|
41
|
+
|
|
42
|
+
## Step 2 — determine current phase
|
|
43
|
+
|
|
44
|
+
Apply this heuristic, top-down. Stop at the first match.
|
|
45
|
+
|
|
46
|
+
1. **No `.wize/` folder.** → Tell the user the kit isn't installed; suggest `npx wize-dev-kit install`.
|
|
47
|
+
2. **No `brief.md`.** → Phase 1. Next: **Pepper / `wize-product-brief`**.
|
|
48
|
+
3. **`brief.md` exists, no `trigger-map.md`.** → Still Phase 1. Next: **Pepper / `wize-trigger-map`**.
|
|
49
|
+
4. **No `prd.md`.** → Phase 2. Next: **Maria Hill / `wize-create-prd`**.
|
|
50
|
+
5. **`prd.md` exists, no `ux-scenarios.md`.** → Phase 2 UX. Next: **Mantis / `wize-ux-scenarios`**.
|
|
51
|
+
6. **`ux-design/` empty.** → Phase 2 UX continues. Next: **Mantis / `wize-ux-design`**.
|
|
52
|
+
7. **No `tech-vision.md` or `nfr-principles.md`.** → Phase 2→3 boundary. Next: **Fury / `wize-tech-vision`** then `wize-nfr-principles`.
|
|
53
|
+
8. **No `architecture.md`.** → Phase 3. Next: **Tony / `wize-create-architecture`**.
|
|
54
|
+
9. **No `stories/**/*.md`.** → Phase 3 still. Next: **Tony / `wize-create-epics-and-stories`**.
|
|
55
|
+
10. **No `tea/risk-profile.md`.** → Phase 3 closeout. Next: **Hawkeye / `wize-tea-risk`**.
|
|
56
|
+
11. **Has stories but `sprint-status.md` shows no active sprint.** → Phase 4 start. Next: **Maria Hill / `wize-sprint-planning`**.
|
|
57
|
+
12. **Has active sprint, oldest in-flight story has no `tea/.../design.md`.** → Next: **Hawkeye / `wize-tea-design`** for that story.
|
|
58
|
+
13. **In-flight story exists, no implementation commits.** → Next: **Shuri / `wize-dev-story`** on that story.
|
|
59
|
+
14. **In-flight story exists with code, no `gate.md`.** → Next: **Hawkeye / `wize-tea-trace` → `wize-tea-review` → `wize-tea-gate`** for that story.
|
|
60
|
+
15. **All stories gated.** → Next: **Wizer / `wize-retrospective`** + plan next epic.
|
|
61
|
+
|
|
62
|
+
For brownfield repos where `.wize/knowledge/document-project/` is missing, prepend: "Run `wize-document-project` first to baseline the codebase."
|
|
63
|
+
|
|
64
|
+
## Step 3 — respond
|
|
65
|
+
|
|
66
|
+
Default response shape (3 lines):
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Welcome back. {project name} — {profiles, e.g., "Core + Web"}.
|
|
70
|
+
You're at: {phase + last completed artifact}.
|
|
71
|
+
Next: /{next workflow} ({persona}).
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
For `status`, return a markdown table:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
| Item | State |
|
|
78
|
+
|---|---|
|
|
79
|
+
| Phase | … |
|
|
80
|
+
| Profiles | … |
|
|
81
|
+
| Last TEA gate | … (PASS/CONCERNS/FAIL/WAIVED) |
|
|
82
|
+
| In-flight stories | … |
|
|
83
|
+
| Active sprint | … |
|
|
84
|
+
| TEA policy | advisory / enforcing |
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For `personas`, list only personas whose role applies to active profiles. Always include Wizer, Pepper, Peggy, Maria Hill, Mantis, Fury, Tony, Hawkeye, Shuri (all are profile-independent core roles). If `web-overlay` active, note that **Mantis** has the WCAG/responsive playbook loaded and **Hawkeye** has Playwright/Vitest patterns. If `app-overlay` active, note HIG/Material 3 for Mantis and Detox/Maestro for Hawkeye.
|
|
88
|
+
|
|
89
|
+
## Step 4 — offer to act
|
|
90
|
+
|
|
91
|
+
End every response with one of:
|
|
92
|
+
|
|
93
|
+
- "Want me to call {persona}?" — if the next step is clear.
|
|
94
|
+
- "Want me to baseline the repo first?" — if brownfield + no document-project.
|
|
95
|
+
- "Want me to call a party-mode with {persona1} + {persona2}?" — if the next step has a cross-cutting decision.
|
|
96
|
+
|
|
97
|
+
## Style
|
|
98
|
+
|
|
99
|
+
- Speak in the user's `communication` language (from `.wize/config/project.toml`).
|
|
100
|
+
- One sharp question is better than three sentences of advice.
|
|
101
|
+
- If the user invoked `/wize-help next`, give them just the next step in one line and stop.
|
|
102
|
+
- If user invoked `/wize-help status`, give the table and stop. Don't suggest actions.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Shared helpers used by all IDE adapters: kit traversal, asset parsing,
|
|
2
|
+
// and renderers for the Anthropic-style "SKILL.md" format (Claude Code,
|
|
3
|
+
// Antigravity, OpenAI Codex, Kimi Code all use it verbatim).
|
|
4
|
+
//
|
|
5
|
+
// Adapters that need a different format (Cursor .mdc, Windsurf, Continue,
|
|
6
|
+
// OpenCode) consume the parsed assets via collectAssets() and then render
|
|
7
|
+
// per their own conventions.
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('node:fs');
|
|
12
|
+
const path = require('node:path');
|
|
13
|
+
const { walkAgents, walkWorkflows, walkSkills } = require('./validators/walk.js');
|
|
14
|
+
|
|
15
|
+
function readYamlField(content, field) {
|
|
16
|
+
const re = new RegExp('^' + field + ':\\s*(?:"([^"]*)"|\'([^\']*)\'|(.*?))\\s*$', 'm');
|
|
17
|
+
const m = content.match(re);
|
|
18
|
+
if (!m) return null;
|
|
19
|
+
return (m[1] || m[2] || m[3] || '').trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readFrontmatter(content) {
|
|
23
|
+
if (!content.startsWith('---')) return {};
|
|
24
|
+
const end = content.indexOf('\n---', 3);
|
|
25
|
+
if (end === -1) return {};
|
|
26
|
+
const lines = content.slice(3, end).split('\n');
|
|
27
|
+
const out = {};
|
|
28
|
+
for (const line of lines) {
|
|
29
|
+
const m = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\s*(.*?)\s*$/);
|
|
30
|
+
if (m) out[m[1]] = m[2].replace(/^"|"$/g, '').replace(/^'|'$/g, '');
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function bodyAfterFrontmatter(content) {
|
|
36
|
+
if (!content.startsWith('---')) return content;
|
|
37
|
+
const end = content.indexOf('\n---', 3);
|
|
38
|
+
if (end === -1) return content;
|
|
39
|
+
return content.slice(end + 4).replace(/^\s+/, '');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function escapeYamlDouble(str) {
|
|
43
|
+
return String(str).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function clipOneLine(str, max = 240) {
|
|
47
|
+
const flat = String(str).replace(/\s+/g, ' ').trim();
|
|
48
|
+
if (flat.length <= max) return flat;
|
|
49
|
+
return flat.slice(0, max - 1).trimEnd() + '…';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function ensureDir(dir) {
|
|
53
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Walks the kit and returns a flat array of "assets" the adapters can render.
|
|
57
|
+
// Each asset has: { kind: 'agent'|'workflow'|'skill', code, name, title, description, body, overlay }
|
|
58
|
+
function collectAssets(kitRoot, { profiles = ['core'] } = {}) {
|
|
59
|
+
const out = [];
|
|
60
|
+
const profSet = new Set(profiles);
|
|
61
|
+
|
|
62
|
+
for (const yamlPath of walkAgents(kitRoot)) {
|
|
63
|
+
const dir = path.dirname(yamlPath);
|
|
64
|
+
const yaml = fs.readFileSync(yamlPath, 'utf-8');
|
|
65
|
+
const code = readYamlField(yaml, 'code');
|
|
66
|
+
const name = readYamlField(yaml, 'name');
|
|
67
|
+
const title = readYamlField(yaml, 'title') || '';
|
|
68
|
+
const description = readYamlField(yaml, 'description') || '';
|
|
69
|
+
if (!code || !name) continue;
|
|
70
|
+
const personaPath = path.join(dir, 'persona.md');
|
|
71
|
+
const persona = fs.existsSync(personaPath) ? fs.readFileSync(personaPath, 'utf-8') : '';
|
|
72
|
+
out.push({ kind: 'agent', code, name, title, description, body: persona || description, overlay: null });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const wfPath of walkWorkflows(kitRoot)) {
|
|
76
|
+
const content = fs.readFileSync(wfPath, 'utf-8');
|
|
77
|
+
const fm = readFrontmatter(content);
|
|
78
|
+
if (!fm.code) continue;
|
|
79
|
+
if (fm.overlay === 'web' && !profSet.has('web-overlay')) continue;
|
|
80
|
+
if (fm.overlay === 'app' && !profSet.has('app-overlay')) continue;
|
|
81
|
+
out.push({
|
|
82
|
+
kind: 'workflow',
|
|
83
|
+
code: fm.code,
|
|
84
|
+
name: fm.name || fm.code,
|
|
85
|
+
title: fm.phase || fm.gate || '',
|
|
86
|
+
description: `${fm.phase || fm.gate || 'workflow'}: ${fm.name || fm.code}`,
|
|
87
|
+
body: bodyAfterFrontmatter(content),
|
|
88
|
+
overlay: fm.overlay || null
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const skPath of walkSkills(kitRoot)) {
|
|
93
|
+
const content = fs.readFileSync(skPath, 'utf-8');
|
|
94
|
+
const fm = readFrontmatter(content);
|
|
95
|
+
if (!fm.code) continue;
|
|
96
|
+
out.push({
|
|
97
|
+
kind: 'skill',
|
|
98
|
+
code: fm.code,
|
|
99
|
+
name: fm.name || fm.code,
|
|
100
|
+
title: fm.module || '',
|
|
101
|
+
description: fm.module ? `${fm.module} skill: ${fm.name || fm.code}` : (fm.name || fm.code),
|
|
102
|
+
body: bodyAfterFrontmatter(content),
|
|
103
|
+
overlay: null
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Anthropic-style SKILL.md renderer used by Claude Code, Antigravity, Codex, Kimi.
|
|
111
|
+
// Each asset becomes <targetDir>/<code>/SKILL.md with YAML frontmatter (name, description).
|
|
112
|
+
function renderAnthropicSkills(kitRoot, targetDir, opts = {}) {
|
|
113
|
+
const assets = collectAssets(kitRoot, opts);
|
|
114
|
+
const written = [];
|
|
115
|
+
if (!opts.dryRun) ensureDir(targetDir);
|
|
116
|
+
|
|
117
|
+
for (const a of assets) {
|
|
118
|
+
const desc = clipOneLine(
|
|
119
|
+
a.kind === 'agent'
|
|
120
|
+
? `${a.name} (${a.title}) — ${a.description}`
|
|
121
|
+
: a.description
|
|
122
|
+
);
|
|
123
|
+
const heading = a.kind === 'agent' ? `# ${a.name} — ${a.title}` : `# ${a.name}`;
|
|
124
|
+
const content = [
|
|
125
|
+
'---',
|
|
126
|
+
`name: ${a.code}`,
|
|
127
|
+
`description: "${escapeYamlDouble(desc)}"`,
|
|
128
|
+
'---',
|
|
129
|
+
'',
|
|
130
|
+
heading,
|
|
131
|
+
'',
|
|
132
|
+
a.body.trim(),
|
|
133
|
+
''
|
|
134
|
+
].join('\n');
|
|
135
|
+
|
|
136
|
+
if (opts.dryRun) {
|
|
137
|
+
written.push(`[dry-run] ${a.code}/SKILL.md`);
|
|
138
|
+
} else {
|
|
139
|
+
const skillDir = path.join(targetDir, a.code);
|
|
140
|
+
ensureDir(skillDir);
|
|
141
|
+
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), content, 'utf-8');
|
|
142
|
+
written.push(`${a.code}/SKILL.md`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { written, skipped: [] };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Convenience emitter for a root-level AGENTS.md — read by Codex, Cursor,
|
|
149
|
+
// Windsurf and Antigravity as a baseline pointer to the kit's deeper docs.
|
|
150
|
+
function renderAgentsMd(kitRoot, projectRoot, opts = {}) {
|
|
151
|
+
const assets = collectAssets(kitRoot, opts);
|
|
152
|
+
const agents = assets.filter(a => a.kind === 'agent');
|
|
153
|
+
const file = path.join(projectRoot, 'AGENTS.md');
|
|
154
|
+
if (fs.existsSync(file) && !opts.overwrite) {
|
|
155
|
+
return { written: [], skipped: ['AGENTS.md (already present; not overwritten)'] };
|
|
156
|
+
}
|
|
157
|
+
const lines = [
|
|
158
|
+
'# Agents — Wize Development Kit',
|
|
159
|
+
'',
|
|
160
|
+
'This repo is wired with the [`wize-dev-kit`](https://www.npmjs.com/package/wize-dev-kit).',
|
|
161
|
+
'Detailed artifacts live under `.wize/`. The agents below are activated through your AI IDE',
|
|
162
|
+
'using slash commands (Claude Code, Codex, Cursor, Windsurf, Antigravity all read this file).',
|
|
163
|
+
'',
|
|
164
|
+
'## Roster',
|
|
165
|
+
''
|
|
166
|
+
];
|
|
167
|
+
for (const a of agents) {
|
|
168
|
+
lines.push(`- **${a.name}** (\`${a.code}\`) — ${a.title}. ${clipOneLine(a.description, 180)}`);
|
|
169
|
+
}
|
|
170
|
+
lines.push('', '## Where to start', '', 'Activate the orchestrator: `wize-orchestrator` (Wizer). Then ask `/wize-help`.', '');
|
|
171
|
+
|
|
172
|
+
if (opts.dryRun) return { written: ['[dry-run] AGENTS.md'], skipped: [] };
|
|
173
|
+
fs.writeFileSync(file, lines.join('\n'), 'utf-8');
|
|
174
|
+
return { written: ['AGENTS.md'], skipped: [] };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
module.exports = {
|
|
178
|
+
collectAssets,
|
|
179
|
+
renderAnthropicSkills,
|
|
180
|
+
renderAgentsMd,
|
|
181
|
+
// Re-exports for adapter authors who need to roll their own format:
|
|
182
|
+
readFrontmatter,
|
|
183
|
+
bodyAfterFrontmatter,
|
|
184
|
+
escapeYamlDouble,
|
|
185
|
+
clipOneLine,
|
|
186
|
+
ensureDir
|
|
187
|
+
};
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
const fs = require('node:fs');
|
|
13
13
|
const path = require('node:path');
|
|
14
14
|
const readline = require('node:readline');
|
|
15
|
+
const prompts = require('prompts');
|
|
16
|
+
|
|
17
|
+
const INTERACTIVE = process.stdout.isTTY && process.stdin.isTTY;
|
|
15
18
|
|
|
16
19
|
const KIT_ROOT = path.resolve(__dirname, '..', '..');
|
|
17
20
|
const KIT_VERSION = require(path.join(KIT_ROOT, 'package.json')).version;
|
|
@@ -94,12 +97,41 @@ async function confirm(question, defaultYes = true) {
|
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
async function selectLanguage(label, defaultCode = 'en') {
|
|
100
|
+
if (INTERACTIVE) {
|
|
101
|
+
const choices = LANGUAGES.map(l => ({
|
|
102
|
+
title: `${l.code.padEnd(6)} — ${l.label}`,
|
|
103
|
+
value: l.code,
|
|
104
|
+
description: l.code === defaultCode ? 'default' : undefined
|
|
105
|
+
}));
|
|
106
|
+
choices.push({ title: 'Other (type a custom BCP-47 code)…', value: '__custom' });
|
|
107
|
+
const initial = LANGUAGES.findIndex(l => l.code === defaultCode);
|
|
108
|
+
const { picked } = await prompts({
|
|
109
|
+
type: 'select',
|
|
110
|
+
name: 'picked',
|
|
111
|
+
message: label,
|
|
112
|
+
choices,
|
|
113
|
+
initial: initial === -1 ? 0 : initial
|
|
114
|
+
});
|
|
115
|
+
if (picked === undefined) process.exit(130);
|
|
116
|
+
if (picked === '__custom') {
|
|
117
|
+
const { custom } = await prompts({
|
|
118
|
+
type: 'text',
|
|
119
|
+
name: 'custom',
|
|
120
|
+
message: 'Type a BCP-47 language code (e.g. ko, ar, nl, ru)',
|
|
121
|
+
initial: defaultCode,
|
|
122
|
+
validate: v => /^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})?$/.test(v) || 'use a valid code like "en" or "pt-BR"'
|
|
123
|
+
});
|
|
124
|
+
return custom || defaultCode;
|
|
125
|
+
}
|
|
126
|
+
return picked;
|
|
127
|
+
}
|
|
128
|
+
// Non-TTY fallback: number-driven.
|
|
97
129
|
console.log(`\n${label}:`);
|
|
98
130
|
LANGUAGES.forEach((l, i) => {
|
|
99
131
|
const def = l.code === defaultCode ? '(default)' : '';
|
|
100
132
|
console.log(` ${String(i + 1).padStart(2)}. ${l.code.padEnd(6)} — ${l.label} ${def}`);
|
|
101
133
|
});
|
|
102
|
-
const ans = (await prompt(`Pick a number, type a code
|
|
134
|
+
const ans = (await prompt(`Pick a number, type a code, or ENTER for ${defaultCode}: `)).trim();
|
|
103
135
|
if (!ans) return defaultCode;
|
|
104
136
|
const asNum = parseInt(ans, 10);
|
|
105
137
|
if (!isNaN(asNum) && asNum >= 1 && asNum <= LANGUAGES.length) return LANGUAGES[asNum - 1].code;
|
|
@@ -107,6 +139,26 @@ async function selectLanguage(label, defaultCode = 'en') {
|
|
|
107
139
|
}
|
|
108
140
|
|
|
109
141
|
async function multiSelect(label, items, isSelected = i => i.default) {
|
|
142
|
+
if (INTERACTIVE) {
|
|
143
|
+
const { picked } = await prompts({
|
|
144
|
+
type: 'multiselect',
|
|
145
|
+
name: 'picked',
|
|
146
|
+
message: label,
|
|
147
|
+
hint: '↑/↓ navigate · space toggle · enter confirm',
|
|
148
|
+
instructions: false,
|
|
149
|
+
choices: items.map(it => ({
|
|
150
|
+
title: it.label + (it.required ? ' (required)' : ''),
|
|
151
|
+
value: it.code,
|
|
152
|
+
selected: it.required || isSelected(it),
|
|
153
|
+
disabled: it.required
|
|
154
|
+
}))
|
|
155
|
+
});
|
|
156
|
+
if (picked === undefined) process.exit(130);
|
|
157
|
+
const codes = new Set(picked);
|
|
158
|
+
for (const req of items.filter(i => i.required)) codes.add(req.code);
|
|
159
|
+
return items.filter(it => codes.has(it.code));
|
|
160
|
+
}
|
|
161
|
+
// Non-TTY fallback.
|
|
110
162
|
console.log(`\n${label}:`);
|
|
111
163
|
items.forEach((it, i) => {
|
|
112
164
|
const star = it.required ? '★ ' : ' ';
|
|
@@ -114,9 +166,7 @@ async function multiSelect(label, items, isSelected = i => i.default) {
|
|
|
114
166
|
console.log(` ${i + 1}. ${star}${it.label} ${def}`);
|
|
115
167
|
});
|
|
116
168
|
const ans = await prompt('Pick numbers (comma-separated) or ENTER for defaults: ');
|
|
117
|
-
if (!ans)
|
|
118
|
-
return items.filter(it => it.required || isSelected(it));
|
|
119
|
-
}
|
|
169
|
+
if (!ans) return items.filter(it => it.required || isSelected(it));
|
|
120
170
|
const indices = ans.split(',').map(s => parseInt(s.trim(), 10) - 1).filter(i => !isNaN(i));
|
|
121
171
|
const picked = indices.map(i => items[i]).filter(Boolean);
|
|
122
172
|
for (const req of items.filter(i => i.required)) {
|
|
@@ -227,6 +277,35 @@ function userToml() {
|
|
|
227
277
|
`;
|
|
228
278
|
}
|
|
229
279
|
|
|
280
|
+
function renderAdapters({ kitRoot, projectRoot, targets, profiles }) {
|
|
281
|
+
const results = [];
|
|
282
|
+
const profileCodes = profiles.map(p => p.code);
|
|
283
|
+
for (const target of targets) {
|
|
284
|
+
const adapterDir = path.join(kitRoot, 'adapters', target.code);
|
|
285
|
+
const renderPath = path.join(adapterDir, 'render.js');
|
|
286
|
+
if (!fs.existsSync(renderPath)) {
|
|
287
|
+
results.push({ code: target.code, skipped: true, reason: 'adapter missing' });
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
const mod = require(renderPath);
|
|
292
|
+
if (typeof mod.render !== 'function') {
|
|
293
|
+
results.push({ code: target.code, skipped: true, reason: 'no render() export' });
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const out = mod.render(kitRoot, projectRoot, { profiles: profileCodes });
|
|
297
|
+
if (out && typeof out === 'object' && Array.isArray(out.written)) {
|
|
298
|
+
results.push({ code: target.code, written: out.written.length });
|
|
299
|
+
} else {
|
|
300
|
+
results.push({ code: target.code, skipped: true, reason: 'stub (no skills emitted)' });
|
|
301
|
+
}
|
|
302
|
+
} catch (err) {
|
|
303
|
+
results.push({ code: target.code, error: err.message });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return results;
|
|
307
|
+
}
|
|
308
|
+
|
|
230
309
|
async function cmdInstall(args) {
|
|
231
310
|
const cwd = process.cwd();
|
|
232
311
|
console.log(logo());
|
|
@@ -270,8 +349,18 @@ async function cmdInstall(args) {
|
|
|
270
349
|
console.log(`✓ profiles: ${profiles.map(p => p.code).join(', ')}`);
|
|
271
350
|
console.log(`✓ ide targets: ${targets.map(t => t.code).join(', ')}`);
|
|
272
351
|
|
|
273
|
-
console.log('\
|
|
274
|
-
|
|
352
|
+
console.log('\nGenerating IDE adapters...');
|
|
353
|
+
const renderResults = renderAdapters({
|
|
354
|
+
kitRoot: KIT_ROOT,
|
|
355
|
+
projectRoot: cwd,
|
|
356
|
+
targets,
|
|
357
|
+
profiles
|
|
358
|
+
});
|
|
359
|
+
for (const r of renderResults) {
|
|
360
|
+
if (r.skipped) console.log(` - ${r.code}: skipped (${r.reason})`);
|
|
361
|
+
else if (r.written) console.log(` ✓ ${r.code}: ${r.written} skill(s) emitted`);
|
|
362
|
+
else if (r.error) console.log(` ✖ ${r.code}: ${r.error}`);
|
|
363
|
+
}
|
|
275
364
|
|
|
276
365
|
if (detection.brownfield) {
|
|
277
366
|
const baseline = await confirm('\nRun `wize-document-project` to baseline the existing repo now?', true);
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
code: wize-help
|
|
3
|
-
name: Help
|
|
4
|
-
module: core
|
|
5
|
-
status: stub
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Help
|
|
9
|
-
|
|
10
|
-
Routes user intent to the right agent or workflow.
|
|
11
|
-
|
|
12
|
-
## Examples
|
|
13
|
-
- "I need to start a new project." → Wizer onboarding.
|
|
14
|
-
- "I have a brief; what's next?" → Maria Hill / `wize-create-prd`.
|
|
15
|
-
- "Design system?" → Mantis / `wize-design-system`.
|
|
16
|
-
- "Architecture?" → Tony / `wize-create-architecture`.
|
|
17
|
-
- "Test plan for this story?" → Hawkeye / `wize-tea-design`.
|
|
18
|
-
- "Add a custom agent." → Builder / `wize-create-agent`.
|