Files
rUv 0f3c45101b fix: address #3045 #3064 #3065 + new ruflo-deepseek-harness plugin (3.38.13) (#3078)
Bug fixes
- #3045 statusline.cjs: getGitInfo() runs the 5-command git chain per render;
  on large repos with concurrent Claude Code sessions this queued subprocesses
  faster than they finished (reporter observed hundreds of orphan children +
  load-avg in the hundreds). Added a per-cwd tmp file cache with 5s TTL —
  dirty status still feels live, pileup is bounded.

- #3064 hooks post-task: the narrow ad-hoc regex /^[a-zA-Z0-9_-]+$/ silently
  dropped every colon-namespaced plugin agent (ruflo-core:reviewer,
  feature-dev:code-explorer, ...) — i.e. every Claude Code plugin agent. The
  canonical validateIdentifier() upstream already allows ':' and '.', so the
  redundant regex is removed. Regression test locks in 4 agent-shape cases,
  proven to catch the bug: 2 pass / 2 fail on revert, 4 pass with fix.

- #3065 harness-gepa SKILL.md: unquoted colon in `description` broke YAML
  parsing in `npx skills add`. Quoted the description; rephrased the bare
  "(default:" to avoid the leading colon.

New plugin: ruflo-deepseek-harness
- plugins/ruflo-deepseek-harness/: sibling to ruflo-metaharness (ADR-150
  shape). Two skills: `deepseek-chat` (non-reasoning) and `deepseek-reason`
  (surfaces reasoning_content separately). Reads DEEPSEEK_API_KEY from env;
  degrades gracefully (exit 0 with `{status: 'degraded', reason, hint}`)
  when the key is missing or the API is unreachable. `--alert-on-error`
  flag opts into hard exit 1 for CI gates. Smoke-tested locally.

Release
- Bump @claude-flow/cli, claude-flow, ruflo: 3.38.12 → 3.38.13 (PATCH:
  bug fixes; the new plugin is scaffolding under plugins/ and not part of
  the npm-published CLI packages).

Not fixed
- #3051 memory_store tags: current source's memory_store handler passes
  tags straight through to storeEntry; a maintainer already verified live
  round-trip works on fa13ee4ad. Reporter has not yet supplied the
  ruflo/@claude-flow/cli version that hosted the affected MCP server,
  so the affected release path is unknown. Left as-is pending that reply.


Claude-Session: https://claude.ai/code/session_0118jMsYhwHD5dx2vStENsEB
2026-08-21 10:10:13 -04:00

82 lines
2.5 KiB
JavaScript

#!/usr/bin/env node
/**
* DeepSeek reasoning completion — `deepseek-reasoner` (aka R1).
*
* Surfaces the model's `reasoning_content` (chain-of-thought) separately
* from the final `content`, so tooling can display or discard the CoT
* without re-parsing the message.
*
* Usage:
* node plugins/ruflo-deepseek-harness/scripts/reason.mjs \
* --prompt "Prove that sqrt(2) is irrational." \
* [--system "..."] \
* [--model deepseek-reasoner] \
* [--max-tokens 4096] \
* [--show-reasoning] \
* [--format table|json] \
* [--alert-on-error]
*
* Notes:
* - `deepseek-reasoner` ignores `temperature`/`top_p` per DeepSeek's API
* docs, so this script does not forward them.
* - `--show-reasoning` in table mode prints the chain-of-thought before
* the answer; in JSON mode the reasoning is always included.
*/
import { parseArgs, emitAndExit, deepseekChat, extractCompletion } from './_deepseek.mjs';
const args = parseArgs(process.argv.slice(2));
const alertOnError = args['alert-on-error'] === true;
const prompt = args.prompt;
if (!prompt || prompt === true) {
emitAndExit(
{ status: 'error', reason: '--prompt is required (a string of at least 1 char)' },
{ alertOnError: true }
);
}
const messages = [];
if (args.system && args.system !== true) messages.push({ role: 'system', content: args.system });
messages.push({ role: 'user', content: prompt });
const model = (args.model && args.model !== true) ? args.model : 'deepseek-reasoner';
const maxTokens = args['max-tokens'] !== undefined ? Number(args['max-tokens']) : undefined;
const result = await deepseekChat({ model, messages, maxTokens });
if (!result.ok) {
emitAndExit(
{ status: 'degraded', model, reason: result.reason, hint: result.hint || null },
{ alertOnError }
);
}
const { content, reasoning, finishReason } = extractCompletion(result.data);
const usage = result.data.usage || {};
if (args.format === 'table') {
if (args['show-reasoning']) {
process.stdout.write('--- reasoning ---\n' + reasoning + '\n--- answer ---\n');
}
process.stdout.write(content + '\n');
process.exit(0);
}
emitAndExit({
status: 'ok',
model,
content,
reasoning,
finishReason,
usage: {
promptTokens: usage.prompt_tokens ?? null,
completionTokens: usage.completion_tokens ?? null,
reasoningTokens:
usage.completion_tokens_details && usage.completion_tokens_details.reasoning_tokens
? usage.completion_tokens_details.reasoning_tokens
: null,
totalTokens: usage.total_tokens ?? null,
},
});