@mcp-graph-workflow/agent-graph-flow 0.2.0 → 0.4.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/dist/cli/index.js +485 -101
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -11,9 +11,9 @@ import { randomUUID, randomBytes, createHash } from 'crypto';
|
|
|
11
11
|
import os, { homedir } from 'os';
|
|
12
12
|
import { stat, readFile, access, constants } from 'fs/promises';
|
|
13
13
|
import { execFile, execSync, spawn } from 'child_process';
|
|
14
|
-
import { render, useApp, Box, Text } from 'ink';
|
|
14
|
+
import { render, useApp, useInput, Box, Text } from 'ink';
|
|
15
15
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
16
|
-
import { createElement, useState,
|
|
16
|
+
import { createElement, useState, useEffect, useCallback } from 'react';
|
|
17
17
|
import TextInput from 'ink-text-input';
|
|
18
18
|
import Spinner from 'ink-spinner';
|
|
19
19
|
import { Command } from 'commander';
|
|
@@ -6839,9 +6839,15 @@ var init_copilot_sdk_adapter = __esm({
|
|
|
6839
6839
|
init_logger();
|
|
6840
6840
|
log22 = createLogger({ layer: "core", source: "copilot-sdk-adapter.ts" });
|
|
6841
6841
|
ModelAdapterError = class extends McpGraphError {
|
|
6842
|
-
|
|
6842
|
+
/** HTTP status quando a falha veio de uma resposta do provider (p/ classifyLlmError). */
|
|
6843
|
+
status;
|
|
6844
|
+
/** Espera sugerida (ms), derivada de `retry-after` em respostas 429. */
|
|
6845
|
+
retryAfterMs;
|
|
6846
|
+
constructor(message, opts = {}) {
|
|
6843
6847
|
super(`Model adapter error: ${message}`);
|
|
6844
6848
|
this.name = "ModelAdapterError";
|
|
6849
|
+
this.status = opts.status;
|
|
6850
|
+
this.retryAfterMs = opts.retryAfterMs;
|
|
6845
6851
|
}
|
|
6846
6852
|
};
|
|
6847
6853
|
CopilotSdkAdapter = class {
|
|
@@ -6932,9 +6938,16 @@ var init_copilot_api_adapter = __esm({
|
|
|
6932
6938
|
}
|
|
6933
6939
|
if (!res.ok) {
|
|
6934
6940
|
if (res.status === 401 || res.status === 403) {
|
|
6935
|
-
throw new ModelAdapterError("token Copilot expirado/inv\xE1lido (401/403) \u2014 rode `agf login` novamente."
|
|
6941
|
+
throw new ModelAdapterError("token Copilot expirado/inv\xE1lido (401/403) \u2014 rode `agf login` novamente.", {
|
|
6942
|
+
status: res.status
|
|
6943
|
+
});
|
|
6936
6944
|
}
|
|
6937
|
-
|
|
6945
|
+
const retryAfterRaw = res.headers?.get?.("retry-after");
|
|
6946
|
+
const retryAfterMs = retryAfterRaw && Number.isFinite(Number(retryAfterRaw)) ? Math.round(Number(retryAfterRaw) * 1e3) : void 0;
|
|
6947
|
+
throw new ModelAdapterError(`API do Copilot retornou ${res.status}: ${(await res.text()).slice(0, 200)}`, {
|
|
6948
|
+
status: res.status,
|
|
6949
|
+
retryAfterMs
|
|
6950
|
+
});
|
|
6938
6951
|
}
|
|
6939
6952
|
const body = await res.json();
|
|
6940
6953
|
const text = body.choices?.[0]?.message?.content;
|
|
@@ -7078,8 +7091,8 @@ var init_implementation_executor = __esm({
|
|
|
7078
7091
|
};
|
|
7079
7092
|
defaultRunner = (command, cwd) => {
|
|
7080
7093
|
try {
|
|
7081
|
-
const
|
|
7082
|
-
return { exitCode: 0, output:
|
|
7094
|
+
const output17 = execSync(command, { cwd, encoding: "utf8", stdio: "pipe" });
|
|
7095
|
+
return { exitCode: 0, output: output17 };
|
|
7083
7096
|
} catch (err) {
|
|
7084
7097
|
const e = err;
|
|
7085
7098
|
return {
|
|
@@ -7159,7 +7172,83 @@ var init_plan_parser = __esm({
|
|
|
7159
7172
|
}
|
|
7160
7173
|
});
|
|
7161
7174
|
|
|
7175
|
+
// src/core/model-hub/llm-error.ts
|
|
7176
|
+
function readHeader(headers, name) {
|
|
7177
|
+
if (!headers || typeof headers !== "object") return null;
|
|
7178
|
+
const getter = headers.get;
|
|
7179
|
+
if (typeof getter === "function") {
|
|
7180
|
+
const v = getter.call(headers, name);
|
|
7181
|
+
return typeof v === "string" ? v : null;
|
|
7182
|
+
}
|
|
7183
|
+
const lower = name.toLowerCase();
|
|
7184
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
7185
|
+
if (k.toLowerCase() === lower && typeof v === "string") return v;
|
|
7186
|
+
}
|
|
7187
|
+
return null;
|
|
7188
|
+
}
|
|
7189
|
+
function extractStatus(err) {
|
|
7190
|
+
const candidates = [err.status, err.statusCode, err.response?.status];
|
|
7191
|
+
for (const c of candidates) {
|
|
7192
|
+
const n = typeof c === "number" ? c : typeof c === "string" ? Number(c) : NaN;
|
|
7193
|
+
if (Number.isFinite(n) && n >= 100 && n < 600) return n;
|
|
7194
|
+
}
|
|
7195
|
+
if (typeof err.message === "string") {
|
|
7196
|
+
const m = err.message.match(/\b(4\d\d|5\d\d)\b/);
|
|
7197
|
+
if (m) return Number(m[1]);
|
|
7198
|
+
}
|
|
7199
|
+
return void 0;
|
|
7200
|
+
}
|
|
7201
|
+
function resolveRetryAfterMs(err) {
|
|
7202
|
+
if (typeof err.retryAfterMs === "number" && err.retryAfterMs >= 0) return err.retryAfterMs;
|
|
7203
|
+
const headers = err.headers ?? err.response?.headers;
|
|
7204
|
+
const raw = readHeader(headers, "retry-after");
|
|
7205
|
+
if (raw !== null) {
|
|
7206
|
+
const seconds = Number(raw);
|
|
7207
|
+
if (Number.isFinite(seconds) && seconds >= 0) return Math.round(seconds * 1e3);
|
|
7208
|
+
}
|
|
7209
|
+
return DEFAULT_RATE_LIMIT_MS;
|
|
7210
|
+
}
|
|
7211
|
+
function classifyLlmError(err) {
|
|
7212
|
+
const e = err && typeof err === "object" ? err : {};
|
|
7213
|
+
const message = typeof e.message === "string" ? e.message : "";
|
|
7214
|
+
const status = extractStatus(e);
|
|
7215
|
+
if (status === 429) {
|
|
7216
|
+
return { kind: "rate_limit", retryable: true, retryAfterMs: resolveRetryAfterMs(e) };
|
|
7217
|
+
}
|
|
7218
|
+
if (status === 401 || status === 403) {
|
|
7219
|
+
return { kind: "auth", retryable: false };
|
|
7220
|
+
}
|
|
7221
|
+
if (status !== void 0 && status >= 400 && status < 500 && CONTENT_POLICY_RE.test(message)) {
|
|
7222
|
+
return { kind: "content_policy", retryable: false };
|
|
7223
|
+
}
|
|
7224
|
+
if (status !== void 0 && status >= 400 && status < 500) {
|
|
7225
|
+
return { kind: "invalid_request", retryable: false };
|
|
7226
|
+
}
|
|
7227
|
+
if (status !== void 0 && RETRYABLE_5XX.has(status)) {
|
|
7228
|
+
return { kind: "server", retryable: true };
|
|
7229
|
+
}
|
|
7230
|
+
const code = typeof e.code === "string" ? e.code : "";
|
|
7231
|
+
if (NETWORK_CODE.has(code) || status === void 0 && NETWORK_MSG_RE.test(message)) {
|
|
7232
|
+
return { kind: "network", retryable: true };
|
|
7233
|
+
}
|
|
7234
|
+
return { kind: "unknown", retryable: false };
|
|
7235
|
+
}
|
|
7236
|
+
var DEFAULT_RATE_LIMIT_MS, CONTENT_POLICY_RE, NETWORK_CODE, NETWORK_MSG_RE, RETRYABLE_5XX;
|
|
7237
|
+
var init_llm_error = __esm({
|
|
7238
|
+
"src/core/model-hub/llm-error.ts"() {
|
|
7239
|
+
init_esm_shims();
|
|
7240
|
+
DEFAULT_RATE_LIMIT_MS = 1e3;
|
|
7241
|
+
CONTENT_POLICY_RE = /content[_\s-]?filter|content[_\s-]?policy|policy violation|moderat/i;
|
|
7242
|
+
NETWORK_CODE = /* @__PURE__ */ new Set(["ECONNRESET", "ETIMEDOUT", "ECONNREFUSED", "EAI_AGAIN", "EPIPE", "ENOTFOUND"]);
|
|
7243
|
+
NETWORK_MSG_RE = /fetch failed|network|socket hang up|timed? out|timeout|aborted|abort/i;
|
|
7244
|
+
RETRYABLE_5XX = /* @__PURE__ */ new Set([500, 502, 503, 504, 529]);
|
|
7245
|
+
}
|
|
7246
|
+
});
|
|
7247
|
+
|
|
7162
7248
|
// src/core/autonomy/implement-attempt.ts
|
|
7249
|
+
function defaultSleep(ms) {
|
|
7250
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
7251
|
+
}
|
|
7163
7252
|
function buildInitialPrompt(node, opts = {}) {
|
|
7164
7253
|
const repoMapBlock = opts.repoMap && opts.repoMap.length > 0 ? [`Contexto do reposit\xF3rio (refer\xEAncia, n\xE3o reescreva o que j\xE1 existe):`, opts.repoMap, ""] : [];
|
|
7165
7254
|
const flowBlock = opts.flowContext && opts.flowContext.length > 0 ? [opts.flowContext, ""] : [];
|
|
@@ -7191,23 +7280,52 @@ function buildRetryPrompt(node, failure, maxFeedbackChars) {
|
|
|
7191
7280
|
async function attemptImplementation(deps, options) {
|
|
7192
7281
|
const maxAttempts = Math.max(1, options.maxAttempts);
|
|
7193
7282
|
const maxFeedbackChars = options.maxFeedbackChars ?? DEFAULT_MAX_FEEDBACK_CHARS;
|
|
7283
|
+
const sleep2 = deps.sleep ?? defaultSleep;
|
|
7194
7284
|
let lastResult;
|
|
7195
7285
|
let lastError;
|
|
7196
7286
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
7197
7287
|
const prompt = attempt === 1 || !lastResult ? buildInitialPrompt(options.node, { repoMap: options.repoMap, flowContext: options.flowContext }) : buildRetryPrompt(options.node, lastResult, maxFeedbackChars);
|
|
7288
|
+
let text;
|
|
7289
|
+
try {
|
|
7290
|
+
text = await deps.generate(prompt);
|
|
7291
|
+
} catch (err) {
|
|
7292
|
+
lastError = getErrorMessage(err);
|
|
7293
|
+
const cls = classifyLlmError(err);
|
|
7294
|
+
if (!cls.retryable) {
|
|
7295
|
+
log25.warn("Erro permanente do provider \u2014 escalando sem re-tentar", {
|
|
7296
|
+
attempt,
|
|
7297
|
+
node: options.node.id,
|
|
7298
|
+
kind: cls.kind,
|
|
7299
|
+
error: lastError
|
|
7300
|
+
});
|
|
7301
|
+
return { success: false, attempts: attempt, lastResult, error: lastError };
|
|
7302
|
+
}
|
|
7303
|
+
if (cls.retryAfterMs && cls.retryAfterMs > 0) await sleep2(cls.retryAfterMs);
|
|
7304
|
+
log25.warn("Erro transit\xF3rio do provider \u2014 re-tentando", {
|
|
7305
|
+
attempt,
|
|
7306
|
+
node: options.node.id,
|
|
7307
|
+
kind: cls.kind,
|
|
7308
|
+
retryAfterMs: cls.retryAfterMs
|
|
7309
|
+
});
|
|
7310
|
+
continue;
|
|
7311
|
+
}
|
|
7198
7312
|
let plan;
|
|
7199
7313
|
try {
|
|
7200
|
-
const text = await deps.generate(prompt);
|
|
7201
7314
|
plan = parseImplementationPlan(text);
|
|
7202
7315
|
} catch (err) {
|
|
7203
|
-
lastError =
|
|
7316
|
+
lastError = getErrorMessage(err);
|
|
7204
7317
|
log25.warn("Tentativa falhou no parse", { attempt, node: options.node.id, error: lastError });
|
|
7205
7318
|
continue;
|
|
7206
7319
|
}
|
|
7207
7320
|
lastResult = await deps.execute(plan);
|
|
7208
7321
|
if (lastResult.testPassed === true) {
|
|
7209
7322
|
log25.info("Implementa\xE7\xE3o verde", { attempt, node: options.node.id });
|
|
7210
|
-
|
|
7323
|
+
const appliedEdits = (plan.edits ?? []).map((e) => ({
|
|
7324
|
+
path: e.path,
|
|
7325
|
+
oldString: e.oldString,
|
|
7326
|
+
newString: e.newString
|
|
7327
|
+
}));
|
|
7328
|
+
return { success: true, attempts: attempt, lastResult, appliedEdits };
|
|
7211
7329
|
}
|
|
7212
7330
|
lastError = lastResult.testOutput;
|
|
7213
7331
|
log25.warn("Testes vermelhos", { attempt, node: options.node.id });
|
|
@@ -7219,13 +7337,88 @@ var init_implement_attempt = __esm({
|
|
|
7219
7337
|
"src/core/autonomy/implement-attempt.ts"() {
|
|
7220
7338
|
init_esm_shims();
|
|
7221
7339
|
init_logger();
|
|
7340
|
+
init_errors();
|
|
7222
7341
|
init_truncate();
|
|
7223
7342
|
init_plan_parser();
|
|
7343
|
+
init_llm_error();
|
|
7224
7344
|
log25 = createLogger({ layer: "core", source: "implement-attempt.ts" });
|
|
7225
7345
|
DEFAULT_MAX_FEEDBACK_CHARS = 1200;
|
|
7226
7346
|
}
|
|
7227
7347
|
});
|
|
7228
7348
|
|
|
7349
|
+
// src/core/autonomy/exec-policy.ts
|
|
7350
|
+
function norm(cmd) {
|
|
7351
|
+
return cmd.trim().replace(/\s+/g, " ");
|
|
7352
|
+
}
|
|
7353
|
+
function evaluateExecPolicy(command, rules, defaultEffect = "ask") {
|
|
7354
|
+
const cmd = norm(command);
|
|
7355
|
+
let best;
|
|
7356
|
+
for (const rule of rules) {
|
|
7357
|
+
const m = norm(rule.match);
|
|
7358
|
+
if (cmd === m || cmd.startsWith(m + " ") || cmd.startsWith(m)) {
|
|
7359
|
+
if (!best || m.length > norm(best.match).length) best = rule;
|
|
7360
|
+
}
|
|
7361
|
+
}
|
|
7362
|
+
if (best) return { effect: best.effect, matchedRule: best };
|
|
7363
|
+
const low = cmd.toLowerCase();
|
|
7364
|
+
for (const danger of DEFAULT_DENY) {
|
|
7365
|
+
if (low.includes(danger)) return { effect: "deny", builtin: true };
|
|
7366
|
+
}
|
|
7367
|
+
return { effect: defaultEffect };
|
|
7368
|
+
}
|
|
7369
|
+
function guardExecRunner(base, opts = {}) {
|
|
7370
|
+
const rules = opts.rules ?? [];
|
|
7371
|
+
const defaultEffect = opts.defaultEffect ?? "ask";
|
|
7372
|
+
return (command, cwd) => {
|
|
7373
|
+
const decision = evaluateExecPolicy(command, rules, defaultEffect);
|
|
7374
|
+
const allowed = decision.effect === "allow" || decision.effect === "ask" && opts.cache?.isApproved(command) === true;
|
|
7375
|
+
if (allowed) return base(command, cwd);
|
|
7376
|
+
const reason = decision.effect === "deny" ? decision.builtin ? "deny (built-in perigoso)" : "deny (regra)" : "ask (n\xE3o aprovado nesta sess\xE3o)";
|
|
7377
|
+
return { exitCode: 126, output: `[exec-policy] comando bloqueado \u2014 ${reason}: ${command}` };
|
|
7378
|
+
};
|
|
7379
|
+
}
|
|
7380
|
+
var DEFAULT_DENY;
|
|
7381
|
+
var init_exec_policy = __esm({
|
|
7382
|
+
"src/core/autonomy/exec-policy.ts"() {
|
|
7383
|
+
init_esm_shims();
|
|
7384
|
+
DEFAULT_DENY = [
|
|
7385
|
+
"rm -rf",
|
|
7386
|
+
"sudo ",
|
|
7387
|
+
"git push --force",
|
|
7388
|
+
"git push -f",
|
|
7389
|
+
"chmod -R 777",
|
|
7390
|
+
"chmod 777",
|
|
7391
|
+
"dd if=",
|
|
7392
|
+
":(){",
|
|
7393
|
+
"mkfs",
|
|
7394
|
+
"| sh",
|
|
7395
|
+
"|sh",
|
|
7396
|
+
"| bash",
|
|
7397
|
+
"> /dev/sd"
|
|
7398
|
+
];
|
|
7399
|
+
}
|
|
7400
|
+
});
|
|
7401
|
+
|
|
7402
|
+
// src/tui/diff-render.ts
|
|
7403
|
+
function renderEditDiff(edit) {
|
|
7404
|
+
const lines = [`\u2500\u2500 ${edit.path} \u2500\u2500`];
|
|
7405
|
+
if (edit.oldString.length > 0) {
|
|
7406
|
+
for (const l of edit.oldString.split("\n")) lines.push(`- ${l}`);
|
|
7407
|
+
}
|
|
7408
|
+
if (edit.newString.length > 0) {
|
|
7409
|
+
for (const l of edit.newString.split("\n")) lines.push(`+ ${l}`);
|
|
7410
|
+
}
|
|
7411
|
+
return lines;
|
|
7412
|
+
}
|
|
7413
|
+
function renderPlanDiff(edits) {
|
|
7414
|
+
return edits.flatMap(renderEditDiff);
|
|
7415
|
+
}
|
|
7416
|
+
var init_diff_render = __esm({
|
|
7417
|
+
"src/tui/diff-render.ts"() {
|
|
7418
|
+
init_esm_shims();
|
|
7419
|
+
}
|
|
7420
|
+
});
|
|
7421
|
+
|
|
7229
7422
|
// src/core/code/code-store.ts
|
|
7230
7423
|
function rowToSymbol(row) {
|
|
7231
7424
|
return {
|
|
@@ -8467,6 +8660,18 @@ function buildLiveImplement(options) {
|
|
|
8467
8660
|
onLog?.(`[live] provider: ${resolved.kind === "api" ? "Copilot API (HTTP, logado)" : "Copilot CLI"}`);
|
|
8468
8661
|
const client = new TieredModelClient(resolved.adapter, config);
|
|
8469
8662
|
const maxAttempts = Math.max(1, retries);
|
|
8663
|
+
let execRules = [];
|
|
8664
|
+
let execDefault = "allow";
|
|
8665
|
+
const rawPolicy = store2.getProjectSetting("exec_policy");
|
|
8666
|
+
if (rawPolicy) {
|
|
8667
|
+
try {
|
|
8668
|
+
const parsed = JSON.parse(rawPolicy);
|
|
8669
|
+
if (parsed.default) execDefault = parsed.default;
|
|
8670
|
+
if (Array.isArray(parsed.rules)) execRules = parsed.rules;
|
|
8671
|
+
} catch {
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
const guardedRunner = guardExecRunner(defaultRunner, { rules: execRules, defaultEffect: execDefault });
|
|
8470
8675
|
const codeStore = new CodeStore(store2.getDb());
|
|
8471
8676
|
const projectId = store2.getProject()?.id;
|
|
8472
8677
|
const repoSymbols = projectId ? codeStore.getAllSymbols(projectId) : [];
|
|
@@ -8494,7 +8699,7 @@ function buildLiveImplement(options) {
|
|
|
8494
8699
|
});
|
|
8495
8700
|
return res.text;
|
|
8496
8701
|
},
|
|
8497
|
-
execute: (plan) => executePlan(plan, { workspaceDir: dir, defaultTestCommand: testCmd })
|
|
8702
|
+
execute: (plan) => executePlan(plan, { workspaceDir: dir, defaultTestCommand: testCmd, runCommand: guardedRunner })
|
|
8498
8703
|
},
|
|
8499
8704
|
{ node, maxAttempts, repoMap, flowContext }
|
|
8500
8705
|
);
|
|
@@ -8503,6 +8708,9 @@ function buildLiveImplement(options) {
|
|
|
8503
8708
|
onLog?.(
|
|
8504
8709
|
` [live] ${client.modelFor("implement")}: ${outcome.attempts} tentativa(s), ${files} arquivo(s), ${task.total} tok \u2192 ${outcome.success ? "verde" : "escala"}`
|
|
8505
8710
|
);
|
|
8711
|
+
if (outcome.success && outcome.appliedEdits && outcome.appliedEdits.length > 0) {
|
|
8712
|
+
for (const line of renderPlanDiff(outcome.appliedEdits)) onLog?.(line);
|
|
8713
|
+
}
|
|
8506
8714
|
try {
|
|
8507
8715
|
const applied = outcome.lastResult?.applied ?? [];
|
|
8508
8716
|
insertEpisodicOutcome(store2.getDb(), {
|
|
@@ -8530,6 +8738,8 @@ var init_live_implement = __esm({
|
|
|
8530
8738
|
init_resolve_adapter();
|
|
8531
8739
|
init_implementation_executor();
|
|
8532
8740
|
init_implement_attempt();
|
|
8741
|
+
init_exec_policy();
|
|
8742
|
+
init_diff_render();
|
|
8533
8743
|
init_code_store();
|
|
8534
8744
|
init_repo_map();
|
|
8535
8745
|
init_flow_compact();
|
|
@@ -8568,6 +8778,18 @@ var init_store_port = __esm({
|
|
|
8568
8778
|
init_definition_of_done();
|
|
8569
8779
|
}
|
|
8570
8780
|
});
|
|
8781
|
+
|
|
8782
|
+
// src/tui/status-line.ts
|
|
8783
|
+
function formatStatusLine(input) {
|
|
8784
|
+
const tokens = `${Math.max(0, Math.round(input.totalTokens))} tok`;
|
|
8785
|
+
const cost = `$${input.costUsd.toFixed(4)}`;
|
|
8786
|
+
return `\u26C1 ${tokens} \xB7 ${cost} \xB7 ${input.model}`;
|
|
8787
|
+
}
|
|
8788
|
+
var init_status_line = __esm({
|
|
8789
|
+
"src/tui/status-line.ts"() {
|
|
8790
|
+
init_esm_shims();
|
|
8791
|
+
}
|
|
8792
|
+
});
|
|
8571
8793
|
function TaskRow({ task }) {
|
|
8572
8794
|
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
8573
8795
|
" ",
|
|
@@ -8594,6 +8816,7 @@ function App({ model }) {
|
|
|
8594
8816
|
" ",
|
|
8595
8817
|
model.wip
|
|
8596
8818
|
] }) }),
|
|
8819
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: "green", children: formatStatusLine({ totalTokens: model.tokens.total, costUsd: model.tokens.costUsd, model: model.modelLabel }) }) }),
|
|
8597
8820
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
8598
8821
|
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
8599
8822
|
"Tasks ativas (",
|
|
@@ -8624,6 +8847,7 @@ var STATUS_ICON;
|
|
|
8624
8847
|
var init_app = __esm({
|
|
8625
8848
|
"src/tui/app.tsx"() {
|
|
8626
8849
|
init_esm_shims();
|
|
8850
|
+
init_status_line();
|
|
8627
8851
|
STATUS_ICON = {
|
|
8628
8852
|
in_progress: "\u25CF",
|
|
8629
8853
|
ready: "\u25CB",
|
|
@@ -8674,6 +8898,7 @@ var init_banner_screen = __esm({
|
|
|
8674
8898
|
function CommandBar({ value, onChange, onSubmit, suggestions }) {
|
|
8675
8899
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
8676
8900
|
suggestions.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: suggestions.map((c) => /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
8901
|
+
c.source === "skill" ? /* @__PURE__ */ jsx(Text, { color: "magenta", children: "[skill] " }) : null,
|
|
8677
8902
|
c.usage,
|
|
8678
8903
|
" \u2014 ",
|
|
8679
8904
|
c.desc
|
|
@@ -8690,6 +8915,44 @@ var init_command_bar = __esm({
|
|
|
8690
8915
|
}
|
|
8691
8916
|
});
|
|
8692
8917
|
|
|
8918
|
+
// src/tui/history.ts
|
|
8919
|
+
function valueAt(state, cursor) {
|
|
8920
|
+
if (cursor < 0) return state.draft;
|
|
8921
|
+
return state.history[state.history.length - 1 - cursor];
|
|
8922
|
+
}
|
|
8923
|
+
function navigateHistory(state, dir) {
|
|
8924
|
+
const last = state.history.length - 1;
|
|
8925
|
+
if (last < 0) return { value: state.draft, cursor: -1 };
|
|
8926
|
+
let cursor = state.cursor;
|
|
8927
|
+
if (dir === "up") {
|
|
8928
|
+
cursor = Math.min(cursor + 1, last);
|
|
8929
|
+
} else {
|
|
8930
|
+
cursor = Math.max(cursor - 1, -1);
|
|
8931
|
+
}
|
|
8932
|
+
return { value: valueAt(state, cursor), cursor };
|
|
8933
|
+
}
|
|
8934
|
+
var init_history = __esm({
|
|
8935
|
+
"src/tui/history.ts"() {
|
|
8936
|
+
init_esm_shims();
|
|
8937
|
+
}
|
|
8938
|
+
});
|
|
8939
|
+
|
|
8940
|
+
// src/tui/elapsed.ts
|
|
8941
|
+
function formatElapsed(ms) {
|
|
8942
|
+
const totalSec = Math.max(0, Math.floor(ms / 1e3));
|
|
8943
|
+
const h = Math.floor(totalSec / 3600);
|
|
8944
|
+
const m = Math.floor(totalSec % 3600 / 60);
|
|
8945
|
+
const s = totalSec % 60;
|
|
8946
|
+
if (h > 0) return `${h}h ${String(m).padStart(2, "0")}m`;
|
|
8947
|
+
if (m > 0) return `${m}m ${String(s).padStart(2, "0")}s`;
|
|
8948
|
+
return `${s}s`;
|
|
8949
|
+
}
|
|
8950
|
+
var init_elapsed = __esm({
|
|
8951
|
+
"src/tui/elapsed.ts"() {
|
|
8952
|
+
init_esm_shims();
|
|
8953
|
+
}
|
|
8954
|
+
});
|
|
8955
|
+
|
|
8693
8956
|
// src/tui/dispatch.ts
|
|
8694
8957
|
function parseCommand(input) {
|
|
8695
8958
|
const trimmed = input.trim();
|
|
@@ -8699,11 +8962,38 @@ function parseCommand(input) {
|
|
|
8699
8962
|
if (sp === -1) return { cmd: body.toLowerCase(), args: "" };
|
|
8700
8963
|
return { cmd: body.slice(0, sp).toLowerCase(), args: body.slice(sp + 1).trim() };
|
|
8701
8964
|
}
|
|
8965
|
+
function fuzzyScore(query, text) {
|
|
8966
|
+
const q = query.toLowerCase();
|
|
8967
|
+
const t = text.toLowerCase();
|
|
8968
|
+
if (q === "") return 0;
|
|
8969
|
+
let qi = 0;
|
|
8970
|
+
let score = 0;
|
|
8971
|
+
let lastMatch = -1;
|
|
8972
|
+
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
|
|
8973
|
+
if (t[ti] === q[qi]) {
|
|
8974
|
+
if (lastMatch === -1) score += ti;
|
|
8975
|
+
else score += ti - lastMatch - 1;
|
|
8976
|
+
lastMatch = ti;
|
|
8977
|
+
qi++;
|
|
8978
|
+
}
|
|
8979
|
+
}
|
|
8980
|
+
return qi === q.length ? score : null;
|
|
8981
|
+
}
|
|
8982
|
+
function fuzzyFilter(query, commands) {
|
|
8983
|
+
if (query.trim() === "") return [...commands];
|
|
8984
|
+
const scored = [];
|
|
8985
|
+
commands.forEach((cmd, idx) => {
|
|
8986
|
+
const score = fuzzyScore(query, cmd.name);
|
|
8987
|
+
if (score !== null) scored.push({ cmd, score, idx });
|
|
8988
|
+
});
|
|
8989
|
+
scored.sort((a, b) => a.score !== b.score ? a.score - b.score : a.idx - b.idx);
|
|
8990
|
+
return scored.map((s) => s.cmd);
|
|
8991
|
+
}
|
|
8702
8992
|
function filterCommands(input, extra = []) {
|
|
8703
8993
|
const trimmed = input.trim();
|
|
8704
8994
|
if (!trimmed.startsWith("/")) return [];
|
|
8705
|
-
const
|
|
8706
|
-
return [...COMMANDS, ...extra]
|
|
8995
|
+
const query = trimmed.slice(1).split(" ")[0];
|
|
8996
|
+
return fuzzyFilter(query, [...COMMANDS, ...extra]);
|
|
8707
8997
|
}
|
|
8708
8998
|
async function runAsyncCommand(port, parsed, _onLine) {
|
|
8709
8999
|
switch (parsed.cmd) {
|
|
@@ -8802,13 +9092,36 @@ function InteractiveApp({ dashboard, port, asyncPort, liveRunner, skillCommands
|
|
|
8802
9092
|
const [input, setInput] = useState("");
|
|
8803
9093
|
const [log48, setLog] = useState([]);
|
|
8804
9094
|
const [running, setRunning] = useState(false);
|
|
9095
|
+
const [elapsedMs, setElapsedMs] = useState(0);
|
|
8805
9096
|
const [showHelp, setShowHelp] = useState(false);
|
|
9097
|
+
useEffect(() => {
|
|
9098
|
+
if (!running) return;
|
|
9099
|
+
setElapsedMs(0);
|
|
9100
|
+
const startedAt = Date.now();
|
|
9101
|
+
const t = setInterval(() => setElapsedMs(Date.now() - startedAt), 1e3);
|
|
9102
|
+
return () => clearInterval(t);
|
|
9103
|
+
}, [running]);
|
|
9104
|
+
const [history, setHistory] = useState([]);
|
|
9105
|
+
const [histCursor, setHistCursor] = useState(-1);
|
|
9106
|
+
const [draft, setDraft] = useState("");
|
|
8806
9107
|
const append = (line) => setLog((prev) => [...prev, line].slice(-MAX_LOG_LINES));
|
|
9108
|
+
useInput((_input, key) => {
|
|
9109
|
+
if (phase !== "dashboard" || running) return;
|
|
9110
|
+
if (!key.upArrow && !key.downArrow) return;
|
|
9111
|
+
const effectiveDraft = histCursor === -1 ? input : draft;
|
|
9112
|
+
if (histCursor === -1) setDraft(input);
|
|
9113
|
+
const result = navigateHistory({ history, cursor: histCursor, draft: effectiveDraft }, key.upArrow ? "up" : "down");
|
|
9114
|
+
setHistCursor(result.cursor);
|
|
9115
|
+
setInput(result.value);
|
|
9116
|
+
});
|
|
8807
9117
|
const submit = (raw) => {
|
|
8808
9118
|
const parsed = parseCommand(raw);
|
|
8809
9119
|
setInput("");
|
|
8810
9120
|
const text = raw.trim();
|
|
8811
9121
|
if (text === "" || running) return;
|
|
9122
|
+
setHistory((prev) => [...prev, text]);
|
|
9123
|
+
setHistCursor(-1);
|
|
9124
|
+
setDraft("");
|
|
8812
9125
|
if (parsed.cmd === "quit") {
|
|
8813
9126
|
exit();
|
|
8814
9127
|
return;
|
|
@@ -8860,7 +9173,10 @@ ${skill.body}` : `Skill n\xE3o encontrada: ${parsed.cmd}`);
|
|
|
8860
9173
|
] }),
|
|
8861
9174
|
running && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
8862
9175
|
/* @__PURE__ */ jsx(Spinner, { type: "dots" }),
|
|
8863
|
-
" executando\u2026"
|
|
9176
|
+
" executando\u2026 ",
|
|
9177
|
+
formatElapsed(elapsedMs),
|
|
9178
|
+
" \xB7 ",
|
|
9179
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Esc para interromper" })
|
|
8864
9180
|
] }) }),
|
|
8865
9181
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(CommandBar, { value: input, onChange: setInput, onSubmit: submit, suggestions: filterCommands(input, skillCommands) }) })
|
|
8866
9182
|
] });
|
|
@@ -8872,6 +9188,8 @@ var init_interactive_app = __esm({
|
|
|
8872
9188
|
init_app();
|
|
8873
9189
|
init_banner_screen();
|
|
8874
9190
|
init_command_bar();
|
|
9191
|
+
init_history();
|
|
9192
|
+
init_elapsed();
|
|
8875
9193
|
init_dispatch();
|
|
8876
9194
|
MAX_LOG_LINES = 12;
|
|
8877
9195
|
}
|
|
@@ -9522,15 +9840,15 @@ function meanPoolAndNormalize(data, validTokens, dim) {
|
|
|
9522
9840
|
embedding[dVar] += data[tVar * dim + dVar];
|
|
9523
9841
|
}
|
|
9524
9842
|
}
|
|
9525
|
-
let
|
|
9843
|
+
let norm2 = 0;
|
|
9526
9844
|
for (let dVar = 0; dVar < dim; dVar++) {
|
|
9527
9845
|
embedding[dVar] /= validTokens;
|
|
9528
|
-
|
|
9846
|
+
norm2 += embedding[dVar] * embedding[dVar];
|
|
9529
9847
|
}
|
|
9530
|
-
|
|
9531
|
-
if (
|
|
9848
|
+
norm2 = Math.sqrt(norm2);
|
|
9849
|
+
if (norm2 > 0) {
|
|
9532
9850
|
for (let dVar = 0; dVar < dim; dVar++) {
|
|
9533
|
-
embedding[dVar] /=
|
|
9851
|
+
embedding[dVar] /= norm2;
|
|
9534
9852
|
}
|
|
9535
9853
|
}
|
|
9536
9854
|
return embedding;
|
|
@@ -10403,7 +10721,8 @@ async function launchTui(store2) {
|
|
|
10403
10721
|
return skills.map((s) => ({
|
|
10404
10722
|
name: s.name,
|
|
10405
10723
|
usage: `/${s.name}`,
|
|
10406
|
-
desc: `[${s.category}] ${s.description}
|
|
10724
|
+
desc: `[${s.category}] ${s.description}`,
|
|
10725
|
+
source: "skill"
|
|
10407
10726
|
}));
|
|
10408
10727
|
});
|
|
10409
10728
|
const instance = render(
|
|
@@ -11021,7 +11340,7 @@ init_store_port();
|
|
|
11021
11340
|
// src/cli/shared/enable-flow.ts
|
|
11022
11341
|
init_esm_shims();
|
|
11023
11342
|
init_flow_config();
|
|
11024
|
-
function
|
|
11343
|
+
function setFlowEnabled(store2, enabled) {
|
|
11025
11344
|
const raw = store2.getProjectSetting(FLOW_CONFIG_SETTING_KEY);
|
|
11026
11345
|
let current = {};
|
|
11027
11346
|
if (raw) {
|
|
@@ -11031,24 +11350,88 @@ function enableFlowConfig(store2) {
|
|
|
11031
11350
|
current = {};
|
|
11032
11351
|
}
|
|
11033
11352
|
}
|
|
11034
|
-
store2.setProjectSetting(FLOW_CONFIG_SETTING_KEY, JSON.stringify({ ...current, enabled
|
|
11353
|
+
store2.setProjectSetting(FLOW_CONFIG_SETTING_KEY, JSON.stringify({ ...current, enabled }));
|
|
11354
|
+
}
|
|
11355
|
+
function enableFlowConfig(store2) {
|
|
11356
|
+
setFlowEnabled(store2, true);
|
|
11357
|
+
}
|
|
11358
|
+
|
|
11359
|
+
// src/cli/commands/profile-cmd.ts
|
|
11360
|
+
init_esm_shims();
|
|
11361
|
+
|
|
11362
|
+
// src/core/config/profiles.ts
|
|
11363
|
+
init_esm_shims();
|
|
11364
|
+
var BUILT_IN_PROFILES = {
|
|
11365
|
+
fast: { modelTier: "cheap", flow: false, retries: 1 },
|
|
11366
|
+
build: { modelTier: "build", flow: true, retries: 2 },
|
|
11367
|
+
frontier: { modelTier: "frontier", flow: true, retries: 3 }
|
|
11368
|
+
};
|
|
11369
|
+
function resolveProfile(name) {
|
|
11370
|
+
return BUILT_IN_PROFILES[name];
|
|
11371
|
+
}
|
|
11372
|
+
function listProfiles() {
|
|
11373
|
+
return Object.keys(BUILT_IN_PROFILES);
|
|
11035
11374
|
}
|
|
11375
|
+
|
|
11376
|
+
// src/cli/commands/profile-cmd.ts
|
|
11377
|
+
init_tier_router();
|
|
11036
11378
|
function output7(msg) {
|
|
11037
11379
|
process.stdout.write(msg + "\n");
|
|
11038
11380
|
}
|
|
11381
|
+
function applyProfile(store2, name) {
|
|
11382
|
+
const profile = resolveProfile(name);
|
|
11383
|
+
if (!profile) return void 0;
|
|
11384
|
+
store2.setProjectSetting("model", resolveTierModel(profile.modelTier));
|
|
11385
|
+
setFlowEnabled(store2, profile.flow);
|
|
11386
|
+
return profile;
|
|
11387
|
+
}
|
|
11388
|
+
function profileCommand() {
|
|
11389
|
+
const cmd = new Command("profile").description("Bundles de trabalho: tier de modelo + flow + retries");
|
|
11390
|
+
cmd.command("list").description("Lista os profiles dispon\xEDveis").action(() => {
|
|
11391
|
+
for (const name of listProfiles()) {
|
|
11392
|
+
const p = BUILT_IN_PROFILES[name];
|
|
11393
|
+
output7(`${name.padEnd(10)} tier=${p.modelTier} flow=${p.flow} retries=${p.retries}`);
|
|
11394
|
+
}
|
|
11395
|
+
});
|
|
11396
|
+
cmd.command("show <nome>").description("Detalha um profile").action((nome) => {
|
|
11397
|
+
const p = resolveProfile(nome);
|
|
11398
|
+
if (!p) {
|
|
11399
|
+
output7(`Profile desconhecido: ${nome}. Tente 'profile list'.`);
|
|
11400
|
+
process.exitCode = 1;
|
|
11401
|
+
return;
|
|
11402
|
+
}
|
|
11403
|
+
output7(`${nome}: tier=${p.modelTier} \xB7 flow=${p.flow} \xB7 retries=${p.retries} \xB7 modelo=${resolveTierModel(p.modelTier)}`);
|
|
11404
|
+
});
|
|
11405
|
+
return cmd;
|
|
11406
|
+
}
|
|
11407
|
+
function output8(msg) {
|
|
11408
|
+
process.stdout.write(msg + "\n");
|
|
11409
|
+
}
|
|
11039
11410
|
function autopilotCommand() {
|
|
11040
|
-
return new Command("autopilot").description("Loop aut\xF4nomo com guardrails: next \u2192 in_progress \u2192 DoD \u2192 done|escalate (WIP=1)").option("-d, --dir <dir>", "Diret\xF3rio do projeto", process.cwd()).option("-m, --max <n>", "Budget: m\xE1ximo de tasks por sess\xE3o (cost-runaway guard)", "5").option("--simulate", "Simula impl bem-sucedida (deixa o DoD real decidir) \u2014 n\xE3o escreve c\xF3digo", false).option("--live", "Invoca o modelo real via SDK do Copilot: gera plano \u2192 aplica \u2192 roda testes \u2192 done|escala", false).option("--test-cmd <cmd>", "Comando de teste rodado no modo --live quando o plano n\xE3o traz um", "npm test").option("--retries <n>", "Tentativas por task no --live (retry com feedback compacto do teste)", "2").option("--flow", "Ativa a dilui\xE7\xE3o de contexto por \u03BB_flow (hipofrontalidade) no --live", false).action(
|
|
11411
|
+
return new Command("autopilot").description("Loop aut\xF4nomo com guardrails: next \u2192 in_progress \u2192 DoD \u2192 done|escalate (WIP=1)").option("-d, --dir <dir>", "Diret\xF3rio do projeto", process.cwd()).option("-m, --max <n>", "Budget: m\xE1ximo de tasks por sess\xE3o (cost-runaway guard)", "5").option("--simulate", "Simula impl bem-sucedida (deixa o DoD real decidir) \u2014 n\xE3o escreve c\xF3digo", false).option("--live", "Invoca o modelo real via SDK do Copilot: gera plano \u2192 aplica \u2192 roda testes \u2192 done|escala", false).option("--test-cmd <cmd>", "Comando de teste rodado no modo --live quando o plano n\xE3o traz um", "npm test").option("--retries <n>", "Tentativas por task no --live (retry com feedback compacto do teste)", "2").option("--flow", "Ativa a dilui\xE7\xE3o de contexto por \u03BB_flow (hipofrontalidade) no --live", false).option("--profile <nome>", "Aplica um bundle de trabalho (fast|build|frontier): tier+flow+retries").action(
|
|
11041
11412
|
async (opts) => {
|
|
11042
11413
|
const store2 = openStoreOrFail(opts.dir, { requireExisting: true });
|
|
11043
11414
|
try {
|
|
11044
11415
|
const maxIterations = Math.max(1, parseInt(opts.max, 10) || 5);
|
|
11045
11416
|
const port = makeStorePort(store2);
|
|
11417
|
+
let retriesOverride;
|
|
11418
|
+
if (opts.profile) {
|
|
11419
|
+
const applied = applyProfile(store2, opts.profile);
|
|
11420
|
+
if (!applied) {
|
|
11421
|
+
output8(`Profile desconhecido: ${opts.profile}. Tente 'profile list'.`);
|
|
11422
|
+
store2.close();
|
|
11423
|
+
return;
|
|
11424
|
+
}
|
|
11425
|
+
retriesOverride = applied.retries;
|
|
11426
|
+
output8(`[PROFILE] ${opts.profile}: tier=${applied.modelTier} flow=${applied.flow} retries=${applied.retries}
|
|
11427
|
+
`);
|
|
11428
|
+
}
|
|
11046
11429
|
if (opts.flow) {
|
|
11047
11430
|
enableFlowConfig(store2);
|
|
11048
|
-
|
|
11431
|
+
output8("[FLOW] \u03BB_flow ativo: contexto do grafo dilu\xEDdo por \u03A6(t) (esquecimento din\xE2mico).\n");
|
|
11049
11432
|
}
|
|
11050
|
-
if (opts.simulate)
|
|
11051
|
-
if (opts.live)
|
|
11433
|
+
if (opts.simulate) output8("[SIMULA\xC7\xC3O] impl tratada como verde \u2014 DoD real decide prontid\xE3o.\n");
|
|
11434
|
+
if (opts.live) output8("[LIVE] modelo via SDK do Copilot: gera plano \u2192 aplica no workspace \u2192 roda testes.\n");
|
|
11052
11435
|
let implement;
|
|
11053
11436
|
let ledger;
|
|
11054
11437
|
if (opts.live) {
|
|
@@ -11057,11 +11440,11 @@ function autopilotCommand() {
|
|
|
11057
11440
|
store: store2,
|
|
11058
11441
|
dir: opts.dir,
|
|
11059
11442
|
testCmd: opts.testCmd,
|
|
11060
|
-
retries: parseInt(opts.retries, 10) || 2,
|
|
11443
|
+
retries: retriesOverride ?? (parseInt(opts.retries, 10) || 2),
|
|
11061
11444
|
ledger,
|
|
11062
|
-
onLog:
|
|
11445
|
+
onLog: output8
|
|
11063
11446
|
});
|
|
11064
|
-
if (live.repoSymbolCount > 0)
|
|
11447
|
+
if (live.repoSymbolCount > 0) output8(`[LIVE] repo-map: ${live.repoSymbolCount} s\xEDmbolo(s) indexado(s).`);
|
|
11065
11448
|
implement = live.implement;
|
|
11066
11449
|
} else if (opts.simulate) {
|
|
11067
11450
|
implement = () => true;
|
|
@@ -11071,28 +11454,28 @@ function autopilotCommand() {
|
|
|
11071
11454
|
const result = await runAutopilot(port, { maxIterations, implement });
|
|
11072
11455
|
for (const s of result.steps) {
|
|
11073
11456
|
const icon = s.action === "done" ? "\u2713" : s.action === "escalated" ? "\u26A0" : "\u2192";
|
|
11074
|
-
|
|
11457
|
+
output8(`${icon} ${s.nodeId} ${s.title} [${s.action}] ${s.detail}`);
|
|
11075
11458
|
}
|
|
11076
|
-
|
|
11459
|
+
output8(`
|
|
11077
11460
|
Resumo: ${result.completed} conclu\xEDda(s), ${result.escalated} escalada(s). Parou: ${result.stopped}`);
|
|
11078
11461
|
if (ledger) {
|
|
11079
11462
|
const totals = ledger.totals();
|
|
11080
|
-
|
|
11463
|
+
output8(`
|
|
11081
11464
|
Tokens (sess\xE3o): ${totals.total} (in ${totals.tokensIn} / out ${totals.tokensOut}) em ${totals.calls} chamada(s)`);
|
|
11082
11465
|
for (const t of ledger.tasks()) {
|
|
11083
|
-
|
|
11466
|
+
output8(` ${t.nodeId}: ${t.total} tok (in ${t.tokensIn} / out ${t.tokensOut}, ${t.calls} chamada(s))`);
|
|
11084
11467
|
}
|
|
11085
11468
|
if (result.completed > 0) {
|
|
11086
|
-
|
|
11469
|
+
output8(` m\xE9dia/task conclu\xEDda: ${Math.round(totals.total / result.completed)} tok`);
|
|
11087
11470
|
}
|
|
11088
11471
|
if (totals.calls > 0) {
|
|
11089
11472
|
const sessionId = `autopilot_${randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
11090
11473
|
const rows = persistLedger(store2.getDb(), ledger, { sessionId, provider: "copilot" });
|
|
11091
|
-
|
|
11474
|
+
output8(` ${rows} chamada(s) persistida(s) (session ${sessionId})`);
|
|
11092
11475
|
}
|
|
11093
11476
|
}
|
|
11094
11477
|
if (!opts.simulate && !opts.live && result.stopped === "escalation") {
|
|
11095
|
-
|
|
11478
|
+
output8("\nDica: --simulate exercita o loop + gate DoD; --live invoca o modelo real via SDK do Copilot.");
|
|
11096
11479
|
}
|
|
11097
11480
|
} finally {
|
|
11098
11481
|
store2.close();
|
|
@@ -11105,7 +11488,7 @@ Tokens (sess\xE3o): ${totals.total} (in ${totals.tokensIn} / out ${totals.tokens
|
|
|
11105
11488
|
init_esm_shims();
|
|
11106
11489
|
init_tier_router();
|
|
11107
11490
|
var SETTING_KEY = "model";
|
|
11108
|
-
function
|
|
11491
|
+
function output9(msg) {
|
|
11109
11492
|
process.stdout.write(msg + "\n");
|
|
11110
11493
|
}
|
|
11111
11494
|
function readConfig(dir) {
|
|
@@ -11122,29 +11505,29 @@ function modelCommand() {
|
|
|
11122
11505
|
"Seleciona/inspeciona o modelo do tier-router (pool do Copilot CLI; 'auto' roteia por tarefa)"
|
|
11123
11506
|
);
|
|
11124
11507
|
cmd.command("list").description("Lista o pool agrupado por tier").action(() => {
|
|
11125
|
-
|
|
11508
|
+
output9("auto \u2014 roteia por tarefa (cheap classifica \xB7 build implementa \xB7 frontier planeja)\n");
|
|
11126
11509
|
for (const tier of MODEL_TIERS) {
|
|
11127
|
-
|
|
11510
|
+
output9(`[${tier}]`);
|
|
11128
11511
|
for (const m of modelsForTier(tier)) {
|
|
11129
11512
|
const mark = m.id === DEFAULT_MODEL ? " (default)" : "";
|
|
11130
|
-
|
|
11513
|
+
output9(` ${m.id} \u2014 ${m.label}${mark}`);
|
|
11131
11514
|
}
|
|
11132
11515
|
}
|
|
11133
11516
|
});
|
|
11134
11517
|
cmd.command("current").description("Mostra o modelo/modo selecionado").option("-d, --dir <dir>", "Diret\xF3rio do projeto", process.cwd()).action((opts) => {
|
|
11135
11518
|
const config = readConfig(opts.dir);
|
|
11136
|
-
|
|
11519
|
+
output9(config.mode === "auto" ? "auto (roteamento por tarefa)" : `pinned: ${config.modelId}`);
|
|
11137
11520
|
});
|
|
11138
11521
|
cmd.command("set").description("Fixa um modelo (id do pool) ou 'auto' para roteamento por tarefa").argument("<idOrAuto>", "ID do modelo ou 'auto'").option("-d, --dir <dir>", "Diret\xF3rio do projeto", process.cwd()).action((idOrAuto, opts) => {
|
|
11139
11522
|
const value = idOrAuto.trim();
|
|
11140
11523
|
if (value !== "auto" && !isKnownModel(value)) {
|
|
11141
|
-
|
|
11524
|
+
output9(`Modelo desconhecido: "${value}". Rode 'model list' para ver o pool.`);
|
|
11142
11525
|
process.exit(1);
|
|
11143
11526
|
}
|
|
11144
11527
|
const store2 = openStoreOrFail(opts.dir, { requireExisting: true });
|
|
11145
11528
|
try {
|
|
11146
11529
|
store2.setProjectSetting(SETTING_KEY, value);
|
|
11147
|
-
|
|
11530
|
+
output9(value === "auto" ? "Modo: auto (roteamento por tarefa)." : `Modelo fixado: ${value}.`);
|
|
11148
11531
|
} finally {
|
|
11149
11532
|
store2.close();
|
|
11150
11533
|
}
|
|
@@ -11152,11 +11535,11 @@ function modelCommand() {
|
|
|
11152
11535
|
cmd.command("route").description("Mostra qual modelo o router escolhe para um tipo de tarefa").argument("<kind>", `Tipo: ${TaskKindSchema.options.join("|")}`).option("-d, --dir <dir>", "Diret\xF3rio do projeto", process.cwd()).action((kind, opts) => {
|
|
11153
11536
|
const parsed = TaskKindSchema.safeParse(kind);
|
|
11154
11537
|
if (!parsed.success) {
|
|
11155
|
-
|
|
11538
|
+
output9(`Tipo inv\xE1lido: "${kind}". Esperado: ${TaskKindSchema.options.join(", ")}.`);
|
|
11156
11539
|
process.exit(1);
|
|
11157
11540
|
}
|
|
11158
11541
|
const config = readConfig(opts.dir);
|
|
11159
|
-
|
|
11542
|
+
output9(`${kind} \u2192 ${routeModel(config, parsed.data)}`);
|
|
11160
11543
|
});
|
|
11161
11544
|
return cmd;
|
|
11162
11545
|
}
|
|
@@ -11164,7 +11547,7 @@ function modelCommand() {
|
|
|
11164
11547
|
// src/cli/commands/metrics-cmd.ts
|
|
11165
11548
|
init_esm_shims();
|
|
11166
11549
|
init_llm_call_ledger();
|
|
11167
|
-
function
|
|
11550
|
+
function output10(msg) {
|
|
11168
11551
|
process.stdout.write(msg + "\n");
|
|
11169
11552
|
}
|
|
11170
11553
|
function metricsCommand() {
|
|
@@ -11173,30 +11556,30 @@ function metricsCommand() {
|
|
|
11173
11556
|
try {
|
|
11174
11557
|
const summary = summarizeLedger(store2.getDb(), { sessionId: opts.session });
|
|
11175
11558
|
if (summary.totals.calls === 0) {
|
|
11176
|
-
|
|
11559
|
+
output10("Sem chamadas de modelo registradas. Rode `autopilot --live` para gerar m\xE9tricas.");
|
|
11177
11560
|
return;
|
|
11178
11561
|
}
|
|
11179
11562
|
const usd = (n) => `$${n.toFixed(4)}`;
|
|
11180
11563
|
const { totals } = summary;
|
|
11181
11564
|
const taskCount = summary.byTask.length;
|
|
11182
11565
|
const avgCost = taskCount > 0 ? totals.costUsd / taskCount : 0;
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11566
|
+
output10(`Tokens totais: ${totals.total} (in ${totals.tokensIn} / out ${totals.tokensOut}) em ${totals.calls} chamada(s)`);
|
|
11567
|
+
output10(`Custo total: \u2248 ${usd(totals.costUsd)}`);
|
|
11568
|
+
output10(`M\xE9dia por task: ${summary.avgTokensPerTask} tok \u2248 ${usd(avgCost)} | ${taskCount} task(s), ${summary.bySession.length} sess\xE3o(\xF5es)`);
|
|
11186
11569
|
const top = Math.max(1, parseInt(opts.top, 10) || 10);
|
|
11187
|
-
|
|
11570
|
+
output10(`
|
|
11188
11571
|
Tokens por task (top ${top}):`);
|
|
11189
11572
|
for (const t of summary.byTask.slice(0, top)) {
|
|
11190
|
-
|
|
11573
|
+
output10(` ${t.nodeId}: ${t.total} tok \u2248 ${usd(t.costUsd)} (in ${t.tokensIn} / out ${t.tokensOut}, ${t.calls} chamada(s))`);
|
|
11191
11574
|
}
|
|
11192
11575
|
if (!opts.session) {
|
|
11193
|
-
|
|
11576
|
+
output10("\nTokens por sess\xE3o:");
|
|
11194
11577
|
for (const s of summary.bySession) {
|
|
11195
|
-
|
|
11578
|
+
output10(` ${s.sessionId}: ${s.total} tok \u2248 ${usd(s.costUsd)} (${s.calls} chamada(s))`);
|
|
11196
11579
|
}
|
|
11197
11580
|
}
|
|
11198
11581
|
if (totals.costUsd === 0) {
|
|
11199
|
-
|
|
11582
|
+
output10("\nNota: custo $0 \u2014 modelos usados n\xE3o t\xEAm pre\xE7o cadastrado (ver cost-tracker MODEL_PRICING).");
|
|
11200
11583
|
}
|
|
11201
11584
|
} finally {
|
|
11202
11585
|
store2.close();
|
|
@@ -11211,7 +11594,7 @@ init_resolve_adapter();
|
|
|
11211
11594
|
init_implementation_executor();
|
|
11212
11595
|
init_implement_attempt();
|
|
11213
11596
|
init_token_ledger();
|
|
11214
|
-
function
|
|
11597
|
+
function output11(msg) {
|
|
11215
11598
|
process.stdout.write(msg + "\n");
|
|
11216
11599
|
}
|
|
11217
11600
|
function runCommand() {
|
|
@@ -11222,7 +11605,7 @@ function runCommand() {
|
|
|
11222
11605
|
const maxAttempts = Math.max(1, parseInt(opts.retries, 10) || 2);
|
|
11223
11606
|
const ledger = new TokenLedger();
|
|
11224
11607
|
const node = { id: `run_${randomUUID().replace(/-/g, "").slice(0, 8)}`, title: prompt };
|
|
11225
|
-
|
|
11608
|
+
output11(`[run] ${client.modelFor("implement")} via ${resolved.kind === "api" ? "API HTTP" : "CLI"} \u2192 "${prompt}"`);
|
|
11226
11609
|
const outcome = await attemptImplementation(
|
|
11227
11610
|
{
|
|
11228
11611
|
generate: async (p) => {
|
|
@@ -11242,7 +11625,7 @@ function runCommand() {
|
|
|
11242
11625
|
);
|
|
11243
11626
|
const files = outcome.lastResult?.applied.length ?? 0;
|
|
11244
11627
|
const totals = ledger.totals();
|
|
11245
|
-
|
|
11628
|
+
output11(
|
|
11246
11629
|
`${outcome.success ? "\u2713" : "\u26A0"} ${outcome.attempts} tentativa(s), ${files} arquivo(s), ${totals.total} tok (in ${totals.tokensIn} / out ${totals.tokensOut}) \u2192 ${outcome.success ? "verde" : "falhou (testes n\xE3o passaram)"}`
|
|
11247
11630
|
);
|
|
11248
11631
|
if (!outcome.success) process.exitCode = 1;
|
|
@@ -11266,7 +11649,7 @@ function tuiCommand() {
|
|
|
11266
11649
|
// src/cli/commands/login-cmd.ts
|
|
11267
11650
|
init_esm_shims();
|
|
11268
11651
|
init_copilot_auth();
|
|
11269
|
-
function
|
|
11652
|
+
function output12(msg) {
|
|
11270
11653
|
process.stdout.write(msg + "\n");
|
|
11271
11654
|
}
|
|
11272
11655
|
function loginCommand() {
|
|
@@ -11274,15 +11657,15 @@ function loginCommand() {
|
|
|
11274
11657
|
const path22 = defaultAuthPath();
|
|
11275
11658
|
if (opts.token) {
|
|
11276
11659
|
saveAuth(path22, { githubToken: opts.token });
|
|
11277
|
-
|
|
11660
|
+
output12(`\u2713 Token salvo em ${path22}. Provider HTTP do Copilot habilitado.`);
|
|
11278
11661
|
return;
|
|
11279
11662
|
}
|
|
11280
11663
|
const device = await requestDeviceCode(globalThis.fetch);
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11664
|
+
output12("\nPara autenticar, abra:");
|
|
11665
|
+
output12(` ${device.verificationUri}`);
|
|
11666
|
+
output12(`e informe o c\xF3digo: \x1B[1m${device.userCode}\x1B[0m
|
|
11284
11667
|
`);
|
|
11285
|
-
|
|
11668
|
+
output12("Aguardando autoriza\xE7\xE3o\u2026 (Ctrl+C para cancelar)");
|
|
11286
11669
|
const deadline = Date.now() + device.expiresIn * 1e3;
|
|
11287
11670
|
let interval = Math.max(1, device.interval);
|
|
11288
11671
|
while (Date.now() < deadline) {
|
|
@@ -11290,13 +11673,13 @@ function loginCommand() {
|
|
|
11290
11673
|
const r = await pollForAccessToken(globalThis.fetch, device.deviceCode);
|
|
11291
11674
|
if ("accessToken" in r) {
|
|
11292
11675
|
saveAuth(path22, { githubToken: r.accessToken });
|
|
11293
|
-
|
|
11676
|
+
output12(`
|
|
11294
11677
|
\u2713 Autenticado. Token salvo em ${path22}. Provider HTTP do Copilot habilitado.`);
|
|
11295
11678
|
return;
|
|
11296
11679
|
}
|
|
11297
11680
|
if ("slowDown" in r) interval += 5;
|
|
11298
11681
|
}
|
|
11299
|
-
|
|
11682
|
+
output12("\n\u26A0 Tempo esgotado sem autoriza\xE7\xE3o. Rode `agf login` de novo.");
|
|
11300
11683
|
process.exitCode = 1;
|
|
11301
11684
|
});
|
|
11302
11685
|
}
|
|
@@ -11305,9 +11688,9 @@ function logoutCommand() {
|
|
|
11305
11688
|
const path22 = defaultAuthPath();
|
|
11306
11689
|
if (existsSync(path22) && loadAuth(path22)) {
|
|
11307
11690
|
rmSync(path22, { force: true });
|
|
11308
|
-
|
|
11691
|
+
output12(`\u2713 Logout \u2014 ${path22} removido.`);
|
|
11309
11692
|
} else {
|
|
11310
|
-
|
|
11693
|
+
output12("Nenhum login salvo.");
|
|
11311
11694
|
}
|
|
11312
11695
|
});
|
|
11313
11696
|
}
|
|
@@ -13968,7 +14351,7 @@ function reapDaemons(options = {}) {
|
|
|
13968
14351
|
// src/cli/commands/daemon-cmd.ts
|
|
13969
14352
|
init_logger();
|
|
13970
14353
|
var log44 = createLogger({ layer: "cli", source: "daemon.ts" });
|
|
13971
|
-
function
|
|
14354
|
+
function output13(msg) {
|
|
13972
14355
|
process.stdout.write(msg + "\n");
|
|
13973
14356
|
}
|
|
13974
14357
|
function daemonCommand() {
|
|
@@ -13979,23 +14362,23 @@ function daemonCommand() {
|
|
|
13979
14362
|
const prefix = opts.dryRun ? "[dry-run] " : "";
|
|
13980
14363
|
for (const a of report.actions) {
|
|
13981
14364
|
if (a.outcome === "kept") continue;
|
|
13982
|
-
|
|
14365
|
+
output13(`${prefix}${a.outcome}: ${a.stateDir}${a.pid ? ` (pid=${a.pid})` : ""} \u2014 ${a.reason}`);
|
|
13983
14366
|
}
|
|
13984
|
-
|
|
14367
|
+
output13(
|
|
13985
14368
|
`${prefix}daemon prune: scanned ${report.scanned}, killed ${report.killed}, removed ${report.removed}, kept ${report.kept}`
|
|
13986
14369
|
);
|
|
13987
14370
|
});
|
|
13988
14371
|
cmd.command("list").description("List daemon state directories and their status (read-only)").action(() => {
|
|
13989
14372
|
const report = reapDaemons({ dryRun: true });
|
|
13990
14373
|
if (report.scanned === 0) {
|
|
13991
|
-
|
|
14374
|
+
output13("daemon list: no daemon state directories found");
|
|
13992
14375
|
return;
|
|
13993
14376
|
}
|
|
13994
14377
|
for (const a of report.actions) {
|
|
13995
14378
|
const would = a.outcome === "kept" ? "alive" : `stale \u2192 would ${a.outcome}`;
|
|
13996
|
-
|
|
14379
|
+
output13(`${would}: ${a.stateDir}${a.pid ? ` (pid=${a.pid})` : ""} \u2014 ${a.reason}`);
|
|
13997
14380
|
}
|
|
13998
|
-
|
|
14381
|
+
output13(`daemon list: ${report.scanned} state dir(s), ${report.kept} alive`);
|
|
13999
14382
|
});
|
|
14000
14383
|
return cmd;
|
|
14001
14384
|
}
|
|
@@ -14047,7 +14430,7 @@ function formatProviderReport(report) {
|
|
|
14047
14430
|
init_errors();
|
|
14048
14431
|
init_logger();
|
|
14049
14432
|
var log45 = createLogger({ layer: "cli", source: "doctor.ts" });
|
|
14050
|
-
function
|
|
14433
|
+
function output14(msg) {
|
|
14051
14434
|
process.stdout.write(msg + "\n");
|
|
14052
14435
|
}
|
|
14053
14436
|
var LEVEL_ICON2 = {
|
|
@@ -14070,31 +14453,31 @@ function doctorCommand() {
|
|
|
14070
14453
|
if (opts.providers) {
|
|
14071
14454
|
const providerReport = checkProviders();
|
|
14072
14455
|
if (opts.json) {
|
|
14073
|
-
|
|
14456
|
+
output14(JSON.stringify(providerReport, null, 2));
|
|
14074
14457
|
} else {
|
|
14075
|
-
|
|
14458
|
+
output14("mcp-graph doctor \u2014 LLM providers\n");
|
|
14076
14459
|
for (const line of formatProviderReport(providerReport)) {
|
|
14077
|
-
|
|
14460
|
+
output14(line);
|
|
14078
14461
|
}
|
|
14079
14462
|
}
|
|
14080
14463
|
return;
|
|
14081
14464
|
}
|
|
14082
14465
|
const report = await runDoctor(opts.dir);
|
|
14083
14466
|
if (opts.json) {
|
|
14084
|
-
|
|
14467
|
+
output14(JSON.stringify(report, null, 2));
|
|
14085
14468
|
} else {
|
|
14086
|
-
|
|
14469
|
+
output14("mcp-graph doctor\n");
|
|
14087
14470
|
for (const check of report.checks) {
|
|
14088
|
-
|
|
14471
|
+
output14(formatCheck(check));
|
|
14089
14472
|
}
|
|
14090
|
-
|
|
14091
|
-
|
|
14473
|
+
output14("");
|
|
14474
|
+
output14(
|
|
14092
14475
|
`Summary: ${report.summary.ok} ok, ${report.summary.warning} warnings, ${report.summary.error} errors`
|
|
14093
14476
|
);
|
|
14094
14477
|
if (report.passed) {
|
|
14095
|
-
|
|
14478
|
+
output14("\nAll critical checks passed.");
|
|
14096
14479
|
} else {
|
|
14097
|
-
|
|
14480
|
+
output14("\nSome critical checks failed. Fix errors above.");
|
|
14098
14481
|
}
|
|
14099
14482
|
}
|
|
14100
14483
|
if (!report.passed) {
|
|
@@ -14178,13 +14561,13 @@ function pruneOrphanWorktrees(options) {
|
|
|
14178
14561
|
}
|
|
14179
14562
|
}
|
|
14180
14563
|
try {
|
|
14181
|
-
const
|
|
14564
|
+
const output17 = execSync("git worktree prune --verbose", execOpts).toString();
|
|
14182
14565
|
if (reapedBranches > 0 || reapedWorktrees > 0) {
|
|
14183
14566
|
log46.info("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, ttlMs });
|
|
14184
14567
|
} else {
|
|
14185
|
-
log46.debug("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, output:
|
|
14568
|
+
log46.debug("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, output: output17 });
|
|
14186
14569
|
}
|
|
14187
|
-
return { pruned: true, reapedBranches, reapedWorktrees, output:
|
|
14570
|
+
return { pruned: true, reapedBranches, reapedWorktrees, output: output17 };
|
|
14188
14571
|
} catch (err) {
|
|
14189
14572
|
const error = String(err);
|
|
14190
14573
|
log46.debug("shadow-branch:prune-failed", { error });
|
|
@@ -14195,7 +14578,7 @@ function pruneOrphanWorktrees(options) {
|
|
|
14195
14578
|
// src/cli/commands/gc-cmd.ts
|
|
14196
14579
|
init_logger();
|
|
14197
14580
|
var log47 = createLogger({ layer: "cli", source: "gc.ts" });
|
|
14198
|
-
function
|
|
14581
|
+
function output15(msg) {
|
|
14199
14582
|
process.stdout.write(msg + "\n");
|
|
14200
14583
|
}
|
|
14201
14584
|
function gcCommand() {
|
|
@@ -14205,9 +14588,9 @@ function gcCommand() {
|
|
|
14205
14588
|
log47.info("cli:gc:start", { dir: opts.dir, ttlMs });
|
|
14206
14589
|
const result = pruneOrphanWorktrees({ cwd: opts.dir, ttlMs });
|
|
14207
14590
|
if (result.pruned) {
|
|
14208
|
-
|
|
14591
|
+
output15(`gc: reaped ${result.reapedBranches} branches, ${result.reapedWorktrees} worktrees`);
|
|
14209
14592
|
} else {
|
|
14210
|
-
|
|
14593
|
+
output15(`gc: failed \u2014 ${result.error ?? "unknown error"}`);
|
|
14211
14594
|
process.exit(1);
|
|
14212
14595
|
}
|
|
14213
14596
|
});
|
|
@@ -14216,7 +14599,7 @@ function gcCommand() {
|
|
|
14216
14599
|
// src/cli/commands/skill-cmd.ts
|
|
14217
14600
|
init_esm_shims();
|
|
14218
14601
|
init_skill_registry();
|
|
14219
|
-
function
|
|
14602
|
+
function output16(msg) {
|
|
14220
14603
|
process.stdout.write(msg + "\n");
|
|
14221
14604
|
}
|
|
14222
14605
|
function skillCommand() {
|
|
@@ -14230,26 +14613,26 @@ function skillCommand() {
|
|
|
14230
14613
|
if (seen.has(s.name)) continue;
|
|
14231
14614
|
seen.add(s.name);
|
|
14232
14615
|
count += 1;
|
|
14233
|
-
|
|
14616
|
+
output16(`${s.name.padEnd(28)} [${s.category}] ${s.description}`);
|
|
14234
14617
|
}
|
|
14235
14618
|
}
|
|
14236
|
-
if (count === 0)
|
|
14237
|
-
else
|
|
14619
|
+
if (count === 0) output16("Nenhuma skill encontrada.");
|
|
14620
|
+
else output16(`
|
|
14238
14621
|
${count} skill(s).`);
|
|
14239
14622
|
});
|
|
14240
14623
|
cmd.command("show <nome>").description("Imprime as instru\xE7\xF5es completas de uma skill").option("-d, --dir <dir>", "Raiz do projeto", process.cwd()).action((nome, opts) => {
|
|
14241
14624
|
for (const root of defaultSkillRoots(opts.dir)) {
|
|
14242
14625
|
const found = invokeSkill(root, nome);
|
|
14243
14626
|
if (found) {
|
|
14244
|
-
|
|
14245
|
-
|
|
14246
|
-
if (found.phases.length > 0)
|
|
14247
|
-
|
|
14248
|
-
|
|
14627
|
+
output16(`=== ${found.name} ===`);
|
|
14628
|
+
output16(`[${found.category}] ${found.description}`);
|
|
14629
|
+
if (found.phases.length > 0) output16(`fases: ${found.phases.join(", ")}`);
|
|
14630
|
+
output16("");
|
|
14631
|
+
output16(found.body);
|
|
14249
14632
|
return;
|
|
14250
14633
|
}
|
|
14251
14634
|
}
|
|
14252
|
-
|
|
14635
|
+
output16(`Skill n\xE3o encontrada: ${nome}. Tente 'skill list'.`);
|
|
14253
14636
|
process.exitCode = 1;
|
|
14254
14637
|
});
|
|
14255
14638
|
return cmd;
|
|
@@ -14276,6 +14659,7 @@ program.addCommand(daemonCommand());
|
|
|
14276
14659
|
program.addCommand(doctorCommand());
|
|
14277
14660
|
program.addCommand(gcCommand());
|
|
14278
14661
|
program.addCommand(skillCommand());
|
|
14662
|
+
program.addCommand(profileCommand());
|
|
14279
14663
|
function shouldLaunchTui() {
|
|
14280
14664
|
const noArgs = process.argv.length <= 2;
|
|
14281
14665
|
const isTty = Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-graph-workflow/agent-graph-flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Agente SWE autônomo, local-first e token-frugal: PRD → grafo de execução persistente, TDD obrigatório, custo de token brutalmente baixo. AGPL v3.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|