@vectorplane/ctrl-cli 0.1.8 → 0.1.9

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/README.md CHANGED
@@ -17,6 +17,7 @@ vp login
17
17
  vp logout
18
18
  vp status
19
19
  vp sync
20
+ vp draft create --type progress --title "Entrega concluída" --content "Resumo da mudança"
20
21
  ```
21
22
 
22
23
  ## Comandos
@@ -45,6 +46,13 @@ vp sync
45
46
  - mostra o estado da sessão local
46
47
  - exibe workspace ativo, diretório atual e última sincronização
47
48
 
49
+ ### `vp draft`
50
+
51
+ - cria drafts editoriais ligados ao workspace atual
52
+ - lista drafts existentes para conferência rápida
53
+ - suporta `ui`, `ux`, `project_skeleton`, `template_engineering`, `patterns`, `progress`, `decisions` e `architecture`
54
+ - aceita `--no-impact` para registrar explicitamente quando uma lane não foi afetada
55
+
48
56
  ### Comandos adicionais já implementados
49
57
 
50
58
  - `vp whoami`
@@ -64,6 +72,8 @@ vp sync
64
72
  - obtém instruções de setup do workspace
65
73
  - `vp event send`
66
74
  - envia eventos operacionais
75
+ - `vp draft`
76
+ - envia e consulta drafts editoriais do workspace
67
77
 
68
78
  ## Persistência local
69
79
 
@@ -0,0 +1 @@
1
+ export declare function runDraftCommand(cliVersion: string, args: string[]): Promise<number>;
@@ -0,0 +1,166 @@
1
+ import { getBooleanOption, getStringOption, parseArgs, requirePositional } from "../core/cli.js";
2
+ import { ensureSessionAvailable, getProfileState } from "../core/config.js";
3
+ import { collectGitContext } from "../core/git.js";
4
+ import { collectMachineContext, collectRuntimeContext } from "../core/machine.js";
5
+ import { loadRuntimeStatus } from "../core/runtime.js";
6
+ import { ensureFreshSession, resolveWorkspaceSlug } from "../core/session.js";
7
+ import { VectorPlaneApiClient } from "../core/api.js";
8
+ import { getBoundWorkspace, resolveWorkspaceRoot } from "../core/workspace-binding.js";
9
+ import { ValidationError } from "../core/errors.js";
10
+ const VALID_DRAFT_TYPES = new Set([
11
+ "architecture",
12
+ "decisions",
13
+ "progress",
14
+ "patterns",
15
+ "ui",
16
+ "ux",
17
+ "template_engineering",
18
+ "project_skeleton",
19
+ ]);
20
+ const VALID_STATUSES = new Set([
21
+ "draft",
22
+ "in_review",
23
+ "approved",
24
+ "merged",
25
+ "rejected",
26
+ "archived",
27
+ ]);
28
+ function slugify(value) {
29
+ return value
30
+ .trim()
31
+ .toLowerCase()
32
+ .normalize("NFD")
33
+ .replace(/[\u0300-\u036f]/g, "")
34
+ .replace(/[^a-z0-9]+/g, "-")
35
+ .replace(/^-+|-+$/g, "")
36
+ .slice(0, 120);
37
+ }
38
+ function inferMemoryKey(type, title) {
39
+ const slug = slugify(title);
40
+ if (!slug)
41
+ return undefined;
42
+ const prefix = type === "decisions"
43
+ ? "decision"
44
+ : type === "patterns"
45
+ ? "pattern"
46
+ : type === "template_engineering"
47
+ ? "template"
48
+ : type === "project_skeleton"
49
+ ? "skeleton"
50
+ : type;
51
+ return `${prefix}:${slug}`;
52
+ }
53
+ function validateDraftType(value) {
54
+ if (!value || !VALID_DRAFT_TYPES.has(value)) {
55
+ throw new ValidationError("Informe um tipo válido em `--type`.");
56
+ }
57
+ return value;
58
+ }
59
+ function validateStatus(value) {
60
+ if (!value)
61
+ return undefined;
62
+ if (!VALID_STATUSES.has(value)) {
63
+ throw new ValidationError("Informe um status válido em `--status`.");
64
+ }
65
+ return value;
66
+ }
67
+ function buildNoImpactDraft(type, reason) {
68
+ const laneLabel = type.replaceAll("_", " ");
69
+ const explanation = reason?.trim() || "sem impacto identificado nesta tarefa";
70
+ return {
71
+ title: `Sem impacto em ${laneLabel}`,
72
+ content: `Esta tarefa não alterou diretamente a lane ${laneLabel}. Escopo efetivo: ${explanation}.`,
73
+ };
74
+ }
75
+ function printDraftList(drafts) {
76
+ if (drafts.length === 0) {
77
+ process.stdout.write("VectorPlane: nenhum draft encontrado.\n");
78
+ return;
79
+ }
80
+ for (const draft of drafts) {
81
+ process.stdout.write(`${draft.id} | ${draft.docType} | ${draft.status} | ${draft.title}\n`);
82
+ }
83
+ }
84
+ async function resolveAuthenticatedWorkspace(cliVersion, rawArgs) {
85
+ const runtime = await loadRuntimeStatus();
86
+ const session = await ensureSessionAvailable(runtime.profile.name);
87
+ const parsed = parseArgs(rawArgs);
88
+ const [git, machine, runtimeContext] = await Promise.all([
89
+ collectGitContext(process.cwd()),
90
+ collectMachineContext(runtime.device, runtime.config),
91
+ collectRuntimeContext(cliVersion, "draft", process.argv.slice(2)),
92
+ ]);
93
+ const apiClient = new VectorPlaneApiClient(runtime.profile.apiBaseUrl, runtime.config.requestTimeoutMs, runtime.logger);
94
+ const freshSession = await ensureFreshSession({
95
+ profileName: runtime.profile.name,
96
+ session,
97
+ machine,
98
+ runtime: runtimeContext,
99
+ device: runtime.device,
100
+ apiClient,
101
+ logger: runtime.logger,
102
+ });
103
+ const rootPath = resolveWorkspaceRoot(process.cwd(), git);
104
+ const boundWorkspace = await getBoundWorkspace(rootPath);
105
+ const profileState = getProfileState(runtime.state, runtime.profile.name);
106
+ const workspace = resolveWorkspaceSlug(runtime.profile, freshSession, getStringOption(parsed, "workspace"), boundWorkspace, profileState.lastWorkspace);
107
+ if (!workspace) {
108
+ throw new ValidationError("Nenhum workspace resolvido para operar drafts.");
109
+ }
110
+ return { parsed, runtime, apiClient, freshSession, git, workspace };
111
+ }
112
+ async function runDraftCreate(cliVersion, args) {
113
+ const { parsed, runtime, apiClient, freshSession, git, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
114
+ const type = validateDraftType(getStringOption(parsed, "type"));
115
+ const noImpact = getBooleanOption(parsed, "no-impact");
116
+ const explicitTitle = getStringOption(parsed, "title")?.trim();
117
+ const explicitContent = getStringOption(parsed, "content")?.trim();
118
+ const reason = getStringOption(parsed, "reason");
119
+ const derived = noImpact ? buildNoImpactDraft(type, reason) : null;
120
+ const title = explicitTitle || derived?.title;
121
+ const content = explicitContent || derived?.content;
122
+ if (!title) {
123
+ throw new ValidationError("Informe `--title` para criar o draft.");
124
+ }
125
+ if (!content) {
126
+ throw new ValidationError("Informe `--content` para criar o draft.");
127
+ }
128
+ const payload = {
129
+ docType: type,
130
+ title,
131
+ content,
132
+ memoryKey: getStringOption(parsed, "memory-key")?.trim() || inferMemoryKey(type, title),
133
+ featureKey: getStringOption(parsed, "feature")?.trim() || undefined,
134
+ branch: getStringOption(parsed, "branch")?.trim() || git.branch || undefined,
135
+ taskKey: getStringOption(parsed, "task")?.trim() || undefined,
136
+ };
137
+ const draft = await apiClient.createMemoryDraft(freshSession.accessToken, workspace, payload);
138
+ runtime.logger.success(`draft enviado para ${type}.`);
139
+ process.stdout.write(`ID: ${draft.id}\n`);
140
+ process.stdout.write(`Workspace: ${workspace}\n`);
141
+ process.stdout.write(`Status: ${draft.status}\n`);
142
+ return 0;
143
+ }
144
+ async function runDraftList(cliVersion, args) {
145
+ const { parsed, apiClient, freshSession, workspace } = await resolveAuthenticatedWorkspace(cliVersion, args);
146
+ const status = validateStatus(getStringOption(parsed, "status"));
147
+ const drafts = await apiClient.listMemoryDrafts(freshSession.accessToken, workspace, status);
148
+ if (getBooleanOption(parsed, "json")) {
149
+ process.stdout.write(`${JSON.stringify(drafts, null, 2)}\n`);
150
+ return 0;
151
+ }
152
+ printDraftList(drafts);
153
+ return 0;
154
+ }
155
+ export async function runDraftCommand(cliVersion, args) {
156
+ const parsed = parseArgs(args);
157
+ const subcommand = requirePositional(parsed, 0, "Uso: vp draft <create|list> [...]");
158
+ if (subcommand === "create") {
159
+ return runDraftCreate(cliVersion, args.slice(1));
160
+ }
161
+ if (subcommand === "list") {
162
+ return runDraftList(cliVersion, args.slice(1));
163
+ }
164
+ throw new ValidationError("Uso: vp draft <create|list> [...]");
165
+ }
166
+ //# sourceMappingURL=draft.js.map
@@ -1,6 +1,6 @@
1
1
  import type { Logger } from "./logger.js";
2
2
  import type { CurrentUserResponse, AuthCodeExchangeRequest, AuthTokenExchangeResponse, CreateLoginAttemptRequest, CreateLoginAttemptResponse, LoginAttemptStatusResponse, RefreshTokenRequest } from "../types/auth.js";
3
- import type { AgentCheckoutRequest, AgentHeartbeatRequest, AgentSessionRequest, AgentSessionResponse, EventRequest, ResolveWorkspaceRequest, ResolveWorkspaceResponse, SyncRequest, SyncResponse } from "../types/api.js";
3
+ import type { AgentCheckoutRequest, AgentHeartbeatRequest, AgentSessionRequest, AgentSessionResponse, EventRequest, MemoryDraftCreateRequest, MemoryDraftRecord, MemoryDraftStatus, ResolveWorkspaceRequest, ResolveWorkspaceResponse, SyncRequest, SyncResponse } from "../types/api.js";
4
4
  export declare class VectorPlaneApiClient {
5
5
  private readonly apiBaseUrl;
6
6
  private readonly timeoutMs;
@@ -22,6 +22,8 @@ export declare class VectorPlaneApiClient {
22
22
  getWorkspaceSnapshot(accessToken: string, workspaceId: string): Promise<Record<string, unknown>>;
23
23
  getWorkspaceDeliveryContext(accessToken: string, workspaceId: string): Promise<Record<string, unknown>>;
24
24
  getAgentSetup(accessToken: string, workspaceId: string): Promise<Record<string, unknown>>;
25
+ listMemoryDrafts(accessToken: string, workspaceRef: string, status?: MemoryDraftStatus): Promise<MemoryDraftRecord[]>;
26
+ createMemoryDraft(accessToken: string, workspaceRef: string, payload: MemoryDraftCreateRequest): Promise<MemoryDraftRecord>;
25
27
  checkInAgent(accessToken: string, payload: AgentSessionRequest): Promise<AgentSessionResponse>;
26
28
  heartbeatAgent(accessToken: string, payload: AgentHeartbeatRequest): Promise<AgentSessionResponse>;
27
29
  checkOutAgent(accessToken: string, payload: AgentCheckoutRequest): Promise<AgentSessionResponse>;
package/dist/core/api.js CHANGED
@@ -160,6 +160,25 @@ export class VectorPlaneApiClient {
160
160
  },
161
161
  }, this.timeoutMs, this.logger);
162
162
  }
163
+ async listMemoryDrafts(accessToken, workspaceRef, status) {
164
+ const url = new URL(`${this.apiBaseUrl}/cli/workspace/${workspaceRef}/memory-drafts`);
165
+ if (status) {
166
+ url.searchParams.set("status", status);
167
+ }
168
+ return requestJson(url.toString(), {
169
+ method: "GET",
170
+ headers: {
171
+ "Authorization": `Bearer ${accessToken}`,
172
+ },
173
+ }, this.timeoutMs, this.logger);
174
+ }
175
+ async createMemoryDraft(accessToken, workspaceRef, payload) {
176
+ return requestJson(`${this.apiBaseUrl}/cli/workspace/${workspaceRef}/memory-drafts`, {
177
+ method: "POST",
178
+ headers: authHeaders(accessToken),
179
+ body: JSON.stringify(payload),
180
+ }, this.timeoutMs, this.logger);
181
+ }
163
182
  async checkInAgent(accessToken, payload) {
164
183
  return requestJson(`${this.apiBaseUrl}/cli/workspace/${payload.workspaceId}/agents/check-in`, {
165
184
  method: "POST",
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { runBootstrapCommand } from "./commands/bootstrap.js";
5
5
  import { runConfigCommand } from "./commands/config.js";
6
6
  import { runContextCommand } from "./commands/context.js";
7
7
  import { runDoctorCommand } from "./commands/doctor.js";
8
+ import { runDraftCommand } from "./commands/draft.js";
8
9
  import { runEventCommand } from "./commands/event.js";
9
10
  import { runLoginCommand } from "./commands/login.js";
10
11
  import { runLogoutCommand } from "./commands/logout.js";
@@ -32,6 +33,7 @@ function printHelp() {
32
33
  process.stdout.write(" status\n");
33
34
  process.stdout.write(" whoami\n");
34
35
  process.stdout.write(" doctor\n");
36
+ process.stdout.write(" draft <create|list>\n");
35
37
  process.stdout.write(" config <profile|get|set>\n");
36
38
  process.stdout.write(" workspace <current|use|resolve|clear>\n");
37
39
  process.stdout.write(" session <check-in|heartbeat|check-out>\n");
@@ -60,6 +62,8 @@ export async function runCli(args) {
60
62
  return runWhoAmICommand(cliVersion);
61
63
  case "doctor":
62
64
  return runDoctorCommand(cliVersion);
65
+ case "draft":
66
+ return runDraftCommand(cliVersion, rest);
63
67
  case "config":
64
68
  return runConfigCommand(rest);
65
69
  case "workspace":
@@ -59,3 +59,30 @@ export interface AgentSessionResponse {
59
59
  workspaceId?: string;
60
60
  [key: string]: unknown;
61
61
  }
62
+ export type MemoryDraftType = "architecture" | "decisions" | "progress" | "patterns" | "ui" | "ux" | "template_engineering" | "project_skeleton";
63
+ export type MemoryDraftStatus = "draft" | "in_review" | "approved" | "merged" | "rejected" | "archived";
64
+ export interface MemoryDraftCreateRequest {
65
+ docType: MemoryDraftType;
66
+ title: string;
67
+ content: string;
68
+ memoryKey?: string;
69
+ featureKey?: string;
70
+ branch?: string;
71
+ taskKey?: string;
72
+ }
73
+ export interface MemoryDraftRecord {
74
+ id: string;
75
+ workspaceId: string;
76
+ docType: MemoryDraftType;
77
+ title: string;
78
+ content: string;
79
+ memoryKey: string | null;
80
+ featureKey: string | null;
81
+ branch: string | null;
82
+ taskKey: string | null;
83
+ status: MemoryDraftStatus;
84
+ suggestedMergeStrategy?: string | null;
85
+ createdAt: string;
86
+ updatedAt?: string;
87
+ [key: string]: unknown;
88
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectorplane/ctrl-cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Official VectorPlane CLI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",