@arvorco/relentless 0.1.27 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/relentless.ts CHANGED
@@ -427,6 +427,127 @@ agents
427
427
  }
428
428
  });
429
429
 
430
+ // Queue commands
431
+ const queue = program.command("queue").description("Manage queue for mid-run guidance");
432
+
433
+ queue
434
+ .command("add <message>")
435
+ .description("Add a message or command to the queue")
436
+ .requiredOption("-f, --feature <name>", "Feature name")
437
+ .option("-d, --dir <path>", "Project directory", process.cwd())
438
+ .action(async (message, options) => {
439
+ const { queueAdd, resolveFeaturePath } = await import("../src/cli/queue");
440
+
441
+ const resolved = await resolveFeaturePath(options.dir, options.feature);
442
+ if (resolved.error) {
443
+ console.error(chalk.red(resolved.error));
444
+ process.exit(1);
445
+ }
446
+
447
+ const result = await queueAdd({
448
+ message,
449
+ featurePath: resolved.path!,
450
+ });
451
+
452
+ if (result.success) {
453
+ console.log(chalk.green(`✓ ${result.message}`));
454
+ } else {
455
+ console.error(chalk.red(`Error: ${result.error}`));
456
+ process.exit(1);
457
+ }
458
+ });
459
+
460
+ queue
461
+ .command("list")
462
+ .description("List queue contents for a feature")
463
+ .requiredOption("-f, --feature <name>", "Feature name")
464
+ .option("-d, --dir <path>", "Project directory", process.cwd())
465
+ .option("-a, --all", "Show all items including processed", false)
466
+ .action(async (options) => {
467
+ const { queueList, formatQueueList, resolveFeaturePath } = await import("../src/cli/queue");
468
+
469
+ const resolved = await resolveFeaturePath(options.dir, options.feature);
470
+ if (resolved.error) {
471
+ console.error(chalk.red(resolved.error));
472
+ process.exit(1);
473
+ }
474
+
475
+ const result = await queueList({
476
+ featurePath: resolved.path!,
477
+ showAll: options.all,
478
+ });
479
+
480
+ if (result.success) {
481
+ const output = formatQueueList({
482
+ ...result,
483
+ featureName: options.feature,
484
+ });
485
+ console.log(output);
486
+ } else {
487
+ console.error(chalk.red(`Error: ${result.error}`));
488
+ process.exit(1);
489
+ }
490
+ });
491
+
492
+ queue
493
+ .command("remove <index>")
494
+ .description("Remove an item from the queue by index (1-based)")
495
+ .requiredOption("-f, --feature <name>", "Feature name")
496
+ .option("-d, --dir <path>", "Project directory", process.cwd())
497
+ .action(async (index, options) => {
498
+ const { queueRemove, resolveFeaturePath } = await import("../src/cli/queue");
499
+
500
+ const resolved = await resolveFeaturePath(options.dir, options.feature);
501
+ if (resolved.error) {
502
+ console.error(chalk.red(resolved.error));
503
+ process.exit(1);
504
+ }
505
+
506
+ const parsedIndex = parseInt(index, 10);
507
+ if (isNaN(parsedIndex)) {
508
+ console.error(chalk.red(`Invalid index: ${index}. Must be a number`));
509
+ process.exit(1);
510
+ }
511
+
512
+ const result = await queueRemove({
513
+ index: parsedIndex,
514
+ featurePath: resolved.path!,
515
+ });
516
+
517
+ if (result.success) {
518
+ console.log(chalk.green(`✓ ${result.message}`));
519
+ } else {
520
+ console.error(chalk.red(`Error: ${result.error}`));
521
+ process.exit(1);
522
+ }
523
+ });
524
+
525
+ queue
526
+ .command("clear")
527
+ .description("Clear all items from the queue")
528
+ .requiredOption("-f, --feature <name>", "Feature name")
529
+ .option("-d, --dir <path>", "Project directory", process.cwd())
530
+ .action(async (options) => {
531
+ const { queueClear, resolveFeaturePath } = await import("../src/cli/queue");
532
+
533
+ const resolved = await resolveFeaturePath(options.dir, options.feature);
534
+ if (resolved.error) {
535
+ console.error(chalk.red(resolved.error));
536
+ process.exit(1);
537
+ }
538
+
539
+ const result = await queueClear({
540
+ featurePath: resolved.path!,
541
+ });
542
+
543
+ if (result.success) {
544
+ console.log(chalk.green(`✓ ${result.message}`));
545
+ } else {
546
+ console.error(chalk.red(`Error: ${result.error}`));
547
+ process.exit(1);
548
+ }
549
+ });
550
+
430
551
  // Analyze command
