u-foo 2.4.5 → 2.4.7

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 (64) hide show
  1. package/README.md +6 -8
  2. package/README.zh-CN.md +5 -5
  3. package/SKILLS/ufoo/SKILL.md +2 -2
  4. package/SKILLS/uinit/SKILL.md +8 -11
  5. package/package.json +3 -8
  6. package/scripts/postinstall.js +1 -21
  7. package/src/agents/launch/launcher.js +1 -13
  8. package/src/app/chat/commandExecutor.js +14 -9
  9. package/src/app/chat/commands.js +2 -2
  10. package/src/app/chat/daemonCoordinator.js +4 -0
  11. package/src/app/chat/daemonReconnect.js +17 -7
  12. package/src/app/cli/features/doctor.js +11 -18
  13. package/src/app/cli/features/init.js +12 -191
  14. package/src/app/cli/features/skills.js +0 -15
  15. package/src/app/cli/run.js +12 -7
  16. package/src/code/README.md +4 -4
  17. package/src/code/cli.js +1 -1
  18. package/src/code/launcher/ucode.js +18 -45
  19. package/src/code/skills/loader.js +0 -13
  20. package/src/coordination/context/doctor.js +6 -26
  21. package/src/online/server.js +1 -1
  22. package/src/runtime/daemon/index.js +1 -1
  23. package/src/runtime/daemon/mcpServer.js +1 -1
  24. package/src/runtime/daemon/restart.js +293 -0
  25. package/src/runtime/daemon/run.js +31 -37
  26. package/src/runtime/terminal/index.js +1 -1
  27. package/src/ui/MIGRATION.md +8 -10
  28. package/src/ui/ink/ChatApp.js +12 -6
  29. package/bin/ucode-core.js +0 -15
  30. package/bin/ufoo +0 -71
  31. package/modules/AGENTS.template.md +0 -8
  32. package/modules/bus/README.md +0 -140
  33. package/modules/context/README.md +0 -60
  34. package/modules/online/README.md +0 -92
  35. package/modules/resources/ICONS/README.md +0 -12
  36. package/modules/resources/ICONS/libraries/README.md +0 -17
  37. package/modules/resources/ICONS/libraries/heroicons/LICENSE +0 -22
  38. package/modules/resources/ICONS/libraries/heroicons/README.md +0 -15
  39. package/modules/resources/ICONS/libraries/heroicons/arrow-right.svg +0 -4
  40. package/modules/resources/ICONS/libraries/heroicons/check.svg +0 -4
  41. package/modules/resources/ICONS/libraries/heroicons/chevron-down.svg +0 -4
  42. package/modules/resources/ICONS/libraries/heroicons/cog-6-tooth.svg +0 -5
  43. package/modules/resources/ICONS/libraries/heroicons/magnifying-glass.svg +0 -4
  44. package/modules/resources/ICONS/libraries/heroicons/x-mark.svg +0 -4
  45. package/modules/resources/ICONS/libraries/lucide/LICENSE +0 -40
  46. package/modules/resources/ICONS/libraries/lucide/README.md +0 -15
  47. package/modules/resources/ICONS/libraries/lucide/arrow-right.svg +0 -15
  48. package/modules/resources/ICONS/libraries/lucide/check.svg +0 -14
  49. package/modules/resources/ICONS/libraries/lucide/chevron-down.svg +0 -14
  50. package/modules/resources/ICONS/libraries/lucide/search.svg +0 -15
  51. package/modules/resources/ICONS/libraries/lucide/settings.svg +0 -15
  52. package/modules/resources/ICONS/libraries/lucide/x.svg +0 -15
  53. package/modules/resources/ICONS/rules.md +0 -7
  54. package/modules/resources/README.md +0 -9
  55. package/modules/resources/UI/ANTI-PATTERNS.md +0 -6
  56. package/modules/resources/UI/TONE.md +0 -6
  57. package/scripts/chat-app-smoke.js +0 -30
  58. package/scripts/global-chat-switch-benchmark.js +0 -406
  59. package/scripts/ink-demo.js +0 -23
  60. package/scripts/ink-smoke.js +0 -30
  61. package/scripts/ucode-app-smoke.js +0 -36
  62. /package/{modules/bus/SKILLS → SKILLS}/ubus/SKILL.md +0 -0
  63. /package/{modules/context/SKILLS → SKILLS}/uctx/SKILL.md +0 -0
  64. /package/{modules/online/SKILLS → SKILLS}/ufoo-online/SKILL.md +0 -0
