ccg-workflow 3.1.3 → 3.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/shared/{ccg-workflow.DCFZF0Bl.mjs → ccg-workflow.DpOmP1et.mjs} +2 -2
- package/package.json +1 -1
- package/templates/codex/hooks/ccg-workflow.py +15 -2
- package/templates/hooks/subagent-context.js +17 -1
- package/templates/hooks/task-utils.js +31 -8
package/README.md
CHANGED
|
@@ -144,7 +144,7 @@ CCG installs 4 hooks into `~/.claude/settings.json`:
|
|
|
144
144
|
|------|-------|---------|
|
|
145
145
|
| workflow-state.js | UserPromptSubmit | Injects task state breadcrumb every turn |
|
|
146
146
|
| session-start.js | SessionStart | Injects full project context on session start/clear/compact |
|
|
147
|
-
| subagent-context.js | PreToolUse (Bash/Agent) | Injects spec + task context into codeagent-wrapper calls
|
|
147
|
+
| subagent-context.js | PreToolUse (Bash/Agent) | Injects spec + task context: directly into Team member prompt via `updatedInput`, into lead context for codeagent-wrapper calls |
|
|
148
148
|
| skill-router.js | UserPromptSubmit | Auto-injects domain knowledge when keywords detected |
|
|
149
149
|
|
|
150
150
|
Hooks are JavaScript, zero dependencies, silent on failure.
|
|
@@ -253,4 +253,4 @@ MIT
|
|
|
253
253
|
|
|
254
254
|
---
|
|
255
255
|
|
|
256
|
-
v3.1.
|
|
256
|
+
v3.1.5 | [Issues](https://github.com/fengshao1227/ccg-workflow/issues) | [Contributing](./CONTRIBUTING.md)
|
package/README.zh-CN.md
CHANGED
|
@@ -145,7 +145,7 @@ CCG 在 `~/.claude/settings.json` 注册 4 个 Hook:
|
|
|
145
145
|
|------|------|------|
|
|
146
146
|
| workflow-state.js | UserPromptSubmit | 每轮注入任务状态面包屑 |
|
|
147
147
|
| session-start.js | SessionStart | 会话开始/压缩时注入完整项目上下文 |
|
|
148
|
-
| subagent-context.js | PreToolUse |
|
|
148
|
+
| subagent-context.js | PreToolUse | Team spawn 时通过 `updatedInput` 直接注入子 agent prompt;codeagent-wrapper 时注入主控上下文 |
|
|
149
149
|
| skill-router.js | UserPromptSubmit | 检测域关键词,自动注入知识文件 |
|
|
150
150
|
|
|
151
151
|
纯 JavaScript,零依赖,失败时静默退出。
|
|
@@ -248,4 +248,4 @@ MIT
|
|
|
248
248
|
|
|
249
249
|
---
|
|
250
250
|
|
|
251
|
-
v3.
|
|
251
|
+
v3.1.5 | [Issues](https://github.com/fengshao1227/ccg-workflow/issues) | [Contributing](./CONTRIBUTING.md)
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import cac from 'cac';
|
|
3
3
|
import ansis from 'ansis';
|
|
4
|
-
import { B as diagnoseMcpConfig, C as isWindows, D as readClaudeCodeConfig, E as fixWindowsMcpConfig, F as writeClaudeCodeConfig, r as readCcgConfig, b as initI18n, a as i18n, s as showMainMenu, i as init, G as configMcp, H as version } from './shared/ccg-workflow.
|
|
4
|
+
import { B as diagnoseMcpConfig, C as isWindows, D as readClaudeCodeConfig, E as fixWindowsMcpConfig, F as writeClaudeCodeConfig, r as readCcgConfig, b as initI18n, a as i18n, s as showMainMenu, i as init, G as configMcp, H as version } from './shared/ccg-workflow.DpOmP1et.mjs';
|
|
5
5
|
import 'inquirer';
|
|
6
6
|
import 'ora';
|
|
7
7
|
import 'node:child_process';
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as changeLanguage, z as checkForUpdates, A as compareVersions, d as createDefaultConfig, e as createDefaultRouting, g as getCcgDir, f as getConfigPath, x as getCurrentVersion, y as getLatestVersion, j as getWorkflowById, h as getWorkflowConfigs, a as i18n, i as init, b as initI18n, l as installAceTool, m as installAceToolRs, n as installCodexMode, k as installWorkflows, t as migrateToV1_4_0, v as needsMigration, r as readCcgConfig, s as showMainMenu, q as uninstallAceTool, o as uninstallCodexMode, p as uninstallWorkflows, u as update, w as writeCcgConfig } from './shared/ccg-workflow.
|
|
1
|
+
export { c as changeLanguage, z as checkForUpdates, A as compareVersions, d as createDefaultConfig, e as createDefaultRouting, g as getCcgDir, f as getConfigPath, x as getCurrentVersion, y as getLatestVersion, j as getWorkflowById, h as getWorkflowConfigs, a as i18n, i as init, b as initI18n, l as installAceTool, m as installAceToolRs, n as installCodexMode, k as installWorkflows, t as migrateToV1_4_0, v as needsMigration, r as readCcgConfig, s as showMainMenu, q as uninstallAceTool, o as uninstallCodexMode, p as uninstallWorkflows, u as update, w as writeCcgConfig } from './shared/ccg-workflow.DpOmP1et.mjs';
|
|
2
2
|
import 'ansis';
|
|
3
3
|
import 'inquirer';
|
|
4
4
|
import 'ora';
|
|
@@ -10,7 +10,7 @@ import fs from 'fs-extra';
|
|
|
10
10
|
import { parse, stringify } from 'smol-toml';
|
|
11
11
|
import i18next from 'i18next';
|
|
12
12
|
|
|
13
|
-
const version = "3.1.
|
|
13
|
+
const version = "3.1.5";
|
|
14
14
|
|
|
15
15
|
function cmd(id, order, category, name, nameEn, description, descriptionEn, cmdOverride) {
|
|
16
16
|
return {
|
|
@@ -887,7 +887,7 @@ async function removeFastContextPrompt() {
|
|
|
887
887
|
await removeFromFile(join(homedir(), ".gemini", "GEMINI.md"));
|
|
888
888
|
}
|
|
889
889
|
|
|
890
|
-
const EXPECTED_BINARY_VERSION = "5.11.
|
|
890
|
+
const EXPECTED_BINARY_VERSION = "5.11.1";
|
|
891
891
|
const GITHUB_REPO = "fengshao1227/ccg-workflow";
|
|
892
892
|
const RELEASE_TAG = "preset";
|
|
893
893
|
const BINARY_SOURCES = [
|
package/package.json
CHANGED
|
@@ -16,6 +16,19 @@ from pathlib import Path
|
|
|
16
16
|
from datetime import datetime
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
# Terminal task statuses — matched case-insensitively after trim. Covers common
|
|
20
|
+
# synonyms the model may write (done/finished/closed/...) so a finished task is
|
|
21
|
+
# never misjudged as still active. Canonical write value is "completed".
|
|
22
|
+
_TERMINAL_STATUSES = frozenset({
|
|
23
|
+
"completed", "complete", "done", "finished", "finish",
|
|
24
|
+
"archived", "archive", "cancelled", "canceled", "closed", "resolved", "abandoned",
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _is_terminal_status(status):
|
|
29
|
+
return str(status or "").strip().lower() in _TERMINAL_STATUSES
|
|
30
|
+
|
|
31
|
+
|
|
19
32
|
def find_project_root():
|
|
20
33
|
"""Walk up to find .ccg/ or .git/"""
|
|
21
34
|
d = os.environ.get("CODEX_PROJECT_DIR", os.getcwd())
|
|
@@ -43,7 +56,7 @@ def get_active_task(root):
|
|
|
43
56
|
try:
|
|
44
57
|
with open(task_file) as f:
|
|
45
58
|
task = json.load(f)
|
|
46
|
-
if task.get("status")
|
|
59
|
+
if not _is_terminal_status(task.get("status")):
|
|
47
60
|
task["_dir"] = os.path.join(tasks_dir, name)
|
|
48
61
|
task["_name"] = name
|
|
49
62
|
return task
|
|
@@ -177,7 +190,7 @@ def build_guidance(task, progress, root):
|
|
|
177
190
|
parts.append(f"Spec files available: {', '.join(specs)} — read before writing code.")
|
|
178
191
|
|
|
179
192
|
# --- Archive reminder ---
|
|
180
|
-
if phase
|
|
193
|
+
if phase in ("completed", "done", "finished") or _is_terminal_status(task.get("status")):
|
|
181
194
|
parts.append("")
|
|
182
195
|
parts.append("⛔ Task completed. You MUST archive it now:")
|
|
183
196
|
parts.append(f" mkdir -p .ccg/tasks/archive/$(date +%Y-%m) && mv .ccg/tasks/{task['_name']} .ccg/tasks/archive/$(date +%Y-%m)/")
|
|
@@ -155,7 +155,23 @@ Agent role: ${detectedRole}
|
|
|
155
155
|
|
|
156
156
|
if (contextParts.length === 0) process.exit(0);
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
const injected = `<ccg-injected-context>\n${contextParts.join('\n\n')}\n</ccg-injected-context>`;
|
|
159
|
+
|
|
160
|
+
// Agent/Team spawn: rewrite the spawned teammate's OWN prompt via updatedInput.
|
|
161
|
+
// A PreToolUse hook's additionalContext only reaches the CALLING (lead) session,
|
|
162
|
+
// which the not-yet-created subagent can never read. updatedInput replaces the
|
|
163
|
+
// entire tool input, so we spread toolInput to keep subagent_type/name/team_name/
|
|
164
|
+
// model intact and only prepend the spec to `prompt`. The teammate is born with
|
|
165
|
+
// the spec already in its prompt. (Docs: PreToolUse → updatedInput.)
|
|
166
|
+
if (isTeamSpawn && typeof toolInput.prompt === 'string') {
|
|
167
|
+
outputHook('PreToolUse', null, {
|
|
168
|
+
updatedInput: { ...toolInput, prompt: `${injected}\n\n---\n\n${toolInput.prompt}` }
|
|
169
|
+
});
|
|
170
|
+
} else {
|
|
171
|
+
// Bash / codeagent-wrapper: the lead builds the HEREDOC task text itself, so the
|
|
172
|
+
// spec belongs in the lead's context where it can be woven into the command.
|
|
173
|
+
outputHook('PreToolUse', injected);
|
|
174
|
+
}
|
|
159
175
|
} catch {
|
|
160
176
|
process.exit(0);
|
|
161
177
|
}
|
|
@@ -18,6 +18,21 @@ function findProjectRoot(startDir) {
|
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// Terminal task statuses — a task in any of these is no longer active, so the
|
|
22
|
+
// hooks stop injecting its breadcrumb. Matched case-insensitively after trim,
|
|
23
|
+
// covering common synonyms the model may write (done/finished/closed/...) so a
|
|
24
|
+
// committed-and-finished task is never misjudged as still in progress. The
|
|
25
|
+
// canonical write-side value is "completed" (see go.md), but the read side must
|
|
26
|
+
// be forgiving because status is free-text written by the model.
|
|
27
|
+
const TERMINAL_STATUSES = new Set([
|
|
28
|
+
'completed', 'complete', 'done', 'finished', 'finish',
|
|
29
|
+
'archived', 'archive', 'cancelled', 'canceled', 'closed', 'resolved', 'abandoned'
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
function isTerminalStatus(status) {
|
|
33
|
+
return TERMINAL_STATUSES.has(String(status == null ? '' : status).trim().toLowerCase());
|
|
34
|
+
}
|
|
35
|
+
|
|
21
36
|
function getActiveTask(projectRoot) {
|
|
22
37
|
const tasksDir = path.join(projectRoot, '.ccg', 'tasks');
|
|
23
38
|
if (!fs.existsSync(tasksDir)) return null;
|
|
@@ -40,7 +55,7 @@ function getActiveTask(projectRoot) {
|
|
|
40
55
|
if (!fs.existsSync(taskPath)) continue; // stale pointer detection
|
|
41
56
|
const raw = fs.readFileSync(taskPath, 'utf-8');
|
|
42
57
|
const task = JSON.parse(raw);
|
|
43
|
-
if (task.status
|
|
58
|
+
if (!isTerminalStatus(task.status)) {
|
|
44
59
|
return { dir: path.join(tasksDir, dir), ...task, _stale: false };
|
|
45
60
|
}
|
|
46
61
|
} catch { /* skip malformed */ }
|
|
@@ -95,13 +110,20 @@ function getGitInfo(projectRoot) {
|
|
|
95
110
|
} catch { return { branch: 'unknown', dirtyCount: 0 }; }
|
|
96
111
|
}
|
|
97
112
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
113
|
+
// outputHook(event, additionalContext) → inject context into the CALLING session
|
|
114
|
+
// outputHook(event, null, { updatedInput, ... }) → rewrite the tool input before it runs
|
|
115
|
+
// `extra` is merged into hookSpecificOutput, so it can carry updatedInput /
|
|
116
|
+
// permissionDecision / permissionDecisionReason. Pass additionalContext = null
|
|
117
|
+
// to omit it. Back-compatible: existing 2-arg calls behave exactly as before.
|
|
118
|
+
function outputHook(eventName, additionalContext, extra) {
|
|
119
|
+
const hookSpecificOutput = { hookEventName: eventName };
|
|
120
|
+
if (additionalContext != null && additionalContext !== '') {
|
|
121
|
+
hookSpecificOutput.additionalContext = additionalContext;
|
|
122
|
+
}
|
|
123
|
+
if (extra && typeof extra === 'object') {
|
|
124
|
+
Object.assign(hookSpecificOutput, extra);
|
|
125
|
+
}
|
|
126
|
+
console.log(JSON.stringify({ hookSpecificOutput }));
|
|
105
127
|
}
|
|
106
128
|
|
|
107
129
|
function archiveTask(taskDir, projectRoot) {
|
|
@@ -176,6 +198,7 @@ function detectLoop(turns, threshold) {
|
|
|
176
198
|
module.exports = {
|
|
177
199
|
findProjectRoot,
|
|
178
200
|
getActiveTask,
|
|
201
|
+
isTerminalStatus,
|
|
179
202
|
readFileSafe,
|
|
180
203
|
readJsonSafe,
|
|
181
204
|
readContextJsonl,
|