raptor-aios 0.6.1 → 0.6.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 +26 -1
- package/dist/_core/dist/agents/index.js +1 -1
- package/dist/_core/dist/agents/slash-materializer.js +17 -11
- package/dist/_core/package.json +1 -1
- package/dist/commands/init.js +6 -113
- package/dist/commands/new.js +29 -13
- package/dist/commands/upgrade.js +231 -82
- package/dist/shared/project.js +6 -0
- package/dist/shared/scaffold.js +162 -0
- package/package.json +1 -1
- package/scripts/prepare-npm.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,11 +3,36 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [0.6.3] - 2026-06-06
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **`raptor new --jira=<KEY>` no longer creates an empty, mislabelled spec when the Jira card can't be read.** Previously, when a ticket was given but Jira was not connected (or the token had expired, or the issue was unreachable), `new` warned and then degraded to seeding a spec stamped with the Jira ID but **no card content** — and created the branch too. Now, when a fetch was expected (the default), an unreadable card is a **hard stop**: `new` aborts **before any side effect** (no spec, no directory, no branch) with an actionable message that states the reason and points to `raptor jira connect` (or configuring the Jira MCP server). The intentional offline path is unchanged — pass `--no-jira-fetch` to record the ticket ID only.
|
|
11
|
+
|
|
12
|
+
### Internal
|
|
13
|
+
|
|
14
|
+
- `New.fetchJiraContext` now returns a discriminated result (`{ ok, context }` | `{ ok: false, reason }`) so the caller can abort with the specific failure reason instead of silently degrading. Removed the now-unreachable `"id only (fetch unavailable)"` status tag.
|
|
15
|
+
|
|
16
|
+
## [0.6.2] - 2026-06-06
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **`raptor upgrade` is now a real, non-destructive project sync.** Previously it only bumped `raptor_version` as a side effect of a manifest schema-version change, so a same-schema release (e.g. 0.5.0 → 0.6.x) left the project stamped at the old version and brought none of the framework updates. Now `raptor upgrade`:
|
|
21
|
+
- **Re-stamps the version unconditionally** in `raptor.manifest.json` and, surgically (single `version:` line), in `.raptor/raptor.yml` — preserving the user's `mcp`/`git`/`jira` sections **and comments** byte-for-byte (no template re-render).
|
|
22
|
+
- **Refreshes every framework-owned file** to the installed version — shell scripts, command templates, the rich slash commands (`specify`/`plan`/`tasks`/… now carry the design-gate instructions), and bundled extensions — **backing each up to `.raptor/backup/<timestamp>/` before overwriting**, and skipping files that are already current (idempotent).
|
|
23
|
+
- **Upgrades the constitution CORE block in place** when outdated (reusing the `repair constitution` mechanism): preserves preset (M*) and project (P*) articles, logs `constitution.upgraded`, and keeps `gate.constitution.integrity` / `gate.amendment.core` green. Requires a human approver (C5, `git user.email`); skips with a warning when absent or when the CORE was hand-edited (run `raptor repair constitution` first).
|
|
24
|
+
- **Never touches user-owned content**: `init-options.json`, `.raptor/specs/`, `.raptor/memory/*`, `.raptor/templates/overrides/`, custom extensions, and an existing `agents.yml` are left as-is. `--dry-run` previews the full change list and writes nothing.
|
|
25
|
+
|
|
26
|
+
### Internal
|
|
27
|
+
|
|
28
|
+
- Extracted the framework-install routines (`installShellScripts`, `installTemplates`, `installBundledExtensions`, `collectFrameworkFiles`) into `packages/cli/src/shared/scaffold.ts`, shared by `init` and `upgrade` so an upgraded project matches a freshly-init'd one. Split the slash-command materializer into a pure `planSlashCommands` (used by upgrade's diff) plus the writing `materializeSlashCommands`.
|
|
29
|
+
|
|
6
30
|
## [0.6.1] - 2026-06-05
|
|
7
31
|
|
|
8
32
|
### Changed
|
|
9
33
|
|
|
10
|
-
- Package `author` set to **Lucas Pedro <lucaspedrolbg@gmail.com> (https://github.com/lucaspedronet)** on the publishable package and the workspace `package.json` files (previously the bare npm handle).
|
|
34
|
+
- Package `author` set to **Lucas Pedro <lucaspedrolbg@gmail.com> (https://github.com/lucaspedronet)** on the publishable package and the workspace `package.json` files (previously the bare npm handle).
|
|
35
|
+
- **Slimmer published package.** The tarball no longer ships `.d.ts` type declarations, `.d.ts.map`/`.js.map` source maps, or `sourceMappingURL` pragmas, and the emitted `.js` is built with `removeComments` — removing the most readable/structured slice of the code (the typed API surface and the JSDoc rationale) and roughly halving the tarball. Runtime behaviour is unchanged. (Note: a public npm package can never truly hide code; this only reduces casual exposure.)
|
|
11
36
|
|
|
12
37
|
## [0.6.0] - 2026-06-05
|
|
13
38
|
|
|
@@ -15,4 +15,4 @@ export { validateHandoff, nextPhase, isTerminalPhase } from "./handoff.js";
|
|
|
15
15
|
export { waitForArtifact, snapshotMtime, snapshotDir, resolveArtifactPath, watchTargetOf, relPath, ArtifactWaitTimeoutError, ArtifactWaitAbortedError, } from "./artifact-watcher.js";
|
|
16
16
|
export { backupIfExists, createBackupSession, backupTimestamp, DEFAULT_BACKUP_SUBDIR, } from "./backup.js";
|
|
17
17
|
export { MARKERS, wrapSpec, wrapUntrusted, sanitizeUntrusted, assertNoMarkers, extractSpec, extractUntrusted, hasSpecMarkers, } from "./prompt-markers.js";
|
|
18
|
-
export { materializeSlashCommands, renderRichCommand, locateRichCommandsDir, } from "./slash-materializer.js";
|
|
18
|
+
export { materializeSlashCommands, planSlashCommands, renderRichCommand, locateRichCommandsDir, } from "./slash-materializer.js";
|
|
@@ -37,10 +37,9 @@ export function renderRichCommand(raw, opts) {
|
|
|
37
37
|
c = c.replace(/\brpt\.([a-z-]+)/g, "raptor-$1");
|
|
38
38
|
return c;
|
|
39
39
|
}
|
|
40
|
-
export function
|
|
40
|
+
export function planSlashCommands(opts) {
|
|
41
41
|
const { projectRoot, adapter, constitutionArticles } = opts;
|
|
42
42
|
const commandDir = join(projectRoot, adapter.commandDir);
|
|
43
|
-
mkdirSync(commandDir, { recursive: true });
|
|
44
43
|
const richDir = locateRichCommandsDir(projectRoot);
|
|
45
44
|
const commands = richDir
|
|
46
45
|
? readdirSync(richDir)
|
|
@@ -48,7 +47,7 @@ export function materializeSlashCommands(opts) {
|
|
|
48
47
|
.map((f) => f.replace(/\.md$/, ""))
|
|
49
48
|
.sort()
|
|
50
49
|
: knownCommands();
|
|
51
|
-
const
|
|
50
|
+
const planned = [];
|
|
52
51
|
for (const command of commands) {
|
|
53
52
|
let content;
|
|
54
53
|
const richPath = richDir ? join(richDir, `${command}.md`) : null;
|
|
@@ -68,16 +67,23 @@ export function materializeSlashCommands(opts) {
|
|
|
68
67
|
content = adapter.packPrompt(canonical);
|
|
69
68
|
}
|
|
70
69
|
const fileName = adapter.commandFileName(command);
|
|
71
|
-
|
|
72
|
-
writeFileSync(filePath, content);
|
|
73
|
-
materialized.push({
|
|
70
|
+
planned.push({
|
|
74
71
|
command,
|
|
75
|
-
path:
|
|
72
|
+
path: join(commandDir, fileName),
|
|
73
|
+
content,
|
|
76
74
|
hash: hashString(content),
|
|
77
75
|
});
|
|
78
76
|
}
|
|
79
|
-
return {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
return { agentId: adapter.id, commands: planned };
|
|
78
|
+
}
|
|
79
|
+
export function materializeSlashCommands(opts) {
|
|
80
|
+
const plan = planSlashCommands(opts);
|
|
81
|
+
const commandDir = join(opts.projectRoot, opts.adapter.commandDir);
|
|
82
|
+
mkdirSync(commandDir, { recursive: true });
|
|
83
|
+
const materialized = [];
|
|
84
|
+
for (const c of plan.commands) {
|
|
85
|
+
writeFileSync(c.path, c.content);
|
|
86
|
+
materialized.push({ command: c.command, path: c.path, hash: c.hash });
|
|
87
|
+
}
|
|
88
|
+
return { agentId: plan.agentId, commands: materialized };
|
|
83
89
|
}
|
package/dist/_core/package.json
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Flags } from "@oclif/core";
|
|
2
2
|
import { BaseCommand } from "../base-command.js";
|
|
3
|
-
import { chmodSync,
|
|
4
|
-
import { basename,
|
|
3
|
+
import { chmodSync, existsSync, mkdirSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
5
|
import { appendAuditEvent, buildContextBlock, coreBlock, CORE_VERSION, createManifest, detectInstalledAgents, generateAgentsYaml, getAgentByKind, getPreset, hashString, knownPresetIds, materializeSlashCommands, renderBundled, renderPresetArticles, SELECTABLE_AGENT_KEYS, upsertContextFile, VERSION, } from "../_core/dist/index.js";
|
|
6
6
|
import { currentActor } from "../shared/project.js";
|
|
7
|
+
import { installBundledExtensions, installShellScripts, installTemplates, } from "../shared/scaffold.js";
|
|
7
8
|
export default class Init extends BaseCommand {
|
|
8
9
|
static description = "Initialize a Raptor project in the current directory";
|
|
9
10
|
static examples = [
|
|
@@ -126,9 +127,9 @@ export default class Init extends BaseCommand {
|
|
|
126
127
|
writeFileSync(join(raptorDir, "memory", "decisions.md"), "# Architecture Decision Records\n\n");
|
|
127
128
|
writeFileSync(join(raptorDir, "memory", "glossary.md"), "# Project Glossary\n\n");
|
|
128
129
|
writeFileSync(join(raptorDir, ".gitignore"), "cache/\n*.log\n");
|
|
129
|
-
this
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
installShellScripts(this, raptorDir, manifest);
|
|
131
|
+
installTemplates(raptorDir);
|
|
132
|
+
installBundledExtensions(this, raptorDir, manifest);
|
|
132
133
|
const installedAgents = detectInstalledAgents();
|
|
133
134
|
const primaryAgent = getAgentByKind(flags.ai);
|
|
134
135
|
if (primaryAgent && !installedAgents.some((a) => a.id === primaryAgent.id)) {
|
|
@@ -273,101 +274,6 @@ export default class Init extends BaseCommand {
|
|
|
273
274
|
}
|
|
274
275
|
return { status: "ok", installed, skipped };
|
|
275
276
|
}
|
|
276
|
-
copyShellScripts(_cwd, raptorDir, manifest) {
|
|
277
|
-
const pkgRoot = this.findRaptorPackageRoot();
|
|
278
|
-
if (!pkgRoot) {
|
|
279
|
-
this.warn("Could not locate Raptor package root — shell scripts not copied.");
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const copied = [];
|
|
283
|
-
const summary = [];
|
|
284
|
-
const shells = [
|
|
285
|
-
{ dir: "bash", ext: ".sh" },
|
|
286
|
-
{ dir: "powershell", ext: ".ps1" },
|
|
287
|
-
];
|
|
288
|
-
for (const shell of shells) {
|
|
289
|
-
const sourceDir = join(pkgRoot, "scripts", shell.dir);
|
|
290
|
-
const targetDir = join(raptorDir, "scripts", shell.dir);
|
|
291
|
-
if (!existsSync(sourceDir))
|
|
292
|
-
continue;
|
|
293
|
-
mkdirSync(targetDir, { recursive: true });
|
|
294
|
-
const files = readdirSync(sourceDir).filter((f) => f.endsWith(shell.ext));
|
|
295
|
-
for (const file of files) {
|
|
296
|
-
const src = join(sourceDir, file);
|
|
297
|
-
const dest = join(targetDir, file);
|
|
298
|
-
copyFileSync(src, dest);
|
|
299
|
-
if (shell.ext === ".sh")
|
|
300
|
-
chmodSync(dest, 0o755);
|
|
301
|
-
copied.push(`scripts/${shell.dir}/${file}`);
|
|
302
|
-
}
|
|
303
|
-
if (files.length > 0) {
|
|
304
|
-
summary.push(`${files.length} ${shell.dir}`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
manifest.scripts_installed = copied;
|
|
308
|
-
writeFileSync(join(raptorDir, "raptor.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
309
|
-
if (summary.length > 0) {
|
|
310
|
-
this.log(` Scripts: ${summary.join(", ")}`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
copyTemplates(raptorDir) {
|
|
314
|
-
const pkgRoot = this.findRaptorPackageRoot();
|
|
315
|
-
if (!pkgRoot)
|
|
316
|
-
return;
|
|
317
|
-
const srcTemplates = join(pkgRoot, "templates");
|
|
318
|
-
if (!existsSync(srcTemplates))
|
|
319
|
-
return;
|
|
320
|
-
const srcCommands = join(srcTemplates, "commands");
|
|
321
|
-
if (existsSync(srcCommands)) {
|
|
322
|
-
const destCommands = join(raptorDir, "templates", "commands");
|
|
323
|
-
mkdirSync(destCommands, { recursive: true });
|
|
324
|
-
for (const f of readdirSync(srcCommands).filter((f) => f.endsWith(".md"))) {
|
|
325
|
-
copyFileSync(join(srcCommands, f), join(destCommands, f));
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
const destTemplates = join(raptorDir, "templates");
|
|
329
|
-
mkdirSync(destTemplates, { recursive: true });
|
|
330
|
-
for (const f of readdirSync(srcTemplates).filter((f) => f.endsWith("-template.md"))) {
|
|
331
|
-
copyFileSync(join(srcTemplates, f), join(destTemplates, f));
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
installBundledExtensions(raptorDir, manifest) {
|
|
335
|
-
const pkgRoot = this.findRaptorPackageRoot();
|
|
336
|
-
if (!pkgRoot)
|
|
337
|
-
return;
|
|
338
|
-
const sourceExtRoot = join(pkgRoot, "extensions");
|
|
339
|
-
if (!existsSync(sourceExtRoot))
|
|
340
|
-
return;
|
|
341
|
-
const registered = [];
|
|
342
|
-
for (const extId of readdirSync(sourceExtRoot)) {
|
|
343
|
-
const sourceExt = join(sourceExtRoot, extId);
|
|
344
|
-
const manifestFile = join(sourceExt, "extension.yml");
|
|
345
|
-
if (!existsSync(manifestFile))
|
|
346
|
-
continue;
|
|
347
|
-
const targetExt = join(raptorDir, "extensions", extId);
|
|
348
|
-
copyDirRecursive(sourceExt, targetExt);
|
|
349
|
-
registered.push(extId);
|
|
350
|
-
}
|
|
351
|
-
if (registered.length === 0)
|
|
352
|
-
return;
|
|
353
|
-
manifest.extensions_registered = registered;
|
|
354
|
-
writeFileSync(join(raptorDir, "raptor.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
355
|
-
this.log(` Ext: ${registered.length} bundled (${registered.join(", ")})`);
|
|
356
|
-
}
|
|
357
|
-
findRaptorPackageRoot() {
|
|
358
|
-
let dir = dirname(new URL(import.meta.url).pathname);
|
|
359
|
-
for (let i = 0; i < 10; i++) {
|
|
360
|
-
if (existsSync(join(dir, "pnpm-workspace.yaml")) ||
|
|
361
|
-
existsSync(join(dir, "scripts", "bash"))) {
|
|
362
|
-
return dir;
|
|
363
|
-
}
|
|
364
|
-
const parent = dirname(dir);
|
|
365
|
-
if (parent === dir)
|
|
366
|
-
break;
|
|
367
|
-
dir = parent;
|
|
368
|
-
}
|
|
369
|
-
return null;
|
|
370
|
-
}
|
|
371
277
|
}
|
|
372
278
|
function describeCi(r) {
|
|
373
279
|
if (r.status === "disabled")
|
|
@@ -388,16 +294,3 @@ function describeHooks(r) {
|
|
|
388
294
|
parts.push(`skipped (already exist): ${r.skipped.join(", ")}`);
|
|
389
295
|
return parts.length > 0 ? parts.join("; ") : "nothing to do";
|
|
390
296
|
}
|
|
391
|
-
function copyDirRecursive(source, target) {
|
|
392
|
-
mkdirSync(target, { recursive: true });
|
|
393
|
-
for (const entry of readdirSync(source)) {
|
|
394
|
-
const src = join(source, entry);
|
|
395
|
-
const dest = join(target, entry);
|
|
396
|
-
if (statSync(src).isDirectory()) {
|
|
397
|
-
copyDirRecursive(src, dest);
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
copyFileSync(src, dest);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
package/dist/commands/new.js
CHANGED
|
@@ -113,7 +113,26 @@ export default class New extends BaseCommand {
|
|
|
113
113
|
const specNeedsSeeding = !existsSync(specPath);
|
|
114
114
|
let jiraContext = null;
|
|
115
115
|
if (specNeedsSeeding && flags.jira && flags["jira-fetch"]) {
|
|
116
|
-
|
|
116
|
+
const result = await this.fetchJiraContext(root, flags.jira);
|
|
117
|
+
if (result.ok) {
|
|
118
|
+
jiraContext = result.context;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this.error([
|
|
122
|
+
`${result.reason}`,
|
|
123
|
+
``,
|
|
124
|
+
`Aborting: --jira=${flags.jira} was given but its Jira card could not be read,`,
|
|
125
|
+
`so there is no card content to seed the spec from. No spec or branch was`,
|
|
126
|
+
`created — a spec stamped with a Jira ID but no Jira content would be misleading.`,
|
|
127
|
+
``,
|
|
128
|
+
`To fix, do one of:`,
|
|
129
|
+
` • Connect Jira, then retry:`,
|
|
130
|
+
` raptor jira connect`,
|
|
131
|
+
` (or configure the Jira MCP server under 'jira:' in .raptor/raptor.yml)`,
|
|
132
|
+
` • Record the ticket ID only, without fetching the card:`,
|
|
133
|
+
` raptor new ${args.slug} --jira=${flags.jira} --no-jira-fetch`,
|
|
134
|
+
].join("\n"), { exit: 1 });
|
|
135
|
+
}
|
|
117
136
|
}
|
|
118
137
|
const designText = specNeedsSeeding
|
|
119
138
|
? [
|
|
@@ -271,9 +290,7 @@ export default class New extends BaseCommand {
|
|
|
271
290
|
? "seeded spec"
|
|
272
291
|
: !specNeedsSeeding
|
|
273
292
|
? "id recorded — spec already authored, not re-seeded"
|
|
274
|
-
:
|
|
275
|
-
? "id only (fetch unavailable)"
|
|
276
|
-
: "id only";
|
|
293
|
+
: "id only";
|
|
277
294
|
this.log(` Jira: ${flags.jira} (${tag})`);
|
|
278
295
|
}
|
|
279
296
|
if (designContext.hasDesign) {
|
|
@@ -416,28 +433,27 @@ export default class New extends BaseCommand {
|
|
|
416
433
|
async fetchJiraContext(root, key) {
|
|
417
434
|
const conn = jiraConn(readConfig(root));
|
|
418
435
|
if (!conn) {
|
|
419
|
-
|
|
420
|
-
return null;
|
|
436
|
+
return { ok: false, reason: `Jira is not connected.` };
|
|
421
437
|
}
|
|
422
438
|
if (!jiraReadyNonInteractive(conn)) {
|
|
423
|
-
|
|
424
|
-
return null;
|
|
439
|
+
return { ok: false, reason: `Jira credentials are missing.` };
|
|
425
440
|
}
|
|
426
441
|
let client;
|
|
427
442
|
try {
|
|
428
443
|
client = await openJiraClient(conn);
|
|
429
444
|
}
|
|
430
445
|
catch {
|
|
431
|
-
|
|
432
|
-
return null;
|
|
446
|
+
return { ok: false, reason: `Jira session expired.` };
|
|
433
447
|
}
|
|
434
448
|
try {
|
|
435
449
|
const issue = await client.getJiraIssue(conn.cloudId, key);
|
|
436
|
-
return mapIssueToSpecContext(issue);
|
|
450
|
+
return { ok: true, context: mapIssueToSpecContext(issue) };
|
|
437
451
|
}
|
|
438
452
|
catch (err) {
|
|
439
|
-
|
|
440
|
-
|
|
453
|
+
return {
|
|
454
|
+
ok: false,
|
|
455
|
+
reason: `Could not fetch ${key} from Jira (${err instanceof Error ? err.message : String(err)}).`,
|
|
456
|
+
};
|
|
441
457
|
}
|
|
442
458
|
finally {
|
|
443
459
|
await client.close();
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,143 +1,292 @@
|
|
|
1
1
|
import { Flags } from "@oclif/core";
|
|
2
2
|
import { BaseCommand } from "../base-command.js";
|
|
3
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { MANIFEST_SCHEMA_VERSION, validateManifest, VERSION, } from "../_core/dist/index.js";
|
|
3
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { appendAuditEvent, buildContextBlock, coreHash, CORE_VERSION, createBackupSession, createManifest, detectInstalledAgents, extractCoreBlock, generateAgentsYaml, getAgentByKind, getPreset, hashString, loadAgentsConfig, MANIFEST_SCHEMA_VERSION, planSlashCommands, rewriteCoreBlock, upsertContextFile, validateManifest, VERSION, } from "../_core/dist/index.js";
|
|
6
|
+
import { collectFrameworkFiles, installBundledExtensions, installShellScripts, installTemplates, } from "../shared/scaffold.js";
|
|
7
|
+
import { currentActor, readInitOptions, restampYmlVersion, } from "../shared/project.js";
|
|
6
8
|
export default class Upgrade extends BaseCommand {
|
|
7
|
-
static description = "
|
|
9
|
+
static description = "Sync the project to the installed Raptor version (non-destructive)";
|
|
8
10
|
static examples = [
|
|
9
11
|
"<%= config.bin %> upgrade",
|
|
10
12
|
"<%= config.bin %> upgrade --dry-run",
|
|
11
13
|
];
|
|
12
14
|
static flags = {
|
|
13
15
|
"dry-run": Flags.boolean({
|
|
14
|
-
description: "Show what would
|
|
16
|
+
description: "Show what would change without writing anything",
|
|
15
17
|
default: false,
|
|
16
18
|
}),
|
|
17
19
|
};
|
|
18
20
|
async run() {
|
|
19
21
|
const { flags } = await this.parse(Upgrade);
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
+
const dry = flags["dry-run"];
|
|
23
|
+
const root = process.cwd();
|
|
24
|
+
const raptorDir = join(root, ".raptor");
|
|
22
25
|
if (!existsSync(raptorDir)) {
|
|
23
26
|
this.error("No .raptor/ directory found. Run 'raptor init' first.");
|
|
24
27
|
}
|
|
25
|
-
const manifestPath = join(raptorDir, "raptor.manifest.json");
|
|
26
|
-
const raptorYmlPath = join(raptorDir, "raptor.yml");
|
|
27
28
|
const changes = [];
|
|
29
|
+
const skipped = [];
|
|
30
|
+
const session = dry ? null : createBackupSession(root);
|
|
31
|
+
const backup = (abs) => {
|
|
32
|
+
session?.backup(abs);
|
|
33
|
+
};
|
|
34
|
+
const hashOf = (p) => existsSync(p) ? hashString(readFileSync(p, "utf8")) : "";
|
|
35
|
+
const manifestPath = join(raptorDir, "raptor.manifest.json");
|
|
36
|
+
const ymlPath = join(raptorDir, "raptor.yml");
|
|
37
|
+
const initOpts = readInitOptions(root);
|
|
38
|
+
let manifest;
|
|
28
39
|
if (!existsSync(manifestPath)) {
|
|
29
|
-
this.log("📦 raptor.manifest.json not found — creating from scratch.");
|
|
30
40
|
let projectName = "unknown";
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const nameMatch = yml.match(/project_name:\s*["']?([^"'\n]+)/);
|
|
36
|
-
const presetMatch = yml.match(/preset:\s*["']?([^"'\n]+)/);
|
|
37
|
-
if (nameMatch)
|
|
38
|
-
projectName = nameMatch[1].trim();
|
|
39
|
-
if (presetMatch)
|
|
40
|
-
preset = presetMatch[1].trim();
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
}
|
|
41
|
+
if (existsSync(ymlPath)) {
|
|
42
|
+
const m = readFileSync(ymlPath, "utf8").match(/name:\s*["']?([^"'\n]+)/);
|
|
43
|
+
if (m)
|
|
44
|
+
projectName = m[1].trim();
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
-
schema_version: MANIFEST_SCHEMA_VERSION,
|
|
46
|
+
manifest = createManifest({
|
|
47
47
|
raptor_version: VERSION,
|
|
48
48
|
project_name: projectName,
|
|
49
|
-
integration: "claude-code",
|
|
49
|
+
integration: initOpts?.ai ?? "claude-code",
|
|
50
50
|
script_type: "sh",
|
|
51
|
-
branch_numbering: "sequential",
|
|
52
|
-
preset,
|
|
51
|
+
branch_numbering: initOpts?.branch_numbering ?? "sequential",
|
|
52
|
+
preset: initOpts?.preset ?? "mobile-opinionated",
|
|
53
53
|
scripts_installed: [],
|
|
54
54
|
templates_installed: [],
|
|
55
55
|
extensions_registered: [],
|
|
56
|
-
|
|
57
|
-
};
|
|
56
|
+
});
|
|
58
57
|
changes.push("CREATE raptor.manifest.json (migrated from raptor.yml)");
|
|
59
|
-
if (!flags["dry-run"]) {
|
|
60
|
-
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
61
|
-
}
|
|
62
58
|
}
|
|
63
59
|
else {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.warn(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
61
|
+
const result = validateManifest(manifest);
|
|
62
|
+
if (!result.valid) {
|
|
63
|
+
for (const err of result.errors)
|
|
64
|
+
this.warn(`manifest: ${err}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (manifest.raptor_version !== VERSION) {
|
|
68
|
+
changes.push(`STAMP raptor_version: ${manifest.raptor_version} → ${VERSION}`);
|
|
69
|
+
}
|
|
70
|
+
if (manifest.schema_version !== MANIFEST_SCHEMA_VERSION) {
|
|
71
|
+
changes.push(`BUMP schema_version: ${manifest.schema_version} → ${MANIFEST_SCHEMA_VERSION}`);
|
|
72
|
+
}
|
|
73
|
+
manifest.raptor_version = VERSION;
|
|
74
|
+
manifest.schema_version = MANIFEST_SCHEMA_VERSION;
|
|
75
|
+
const projectName = typeof manifest.project_name === "string"
|
|
76
|
+
? manifest.project_name
|
|
77
|
+
: "project";
|
|
78
|
+
const presetId = typeof manifest.preset === "string" ? manifest.preset : undefined;
|
|
79
|
+
if (existsSync(ymlPath)) {
|
|
80
|
+
const raw = readFileSync(ymlPath, "utf8");
|
|
81
|
+
let next = restampYmlVersion(raw, VERSION);
|
|
82
|
+
next = next.replace(/\nagents:\s*\[\]\n(?:#[^\n]*\n)*/, "\n# Agent configuration lives in agents.yml — run 'raptor add-agent <key>'.\n");
|
|
83
|
+
if (next !== raw) {
|
|
84
|
+
changes.push("UPDATE raptor.yml (version + legacy cleanup)");
|
|
85
|
+
if (!dry) {
|
|
86
|
+
backup(ymlPath);
|
|
87
|
+
writeFileSync(ymlPath, next);
|
|
73
88
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const fw = collectFrameworkFiles(raptorDir);
|
|
92
|
+
const changedFw = fw.filter((f) => !existsSync(f.dest) || hashOf(f.src) !== hashOf(f.dest));
|
|
93
|
+
if (changedFw.length > 0) {
|
|
94
|
+
changes.push(`REFRESH ${changedFw.length} framework file(s)`);
|
|
95
|
+
if (!dry) {
|
|
96
|
+
for (const f of changedFw)
|
|
97
|
+
backup(f.dest);
|
|
98
|
+
installShellScripts(this, raptorDir, manifest);
|
|
99
|
+
installTemplates(raptorDir);
|
|
100
|
+
installBundledExtensions(this, raptorDir, manifest);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const agents = this.resolveAgents(root, initOpts?.ai);
|
|
104
|
+
const presetArticles = presetId
|
|
105
|
+
? getPreset(presetId)?.articles.map((a) => a.id)
|
|
106
|
+
: undefined;
|
|
107
|
+
let slashChanged = 0;
|
|
108
|
+
let ctxChanged = 0;
|
|
109
|
+
for (const adapter of agents) {
|
|
110
|
+
if (adapter.id === "human")
|
|
111
|
+
continue;
|
|
112
|
+
const plan = planSlashCommands({
|
|
113
|
+
projectRoot: root,
|
|
114
|
+
projectName,
|
|
115
|
+
adapter,
|
|
116
|
+
...(presetArticles ? { constitutionArticles: presetArticles } : {}),
|
|
117
|
+
...(presetId ? { presetId } : {}),
|
|
118
|
+
});
|
|
119
|
+
for (const c of plan.commands) {
|
|
120
|
+
if (existsSync(c.path) && hashOf(c.path) === c.hash)
|
|
121
|
+
continue;
|
|
122
|
+
slashChanged++;
|
|
123
|
+
if (!dry) {
|
|
124
|
+
backup(c.path);
|
|
125
|
+
mkdirSync(dirname(c.path), { recursive: true });
|
|
126
|
+
writeFileSync(c.path, c.content);
|
|
82
127
|
}
|
|
83
128
|
}
|
|
84
|
-
|
|
85
|
-
|
|
129
|
+
if (adapter.contextFile) {
|
|
130
|
+
const ctxPath = join(root, adapter.contextFile);
|
|
131
|
+
const before = existsSync(ctxPath) ? readFileSync(ctxPath, "utf8") : "";
|
|
132
|
+
const block = buildContextBlock({
|
|
133
|
+
projectName,
|
|
134
|
+
...(presetId ? { preset: presetId } : {}),
|
|
135
|
+
...(presetArticles ? { constitutionArticles: presetArticles } : {}),
|
|
136
|
+
});
|
|
137
|
+
if (!before.includes(block)) {
|
|
138
|
+
ctxChanged++;
|
|
139
|
+
if (!dry)
|
|
140
|
+
upsertContextFile(ctxPath, block);
|
|
141
|
+
}
|
|
86
142
|
}
|
|
87
143
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
144
|
+
if (slashChanged > 0) {
|
|
145
|
+
changes.push(`REFRESH ${slashChanged} slash command file(s)`);
|
|
146
|
+
}
|
|
147
|
+
if (ctxChanged > 0) {
|
|
148
|
+
changes.push(`SYNC ${ctxChanged} agent context file(s)`);
|
|
149
|
+
}
|
|
150
|
+
const constPath = join(raptorDir, "constitution.md");
|
|
151
|
+
if (existsSync(constPath)) {
|
|
152
|
+
const src = readFileSync(constPath, "utf8");
|
|
153
|
+
const ex = extractCoreBlock(src);
|
|
154
|
+
if (ex?.isCanonical) {
|
|
155
|
+
}
|
|
156
|
+
else if (ex && !ex.isIntact) {
|
|
157
|
+
this.warn("Constitution CORE block was hand-edited (hash mismatch) — skipping. Run 'raptor repair constitution --confirm' first.");
|
|
158
|
+
skipped.push("constitution CORE (tampered)");
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const actor = currentActor("human");
|
|
162
|
+
if (!actor.email) {
|
|
163
|
+
this.warn("Constitution CORE is outdated but no `git config user.email` is set (C5) — skipping. Set it and re-run to upgrade the CORE.");
|
|
164
|
+
skipped.push("constitution CORE (no C5 approver)");
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
changes.push(`UPGRADE constitution CORE → v${CORE_VERSION}`);
|
|
168
|
+
if (!dry) {
|
|
169
|
+
const memPath = join(raptorDir, "memory", "constitution.md");
|
|
170
|
+
backup(constPath);
|
|
171
|
+
if (existsSync(memPath))
|
|
172
|
+
backup(memPath);
|
|
173
|
+
const prevHash = hashString(src);
|
|
174
|
+
const next = rewriteCoreBlock(src);
|
|
175
|
+
writeFileSync(constPath, next);
|
|
176
|
+
mkdirSync(dirname(memPath), { recursive: true });
|
|
177
|
+
writeFileSync(memPath, next);
|
|
178
|
+
appendAuditEvent(join(raptorDir, "memory", "amendments.log"), {
|
|
179
|
+
event: "constitution.upgraded",
|
|
180
|
+
command: "raptor upgrade",
|
|
181
|
+
actor: { user: actor.user, via: actor.via },
|
|
182
|
+
article: "C4",
|
|
183
|
+
inputs: [
|
|
184
|
+
{
|
|
185
|
+
path: ".raptor/constitution.md",
|
|
186
|
+
hash: prevHash,
|
|
187
|
+
role: "constitution",
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
outputs: [
|
|
191
|
+
{
|
|
192
|
+
path: ".raptor/constitution.md",
|
|
193
|
+
hash: hashString(next),
|
|
194
|
+
role: "constitution",
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
meta: {
|
|
198
|
+
previous_version: ex?.version ?? null,
|
|
199
|
+
new_version: CORE_VERSION,
|
|
200
|
+
new_block_hash: coreHash(),
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
95
205
|
}
|
|
96
206
|
}
|
|
97
207
|
const initOptionsPath = join(raptorDir, "init-options.json");
|
|
98
208
|
if (!existsSync(initOptionsPath)) {
|
|
99
|
-
changes.push("CREATE init-options.json
|
|
100
|
-
if (!
|
|
209
|
+
changes.push("CREATE init-options.json");
|
|
210
|
+
if (!dry) {
|
|
101
211
|
writeFileSync(initOptionsPath, JSON.stringify({
|
|
102
|
-
ai: "claude-code",
|
|
212
|
+
ai: initOpts?.ai ?? "claude-code",
|
|
103
213
|
script_type: "sh",
|
|
104
214
|
branch_numbering: "sequential",
|
|
105
|
-
preset: "mobile-opinionated",
|
|
215
|
+
preset: presetId ?? "mobile-opinionated",
|
|
106
216
|
no_git: false,
|
|
107
217
|
created_at: new Date().toISOString(),
|
|
108
218
|
}, null, 2) + "\n");
|
|
109
219
|
}
|
|
110
220
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const cleaned = ymlContent.replace(/\nagents:\s*\[\]\n(?:#[^\n]*\n)*/, "\n# Agent configuration lives in agents.yml — do not add agents here.\n# Run 'raptor add-agent <key>' to configure agents.\n");
|
|
118
|
-
writeFileSync(raptorYmlPath, cleaned);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
221
|
+
const memC = join(raptorDir, "memory", "constitution.md");
|
|
222
|
+
if (!existsSync(memC) && existsSync(constPath)) {
|
|
223
|
+
changes.push("COPY constitution.md → memory/constitution.md");
|
|
224
|
+
if (!dry) {
|
|
225
|
+
mkdirSync(dirname(memC), { recursive: true });
|
|
226
|
+
copyFileSync(constPath, memC);
|
|
121
227
|
}
|
|
122
|
-
|
|
228
|
+
}
|
|
229
|
+
const agentsYmlPath = join(raptorDir, "agents.yml");
|
|
230
|
+
if (!existsSync(agentsYmlPath)) {
|
|
231
|
+
changes.push("CREATE agents.yml");
|
|
232
|
+
if (!dry) {
|
|
233
|
+
writeFileSync(agentsYmlPath, generateAgentsYaml(agents.map((a) => ({
|
|
234
|
+
id: a.id,
|
|
235
|
+
kind: a.id,
|
|
236
|
+
capabilities: [...a.capabilities],
|
|
237
|
+
}))));
|
|
123
238
|
}
|
|
124
239
|
}
|
|
240
|
+
if (!dry) {
|
|
241
|
+
manifest.updated_at = new Date().toISOString();
|
|
242
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
243
|
+
session?.discardIfEmpty();
|
|
244
|
+
}
|
|
125
245
|
if (changes.length === 0) {
|
|
126
246
|
this.log("✓ Project is up to date. No changes needed.");
|
|
247
|
+
for (const s of skipped)
|
|
248
|
+
this.log(` ⊘ skipped: ${s}`);
|
|
127
249
|
return;
|
|
128
250
|
}
|
|
129
|
-
if (
|
|
130
|
-
this.log("\nDRY RUN — would apply
|
|
131
|
-
for (const
|
|
132
|
-
this.log(` • ${
|
|
133
|
-
|
|
251
|
+
if (dry) {
|
|
252
|
+
this.log("\nDRY RUN — would apply:\n");
|
|
253
|
+
for (const c of changes)
|
|
254
|
+
this.log(` • ${c}`);
|
|
255
|
+
for (const s of skipped)
|
|
256
|
+
this.log(` ⊘ skipped: ${s}`);
|
|
134
257
|
this.log("\nRe-run without --dry-run to apply.");
|
|
135
258
|
}
|
|
136
259
|
else {
|
|
137
|
-
this.log("\n✓ Upgrade complete
|
|
138
|
-
for (const
|
|
139
|
-
this.log(` ✓ ${
|
|
260
|
+
this.log("\n✓ Upgrade complete:\n");
|
|
261
|
+
for (const c of changes)
|
|
262
|
+
this.log(` ✓ ${c}`);
|
|
263
|
+
for (const s of skipped)
|
|
264
|
+
this.log(` ⊘ skipped: ${s}`);
|
|
265
|
+
if (session && existsSync(session.dir)) {
|
|
266
|
+
this.log(`\n 💾 Previous versions backed up to ${session.dir}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
resolveAgents(root, primaryAi) {
|
|
271
|
+
const cfg = loadAgentsConfig(root);
|
|
272
|
+
if (cfg?.agents?.length) {
|
|
273
|
+
const seen = new Set();
|
|
274
|
+
const out = [];
|
|
275
|
+
for (const a of cfg.agents) {
|
|
276
|
+
const adapter = getAgentByKind(a.kind ?? a.id);
|
|
277
|
+
if (adapter && !seen.has(adapter.id)) {
|
|
278
|
+
seen.add(adapter.id);
|
|
279
|
+
out.push(adapter);
|
|
280
|
+
}
|
|
140
281
|
}
|
|
282
|
+
if (out.length)
|
|
283
|
+
return out;
|
|
141
284
|
}
|
|
285
|
+
const primary = primaryAi ? getAgentByKind(primaryAi) : undefined;
|
|
286
|
+
const detected = detectInstalledAgents();
|
|
287
|
+
const out = primary
|
|
288
|
+
? [primary, ...detected.filter((a) => a.id !== primary.id)]
|
|
289
|
+
: detected;
|
|
290
|
+
return out;
|
|
142
291
|
}
|
|
143
292
|
}
|
package/dist/shared/project.js
CHANGED
|
@@ -31,6 +31,12 @@ export function writeConfig(root, config) {
|
|
|
31
31
|
const path = join(root, '.raptor', 'raptor.yml');
|
|
32
32
|
writeFileSync(path, stringifyYaml(config));
|
|
33
33
|
}
|
|
34
|
+
export function restampYmlVersion(raw, version) {
|
|
35
|
+
if (/^version:.*$/m.test(raw)) {
|
|
36
|
+
return raw.replace(/^version:.*$/m, `version: ${version}`);
|
|
37
|
+
}
|
|
38
|
+
return `version: ${version}\n${raw}`;
|
|
39
|
+
}
|
|
34
40
|
export function readInitOptions(root) {
|
|
35
41
|
const path = join(root, '.raptor', 'init-options.json');
|
|
36
42
|
if (!existsSync(path))
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { dirname, join, relative } from "node:path";
|
|
3
|
+
export function findRaptorPackageRoot() {
|
|
4
|
+
let dir = dirname(new URL(import.meta.url).pathname);
|
|
5
|
+
for (let i = 0; i < 10; i++) {
|
|
6
|
+
if (existsSync(join(dir, "pnpm-workspace.yaml")) ||
|
|
7
|
+
existsSync(join(dir, "scripts", "bash"))) {
|
|
8
|
+
return dir;
|
|
9
|
+
}
|
|
10
|
+
const parent = dirname(dir);
|
|
11
|
+
if (parent === dir)
|
|
12
|
+
break;
|
|
13
|
+
dir = parent;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
function walkFiles(dir, onFile) {
|
|
18
|
+
for (const entry of readdirSync(dir)) {
|
|
19
|
+
const abs = join(dir, entry);
|
|
20
|
+
if (statSync(abs).isDirectory())
|
|
21
|
+
walkFiles(abs, onFile);
|
|
22
|
+
else
|
|
23
|
+
onFile(abs);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function collectFrameworkFiles(raptorDir) {
|
|
27
|
+
const pkgRoot = findRaptorPackageRoot();
|
|
28
|
+
const out = [];
|
|
29
|
+
if (!pkgRoot)
|
|
30
|
+
return out;
|
|
31
|
+
for (const { dir, ext } of [
|
|
32
|
+
{ dir: "bash", ext: ".sh" },
|
|
33
|
+
{ dir: "powershell", ext: ".ps1" },
|
|
34
|
+
]) {
|
|
35
|
+
const s = join(pkgRoot, "scripts", dir);
|
|
36
|
+
if (!existsSync(s))
|
|
37
|
+
continue;
|
|
38
|
+
for (const f of readdirSync(s).filter((f) => f.endsWith(ext))) {
|
|
39
|
+
out.push({
|
|
40
|
+
src: join(s, f),
|
|
41
|
+
dest: join(raptorDir, "scripts", dir, f),
|
|
42
|
+
exec: ext === ".sh",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const sc = join(pkgRoot, "templates", "commands");
|
|
47
|
+
if (existsSync(sc)) {
|
|
48
|
+
for (const f of readdirSync(sc).filter((f) => f.endsWith(".md"))) {
|
|
49
|
+
out.push({ src: join(sc, f), dest: join(raptorDir, "templates", "commands", f) });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const st = join(pkgRoot, "templates");
|
|
53
|
+
if (existsSync(st)) {
|
|
54
|
+
for (const f of readdirSync(st).filter((f) => f.endsWith("-template.md"))) {
|
|
55
|
+
out.push({ src: join(st, f), dest: join(raptorDir, "templates", f) });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const se = join(pkgRoot, "extensions");
|
|
59
|
+
if (existsSync(se)) {
|
|
60
|
+
for (const extId of readdirSync(se)) {
|
|
61
|
+
const sed = join(se, extId);
|
|
62
|
+
if (!existsSync(join(sed, "extension.yml")))
|
|
63
|
+
continue;
|
|
64
|
+
walkFiles(sed, (abs) => out.push({
|
|
65
|
+
src: abs,
|
|
66
|
+
dest: join(raptorDir, "extensions", extId, relative(sed, abs)),
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
export function copyDirRecursive(source, target) {
|
|
73
|
+
mkdirSync(target, { recursive: true });
|
|
74
|
+
for (const entry of readdirSync(source)) {
|
|
75
|
+
const src = join(source, entry);
|
|
76
|
+
const dest = join(target, entry);
|
|
77
|
+
if (statSync(src).isDirectory()) {
|
|
78
|
+
copyDirRecursive(src, dest);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
copyFileSync(src, dest);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function installShellScripts(cmd, raptorDir, manifest) {
|
|
86
|
+
const pkgRoot = findRaptorPackageRoot();
|
|
87
|
+
if (!pkgRoot) {
|
|
88
|
+
cmd.warn("Could not locate Raptor package root — shell scripts not copied.");
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
const copied = [];
|
|
92
|
+
const summary = [];
|
|
93
|
+
const shells = [
|
|
94
|
+
{ dir: "bash", ext: ".sh" },
|
|
95
|
+
{ dir: "powershell", ext: ".ps1" },
|
|
96
|
+
];
|
|
97
|
+
for (const shell of shells) {
|
|
98
|
+
const sourceDir = join(pkgRoot, "scripts", shell.dir);
|
|
99
|
+
const targetDir = join(raptorDir, "scripts", shell.dir);
|
|
100
|
+
if (!existsSync(sourceDir))
|
|
101
|
+
continue;
|
|
102
|
+
mkdirSync(targetDir, { recursive: true });
|
|
103
|
+
const files = readdirSync(sourceDir).filter((f) => f.endsWith(shell.ext));
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
const dest = join(targetDir, file);
|
|
106
|
+
copyFileSync(join(sourceDir, file), dest);
|
|
107
|
+
if (shell.ext === ".sh")
|
|
108
|
+
chmodSync(dest, 0o755);
|
|
109
|
+
copied.push(`scripts/${shell.dir}/${file}`);
|
|
110
|
+
}
|
|
111
|
+
if (files.length > 0)
|
|
112
|
+
summary.push(`${files.length} ${shell.dir}`);
|
|
113
|
+
}
|
|
114
|
+
manifest.scripts_installed = copied;
|
|
115
|
+
writeFileSync(join(raptorDir, "raptor.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
116
|
+
if (summary.length > 0)
|
|
117
|
+
cmd.log(` Scripts: ${summary.join(", ")}`);
|
|
118
|
+
return copied;
|
|
119
|
+
}
|
|
120
|
+
export function installTemplates(raptorDir) {
|
|
121
|
+
const pkgRoot = findRaptorPackageRoot();
|
|
122
|
+
if (!pkgRoot)
|
|
123
|
+
return;
|
|
124
|
+
const srcTemplates = join(pkgRoot, "templates");
|
|
125
|
+
if (!existsSync(srcTemplates))
|
|
126
|
+
return;
|
|
127
|
+
const srcCommands = join(srcTemplates, "commands");
|
|
128
|
+
if (existsSync(srcCommands)) {
|
|
129
|
+
const destCommands = join(raptorDir, "templates", "commands");
|
|
130
|
+
mkdirSync(destCommands, { recursive: true });
|
|
131
|
+
for (const f of readdirSync(srcCommands).filter((f) => f.endsWith(".md"))) {
|
|
132
|
+
copyFileSync(join(srcCommands, f), join(destCommands, f));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const destTemplates = join(raptorDir, "templates");
|
|
136
|
+
mkdirSync(destTemplates, { recursive: true });
|
|
137
|
+
for (const f of readdirSync(srcTemplates).filter((f) => f.endsWith("-template.md"))) {
|
|
138
|
+
copyFileSync(join(srcTemplates, f), join(destTemplates, f));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export function installBundledExtensions(cmd, raptorDir, manifest) {
|
|
142
|
+
const pkgRoot = findRaptorPackageRoot();
|
|
143
|
+
if (!pkgRoot)
|
|
144
|
+
return [];
|
|
145
|
+
const sourceExtRoot = join(pkgRoot, "extensions");
|
|
146
|
+
if (!existsSync(sourceExtRoot))
|
|
147
|
+
return [];
|
|
148
|
+
const registered = [];
|
|
149
|
+
for (const extId of readdirSync(sourceExtRoot)) {
|
|
150
|
+
const sourceExt = join(sourceExtRoot, extId);
|
|
151
|
+
if (!existsSync(join(sourceExt, "extension.yml")))
|
|
152
|
+
continue;
|
|
153
|
+
copyDirRecursive(sourceExt, join(raptorDir, "extensions", extId));
|
|
154
|
+
registered.push(extId);
|
|
155
|
+
}
|
|
156
|
+
if (registered.length === 0)
|
|
157
|
+
return [];
|
|
158
|
+
manifest.extensions_registered = registered;
|
|
159
|
+
writeFileSync(join(raptorDir, "raptor.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
160
|
+
cmd.log(` Ext: ${registered.length} bundled (${registered.join(", ")})`);
|
|
161
|
+
return registered;
|
|
162
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "raptor-aios",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
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