raptor-aios 0.13.0 → 0.13.2
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 +36 -1
- package/README.md +3 -3
- package/dist/_core/dist/git/repo.js +14 -0
- package/dist/_core/dist/jira/mapper.js +5 -1
- package/dist/_core/package.json +1 -1
- package/dist/commands/new.js +28 -1
- package/package.json +1 -1
- package/scripts/prepare-npm.mjs +1 -1
- package/dist/_core/dist/agents/materialize.js +0 -156
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,35 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
-
## [0.13.
|
|
8
|
+
## [0.13.2] - 2026-06-19
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Specs semeadas do Jira não vêm mais com `\*\*lixo\*\*` de escape.** Servidores
|
|
13
|
+
MCP que retornam Markdown como string (notavelmente `mcp-atlassian`, via
|
|
14
|
+
markdownify) **escapam** a pontuação inline (`\*`, `\_`, `` \` ``, `\~`) ao
|
|
15
|
+
converter o wiki/ADF do Jira para Markdown, para mantê-la literal. O
|
|
16
|
+
`flattenAdf` repassava essa string sem desescapar, então o negrito do card
|
|
17
|
+
(`**Como**`) chegava na spec como `\*\*Como\*\*` visível — ruído em toda a spec
|
|
18
|
+
(127 linhas afetadas num card real). Agora `flattenAdf` desescapa a pontuação
|
|
19
|
+
markdown inline na sua branch de string (`unescapeInlineMarkdown`); o ADF
|
|
20
|
+
estruturado é texto cru e permanece intocado.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **`raptor new` agora empilha as specs de verdade — auto-commit da spec na sua
|
|
25
|
+
branch.** O empilhamento de branches (v0.13.0) dependia de a spec anterior estar
|
|
26
|
+
**commitada** antes da próxima `raptor new`, mas havia um impasse: o `new` deixava
|
|
27
|
+
a spec *untracked* e o pre-commit hook **bloqueava** commitá-la manualmente (a spec
|
|
28
|
+
recém-criada falha nos gates M1–M5, que valem para uma feature já preenchida). Sem
|
|
29
|
+
commit, a árvore ficava suja, o guard pulava a criação da próxima branch e o
|
|
30
|
+
empilhamento nunca acontecia — a nova spec não herdava as anteriores. Agora o `new`
|
|
31
|
+
**commita automaticamente** a spec scaffolded na sua branch de feature (escopo
|
|
32
|
+
restrito a `.raptor/specs/<NNN-slug>`, sem varrer outras mudanças; `--no-verify`
|
|
33
|
+
porque os gates não se aplicam a um scaffold). A árvore fica limpa, a próxima
|
|
34
|
+
`raptor new` forka de uma base commitada e **cada branch carrega todas as specs
|
|
35
|
+
anteriores** — preservando dependências entre specs. Use `--no-commit` para
|
|
36
|
+
desativar. Novo helper `commitPaths` em `@raptor/core`.
|
|
9
37
|
|
|
10
38
|
### Fixed
|
|
11
39
|
|
|
@@ -38,6 +66,13 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
38
66
|
reuso-por-slug também consulta as branches, preservando a idempotência de `raptor new`
|
|
39
67
|
(re-rodar um slug que vive só em uma branch irmã volta para o número dela).
|
|
40
68
|
|
|
69
|
+
### Docs
|
|
70
|
+
|
|
71
|
+
- **Novo:** [`docs/feature-branching-and-numbering.md`](docs/feature-branching-and-numbering.md)
|
|
72
|
+
documenta o diagnóstico completo do falso "o `new` sobrescreve a spec anterior" (resolução
|
|
73
|
+
por sufixo + branch forkando da `main` + casca vazia ao trocar de branch) e o modelo de
|
|
74
|
+
branches empilhadas / numeração por união a partir desta release.
|
|
75
|
+
|
|
41
76
|
## [0.12.2] - 2026-06-16
|
|
42
77
|
|
|
43
78
|
### Changed
|
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/raptor-aios)
|
|
8
8
|
[](https://nodejs.org)
|
|
9
9
|
[](https://reactnative.dev)
|
|
10
|
-
[](#️-desenvolvimento-local)
|
|
11
|
+
[](CHANGELOG.md)
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
|
|
14
14
|
</div>
|
|
@@ -621,7 +621,7 @@ Monorepo pnpm (`packages/core` = `@raptor/core`, `packages/cli` = binário `rapt
|
|
|
621
621
|
```bash
|
|
622
622
|
pnpm install
|
|
623
623
|
pnpm build
|
|
624
|
-
pnpm -r test && pnpm test:e2e # ~
|
|
624
|
+
pnpm -r test && pnpm test:e2e # ~1374 testes (1071 core + 258 cli + 45 e2e)
|
|
625
625
|
|
|
626
626
|
# usar a CLI local em outro projeto:
|
|
627
627
|
cd packages/cli && npm link # ou: pnpm link --global
|
|
@@ -56,3 +56,17 @@ export function stagedFiles(exec, cwd) {
|
|
|
56
56
|
.map((s) => s.trim())
|
|
57
57
|
.filter(Boolean);
|
|
58
58
|
}
|
|
59
|
+
export function commitPaths(exec, cwd, paths, message) {
|
|
60
|
+
if (paths.length === 0)
|
|
61
|
+
return { status: 0, committed: false };
|
|
62
|
+
const status = exec(["status", "--porcelain", "--", ...paths], { cwd });
|
|
63
|
+
if (status.status !== 0)
|
|
64
|
+
return { status: status.status, committed: false };
|
|
65
|
+
if (status.stdout.trim() === "")
|
|
66
|
+
return { status: 0, committed: false };
|
|
67
|
+
const add = exec(["add", "--", ...paths], { cwd });
|
|
68
|
+
if (add.status !== 0)
|
|
69
|
+
return { status: add.status, committed: false };
|
|
70
|
+
const commit = exec(["commit", "--no-verify", "-m", message, "--", ...paths], { cwd });
|
|
71
|
+
return { status: commit.status, committed: commit.status === 0 };
|
|
72
|
+
}
|
|
@@ -269,11 +269,15 @@ export function mapIssueToSpecContext(issue, opts = {}) {
|
|
|
269
269
|
jiraCustomSections: sections,
|
|
270
270
|
};
|
|
271
271
|
}
|
|
272
|
+
const MD_INLINE_ESCAPE = /\\([*_`~])/g;
|
|
273
|
+
export function unescapeInlineMarkdown(s) {
|
|
274
|
+
return s.replace(MD_INLINE_ESCAPE, "$1");
|
|
275
|
+
}
|
|
272
276
|
export function flattenAdf(node) {
|
|
273
277
|
if (node == null)
|
|
274
278
|
return "";
|
|
275
279
|
if (typeof node === "string")
|
|
276
|
-
return node.trim();
|
|
280
|
+
return unescapeInlineMarkdown(node.trim());
|
|
277
281
|
if (Array.isArray(node))
|
|
278
282
|
return node.map(flattenAdf).join("");
|
|
279
283
|
if (!isRecord(node))
|
package/dist/_core/package.json
CHANGED
package/dist/commands/new.js
CHANGED
|
@@ -3,7 +3,7 @@ import { BaseCommand } from "../base-command.js";
|
|
|
3
3
|
import { step, heading } from "../shared/ui.js";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, } from "node:fs";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
-
import { appendAuditEvent, buildCanonicalPrompt, deriveBranchName, execSyncGitExecutor, executeWorkflow, listBranches, extractFigmaRefs, hashString, inferKindFromJiraIssueType, loadAgentsConfig, mapDesignToSpecContext, mapIssueToSpecContext, parseFrontmatter, renderBundled, selectAgent, writeDesignScaffold, } from "../_core/dist/index.js";
|
|
6
|
+
import { appendAuditEvent, buildCanonicalPrompt, commitPaths, deriveBranchName, execSyncGitExecutor, executeWorkflow, isGitRepo, listBranches, extractFigmaRefs, hashString, inferKindFromJiraIssueType, loadAgentsConfig, mapDesignToSpecContext, mapIssueToSpecContext, parseFrontmatter, renderBundled, selectAgent, writeDesignScaffold, } from "../_core/dist/index.js";
|
|
7
7
|
import { currentActor, readConfig, readInitOptions, requireProjectRoot, } from "../shared/project.js";
|
|
8
8
|
import { ensureWorkItemBranch, resolveInteractive, } from "../shared/git-prompt.js";
|
|
9
9
|
import { jiraConn, jiraReadyNonInteractive, openJiraClient, } from "../shared/jira.js";
|
|
@@ -67,6 +67,11 @@ export default class New extends BaseCommand {
|
|
|
67
67
|
description: "Skip automatic branch creation even when git.branch_per_feature is enabled",
|
|
68
68
|
default: false,
|
|
69
69
|
}),
|
|
70
|
+
commit: Flags.boolean({
|
|
71
|
+
description: "Auto-commit the scaffolded spec on its feature branch (bypasses the pre-commit hook so the tree stays clean and the next `raptor new` stacks the next spec on top). Use --no-commit to leave the spec uncommitted.",
|
|
72
|
+
allowNo: true,
|
|
73
|
+
default: true,
|
|
74
|
+
}),
|
|
70
75
|
"non-interactive": Flags.boolean({
|
|
71
76
|
description: "Never prompt; on a dirty work tree, skip branch creation instead of asking",
|
|
72
77
|
default: false,
|
|
@@ -379,6 +384,12 @@ export default class New extends BaseCommand {
|
|
|
379
384
|
else if (branchResult?.outcome === "already") {
|
|
380
385
|
this.log(` Branch: ${branchResult.branch} (already checked out)`);
|
|
381
386
|
}
|
|
387
|
+
this.maybeAutoCommitSpec({
|
|
388
|
+
root,
|
|
389
|
+
featureName,
|
|
390
|
+
branchResult,
|
|
391
|
+
doCommit: flags.commit,
|
|
392
|
+
});
|
|
382
393
|
this.log("");
|
|
383
394
|
if (flags["wait-artifact"] && agentInfo.commandFile) {
|
|
384
395
|
await waitForPhaseArtifact({
|
|
@@ -481,6 +492,22 @@ export default class New extends BaseCommand {
|
|
|
481
492
|
interactive: resolveInteractive(ctx.nonInteractive),
|
|
482
493
|
});
|
|
483
494
|
}
|
|
495
|
+
maybeAutoCommitSpec(ctx) {
|
|
496
|
+
if (!ctx.doCommit)
|
|
497
|
+
return;
|
|
498
|
+
if (!ctx.branchResult || ctx.branchResult.outcome === "skipped")
|
|
499
|
+
return;
|
|
500
|
+
if (!isGitRepo(execSyncGitExecutor, ctx.root))
|
|
501
|
+
return;
|
|
502
|
+
const rel = join(".raptor", "specs", ctx.featureName);
|
|
503
|
+
const res = commitPaths(execSyncGitExecutor, ctx.root, [rel], `chore: scaffold spec ${ctx.featureName}`);
|
|
504
|
+
if (res.committed) {
|
|
505
|
+
this.log(` Committed: ${rel} (spec sealed on ${ctx.branchResult.branch} — next \`raptor new\` will stack on top)`);
|
|
506
|
+
}
|
|
507
|
+
else if (res.status !== 0) {
|
|
508
|
+
this.warn(`Auto-commit of the spec failed (git exit ${res.status}). Spec is saved; commit it manually so the next \`raptor new\` stacks.`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
484
511
|
async fetchJiraContext(root, key) {
|
|
485
512
|
const conn = jiraConn(readConfig(root));
|
|
486
513
|
if (!conn) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "raptor-aios",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "Raptor — Spec-Driven Development (SDD) CLI for modern mobile apps. Constitutional gates, audit trail, real verification (a11y/perf/stores/OS matrix), and AI-agent slash commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/scripts/prepare-npm.mjs
CHANGED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-Agent Materialization Pipeline — Materializes slash commands
|
|
3
|
-
* for all registered agents.
|
|
4
|
-
*
|
|
5
|
-
* Given a canonical prompt (agent-neutral), this pipeline:
|
|
6
|
-
* 1. Resolves placeholders (renderer)
|
|
7
|
-
* 2. Packs the prompt for each agent (adapter.packPrompt)
|
|
8
|
-
* 3. Writes command files to the agent's expected location
|
|
9
|
-
*
|
|
10
|
-
* Spec Kit ref: E11 — "Pipeline de materialização cross-agent"
|
|
11
|
-
*/
|
|
12
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
-
import { join, basename } from "node:path";
|
|
14
|
-
import { listAgents, getAgent } from "../agents/registry.js";
|
|
15
|
-
import { renderPrompt, autoResolveContext } from "../prompts/renderer.js";
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Pipeline
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
/**
|
|
20
|
-
* Materialize all canonical prompts for the specified agents.
|
|
21
|
-
*
|
|
22
|
-
* Reads templates from `templates/commands/` and extension `commands/`,
|
|
23
|
-
* renders them, packs them for each agent, and writes output files.
|
|
24
|
-
*/
|
|
25
|
-
export function materializeAllCommands(options) {
|
|
26
|
-
const results = [];
|
|
27
|
-
const ctx = autoResolveContext(options.projectRoot, options.context);
|
|
28
|
-
// Determine target agents
|
|
29
|
-
const registeredAgents = listAgents();
|
|
30
|
-
const targetIds = options.agentIds ?? registeredAgents.map((a) => a.id);
|
|
31
|
-
// Collect prompt templates
|
|
32
|
-
const templates = collectPromptTemplates(options.projectRoot);
|
|
33
|
-
for (const agentId of targetIds) {
|
|
34
|
-
const adapter = getAgent(agentId);
|
|
35
|
-
if (!adapter) {
|
|
36
|
-
results.push({
|
|
37
|
-
agentId,
|
|
38
|
-
commandName: "*",
|
|
39
|
-
outputPath: "",
|
|
40
|
-
written: false,
|
|
41
|
-
error: `Agent '${agentId}' not registered`,
|
|
42
|
-
});
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
for (const { name, content } of templates) {
|
|
46
|
-
const result = materializeSingleCommand(options.projectRoot, adapter, name, content, ctx, options.dryRun);
|
|
47
|
-
results.push(result);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return results;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Materialize a single command for a specific agent.
|
|
54
|
-
*/
|
|
55
|
-
function materializeSingleCommand(projectRoot, adapter, commandName, templateContent, ctx, dryRun) {
|
|
56
|
-
const agentId = adapter.id;
|
|
57
|
-
try {
|
|
58
|
-
// 1. Render placeholders
|
|
59
|
-
const rendered = renderPrompt(templateContent, ctx);
|
|
60
|
-
// 2. Pack for agent (converts to agent-specific format)
|
|
61
|
-
// We construct a mock CanonicalPrompt because static slash commands
|
|
62
|
-
// don't have dynamic feature context yet (handled at runtime).
|
|
63
|
-
const canonical = {
|
|
64
|
-
command: commandName,
|
|
65
|
-
feature: adapter.argumentSyntax, // e.g. "$ARGUMENTS"
|
|
66
|
-
promptHash: "static-template",
|
|
67
|
-
generatedAt: new Date().toISOString(),
|
|
68
|
-
body: rendered,
|
|
69
|
-
scope: { read: [], write: [], forbidden: [] },
|
|
70
|
-
invariants: [],
|
|
71
|
-
inputs: [],
|
|
72
|
-
expectedOutput: "",
|
|
73
|
-
outputSchema: "",
|
|
74
|
-
articles: [],
|
|
75
|
-
};
|
|
76
|
-
const packed = adapter.packPrompt(canonical);
|
|
77
|
-
// 3. Determine output path
|
|
78
|
-
const outputDir = resolveOutputDir(projectRoot, adapter);
|
|
79
|
-
const fileName = adapter.commandFileName(`rpt.${commandName}`);
|
|
80
|
-
const outputPath = join(outputDir, fileName);
|
|
81
|
-
// 4. Write
|
|
82
|
-
if (!dryRun) {
|
|
83
|
-
if (!existsSync(outputDir)) {
|
|
84
|
-
mkdirSync(outputDir, { recursive: true });
|
|
85
|
-
}
|
|
86
|
-
writeFileSync(outputPath, packed);
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
agentId,
|
|
90
|
-
commandName,
|
|
91
|
-
outputPath,
|
|
92
|
-
written: !dryRun,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
return {
|
|
97
|
-
agentId,
|
|
98
|
-
commandName,
|
|
99
|
-
outputPath: "",
|
|
100
|
-
written: false,
|
|
101
|
-
error: err.message,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
// Helpers
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
/**
|
|
109
|
-
* Collect all prompt templates from the project.
|
|
110
|
-
*/
|
|
111
|
-
function collectPromptTemplates(projectRoot) {
|
|
112
|
-
const templates = [];
|
|
113
|
-
// Core prompts
|
|
114
|
-
const coreDir = join(projectRoot, "templates", "commands");
|
|
115
|
-
if (existsSync(coreDir)) {
|
|
116
|
-
for (const file of readdirSync(coreDir)) {
|
|
117
|
-
if (!file.endsWith(".md"))
|
|
118
|
-
continue;
|
|
119
|
-
const name = basename(file, ".md");
|
|
120
|
-
const content = readFileSync(join(coreDir, file), "utf-8");
|
|
121
|
-
templates.push({ name, content });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
// Extension prompts
|
|
125
|
-
const extDir = join(projectRoot, "extensions");
|
|
126
|
-
if (existsSync(extDir)) {
|
|
127
|
-
for (const ext of readdirSync(extDir)) {
|
|
128
|
-
const cmdDir = join(extDir, ext, "commands");
|
|
129
|
-
if (!existsSync(cmdDir))
|
|
130
|
-
continue;
|
|
131
|
-
for (const file of readdirSync(cmdDir)) {
|
|
132
|
-
if (!file.endsWith(".md"))
|
|
133
|
-
continue;
|
|
134
|
-
const name = basename(file, ".md");
|
|
135
|
-
const content = readFileSync(join(cmdDir, file), "utf-8");
|
|
136
|
-
templates.push({ name, content });
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return templates;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Resolve the output directory for an agent's command files.
|
|
144
|
-
*/
|
|
145
|
-
function resolveOutputDir(projectRoot, adapter) {
|
|
146
|
-
// Agent-specific directories
|
|
147
|
-
const agentDirMap = {
|
|
148
|
-
"claude-code": ".claude/commands",
|
|
149
|
-
"cursor": ".cursor/commands",
|
|
150
|
-
"antigravity": ".gemini/commands",
|
|
151
|
-
"gemini": ".gemini/commands",
|
|
152
|
-
"human": ".raptor/commands",
|
|
153
|
-
};
|
|
154
|
-
const dir = agentDirMap[adapter.id] ?? `.raptor/agents/${adapter.id}/commands`;
|
|
155
|
-
return join(projectRoot, dir);
|
|
156
|
-
}
|