package/README.md CHANGED
@@ -69,7 +69,7 @@ Initialize a project and open the chat dashboard:
69
69
 
70
70
  ```bash
71
71
  cd your-project
72
- ufoo init --modules context,bus
72
+ ufoo init --targets context,bus
73
73
  ufoo
74
74
  ```
75
75
 
@@ -167,7 +167,7 @@ still available, but the normal ufoo workflow is to work from chat.
167
167
  These are setup or troubleshooting commands. In chat, use slash commands:
168
168
 
169
169
  ```text
170
- /init context bus resources
170
+ /init context bus
171
171
  /doctor
172
172
  /status
173
173
  /daemon status
@@ -177,11 +177,11 @@ These are setup or troubleshooting commands. In chat, use slash commands:
177
177
  ```
178
178
 
179
179
  `ufoo init` creates `.ufoo/`, ensures `AGENTS.md` and `CLAUDE.md`, initializes
180
- selected modules, and prepares shared storage. `CLAUDE.md` may be a symlink;
181
- edit project instructions in `AGENTS.md`.
180
+ selected workspace state, and prepares shared storage. `CLAUDE.md` may be a
181
+ symlink; edit project instructions in `AGENTS.md`.
182
182
 
183
183
  Before a project has been initialized, the equivalent CLI form is also useful:
184
- `ufoo init --modules context,bus`.
184
+ `ufoo init --targets context,bus`.
185
185
 
186
186
  ### Event Bus
187
187
 
@@ -322,8 +322,7 @@ src/
322
322
  online/ relay client/server/runner/token helpers
323
323
  ```
324
324
 
325
- See [PROJECT.md](PROJECT.md) for the maintainer-facing map and
326
- [docs/source-structure.md](docs/source-structure.md) for detailed package
325
+ See [PROJECT.md](PROJECT.md) for the maintainer-facing map and detailed package
327
326
  ownership.
328
327
 
329
328
  ## Development
@@ -340,7 +339,6 @@ Useful checks:
340
339
  ```bash
341
340
  npm run test:watch
342
341
  npm run test:coverage
343
- npm run bench:global-switch
344
342
  ```
345
343
 
346
344
  The repository is CommonJS, targets Node.js 18+, and has no build step.
package/README.zh-CN.md CHANGED
@@ -67,7 +67,7 @@ npm link
67
67
 
68
68
  ```bash
69
69
  cd your-project
70
- ufoo init --modules context,bus
70
+ ufoo init --targets context,bus
71
71
  ufoo
72
72
  ```
73
73
 
@@ -163,7 +163,7 @@ ufoo -g
163
163
  这些是初始化或排障命令。进入 chat 后优先使用 slash command:
164
164
 
165
165
  ```text
166
- /init context bus resources
166
+ /init context bus
167
167
  /doctor
168
168
  /status
169
169
  /daemon status
@@ -173,10 +173,10 @@ ufoo -g
173
173
  ```
174
174
 
175
175
  `ufoo init` 会创建 `.ufoo/`,确保 `AGENTS.md` 和 `CLAUDE.md` 存在,
176
- 初始化选中的模块,并准备共享存储。`CLAUDE.md` 可以是 symlink;项目指令
177
- 优先编辑 `AGENTS.md`。
176
+ 初始化选中的工作区状态,并准备共享存储。`CLAUDE.md` 可以是 symlink
177
+ 项目指令优先编辑 `AGENTS.md`。
178
178
 
179
- 项目尚未初始化时,也可以先在外部执行等价 CLI:`ufoo init --modules context,bus`。
179
+ 项目尚未初始化时,也可以先在外部执行等价 CLI:`ufoo init --targets context,bus`。
180
180
 
181
181
  ### 事件总线
182
182
 
@@ -13,7 +13,7 @@ ufoo is the multi-agent coordination layer. It provides four capabilities:
13
13
  1. **Context Decisions** — Sparse log of major plan-level choices shared across agents
14
14
  2. **Shared Memory** — Durable, low-noise project facts shared across agents
15
15
  3. **Event Bus** — Inter-agent messaging
16
- 4. **Initialization** — Project setup for ufoo modules
16
+ 4. **Initialization** — Project setup for ufoo workspace state
17
17
 
18
18
  ## 1. Context Decisions (uctx)
19
19
 
@@ -199,7 +199,7 @@ ufoo history prompt [limit] # Render as injectable prompt block
199
199
  Trigger: `/uinit` or `/ufoo init`
200
200
 
201
201
  ```bash