431
552
  program
432
553
  .command("analyze")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arvorco/relentless",
3
- "version": "0.1.27",
3
+ "version": "0.3.0",
4
4
  "description": "Universal AI agent orchestrator - works with Claude Code, Amp, OpenCode, Codex, Droid, and Gemini",
5
5
  "type": "module",
6
6
  "publishConfig": {
package/src/agents/amp.ts CHANGED
@@ -94,22 +94,35 @@ export const ampAdapter: AgentAdapter = {
94
94
 
95
95
  async installSkills(projectPath: string): Promise<void> {
96
96
  // Amp can install skills globally via amp skill add
97
- // For project-local, we copy to the project
97
+ // For project-local, we copy to the project's .amp/skills/
98
98
  const skillsDir = `${projectPath}/.amp/skills`;
99
99
  await Bun.spawn(["mkdir", "-p", skillsDir]).exited;
100
100
 
101
101
  const relentlessRoot = import.meta.dir.replace("/src/agents", "");
102
- await Bun.spawn([
103
- "cp",
104
- "-r",
105
- `${relentlessRoot}/skills/prd`,
106
- `${skillsDir}/`,
107
- ]).exited;
108
- await Bun.spawn([
109
- "cp",
110
- "-r",
111
- `${relentlessRoot}/skills/relentless`,
112
- `${skillsDir}/`,
113
- ]).exited;
102
+ const sourceSkillsDir = `${relentlessRoot}/.claude/skills`;
103
+
104
+ // Copy all skills
105
+ const skills = [
106
+ "prd",
107
+ "relentless",
108
+ "constitution",
109
+ "specify",
110
+ "plan",
111
+ "tasks",
112
+ "checklist",
113
+ "clarify",
114
+ "analyze",
115
+ "implement",
116
+ "taskstoissues",
117
+ ];
118
+
119
+ for (const skill of skills) {
120
+ await Bun.spawn([
121
+ "cp",
122
+ "-r",
123
+ `${sourceSkillsDir}/${skill}`,
124
+ `${skillsDir}/`,
125
+ ]).exited;
126
+ }
114
127
  },
115
128
  };
@@ -10,7 +10,8 @@ import type { AgentAdapter, AgentResult, InvokeOptions, RateLimitInfo } from "./
10
10
  export const codexAdapter: AgentAdapter = {
11
11
  name: "codex",
12
12
  displayName: "OpenAI Codex",
13
- hasSkillSupport: false, // Uses SKILL.md but requires manual setup
13
+ hasSkillSupport: true,
14
+ skillInstallCommand: "codex skill add <skill-name>",
14
15
 
15
16
  async isInstalled(): Promise<boolean> {
16
17
  try {
@@ -86,4 +87,37 @@ export const codexAdapter: AgentAdapter = {
86
87
 
87
88
  return { limited: false };
88
89
  },
90
+
91
+ async installSkills(projectPath: string): Promise<void> {
92
+ // Codex uses .codex/skills/ for project-level skills
93
+ const skillsDir = `${projectPath}/.codex/skills`;
94
+ await Bun.spawn(["mkdir", "-p", skillsDir]).exited;
95
+
96
+ const relentlessRoot = import.meta.dir.replace("/src/agents", "");
97
+ const sourceSkillsDir = `${relentlessRoot}/.claude/skills`;
98
+
99
+ // Copy all skills
100
+ const skills = [
101
+ "prd",
102
+ "relentless",
103
+ "constitution",
104
+ "specify",
105
+ "plan",
106
+ "tasks",
107
+ "checklist",
108
+ "clarify",
109
+ "analyze",
110
+ "implement",
111
+ "taskstoissues",
112
+ ];
113
+
114
+ for (const skill of skills) {
115
+ await Bun.spawn([
116
+ "cp",
117
+ "-r",
118
+ `${sourceSkillsDir}/${skill}`,
119
+ `${skillsDir}/`,
120
+ ]).exited;
121
+ }
122
+ },
89
123
  };
@@ -10,7 +10,8 @@ import type { AgentAdapter, AgentResult, InvokeOptions, RateLimitInfo } from "./
10
10
  export const droidAdapter: AgentAdapter = {
11
11
  name: "droid",
12
12
  displayName: "Factory Droid",
13
- hasSkillSupport: false, // No skill system, uses prompting
13
+ hasSkillSupport: true,
14
+ skillInstallCommand: "droid skill install <skill-name>",
14
15
 
15
16
  async isInstalled(): Promise<boolean> {
16
17
  try {
@@ -87,4 +88,37 @@ export const droidAdapter: AgentAdapter = {
87
88
 
88
89
  return { limited: false };
89
90
  },
91
+
92
+ async installSkills(projectPath: string): Promise<void> {
93
+ // Factory uses .factory/skills/ for skills (PLURAL)
94
+ const skillsDir = `${projectPath}/.factory/skills`;
95
+ await Bun.spawn(["mkdir", "-p", skillsDir]).exited;
96
+
97
+ const relentlessRoot = import.meta.dir.replace("/src/agents", "");
98
+ const sourceSkillsDir = `${relentlessRoot}/.claude/skills`;
99
+
100
+ // Copy all skills
101
+ const skills = [
102
+ "prd",
103
+ "relentless",
104
+ "constitution",
105
+ "specify",
106
+ "plan",
107
+ "tasks",
108
+ "checklist",
109
+ "clarify",
110
+ "analyze",
111
+ "implement",
112
+ "taskstoissues",
113
+ ];
114
+
115
+ for (const skill of skills) {
116
+ await Bun.spawn([
117
+ "cp",
118
+ "-r",
119
+ `${sourceSkillsDir}/${skill}`,
120
+ `${skillsDir}/`,
121
+ ]).exited;
122
+ }
123
+ },
90
124
  };
@@ -10,7 +10,8 @@ import type { AgentAdapter, AgentResult, InvokeOptions, RateLimitInfo } from "./
10
10
  export const opencodeAdapter: AgentAdapter = {
11
11
  name: "opencode",
12
12
  displayName: "OpenCode",
13
- hasSkillSupport: false, // Uses agent system, different from skills
13
+ hasSkillSupport: true,
14
+ skillInstallCommand: "opencode skill add <skill-name>",
14
15
 
15
16
  async isInstalled(): Promise<boolean> {
16
17
  try {
@@ -85,4 +86,37 @@ export const opencodeAdapter: AgentAdapter = {
85
86
 
86
87
  return { limited: false };
87
88
  },
89
+
90
+ async installSkills(projectPath: string): Promise<void> {
91
+ // OpenCode uses .opencode/skill/ (SINGULAR!) for skills
92
+ const skillsDir = `${projectPath}/.opencode/skill`;
93
+ await Bun.spawn(["mkdir", "-p", skillsDir]).exited;
94
+
95
+ const relentlessRoot = import.meta.dir.replace("/src/agents", "");
96
+ const sourceSkillsDir = `${relentlessRoot}/.claude/skills`;
97
+
98
+ // Copy all skills
99
+ const skills = [
100
+ "prd",
101
+ "relentless",
102
+ "constitution",
103
+ "specify",
104
+ "plan",
105
+ "tasks",
106
+ "checklist",
107
+ "clarify",
108
+ "analyze",
109
+ "implement",
110
+ "taskstoissues",
111
+ ];
112
+
113
+ for (const skill of skills) {
114
+ await Bun.spawn([
115
+ "cp",
116
+ "-r",
117
+ `${sourceSkillsDir}/${skill}`,
118
+ `${skillsDir}/`,
119
+ ]).exited;
120
+ }
121
+ },
88
122
  };