@mcp-graph-workflow/agent-graph-flow 0.1.4 → 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.
Files changed (2) hide show
  1. package/dist/cli/index.js +1274 -268
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -11,7 +11,7 @@ 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
16
  import { createElement, useState, useCallback, useEffect } from 'react';
17
17
  import TextInput from 'ink-text-input';
@@ -2863,6 +2863,33 @@ var init_migrations = __esm({
2863
2863
  FROM events e
2864
2864
  WHERE e.kind = 'tool.completed'
2865
2865
  AND e.subjectRef_kind = 'mcp.tool';
2866
+ `
2867
+ },
2868
+ {
2869
+ version: 96,
2870
+ // λ_flow (transient-hypofrontality) telemetry. Records, per context call, the
2871
+ // flow state (Φ, λ_flow) and how much topological decay pruned vs. pinned,
2872
+ // tagged flow_on/flow_off for A/B. Telemetry-only — never affects graph state.
2873
+ // Pairs with flow-metrics-store.ts / flow-report.ts.
2874
+ description: "flow_metrics \u2014 \u03BB_flow (hipofrontalidade) A/B telemetry",
2875
+ sql: `
2876
+ CREATE TABLE IF NOT EXISTS flow_metrics (
2877
+ id TEXT PRIMARY KEY,
2878
+ project_id TEXT NOT NULL,
2879
+ node_id TEXT NOT NULL,
2880
+ mode TEXT NOT NULL,
2881
+ phi REAL NOT NULL,
2882
+ lambda REAL NOT NULL,
2883
+ tokens_baseline INTEGER NOT NULL,
2884
+ tokens_actual INTEGER NOT NULL,
2885
+ pruned_count INTEGER NOT NULL,
2886
+ pinned_count INTEGER NOT NULL,
2887
+ created_at INTEGER NOT NULL
2888
+ );
2889
+ CREATE INDEX IF NOT EXISTS idx_flow_metrics_project_created
2890
+ ON flow_metrics(project_id, created_at DESC);
2891
+ CREATE INDEX IF NOT EXISTS idx_flow_metrics_mode
2892
+ ON flow_metrics(mode, created_at DESC);
2866
2893
  `
2867
2894
  }
2868
2895
  ];
@@ -5408,6 +5435,13 @@ function getNodeAcTexts(doc, nodeId) {
5408
5435
  if (inline.length > 0) return inline;
5409
5436
  return _acByParent.get(nodeId) ?? [];
5410
5437
  }
5438
+ function getNodeAcFromStore(store2, nodeId) {
5439
+ const node = store2.getNodeById(nodeId);
5440
+ if (!node) return [];
5441
+ const inline = node.acceptanceCriteria ?? [];
5442
+ if (inline.length > 0) return inline;
5443
+ return store2.getChildNodes(nodeId).filter((n) => n.type === "acceptance_criteria").map((n) => n.title);
5444
+ }
5411
5445
  function nodeHasAc(doc, nodeId) {
5412
5446
  return getNodeAcTexts(doc, nodeId).length > 0;
5413
5447
  }
@@ -6805,9 +6839,15 @@ var init_copilot_sdk_adapter = __esm({
6805
6839
  init_logger();
6806
6840
  log22 = createLogger({ layer: "core", source: "copilot-sdk-adapter.ts" });
6807
6841
  ModelAdapterError = class extends McpGraphError {
6808
- constructor(message) {
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 = {}) {
6809
6847
  super(`Model adapter error: ${message}`);
6810
6848
  this.name = "ModelAdapterError";
6849
+ this.status = opts.status;
6850
+ this.retryAfterMs = opts.retryAfterMs;
6811
6851
  }
6812
6852
  };
6813
6853
  CopilotSdkAdapter = class {
@@ -6898,9 +6938,16 @@ var init_copilot_api_adapter = __esm({
6898
6938
  }
6899
6939
  if (!res.ok) {
6900
6940
  if (res.status === 401 || res.status === 403) {
6901
- 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
+ });
6902
6944
  }
6903
- throw new ModelAdapterError(`API do Copilot retornou ${res.status}: ${(await res.text()).slice(0, 200)}`);
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
+ });
6904
6951
  }
6905
6952
  const body = await res.json();
6906
6953
  const text = body.choices?.[0]?.message?.content;
@@ -7044,8 +7091,8 @@ var init_implementation_executor = __esm({
7044
7091
  };
7045
7092
  defaultRunner = (command, cwd) => {
7046
7093
  try {
7047
- const output15 = execSync(command, { cwd, encoding: "utf8", stdio: "pipe" });
7048
- return { exitCode: 0, output: output15 };
7094
+ const output16 = execSync(command, { cwd, encoding: "utf8", stdio: "pipe" });
7095
+ return { exitCode: 0, output: output16 };
7049
7096
  } catch (err) {
7050
7097
  const e = err;
7051
7098
  return {
@@ -7125,11 +7172,89 @@ var init_plan_parser = __esm({
7125
7172
  }
7126
7173
  });
7127
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
+
7128
7248
  // src/core/autonomy/implement-attempt.ts
7249
+ function defaultSleep(ms) {
7250
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
7251
+ }
7129
7252
  function buildInitialPrompt(node, opts = {}) {
7130
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, ""] : [];
7254
+ const flowBlock = opts.flowContext && opts.flowContext.length > 0 ? [opts.flowContext, ""] : [];
7131
7255
  return [
7132
7256
  ...repoMapBlock,
7257
+ ...flowBlock,
7133
7258
  `Implemente a task "${node.title}" (id: ${node.id}) seguindo TDD.`,
7134
7259
  "Responda APENAS com um bloco ```json. H\xE1 DOIS mecanismos:",
7135
7260
  '- "edits" (PREFIRA \u2014 economia de token): [{ "path", "oldString", "newString", "replaceAll"? }].',
@@ -7155,16 +7280,40 @@ function buildRetryPrompt(node, failure, maxFeedbackChars) {
7155
7280
  async function attemptImplementation(deps, options) {
7156
7281
  const maxAttempts = Math.max(1, options.maxAttempts);
7157
7282
  const maxFeedbackChars = options.maxFeedbackChars ?? DEFAULT_MAX_FEEDBACK_CHARS;
7283
+ const sleep2 = deps.sleep ?? defaultSleep;
7158
7284
  let lastResult;
7159
7285
  let lastError;
7160
7286
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
7161
- const prompt = attempt === 1 || !lastResult ? buildInitialPrompt(options.node, { repoMap: options.repoMap }) : buildRetryPrompt(options.node, lastResult, maxFeedbackChars);
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
+ }
7162
7312
  let plan;
7163
7313
  try {
7164
- const text = await deps.generate(prompt);
7165
7314
  plan = parseImplementationPlan(text);
7166
7315
  } catch (err) {
7167
- lastError = err instanceof Error ? err.message : String(err);
7316
+ lastError = getErrorMessage(err);
7168
7317
  log25.warn("Tentativa falhou no parse", { attempt, node: options.node.id, error: lastError });
7169
7318
  continue;
7170
7319
  }
@@ -7183,8 +7332,10 @@ var init_implement_attempt = __esm({
7183
7332
  "src/core/autonomy/implement-attempt.ts"() {
7184
7333
  init_esm_shims();
7185
7334
  init_logger();
7335
+ init_errors();
7186
7336
  init_truncate();
7187
7337
  init_plan_parser();
7338
+ init_llm_error();
7188
7339
  log25 = createLogger({ layer: "core", source: "implement-attempt.ts" });
7189
7340
  DEFAULT_MAX_FEEDBACK_CHARS = 1200;
7190
7341
  }
@@ -7573,6 +7724,854 @@ var init_repo_map = __esm({
7573
7724
  FOCUS_BOOST = 3;
7574
7725
  }
7575
7726
  });
7727
+ var LspConfigOverrideSchema, LspDiagnosticSchema, LspDocumentSymbolSchema, LspTextEditSchema, LspWorkspaceEditSchema;
7728
+ var init_lsp_types = __esm({
7729
+ "src/core/lsp/lsp-types.ts"() {
7730
+ init_esm_shims();
7731
+ z.object({
7732
+ languageId: z.string(),
7733
+ extensions: z.array(z.string()),
7734
+ command: z.string(),
7735
+ args: z.array(z.string()),
7736
+ configFiles: z.array(z.string()),
7737
+ probeCommand: z.string().optional(),
7738
+ initializationOptions: z.record(z.string(), z.unknown()).optional(),
7739
+ settings: z.record(z.string(), z.unknown()).optional()
7740
+ });
7741
+ LspConfigOverrideSchema = z.object({
7742
+ languageId: z.string(),
7743
+ command: z.string(),
7744
+ args: z.array(z.string()).default([]),
7745
+ extensions: z.array(z.string()).optional(),
7746
+ initializationOptions: z.record(z.string(), z.unknown()).optional(),
7747
+ settings: z.record(z.string(), z.unknown()).optional()
7748
+ });
7749
+ z.object({
7750
+ file: z.string(),
7751
+ startLine: z.int().min(0),
7752
+ startCharacter: z.int().min(0),
7753
+ endLine: z.int().min(0),
7754
+ endCharacter: z.int().min(0)
7755
+ });
7756
+ z.object({
7757
+ signature: z.string(),
7758
+ documentation: z.string().optional(),
7759
+ language: z.string().optional()
7760
+ });
7761
+ LspDiagnosticSchema = z.object({
7762
+ file: z.string(),
7763
+ startLine: z.int().min(0),
7764
+ startCharacter: z.int().min(0),
7765
+ endLine: z.int().min(0),
7766
+ endCharacter: z.int().min(0),
7767
+ severity: z.number().int().min(1).max(4),
7768
+ message: z.string(),
7769
+ code: z.string().optional(),
7770
+ source: z.string().optional()
7771
+ });
7772
+ z.object({
7773
+ name: z.string(),
7774
+ kind: z.string(),
7775
+ file: z.string(),
7776
+ startLine: z.int().min(0),
7777
+ endLine: z.int().min(0)
7778
+ });
7779
+ LspDocumentSymbolSchema = z.object({
7780
+ name: z.string(),
7781
+ kind: z.string(),
7782
+ file: z.string(),
7783
+ startLine: z.int().min(0),
7784
+ endLine: z.int().min(0),
7785
+ children: z.lazy(() => z.array(LspDocumentSymbolSchema)).optional()
7786
+ });
7787
+ LspTextEditSchema = z.object({
7788
+ file: z.string(),
7789
+ startLine: z.int().min(0),
7790
+ startCharacter: z.int().min(0),
7791
+ endLine: z.int().min(0),
7792
+ endCharacter: z.int().min(0),
7793
+ newText: z.string()
7794
+ });
7795
+ LspWorkspaceEditSchema = z.object({
7796
+ changes: z.array(LspTextEditSchema)
7797
+ });
7798
+ z.object({
7799
+ languageId: z.string(),
7800
+ status: z.enum(["stopped", "starting", "ready", "error"]),
7801
+ pid: z.number().int().optional(),
7802
+ error: z.string().optional()
7803
+ });
7804
+ z.object({
7805
+ languageId: z.string(),
7806
+ confidence: z.number().min(0).max(1),
7807
+ detectedVia: z.enum(["file_extension", "config_file", "shebang"]),
7808
+ fileCount: z.int().min(0),
7809
+ configFile: z.string().optional()
7810
+ });
7811
+ z.object({
7812
+ title: z.string(),
7813
+ kind: z.string().optional(),
7814
+ isPreferred: z.boolean().optional(),
7815
+ edit: LspWorkspaceEditSchema.optional(),
7816
+ diagnostics: z.array(LspDiagnosticSchema).optional()
7817
+ });
7818
+ z.object({
7819
+ applied: z.boolean(),
7820
+ filesModified: z.array(z.string()),
7821
+ totalEdits: z.number().int(),
7822
+ errors: z.array(z.string()),
7823
+ backups: z.map(z.string(), z.string()).optional()
7824
+ });
7825
+ }
7826
+ });
7827
+ var BROWSER_PILOT_MODELS, ContextModeSchema, ProfileFilterConfigSchema, BrowserAutomationConfigSchema, FlowConfigSchema, ConfigSchema;
7828
+ var init_config_schema = __esm({
7829
+ "src/core/config/config-schema.ts"() {
7830
+ init_esm_shims();
7831
+ init_lsp_types();
7832
+ BROWSER_PILOT_MODELS = ["claude-3.5-sonnet", "gpt-4o", "gpt-4o-mini", "o1", "o1-mini"];
7833
+ ContextModeSchema = z.enum(["ultra-lean", "lean", "full"]);
7834
+ ProfileFilterConfigSchema = z.enum(["core", "pro", "expert", "all"]);
7835
+ BrowserAutomationConfigSchema = z.object({
7836
+ enabled: z.boolean().default(false),
7837
+ bridgeUrl: z.string().regex(/^https?:\/\//, "bridgeUrl must start with http:// or https://").default("http://127.0.0.1:9876/v1"),
7838
+ defaultModel: z.enum(BROWSER_PILOT_MODELS).default("claude-3.5-sonnet"),
7839
+ defaultCdpUrl: z.string().min(1).optional(),
7840
+ allowedDomains: z.array(z.string().min(1)).default([]),
7841
+ forbiddenCdpMethods: z.array(z.string().min(1)).default(["Browser.close"]),
7842
+ maxStepsDefault: z.number().int().min(1).max(100).default(25),
7843
+ tokenBudgetPerDay: z.number().int().nonnegative().optional()
7844
+ }).default({
7845
+ enabled: false,
7846
+ bridgeUrl: "http://127.0.0.1:9876/v1",
7847
+ defaultModel: "claude-3.5-sonnet",
7848
+ allowedDomains: [],
7849
+ forbiddenCdpMethods: ["Browser.close"],
7850
+ maxStepsDefault: 25
7851
+ });
7852
+ FlowConfigSchema = z.object({
7853
+ /** Master switch. OFF = byte-identical legacy context behaviour. */
7854
+ enabled: z.boolean().default(false),
7855
+ /** λ_base — minimum architectural forgetting rate. */
7856
+ lambdaBase: z.number().min(0).default(0.15),
7857
+ /** α — hypofrontality accelerator (weight of Φ on λ_flow). */
7858
+ alpha: z.number().min(0).default(1.5),
7859
+ /** BFS depth used to pull distant pinned invariants back into scope. */
7860
+ maxDepth: z.number().int().min(0).max(6).default(3),
7861
+ /** Peripheral neighbours below this decayed weight are pruned (unless pinned). */
7862
+ weightThreshold: z.number().min(0).max(1).default(0.1),
7863
+ /** EMA gain per consecutive success when computing Φ. */
7864
+ emaGain: z.number().min(0).max(1).default(0.34),
7865
+ /** Multiplier applied to Φ on a failure (0 = hard reset → re-hydrate memory). */
7866
+ resetFactor: z.number().min(0).max(1).default(0),
7867
+ /** Damping fraction of `emaGain` applied on a `partial` outcome. */
7868
+ partialFactor: z.number().min(0).max(1).default(0.5),
7869
+ /** rag budget is never scaled below this fraction of baseline (long-range safety). */
7870
+ budgetFloorRatio: z.number().min(0).max(1).default(0.25),
7871
+ /** How many recent task outcomes feed Φ. */
7872
+ historyWindow: z.number().int().min(1).max(200).default(12),
7873
+ /** Node types that are never diluted. */
7874
+ pinnedTypes: z.array(z.string()).default(["constraint", "risk", "decision", "acceptance_criteria", "constitution", "requirement"]),
7875
+ /** A/B experiment: alternate flow_on/flow_off deterministically per node to measure impact. */
7876
+ experiment: z.object({ abEnabled: z.boolean().default(false) }).default({ abEnabled: false })
7877
+ });
7878
+ ConfigSchema = z.object({
7879
+ port: z.number().int().min(1).max(65535).default(3e3),
7880
+ dbPath: z.string().default("workflow-graph"),
7881
+ basePath: z.string().optional(),
7882
+ contextMode: ContextModeSchema.default("lean"),
7883
+ profile: ProfileFilterConfigSchema.default("all"),
7884
+ dashboard: z.object({
7885
+ autoOpen: z.boolean().default(true)
7886
+ }).default({ autoOpen: true }),
7887
+ integrations: z.object({
7888
+ codeGraphAutoIndex: z.boolean().default(true),
7889
+ codeGraphReindexIntervalSec: z.number().int().min(0).default(0),
7890
+ lspServers: z.array(LspConfigOverrideSchema).default([]),
7891
+ browserAutomation: BrowserAutomationConfigSchema
7892
+ }).prefault({}),
7893
+ flow: FlowConfigSchema.prefault({})
7894
+ });
7895
+ }
7896
+ });
7897
+
7898
+ // src/core/context/flow-config.ts
7899
+ function resolveFlowConfig(source) {
7900
+ const raw = source.getProjectSetting(FLOW_CONFIG_SETTING_KEY);
7901
+ if (!raw) return FlowConfigSchema.parse({});
7902
+ try {
7903
+ return FlowConfigSchema.parse(JSON.parse(raw));
7904
+ } catch {
7905
+ return FlowConfigSchema.parse({});
7906
+ }
7907
+ }
7908
+ function flowAbArm(nodeId) {
7909
+ let sum = 0;
7910
+ for (let i = 0; i < nodeId.length; i += 1) sum += nodeId.charCodeAt(i);
7911
+ return sum % 2 === 0 ? "flow_on" : "flow_off";
7912
+ }
7913
+ var FLOW_CONFIG_SETTING_KEY;
7914
+ var init_flow_config = __esm({
7915
+ "src/core/context/flow-config.ts"() {
7916
+ init_esm_shims();
7917
+ init_config_schema();
7918
+ FLOW_CONFIG_SETTING_KEY = "flow_config";
7919
+ }
7920
+ });
7921
+
7922
+ // src/core/context/flow-index.ts
7923
+ function computeFlowIndex(outcomesNewestFirst, tuning = {}) {
7924
+ const { emaGain, resetFactor, partialFactor } = { ...DEFAULT_FLOW_TUNING, ...tuning };
7925
+ let streak = 0;
7926
+ for (const outcome of outcomesNewestFirst) {
7927
+ if (outcome === "success") streak += 1;
7928
+ else break;
7929
+ }
7930
+ let phi = 0;
7931
+ for (let i = outcomesNewestFirst.length - 1; i >= 0; i -= 1) {
7932
+ const outcome = outcomesNewestFirst[i];
7933
+ if (outcome === "success") {
7934
+ phi += emaGain * (1 - phi);
7935
+ } else if (outcome === "failure") {
7936
+ phi *= resetFactor;
7937
+ } else {
7938
+ phi *= 1 - emaGain * partialFactor;
7939
+ }
7940
+ }
7941
+ phi = Math.min(1, Math.max(0, phi));
7942
+ return { phi, streak, sampleCount: outcomesNewestFirst.length };
7943
+ }
7944
+ function computeLambdaFlow(phi, lambdaBase, alpha) {
7945
+ return lambdaBase + alpha * phi;
7946
+ }
7947
+ function decayWeight(lambda, distance) {
7948
+ return Math.exp(-lambda * distance);
7949
+ }
7950
+ var DEFAULT_FLOW_TUNING;
7951
+ var init_flow_index = __esm({
7952
+ "src/core/context/flow-index.ts"() {
7953
+ init_esm_shims();
7954
+ DEFAULT_FLOW_TUNING = {
7955
+ emaGain: 0.34,
7956
+ resetFactor: 0,
7957
+ partialFactor: 0.5
7958
+ };
7959
+ }
7960
+ });
7961
+
7962
+ // src/core/context/token-estimator.ts
7963
+ function isAsciiLetter(code) {
7964
+ return code >= 65 && code <= 90 || code >= 97 && code <= 122;
7965
+ }
7966
+ function isAsciiDigit(code) {
7967
+ return code >= 48 && code <= 57;
7968
+ }
7969
+ function isWhitespace(code) {
7970
+ return code === 32 || code === 9 || code === 10 || code === 13 || code === 12 || code === 11;
7971
+ }
7972
+ function estimateTokens2(text) {
7973
+ if (!text) return 0;
7974
+ let tokens = 0;
7975
+ const len = text.length;
7976
+ let i = 0;
7977
+ while (i < len) {
7978
+ const code = text.charCodeAt(i);
7979
+ if (isWhitespace(code)) {
7980
+ i++;
7981
+ continue;
7982
+ }
7983
+ if (isAsciiLetter(code)) {
7984
+ const start = i;
7985
+ i++;
7986
+ let subWords = 1;
7987
+ while (i < len && isAsciiLetter(text.charCodeAt(i))) {
7988
+ if (i > start && text.charCodeAt(i) >= 65 && text.charCodeAt(i) <= 90 && text.charCodeAt(i - 1) >= 97 && text.charCodeAt(i - 1) <= 122) {
7989
+ subWords++;
7990
+ }
7991
+ i++;
7992
+ }
7993
+ const wordLen = i - start;
7994
+ if (subWords > 1) {
7995
+ tokens += subWords;
7996
+ } else {
7997
+ tokens += wordLen <= 6 ? 1 : wordLen > 20 ? Math.ceil(wordLen / 4) : Math.ceil(wordLen / 5);
7998
+ }
7999
+ continue;
8000
+ }
8001
+ if (isAsciiDigit(code)) {
8002
+ const start = i;
8003
+ i++;
8004
+ while (i < len && isAsciiDigit(text.charCodeAt(i))) i++;
8005
+ const digitsLen = i - start;
8006
+ tokens += Math.ceil(digitsLen / 3);
8007
+ continue;
8008
+ }
8009
+ tokens += 1;
8010
+ i++;
8011
+ }
8012
+ return tokens;
8013
+ }
8014
+ var init_token_estimator = __esm({
8015
+ "src/core/context/token-estimator.ts"() {
8016
+ init_esm_shims();
8017
+ }
8018
+ });
8019
+
8020
+ // src/core/context/compact-context.ts
8021
+ function toTaskSummary(node) {
8022
+ const summary = {
8023
+ id: node.id,
8024
+ type: node.type,
8025
+ title: node.title,
8026
+ status: node.status,
8027
+ priority: node.priority
8028
+ };
8029
+ if (node.description) summary.description = node.description;
8030
+ if (node.sprint) summary.sprint = node.sprint;
8031
+ if (node.xpSize) summary.xpSize = node.xpSize;
8032
+ if (node.tags?.length) summary.tags = node.tags;
8033
+ return summary;
8034
+ }
8035
+ function isInferred(edge) {
8036
+ return edge.metadata?.inferred === true;
8037
+ }
8038
+ function buildTaskContext(store2, nodeId, snapshot) {
8039
+ const resolveNode = (id) => {
8040
+ return store2.getNodeById(id);
8041
+ };
8042
+ const node = resolveNode(nodeId);
8043
+ if (!node) {
8044
+ log26.warn(`buildTaskContext: node ${nodeId} not found`);
8045
+ return null;
8046
+ }
8047
+ let parent = null;
8048
+ if (node.parentId) {
8049
+ const parentNode = resolveNode(node.parentId);
8050
+ if (parentNode) parent = toTaskSummary(parentNode);
8051
+ }
8052
+ let childNodes;
8053
+ {
8054
+ childNodes = store2.getChildNodes(nodeId);
8055
+ }
8056
+ const children = childNodes.map(toTaskSummary);
8057
+ let incomingEdges;
8058
+ let outgoingEdges;
8059
+ {
8060
+ incomingEdges = store2.getEdgesTo(nodeId);
8061
+ outgoingEdges = store2.getEdgesFrom(nodeId);
8062
+ }
8063
+ const blockers = [];
8064
+ const dependsOn = [];
8065
+ const relatedIds = /* @__PURE__ */ new Set();
8066
+ const relatedNodes = [];
8067
+ const implementsNodes = [];
8068
+ const derivedFromNodes = [];
8069
+ let edgeParent = null;
8070
+ const edgeChildren = [];
8071
+ const edgeChildrenIds = /* @__PURE__ */ new Set();
8072
+ for (const edge of incomingEdges) {
8073
+ if (edge.relationType === "blocks") {
8074
+ const blockerNode = resolveNode(edge.from);
8075
+ if (blockerNode) {
8076
+ blockers.push({
8077
+ id: blockerNode.id,
8078
+ title: blockerNode.title,
8079
+ status: blockerNode.status,
8080
+ relationType: edge.relationType,
8081
+ inferred: isInferred(edge)
8082
+ });
8083
+ }
8084
+ } else if (edge.relationType === "related_to") {
8085
+ const relNode = resolveNode(edge.from);
8086
+ if (relNode && !relatedIds.has(relNode.id)) {
8087
+ relatedIds.add(relNode.id);
8088
+ relatedNodes.push(toTaskSummary(relNode));
8089
+ }
8090
+ } else if (edge.relationType === "parent_of" && !edgeParent) {
8091
+ const parentNode = resolveNode(edge.from);
8092
+ if (parentNode) edgeParent = toTaskSummary(parentNode);
8093
+ }
8094
+ }
8095
+ for (const edge of outgoingEdges) {
8096
+ if (edge.relationType === "depends_on") {
8097
+ const depNode = resolveNode(edge.to);
8098
+ if (depNode) {
8099
+ dependsOn.push({
8100
+ id: depNode.id,
8101
+ title: depNode.title,
8102
+ status: depNode.status,
8103
+ resolved: depNode.status === "done",
8104
+ inferred: isInferred(edge)
8105
+ });
8106
+ }
8107
+ } else if (edge.relationType === "related_to") {
8108
+ const relNode = resolveNode(edge.to);
8109
+ if (relNode && !relatedIds.has(relNode.id)) {
8110
+ relatedIds.add(relNode.id);
8111
+ relatedNodes.push(toTaskSummary(relNode));
8112
+ }
8113
+ } else if (edge.relationType === "implements") {
8114
+ const implNode = resolveNode(edge.to);
8115
+ if (implNode) implementsNodes.push(toTaskSummary(implNode));
8116
+ } else if (edge.relationType === "derived_from") {
8117
+ const derivedNode = resolveNode(edge.to);
8118
+ if (derivedNode) derivedFromNodes.push(toTaskSummary(derivedNode));
8119
+ } else if (edge.relationType === "parent_of") {
8120
+ const childNode = resolveNode(edge.to);
8121
+ if (childNode && !edgeChildrenIds.has(childNode.id)) {
8122
+ edgeChildrenIds.add(childNode.id);
8123
+ edgeChildren.push(toTaskSummary(childNode));
8124
+ }
8125
+ }
8126
+ }
8127
+ const acceptanceCriteria = getNodeAcFromStore(store2, node.id);
8128
+ const sourceRef = node.sourceRef ? { ...node.sourceRef } : null;
8129
+ const localNodes = [node, ...childNodes];
8130
+ const originalChars = localNodes.reduce(
8131
+ (sum, n) => sum + n.title.length + (n.description?.length ?? 0) + (n.acceptanceCriteria?.join("").length ?? 0),
8132
+ 0
8133
+ ) + [...incomingEdges, ...outgoingEdges].reduce((sum, e) => sum + (e.reason?.length ?? 0), 0);
8134
+ const summary = toTaskSummary(node);
8135
+ const corePayload = {
8136
+ task: summary,
8137
+ parent,
8138
+ children,
8139
+ blockers,
8140
+ dependsOn,
8141
+ relatedNodes: relatedNodes.length > 0 ? relatedNodes : void 0,
8142
+ implementsNodes: implementsNodes.length > 0 ? implementsNodes : void 0,
8143
+ derivedFromNodes: derivedFromNodes.length > 0 ? derivedFromNodes : void 0,
8144
+ edgeParent: edgeParent ?? void 0,
8145
+ edgeChildren: edgeChildren.length > 0 ? edgeChildren : void 0,
8146
+ acceptanceCriteria,
8147
+ sourceRef,
8148
+ metrics: { originalChars: 0, compactChars: 0, reductionPercent: 0, estimatedTokens: 0 }
8149
+ };
8150
+ const compactJson = JSON.stringify(corePayload);
8151
+ const compactChars = compactJson.length;
8152
+ const reductionPercent = originalChars > 0 ? Math.round((originalChars - compactChars) / originalChars * 100) : 0;
8153
+ const metrics = {
8154
+ originalChars,
8155
+ compactChars,
8156
+ reductionPercent,
8157
+ estimatedTokens: estimateTokens2(compactJson)
8158
+ };
8159
+ const contextPayload = {
8160
+ ...corePayload,
8161
+ node: summary,
8162
+ metrics
8163
+ };
8164
+ log26.info(`Context for ${nodeId}: ${metrics.estimatedTokens} tokens, ${metrics.reductionPercent}% reduction`);
8165
+ return contextPayload;
8166
+ }
8167
+ var log26, KEY_MAP, KEY_LEGEND;
8168
+ var init_compact_context = __esm({
8169
+ "src/core/context/compact-context.ts"() {
8170
+ init_esm_shims();
8171
+ init_ac_helpers();
8172
+ init_token_estimator();
8173
+ init_logger();
8174
+ log26 = createLogger({ layer: "core", source: "compact-context.ts" });
8175
+ KEY_MAP = {
8176
+ // TaskContext top-level
8177
+ task: "tk",
8178
+ node: "n",
8179
+ parent: "par",
8180
+ children: "ch",
8181
+ blockers: "bl",
8182
+ dependsOn: "dep",
8183
+ acceptanceCriteria: "ac",
8184
+ sourceRef: "sr",
8185
+ relatedNodes: "rel",
8186
+ implementsNodes: "impl",
8187
+ derivedFromNodes: "drv",
8188
+ edgeParent: "ep",
8189
+ edgeChildren: "ech",
8190
+ metrics: "m",
8191
+ // TaskSummary / shared fields
8192
+ id: "i",
8193
+ type: "t",
8194
+ title: "n",
8195
+ status: "s",
8196
+ priority: "p",
8197
+ description: "d",
8198
+ sprint: "sp",
8199
+ xpSize: "xs",
8200
+ tags: "tg",
8201
+ // BlockerInfo / DependencyInfo
8202
+ relationType: "rt",
8203
+ inferred: "inf",
8204
+ resolved: "res",
8205
+ // SourceRefInfo
8206
+ file: "f",
8207
+ startLine: "sl",
8208
+ endLine: "el",
8209
+ confidence: "cf",
8210
+ // ContextMetrics
8211
+ originalChars: "oc",
8212
+ compactChars: "cc",
8213
+ reductionPercent: "rp",
8214
+ estimatedTokens: "et"
8215
+ };
8216
+ KEY_LEGEND = {};
8217
+ for (const [full, short] of Object.entries(KEY_MAP)) {
8218
+ KEY_LEGEND[short] = full;
8219
+ }
8220
+ }
8221
+ });
8222
+
8223
+ // src/core/context/topological-decay.ts
8224
+ function corePayloadTokens(ctx, pinned) {
8225
+ const { metrics: _m, node: _n, ...core } = ctx;
8226
+ return estimateTokens2(JSON.stringify(pinned.length > 0 ? { ...core, pinnedInvariants: pinned } : core));
8227
+ }
8228
+ function isPinnedType(type, pinnedTypes) {
8229
+ return pinnedTypes.has(type);
8230
+ }
8231
+ function collectDistantInvariants(store2, rootId, alreadyPresent, maxDepth, pinnedTypes) {
8232
+ if (maxDepth <= 0) return [];
8233
+ const visited = /* @__PURE__ */ new Set([rootId]);
8234
+ const found = /* @__PURE__ */ new Map();
8235
+ let frontier = [rootId];
8236
+ for (let depth = 1; depth <= maxDepth && frontier.length > 0; depth += 1) {
8237
+ const next = [];
8238
+ for (const id of frontier) {
8239
+ const edges = [...store2.getEdgesFrom(id), ...store2.getEdgesTo(id)];
8240
+ for (const edge of edges) {
8241
+ const neighborId = edge.from === id ? edge.to : edge.from;
8242
+ if (visited.has(neighborId)) continue;
8243
+ visited.add(neighborId);
8244
+ next.push(neighborId);
8245
+ if (alreadyPresent.has(neighborId) || found.has(neighborId)) continue;
8246
+ const neighbor = store2.getNodeById(neighborId);
8247
+ if (neighbor && isPinnedType(neighbor.type, pinnedTypes)) {
8248
+ found.set(neighborId, {
8249
+ id: neighbor.id,
8250
+ type: neighbor.type,
8251
+ title: neighbor.title,
8252
+ status: neighbor.status,
8253
+ distance: depth
8254
+ });
8255
+ }
8256
+ }
8257
+ }
8258
+ frontier = next;
8259
+ }
8260
+ return [...found.values()];
8261
+ }
8262
+ function buildDecayedTaskContext(store2, nodeId, opts) {
8263
+ const base = buildTaskContext(store2, nodeId);
8264
+ if (!base) return null;
8265
+ const pinnedTypes = opts.pinnedTypes ?? new Set(DEFAULT_PINNED_TYPES);
8266
+ const tokensBaseline = corePayloadTokens(base, []);
8267
+ const peripheralWeight = decayWeight(opts.lambda, PERIPHERAL_DISTANCE);
8268
+ const peripheralSurvives = peripheralWeight >= opts.weightThreshold;
8269
+ const presentIds = /* @__PURE__ */ new Set([base.task.id]);
8270
+ if (base.parent) presentIds.add(base.parent.id);
8271
+ for (const c of base.children) presentIds.add(c.id);
8272
+ for (const b of base.blockers) presentIds.add(b.id);
8273
+ for (const d of base.dependsOn) presentIds.add(d.id);
8274
+ const ctx = structuredClone(base);
8275
+ let prunedCount = 0;
8276
+ const prunePeripheral = (list) => {
8277
+ if (!list) return list;
8278
+ const kept = list.filter((item) => {
8279
+ presentIds.add(item.id);
8280
+ if (isPinnedType(item.type, pinnedTypes)) return true;
8281
+ if (peripheralSurvives) return true;
8282
+ prunedCount += 1;
8283
+ return false;
8284
+ });
8285
+ return kept.length > 0 ? kept : void 0;
8286
+ };
8287
+ ctx.relatedNodes = prunePeripheral(ctx.relatedNodes);
8288
+ ctx.implementsNodes = prunePeripheral(ctx.implementsNodes);
8289
+ ctx.derivedFromNodes = prunePeripheral(ctx.derivedFromNodes);
8290
+ ctx.edgeChildren = prunePeripheral(ctx.edgeChildren);
8291
+ if (ctx.edgeParent) {
8292
+ presentIds.add(ctx.edgeParent.id);
8293
+ if (!isPinnedType(ctx.edgeParent.type, pinnedTypes) && !peripheralSurvives) {
8294
+ ctx.edgeParent = null;
8295
+ prunedCount += 1;
8296
+ }
8297
+ }
8298
+ const pinnedInvariants = collectDistantInvariants(
8299
+ store2,
8300
+ nodeId,
8301
+ presentIds,
8302
+ opts.maxDepth,
8303
+ pinnedTypes
8304
+ );
8305
+ const tokensActual = corePayloadTokens(ctx, pinnedInvariants);
8306
+ ctx.metrics = {
8307
+ ...ctx.metrics,
8308
+ estimatedTokens: tokensActual
8309
+ };
8310
+ ctx.node = ctx.task;
8311
+ const retainedCount = (ctx.children?.length ?? 0) + (ctx.blockers?.length ?? 0) + (ctx.dependsOn?.length ?? 0) + (ctx.relatedNodes?.length ?? 0) + (ctx.implementsNodes?.length ?? 0) + (ctx.derivedFromNodes?.length ?? 0) + (ctx.edgeChildren?.length ?? 0);
8312
+ log27.debug("flow:decay", {
8313
+ nodeId,
8314
+ lambda: opts.lambda,
8315
+ prunedCount,
8316
+ pinnedCount: pinnedInvariants.length,
8317
+ tokensBaseline,
8318
+ tokensActual
8319
+ });
8320
+ return {
8321
+ context: ctx,
8322
+ meta: {
8323
+ lambda: opts.lambda,
8324
+ prunedCount,
8325
+ retainedCount,
8326
+ pinnedCount: pinnedInvariants.length,
8327
+ tokensBaseline,
8328
+ tokensActual,
8329
+ tokensSaved: tokensBaseline - tokensActual,
8330
+ pinnedInvariants
8331
+ }
8332
+ };
8333
+ }
8334
+ var log27, DEFAULT_PINNED_TYPES, PERIPHERAL_DISTANCE;
8335
+ var init_topological_decay = __esm({
8336
+ "src/core/context/topological-decay.ts"() {
8337
+ init_esm_shims();
8338
+ init_compact_context();
8339
+ init_flow_index();
8340
+ init_token_estimator();
8341
+ init_logger();
8342
+ log27 = createLogger({ layer: "core", source: "topological-decay.ts" });
8343
+ DEFAULT_PINNED_TYPES = [
8344
+ "constraint",
8345
+ "risk",
8346
+ "decision",
8347
+ "acceptance_criteria",
8348
+ "constitution",
8349
+ "requirement"
8350
+ ];
8351
+ PERIPHERAL_DISTANCE = 2;
8352
+ }
8353
+ });
8354
+
8355
+ // src/core/store/episodic-outcomes-store.ts
8356
+ function buildApproachSummary(touchedFiles, acIds) {
8357
+ const files = [...touchedFiles].sort().join("+");
8358
+ const acs = [...acIds].sort().join(",");
8359
+ return `${files}:${acs}`;
8360
+ }
8361
+ function insertEpisodicOutcome(db, outcome) {
8362
+ db.prepare(
8363
+ `INSERT OR IGNORE INTO episodic_outcomes
8364
+ (id, node_id, task_type, tags, approach_summary, outcome, cycle_time_delta, reopen_count, created_at)
8365
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
8366
+ ).run(
8367
+ outcome.id,
8368
+ outcome.nodeId,
8369
+ outcome.taskType,
8370
+ outcome.tags,
8371
+ outcome.approachSummary,
8372
+ outcome.outcome,
8373
+ outcome.cycleTimeDelta,
8374
+ outcome.reopenCount,
8375
+ outcome.createdAt
8376
+ );
8377
+ }
8378
+ function queryEpisodicOutcomes(db, opts = {}) {
8379
+ const conditions = [];
8380
+ const params = [];
8381
+ if (opts.taskType) {
8382
+ conditions.push("task_type = ?");
8383
+ params.push(opts.taskType);
8384
+ }
8385
+ if (opts.maxAgeDays) {
8386
+ const cutoff = Date.now() - opts.maxAgeDays * 24 * 3600 * 1e3;
8387
+ conditions.push("created_at >= ?");
8388
+ params.push(cutoff);
8389
+ }
8390
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
8391
+ const limit = Math.min(opts.limit ?? 100, 500);
8392
+ params.push(limit);
8393
+ const rows = db.prepare(
8394
+ `SELECT id, node_id, task_type, tags, approach_summary, outcome,
8395
+ cycle_time_delta, reopen_count, created_at
8396
+ FROM episodic_outcomes
8397
+ ${where}
8398
+ ORDER BY created_at DESC
8399
+ LIMIT ?`
8400
+ ).all(...params);
8401
+ return rows.map((r) => ({
8402
+ id: r.id,
8403
+ nodeId: r.node_id,
8404
+ taskType: r.task_type,
8405
+ tags: r.tags,
8406
+ approachSummary: r.approach_summary,
8407
+ outcome: r.outcome,
8408
+ cycleTimeDelta: r.cycle_time_delta,
8409
+ reopenCount: r.reopen_count,
8410
+ createdAt: r.created_at
8411
+ }));
8412
+ }
8413
+ var init_episodic_outcomes_store = __esm({
8414
+ "src/core/store/episodic-outcomes-store.ts"() {
8415
+ init_esm_shims();
8416
+ }
8417
+ });
8418
+
8419
+ // src/core/context/flow-metrics-store.ts
8420
+ function insertFlowMetric(db, metric) {
8421
+ db.prepare(
8422
+ `INSERT OR IGNORE INTO flow_metrics
8423
+ (id, project_id, node_id, mode, phi, lambda,
8424
+ tokens_baseline, tokens_actual, pruned_count, pinned_count, created_at)
8425
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
8426
+ ).run(
8427
+ metric.id,
8428
+ metric.projectId,
8429
+ metric.nodeId,
8430
+ metric.mode,
8431
+ metric.phi,
8432
+ metric.lambda,
8433
+ metric.tokensBaseline,
8434
+ metric.tokensActual,
8435
+ metric.prunedCount,
8436
+ metric.pinnedCount,
8437
+ metric.createdAt
8438
+ );
8439
+ }
8440
+ var init_flow_metrics_store = __esm({
8441
+ "src/core/context/flow-metrics-store.ts"() {
8442
+ init_esm_shims();
8443
+ }
8444
+ });
8445
+
8446
+ // src/core/context/flow-compact.ts
8447
+ function formatFlowContext(result) {
8448
+ const { context: ctx, pinnedInvariants } = result;
8449
+ const lines = ["Contexto do grafo (dilu\xEDdo por flow \u2014 \u03A6 governando o esquecimento):"];
8450
+ lines.push(`- Task: ${ctx.task.title} (${ctx.task.id}) [${ctx.task.status}]`);
8451
+ if (ctx.task.description) lines.push(` ${ctx.task.description}`);
8452
+ if (ctx.acceptanceCriteria.length > 0) {
8453
+ lines.push("- Crit\xE9rios de aceita\xE7\xE3o:");
8454
+ for (const ac of ctx.acceptanceCriteria) lines.push(` \u2022 ${ac}`);
8455
+ }
8456
+ const openBlockers = ctx.blockers.filter((b) => b.status !== "done");
8457
+ if (openBlockers.length > 0) {
8458
+ lines.push(`- Bloqueadores: ${openBlockers.map((b) => `${b.title} (${b.status})`).join("; ")}`);
8459
+ }
8460
+ const openDeps = ctx.dependsOn.filter((d) => !d.resolved);
8461
+ if (openDeps.length > 0) {
8462
+ lines.push(`- Depende de: ${openDeps.map((d) => `${d.title} (${d.status})`).join("; ")}`);
8463
+ }
8464
+ if (pinnedInvariants.length > 0) {
8465
+ lines.push("- Invariantes pinados (nunca dilu\xEDdos):");
8466
+ for (const inv of pinnedInvariants) lines.push(` \u2022 [${inv.type}] ${inv.title}`);
8467
+ }
8468
+ return lines.join("\n");
8469
+ }
8470
+ function recordMetric(store2, row) {
8471
+ try {
8472
+ insertFlowMetric(store2.getDb(), {
8473
+ id: generateId("flowm"),
8474
+ createdAt: row.createdAt ?? Date.now(),
8475
+ ...row
8476
+ });
8477
+ } catch (err) {
8478
+ log28.warn("flow:metric:record-failed", { error: err instanceof Error ? err.message : String(err) });
8479
+ }
8480
+ }
8481
+ function applyFlowToCompact(store2, nodeId) {
8482
+ const cfg = resolveFlowConfig(store2);
8483
+ if (!cfg.enabled) return null;
8484
+ const projectId = store2.getActiveProject()?.id ?? "unknown";
8485
+ const outcomes = queryEpisodicOutcomes(store2.getDb(), { limit: cfg.historyWindow }).map((o) => o.outcome);
8486
+ const state = computeFlowIndex(outcomes, {
8487
+ emaGain: cfg.emaGain,
8488
+ resetFactor: cfg.resetFactor,
8489
+ partialFactor: cfg.partialFactor
8490
+ });
8491
+ const lambda = computeLambdaFlow(state.phi, cfg.lambdaBase, cfg.alpha);
8492
+ const mode = cfg.experiment.abEnabled ? flowAbArm(nodeId) : "flow_on";
8493
+ if (mode === "flow_off") {
8494
+ const base = buildTaskContext(store2, nodeId);
8495
+ if (!base) return null;
8496
+ const baseline = base.metrics.estimatedTokens;
8497
+ recordMetric(store2, {
8498
+ projectId,
8499
+ nodeId,
8500
+ mode,
8501
+ phi: state.phi,
8502
+ lambda,
8503
+ tokensBaseline: baseline,
8504
+ tokensActual: baseline,
8505
+ prunedCount: 0,
8506
+ pinnedCount: 0
8507
+ });
8508
+ return {
8509
+ context: base,
8510
+ pinnedInvariants: [],
8511
+ flow: {
8512
+ enabled: true,
8513
+ mode,
8514
+ phi: state.phi,
8515
+ streak: state.streak,
8516
+ lambda,
8517
+ prunedCount: 0,
8518
+ pinnedCount: 0,
8519
+ tokensBaseline: baseline,
8520
+ tokensActual: baseline,
8521
+ tokensSaved: 0
8522
+ }
8523
+ };
8524
+ }
8525
+ const decayed = buildDecayedTaskContext(store2, nodeId, {
8526
+ lambda,
8527
+ maxDepth: cfg.maxDepth,
8528
+ weightThreshold: cfg.weightThreshold,
8529
+ pinnedTypes: new Set(cfg.pinnedTypes)
8530
+ });
8531
+ if (!decayed) return null;
8532
+ recordMetric(store2, {
8533
+ projectId,
8534
+ nodeId,
8535
+ mode,
8536
+ phi: state.phi,
8537
+ lambda,
8538
+ tokensBaseline: decayed.meta.tokensBaseline,
8539
+ tokensActual: decayed.meta.tokensActual,
8540
+ prunedCount: decayed.meta.prunedCount,
8541
+ pinnedCount: decayed.meta.pinnedCount
8542
+ });
8543
+ return {
8544
+ context: decayed.context,
8545
+ pinnedInvariants: decayed.meta.pinnedInvariants,
8546
+ flow: {
8547
+ enabled: true,
8548
+ mode,
8549
+ phi: state.phi,
8550
+ streak: state.streak,
8551
+ lambda,
8552
+ prunedCount: decayed.meta.prunedCount,
8553
+ pinnedCount: decayed.meta.pinnedCount,
8554
+ tokensBaseline: decayed.meta.tokensBaseline,
8555
+ tokensActual: decayed.meta.tokensActual,
8556
+ tokensSaved: decayed.meta.tokensSaved
8557
+ }
8558
+ };
8559
+ }
8560
+ var log28;
8561
+ var init_flow_compact = __esm({
8562
+ "src/core/context/flow-compact.ts"() {
8563
+ init_esm_shims();
8564
+ init_flow_config();
8565
+ init_flow_index();
8566
+ init_topological_decay();
8567
+ init_compact_context();
8568
+ init_episodic_outcomes_store();
8569
+ init_flow_metrics_store();
8570
+ init_id();
8571
+ init_logger();
8572
+ log28 = createLogger({ layer: "core", source: "flow-compact.ts" });
8573
+ }
8574
+ });
7576
8575
 
7577
8576
  // src/cli/shared/live-implement.ts
7578
8577
  function buildLiveImplement(options) {
@@ -7589,6 +8588,14 @@ function buildLiveImplement(options) {
7589
8588
  const repoRelations = projectId ? codeStore.getAllRelations(projectId) : [];
7590
8589
  const implement = async (node) => {
7591
8590
  const repoMap = repoSymbols.length > 0 ? buildRepoMap({ symbols: repoSymbols, relations: repoRelations }, { tokenBudget: REPO_MAP_TOKEN_BUDGET, focus: node.title }).text : void 0;
8591
+ let flowContext;
8592
+ const flow = applyFlowToCompact(store2, node.id);
8593
+ if (flow) {
8594
+ flowContext = formatFlowContext(flow);
8595
+ onLog?.(
8596
+ ` [flow] \u03A6=${flow.flow.phi.toFixed(2)} \u03BB=${flow.flow.lambda.toFixed(2)} podados=${flow.flow.prunedCount} pinados=${flow.flow.pinnedCount} \u2192 ${flow.flow.tokensSaved} tok economizados`
8597
+ );
8598
+ }
7592
8599
  const outcome = await attemptImplementation(
7593
8600
  {
7594
8601
  generate: async (prompt) => {
@@ -7604,13 +8611,28 @@ function buildLiveImplement(options) {
7604
8611
  },
7605
8612
  execute: (plan) => executePlan(plan, { workspaceDir: dir, defaultTestCommand: testCmd })
7606
8613
  },
7607
- { node, maxAttempts, repoMap }
8614
+ { node, maxAttempts, repoMap, flowContext }
7608
8615
  );
7609
8616
  const files = outcome.lastResult?.applied.length ?? 0;
7610
8617
  const task = ledger.byTask(node.id);
7611
8618
  onLog?.(
7612
8619
  ` [live] ${client.modelFor("implement")}: ${outcome.attempts} tentativa(s), ${files} arquivo(s), ${task.total} tok \u2192 ${outcome.success ? "verde" : "escala"}`
7613
8620
  );
8621
+ try {
8622
+ const applied = outcome.lastResult?.applied ?? [];
8623
+ insertEpisodicOutcome(store2.getDb(), {
8624
+ id: generateId("epi"),
8625
+ nodeId: node.id,
8626
+ taskType: "",
8627
+ tags: "",
8628
+ approachSummary: buildApproachSummary(applied, []),
8629
+ outcome: outcome.success ? "success" : "failure",
8630
+ cycleTimeDelta: 0,
8631
+ reopenCount: 0,
8632
+ createdAt: Date.now()
8633
+ });
8634
+ } catch {
8635
+ }
7614
8636
  return outcome.success;
7615
8637
  };
7616
8638
  return { implement, repoSymbolCount: repoSymbols.length };
@@ -7625,6 +8647,9 @@ var init_live_implement = __esm({
7625
8647
  init_implement_attempt();
7626
8648
  init_code_store();
7627
8649
  init_repo_map();
8650
+ init_flow_compact();
8651
+ init_episodic_outcomes_store();
8652
+ init_id();
7628
8653
  REPO_MAP_TOKEN_BUDGET = 1e3;
7629
8654
  }
7630
8655
  });
@@ -7658,6 +8683,18 @@ var init_store_port = __esm({
7658
8683
  init_definition_of_done();
7659
8684
  }
7660
8685
  });
8686
+
8687
+ // src/tui/status-line.ts
8688
+ function formatStatusLine(input) {
8689
+ const tokens = `${Math.max(0, Math.round(input.totalTokens))} tok`;
8690
+ const cost = `$${input.costUsd.toFixed(4)}`;
8691
+ return `\u26C1 ${tokens} \xB7 ${cost} \xB7 ${input.model}`;
8692
+ }
8693
+ var init_status_line = __esm({
8694
+ "src/tui/status-line.ts"() {
8695
+ init_esm_shims();
8696
+ }
8697
+ });
7661
8698
  function TaskRow({ task }) {
7662
8699
  return /* @__PURE__ */ jsxs(Text, { children: [
7663
8700
  " ",
@@ -7684,6 +8721,7 @@ function App({ model }) {
7684
8721
  " ",
7685
8722
  model.wip
7686
8723
  ] }) }),
8724
+ /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: "green", children: formatStatusLine({ totalTokens: model.tokens.total, costUsd: model.tokens.costUsd, model: model.modelLabel }) }) }),
7687
8725
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
7688
8726
  /* @__PURE__ */ jsxs(Text, { bold: true, children: [
7689
8727
  "Tasks ativas (",
@@ -7714,6 +8752,7 @@ var STATUS_ICON;
7714
8752
  var init_app = __esm({
7715
8753
  "src/tui/app.tsx"() {
7716
8754
  init_esm_shims();
8755
+ init_status_line();
7717
8756
  STATUS_ICON = {
7718
8757
  in_progress: "\u25CF",
7719
8758
  ready: "\u25CB",
@@ -7764,6 +8803,7 @@ var init_banner_screen = __esm({
7764
8803
  function CommandBar({ value, onChange, onSubmit, suggestions }) {
7765
8804
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
7766
8805
  suggestions.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: suggestions.map((c) => /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
8806
+ c.source === "skill" ? /* @__PURE__ */ jsx(Text, { color: "magenta", children: "[skill] " }) : null,
7767
8807
  c.usage,
7768
8808
  " \u2014 ",
7769
8809
  c.desc
@@ -7780,6 +8820,28 @@ var init_command_bar = __esm({
7780
8820
  }
7781
8821
  });
7782
8822
 
8823
+ // src/tui/history.ts
8824
+ function valueAt(state, cursor) {
8825
+ if (cursor < 0) return state.draft;
8826
+ return state.history[state.history.length - 1 - cursor];
8827
+ }
8828
+ function navigateHistory(state, dir) {
8829
+ const last = state.history.length - 1;
8830
+ if (last < 0) return { value: state.draft, cursor: -1 };
8831
+ let cursor = state.cursor;
8832
+ if (dir === "up") {
8833
+ cursor = Math.min(cursor + 1, last);
8834
+ } else {
8835
+ cursor = Math.max(cursor - 1, -1);
8836
+ }
8837
+ return { value: valueAt(state, cursor), cursor };
8838
+ }
8839
+ var init_history = __esm({
8840
+ "src/tui/history.ts"() {
8841
+ init_esm_shims();
8842
+ }
8843
+ });
8844
+
7783
8845
  // src/tui/dispatch.ts
7784
8846
  function parseCommand(input) {
7785
8847
  const trimmed = input.trim();
@@ -7789,11 +8851,38 @@ function parseCommand(input) {
7789
8851
  if (sp === -1) return { cmd: body.toLowerCase(), args: "" };
7790
8852
  return { cmd: body.slice(0, sp).toLowerCase(), args: body.slice(sp + 1).trim() };
7791
8853
  }
8854
+ function fuzzyScore(query, text) {
8855
+ const q = query.toLowerCase();
8856
+ const t = text.toLowerCase();
8857
+ if (q === "") return 0;
8858
+ let qi = 0;
8859
+ let score = 0;
8860
+ let lastMatch = -1;
8861
+ for (let ti = 0; ti < t.length && qi < q.length; ti++) {
8862
+ if (t[ti] === q[qi]) {
8863
+ if (lastMatch === -1) score += ti;
8864
+ else score += ti - lastMatch - 1;
8865
+ lastMatch = ti;
8866
+ qi++;
8867
+ }
8868
+ }
8869
+ return qi === q.length ? score : null;
8870
+ }
8871
+ function fuzzyFilter(query, commands) {
8872
+ if (query.trim() === "") return [...commands];
8873
+ const scored = [];
8874
+ commands.forEach((cmd, idx) => {
8875
+ const score = fuzzyScore(query, cmd.name);
8876
+ if (score !== null) scored.push({ cmd, score, idx });
8877
+ });
8878
+ scored.sort((a, b) => a.score !== b.score ? a.score - b.score : a.idx - b.idx);
8879
+ return scored.map((s) => s.cmd);
8880
+ }
7792
8881
  function filterCommands(input, extra = []) {
7793
8882
  const trimmed = input.trim();
7794
8883
  if (!trimmed.startsWith("/")) return [];
7795
- const prefix = trimmed.slice(1).split(" ")[0].toLowerCase();
7796
- return [...COMMANDS, ...extra].filter((c) => c.name.startsWith(prefix));
8884
+ const query = trimmed.slice(1).split(" ")[0];
8885
+ return fuzzyFilter(query, [...COMMANDS, ...extra]);
7797
8886
  }
7798
8887
  async function runAsyncCommand(port, parsed, _onLine) {
7799
8888
  switch (parsed.cmd) {
@@ -7890,15 +8979,30 @@ function InteractiveApp({ dashboard, port, asyncPort, liveRunner, skillCommands
7890
8979
  process.stdout.isTTY ? "banner" : "dashboard"
7891
8980
  );
7892
8981
  const [input, setInput] = useState("");
7893
- const [log45, setLog] = useState([]);
8982
+ const [log48, setLog] = useState([]);
7894
8983
  const [running, setRunning] = useState(false);
7895
8984
  const [showHelp, setShowHelp] = useState(false);
8985
+ const [history, setHistory] = useState([]);
8986
+ const [histCursor, setHistCursor] = useState(-1);
8987
+ const [draft, setDraft] = useState("");
7896
8988
  const append = (line) => setLog((prev) => [...prev, line].slice(-MAX_LOG_LINES));
8989
+ useInput((_input, key) => {
8990
+ if (phase !== "dashboard" || running) return;
8991
+ if (!key.upArrow && !key.downArrow) return;
8992
+ const effectiveDraft = histCursor === -1 ? input : draft;
8993
+ if (histCursor === -1) setDraft(input);
8994
+ const result = navigateHistory({ history, cursor: histCursor, draft: effectiveDraft }, key.upArrow ? "up" : "down");
8995
+ setHistCursor(result.cursor);
8996
+ setInput(result.value);
8997
+ });
7897
8998
  const submit = (raw) => {
7898
8999
  const parsed = parseCommand(raw);
7899
9000
  setInput("");
7900
9001
  const text = raw.trim();
7901
9002
  if (text === "" || running) return;
9003
+ setHistory((prev) => [...prev, text]);
9004
+ setHistCursor(-1);
9005
+ setDraft("");
7902
9006
  if (parsed.cmd === "quit") {
7903
9007
  exit();
7904
9008
  return;
@@ -7939,7 +9043,7 @@ ${skill.body}` : `Skill n\xE3o encontrada: ${parsed.cmd}`);
7939
9043
  }
7940
9044
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
7941
9045
  /* @__PURE__ */ jsx(App, { model: dashboard }),
7942
- log45.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, borderStyle: "single", paddingX: 1, children: log45.map((line, i) => /* @__PURE__ */ jsx(Text, { children: line }, i)) }),
9046
+ log48.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, borderStyle: "single", paddingX: 1, children: log48.map((line, i) => /* @__PURE__ */ jsx(Text, { children: line }, i)) }),
7943
9047
  showHelp && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "round", paddingX: 1, children: [
7944
9048
  /* @__PURE__ */ jsx(Text, { bold: true, children: "Comandos:" }),
7945
9049
  COMMANDS.map((c) => /* @__PURE__ */ jsxs(Text, { children: [
@@ -7962,6 +9066,7 @@ var init_interactive_app = __esm({
7962
9066
  init_app();
7963
9067
  init_banner_screen();
7964
9068
  init_command_bar();
9069
+ init_history();
7965
9070
  init_dispatch();
7966
9071
  MAX_LOG_LINES = 12;
7967
9072
  }
@@ -8166,22 +9271,25 @@ function parseSkillMarkdown(content) {
8166
9271
  } catch (err) {
8167
9272
  return { ok: false, error: `YAML parse error: ${err instanceof Error ? err.message : String(err)}` };
8168
9273
  }
9274
+ const rawTriggers = frontmatter.triggers;
9275
+ const triggers = Array.isArray(rawTriggers) ? rawTriggers.map((t) => typeof t === "string" ? { event: t } : t) : rawTriggers;
9276
+ const phases = Array.isArray(frontmatter.phases) ? frontmatter.phases : [];
8169
9277
  const raw = {
8170
9278
  name: frontmatter.name,
8171
9279
  description: frontmatter.description,
8172
9280
  category: frontmatter.category ?? "know-me",
8173
- phases: frontmatter.phases,
9281
+ phases,
8174
9282
  // §extracta-sweep-1 — optional `platforms:` array; absent = all OSes.
8175
9283
  platforms: frontmatter.platforms,
8176
9284
  instructions: bodyText,
8177
9285
  toolchain: frontmatter.toolchain,
8178
- triggers: frontmatter.triggers,
9286
+ triggers,
8179
9287
  contextTemplate: frontmatter.contextTemplate
8180
9288
  };
8181
9289
  const parsed = CustomSkillInputSchema.safeParse(raw);
8182
9290
  if (!parsed.success) {
8183
9291
  const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
8184
- log26.warn("skill-loader:validation_failed", { issues });
9292
+ log29.debug("skill-loader:validation_failed", { issues });
8185
9293
  return { ok: false, error: `Validation failed: ${issues}` };
8186
9294
  }
8187
9295
  return { ok: true, skill: parsed.data };
@@ -8224,13 +9332,13 @@ function walk(dir, acc) {
8224
9332
  else acc.errors.push({ file: full, error: parsed.error ?? "unknown parse error" });
8225
9333
  }
8226
9334
  }
8227
- var log26;
9335
+ var log29;
8228
9336
  var init_skill_loader = __esm({
8229
9337
  "src/core/skills/skill-loader.ts"() {
8230
9338
  init_esm_shims();
8231
9339
  init_skill_schema();
8232
9340
  init_logger();
8233
- log26 = createLogger({ layer: "core", source: "skill-loader.ts" });
9341
+ log29 = createLogger({ layer: "core", source: "skill-loader.ts" });
8234
9342
  }
8235
9343
  });
8236
9344
  function summarize(skill) {
@@ -8323,7 +9431,7 @@ function mergeGraph(store2, incoming, options) {
8323
9431
  }
8324
9432
  const sourceProject = incoming.project.name;
8325
9433
  const dryRun = options?.dryRun ?? false;
8326
- log27.info("merge-graph:start", {
9434
+ log30.info("merge-graph:start", {
8327
9435
  sourceProject,
8328
9436
  incomingNodes: incoming.nodes.length,
8329
9437
  incomingEdges: incoming.edges.length,
@@ -8375,7 +9483,7 @@ function mergeGraph(store2, incoming, options) {
8375
9483
  edgesInserted2++;
8376
9484
  }
8377
9485
  }
8378
- log27.info("merge-graph:dry-run", {
9486
+ log30.info("merge-graph:dry-run", {
8379
9487
  nodesInserted: nodesToInsert.length,
8380
9488
  nodesSkipped,
8381
9489
  edgesInserted: edgesInserted2,
@@ -8395,7 +9503,7 @@ function mergeGraph(store2, incoming, options) {
8395
9503
  const { nodesInserted, edgesInserted } = store2.mergeInsert(nodesToInsert, validEdges);
8396
9504
  const edgesSkipped = validEdges.length - edgesInserted;
8397
9505
  store2.recordImport(`merge:${sourceProject}`, nodesInserted, edgesInserted);
8398
- log27.info("merge-graph:done", {
9506
+ log30.info("merge-graph:done", {
8399
9507
  sourceProject,
8400
9508
  nodesInserted,
8401
9509
  nodesSkipped,
@@ -8412,14 +9520,14 @@ function mergeGraph(store2, incoming, options) {
8412
9520
  sourceProject
8413
9521
  };
8414
9522
  }
8415
- var log27;
9523
+ var log30;
8416
9524
  var init_import_graph = __esm({
8417
9525
  "src/core/importer/import-graph.ts"() {
8418
9526
  init_esm_shims();
8419
9527
  init_graph_schema();
8420
9528
  init_errors();
8421
9529
  init_logger();
8422
- log27 = createLogger({ layer: "core", source: "import-graph.ts" });
9530
+ log30 = createLogger({ layer: "core", source: "import-graph.ts" });
8423
9531
  }
8424
9532
  });
8425
9533
 
@@ -8530,7 +9638,7 @@ function computeFileSha256(path22) {
8530
9638
  return hash.digest("hex");
8531
9639
  }
8532
9640
  async function downloadFileWithVerify(url, destPath, expectedSha256) {
8533
- log28.info("model-downloader:start", { url, dest: destPath, hasExpectedHash: false });
9641
+ log31.info("model-downloader:start", { url, dest: destPath, hasExpectedHash: false });
8534
9642
  const controller = new AbortController();
8535
9643
  const timeoutId = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
8536
9644
  let response;
@@ -8552,15 +9660,15 @@ async function downloadFileWithVerify(url, destPath, expectedSha256) {
8552
9660
  const actualSha = computeFileSha256(destPath);
8553
9661
  const sizeBytes = statSync(destPath).size;
8554
9662
  const verified = expectedSha256 != null && actualSha === expectedSha256;
8555
- log28.info("model-downloader:ok", { dest: destPath, sizeBytes, sha256: actualSha, verified });
9663
+ log31.info("model-downloader:ok", { dest: destPath, sizeBytes, sha256: actualSha, verified });
8556
9664
  return { sha256: actualSha, verified, sizeBytes };
8557
9665
  }
8558
- var log28, DOWNLOAD_TIMEOUT_MS, ChecksumMismatchError, DownloadError;
9666
+ var log31, DOWNLOAD_TIMEOUT_MS, ChecksumMismatchError, DownloadError;
8559
9667
  var init_model_downloader = __esm({
8560
9668
  "src/core/rag/model-downloader.ts"() {
8561
9669
  init_esm_shims();
8562
9670
  init_logger();
8563
- log28 = createLogger({ layer: "core", source: "model-downloader.ts" });
9671
+ log31 = createLogger({ layer: "core", source: "model-downloader.ts" });
8564
9672
  DOWNLOAD_TIMEOUT_MS = 9e4;
8565
9673
  ChecksumMismatchError = class extends Error {
8566
9674
  constructor(url, expected, actual) {
@@ -8629,15 +9737,15 @@ async function isOnnxAvailable() {
8629
9737
  onnxAvailableCache = true;
8630
9738
  } catch {
8631
9739
  onnxAvailableCache = false;
8632
- log29.warn("onnx:unavailable", { reason: "onnxruntime-node not installed \u2014 RAG will use hash embeddings (degraded mode)" });
9740
+ log32.warn("onnx:unavailable", { reason: "onnxruntime-node not installed \u2014 RAG will use hash embeddings (degraded mode)" });
8633
9741
  }
8634
9742
  return onnxAvailableCache;
8635
9743
  }
8636
9744
  async function logEmbeddingModeOnBoot(isAvailable = isOnnxAvailable, logFn = (event, fields) => {
8637
9745
  if (fields.mode === "neural") {
8638
- log29.info(event, fields);
9746
+ log32.info(event, fields);
8639
9747
  } else {
8640
- log29.warn(event, fields);
9748
+ log32.warn(event, fields);
8641
9749
  }
8642
9750
  }) {
8643
9751
  try {
@@ -8660,7 +9768,7 @@ function ensureOnnxModelDir(modelsDir) {
8660
9768
  async function downloadFile(url, destPath) {
8661
9769
  try {
8662
9770
  const resultValue = await downloadFileWithVerify(url, destPath);
8663
- log29.info("onnx:download:ok", {
9771
+ log32.info("onnx:download:ok", {
8664
9772
  dest: destPath,
8665
9773
  sizeBytes: resultValue.sizeBytes,
8666
9774
  sha256: resultValue.sha256,
@@ -8684,7 +9792,7 @@ async function ensureModelFiles(modelsDir) {
8684
9792
  if (existsSync(modelPath)) {
8685
9793
  const size = statSync(modelPath).size;
8686
9794
  if (size < MIN_MODEL_SIZE) {
8687
- log29.warn("onnx:corrupted-model", { modelPath, sizeBytes: size, minRequired: MIN_MODEL_SIZE });
9795
+ log32.warn("onnx:corrupted-model", { modelPath, sizeBytes: size, minRequired: MIN_MODEL_SIZE });
8688
9796
  unlinkSync(modelPath);
8689
9797
  }
8690
9798
  }
@@ -8693,12 +9801,12 @@ async function ensureModelFiles(modelsDir) {
8693
9801
  const raw = readFileSync(tokenizerPath, "utf-8");
8694
9802
  JSON.parse(raw);
8695
9803
  } catch {
8696
- log29.warn("onnx:corrupted-tokenizer", { tokenizerPath });
9804
+ log32.warn("onnx:corrupted-tokenizer", { tokenizerPath });
8697
9805
  unlinkSync(tokenizerPath);
8698
9806
  }
8699
9807
  }
8700
9808
  if (existsSync(modelPath) && existsSync(tokenizerPath)) {
8701
- log29.debug("onnx:cache-hit", { modelDir });
9809
+ log32.debug("onnx:cache-hit", { modelDir });
8702
9810
  return { modelPath, tokenizerPath };
8703
9811
  }
8704
9812
  mkdirSync(modelDir, { recursive: true });
@@ -8715,7 +9823,7 @@ function loadTokenizer(tokenizerPath) {
8715
9823
  const raw = readFileSync(tokenizerPath, "utf-8");
8716
9824
  return JSON.parse(raw);
8717
9825
  } catch (err) {
8718
- log29.warn("onnx:tokenizer-load-failed", { tokenizerPath, error: err instanceof Error ? err.message : String(err) });
9826
+ log32.warn("onnx:tokenizer-load-failed", { tokenizerPath, error: err instanceof Error ? err.message : String(err) });
8719
9827
  return null;
8720
9828
  }
8721
9829
  }
@@ -8767,15 +9875,15 @@ function startOnnxBackgroundDownload(modelsDir, options = {}) {
8767
9875
  modelPath = join(modelDir, MODEL_FILENAME);
8768
9876
  tokenizerPath = join(modelDir, TOKENIZER_FILENAME);
8769
9877
  const cacheEvent = "onnx:background-ready-from-cache";
8770
- log29.info(cacheEvent, { modelsDir });
9878
+ log32.info(cacheEvent, { modelsDir });
8771
9879
  onLog?.(cacheEvent);
8772
9880
  } else {
8773
9881
  const startEvent = "onnx:background-download-start";
8774
- log29.info(startEvent, { message: "Downloading MiniLM-L6-v2 (23MB)...", modelsDir });
9882
+ log32.info(startEvent, { message: "Downloading MiniLM-L6-v2 (23MB)...", modelsDir });
8775
9883
  onLog?.(startEvent);
8776
9884
  ({ modelPath, tokenizerPath } = await ensureFiles(modelsDir));
8777
9885
  const doneEvent = "onnx:background-download-complete";
8778
- log29.info(doneEvent, { modelsDir });
9886
+ log32.info(doneEvent, { modelsDir });
8779
9887
  onLog?.(doneEvent);
8780
9888
  }
8781
9889
  const provider = makeProvider(modelPath, tokenizerPath);
@@ -8783,7 +9891,7 @@ function startOnnxBackgroundDownload(modelsDir, options = {}) {
8783
9891
  onReady?.(provider);
8784
9892
  } catch (err) {
8785
9893
  const msg = err instanceof Error ? err.message : String(err);
8786
- log29.warn("onnx:background-download-failed", { error: msg, action: "fallback-remains-tfidf" });
9894
+ log32.warn("onnx:background-download-failed", { error: msg, action: "fallback-remains-tfidf" });
8787
9895
  onWarning?.(msg);
8788
9896
  }
8789
9897
  };
@@ -8797,7 +9905,7 @@ function _resetBackgroundDownloadState() {
8797
9905
  async function getOnnxProvider(modelsDir) {
8798
9906
  const available = await isOnnxAvailable();
8799
9907
  if (!available) {
8800
- log29.warn("onnx:provider-degraded", { available: false, impact: "RAG operates with hash embeddings instead of neural \u2014 lower search quality" });
9908
+ log32.warn("onnx:provider-degraded", { available: false, impact: "RAG operates with hash embeddings instead of neural \u2014 lower search quality" });
8801
9909
  return null;
8802
9910
  }
8803
9911
  const existing = providerCache.get(modelsDir);
@@ -8817,8 +9925,8 @@ async function getOnnxProvider(modelsDir) {
8817
9925
  return await creation;
8818
9926
  } catch (err) {
8819
9927
  providerCache.delete(modelsDir);
8820
- log29.error("onnx:provider-init-failed", { error: err instanceof Error ? err.message : String(err) });
8821
- log29.warn("onnx:fallback", {
9928
+ log32.error("onnx:provider-init-failed", { error: err instanceof Error ? err.message : String(err) });
9929
+ log32.warn("onnx:fallback", {
8822
9930
  reason: err instanceof Error ? err.message : String(err),
8823
9931
  action: "return-null-provider",
8824
9932
  modelsDir
@@ -8826,7 +9934,7 @@ async function getOnnxProvider(modelsDir) {
8826
9934
  return null;
8827
9935
  }
8828
9936
  }
8829
- var log29, MODEL_NAME, MODEL_FILENAME, TOKENIZER_FILENAME, EMBEDDING_DIM, MAX_SEQUENCE_LENGTH, MODEL_BASE_URL, MODEL_URL, TOKENIZER_URL, DOWNLOAD_TIMEOUT_MS2, onnxAvailableCache, OnnxEmbeddingProvider, _backgroundProvider, _backgroundStarted, providerCache;
9937
+ var log32, MODEL_NAME, MODEL_FILENAME, TOKENIZER_FILENAME, EMBEDDING_DIM, MAX_SEQUENCE_LENGTH, MODEL_BASE_URL, MODEL_URL, TOKENIZER_URL, DOWNLOAD_TIMEOUT_MS2, onnxAvailableCache, OnnxEmbeddingProvider, _backgroundProvider, _backgroundStarted, providerCache;
8830
9938
  var init_onnx_embeddings = __esm({
8831
9939
  "src/core/rag/onnx-embeddings.ts"() {
8832
9940
  init_esm_shims();
@@ -8834,7 +9942,7 @@ var init_onnx_embeddings = __esm({
8834
9942
  init_errors();
8835
9943
  init_tensor_buffer_pool();
8836
9944
  init_model_downloader();
8837
- log29 = createLogger({ layer: "rag", source: "onnx-embeddings.ts" });
9945
+ log32 = createLogger({ layer: "rag", source: "onnx-embeddings.ts" });
8838
9946
  MODEL_NAME = "all-MiniLM-L6-v2-quantized";
8839
9947
  MODEL_FILENAME = "model.onnx";
8840
9948
  TOKENIZER_FILENAME = "tokenizer.json";
@@ -8866,7 +9974,7 @@ var init_onnx_embeddings = __esm({
8866
9974
  throw new OnnxModelNotFoundError(`Failed to load tokenizer: ${this.tokenizerPath}`);
8867
9975
  }
8868
9976
  this.vocab = config.model?.vocab ?? {};
8869
- log29.info("onnx:session-created", { model: this.modelPath });
9977
+ log32.info("onnx:session-created", { model: this.modelPath });
8870
9978
  return this.session;
8871
9979
  }
8872
9980
  async generateEmbedding(text) {
@@ -8995,7 +10103,7 @@ async function checkSqliteDatabase(basePath) {
8995
10103
  };
8996
10104
  }
8997
10105
  } catch (err) {
8998
- log30.debug("intentional-swallow", { error: String(err), reason: "statSync race with deletion; fall through to open attempt" });
10106
+ log33.debug("intentional-swallow", { error: String(err), reason: "statSync race with deletion; fall through to open attempt" });
8999
10107
  }
9000
10108
  try {
9001
10109
  const db = new Database2(dbPath, { readonly: true });
@@ -9045,7 +10153,7 @@ async function checkDbIntegrity2(basePath) {
9045
10153
  };
9046
10154
  }
9047
10155
  } catch (err) {
9048
- log30.debug("intentional-swallow", { error: String(err), reason: "fall through to open attempt" });
10156
+ log33.debug("intentional-swallow", { error: String(err), reason: "fall through to open attempt" });
9049
10157
  }
9050
10158
  try {
9051
10159
  const db = new Database2(dbPath, { readonly: true });
@@ -9053,7 +10161,7 @@ async function checkDbIntegrity2(basePath) {
9053
10161
  try {
9054
10162
  schemaCount = db.prepare("SELECT count(*) as n FROM sqlite_master WHERE type IN ('table','view')").get().n;
9055
10163
  } catch (err) {
9056
- log30.debug("intentional-swallow", { error: String(err), reason: "reading sqlite_master itself failed \u2014 DB is unreadable, fall through" });
10164
+ log33.debug("intentional-swallow", { error: String(err), reason: "reading sqlite_master itself failed \u2014 DB is unreadable, fall through" });
9057
10165
  }
9058
10166
  if (schemaCount === 0) {
9059
10167
  db.close();
@@ -9213,7 +10321,7 @@ async function checkIntegrations(basePath) {
9213
10321
  });
9214
10322
  return results;
9215
10323
  } catch (err) {
9216
- log30.debug("doctor:integrations:fail", {
10324
+ log33.debug("doctor:integrations:fail", {
9217
10325
  error: err instanceof Error ? err.message : String(err)
9218
10326
  });
9219
10327
  return [
@@ -9264,7 +10372,7 @@ async function checkOnnxStatus() {
9264
10372
  const { isOnnxAvailable: isOnnxAvailable2 } = await Promise.resolve().then(() => (init_onnx_embeddings(), onnx_embeddings_exports));
9265
10373
  return checkOnnxStatusWith(isOnnxAvailable2);
9266
10374
  }
9267
- var log30, MIN_NODE_VERSION;
10375
+ var log33, MIN_NODE_VERSION;
9268
10376
  var init_doctor_checks = __esm({
9269
10377
  "src/core/doctor/doctor-checks.ts"() {
9270
10378
  init_esm_shims();
@@ -9272,7 +10380,7 @@ var init_doctor_checks = __esm({
9272
10380
  init_fs();
9273
10381
  init_tool_status();
9274
10382
  init_logger();
9275
- log30 = createLogger({ layer: "core", source: "doctor-checks.ts" });
10383
+ log33 = createLogger({ layer: "core", source: "doctor-checks.ts" });
9276
10384
  MIN_NODE_VERSION = 20;
9277
10385
  }
9278
10386
  });
@@ -9297,7 +10405,7 @@ async function runDoctor(basePath) {
9297
10405
  if (!basePath) {
9298
10406
  throw new McpGraphError("Doctor requires a valid base path");
9299
10407
  }
9300
- log31.info("Running doctor checks", { basePath });
10408
+ log34.info("Running doctor checks", { basePath });
9301
10409
  const checks = [];
9302
10410
  checks.push(checkNodeVersion());
9303
10411
  checks.push(checkConfigFile(basePath));
@@ -9335,7 +10443,7 @@ async function runDoctor(basePath) {
9335
10443
  }
9336
10444
  }
9337
10445
  for (const result of checks) {
9338
- log31.event(
10446
+ log34.event(
9339
10447
  { action: "health.check", category: "health", outcome: result.level === "ok" ? "success" : "failure" },
9340
10448
  `health.check.${result.name}`,
9341
10449
  { check: result.name }
@@ -9348,7 +10456,7 @@ async function runDoctor(basePath) {
9348
10456
  passed: summary.error === 0
9349
10457
  };
9350
10458
  }
9351
- var log31;
10459
+ var log34;
9352
10460
  var init_doctor_runner = __esm({
9353
10461
  "src/core/doctor/doctor-runner.ts"() {
9354
10462
  init_esm_shims();
@@ -9357,7 +10465,7 @@ var init_doctor_runner = __esm({
9357
10465
  init_errors();
9358
10466
  init_logger();
9359
10467
  init_doctor_checks();
9360
- log31 = createLogger({ layer: "core", source: "doctor-runner.ts" });
10468
+ log34 = createLogger({ layer: "core", source: "doctor-runner.ts" });
9361
10469
  }
9362
10470
  });
9363
10471
 
@@ -9490,7 +10598,8 @@ async function launchTui(store2) {
9490
10598
  return skills.map((s) => ({
9491
10599
  name: s.name,
9492
10600
  usage: `/${s.name}`,
9493
- desc: `[${s.category}] ${s.description}`
10601
+ desc: `[${s.category}] ${s.description}`,
10602
+ source: "skill"
9494
10603
  }));
9495
10604
  });
9496
10605
  const instance = render(
@@ -10104,16 +11213,36 @@ init_token_ledger();
10104
11213
  init_llm_call_ledger();
10105
11214
  init_live_implement();
10106
11215
  init_store_port();
11216
+
11217
+ // src/cli/shared/enable-flow.ts
11218
+ init_esm_shims();
11219
+ init_flow_config();
11220
+ function enableFlowConfig(store2) {
11221
+ const raw = store2.getProjectSetting(FLOW_CONFIG_SETTING_KEY);
11222
+ let current = {};
11223
+ if (raw) {
11224
+ try {
11225
+ current = JSON.parse(raw);
11226
+ } catch {
11227
+ current = {};
11228
+ }
11229
+ }
11230
+ store2.setProjectSetting(FLOW_CONFIG_SETTING_KEY, JSON.stringify({ ...current, enabled: true }));
11231
+ }
10107
11232
  function output7(msg) {
10108
11233
  process.stdout.write(msg + "\n");
10109
11234
  }
10110
11235
  function autopilotCommand() {
10111
- 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").action(
11236
+ 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(
10112
11237
  async (opts) => {
10113
11238
  const store2 = openStoreOrFail(opts.dir, { requireExisting: true });
10114
11239
  try {
10115
11240
  const maxIterations = Math.max(1, parseInt(opts.max, 10) || 5);
10116
11241
  const port = makeStorePort(store2);
11242
+ if (opts.flow) {
11243
+ enableFlowConfig(store2);
11244
+ output7("[FLOW] \u03BB_flow ativo: contexto do grafo dilu\xEDdo por \u03A6(t) (esquecimento din\xE2mico).\n");
11245
+ }
10117
11246
  if (opts.simulate) output7("[SIMULA\xC7\xC3O] impl tratada como verde \u2014 DoD real decide prontid\xE3o.\n");
10118
11247
  if (opts.live) output7("[LIVE] modelo via SDK do Copilot: gera plano \u2192 aplica no workspace \u2192 roda testes.\n");
10119
11248
  let implement;
@@ -10413,7 +11542,7 @@ function whichCommand() {
10413
11542
  }
10414
11543
 
10415
11544
  // src/core/lsp/lsp-deps-installer.ts
10416
- var log33 = createLogger({ layer: "core", source: "lsp-deps-installer.ts" });
11545
+ var log36 = createLogger({ layer: "core", source: "lsp-deps-installer.ts" });
10417
11546
  var execAsync = promisify(execFile);
10418
11547
  var LSP_NPM_PACKAGES = {
10419
11548
  typescript: "typescript-language-server",
@@ -10464,7 +11593,7 @@ var LSP_SYSTEM_PACKAGES = {
10464
11593
  async function checkLspDep(languageId, command) {
10465
11594
  try {
10466
11595
  await execAsync(whichCommand(), [command]);
10467
- log33.info("LSP server available", { languageId, command });
11596
+ log36.info("LSP server available", { languageId, command });
10468
11597
  return {
10469
11598
  name: command,
10470
11599
  languageId,
@@ -10510,7 +11639,7 @@ function getServerCommand(languageId) {
10510
11639
  }
10511
11640
  async function installLspDeps(detectedLanguages) {
10512
11641
  if (detectedLanguages.length === 0) return [];
10513
- log33.info("Checking LSP server dependencies", {
11642
+ log36.info("Checking LSP server dependencies", {
10514
11643
  languages: detectedLanguages.join(", ")
10515
11644
  });
10516
11645
  const results = [];
@@ -10522,7 +11651,7 @@ async function installLspDeps(detectedLanguages) {
10522
11651
  }
10523
11652
  const available = results.filter((r) => r.status === "already_available").length;
10524
11653
  const missing = results.filter((r) => r.status === "not_found").length;
10525
- log33.info("LSP dependency check complete", {
11654
+ log36.info("LSP dependency check complete", {
10526
11655
  total: String(results.length),
10527
11656
  available: String(available),
10528
11657
  missing: String(missing)
@@ -10533,7 +11662,7 @@ async function installLspDeps(detectedLanguages) {
10533
11662
  // src/core/lsp/language-detector.ts
10534
11663
  init_esm_shims();
10535
11664
  init_logger();
10536
- var log34 = createLogger({ layer: "core", source: "language-detector.ts" });
11665
+ var log37 = createLogger({ layer: "core", source: "language-detector.ts" });
10537
11666
  var CONFIG_FILE_MAP = {
10538
11667
  "tsconfig.json": "typescript",
10539
11668
  "jsconfig.json": "typescript",
@@ -10573,7 +11702,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
10573
11702
  "__pycache__"
10574
11703
  ]);
10575
11704
  function detectProjectLanguages(projectPath, registry) {
10576
- log34.debug("detecting project languages", { projectPath });
11705
+ log37.debug("detecting project languages", { projectPath });
10577
11706
  const configDetections = /* @__PURE__ */ new Map();
10578
11707
  const fileCounts = /* @__PURE__ */ new Map();
10579
11708
  detectConfigFiles(projectPath, configDetections);
@@ -10601,7 +11730,7 @@ function detectProjectLanguages(projectPath, registry) {
10601
11730
  });
10602
11731
  }
10603
11732
  results.sort((a, b) => b.fileCount - a.fileCount);
10604
- log34.info("project languages detected", {
11733
+ log37.info("project languages detected", {
10605
11734
  count: String(results.length),
10606
11735
  languages: results.map((r) => r.languageId).join(",")
10607
11736
  });
@@ -10612,7 +11741,7 @@ function detectConfigFiles(rootPath, configDetections) {
10612
11741
  try {
10613
11742
  entries = readdirSync(rootPath, { withFileTypes: true, encoding: "utf-8" });
10614
11743
  } catch {
10615
- log34.debug("cannot read root directory for config detection", { rootPath });
11744
+ log37.debug("cannot read root directory for config detection", { rootPath });
10616
11745
  return;
10617
11746
  }
10618
11747
  for (const entry of entries) {
@@ -10637,7 +11766,7 @@ function walkAndCountFiles(dirPath, registry, fileCounts) {
10637
11766
  try {
10638
11767
  entries = readdirSync(dirPath, { withFileTypes: true, encoding: "utf-8" });
10639
11768
  } catch {
10640
- log34.debug("cannot read directory, skipping", { dirPath });
11769
+ log37.debug("cannot read directory, skipping", { dirPath });
10641
11770
  return;
10642
11771
  }
10643
11772
  for (const entry of entries) {
@@ -11722,176 +12851,10 @@ function applySection(existingContent, newSection) {
11722
12851
 
11723
12852
  // src/core/config/config-loader.ts
11724
12853
  init_esm_shims();
11725
-
11726
- // src/core/config/config-schema.ts
11727
- init_esm_shims();
11728
-
11729
- // src/core/lsp/lsp-types.ts
11730
- init_esm_shims();
11731
- z.object({
11732
- languageId: z.string(),
11733
- extensions: z.array(z.string()),
11734
- command: z.string(),
11735
- args: z.array(z.string()),
11736
- configFiles: z.array(z.string()),
11737
- probeCommand: z.string().optional(),
11738
- initializationOptions: z.record(z.string(), z.unknown()).optional(),
11739
- settings: z.record(z.string(), z.unknown()).optional()
11740
- });
11741
- var LspConfigOverrideSchema = z.object({
11742
- languageId: z.string(),
11743
- command: z.string(),
11744
- args: z.array(z.string()).default([]),
11745
- extensions: z.array(z.string()).optional(),
11746
- initializationOptions: z.record(z.string(), z.unknown()).optional(),
11747
- settings: z.record(z.string(), z.unknown()).optional()
11748
- });
11749
- z.object({
11750
- file: z.string(),
11751
- startLine: z.int().min(0),
11752
- startCharacter: z.int().min(0),
11753
- endLine: z.int().min(0),
11754
- endCharacter: z.int().min(0)
11755
- });
11756
- z.object({
11757
- signature: z.string(),
11758
- documentation: z.string().optional(),
11759
- language: z.string().optional()
11760
- });
11761
- var LspDiagnosticSchema = z.object({
11762
- file: z.string(),
11763
- startLine: z.int().min(0),
11764
- startCharacter: z.int().min(0),
11765
- endLine: z.int().min(0),
11766
- endCharacter: z.int().min(0),
11767
- severity: z.number().int().min(1).max(4),
11768
- message: z.string(),
11769
- code: z.string().optional(),
11770
- source: z.string().optional()
11771
- });
11772
- z.object({
11773
- name: z.string(),
11774
- kind: z.string(),
11775
- file: z.string(),
11776
- startLine: z.int().min(0),
11777
- endLine: z.int().min(0)
11778
- });
11779
- var LspDocumentSymbolSchema = z.object({
11780
- name: z.string(),
11781
- kind: z.string(),
11782
- file: z.string(),
11783
- startLine: z.int().min(0),
11784
- endLine: z.int().min(0),
11785
- children: z.lazy(() => z.array(LspDocumentSymbolSchema)).optional()
11786
- });
11787
- var LspTextEditSchema = z.object({
11788
- file: z.string(),
11789
- startLine: z.int().min(0),
11790
- startCharacter: z.int().min(0),
11791
- endLine: z.int().min(0),
11792
- endCharacter: z.int().min(0),
11793
- newText: z.string()
11794
- });
11795
- var LspWorkspaceEditSchema = z.object({
11796
- changes: z.array(LspTextEditSchema)
11797
- });
11798
- z.object({
11799
- languageId: z.string(),
11800
- status: z.enum(["stopped", "starting", "ready", "error"]),
11801
- pid: z.number().int().optional(),
11802
- error: z.string().optional()
11803
- });
11804
- z.object({
11805
- languageId: z.string(),
11806
- confidence: z.number().min(0).max(1),
11807
- detectedVia: z.enum(["file_extension", "config_file", "shebang"]),
11808
- fileCount: z.int().min(0),
11809
- configFile: z.string().optional()
11810
- });
11811
- z.object({
11812
- title: z.string(),
11813
- kind: z.string().optional(),
11814
- isPreferred: z.boolean().optional(),
11815
- edit: LspWorkspaceEditSchema.optional(),
11816
- diagnostics: z.array(LspDiagnosticSchema).optional()
11817
- });
11818
- z.object({
11819
- applied: z.boolean(),
11820
- filesModified: z.array(z.string()),
11821
- totalEdits: z.number().int(),
11822
- errors: z.array(z.string()),
11823
- backups: z.map(z.string(), z.string()).optional()
11824
- });
11825
-
11826
- // src/core/config/config-schema.ts
11827
- var BROWSER_PILOT_MODELS = ["claude-3.5-sonnet", "gpt-4o", "gpt-4o-mini", "o1", "o1-mini"];
11828
- var ContextModeSchema = z.enum(["ultra-lean", "lean", "full"]);
11829
- var ProfileFilterConfigSchema = z.enum(["core", "pro", "expert", "all"]);
11830
- var BrowserAutomationConfigSchema = z.object({
11831
- enabled: z.boolean().default(false),
11832
- bridgeUrl: z.string().regex(/^https?:\/\//, "bridgeUrl must start with http:// or https://").default("http://127.0.0.1:9876/v1"),
11833
- defaultModel: z.enum(BROWSER_PILOT_MODELS).default("claude-3.5-sonnet"),
11834
- defaultCdpUrl: z.string().min(1).optional(),
11835
- allowedDomains: z.array(z.string().min(1)).default([]),
11836
- forbiddenCdpMethods: z.array(z.string().min(1)).default(["Browser.close"]),
11837
- maxStepsDefault: z.number().int().min(1).max(100).default(25),
11838
- tokenBudgetPerDay: z.number().int().nonnegative().optional()
11839
- }).default({
11840
- enabled: false,
11841
- bridgeUrl: "http://127.0.0.1:9876/v1",
11842
- defaultModel: "claude-3.5-sonnet",
11843
- allowedDomains: [],
11844
- forbiddenCdpMethods: ["Browser.close"],
11845
- maxStepsDefault: 25
11846
- });
11847
- var FlowConfigSchema = z.object({
11848
- /** Master switch. OFF = byte-identical legacy context behaviour. */
11849
- enabled: z.boolean().default(false),
11850
- /** λ_base — minimum architectural forgetting rate. */
11851
- lambdaBase: z.number().min(0).default(0.15),
11852
- /** α — hypofrontality accelerator (weight of Φ on λ_flow). */
11853
- alpha: z.number().min(0).default(1.5),
11854
- /** BFS depth used to pull distant pinned invariants back into scope. */
11855
- maxDepth: z.number().int().min(0).max(6).default(3),
11856
- /** Peripheral neighbours below this decayed weight are pruned (unless pinned). */
11857
- weightThreshold: z.number().min(0).max(1).default(0.1),
11858
- /** EMA gain per consecutive success when computing Φ. */
11859
- emaGain: z.number().min(0).max(1).default(0.34),
11860
- /** Multiplier applied to Φ on a failure (0 = hard reset → re-hydrate memory). */
11861
- resetFactor: z.number().min(0).max(1).default(0),
11862
- /** Damping fraction of `emaGain` applied on a `partial` outcome. */
11863
- partialFactor: z.number().min(0).max(1).default(0.5),
11864
- /** rag budget is never scaled below this fraction of baseline (long-range safety). */
11865
- budgetFloorRatio: z.number().min(0).max(1).default(0.25),
11866
- /** How many recent task outcomes feed Φ. */
11867
- historyWindow: z.number().int().min(1).max(200).default(12),
11868
- /** Node types that are never diluted. */
11869
- pinnedTypes: z.array(z.string()).default(["constraint", "risk", "decision", "acceptance_criteria", "constitution", "requirement"]),
11870
- /** A/B experiment: alternate flow_on/flow_off deterministically per node to measure impact. */
11871
- experiment: z.object({ abEnabled: z.boolean().default(false) }).default({ abEnabled: false })
11872
- });
11873
- var ConfigSchema = z.object({
11874
- port: z.number().int().min(1).max(65535).default(3e3),
11875
- dbPath: z.string().default("workflow-graph"),
11876
- basePath: z.string().optional(),
11877
- contextMode: ContextModeSchema.default("lean"),
11878
- profile: ProfileFilterConfigSchema.default("all"),
11879
- dashboard: z.object({
11880
- autoOpen: z.boolean().default(true)
11881
- }).default({ autoOpen: true }),
11882
- integrations: z.object({
11883
- codeGraphAutoIndex: z.boolean().default(true),
11884
- codeGraphReindexIntervalSec: z.number().int().min(0).default(0),
11885
- lspServers: z.array(LspConfigOverrideSchema).default([]),
11886
- browserAutomation: BrowserAutomationConfigSchema
11887
- }).prefault({}),
11888
- flow: FlowConfigSchema.prefault({})
11889
- });
11890
-
11891
- // src/core/config/config-loader.ts
12854
+ init_config_schema();
11892
12855
  init_errors();
11893
12856
  init_logger();
11894
- var log35 = createLogger({ layer: "core", source: "config-loader.ts" });
12857
+ var log38 = createLogger({ layer: "core", source: "config-loader.ts" });
11895
12858
  var CONFIG_FILENAME = "mcp-graph.config.json";
11896
12859
  function loadConfig(basePath) {
11897
12860
  const resolvedBase = basePath ?? process.cwd();
@@ -11901,13 +12864,13 @@ function loadConfig(basePath) {
11901
12864
  try {
11902
12865
  const raw = readFileSync(configPath, "utf-8").replace(/^\uFEFF/, "");
11903
12866
  fileConfig = JSON.parse(raw);
11904
- log35.info(`Config loaded from ${configPath}`);
12867
+ log38.info(`Config loaded from ${configPath}`);
11905
12868
  } catch (err) {
11906
12869
  const msg = err instanceof Error ? err.message : String(err);
11907
12870
  throw new McpGraphError(`Invalid config at ${configPath}: ${msg}`);
11908
12871
  }
11909
12872
  } else {
11910
- log35.info("No config file found, using defaults");
12873
+ log38.info("No config file found, using defaults");
11911
12874
  }
11912
12875
  if (process.env.MCP_PORT) {
11913
12876
  const envPort = parseInt(process.env.MCP_PORT, 10);
@@ -11927,7 +12890,7 @@ function loadConfig(basePath) {
11927
12890
  // src/core/config/ignore-templates.ts
11928
12891
  init_esm_shims();
11929
12892
  init_logger();
11930
- var log36 = createLogger({ layer: "core", source: "ignore-templates.ts" });
12893
+ var log39 = createLogger({ layer: "core", source: "ignore-templates.ts" });
11931
12894
  var IGNORE_TEMPLATE = `# ========================================
11932
12895
  # LEAN CONTEXT (mcp-graph)
11933
12896
  # Filosofia: zero auto-load, tudo on-demand via MCP
@@ -12078,21 +13041,21 @@ release-please-config.json
12078
13041
  function ensureClaudeIgnore(projectDir) {
12079
13042
  const filePath = path17__default.join(projectDir, ".claudeignore");
12080
13043
  if (existsSync(filePath)) {
12081
- log36.debug(".claudeignore already exists, skipping");
13044
+ log39.debug(".claudeignore already exists, skipping");
12082
13045
  return false;
12083
13046
  }
12084
13047
  writeFileSync(filePath, IGNORE_TEMPLATE, "utf-8");
12085
- log36.info(".claudeignore created with lean context template");
13048
+ log39.info(".claudeignore created with lean context template");
12086
13049
  return true;
12087
13050
  }
12088
13051
  function ensureCopilotIgnore(projectDir) {
12089
13052
  const filePath = path17__default.join(projectDir, ".copilotignore");
12090
13053
  if (existsSync(filePath)) {
12091
- log36.debug(".copilotignore already exists, skipping");
13054
+ log39.debug(".copilotignore already exists, skipping");
12092
13055
  return false;
12093
13056
  }
12094
13057
  writeFileSync(filePath, IGNORE_TEMPLATE, "utf-8");
12095
- log36.info(".copilotignore created with lean context template");
13058
+ log39.info(".copilotignore created with lean context template");
12096
13059
  return true;
12097
13060
  }
12098
13061
  function updateIgnoreFile(filePath, label, dryRun) {
@@ -12100,7 +13063,7 @@ function updateIgnoreFile(filePath, label, dryRun) {
12100
13063
  if (!exists) {
12101
13064
  if (!dryRun) {
12102
13065
  writeFileSync(filePath, IGNORE_TEMPLATE, "utf-8");
12103
- log36.info(`${label} created with lean context template`);
13066
+ log39.info(`${label} created with lean context template`);
12104
13067
  }
12105
13068
  return { status: "created", message: `${label} created` };
12106
13069
  }
@@ -12110,7 +13073,7 @@ function updateIgnoreFile(filePath, label, dryRun) {
12110
13073
  }
12111
13074
  if (!dryRun) {
12112
13075
  writeFileSync(filePath, IGNORE_TEMPLATE, "utf-8");
12113
- log36.info(`${label} updated to latest template`);
13076
+ log39.info(`${label} updated to latest template`);
12114
13077
  }
12115
13078
  return { status: "updated", message: `${label} updated` };
12116
13079
  }
@@ -12787,7 +13750,7 @@ init_registry();
12787
13750
  // src/core/atomic-files/writer-markdown.ts
12788
13751
  init_esm_shims();
12789
13752
  init_logger();
12790
- var log37 = createLogger({ layer: "core", source: "writer-markdown.ts" });
13753
+ var log40 = createLogger({ layer: "core", source: "writer-markdown.ts" });
12791
13754
  var markerStart2 = (id) => `<!-- MCP-GRAPH:MANAGED-START:${id} -->`;
12792
13755
  var markerEnd2 = (id) => `<!-- MCP-GRAPH:MANAGED-END:${id} -->`;
12793
13756
  function extractManagedBlock(content, fileId) {
@@ -12849,7 +13812,7 @@ async function write(file, mode) {
12849
13812
  const existingBlock = extractManagedBlock(current, fileId);
12850
13813
  const tampered = existingBlock !== null && detectTampering(filePath, fileId, existingBlock);
12851
13814
  if (tampered) {
12852
- log37.warn("managed block tampered \u2014 system reconquering", { fileId, filePath });
13815
+ log40.warn("managed block tampered \u2014 system reconquering", { fileId, filePath });
12853
13816
  fs.writeFileSync(filePath + ".user-modified.bak", current, "utf8");
12854
13817
  }
12855
13818
  if (!tampered && existingBlock === managedContent) {
@@ -12887,7 +13850,7 @@ async function atomicWrite(filePath, content) {
12887
13850
  try {
12888
13851
  fs.unlinkSync(tmp);
12889
13852
  } catch (e) {
12890
- log37.debug("intentional swallow", { error: e, reason: "tmp file already gone, cleanup not needed" });
13853
+ log40.debug("intentional swallow", { error: e, reason: "tmp file already gone, cleanup not needed" });
12891
13854
  }
12892
13855
  throw err;
12893
13856
  }
@@ -12915,7 +13878,7 @@ async function runAtomicWrites(mode) {
12915
13878
  }
12916
13879
 
12917
13880
  // src/cli/commands/init-cmd.ts
12918
- var log38 = createLogger({ layer: "cli", source: "init.ts" });
13881
+ var log41 = createLogger({ layer: "cli", source: "init.ts" });
12919
13882
  var LEVEL_ICON = { ok: "\u2713", warning: "\u26A0", error: "\u2717" };
12920
13883
  async function runInitOrchestration(opts, deps) {
12921
13884
  const { dir, skipNeural, noServe, port } = opts;
@@ -13028,7 +13991,7 @@ function initCommand() {
13028
13991
  const dir = path17__default.resolve(opts.dir);
13029
13992
  const port = parseInt(opts.port, 10);
13030
13993
  if (isNaN(port) || port < 1 || port > 65535) {
13031
- log38.error("Invalid port number", { port: opts.port });
13994
+ log41.error("Invalid port number", { port: opts.port });
13032
13995
  process.exitCode = 1;
13033
13996
  return;
13034
13997
  }
@@ -13046,7 +14009,7 @@ function initCommand() {
13046
14009
  out("\nPronto. Execute `mcp-graph serve` para iniciar o servidor.");
13047
14010
  }
13048
14011
  } catch (error) {
13049
- log38.error("Init failed", { error: getErrorMessage(error) });
14012
+ log41.error("Init failed", { error: getErrorMessage(error) });
13050
14013
  process.exitCode = 1;
13051
14014
  }
13052
14015
  });
@@ -13111,7 +14074,7 @@ function readDaemonMeta(stateDir) {
13111
14074
 
13112
14075
  // src/core/daemon/daemon-reaper.ts
13113
14076
  init_logger();
13114
- var log40 = createLogger({ layer: "core", source: "daemon-reaper" });
14077
+ var log43 = createLogger({ layer: "core", source: "daemon-reaper" });
13115
14078
  function defaultDaemonRoot(home = os.homedir()) {
13116
14079
  return path17__default.join(home, ".mcp-graph");
13117
14080
  }
@@ -13150,7 +14113,7 @@ function reapDaemons(options = {}) {
13150
14113
  try {
13151
14114
  kill(lock.pid);
13152
14115
  } catch (err) {
13153
- log40.debug("intentional-swallow", {
14116
+ log43.debug("intentional-swallow", {
13154
14117
  error: String(err),
13155
14118
  reason: "process vanished between liveness probe and signal \u2014 treat as already reaped"
13156
14119
  });
@@ -13180,7 +14143,7 @@ function reapDaemons(options = {}) {
13180
14143
  try {
13181
14144
  fs__default.rmSync(stateDir, { recursive: true, force: true });
13182
14145
  } catch (err) {
13183
- log40.debug("intentional-swallow", {
14146
+ log43.debug("intentional-swallow", {
13184
14147
  error: String(err),
13185
14148
  reason: "state dir removal hit a permission error or race \u2014 next reaper run retries"
13186
14149
  });
@@ -13200,14 +14163,14 @@ function reapDaemons(options = {}) {
13200
14163
 
13201
14164
  // src/cli/commands/daemon-cmd.ts
13202
14165
  init_logger();
13203
- var log41 = createLogger({ layer: "cli", source: "daemon.ts" });
14166
+ var log44 = createLogger({ layer: "cli", source: "daemon.ts" });
13204
14167
  function output12(msg) {
13205
14168
  process.stdout.write(msg + "\n");
13206
14169
  }
13207
14170
  function daemonCommand() {
13208
14171
  const cmd = new Command("daemon").description("Inspect and clean up mcp-graph daemons");
13209
14172
  cmd.command("prune").description("Kill orphaned daemons (workspace gone) and remove stale state dirs").option("--dry-run", "Show what would be reaped without killing or deleting", false).action((opts) => {
13210
- log41.info("cli:daemon:prune", { dryRun: opts.dryRun });
14173
+ log44.info("cli:daemon:prune", { dryRun: opts.dryRun });
13211
14174
  const report = reapDaemons({ dryRun: opts.dryRun });
13212
14175
  const prefix = opts.dryRun ? "[dry-run] " : "";
13213
14176
  for (const a of report.actions) {
@@ -13279,7 +14242,7 @@ function formatProviderReport(report) {
13279
14242
  // src/cli/commands/doctor-cmd.ts
13280
14243
  init_errors();
13281
14244
  init_logger();
13282
- var log42 = createLogger({ layer: "cli", source: "doctor.ts" });
14245
+ var log45 = createLogger({ layer: "cli", source: "doctor.ts" });
13283
14246
  function output13(msg) {
13284
14247
  process.stdout.write(msg + "\n");
13285
14248
  }
@@ -13334,7 +14297,7 @@ function doctorCommand() {
13334
14297
  process.exit(1);
13335
14298
  }
13336
14299
  } catch (err) {
13337
- log42.error(`Doctor failed: ${getErrorMessage(err)}`);
14300
+ log45.error(`Doctor failed: ${getErrorMessage(err)}`);
13338
14301
  process.exit(1);
13339
14302
  }
13340
14303
  });
@@ -13346,7 +14309,7 @@ init_esm_shims();
13346
14309
  // src/core/autonomy/shadow-branch.ts
13347
14310
  init_esm_shims();
13348
14311
  init_logger();
13349
- var log43 = createLogger({ layer: "core", source: "shadow-branch.ts" });
14312
+ var log46 = createLogger({ layer: "core", source: "shadow-branch.ts" });
13350
14313
  function parseShadowTimestamp(branchName) {
13351
14314
  const match = /-(\d{10,})$/.exec(branchName);
13352
14315
  if (!match) return null;
@@ -13388,7 +14351,7 @@ function pruneOrphanWorktrees(options) {
13388
14351
  ).toString();
13389
14352
  branches = out2.split("\n").map((s) => s.trim()).filter(Boolean);
13390
14353
  } catch (err) {
13391
- log43.debug("shadow-branch:prune:list-failed", { error: String(err) });
14354
+ log46.debug("shadow-branch:prune:list-failed", { error: String(err) });
13392
14355
  }
13393
14356
  const wtMap = branches.length > 0 ? listShadowWorktrees(execOpts) : /* @__PURE__ */ new Map();
13394
14357
  for (const branch of branches) {
@@ -13400,34 +14363,34 @@ function pruneOrphanWorktrees(options) {
13400
14363
  execSync(`git worktree remove --force --force ${wtPath}`, execOpts);
13401
14364
  reapedWorktrees += 1;
13402
14365
  } catch (err) {
13403
- log43.debug("shadow-branch:prune:wt-remove-failed", { branch, wtPath, error: String(err) });
14366
+ log46.debug("shadow-branch:prune:wt-remove-failed", { branch, wtPath, error: String(err) });
13404
14367
  }
13405
14368
  }
13406
14369
  try {
13407
14370
  execSync(`git branch -D ${branch}`, execOpts);
13408
14371
  reapedBranches += 1;
13409
14372
  } catch (err) {
13410
- log43.debug("shadow-branch:prune:branch-delete-failed", { branch, error: String(err) });
14373
+ log46.debug("shadow-branch:prune:branch-delete-failed", { branch, error: String(err) });
13411
14374
  }
13412
14375
  }
13413
14376
  try {
13414
- const output15 = execSync("git worktree prune --verbose", execOpts).toString();
14377
+ const output16 = execSync("git worktree prune --verbose", execOpts).toString();
13415
14378
  if (reapedBranches > 0 || reapedWorktrees > 0) {
13416
- log43.info("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, ttlMs });
14379
+ log46.info("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, ttlMs });
13417
14380
  } else {
13418
- log43.debug("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, output: output15 });
14381
+ log46.debug("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, output: output16 });
13419
14382
  }
13420
- return { pruned: true, reapedBranches, reapedWorktrees, output: output15 };
14383
+ return { pruned: true, reapedBranches, reapedWorktrees, output: output16 };
13421
14384
  } catch (err) {
13422
14385
  const error = String(err);
13423
- log43.debug("shadow-branch:prune-failed", { error });
14386
+ log46.debug("shadow-branch:prune-failed", { error });
13424
14387
  return { pruned: false, reapedBranches, reapedWorktrees, error };
13425
14388
  }
13426
14389
  }
13427
14390
 
13428
14391
  // src/cli/commands/gc-cmd.ts
13429
14392
  init_logger();
13430
- var log44 = createLogger({ layer: "cli", source: "gc.ts" });
14393
+ var log47 = createLogger({ layer: "cli", source: "gc.ts" });
13431
14394
  function output14(msg) {
13432
14395
  process.stdout.write(msg + "\n");
13433
14396
  }
@@ -13435,7 +14398,7 @@ function gcCommand() {
13435
14398
  return new Command("gc").description("Garbage-collect orphan ai-shadow/* worktrees and branches").option("-d, --dir <dir>", "Project directory (git root)", process.cwd()).option("--ttl <minutes>", "Only reap branches older than N minutes (0 = all)", "0").action((opts) => {
13436
14399
  const ttlMinutes = parseInt(opts.ttl, 10);
13437
14400
  const ttlMs = Number.isFinite(ttlMinutes) && ttlMinutes > 0 ? ttlMinutes * 60 * 1e3 : 0;
13438
- log44.info("cli:gc:start", { dir: opts.dir, ttlMs });
14401
+ log47.info("cli:gc:start", { dir: opts.dir, ttlMs });
13439
14402
  const result = pruneOrphanWorktrees({ cwd: opts.dir, ttlMs });
13440
14403
  if (result.pruned) {
13441
14404
  output14(`gc: reaped ${result.reapedBranches} branches, ${result.reapedWorktrees} worktrees`);
@@ -13446,6 +14409,48 @@ function gcCommand() {
13446
14409
  });
13447
14410
  }
13448
14411
 
14412
+ // src/cli/commands/skill-cmd.ts
14413
+ init_esm_shims();
14414
+ init_skill_registry();
14415
+ function output15(msg) {
14416
+ process.stdout.write(msg + "\n");
14417
+ }
14418
+ function skillCommand() {
14419
+ const cmd = new Command("skill").description("Lista e exibe skills (instru\xE7\xF5es para agentes)");
14420
+ cmd.command("list").description("Lista as skills dispon\xEDveis (src/skills, .agents/skills, .claude/skills)").option("-p, --phase <fase>", "Ordena/filtra pela fase do ciclo (ANALYZE, IMPLEMENT, \u2026)").option("-d, --dir <dir>", "Raiz do projeto", process.cwd()).action((opts) => {
14421
+ const seen = /* @__PURE__ */ new Set();
14422
+ let count = 0;
14423
+ for (const root of defaultSkillRoots(opts.dir)) {
14424
+ const { skills } = listSkills(root, opts.phase);
14425
+ for (const s of skills) {
14426
+ if (seen.has(s.name)) continue;
14427
+ seen.add(s.name);
14428
+ count += 1;
14429
+ output15(`${s.name.padEnd(28)} [${s.category}] ${s.description}`);
14430
+ }
14431
+ }
14432
+ if (count === 0) output15("Nenhuma skill encontrada.");
14433
+ else output15(`
14434
+ ${count} skill(s).`);
14435
+ });
14436
+ 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) => {
14437
+ for (const root of defaultSkillRoots(opts.dir)) {
14438
+ const found = invokeSkill(root, nome);
14439
+ if (found) {
14440
+ output15(`=== ${found.name} ===`);
14441
+ output15(`[${found.category}] ${found.description}`);
14442
+ if (found.phases.length > 0) output15(`fases: ${found.phases.join(", ")}`);
14443
+ output15("");
14444
+ output15(found.body);
14445
+ return;
14446
+ }
14447
+ }
14448
+ output15(`Skill n\xE3o encontrada: ${nome}. Tente 'skill list'.`);
14449
+ process.exitCode = 1;
14450
+ });
14451
+ return cmd;
14452
+ }
14453
+
13449
14454
  // src/cli/index.ts
13450
14455
  var program = new Command();
13451
14456
  program.name("agent-graph-flow").description(PROMISE).version(VERSION, "-v, --version");
@@ -13466,6 +14471,7 @@ program.addCommand(initCommand());
13466
14471
  program.addCommand(daemonCommand());
13467
14472
  program.addCommand(doctorCommand());
13468
14473
  program.addCommand(gcCommand());
14474
+ program.addCommand(skillCommand());
13469
14475
  function shouldLaunchTui() {
13470
14476
  const noArgs = process.argv.length <= 2;
13471
14477
  const isTty = Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);