202
- ufoo init --modules context,bus --project $(pwd)
202
+ ufoo init --targets context,bus --project $(pwd)
203
203
  ```
204
204
 
205
205
  After init, auto-join bus if enabled.
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: uinit
3
3
  description: |
4
- Initialize ufoo modules in current project.
4
+ Initialize ufoo workspace state in current project.
5
5
  Use when: (1) new project needs context/bus enabled, (2) user inputs /uinit or /ufoo init.
6
- Provides interactive module selection, defaults to all selected.
6
+ Provides interactive target selection, defaults to all selected.
7
7
  ---
8
8
 
9
9
  # uinit
10
10
 
11
- Initialize ufoo modules in current project.
11
+ Initialize ufoo workspace state in current project.
12
12
 
13
13
  ## Trigger
14
14
 
@@ -16,22 +16,20 @@ User inputs `/uinit` or `/ufoo init`
16
16
 
17
17
  ## Execution Flow
18
18
 
19
- ### 1. Ask user to select modules
19
+ ### 1. Ask user to select init targets
20
20
 
21
21
  Use AskUserQuestion tool, provide multi-select, default all selected:
22
22
 
23
23
  ```
24
- Please select modules to enable:
24
+ Please select ufoo state to enable:
25
25
 
26
26
  ☑ context - Shared context protocol (.ufoo/context/)
27
27
  ☑ bus - Agent event bus (.ufoo/bus/ + .ufoo/agent/)
28
- ☐ resources - UI/Icons resources (optional)
29
28
  ```
30
29
 
31
30
  Options:
32
31
  - `context` (recommended) - Shared context, sparse decision log for major plan-level choices
33
32
  - `bus` (recommended) - Multi-agent communication, task delegation, message passing
34
- - `resources` (optional) - UI tone guide, icon library
35
33
 
36
34
  Default selected: context, bus
37
35
 
@@ -40,10 +38,10 @@ Default selected: context, bus
40
38
  Based on user selection, execute:
41
39
 
42
40
  ```bash
43
- ufoo init --modules <selected_modules> --project $(pwd)
41
+ ufoo init --targets <selected_targets> --project $(pwd)
44
42
  ```
45
43
 
46
- ### 3. If bus module selected, auto-join bus
44
+ ### 3. If bus target selected, auto-join bus
47
45
 
48
46
  ```bash
49
47
  SUBSCRIBER="${UFOO_SUBSCRIBER_ID:-$(ufoo bus whoami 2>/dev/null || true)}"
