@mcp-graph-workflow/agent-graph-flow 0.1.3 → 0.2.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 +1134 -309
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -12,9 +12,9 @@ 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
14
  import { render, useApp, Box, Text } from 'ink';
15
- import { jsxs, jsx } from 'react/jsx-runtime';
15
+ import { jsx, jsxs } from 'react/jsx-runtime';
16
+ import { createElement, useState, useCallback, useEffect } from 'react';
16
17
  import TextInput from 'ink-text-input';
17
- import { createElement, useState } from 'react';
18
18
  import Spinner from 'ink-spinner';
19
19
  import { Command } from 'commander';
20
20
  import { setTimeout as setTimeout$1 } from 'timers/promises';
@@ -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
  }
@@ -7044,8 +7078,8 @@ var init_implementation_executor = __esm({
7044
7078
  };
7045
7079
  defaultRunner = (command, cwd) => {
7046
7080
  try {
7047
- const output15 = execSync(command, { cwd, encoding: "utf8", stdio: "pipe" });
7048
- return { exitCode: 0, output: output15 };
7081
+ const output16 = execSync(command, { cwd, encoding: "utf8", stdio: "pipe" });
7082
+ return { exitCode: 0, output: output16 };
7049
7083
  } catch (err) {
7050
7084
  const e = err;
7051
7085
  return {
@@ -7128,8 +7162,10 @@ var init_plan_parser = __esm({
7128
7162
  // src/core/autonomy/implement-attempt.ts
7129
7163
  function buildInitialPrompt(node, opts = {}) {
7130
7164
  const repoMapBlock = opts.repoMap && opts.repoMap.length > 0 ? [`Contexto do reposit\xF3rio (refer\xEAncia, n\xE3o reescreva o que j\xE1 existe):`, opts.repoMap, ""] : [];
7165
+ const flowBlock = opts.flowContext && opts.flowContext.length > 0 ? [opts.flowContext, ""] : [];
7131
7166
  return [
7132
7167
  ...repoMapBlock,
7168
+ ...flowBlock,
7133
7169
  `Implemente a task "${node.title}" (id: ${node.id}) seguindo TDD.`,
7134
7170
  "Responda APENAS com um bloco ```json. H\xE1 DOIS mecanismos:",
7135
7171
  '- "edits" (PREFIRA \u2014 economia de token): [{ "path", "oldString", "newString", "replaceAll"? }].',
@@ -7158,7 +7194,7 @@ async function attemptImplementation(deps, options) {
7158
7194
  let lastResult;
7159
7195
  let lastError;
7160
7196
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
7161
- const prompt = attempt === 1 || !lastResult ? buildInitialPrompt(options.node, { repoMap: options.repoMap }) : buildRetryPrompt(options.node, lastResult, maxFeedbackChars);
7197
+ const prompt = attempt === 1 || !lastResult ? buildInitialPrompt(options.node, { repoMap: options.repoMap, flowContext: options.flowContext }) : buildRetryPrompt(options.node, lastResult, maxFeedbackChars);
7162
7198
  let plan;
7163
7199
  try {
7164
7200
  const text = await deps.generate(prompt);
@@ -7573,6 +7609,854 @@ var init_repo_map = __esm({
7573
7609
  FOCUS_BOOST = 3;
7574
7610
  }
7575
7611
  });
7612
+ var LspConfigOverrideSchema, LspDiagnosticSchema, LspDocumentSymbolSchema, LspTextEditSchema, LspWorkspaceEditSchema;
7613
+ var init_lsp_types = __esm({
7614
+ "src/core/lsp/lsp-types.ts"() {
7615
+ init_esm_shims();
7616
+ z.object({
7617
+ languageId: z.string(),
7618
+ extensions: z.array(z.string()),
7619
+ command: z.string(),
7620
+ args: z.array(z.string()),
7621
+ configFiles: z.array(z.string()),
7622
+ probeCommand: z.string().optional(),
7623
+ initializationOptions: z.record(z.string(), z.unknown()).optional(),
7624
+ settings: z.record(z.string(), z.unknown()).optional()
7625
+ });
7626
+ LspConfigOverrideSchema = z.object({
7627
+ languageId: z.string(),
7628
+ command: z.string(),
7629
+ args: z.array(z.string()).default([]),
7630
+ extensions: z.array(z.string()).optional(),
7631
+ initializationOptions: z.record(z.string(), z.unknown()).optional(),
7632
+ settings: z.record(z.string(), z.unknown()).optional()
7633
+ });
7634
+ z.object({
7635
+ file: z.string(),
7636
+ startLine: z.int().min(0),
7637
+ startCharacter: z.int().min(0),
7638
+ endLine: z.int().min(0),
7639
+ endCharacter: z.int().min(0)
7640
+ });
7641
+ z.object({
7642
+ signature: z.string(),
7643
+ documentation: z.string().optional(),
7644
+ language: z.string().optional()
7645
+ });
7646
+ LspDiagnosticSchema = z.object({
7647
+ file: z.string(),
7648
+ startLine: z.int().min(0),
7649
+ startCharacter: z.int().min(0),
7650
+ endLine: z.int().min(0),
7651
+ endCharacter: z.int().min(0),
7652
+ severity: z.number().int().min(1).max(4),
7653
+ message: z.string(),
7654
+ code: z.string().optional(),
7655
+ source: z.string().optional()
7656
+ });
7657
+ z.object({
7658
+ name: z.string(),
7659
+ kind: z.string(),
7660
+ file: z.string(),
7661
+ startLine: z.int().min(0),
7662
+ endLine: z.int().min(0)
7663
+ });
7664
+ LspDocumentSymbolSchema = z.object({
7665
+ name: z.string(),
7666
+ kind: z.string(),
7667
+ file: z.string(),
7668
+ startLine: z.int().min(0),
7669
+ endLine: z.int().min(0),
7670
+ children: z.lazy(() => z.array(LspDocumentSymbolSchema)).optional()
7671
+ });
7672
+ LspTextEditSchema = z.object({
7673
+ file: z.string(),
7674
+ startLine: z.int().min(0),
7675
+ startCharacter: z.int().min(0),
7676
+ endLine: z.int().min(0),
7677
+ endCharacter: z.int().min(0),
7678
+ newText: z.string()
7679
+ });
7680
+ LspWorkspaceEditSchema = z.object({
7681
+ changes: z.array(LspTextEditSchema)
7682
+ });
7683
+ z.object({
7684
+ languageId: z.string(),
7685
+ status: z.enum(["stopped", "starting", "ready", "error"]),
7686
+ pid: z.number().int().optional(),
7687
+ error: z.string().optional()
7688
+ });
7689
+ z.object({
7690
+ languageId: z.string(),
7691
+ confidence: z.number().min(0).max(1),
7692
+ detectedVia: z.enum(["file_extension", "config_file", "shebang"]),
7693
+ fileCount: z.int().min(0),
7694
+ configFile: z.string().optional()
7695
+ });
7696
+ z.object({
7697
+ title: z.string(),
7698
+ kind: z.string().optional(),
7699
+ isPreferred: z.boolean().optional(),
7700
+ edit: LspWorkspaceEditSchema.optional(),
7701
+ diagnostics: z.array(LspDiagnosticSchema).optional()
7702
+ });
7703
+ z.object({
7704
+ applied: z.boolean(),
7705
+ filesModified: z.array(z.string()),
7706
+ totalEdits: z.number().int(),
7707
+ errors: z.array(z.string()),
7708
+ backups: z.map(z.string(), z.string()).optional()
7709
+ });
7710
+ }
7711
+ });
7712
+ var BROWSER_PILOT_MODELS, ContextModeSchema, ProfileFilterConfigSchema, BrowserAutomationConfigSchema, FlowConfigSchema, ConfigSchema;
7713
+ var init_config_schema = __esm({
7714
+ "src/core/config/config-schema.ts"() {
7715
+ init_esm_shims();
7716
+ init_lsp_types();
7717
+ BROWSER_PILOT_MODELS = ["claude-3.5-sonnet", "gpt-4o", "gpt-4o-mini", "o1", "o1-mini"];
7718
+ ContextModeSchema = z.enum(["ultra-lean", "lean", "full"]);
7719
+ ProfileFilterConfigSchema = z.enum(["core", "pro", "expert", "all"]);
7720
+ BrowserAutomationConfigSchema = z.object({
7721
+ enabled: z.boolean().default(false),
7722
+ bridgeUrl: z.string().regex(/^https?:\/\//, "bridgeUrl must start with http:// or https://").default("http://127.0.0.1:9876/v1"),
7723
+ defaultModel: z.enum(BROWSER_PILOT_MODELS).default("claude-3.5-sonnet"),
7724
+ defaultCdpUrl: z.string().min(1).optional(),
7725
+ allowedDomains: z.array(z.string().min(1)).default([]),
7726
+ forbiddenCdpMethods: z.array(z.string().min(1)).default(["Browser.close"]),
7727
+ maxStepsDefault: z.number().int().min(1).max(100).default(25),
7728
+ tokenBudgetPerDay: z.number().int().nonnegative().optional()
7729
+ }).default({
7730
+ enabled: false,
7731
+ bridgeUrl: "http://127.0.0.1:9876/v1",
7732
+ defaultModel: "claude-3.5-sonnet",
7733
+ allowedDomains: [],
7734
+ forbiddenCdpMethods: ["Browser.close"],
7735
+ maxStepsDefault: 25
7736
+ });
7737
+ FlowConfigSchema = z.object({
7738
+ /** Master switch. OFF = byte-identical legacy context behaviour. */
7739
+ enabled: z.boolean().default(false),
7740
+ /** λ_base — minimum architectural forgetting rate. */
7741
+ lambdaBase: z.number().min(0).default(0.15),
7742
+ /** α — hypofrontality accelerator (weight of Φ on λ_flow). */
7743
+ alpha: z.number().min(0).default(1.5),
7744
+ /** BFS depth used to pull distant pinned invariants back into scope. */
7745
+ maxDepth: z.number().int().min(0).max(6).default(3),
7746
+ /** Peripheral neighbours below this decayed weight are pruned (unless pinned). */
7747
+ weightThreshold: z.number().min(0).max(1).default(0.1),
7748
+ /** EMA gain per consecutive success when computing Φ. */
7749
+ emaGain: z.number().min(0).max(1).default(0.34),
7750
+ /** Multiplier applied to Φ on a failure (0 = hard reset → re-hydrate memory). */
7751
+ resetFactor: z.number().min(0).max(1).default(0),
7752
+ /** Damping fraction of `emaGain` applied on a `partial` outcome. */
7753
+ partialFactor: z.number().min(0).max(1).default(0.5),
7754
+ /** rag budget is never scaled below this fraction of baseline (long-range safety). */
7755
+ budgetFloorRatio: z.number().min(0).max(1).default(0.25),
7756
+ /** How many recent task outcomes feed Φ. */
7757
+ historyWindow: z.number().int().min(1).max(200).default(12),
7758
+ /** Node types that are never diluted. */
7759
+ pinnedTypes: z.array(z.string()).default(["constraint", "risk", "decision", "acceptance_criteria", "constitution", "requirement"]),
7760
+ /** A/B experiment: alternate flow_on/flow_off deterministically per node to measure impact. */
7761
+ experiment: z.object({ abEnabled: z.boolean().default(false) }).default({ abEnabled: false })
7762
+ });
7763
+ ConfigSchema = z.object({
7764
+ port: z.number().int().min(1).max(65535).default(3e3),
7765
+ dbPath: z.string().default("workflow-graph"),
7766
+ basePath: z.string().optional(),
7767
+ contextMode: ContextModeSchema.default("lean"),
7768
+ profile: ProfileFilterConfigSchema.default("all"),
7769
+ dashboard: z.object({
7770
+ autoOpen: z.boolean().default(true)
7771
+ }).default({ autoOpen: true }),
7772
+ integrations: z.object({
7773
+ codeGraphAutoIndex: z.boolean().default(true),
7774
+ codeGraphReindexIntervalSec: z.number().int().min(0).default(0),
7775
+ lspServers: z.array(LspConfigOverrideSchema).default([]),
7776
+ browserAutomation: BrowserAutomationConfigSchema
7777
+ }).prefault({}),
7778
+ flow: FlowConfigSchema.prefault({})
7779
+ });
7780
+ }
7781
+ });
7782
+
7783
+ // src/core/context/flow-config.ts
7784
+ function resolveFlowConfig(source) {
7785
+ const raw = source.getProjectSetting(FLOW_CONFIG_SETTING_KEY);
7786
+ if (!raw) return FlowConfigSchema.parse({});
7787
+ try {
7788
+ return FlowConfigSchema.parse(JSON.parse(raw));
7789
+ } catch {
7790
+ return FlowConfigSchema.parse({});
7791
+ }
7792
+ }
7793
+ function flowAbArm(nodeId) {
7794
+ let sum = 0;
7795
+ for (let i = 0; i < nodeId.length; i += 1) sum += nodeId.charCodeAt(i);
7796
+ return sum % 2 === 0 ? "flow_on" : "flow_off";
7797
+ }
7798
+ var FLOW_CONFIG_SETTING_KEY;
7799
+ var init_flow_config = __esm({
7800
+ "src/core/context/flow-config.ts"() {
7801
+ init_esm_shims();
7802
+ init_config_schema();
7803
+ FLOW_CONFIG_SETTING_KEY = "flow_config";
7804
+ }
7805
+ });
7806
+
7807
+ // src/core/context/flow-index.ts
7808
+ function computeFlowIndex(outcomesNewestFirst, tuning = {}) {
7809
+ const { emaGain, resetFactor, partialFactor } = { ...DEFAULT_FLOW_TUNING, ...tuning };
7810
+ let streak = 0;
7811
+ for (const outcome of outcomesNewestFirst) {
7812
+ if (outcome === "success") streak += 1;
7813
+ else break;
7814
+ }
7815
+ let phi = 0;
7816
+ for (let i = outcomesNewestFirst.length - 1; i >= 0; i -= 1) {
7817
+ const outcome = outcomesNewestFirst[i];
7818
+ if (outcome === "success") {
7819
+ phi += emaGain * (1 - phi);
7820
+ } else if (outcome === "failure") {
7821
+ phi *= resetFactor;
7822
+ } else {
7823
+ phi *= 1 - emaGain * partialFactor;
7824
+ }
7825
+ }
7826
+ phi = Math.min(1, Math.max(0, phi));
7827
+ return { phi, streak, sampleCount: outcomesNewestFirst.length };
7828
+ }
7829
+ function computeLambdaFlow(phi, lambdaBase, alpha) {
7830
+ return lambdaBase + alpha * phi;
7831
+ }
7832
+ function decayWeight(lambda, distance) {
7833
+ return Math.exp(-lambda * distance);
7834
+ }
7835
+ var DEFAULT_FLOW_TUNING;
7836
+ var init_flow_index = __esm({
7837
+ "src/core/context/flow-index.ts"() {
7838
+ init_esm_shims();
7839
+ DEFAULT_FLOW_TUNING = {
7840
+ emaGain: 0.34,
7841
+ resetFactor: 0,
7842
+ partialFactor: 0.5
7843
+ };
7844
+ }
7845
+ });
7846
+
7847
+ // src/core/context/token-estimator.ts
7848
+ function isAsciiLetter(code) {
7849
+ return code >= 65 && code <= 90 || code >= 97 && code <= 122;
7850
+ }
7851
+ function isAsciiDigit(code) {
7852
+ return code >= 48 && code <= 57;
7853
+ }
7854
+ function isWhitespace(code) {
7855
+ return code === 32 || code === 9 || code === 10 || code === 13 || code === 12 || code === 11;
7856
+ }
7857
+ function estimateTokens2(text) {
7858
+ if (!text) return 0;
7859
+ let tokens = 0;
7860
+ const len = text.length;
7861
+ let i = 0;
7862
+ while (i < len) {
7863
+ const code = text.charCodeAt(i);
7864
+ if (isWhitespace(code)) {
7865
+ i++;
7866
+ continue;
7867
+ }
7868
+ if (isAsciiLetter(code)) {
7869
+ const start = i;
7870
+ i++;
7871
+ let subWords = 1;
7872
+ while (i < len && isAsciiLetter(text.charCodeAt(i))) {
7873
+ if (i > start && text.charCodeAt(i) >= 65 && text.charCodeAt(i) <= 90 && text.charCodeAt(i - 1) >= 97 && text.charCodeAt(i - 1) <= 122) {
7874
+ subWords++;
7875
+ }
7876
+ i++;
7877
+ }
7878
+ const wordLen = i - start;
7879
+ if (subWords > 1) {
7880
+ tokens += subWords;
7881
+ } else {
7882
+ tokens += wordLen <= 6 ? 1 : wordLen > 20 ? Math.ceil(wordLen / 4) : Math.ceil(wordLen / 5);
7883
+ }
7884
+ continue;
7885
+ }
7886
+ if (isAsciiDigit(code)) {
7887
+ const start = i;
7888
+ i++;
7889
+ while (i < len && isAsciiDigit(text.charCodeAt(i))) i++;
7890
+ const digitsLen = i - start;
7891
+ tokens += Math.ceil(digitsLen / 3);
7892
+ continue;
7893
+ }
7894
+ tokens += 1;
7895
+ i++;
7896
+ }
7897
+ return tokens;
7898
+ }
7899
+ var init_token_estimator = __esm({
7900
+ "src/core/context/token-estimator.ts"() {
7901
+ init_esm_shims();
7902
+ }
7903
+ });
7904
+
7905
+ // src/core/context/compact-context.ts
7906
+ function toTaskSummary(node) {
7907
+ const summary = {
7908
+ id: node.id,
7909
+ type: node.type,
7910
+ title: node.title,
7911
+ status: node.status,
7912
+ priority: node.priority
7913
+ };
7914
+ if (node.description) summary.description = node.description;
7915
+ if (node.sprint) summary.sprint = node.sprint;
7916
+ if (node.xpSize) summary.xpSize = node.xpSize;
7917
+ if (node.tags?.length) summary.tags = node.tags;
7918
+ return summary;
7919
+ }
7920
+ function isInferred(edge) {
7921
+ return edge.metadata?.inferred === true;
7922
+ }
7923
+ function buildTaskContext(store2, nodeId, snapshot) {
7924
+ const resolveNode = (id) => {
7925
+ return store2.getNodeById(id);
7926
+ };
7927
+ const node = resolveNode(nodeId);
7928
+ if (!node) {
7929
+ log26.warn(`buildTaskContext: node ${nodeId} not found`);
7930
+ return null;
7931
+ }
7932
+ let parent = null;
7933
+ if (node.parentId) {
7934
+ const parentNode = resolveNode(node.parentId);
7935
+ if (parentNode) parent = toTaskSummary(parentNode);
7936
+ }
7937
+ let childNodes;
7938
+ {
7939
+ childNodes = store2.getChildNodes(nodeId);
7940
+ }
7941
+ const children = childNodes.map(toTaskSummary);
7942
+ let incomingEdges;
7943
+ let outgoingEdges;
7944
+ {
7945
+ incomingEdges = store2.getEdgesTo(nodeId);
7946
+ outgoingEdges = store2.getEdgesFrom(nodeId);
7947
+ }
7948
+ const blockers = [];
7949
+ const dependsOn = [];
7950
+ const relatedIds = /* @__PURE__ */ new Set();
7951
+ const relatedNodes = [];
7952
+ const implementsNodes = [];
7953
+ const derivedFromNodes = [];
7954
+ let edgeParent = null;
7955
+ const edgeChildren = [];
7956
+ const edgeChildrenIds = /* @__PURE__ */ new Set();
7957
+ for (const edge of incomingEdges) {
7958
+ if (edge.relationType === "blocks") {
7959
+ const blockerNode = resolveNode(edge.from);
7960
+ if (blockerNode) {
7961
+ blockers.push({
7962
+ id: blockerNode.id,
7963
+ title: blockerNode.title,
7964
+ status: blockerNode.status,
7965
+ relationType: edge.relationType,
7966
+ inferred: isInferred(edge)
7967
+ });
7968
+ }
7969
+ } else if (edge.relationType === "related_to") {
7970
+ const relNode = resolveNode(edge.from);
7971
+ if (relNode && !relatedIds.has(relNode.id)) {
7972
+ relatedIds.add(relNode.id);
7973
+ relatedNodes.push(toTaskSummary(relNode));
7974
+ }
7975
+ } else if (edge.relationType === "parent_of" && !edgeParent) {
7976
+ const parentNode = resolveNode(edge.from);
7977
+ if (parentNode) edgeParent = toTaskSummary(parentNode);
7978
+ }
7979
+ }
7980
+ for (const edge of outgoingEdges) {
7981
+ if (edge.relationType === "depends_on") {
7982
+ const depNode = resolveNode(edge.to);
7983
+ if (depNode) {
7984
+ dependsOn.push({
7985
+ id: depNode.id,
7986
+ title: depNode.title,
7987
+ status: depNode.status,
7988
+ resolved: depNode.status === "done",
7989
+ inferred: isInferred(edge)
7990
+ });
7991
+ }
7992
+ } else if (edge.relationType === "related_to") {
7993
+ const relNode = resolveNode(edge.to);
7994
+ if (relNode && !relatedIds.has(relNode.id)) {
7995
+ relatedIds.add(relNode.id);
7996
+ relatedNodes.push(toTaskSummary(relNode));
7997
+ }
7998
+ } else if (edge.relationType === "implements") {
7999
+ const implNode = resolveNode(edge.to);
8000
+ if (implNode) implementsNodes.push(toTaskSummary(implNode));
8001
+ } else if (edge.relationType === "derived_from") {
8002
+ const derivedNode = resolveNode(edge.to);
8003
+ if (derivedNode) derivedFromNodes.push(toTaskSummary(derivedNode));
8004
+ } else if (edge.relationType === "parent_of") {
8005
+ const childNode = resolveNode(edge.to);
8006
+ if (childNode && !edgeChildrenIds.has(childNode.id)) {
8007
+ edgeChildrenIds.add(childNode.id);
8008
+ edgeChildren.push(toTaskSummary(childNode));
8009
+ }
8010
+ }
8011
+ }
8012
+ const acceptanceCriteria = getNodeAcFromStore(store2, node.id);
8013
+ const sourceRef = node.sourceRef ? { ...node.sourceRef } : null;
8014
+ const localNodes = [node, ...childNodes];
8015
+ const originalChars = localNodes.reduce(
8016
+ (sum, n) => sum + n.title.length + (n.description?.length ?? 0) + (n.acceptanceCriteria?.join("").length ?? 0),
8017
+ 0
8018
+ ) + [...incomingEdges, ...outgoingEdges].reduce((sum, e) => sum + (e.reason?.length ?? 0), 0);
8019
+ const summary = toTaskSummary(node);
8020
+ const corePayload = {
8021
+ task: summary,
8022
+ parent,
8023
+ children,
8024
+ blockers,
8025
+ dependsOn,
8026
+ relatedNodes: relatedNodes.length > 0 ? relatedNodes : void 0,
8027
+ implementsNodes: implementsNodes.length > 0 ? implementsNodes : void 0,
8028
+ derivedFromNodes: derivedFromNodes.length > 0 ? derivedFromNodes : void 0,
8029
+ edgeParent: edgeParent ?? void 0,
8030
+ edgeChildren: edgeChildren.length > 0 ? edgeChildren : void 0,
8031
+ acceptanceCriteria,
8032
+ sourceRef,
8033
+ metrics: { originalChars: 0, compactChars: 0, reductionPercent: 0, estimatedTokens: 0 }
8034
+ };
8035
+ const compactJson = JSON.stringify(corePayload);
8036
+ const compactChars = compactJson.length;
8037
+ const reductionPercent = originalChars > 0 ? Math.round((originalChars - compactChars) / originalChars * 100) : 0;
8038
+ const metrics = {
8039
+ originalChars,
8040
+ compactChars,
8041
+ reductionPercent,
8042
+ estimatedTokens: estimateTokens2(compactJson)
8043
+ };
8044
+ const contextPayload = {
8045
+ ...corePayload,
8046
+ node: summary,
8047
+ metrics
8048
+ };
8049
+ log26.info(`Context for ${nodeId}: ${metrics.estimatedTokens} tokens, ${metrics.reductionPercent}% reduction`);
8050
+ return contextPayload;
8051
+ }
8052
+ var log26, KEY_MAP, KEY_LEGEND;
8053
+ var init_compact_context = __esm({
8054
+ "src/core/context/compact-context.ts"() {
8055
+ init_esm_shims();
8056
+ init_ac_helpers();
8057
+ init_token_estimator();
8058
+ init_logger();
8059
+ log26 = createLogger({ layer: "core", source: "compact-context.ts" });
8060
+ KEY_MAP = {
8061
+ // TaskContext top-level
8062
+ task: "tk",
8063
+ node: "n",
8064
+ parent: "par",
8065
+ children: "ch",
8066
+ blockers: "bl",
8067
+ dependsOn: "dep",
8068
+ acceptanceCriteria: "ac",
8069
+ sourceRef: "sr",
8070
+ relatedNodes: "rel",
8071
+ implementsNodes: "impl",
8072
+ derivedFromNodes: "drv",
8073
+ edgeParent: "ep",
8074
+ edgeChildren: "ech",
8075
+ metrics: "m",
8076
+ // TaskSummary / shared fields
8077
+ id: "i",
8078
+ type: "t",
8079
+ title: "n",
8080
+ status: "s",
8081
+ priority: "p",
8082
+ description: "d",
8083
+ sprint: "sp",
8084
+ xpSize: "xs",
8085
+ tags: "tg",
8086
+ // BlockerInfo / DependencyInfo
8087
+ relationType: "rt",
8088
+ inferred: "inf",
8089
+ resolved: "res",
8090
+ // SourceRefInfo
8091
+ file: "f",
8092
+ startLine: "sl",
8093
+ endLine: "el",
8094
+ confidence: "cf",
8095
+ // ContextMetrics
8096
+ originalChars: "oc",
8097
+ compactChars: "cc",
8098
+ reductionPercent: "rp",
8099
+ estimatedTokens: "et"
8100
+ };
8101
+ KEY_LEGEND = {};
8102
+ for (const [full, short] of Object.entries(KEY_MAP)) {
8103
+ KEY_LEGEND[short] = full;
8104
+ }
8105
+ }
8106
+ });
8107
+
8108
+ // src/core/context/topological-decay.ts
8109
+ function corePayloadTokens(ctx, pinned) {
8110
+ const { metrics: _m, node: _n, ...core } = ctx;
8111
+ return estimateTokens2(JSON.stringify(pinned.length > 0 ? { ...core, pinnedInvariants: pinned } : core));
8112
+ }
8113
+ function isPinnedType(type, pinnedTypes) {
8114
+ return pinnedTypes.has(type);
8115
+ }
8116
+ function collectDistantInvariants(store2, rootId, alreadyPresent, maxDepth, pinnedTypes) {
8117
+ if (maxDepth <= 0) return [];
8118
+ const visited = /* @__PURE__ */ new Set([rootId]);
8119
+ const found = /* @__PURE__ */ new Map();
8120
+ let frontier = [rootId];
8121
+ for (let depth = 1; depth <= maxDepth && frontier.length > 0; depth += 1) {
8122
+ const next = [];
8123
+ for (const id of frontier) {
8124
+ const edges = [...store2.getEdgesFrom(id), ...store2.getEdgesTo(id)];
8125
+ for (const edge of edges) {
8126
+ const neighborId = edge.from === id ? edge.to : edge.from;
8127
+ if (visited.has(neighborId)) continue;
8128
+ visited.add(neighborId);
8129
+ next.push(neighborId);
8130
+ if (alreadyPresent.has(neighborId) || found.has(neighborId)) continue;
8131
+ const neighbor = store2.getNodeById(neighborId);
8132
+ if (neighbor && isPinnedType(neighbor.type, pinnedTypes)) {
8133
+ found.set(neighborId, {
8134
+ id: neighbor.id,
8135
+ type: neighbor.type,
8136
+ title: neighbor.title,
8137
+ status: neighbor.status,
8138
+ distance: depth
8139
+ });
8140
+ }
8141
+ }
8142
+ }
8143
+ frontier = next;
8144
+ }
8145
+ return [...found.values()];
8146
+ }
8147
+ function buildDecayedTaskContext(store2, nodeId, opts) {
8148
+ const base = buildTaskContext(store2, nodeId);
8149
+ if (!base) return null;
8150
+ const pinnedTypes = opts.pinnedTypes ?? new Set(DEFAULT_PINNED_TYPES);
8151
+ const tokensBaseline = corePayloadTokens(base, []);
8152
+ const peripheralWeight = decayWeight(opts.lambda, PERIPHERAL_DISTANCE);
8153
+ const peripheralSurvives = peripheralWeight >= opts.weightThreshold;
8154
+ const presentIds = /* @__PURE__ */ new Set([base.task.id]);
8155
+ if (base.parent) presentIds.add(base.parent.id);
8156
+ for (const c of base.children) presentIds.add(c.id);
8157
+ for (const b of base.blockers) presentIds.add(b.id);
8158
+ for (const d of base.dependsOn) presentIds.add(d.id);
8159
+ const ctx = structuredClone(base);
8160
+ let prunedCount = 0;
8161
+ const prunePeripheral = (list) => {
8162
+ if (!list) return list;
8163
+ const kept = list.filter((item) => {
8164
+ presentIds.add(item.id);
8165
+ if (isPinnedType(item.type, pinnedTypes)) return true;
8166
+ if (peripheralSurvives) return true;
8167
+ prunedCount += 1;
8168
+ return false;
8169
+ });
8170
+ return kept.length > 0 ? kept : void 0;
8171
+ };
8172
+ ctx.relatedNodes = prunePeripheral(ctx.relatedNodes);
8173
+ ctx.implementsNodes = prunePeripheral(ctx.implementsNodes);
8174
+ ctx.derivedFromNodes = prunePeripheral(ctx.derivedFromNodes);
8175
+ ctx.edgeChildren = prunePeripheral(ctx.edgeChildren);
8176
+ if (ctx.edgeParent) {
8177
+ presentIds.add(ctx.edgeParent.id);
8178
+ if (!isPinnedType(ctx.edgeParent.type, pinnedTypes) && !peripheralSurvives) {
8179
+ ctx.edgeParent = null;
8180
+ prunedCount += 1;
8181
+ }
8182
+ }
8183
+ const pinnedInvariants = collectDistantInvariants(
8184
+ store2,
8185
+ nodeId,
8186
+ presentIds,
8187
+ opts.maxDepth,
8188
+ pinnedTypes
8189
+ );
8190
+ const tokensActual = corePayloadTokens(ctx, pinnedInvariants);
8191
+ ctx.metrics = {
8192
+ ...ctx.metrics,
8193
+ estimatedTokens: tokensActual
8194
+ };
8195
+ ctx.node = ctx.task;
8196
+ 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);
8197
+ log27.debug("flow:decay", {
8198
+ nodeId,
8199
+ lambda: opts.lambda,
8200
+ prunedCount,
8201
+ pinnedCount: pinnedInvariants.length,
8202
+ tokensBaseline,
8203
+ tokensActual
8204
+ });
8205
+ return {
8206
+ context: ctx,
8207
+ meta: {
8208
+ lambda: opts.lambda,
8209
+ prunedCount,
8210
+ retainedCount,
8211
+ pinnedCount: pinnedInvariants.length,
8212
+ tokensBaseline,
8213
+ tokensActual,
8214
+ tokensSaved: tokensBaseline - tokensActual,
8215
+ pinnedInvariants
8216
+ }
8217
+ };
8218
+ }
8219
+ var log27, DEFAULT_PINNED_TYPES, PERIPHERAL_DISTANCE;
8220
+ var init_topological_decay = __esm({
8221
+ "src/core/context/topological-decay.ts"() {
8222
+ init_esm_shims();
8223
+ init_compact_context();
8224
+ init_flow_index();
8225
+ init_token_estimator();
8226
+ init_logger();
8227
+ log27 = createLogger({ layer: "core", source: "topological-decay.ts" });
8228
+ DEFAULT_PINNED_TYPES = [
8229
+ "constraint",
8230
+ "risk",
8231
+ "decision",
8232
+ "acceptance_criteria",
8233
+ "constitution",
8234
+ "requirement"
8235
+ ];
8236
+ PERIPHERAL_DISTANCE = 2;
8237
+ }
8238
+ });
8239
+
8240
+ // src/core/store/episodic-outcomes-store.ts
8241
+ function buildApproachSummary(touchedFiles, acIds) {
8242
+ const files = [...touchedFiles].sort().join("+");
8243
+ const acs = [...acIds].sort().join(",");
8244
+ return `${files}:${acs}`;
8245
+ }
8246
+ function insertEpisodicOutcome(db, outcome) {
8247
+ db.prepare(
8248
+ `INSERT OR IGNORE INTO episodic_outcomes
8249
+ (id, node_id, task_type, tags, approach_summary, outcome, cycle_time_delta, reopen_count, created_at)
8250
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
8251
+ ).run(
8252
+ outcome.id,
8253
+ outcome.nodeId,
8254
+ outcome.taskType,
8255
+ outcome.tags,
8256
+ outcome.approachSummary,
8257
+ outcome.outcome,
8258
+ outcome.cycleTimeDelta,
8259
+ outcome.reopenCount,
8260
+ outcome.createdAt
8261
+ );
8262
+ }
8263
+ function queryEpisodicOutcomes(db, opts = {}) {
8264
+ const conditions = [];
8265
+ const params = [];
8266
+ if (opts.taskType) {
8267
+ conditions.push("task_type = ?");
8268
+ params.push(opts.taskType);
8269
+ }
8270
+ if (opts.maxAgeDays) {
8271
+ const cutoff = Date.now() - opts.maxAgeDays * 24 * 3600 * 1e3;
8272
+ conditions.push("created_at >= ?");
8273
+ params.push(cutoff);
8274
+ }
8275
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
8276
+ const limit = Math.min(opts.limit ?? 100, 500);
8277
+ params.push(limit);
8278
+ const rows = db.prepare(
8279
+ `SELECT id, node_id, task_type, tags, approach_summary, outcome,
8280
+ cycle_time_delta, reopen_count, created_at
8281
+ FROM episodic_outcomes
8282
+ ${where}
8283
+ ORDER BY created_at DESC
8284
+ LIMIT ?`
8285
+ ).all(...params);
8286
+ return rows.map((r) => ({
8287
+ id: r.id,
8288
+ nodeId: r.node_id,
8289
+ taskType: r.task_type,
8290
+ tags: r.tags,
8291
+ approachSummary: r.approach_summary,
8292
+ outcome: r.outcome,
8293
+ cycleTimeDelta: r.cycle_time_delta,
8294
+ reopenCount: r.reopen_count,
8295
+ createdAt: r.created_at
8296
+ }));
8297
+ }
8298
+ var init_episodic_outcomes_store = __esm({
8299
+ "src/core/store/episodic-outcomes-store.ts"() {
8300
+ init_esm_shims();
8301
+ }
8302
+ });
8303
+
8304
+ // src/core/context/flow-metrics-store.ts
8305
+ function insertFlowMetric(db, metric) {
8306
+ db.prepare(
8307
+ `INSERT OR IGNORE INTO flow_metrics
8308
+ (id, project_id, node_id, mode, phi, lambda,
8309
+ tokens_baseline, tokens_actual, pruned_count, pinned_count, created_at)
8310
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
8311
+ ).run(
8312
+ metric.id,
8313
+ metric.projectId,
8314
+ metric.nodeId,
8315
+ metric.mode,
8316
+ metric.phi,
8317
+ metric.lambda,
8318
+ metric.tokensBaseline,
8319
+ metric.tokensActual,
8320
+ metric.prunedCount,
8321
+ metric.pinnedCount,
8322
+ metric.createdAt
8323
+ );
8324
+ }
8325
+ var init_flow_metrics_store = __esm({
8326
+ "src/core/context/flow-metrics-store.ts"() {
8327
+ init_esm_shims();
8328
+ }
8329
+ });
8330
+
8331
+ // src/core/context/flow-compact.ts
8332
+ function formatFlowContext(result) {
8333
+ const { context: ctx, pinnedInvariants } = result;
8334
+ const lines = ["Contexto do grafo (dilu\xEDdo por flow \u2014 \u03A6 governando o esquecimento):"];
8335
+ lines.push(`- Task: ${ctx.task.title} (${ctx.task.id}) [${ctx.task.status}]`);
8336
+ if (ctx.task.description) lines.push(` ${ctx.task.description}`);
8337
+ if (ctx.acceptanceCriteria.length > 0) {
8338
+ lines.push("- Crit\xE9rios de aceita\xE7\xE3o:");
8339
+ for (const ac of ctx.acceptanceCriteria) lines.push(` \u2022 ${ac}`);
8340
+ }
8341
+ const openBlockers = ctx.blockers.filter((b) => b.status !== "done");
8342
+ if (openBlockers.length > 0) {
8343
+ lines.push(`- Bloqueadores: ${openBlockers.map((b) => `${b.title} (${b.status})`).join("; ")}`);
8344
+ }
8345
+ const openDeps = ctx.dependsOn.filter((d) => !d.resolved);
8346
+ if (openDeps.length > 0) {
8347
+ lines.push(`- Depende de: ${openDeps.map((d) => `${d.title} (${d.status})`).join("; ")}`);
8348
+ }
8349
+ if (pinnedInvariants.length > 0) {
8350
+ lines.push("- Invariantes pinados (nunca dilu\xEDdos):");
8351
+ for (const inv of pinnedInvariants) lines.push(` \u2022 [${inv.type}] ${inv.title}`);
8352
+ }
8353
+ return lines.join("\n");
8354
+ }
8355
+ function recordMetric(store2, row) {
8356
+ try {
8357
+ insertFlowMetric(store2.getDb(), {
8358
+ id: generateId("flowm"),
8359
+ createdAt: row.createdAt ?? Date.now(),
8360
+ ...row
8361
+ });
8362
+ } catch (err) {
8363
+ log28.warn("flow:metric:record-failed", { error: err instanceof Error ? err.message : String(err) });
8364
+ }
8365
+ }
8366
+ function applyFlowToCompact(store2, nodeId) {
8367
+ const cfg = resolveFlowConfig(store2);
8368
+ if (!cfg.enabled) return null;
8369
+ const projectId = store2.getActiveProject()?.id ?? "unknown";
8370
+ const outcomes = queryEpisodicOutcomes(store2.getDb(), { limit: cfg.historyWindow }).map((o) => o.outcome);
8371
+ const state = computeFlowIndex(outcomes, {
8372
+ emaGain: cfg.emaGain,
8373
+ resetFactor: cfg.resetFactor,
8374
+ partialFactor: cfg.partialFactor
8375
+ });
8376
+ const lambda = computeLambdaFlow(state.phi, cfg.lambdaBase, cfg.alpha);
8377
+ const mode = cfg.experiment.abEnabled ? flowAbArm(nodeId) : "flow_on";
8378
+ if (mode === "flow_off") {
8379
+ const base = buildTaskContext(store2, nodeId);
8380
+ if (!base) return null;
8381
+ const baseline = base.metrics.estimatedTokens;
8382
+ recordMetric(store2, {
8383
+ projectId,
8384
+ nodeId,
8385
+ mode,
8386
+ phi: state.phi,
8387
+ lambda,
8388
+ tokensBaseline: baseline,
8389
+ tokensActual: baseline,
8390
+ prunedCount: 0,
8391
+ pinnedCount: 0
8392
+ });
8393
+ return {
8394
+ context: base,
8395
+ pinnedInvariants: [],
8396
+ flow: {
8397
+ enabled: true,
8398
+ mode,
8399
+ phi: state.phi,
8400
+ streak: state.streak,
8401
+ lambda,
8402
+ prunedCount: 0,
8403
+ pinnedCount: 0,
8404
+ tokensBaseline: baseline,
8405
+ tokensActual: baseline,
8406
+ tokensSaved: 0
8407
+ }
8408
+ };
8409
+ }
8410
+ const decayed = buildDecayedTaskContext(store2, nodeId, {
8411
+ lambda,
8412
+ maxDepth: cfg.maxDepth,
8413
+ weightThreshold: cfg.weightThreshold,
8414
+ pinnedTypes: new Set(cfg.pinnedTypes)
8415
+ });
8416
+ if (!decayed) return null;
8417
+ recordMetric(store2, {
8418
+ projectId,
8419
+ nodeId,
8420
+ mode,
8421
+ phi: state.phi,
8422
+ lambda,
8423
+ tokensBaseline: decayed.meta.tokensBaseline,
8424
+ tokensActual: decayed.meta.tokensActual,
8425
+ prunedCount: decayed.meta.prunedCount,
8426
+ pinnedCount: decayed.meta.pinnedCount
8427
+ });
8428
+ return {
8429
+ context: decayed.context,
8430
+ pinnedInvariants: decayed.meta.pinnedInvariants,
8431
+ flow: {
8432
+ enabled: true,
8433
+ mode,
8434
+ phi: state.phi,
8435
+ streak: state.streak,
8436
+ lambda,
8437
+ prunedCount: decayed.meta.prunedCount,
8438
+ pinnedCount: decayed.meta.pinnedCount,
8439
+ tokensBaseline: decayed.meta.tokensBaseline,
8440
+ tokensActual: decayed.meta.tokensActual,
8441
+ tokensSaved: decayed.meta.tokensSaved
8442
+ }
8443
+ };
8444
+ }
8445
+ var log28;
8446
+ var init_flow_compact = __esm({
8447
+ "src/core/context/flow-compact.ts"() {
8448
+ init_esm_shims();
8449
+ init_flow_config();
8450
+ init_flow_index();
8451
+ init_topological_decay();
8452
+ init_compact_context();
8453
+ init_episodic_outcomes_store();
8454
+ init_flow_metrics_store();
8455
+ init_id();
8456
+ init_logger();
8457
+ log28 = createLogger({ layer: "core", source: "flow-compact.ts" });
8458
+ }
8459
+ });
7576
8460
 
7577
8461
  // src/cli/shared/live-implement.ts
7578
8462
  function buildLiveImplement(options) {
@@ -7589,6 +8473,14 @@ function buildLiveImplement(options) {
7589
8473
  const repoRelations = projectId ? codeStore.getAllRelations(projectId) : [];
7590
8474
  const implement = async (node) => {
7591
8475
  const repoMap = repoSymbols.length > 0 ? buildRepoMap({ symbols: repoSymbols, relations: repoRelations }, { tokenBudget: REPO_MAP_TOKEN_BUDGET, focus: node.title }).text : void 0;
8476
+ let flowContext;
8477
+ const flow = applyFlowToCompact(store2, node.id);
8478
+ if (flow) {
8479
+ flowContext = formatFlowContext(flow);
8480
+ onLog?.(
8481
+ ` [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`
8482
+ );
8483
+ }
7592
8484
  const outcome = await attemptImplementation(
7593
8485
  {
7594
8486
  generate: async (prompt) => {
@@ -7604,13 +8496,28 @@ function buildLiveImplement(options) {
7604
8496
  },
7605
8497
  execute: (plan) => executePlan(plan, { workspaceDir: dir, defaultTestCommand: testCmd })
7606
8498
  },
7607
- { node, maxAttempts, repoMap }
8499
+ { node, maxAttempts, repoMap, flowContext }
7608
8500
  );
7609
8501
  const files = outcome.lastResult?.applied.length ?? 0;
7610
8502
  const task = ledger.byTask(node.id);
7611
8503
  onLog?.(
7612
8504
  ` [live] ${client.modelFor("implement")}: ${outcome.attempts} tentativa(s), ${files} arquivo(s), ${task.total} tok \u2192 ${outcome.success ? "verde" : "escala"}`
7613
8505
  );
8506
+ try {
8507
+ const applied = outcome.lastResult?.applied ?? [];
8508
+ insertEpisodicOutcome(store2.getDb(), {
8509
+ id: generateId("epi"),
8510
+ nodeId: node.id,
8511
+ taskType: "",
8512
+ tags: "",
8513
+ approachSummary: buildApproachSummary(applied, []),
8514
+ outcome: outcome.success ? "success" : "failure",
8515
+ cycleTimeDelta: 0,
8516
+ reopenCount: 0,
8517
+ createdAt: Date.now()
8518
+ });
8519
+ } catch {
8520
+ }
7614
8521
  return outcome.success;
7615
8522
  };
7616
8523
  return { implement, repoSymbolCount: repoSymbols.length };
@@ -7625,6 +8532,9 @@ var init_live_implement = __esm({
7625
8532
  init_implement_attempt();
7626
8533
  init_code_store();
7627
8534
  init_repo_map();
8535
+ init_flow_compact();
8536
+ init_episodic_outcomes_store();
8537
+ init_id();
7628
8538
  REPO_MAP_TOKEN_BUDGET = 1e3;
7629
8539
  }
7630
8540
  });
@@ -7723,6 +8633,44 @@ var init_app = __esm({
7723
8633
  };
7724
8634
  }
7725
8635
  });
8636
+ function BannerScreen({ onDone }) {
8637
+ const cols = process.stdout.columns ?? 80;
8638
+ const maxSteps = Math.max(cols - 14, 10);
8639
+ const [step, setStep] = useState(0);
8640
+ const done = useCallback(onDone, [onDone]);
8641
+ useEffect(() => {
8642
+ if (step >= maxSteps) {
8643
+ const t2 = setTimeout(done, 300);
8644
+ return () => clearTimeout(t2);
8645
+ }
8646
+ const t = setTimeout(() => setStep((s) => s + 1), FRAME_MS);
8647
+ return () => clearTimeout(t);
8648
+ }, [step, maxSteps, done]);
8649
+ const bug = BUG_FRAMES[step % BUG_FRAMES.length];
8650
+ const trailLen = Math.min(step, maxSteps - 1);
8651
+ const trail = "\xB7".repeat(trailLen);
8652
+ const pad = " ".repeat(step < maxSteps ? step : maxSteps);
8653
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingTop: 1, children: [
8654
+ /* @__PURE__ */ jsxs(Text, { color: "green", children: [
8655
+ pad,
8656
+ trail,
8657
+ bug
8658
+ ] }),
8659
+ step >= maxSteps && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
8660
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: TAGLINE }),
8661
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " type /help to see all commands" })
8662
+ ] })
8663
+ ] });
8664
+ }
8665
+ var BUG_FRAMES, TAGLINE, FRAME_MS;
8666
+ var init_banner_screen = __esm({
8667
+ "src/tui/banner-screen.tsx"() {
8668
+ init_esm_shims();
8669
+ BUG_FRAMES = [" /\\(\u2022\u203F\u2022)/\\ ", " \\(\u2022\u203F\u2022)/ ", " /\\(\u2022\u203F\u2022)/\\ "];
8670
+ TAGLINE = "mcp-graph-agent \u2014 Software Engineer as a Service";
8671
+ FRAME_MS = 28;
8672
+ }
8673
+ });
7726
8674
  function CommandBar({ value, onChange, onSubmit, suggestions }) {
7727
8675
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
7728
8676
  suggestions.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginBottom: 1, children: suggestions.map((c) => /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
@@ -7848,8 +8796,11 @@ var init_dispatch = __esm({
7848
8796
  });
7849
8797
  function InteractiveApp({ dashboard, port, asyncPort, liveRunner, skillCommands = [] }) {
7850
8798
  const { exit } = useApp();
8799
+ const [phase, setPhase] = useState(
8800
+ process.stdout.isTTY ? "banner" : "dashboard"
8801
+ );
7851
8802
  const [input, setInput] = useState("");
7852
- const [log45, setLog] = useState([]);
8803
+ const [log48, setLog] = useState([]);
7853
8804
  const [running, setRunning] = useState(false);
7854
8805
  const [showHelp, setShowHelp] = useState(false);
7855
8806
  const append = (line) => setLog((prev) => [...prev, line].slice(-MAX_LOG_LINES));
@@ -7877,15 +8828,28 @@ function InteractiveApp({ dashboard, port, asyncPort, liveRunner, skillCommands
7877
8828
  if (ASYNC_CMDS.includes(parsed.cmd) && asyncPort) {
7878
8829
  append(`\u203A ${text}`);
7879
8830
  setRunning(true);
7880
- runAsyncCommand(asyncPort, parsed).then((summary) => append(summary)).catch((err) => append(`erro: ${err instanceof Error ? err.message : String(err)}`)).finally(() => setRunning(false));
8831
+ setImmediate(() => {
8832
+ void runAsyncCommand(asyncPort, parsed).then((summary) => append(summary)).catch((err) => append(`erro: ${err instanceof Error ? err.message : String(err)}`)).finally(() => setRunning(false));
8833
+ });
8834
+ return;
8835
+ }
8836
+ const matchedSkill = skillCommands.find((sc) => sc.name === parsed.cmd);
8837
+ if (matchedSkill) {
8838
+ const skill = port.getSkill(parsed.cmd);
8839
+ append(`\u203A ${text}`);
8840
+ append(skill ? `=== ${skill.name} ===
8841
+ ${skill.body}` : `Skill n\xE3o encontrada: ${parsed.cmd}`);
7881
8842
  return;
7882
8843
  }
7883
8844
  append(`\u203A ${text}`);
7884
8845
  append(runReadCommand(port, parsed));
7885
8846
  };
8847
+ if (phase === "banner") {
8848
+ return /* @__PURE__ */ jsx(BannerScreen, { onDone: () => setPhase("dashboard") });
8849
+ }
7886
8850
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
7887
8851
  /* @__PURE__ */ jsx(App, { model: dashboard }),
7888
- 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)) }),
8852
+ 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)) }),
7889
8853
  showHelp && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "round", paddingX: 1, children: [
7890
8854
  /* @__PURE__ */ jsx(Text, { bold: true, children: "Comandos:" }),
7891
8855
  COMMANDS.map((c) => /* @__PURE__ */ jsxs(Text, { children: [
@@ -7906,6 +8870,7 @@ var init_interactive_app = __esm({
7906
8870
  "src/tui/interactive-app.tsx"() {
7907
8871
  init_esm_shims();
7908
8872
  init_app();
8873
+ init_banner_screen();
7909
8874
  init_command_bar();
7910
8875
  init_dispatch();
7911
8876
  MAX_LOG_LINES = 12;
@@ -8111,22 +9076,25 @@ function parseSkillMarkdown(content) {
8111
9076
  } catch (err) {
8112
9077
  return { ok: false, error: `YAML parse error: ${err instanceof Error ? err.message : String(err)}` };
8113
9078
  }
9079
+ const rawTriggers = frontmatter.triggers;
9080
+ const triggers = Array.isArray(rawTriggers) ? rawTriggers.map((t) => typeof t === "string" ? { event: t } : t) : rawTriggers;
9081
+ const phases = Array.isArray(frontmatter.phases) ? frontmatter.phases : [];
8114
9082
  const raw = {
8115
9083
  name: frontmatter.name,
8116
9084
  description: frontmatter.description,
8117
9085
  category: frontmatter.category ?? "know-me",
8118
- phases: frontmatter.phases,
9086
+ phases,
8119
9087
  // §extracta-sweep-1 — optional `platforms:` array; absent = all OSes.
8120
9088
  platforms: frontmatter.platforms,
8121
9089
  instructions: bodyText,
8122
9090
  toolchain: frontmatter.toolchain,
8123
- triggers: frontmatter.triggers,
9091
+ triggers,
8124
9092
  contextTemplate: frontmatter.contextTemplate
8125
9093
  };
8126
9094
  const parsed = CustomSkillInputSchema.safeParse(raw);
8127
9095
  if (!parsed.success) {
8128
9096
  const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
8129
- log26.warn("skill-loader:validation_failed", { issues });
9097
+ log29.debug("skill-loader:validation_failed", { issues });
8130
9098
  return { ok: false, error: `Validation failed: ${issues}` };
8131
9099
  }
8132
9100
  return { ok: true, skill: parsed.data };
@@ -8169,13 +9137,13 @@ function walk(dir, acc) {
8169
9137
  else acc.errors.push({ file: full, error: parsed.error ?? "unknown parse error" });
8170
9138
  }
8171
9139
  }
8172
- var log26;
9140
+ var log29;
8173
9141
  var init_skill_loader = __esm({
8174
9142
  "src/core/skills/skill-loader.ts"() {
8175
9143
  init_esm_shims();
8176
9144
  init_skill_schema();
8177
9145
  init_logger();
8178
- log26 = createLogger({ layer: "core", source: "skill-loader.ts" });
9146
+ log29 = createLogger({ layer: "core", source: "skill-loader.ts" });
8179
9147
  }
8180
9148
  });
8181
9149
  function summarize(skill) {
@@ -8208,7 +9176,11 @@ function invokeSkill(dir, name) {
8208
9176
  };
8209
9177
  }
8210
9178
  function defaultSkillRoots(projectRoot = process.cwd()) {
8211
- return [join(projectRoot, "src/skills"), join(projectRoot, ".claude/skills")];
9179
+ return [
9180
+ join(projectRoot, "src/skills"),
9181
+ join(projectRoot, ".agents/skills"),
9182
+ join(projectRoot, ".claude/skills")
9183
+ ];
8212
9184
  }
8213
9185
  var init_skill_registry = __esm({
8214
9186
  "src/core/skills/skill-registry.ts"() {
@@ -8264,7 +9236,7 @@ function mergeGraph(store2, incoming, options) {
8264
9236
  }
8265
9237
  const sourceProject = incoming.project.name;
8266
9238
  const dryRun = options?.dryRun ?? false;
8267
- log27.info("merge-graph:start", {
9239
+ log30.info("merge-graph:start", {
8268
9240
  sourceProject,
8269
9241
  incomingNodes: incoming.nodes.length,
8270
9242
  incomingEdges: incoming.edges.length,
@@ -8316,7 +9288,7 @@ function mergeGraph(store2, incoming, options) {
8316
9288
  edgesInserted2++;
8317
9289
  }
8318
9290
  }
8319
- log27.info("merge-graph:dry-run", {
9291
+ log30.info("merge-graph:dry-run", {
8320
9292
  nodesInserted: nodesToInsert.length,
8321
9293
  nodesSkipped,
8322
9294
  edgesInserted: edgesInserted2,
@@ -8336,7 +9308,7 @@ function mergeGraph(store2, incoming, options) {
8336
9308
  const { nodesInserted, edgesInserted } = store2.mergeInsert(nodesToInsert, validEdges);
8337
9309
  const edgesSkipped = validEdges.length - edgesInserted;
8338
9310
  store2.recordImport(`merge:${sourceProject}`, nodesInserted, edgesInserted);
8339
- log27.info("merge-graph:done", {
9311
+ log30.info("merge-graph:done", {
8340
9312
  sourceProject,
8341
9313
  nodesInserted,
8342
9314
  nodesSkipped,
@@ -8353,14 +9325,14 @@ function mergeGraph(store2, incoming, options) {
8353
9325
  sourceProject
8354
9326
  };
8355
9327
  }
8356
- var log27;
9328
+ var log30;
8357
9329
  var init_import_graph = __esm({
8358
9330
  "src/core/importer/import-graph.ts"() {
8359
9331
  init_esm_shims();
8360
9332
  init_graph_schema();
8361
9333
  init_errors();
8362
9334
  init_logger();
8363
- log27 = createLogger({ layer: "core", source: "import-graph.ts" });
9335
+ log30 = createLogger({ layer: "core", source: "import-graph.ts" });
8364
9336
  }
8365
9337
  });
8366
9338
 
@@ -8471,7 +9443,7 @@ function computeFileSha256(path22) {
8471
9443
  return hash.digest("hex");
8472
9444
  }
8473
9445
  async function downloadFileWithVerify(url, destPath, expectedSha256) {
8474
- log28.info("model-downloader:start", { url, dest: destPath, hasExpectedHash: false });
9446
+ log31.info("model-downloader:start", { url, dest: destPath, hasExpectedHash: false });
8475
9447
  const controller = new AbortController();
8476
9448
  const timeoutId = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
8477
9449
  let response;
@@ -8493,15 +9465,15 @@ async function downloadFileWithVerify(url, destPath, expectedSha256) {
8493
9465
  const actualSha = computeFileSha256(destPath);
8494
9466
  const sizeBytes = statSync(destPath).size;
8495
9467
  const verified = expectedSha256 != null && actualSha === expectedSha256;
8496
- log28.info("model-downloader:ok", { dest: destPath, sizeBytes, sha256: actualSha, verified });
9468
+ log31.info("model-downloader:ok", { dest: destPath, sizeBytes, sha256: actualSha, verified });
8497
9469
  return { sha256: actualSha, verified, sizeBytes };
8498
9470
  }
8499
- var log28, DOWNLOAD_TIMEOUT_MS, ChecksumMismatchError, DownloadError;
9471
+ var log31, DOWNLOAD_TIMEOUT_MS, ChecksumMismatchError, DownloadError;
8500
9472
  var init_model_downloader = __esm({
8501
9473
  "src/core/rag/model-downloader.ts"() {
8502
9474
  init_esm_shims();
8503
9475
  init_logger();
8504
- log28 = createLogger({ layer: "core", source: "model-downloader.ts" });
9476
+ log31 = createLogger({ layer: "core", source: "model-downloader.ts" });
8505
9477
  DOWNLOAD_TIMEOUT_MS = 9e4;
8506
9478
  ChecksumMismatchError = class extends Error {
8507
9479
  constructor(url, expected, actual) {
@@ -8570,15 +9542,15 @@ async function isOnnxAvailable() {
8570
9542
  onnxAvailableCache = true;
8571
9543
  } catch {
8572
9544
  onnxAvailableCache = false;
8573
- log29.warn("onnx:unavailable", { reason: "onnxruntime-node not installed \u2014 RAG will use hash embeddings (degraded mode)" });
9545
+ log32.warn("onnx:unavailable", { reason: "onnxruntime-node not installed \u2014 RAG will use hash embeddings (degraded mode)" });
8574
9546
  }
8575
9547
  return onnxAvailableCache;
8576
9548
  }
8577
9549
  async function logEmbeddingModeOnBoot(isAvailable = isOnnxAvailable, logFn = (event, fields) => {
8578
9550
  if (fields.mode === "neural") {
8579
- log29.info(event, fields);
9551
+ log32.info(event, fields);
8580
9552
  } else {
8581
- log29.warn(event, fields);
9553
+ log32.warn(event, fields);
8582
9554
  }
8583
9555
  }) {
8584
9556
  try {
@@ -8601,7 +9573,7 @@ function ensureOnnxModelDir(modelsDir) {
8601
9573
  async function downloadFile(url, destPath) {
8602
9574
  try {
8603
9575
  const resultValue = await downloadFileWithVerify(url, destPath);
8604
- log29.info("onnx:download:ok", {
9576
+ log32.info("onnx:download:ok", {
8605
9577
  dest: destPath,
8606
9578
  sizeBytes: resultValue.sizeBytes,
8607
9579
  sha256: resultValue.sha256,
@@ -8625,7 +9597,7 @@ async function ensureModelFiles(modelsDir) {
8625
9597
  if (existsSync(modelPath)) {
8626
9598
  const size = statSync(modelPath).size;
8627
9599
  if (size < MIN_MODEL_SIZE) {
8628
- log29.warn("onnx:corrupted-model", { modelPath, sizeBytes: size, minRequired: MIN_MODEL_SIZE });
9600
+ log32.warn("onnx:corrupted-model", { modelPath, sizeBytes: size, minRequired: MIN_MODEL_SIZE });
8629
9601
  unlinkSync(modelPath);
8630
9602
  }
8631
9603
  }
@@ -8634,12 +9606,12 @@ async function ensureModelFiles(modelsDir) {
8634
9606
  const raw = readFileSync(tokenizerPath, "utf-8");
8635
9607
  JSON.parse(raw);
8636
9608
  } catch {
8637
- log29.warn("onnx:corrupted-tokenizer", { tokenizerPath });
9609
+ log32.warn("onnx:corrupted-tokenizer", { tokenizerPath });
8638
9610
  unlinkSync(tokenizerPath);
8639
9611
  }
8640
9612
  }
8641
9613
  if (existsSync(modelPath) && existsSync(tokenizerPath)) {
8642
- log29.debug("onnx:cache-hit", { modelDir });
9614
+ log32.debug("onnx:cache-hit", { modelDir });
8643
9615
  return { modelPath, tokenizerPath };
8644
9616
  }
8645
9617
  mkdirSync(modelDir, { recursive: true });
@@ -8656,7 +9628,7 @@ function loadTokenizer(tokenizerPath) {
8656
9628
  const raw = readFileSync(tokenizerPath, "utf-8");
8657
9629
  return JSON.parse(raw);
8658
9630
  } catch (err) {
8659
- log29.warn("onnx:tokenizer-load-failed", { tokenizerPath, error: err instanceof Error ? err.message : String(err) });
9631
+ log32.warn("onnx:tokenizer-load-failed", { tokenizerPath, error: err instanceof Error ? err.message : String(err) });
8660
9632
  return null;
8661
9633
  }
8662
9634
  }
@@ -8708,15 +9680,15 @@ function startOnnxBackgroundDownload(modelsDir, options = {}) {
8708
9680
  modelPath = join(modelDir, MODEL_FILENAME);
8709
9681
  tokenizerPath = join(modelDir, TOKENIZER_FILENAME);
8710
9682
  const cacheEvent = "onnx:background-ready-from-cache";
8711
- log29.info(cacheEvent, { modelsDir });
9683
+ log32.info(cacheEvent, { modelsDir });
8712
9684
  onLog?.(cacheEvent);
8713
9685
  } else {
8714
9686
  const startEvent = "onnx:background-download-start";
8715
- log29.info(startEvent, { message: "Downloading MiniLM-L6-v2 (23MB)...", modelsDir });
9687
+ log32.info(startEvent, { message: "Downloading MiniLM-L6-v2 (23MB)...", modelsDir });
8716
9688
  onLog?.(startEvent);
8717
9689
  ({ modelPath, tokenizerPath } = await ensureFiles(modelsDir));
8718
9690
  const doneEvent = "onnx:background-download-complete";
8719
- log29.info(doneEvent, { modelsDir });
9691
+ log32.info(doneEvent, { modelsDir });
8720
9692
  onLog?.(doneEvent);
8721
9693
  }
8722
9694
  const provider = makeProvider(modelPath, tokenizerPath);
@@ -8724,7 +9696,7 @@ function startOnnxBackgroundDownload(modelsDir, options = {}) {
8724
9696
  onReady?.(provider);
8725
9697
  } catch (err) {
8726
9698
  const msg = err instanceof Error ? err.message : String(err);
8727
- log29.warn("onnx:background-download-failed", { error: msg, action: "fallback-remains-tfidf" });
9699
+ log32.warn("onnx:background-download-failed", { error: msg, action: "fallback-remains-tfidf" });
8728
9700
  onWarning?.(msg);
8729
9701
  }
8730
9702
  };
@@ -8738,7 +9710,7 @@ function _resetBackgroundDownloadState() {
8738
9710
  async function getOnnxProvider(modelsDir) {
8739
9711
  const available = await isOnnxAvailable();
8740
9712
  if (!available) {
8741
- log29.warn("onnx:provider-degraded", { available: false, impact: "RAG operates with hash embeddings instead of neural \u2014 lower search quality" });
9713
+ log32.warn("onnx:provider-degraded", { available: false, impact: "RAG operates with hash embeddings instead of neural \u2014 lower search quality" });
8742
9714
  return null;
8743
9715
  }
8744
9716
  const existing = providerCache.get(modelsDir);
@@ -8758,8 +9730,8 @@ async function getOnnxProvider(modelsDir) {
8758
9730
  return await creation;
8759
9731
  } catch (err) {
8760
9732
  providerCache.delete(modelsDir);
8761
- log29.error("onnx:provider-init-failed", { error: err instanceof Error ? err.message : String(err) });
8762
- log29.warn("onnx:fallback", {
9733
+ log32.error("onnx:provider-init-failed", { error: err instanceof Error ? err.message : String(err) });
9734
+ log32.warn("onnx:fallback", {
8763
9735
  reason: err instanceof Error ? err.message : String(err),
8764
9736
  action: "return-null-provider",
8765
9737
  modelsDir
@@ -8767,7 +9739,7 @@ async function getOnnxProvider(modelsDir) {
8767
9739
  return null;
8768
9740
  }
8769
9741
  }
8770
- 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;
9742
+ 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;
8771
9743
  var init_onnx_embeddings = __esm({
8772
9744
  "src/core/rag/onnx-embeddings.ts"() {
8773
9745
  init_esm_shims();
@@ -8775,7 +9747,7 @@ var init_onnx_embeddings = __esm({
8775
9747
  init_errors();
8776
9748
  init_tensor_buffer_pool();
8777
9749
  init_model_downloader();
8778
- log29 = createLogger({ layer: "rag", source: "onnx-embeddings.ts" });
9750
+ log32 = createLogger({ layer: "rag", source: "onnx-embeddings.ts" });
8779
9751
  MODEL_NAME = "all-MiniLM-L6-v2-quantized";
8780
9752
  MODEL_FILENAME = "model.onnx";
8781
9753
  TOKENIZER_FILENAME = "tokenizer.json";
@@ -8807,7 +9779,7 @@ var init_onnx_embeddings = __esm({
8807
9779
  throw new OnnxModelNotFoundError(`Failed to load tokenizer: ${this.tokenizerPath}`);
8808
9780
  }
8809
9781
  this.vocab = config.model?.vocab ?? {};
8810
- log29.info("onnx:session-created", { model: this.modelPath });
9782
+ log32.info("onnx:session-created", { model: this.modelPath });
8811
9783
  return this.session;
8812
9784
  }
8813
9785
  async generateEmbedding(text) {
@@ -8936,7 +9908,7 @@ async function checkSqliteDatabase(basePath) {
8936
9908
  };
8937
9909
  }
8938
9910
  } catch (err) {
8939
- log30.debug("intentional-swallow", { error: String(err), reason: "statSync race with deletion; fall through to open attempt" });
9911
+ log33.debug("intentional-swallow", { error: String(err), reason: "statSync race with deletion; fall through to open attempt" });
8940
9912
  }
8941
9913
  try {
8942
9914
  const db = new Database2(dbPath, { readonly: true });
@@ -8986,7 +9958,7 @@ async function checkDbIntegrity2(basePath) {
8986
9958
  };
8987
9959
  }
8988
9960
  } catch (err) {
8989
- log30.debug("intentional-swallow", { error: String(err), reason: "fall through to open attempt" });
9961
+ log33.debug("intentional-swallow", { error: String(err), reason: "fall through to open attempt" });
8990
9962
  }
8991
9963
  try {
8992
9964
  const db = new Database2(dbPath, { readonly: true });
@@ -8994,7 +9966,7 @@ async function checkDbIntegrity2(basePath) {
8994
9966
  try {
8995
9967
  schemaCount = db.prepare("SELECT count(*) as n FROM sqlite_master WHERE type IN ('table','view')").get().n;
8996
9968
  } catch (err) {
8997
- log30.debug("intentional-swallow", { error: String(err), reason: "reading sqlite_master itself failed \u2014 DB is unreadable, fall through" });
9969
+ log33.debug("intentional-swallow", { error: String(err), reason: "reading sqlite_master itself failed \u2014 DB is unreadable, fall through" });
8998
9970
  }
8999
9971
  if (schemaCount === 0) {
9000
9972
  db.close();
@@ -9154,7 +10126,7 @@ async function checkIntegrations(basePath) {
9154
10126
  });
9155
10127
  return results;
9156
10128
  } catch (err) {
9157
- log30.debug("doctor:integrations:fail", {
10129
+ log33.debug("doctor:integrations:fail", {
9158
10130
  error: err instanceof Error ? err.message : String(err)
9159
10131
  });
9160
10132
  return [
@@ -9205,7 +10177,7 @@ async function checkOnnxStatus() {
9205
10177
  const { isOnnxAvailable: isOnnxAvailable2 } = await Promise.resolve().then(() => (init_onnx_embeddings(), onnx_embeddings_exports));
9206
10178
  return checkOnnxStatusWith(isOnnxAvailable2);
9207
10179
  }
9208
- var log30, MIN_NODE_VERSION;
10180
+ var log33, MIN_NODE_VERSION;
9209
10181
  var init_doctor_checks = __esm({
9210
10182
  "src/core/doctor/doctor-checks.ts"() {
9211
10183
  init_esm_shims();
@@ -9213,7 +10185,7 @@ var init_doctor_checks = __esm({
9213
10185
  init_fs();
9214
10186
  init_tool_status();
9215
10187
  init_logger();
9216
- log30 = createLogger({ layer: "core", source: "doctor-checks.ts" });
10188
+ log33 = createLogger({ layer: "core", source: "doctor-checks.ts" });
9217
10189
  MIN_NODE_VERSION = 20;
9218
10190
  }
9219
10191
  });
@@ -9238,7 +10210,7 @@ async function runDoctor(basePath) {
9238
10210
  if (!basePath) {
9239
10211
  throw new McpGraphError("Doctor requires a valid base path");
9240
10212
  }
9241
- log31.info("Running doctor checks", { basePath });
10213
+ log34.info("Running doctor checks", { basePath });
9242
10214
  const checks = [];
9243
10215
  checks.push(checkNodeVersion());
9244
10216
  checks.push(checkConfigFile(basePath));
@@ -9276,7 +10248,7 @@ async function runDoctor(basePath) {
9276
10248
  }
9277
10249
  }
9278
10250
  for (const result of checks) {
9279
- log31.event(
10251
+ log34.event(
9280
10252
  { action: "health.check", category: "health", outcome: result.level === "ok" ? "success" : "failure" },
9281
10253
  `health.check.${result.name}`,
9282
10254
  { check: result.name }
@@ -9289,7 +10261,7 @@ async function runDoctor(basePath) {
9289
10261
  passed: summary.error === 0
9290
10262
  };
9291
10263
  }
9292
- var log31;
10264
+ var log34;
9293
10265
  var init_doctor_runner = __esm({
9294
10266
  "src/core/doctor/doctor-runner.ts"() {
9295
10267
  init_esm_shims();
@@ -9298,7 +10270,7 @@ var init_doctor_runner = __esm({
9298
10270
  init_errors();
9299
10271
  init_logger();
9300
10272
  init_doctor_checks();
9301
- log31 = createLogger({ layer: "core", source: "doctor-runner.ts" });
10273
+ log34 = createLogger({ layer: "core", source: "doctor-runner.ts" });
9302
10274
  }
9303
10275
  });
9304
10276
 
@@ -9429,8 +10401,8 @@ async function launchTui(store2) {
9429
10401
  const skillCommands = defaultSkillRoots(dir).flatMap((r) => {
9430
10402
  const { skills } = listSkills(r);
9431
10403
  return skills.map((s) => ({
9432
- name: `skill:${s.name}`,
9433
- usage: `/skill ${s.name}`,
10404
+ name: s.name,
10405
+ usage: `/${s.name}`,
9434
10406
  desc: `[${s.category}] ${s.description}`
9435
10407
  }));
9436
10408
  });
@@ -10045,16 +11017,36 @@ init_token_ledger();
10045
11017
  init_llm_call_ledger();
10046
11018
  init_live_implement();
10047
11019
  init_store_port();
11020
+
11021
+ // src/cli/shared/enable-flow.ts
11022
+ init_esm_shims();
11023
+ init_flow_config();
11024
+ function enableFlowConfig(store2) {
11025
+ const raw = store2.getProjectSetting(FLOW_CONFIG_SETTING_KEY);
11026
+ let current = {};
11027
+ if (raw) {
11028
+ try {
11029
+ current = JSON.parse(raw);
11030
+ } catch {
11031
+ current = {};
11032
+ }
11033
+ }
11034
+ store2.setProjectSetting(FLOW_CONFIG_SETTING_KEY, JSON.stringify({ ...current, enabled: true }));
11035
+ }
10048
11036
  function output7(msg) {
10049
11037
  process.stdout.write(msg + "\n");
10050
11038
  }
10051
11039
  function autopilotCommand() {
10052
- 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(
11040
+ return new Command("autopilot").description("Loop aut\xF4nomo com guardrails: next \u2192 in_progress \u2192 DoD \u2192 done|escalate (WIP=1)").option("-d, --dir <dir>", "Diret\xF3rio do projeto", process.cwd()).option("-m, --max <n>", "Budget: m\xE1ximo de tasks por sess\xE3o (cost-runaway guard)", "5").option("--simulate", "Simula impl bem-sucedida (deixa o DoD real decidir) \u2014 n\xE3o escreve c\xF3digo", false).option("--live", "Invoca o modelo real via SDK do Copilot: gera plano \u2192 aplica \u2192 roda testes \u2192 done|escala", false).option("--test-cmd <cmd>", "Comando de teste rodado no modo --live quando o plano n\xE3o traz um", "npm test").option("--retries <n>", "Tentativas por task no --live (retry com feedback compacto do teste)", "2").option("--flow", "Ativa a dilui\xE7\xE3o de contexto por \u03BB_flow (hipofrontalidade) no --live", false).action(
10053
11041
  async (opts) => {
10054
11042
  const store2 = openStoreOrFail(opts.dir, { requireExisting: true });
10055
11043
  try {
10056
11044
  const maxIterations = Math.max(1, parseInt(opts.max, 10) || 5);
10057
11045
  const port = makeStorePort(store2);
11046
+ if (opts.flow) {
11047
+ enableFlowConfig(store2);
11048
+ output7("[FLOW] \u03BB_flow ativo: contexto do grafo dilu\xEDdo por \u03A6(t) (esquecimento din\xE2mico).\n");
11049
+ }
10058
11050
  if (opts.simulate) output7("[SIMULA\xC7\xC3O] impl tratada como verde \u2014 DoD real decide prontid\xE3o.\n");
10059
11051
  if (opts.live) output7("[LIVE] modelo via SDK do Copilot: gera plano \u2192 aplica no workspace \u2192 roda testes.\n");
10060
11052
  let implement;
@@ -10259,52 +11251,8 @@ function runCommand() {
10259
11251
 
10260
11252
  // src/cli/commands/tui-cmd.ts
10261
11253
  init_esm_shims();
10262
-
10263
- // src/cli/banner.ts
10264
- init_esm_shims();
10265
- var GREEN = "\x1B[32m";
10266
- var BOLD = "\x1B[1m";
10267
- var DIM = "\x1B[2m";
10268
- var RESET = "\x1B[0m";
10269
- var CLEAR_LINE = "\r\x1B[K";
10270
- var BUG_FRAMES = [
10271
- " /\\(\u2022\u203F\u2022)/\\ ",
10272
- " /\\(\u2022\u203F\u2022)/\\ ",
10273
- " \\(\u2022\u203F\u2022)/ ",
10274
- " \\(\u2022\u203F\u2022)/ ",
10275
- " /\\(\u2022\u203F\u2022)/\\ "
10276
- ];
10277
- var TRAIL = "\xB7";
10278
- var TAGLINE = "mcp-graph-agent \u2014 Software Engineer as a Service";
10279
- function sleep(ms) {
10280
- return new Promise((r) => setTimeout(r, ms));
10281
- }
10282
- async function showBanner() {
10283
- if (!process.stdout.isTTY) return;
10284
- const cols = process.stdout.columns ?? 80;
10285
- const bugWidth = BUG_FRAMES[0].length;
10286
- const steps = Math.max(cols - bugWidth - 2, 10);
10287
- let trail = "";
10288
- for (let i = 0; i <= steps; i++) {
10289
- const frame = BUG_FRAMES[i % BUG_FRAMES.length];
10290
- const pad = " ".repeat(i);
10291
- process.stdout.write(`${CLEAR_LINE}${GREEN}${pad}${trail}${frame}${RESET}`);
10292
- trail += TRAIL;
10293
- if (trail.length > i) trail = trail.slice(-i || 0);
10294
- await sleep(28);
10295
- }
10296
- process.stdout.write(`
10297
- ${CLEAR_LINE}${BOLD}${GREEN}${TAGLINE}${RESET}
10298
- `);
10299
- process.stdout.write(`${DIM} type /help to see all commands${RESET}
10300
-
10301
- `);
10302
- }
10303
-
10304
- // src/cli/commands/tui-cmd.ts
10305
11254
  function tuiCommand() {
10306
11255
  return new Command("tui").description("Abre a TUI interativa (dashboard do grafo + tokens)").option("-d, --dir <dir>", "Diret\xF3rio do projeto", process.cwd()).action(async (opts) => {
10307
- await showBanner();
10308
11256
  const store2 = openStoreOrFail(opts.dir);
10309
11257
  try {
10310
11258
  const { launchTui: launchTui2 } = await Promise.resolve().then(() => (init_launch(), launch_exports));
@@ -10398,7 +11346,7 @@ function whichCommand() {
10398
11346
  }
10399
11347
 
10400
11348
  // src/core/lsp/lsp-deps-installer.ts
10401
- var log33 = createLogger({ layer: "core", source: "lsp-deps-installer.ts" });
11349
+ var log36 = createLogger({ layer: "core", source: "lsp-deps-installer.ts" });
10402
11350
  var execAsync = promisify(execFile);
10403
11351
  var LSP_NPM_PACKAGES = {
10404
11352
  typescript: "typescript-language-server",
@@ -10449,7 +11397,7 @@ var LSP_SYSTEM_PACKAGES = {
10449
11397
  async function checkLspDep(languageId, command) {
10450
11398
  try {
10451
11399
  await execAsync(whichCommand(), [command]);
10452
- log33.info("LSP server available", { languageId, command });
11400
+ log36.info("LSP server available", { languageId, command });
10453
11401
  return {
10454
11402
  name: command,
10455
11403
  languageId,
@@ -10495,7 +11443,7 @@ function getServerCommand(languageId) {
10495
11443
  }
10496
11444
  async function installLspDeps(detectedLanguages) {
10497
11445
  if (detectedLanguages.length === 0) return [];
10498
- log33.info("Checking LSP server dependencies", {
11446
+ log36.info("Checking LSP server dependencies", {
10499
11447
  languages: detectedLanguages.join(", ")
10500
11448
  });
10501
11449
  const results = [];
@@ -10507,7 +11455,7 @@ async function installLspDeps(detectedLanguages) {
10507
11455
  }
10508
11456
  const available = results.filter((r) => r.status === "already_available").length;
10509
11457
  const missing = results.filter((r) => r.status === "not_found").length;
10510
- log33.info("LSP dependency check complete", {
11458
+ log36.info("LSP dependency check complete", {
10511
11459
  total: String(results.length),
10512
11460
  available: String(available),
10513
11461
  missing: String(missing)
@@ -10518,7 +11466,7 @@ async function installLspDeps(detectedLanguages) {
10518
11466
  // src/core/lsp/language-detector.ts
10519
11467
  init_esm_shims();
10520
11468
  init_logger();
10521
- var log34 = createLogger({ layer: "core", source: "language-detector.ts" });
11469
+ var log37 = createLogger({ layer: "core", source: "language-detector.ts" });
10522
11470
  var CONFIG_FILE_MAP = {
10523
11471
  "tsconfig.json": "typescript",
10524
11472
  "jsconfig.json": "typescript",
@@ -10558,7 +11506,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
10558
11506
  "__pycache__"
10559
11507
  ]);
10560
11508
  function detectProjectLanguages(projectPath, registry) {
10561
- log34.debug("detecting project languages", { projectPath });
11509
+ log37.debug("detecting project languages", { projectPath });
10562
11510
  const configDetections = /* @__PURE__ */ new Map();
10563
11511
  const fileCounts = /* @__PURE__ */ new Map();
10564
11512
  detectConfigFiles(projectPath, configDetections);
@@ -10586,7 +11534,7 @@ function detectProjectLanguages(projectPath, registry) {
10586
11534
  });
10587
11535
  }
10588
11536
  results.sort((a, b) => b.fileCount - a.fileCount);
10589
- log34.info("project languages detected", {
11537
+ log37.info("project languages detected", {
10590
11538
  count: String(results.length),
10591
11539
  languages: results.map((r) => r.languageId).join(",")
10592
11540
  });
@@ -10597,7 +11545,7 @@ function detectConfigFiles(rootPath, configDetections) {
10597
11545
  try {
10598
11546
  entries = readdirSync(rootPath, { withFileTypes: true, encoding: "utf-8" });
10599
11547
  } catch {
10600
- log34.debug("cannot read root directory for config detection", { rootPath });
11548
+ log37.debug("cannot read root directory for config detection", { rootPath });
10601
11549
  return;
10602
11550
  }
10603
11551
  for (const entry of entries) {
@@ -10622,7 +11570,7 @@ function walkAndCountFiles(dirPath, registry, fileCounts) {
10622
11570
  try {
10623
11571
  entries = readdirSync(dirPath, { withFileTypes: true, encoding: "utf-8" });
10624
11572
  } catch {
10625
- log34.debug("cannot read directory, skipping", { dirPath });
11573
+ log37.debug("cannot read directory, skipping", { dirPath });
10626
11574
  return;
10627
11575
  }
10628
11576
  for (const entry of entries) {
@@ -11707,176 +12655,10 @@ function applySection(existingContent, newSection) {
11707
12655
 
11708
12656
  // src/core/config/config-loader.ts
11709
12657
  init_esm_shims();
11710
-
11711
- // src/core/config/config-schema.ts
11712
- init_esm_shims();
11713
-
11714
- // src/core/lsp/lsp-types.ts
11715
- init_esm_shims();
11716
- z.object({
11717
- languageId: z.string(),
11718
- extensions: z.array(z.string()),
11719
- command: z.string(),
11720
- args: z.array(z.string()),
11721
- configFiles: z.array(z.string()),
11722
- probeCommand: z.string().optional(),
11723
- initializationOptions: z.record(z.string(), z.unknown()).optional(),
11724
- settings: z.record(z.string(), z.unknown()).optional()
11725
- });
11726
- var LspConfigOverrideSchema = z.object({
11727
- languageId: z.string(),
11728
- command: z.string(),
11729
- args: z.array(z.string()).default([]),
11730
- extensions: z.array(z.string()).optional(),
11731
- initializationOptions: z.record(z.string(), z.unknown()).optional(),
11732
- settings: z.record(z.string(), z.unknown()).optional()
11733
- });
11734
- z.object({
11735
- file: z.string(),
11736
- startLine: z.int().min(0),
11737
- startCharacter: z.int().min(0),
11738
- endLine: z.int().min(0),
11739
- endCharacter: z.int().min(0)
11740
- });
11741
- z.object({
11742
- signature: z.string(),
11743
- documentation: z.string().optional(),
11744
- language: z.string().optional()
11745
- });
11746
- var LspDiagnosticSchema = z.object({
11747
- file: z.string(),
11748
- startLine: z.int().min(0),
11749
- startCharacter: z.int().min(0),
11750
- endLine: z.int().min(0),
11751
- endCharacter: z.int().min(0),
11752
- severity: z.number().int().min(1).max(4),
11753
- message: z.string(),
11754
- code: z.string().optional(),
11755
- source: z.string().optional()
11756
- });
11757
- z.object({
11758
- name: z.string(),
11759
- kind: z.string(),
11760
- file: z.string(),
11761
- startLine: z.int().min(0),
11762
- endLine: z.int().min(0)
11763
- });
11764
- var LspDocumentSymbolSchema = z.object({
11765
- name: z.string(),
11766
- kind: z.string(),
11767
- file: z.string(),
11768
- startLine: z.int().min(0),
11769
- endLine: z.int().min(0),
11770
- children: z.lazy(() => z.array(LspDocumentSymbolSchema)).optional()
11771
- });
11772
- var LspTextEditSchema = z.object({
11773
- file: z.string(),
11774
- startLine: z.int().min(0),
11775
- startCharacter: z.int().min(0),
11776
- endLine: z.int().min(0),
11777
- endCharacter: z.int().min(0),
11778
- newText: z.string()
11779
- });
11780
- var LspWorkspaceEditSchema = z.object({
11781
- changes: z.array(LspTextEditSchema)
11782
- });
11783
- z.object({
11784
- languageId: z.string(),
11785
- status: z.enum(["stopped", "starting", "ready", "error"]),
11786
- pid: z.number().int().optional(),
11787
- error: z.string().optional()
11788
- });
11789
- z.object({
11790
- languageId: z.string(),
11791
- confidence: z.number().min(0).max(1),
11792
- detectedVia: z.enum(["file_extension", "config_file", "shebang"]),
11793
- fileCount: z.int().min(0),
11794
- configFile: z.string().optional()
11795
- });
11796
- z.object({
11797
- title: z.string(),
11798
- kind: z.string().optional(),
11799
- isPreferred: z.boolean().optional(),
11800
- edit: LspWorkspaceEditSchema.optional(),
11801
- diagnostics: z.array(LspDiagnosticSchema).optional()
11802
- });
11803
- z.object({
11804
- applied: z.boolean(),
11805
- filesModified: z.array(z.string()),
11806
- totalEdits: z.number().int(),
11807
- errors: z.array(z.string()),
11808
- backups: z.map(z.string(), z.string()).optional()
11809
- });
11810
-
11811
- // src/core/config/config-schema.ts
11812
- var BROWSER_PILOT_MODELS = ["claude-3.5-sonnet", "gpt-4o", "gpt-4o-mini", "o1", "o1-mini"];
11813
- var ContextModeSchema = z.enum(["ultra-lean", "lean", "full"]);
11814
- var ProfileFilterConfigSchema = z.enum(["core", "pro", "expert", "all"]);
11815
- var BrowserAutomationConfigSchema = z.object({
11816
- enabled: z.boolean().default(false),
11817
- bridgeUrl: z.string().regex(/^https?:\/\//, "bridgeUrl must start with http:// or https://").default("http://127.0.0.1:9876/v1"),
11818
- defaultModel: z.enum(BROWSER_PILOT_MODELS).default("claude-3.5-sonnet"),
11819
- defaultCdpUrl: z.string().min(1).optional(),
11820
- allowedDomains: z.array(z.string().min(1)).default([]),
11821
- forbiddenCdpMethods: z.array(z.string().min(1)).default(["Browser.close"]),
11822
- maxStepsDefault: z.number().int().min(1).max(100).default(25),
11823
- tokenBudgetPerDay: z.number().int().nonnegative().optional()
11824
- }).default({
11825
- enabled: false,
11826
- bridgeUrl: "http://127.0.0.1:9876/v1",
11827
- defaultModel: "claude-3.5-sonnet",
11828
- allowedDomains: [],
11829
- forbiddenCdpMethods: ["Browser.close"],
11830
- maxStepsDefault: 25
11831
- });
11832
- var FlowConfigSchema = z.object({
11833
- /** Master switch. OFF = byte-identical legacy context behaviour. */
11834
- enabled: z.boolean().default(false),
11835
- /** λ_base — minimum architectural forgetting rate. */
11836
- lambdaBase: z.number().min(0).default(0.15),
11837
- /** α — hypofrontality accelerator (weight of Φ on λ_flow). */
11838
- alpha: z.number().min(0).default(1.5),
11839
- /** BFS depth used to pull distant pinned invariants back into scope. */
11840
- maxDepth: z.number().int().min(0).max(6).default(3),
11841
- /** Peripheral neighbours below this decayed weight are pruned (unless pinned). */
11842
- weightThreshold: z.number().min(0).max(1).default(0.1),
11843
- /** EMA gain per consecutive success when computing Φ. */
11844
- emaGain: z.number().min(0).max(1).default(0.34),
11845
- /** Multiplier applied to Φ on a failure (0 = hard reset → re-hydrate memory). */
11846
- resetFactor: z.number().min(0).max(1).default(0),
11847
- /** Damping fraction of `emaGain` applied on a `partial` outcome. */
11848
- partialFactor: z.number().min(0).max(1).default(0.5),
11849
- /** rag budget is never scaled below this fraction of baseline (long-range safety). */
11850
- budgetFloorRatio: z.number().min(0).max(1).default(0.25),
11851
- /** How many recent task outcomes feed Φ. */
11852
- historyWindow: z.number().int().min(1).max(200).default(12),
11853
- /** Node types that are never diluted. */
11854
- pinnedTypes: z.array(z.string()).default(["constraint", "risk", "decision", "acceptance_criteria", "constitution", "requirement"]),
11855
- /** A/B experiment: alternate flow_on/flow_off deterministically per node to measure impact. */
11856
- experiment: z.object({ abEnabled: z.boolean().default(false) }).default({ abEnabled: false })
11857
- });
11858
- var ConfigSchema = z.object({
11859
- port: z.number().int().min(1).max(65535).default(3e3),
11860
- dbPath: z.string().default("workflow-graph"),
11861
- basePath: z.string().optional(),
11862
- contextMode: ContextModeSchema.default("lean"),
11863
- profile: ProfileFilterConfigSchema.default("all"),
11864
- dashboard: z.object({
11865
- autoOpen: z.boolean().default(true)
11866
- }).default({ autoOpen: true }),
11867
- integrations: z.object({
11868
- codeGraphAutoIndex: z.boolean().default(true),
11869
- codeGraphReindexIntervalSec: z.number().int().min(0).default(0),
11870
- lspServers: z.array(LspConfigOverrideSchema).default([]),
11871
- browserAutomation: BrowserAutomationConfigSchema
11872
- }).prefault({}),
11873
- flow: FlowConfigSchema.prefault({})
11874
- });
11875
-
11876
- // src/core/config/config-loader.ts
12658
+ init_config_schema();
11877
12659
  init_errors();
11878
12660
  init_logger();
11879
- var log35 = createLogger({ layer: "core", source: "config-loader.ts" });
12661
+ var log38 = createLogger({ layer: "core", source: "config-loader.ts" });
11880
12662
  var CONFIG_FILENAME = "mcp-graph.config.json";
11881
12663
  function loadConfig(basePath) {
11882
12664
  const resolvedBase = basePath ?? process.cwd();
@@ -11886,13 +12668,13 @@ function loadConfig(basePath) {
11886
12668
  try {
11887
12669
  const raw = readFileSync(configPath, "utf-8").replace(/^\uFEFF/, "");
11888
12670
  fileConfig = JSON.parse(raw);
11889
- log35.info(`Config loaded from ${configPath}`);
12671
+ log38.info(`Config loaded from ${configPath}`);
11890
12672
  } catch (err) {
11891
12673
  const msg = err instanceof Error ? err.message : String(err);
11892
12674
  throw new McpGraphError(`Invalid config at ${configPath}: ${msg}`);
11893
12675
  }
11894
12676
  } else {
11895
- log35.info("No config file found, using defaults");
12677
+ log38.info("No config file found, using defaults");
11896
12678
  }
11897
12679
  if (process.env.MCP_PORT) {
11898
12680
  const envPort = parseInt(process.env.MCP_PORT, 10);
@@ -11912,7 +12694,7 @@ function loadConfig(basePath) {
11912
12694
  // src/core/config/ignore-templates.ts
11913
12695
  init_esm_shims();
11914
12696
  init_logger();
11915
- var log36 = createLogger({ layer: "core", source: "ignore-templates.ts" });
12697
+ var log39 = createLogger({ layer: "core", source: "ignore-templates.ts" });
11916
12698
  var IGNORE_TEMPLATE = `# ========================================
11917
12699
  # LEAN CONTEXT (mcp-graph)
11918
12700
  # Filosofia: zero auto-load, tudo on-demand via MCP
@@ -12063,21 +12845,21 @@ release-please-config.json
12063
12845
  function ensureClaudeIgnore(projectDir) {
12064
12846
  const filePath = path17__default.join(projectDir, ".claudeignore");
12065
12847
  if (existsSync(filePath)) {
12066
- log36.debug(".claudeignore already exists, skipping");
12848
+ log39.debug(".claudeignore already exists, skipping");
12067
12849
  return false;
12068
12850
  }
12069
12851
  writeFileSync(filePath, IGNORE_TEMPLATE, "utf-8");
12070
- log36.info(".claudeignore created with lean context template");
12852
+ log39.info(".claudeignore created with lean context template");
12071
12853
  return true;
12072
12854
  }
12073
12855
  function ensureCopilotIgnore(projectDir) {
12074
12856
  const filePath = path17__default.join(projectDir, ".copilotignore");
12075
12857
  if (existsSync(filePath)) {
12076
- log36.debug(".copilotignore already exists, skipping");
12858
+ log39.debug(".copilotignore already exists, skipping");
12077
12859
  return false;
12078
12860
  }
12079
12861
  writeFileSync(filePath, IGNORE_TEMPLATE, "utf-8");
12080
- log36.info(".copilotignore created with lean context template");
12862
+ log39.info(".copilotignore created with lean context template");
12081
12863
  return true;
12082
12864
  }
12083
12865
  function updateIgnoreFile(filePath, label, dryRun) {
@@ -12085,7 +12867,7 @@ function updateIgnoreFile(filePath, label, dryRun) {
12085
12867
  if (!exists) {
12086
12868
  if (!dryRun) {
12087
12869
  writeFileSync(filePath, IGNORE_TEMPLATE, "utf-8");
12088
- log36.info(`${label} created with lean context template`);
12870
+ log39.info(`${label} created with lean context template`);
12089
12871
  }
12090
12872
  return { status: "created", message: `${label} created` };
12091
12873
  }
@@ -12095,7 +12877,7 @@ function updateIgnoreFile(filePath, label, dryRun) {
12095
12877
  }
12096
12878
  if (!dryRun) {
12097
12879
  writeFileSync(filePath, IGNORE_TEMPLATE, "utf-8");
12098
- log36.info(`${label} updated to latest template`);
12880
+ log39.info(`${label} updated to latest template`);
12099
12881
  }
12100
12882
  return { status: "updated", message: `${label} updated` };
12101
12883
  }
@@ -12772,7 +13554,7 @@ init_registry();
12772
13554
  // src/core/atomic-files/writer-markdown.ts
12773
13555
  init_esm_shims();
12774
13556
  init_logger();
12775
- var log37 = createLogger({ layer: "core", source: "writer-markdown.ts" });
13557
+ var log40 = createLogger({ layer: "core", source: "writer-markdown.ts" });
12776
13558
  var markerStart2 = (id) => `<!-- MCP-GRAPH:MANAGED-START:${id} -->`;
12777
13559
  var markerEnd2 = (id) => `<!-- MCP-GRAPH:MANAGED-END:${id} -->`;
12778
13560
  function extractManagedBlock(content, fileId) {
@@ -12834,7 +13616,7 @@ async function write(file, mode) {
12834
13616
  const existingBlock = extractManagedBlock(current, fileId);
12835
13617
  const tampered = existingBlock !== null && detectTampering(filePath, fileId, existingBlock);
12836
13618
  if (tampered) {
12837
- log37.warn("managed block tampered \u2014 system reconquering", { fileId, filePath });
13619
+ log40.warn("managed block tampered \u2014 system reconquering", { fileId, filePath });
12838
13620
  fs.writeFileSync(filePath + ".user-modified.bak", current, "utf8");
12839
13621
  }
12840
13622
  if (!tampered && existingBlock === managedContent) {
@@ -12872,7 +13654,7 @@ async function atomicWrite(filePath, content) {
12872
13654
  try {
12873
13655
  fs.unlinkSync(tmp);
12874
13656
  } catch (e) {
12875
- log37.debug("intentional swallow", { error: e, reason: "tmp file already gone, cleanup not needed" });
13657
+ log40.debug("intentional swallow", { error: e, reason: "tmp file already gone, cleanup not needed" });
12876
13658
  }
12877
13659
  throw err;
12878
13660
  }
@@ -12900,7 +13682,7 @@ async function runAtomicWrites(mode) {
12900
13682
  }
12901
13683
 
12902
13684
  // src/cli/commands/init-cmd.ts
12903
- var log38 = createLogger({ layer: "cli", source: "init.ts" });
13685
+ var log41 = createLogger({ layer: "cli", source: "init.ts" });
12904
13686
  var LEVEL_ICON = { ok: "\u2713", warning: "\u26A0", error: "\u2717" };
12905
13687
  async function runInitOrchestration(opts, deps) {
12906
13688
  const { dir, skipNeural, noServe, port } = opts;
@@ -13013,7 +13795,7 @@ function initCommand() {
13013
13795
  const dir = path17__default.resolve(opts.dir);
13014
13796
  const port = parseInt(opts.port, 10);
13015
13797
  if (isNaN(port) || port < 1 || port > 65535) {
13016
- log38.error("Invalid port number", { port: opts.port });
13798
+ log41.error("Invalid port number", { port: opts.port });
13017
13799
  process.exitCode = 1;
13018
13800
  return;
13019
13801
  }
@@ -13031,7 +13813,7 @@ function initCommand() {
13031
13813
  out("\nPronto. Execute `mcp-graph serve` para iniciar o servidor.");
13032
13814
  }
13033
13815
  } catch (error) {
13034
- log38.error("Init failed", { error: getErrorMessage(error) });
13816
+ log41.error("Init failed", { error: getErrorMessage(error) });
13035
13817
  process.exitCode = 1;
13036
13818
  }
13037
13819
  });
@@ -13096,7 +13878,7 @@ function readDaemonMeta(stateDir) {
13096
13878
 
13097
13879
  // src/core/daemon/daemon-reaper.ts
13098
13880
  init_logger();
13099
- var log40 = createLogger({ layer: "core", source: "daemon-reaper" });
13881
+ var log43 = createLogger({ layer: "core", source: "daemon-reaper" });
13100
13882
  function defaultDaemonRoot(home = os.homedir()) {
13101
13883
  return path17__default.join(home, ".mcp-graph");
13102
13884
  }
@@ -13135,7 +13917,7 @@ function reapDaemons(options = {}) {
13135
13917
  try {
13136
13918
  kill(lock.pid);
13137
13919
  } catch (err) {
13138
- log40.debug("intentional-swallow", {
13920
+ log43.debug("intentional-swallow", {
13139
13921
  error: String(err),
13140
13922
  reason: "process vanished between liveness probe and signal \u2014 treat as already reaped"
13141
13923
  });
@@ -13165,7 +13947,7 @@ function reapDaemons(options = {}) {
13165
13947
  try {
13166
13948
  fs__default.rmSync(stateDir, { recursive: true, force: true });
13167
13949
  } catch (err) {
13168
- log40.debug("intentional-swallow", {
13950
+ log43.debug("intentional-swallow", {
13169
13951
  error: String(err),
13170
13952
  reason: "state dir removal hit a permission error or race \u2014 next reaper run retries"
13171
13953
  });
@@ -13185,14 +13967,14 @@ function reapDaemons(options = {}) {
13185
13967
 
13186
13968
  // src/cli/commands/daemon-cmd.ts
13187
13969
  init_logger();
13188
- var log41 = createLogger({ layer: "cli", source: "daemon.ts" });
13970
+ var log44 = createLogger({ layer: "cli", source: "daemon.ts" });
13189
13971
  function output12(msg) {
13190
13972
  process.stdout.write(msg + "\n");
13191
13973
  }
13192
13974
  function daemonCommand() {
13193
13975
  const cmd = new Command("daemon").description("Inspect and clean up mcp-graph daemons");
13194
13976
  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) => {
13195
- log41.info("cli:daemon:prune", { dryRun: opts.dryRun });
13977
+ log44.info("cli:daemon:prune", { dryRun: opts.dryRun });
13196
13978
  const report = reapDaemons({ dryRun: opts.dryRun });
13197
13979
  const prefix = opts.dryRun ? "[dry-run] " : "";
13198
13980
  for (const a of report.actions) {
@@ -13264,7 +14046,7 @@ function formatProviderReport(report) {
13264
14046
  // src/cli/commands/doctor-cmd.ts
13265
14047
  init_errors();
13266
14048
  init_logger();
13267
- var log42 = createLogger({ layer: "cli", source: "doctor.ts" });
14049
+ var log45 = createLogger({ layer: "cli", source: "doctor.ts" });
13268
14050
  function output13(msg) {
13269
14051
  process.stdout.write(msg + "\n");
13270
14052
  }
@@ -13319,7 +14101,7 @@ function doctorCommand() {
13319
14101
  process.exit(1);
13320
14102
  }
13321
14103
  } catch (err) {
13322
- log42.error(`Doctor failed: ${getErrorMessage(err)}`);
14104
+ log45.error(`Doctor failed: ${getErrorMessage(err)}`);
13323
14105
  process.exit(1);
13324
14106
  }
13325
14107
  });
@@ -13331,7 +14113,7 @@ init_esm_shims();
13331
14113
  // src/core/autonomy/shadow-branch.ts
13332
14114
  init_esm_shims();
13333
14115
  init_logger();
13334
- var log43 = createLogger({ layer: "core", source: "shadow-branch.ts" });
14116
+ var log46 = createLogger({ layer: "core", source: "shadow-branch.ts" });
13335
14117
  function parseShadowTimestamp(branchName) {
13336
14118
  const match = /-(\d{10,})$/.exec(branchName);
13337
14119
  if (!match) return null;
@@ -13373,7 +14155,7 @@ function pruneOrphanWorktrees(options) {
13373
14155
  ).toString();
13374
14156
  branches = out2.split("\n").map((s) => s.trim()).filter(Boolean);
13375
14157
  } catch (err) {
13376
- log43.debug("shadow-branch:prune:list-failed", { error: String(err) });
14158
+ log46.debug("shadow-branch:prune:list-failed", { error: String(err) });
13377
14159
  }
13378
14160
  const wtMap = branches.length > 0 ? listShadowWorktrees(execOpts) : /* @__PURE__ */ new Map();
13379
14161
  for (const branch of branches) {
@@ -13385,34 +14167,34 @@ function pruneOrphanWorktrees(options) {
13385
14167
  execSync(`git worktree remove --force --force ${wtPath}`, execOpts);
13386
14168
  reapedWorktrees += 1;
13387
14169
  } catch (err) {
13388
- log43.debug("shadow-branch:prune:wt-remove-failed", { branch, wtPath, error: String(err) });
14170
+ log46.debug("shadow-branch:prune:wt-remove-failed", { branch, wtPath, error: String(err) });
13389
14171
  }
13390
14172
  }
13391
14173
  try {
13392
14174
  execSync(`git branch -D ${branch}`, execOpts);
13393
14175
  reapedBranches += 1;
13394
14176
  } catch (err) {
13395
- log43.debug("shadow-branch:prune:branch-delete-failed", { branch, error: String(err) });
14177
+ log46.debug("shadow-branch:prune:branch-delete-failed", { branch, error: String(err) });
13396
14178
  }
13397
14179
  }
13398
14180
  try {
13399
- const output15 = execSync("git worktree prune --verbose", execOpts).toString();
14181
+ const output16 = execSync("git worktree prune --verbose", execOpts).toString();
13400
14182
  if (reapedBranches > 0 || reapedWorktrees > 0) {
13401
- log43.info("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, ttlMs });
14183
+ log46.info("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, ttlMs });
13402
14184
  } else {
13403
- log43.debug("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, output: output15 });
14185
+ log46.debug("shadow-branch:prune-ok", { reapedBranches, reapedWorktrees, output: output16 });
13404
14186
  }
13405
- return { pruned: true, reapedBranches, reapedWorktrees, output: output15 };
14187
+ return { pruned: true, reapedBranches, reapedWorktrees, output: output16 };
13406
14188
  } catch (err) {
13407
14189
  const error = String(err);
13408
- log43.debug("shadow-branch:prune-failed", { error });
14190
+ log46.debug("shadow-branch:prune-failed", { error });
13409
14191
  return { pruned: false, reapedBranches, reapedWorktrees, error };
13410
14192
  }
13411
14193
  }
13412
14194
 
13413
14195
  // src/cli/commands/gc-cmd.ts
13414
14196
  init_logger();
13415
- var log44 = createLogger({ layer: "cli", source: "gc.ts" });
14197
+ var log47 = createLogger({ layer: "cli", source: "gc.ts" });
13416
14198
  function output14(msg) {
13417
14199
  process.stdout.write(msg + "\n");
13418
14200
  }
@@ -13420,7 +14202,7 @@ function gcCommand() {
13420
14202
  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) => {
13421
14203
  const ttlMinutes = parseInt(opts.ttl, 10);
13422
14204
  const ttlMs = Number.isFinite(ttlMinutes) && ttlMinutes > 0 ? ttlMinutes * 60 * 1e3 : 0;
13423
- log44.info("cli:gc:start", { dir: opts.dir, ttlMs });
14205
+ log47.info("cli:gc:start", { dir: opts.dir, ttlMs });
13424
14206
  const result = pruneOrphanWorktrees({ cwd: opts.dir, ttlMs });
13425
14207
  if (result.pruned) {
13426
14208
  output14(`gc: reaped ${result.reapedBranches} branches, ${result.reapedWorktrees} worktrees`);
@@ -13431,6 +14213,48 @@ function gcCommand() {
13431
14213
  });
13432
14214
  }
13433
14215
 
14216
+ // src/cli/commands/skill-cmd.ts
14217
+ init_esm_shims();
14218
+ init_skill_registry();
14219
+ function output15(msg) {
14220
+ process.stdout.write(msg + "\n");
14221
+ }
14222
+ function skillCommand() {
14223
+ const cmd = new Command("skill").description("Lista e exibe skills (instru\xE7\xF5es para agentes)");
14224
+ 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) => {
14225
+ const seen = /* @__PURE__ */ new Set();
14226
+ let count = 0;
14227
+ for (const root of defaultSkillRoots(opts.dir)) {
14228
+ const { skills } = listSkills(root, opts.phase);
14229
+ for (const s of skills) {
14230
+ if (seen.has(s.name)) continue;
14231
+ seen.add(s.name);
14232
+ count += 1;
14233
+ output15(`${s.name.padEnd(28)} [${s.category}] ${s.description}`);
14234
+ }
14235
+ }
14236
+ if (count === 0) output15("Nenhuma skill encontrada.");
14237
+ else output15(`
14238
+ ${count} skill(s).`);
14239
+ });
14240
+ cmd.command("show <nome>").description("Imprime as instru\xE7\xF5es completas de uma skill").option("-d, --dir <dir>", "Raiz do projeto", process.cwd()).action((nome, opts) => {
14241
+ for (const root of defaultSkillRoots(opts.dir)) {
14242
+ const found = invokeSkill(root, nome);
14243
+ if (found) {
14244
+ output15(`=== ${found.name} ===`);
14245
+ output15(`[${found.category}] ${found.description}`);
14246
+ if (found.phases.length > 0) output15(`fases: ${found.phases.join(", ")}`);
14247
+ output15("");
14248
+ output15(found.body);
14249
+ return;
14250
+ }
14251
+ }
14252
+ output15(`Skill n\xE3o encontrada: ${nome}. Tente 'skill list'.`);
14253
+ process.exitCode = 1;
14254
+ });
14255
+ return cmd;
14256
+ }
14257
+
13434
14258
  // src/cli/index.ts
13435
14259
  var program = new Command();
13436
14260
  program.name("agent-graph-flow").description(PROMISE).version(VERSION, "-v, --version");
@@ -13451,6 +14275,7 @@ program.addCommand(initCommand());
13451
14275
  program.addCommand(daemonCommand());
13452
14276
  program.addCommand(doctorCommand());
13453
14277
  program.addCommand(gcCommand());
14278
+ program.addCommand(skillCommand());
13454
14279
  function shouldLaunchTui() {
13455
14280
  const noArgs = process.argv.length <= 2;
13456
14281
  const isTty = Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);