@@ -60,7 +58,7 @@ fi
60
58
  ```
61
59
  === ufoo initialization complete ===
62
60
 
63
- Enabled modules:
61
+ Enabled ufoo state:
64
62
  ✓ core memory → .ufoo/memory/
65
63
  ✓ context → .ufoo/context/
66
64
  ✓ bus → .ufoo/bus/ + .ufoo/agent/
@@ -76,4 +74,3 @@ Next steps:
76
74
 
77
75
  - If .ufoo/memory, .ufoo/context, .ufoo/bus, or .ufoo/agent already exists, skip creation
78
76
  - After initialization, reuse existing subscriber ID first, join only as fallback (if bus enabled)
79
- - AGENTS.md will have protocol description block injected
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u-foo",
3
- "version": "2.4.5",
3
+ "version": "2.4.7",
4
4
  "description": "Multi-Agent Workspace Protocol. Just add u. claude → uclaude, codex → ucodex.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://ufoo.dev",
@@ -20,8 +20,7 @@
20
20
  "uclaude": "bin/uclaude.js",
21
21
  "ucodex": "bin/ucodex.js",
22
22
  "uagy": "bin/uagy.js",
23
- "ucode": "bin/ucode.js",
24
- "ucode-core": "bin/ucode-core.js"
23
+ "ucode": "bin/ucode.js"
25
24
  },
26
25
  "files": [
27
26
  "bin/",
@@ -30,7 +29,6 @@
30
29
  "online/",
31
30
  "scripts/",
32
31
  "SKILLS/",
33
- "modules/",
34
32
  "LICENSE",
35
33
  "README.md"
36
34
  ],
@@ -42,10 +40,7 @@
42
40
  "postinstall": "node scripts/postinstall.js",
43
41
  "test": "jest",
44
42
  "test:watch": "jest --watch",
45
- "test:coverage": "jest --coverage",
46
- "ink:demo": "node scripts/ink-demo.js",
47
- "ink:smoke": "node scripts/ink-smoke.js",
48
- "bench:global-switch": "node scripts/global-chat-switch-benchmark.js"
43
+ "test:coverage": "jest --coverage"
49
44
  },
50
45
  "dependencies": {
51
46
  "@anthropic-ai/claude-agent-sdk": "^0.2.138",
@@ -30,11 +30,9 @@ for (const platform of platforms) {
30
30
  }
31
31
  }
32
32
 
33
- // Collect all skill sources from package
33
+ // Collect all skill sources from the package-level SKILLS directory.
34
34
  function collectSkillSources(pkgRoot) {
35
35
  const sources = [];
36
-
37
- // Top-level SKILLS/
38
36
  const topSkills = path.join(pkgRoot, "SKILLS");
39
37
  if (fs.existsSync(topSkills)) {
40
38
  for (const entry of fs.readdirSync(topSkills, { withFileTypes: true })) {
@@ -46,24 +44,6 @@ function collectSkillSources(pkgRoot) {
46
44
  }
47
45
  }
48
46
  }
49
-
50
- // modules/*/SKILLS/
51
- const modulesDir = path.join(pkgRoot, "modules");
52
- if (fs.existsSync(modulesDir)) {
53
- for (const mod of fs.readdirSync(modulesDir, { withFileTypes: true })) {
54
- if (!mod.isDirectory()) continue;
55
- const modSkills = path.join(modulesDir, mod.name, "SKILLS");
56
- if (!fs.existsSync(modSkills)) continue;
57
- for (const entry of fs.readdirSync(modSkills, { withFileTypes: true })) {
58
- if (!entry.isDirectory()) continue;
59
- const skillMd = path.join(modSkills, entry.name, "SKILL.md");
60
- if (fs.existsSync(skillMd)) {
61
- sources.push({ name: entry.name, dir: path.join(modSkills, entry.name), md: skillMd });
62
- }
63
- }
64
- }
65
- }
66
-
67
47
  return sources;
68
48
  }
69
49
 
@@ -323,23 +323,11 @@ class AgentLauncher {
323
323
 
324
324
  if (!fs.existsSync(busDir)) {
325
325
  // 调用 ufoo init
326
- spawnSync("ufoo", ["init", "--modules", "context,bus"], {
326
+ spawnSync("ufoo", ["init", "--targets", "context,bus"], {
327
327
  cwd: this.cwd,
328
328
  stdio: "ignore",
329
329
  });
330
330
  }
331
-
332
- // 检查 AGENTS.md 是否有 ufoo template
333
- const agentsFile = path.join(this.cwd, "AGENTS.md");
334
- if (fs.existsSync(agentsFile)) {
335
- const content = fs.readFileSync(agentsFile, "utf8");
336
- if (!content.includes("<!-- ufoo -->")) {
337
- spawnSync("ufoo", ["init", "--modules", "context,bus"], {
338
- cwd: this.cwd,
339
- stdio: "ignore",
340
- });
341
- }
342
- }
343
331
  }
344
332
 
345
333
  /**
@@ -19,6 +19,7 @@ const { parseIntervalMs, formatIntervalMs } = require("./cronScheduler");
19
19
  const { isGlobalControllerProjectRoot, resolveGlobalControllerUfooDir } = require("../../runtime/projects");
20
20
  const { loadPromptProfileRegistry } = require("../../orchestration/groups/promptProfiles");
21
21
  const { resolveSoloAgentType } = require("../../orchestration/solo/commands");
22
+ const { restartDaemonLifecycle } = require("../../runtime/daemon/restart");
22
23
  const {
23
24
  inspectDirectAuthStatus,
24
25
  formatDirectAuthStatus,
@@ -269,15 +270,19 @@ function createCommandExecutor(options = {}) {
269
270
  }
270
271
 
271
272
  statusMsg("{gray-fg}⚙{/gray-fg} Restarting daemon...");
272
- stopDaemon(targetRoot, { source: "chat-command:/daemon restart" });
273
- await sleep(500);
274
- if (isDaemonRunning(targetRoot)) {
273
+ const result = await restartDaemonLifecycle({
274
+ projectRoot: targetRoot,
275
+ isRunning: isDaemonRunning,
276
+ stopDaemon,
277
+ startDaemon,
278
+ stopOptions: { source: "chat-command:/daemon restart" },
279
+ sleep,
280
+ });
281
+ if (result.error === "failed_to_stop") {
275
282
  statusMsg("{gray-fg}✗{/gray-fg} Failed to stop daemon");
276
283
  return;
277
284
  }
278
- startDaemon(targetRoot);
279
- await sleep(1000);
280
- if (isDaemonRunning(targetRoot)) {
285
+ if (result.ok) {
281
286
  statusMsg("{gray-fg}✓{/gray-fg} Daemon restarted");
282
287
  } else {
283
288
  statusMsg("{gray-fg}✗{/gray-fg} Failed to restart daemon");
@@ -298,7 +303,7 @@ function createCommandExecutor(options = {}) {
298
303
  }
299
304
 
300
305
  async function handleInitCommand(args = []) {
301
- logMessage("system", "{white-fg}⚙{/white-fg} Initializing ufoo modules...");
306
+ logMessage("system", "{white-fg}⚙{/white-fg} Initializing ufoo workspace...");
302
307
 
303
308
  await withCapturedConsole(
304
309
  {
@@ -314,8 +319,8 @@ function createCommandExecutor(options = {}) {
314
319
  try {
315
320
  const repoRoot = path.join(__dirname, "..", "..");
316
321
  const init = createInit(repoRoot);
317
- const modules = args.length > 0 ? args.join(",") : "context,bus";
318
- await init.init({ modules, project: projectRoot });
322
+ const targets = args.length > 0 ? args.join(",") : "context,bus";
323
+ await init.init({ targets, project: projectRoot });
319
324
 
320
325
  logMessage("system", "{white-fg}✓{/white-fg} Initialization complete");
321
326
  renderScreen();
@@ -47,7 +47,7 @@ const COMMAND_TREE = {
47
47
  templates: { desc: "List available templates" },
48
48
  },
49
49
  },
50
- "/init": { desc: "Initialize modules" },
50
+ "/init": { desc: "Initialize workspace" },
51
51
  "/multi": { desc: "Toggle multi-window agent view" },
52
52
  "/open": { desc: "Open project path in global mode" },
53
53
  "/launch": {
@@ -305,7 +305,7 @@ function describeCommandForChat(text) {
305
305
  if (command === "multi") return "Toggling multi-pane view";
306
306
  if (command === "open") return `Opening project ${args[0] || ""}`.trim();
307
307
  if (command === "resume") return args[0] === "list" ? "Listing recoverable agents" : `Resuming ${args[0] || "agents"}`;
308
- if (command === "init") return "Initializing ufoo modules";
308
+ if (command === "init") return "Initializing ufoo workspace";
309
309
 
310
310
  return `Running /${command}`;
311
311
  }
@@ -12,8 +12,10 @@ function createDaemonCoordinator(options = {}) {
12
12
  logMessage,
13
13
  stopDaemon,
14
14
  startDaemon,
15
+ isDaemonRunning,
15
16
  daemonConnection,
16
17
  restartDaemon,
18
+ sleep,
17
19
  } = options;
18
20
 
19
21
  const connectClientFn = connectClient
@@ -37,9 +39,11 @@ function createDaemonCoordinator(options = {}) {
37
39
  projectRoot,
38
40
  stopDaemon,
39
41
  startDaemon,
42
+ isDaemonRunning,
40
43
  daemonConnection: connection,
41
44
  logMessage,
42
45
  resolveStatusLine,
46
+ sleep,
43
47
  });
44
48
  let switchProjectChain = Promise.resolve();
45
49
 
@@ -1,4 +1,5 @@
1
1
  const { restartLocks } = require("./daemonTransport");
2
+ const { restartDaemonLifecycle } = require("../../runtime/daemon/restart");
2
3
 
3
4
  function resolveDaemonConnection(daemonConnection) {
4
5
  return typeof daemonConnection === "function" ? daemonConnection() : daemonConnection;
@@ -9,9 +10,11 @@ function restartDaemonFlow(options = {}) {
9
10
  projectRoot,
10
11
  stopDaemon,
11
12
  startDaemon,
13
+ isDaemonRunning,
12
14
  daemonConnection,
13
15
  logMessage,
14
16
  resolveStatusLine = null,
17
+ sleep,
15
18
  } = options;
16
19
 
17
20
  const statusMsg = resolveStatusLine || ((text) => logMessage("status", text));
@@ -26,14 +29,21 @@ function restartDaemonFlow(options = {}) {
26
29
  if (connection) {
27
30
  connection.close();
28
31
  }
29
- stopDaemon(projectRoot);
30
- startDaemon(projectRoot);
31
- const connected = connection ? await connection.connect() : false;
32
- if (connected) {
33
- if (typeof connection.requestStatus === "function") {
34
- connection.requestStatus();
35
- }
32
+ const result = await restartDaemonLifecycle({
33
+ projectRoot,
34
+ isRunning: isDaemonRunning,
35
+ stopDaemon,
36
+ startDaemon,
37
+ connect: connection ? () => connection.connect() : null,
38
+ requestStatus: connection && typeof connection.requestStatus === "function"
39
+ ? () => connection.requestStatus()
40
+ : null,
41
+ sleep,
42
+ });
43
+ if (result.ok) {
36
44
  statusMsg("{gray-fg}✓{/gray-fg} Daemon reconnected");
45
+ } else if (result.error === "failed_to_stop") {
46
+ statusMsg("{gray-fg}✗{/gray-fg} Failed to stop daemon");
37
47
  } else {
38
48
  statusMsg("{gray-fg}✗{/gray-fg} Failed to reconnect to daemon");
39
49
  }
@@ -14,29 +14,22 @@ class RepoDoctor {
14
14
  }
15
15
 
16
16
  run() {
17
- const contextMod = path.join(this.repoRoot, "modules", "context");
17
+ const skillsDir = path.join(this.repoRoot, "SKILLS");
18
+ const contextSkill = path.join(skillsDir, "uctx", "SKILL.md");
19
+ const busSkill = path.join(skillsDir, "ubus", "SKILL.md");
18
20
 
19
- const contextExists = fs.existsSync(contextMod);
20
- if (!contextExists) {
21
- this.fail(`missing ${contextMod}`);
22
- }
21
+ if (!fs.existsSync(contextSkill)) this.fail(`missing ${contextSkill}`);
22
+ if (!fs.existsSync(busSkill)) this.fail(`missing ${busSkill}`);
23
23
 
24
- if (contextExists) {
25
- const contextDoctor = new ContextDoctor(this.repoRoot);
26
- const ok = contextDoctor.lintProtocol();
27
- if (!ok) this.failed = true;
28
- }
24
+ const contextDoctor = new ContextDoctor(this.repoRoot);
25
+ const ok = contextDoctor.lintProtocol();
26
+ if (!ok) this.failed = true;
29
27
 
30
28
  console.log("=== ufoo doctor ===");
31
29
  console.log(`Monorepo: ${this.repoRoot}`);
32
- console.log("Modules:");
33
- if (contextExists) {
34
- console.log(`- context: ${contextMod}`);
35
- }
36
- const resources = path.join(this.repoRoot, "modules", "resources");
37
- if (fs.existsSync(resources)) {
38
- console.log(`- resources: ${resources}`);
39
- }
30
+ console.log("Skills:");
31
+ if (fs.existsSync(contextSkill)) console.log(`- uctx: ${contextSkill}`);
32
+ if (fs.existsSync(busSkill)) console.log(`- ubus: ${busSkill}`);
40
33
 
41
34
  if (this.failed) {
42
35
  console.log("Status: FAILED");