deuk-agent-flow 4.0.37 → 5.0.2
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/CHANGELOG.ko.md +282 -0
- package/CHANGELOG.md +788 -120
- package/LICENSE +0 -0
- package/README.ko.md +97 -17
- package/README.md +108 -25
- package/bin/deuk-agent-flow.js +11 -13
- package/bin/deuk-agent-rule.js +1 -1
- package/bundled/README.md +3 -0
- package/bundled/deuk-agent-flow.vsix +0 -0
- package/core-rules/AGENTS.md +30 -118
- package/docs/architecture.ko.md +155 -2
- package/docs/architecture.md +155 -2
- package/docs/assets/agentflow-panel-skills.png +0 -0
- package/docs/assets/agentflow-panel.png +0 -0
- package/docs/how-it-works.ko.md +109 -52
- package/docs/how-it-works.md +128 -71
- package/docs/principles.ko.md +68 -68
- package/docs/principles.md +68 -68
- package/docs/usage-guide.ko.md +251 -212
- package/package.json +41 -34
- package/scripts/bundle-vscode-vsix.ts +67 -0
- package/scripts/{cli-args.mjs → cli-args.ts} +49 -12
- package/scripts/cli-init-commands.ts +99 -0
- package/scripts/cli-init-logic.ts +46 -0
- package/scripts/{cli-prompts.mjs → cli-prompts.ts} +19 -34
- package/scripts/{cli-rule-compiler.mjs → cli-rule-compiler.ts} +128 -112
- package/scripts/cli-skill-commands.ts +707 -0
- package/scripts/{cli-telemetry-commands.mjs → cli-telemetry-commands.ts} +25 -22
- package/scripts/cli-ticket-command-shared.ts +4 -0
- package/scripts/cli-ticket-commands.ts +3723 -0
- package/scripts/cli-ticket-index.ts +283 -0
- package/scripts/{cli-ticket-migration.mjs → cli-ticket-migration.ts} +44 -68
- package/scripts/cli-ticket-parser.ts +100 -0
- package/scripts/{cli-usage-commands.mjs → cli-usage-commands.ts} +325 -326
- package/scripts/cli-utils.ts +1560 -0
- package/scripts/cli.ts +695 -0
- package/scripts/{lint-md.mjs → lint-md.ts} +32 -42
- package/scripts/lint-rules.ts +203 -0
- package/scripts/{merge-logic.mjs → merge-logic.ts} +44 -44
- package/scripts/{plan-parser.mjs → plan-parser.ts} +53 -53
- package/templates/MODULE_RULE_TEMPLATE.md +11 -11
- package/templates/PROJECT_RULE.md +46 -47
- package/templates/TICKET_TEMPLATE.ko.md +48 -44
- package/templates/TICKET_TEMPLATE.md +48 -44
- package/templates/project-memory.md +19 -0
- package/templates/project-pilot/CONFORMANCE_GATE_TEMPLATE.md +25 -23
- package/templates/project-pilot/DRIFT_CHECKLIST.md +27 -19
- package/templates/project-pilot/FLOW_CONTRACT_TEMPLATE.md +26 -26
- package/templates/project-pilot/IMPLEMENTATION_MATRIX_TEMPLATE.md +30 -30
- package/templates/project-pilot/INTEGRATION_CONTRACT_TEMPLATE.md +26 -26
- package/templates/project-pilot/OWNER_MAP_TEMPLATE.md +15 -15
- package/templates/project-pilot/PROJECT_PILOT_RULE_TEMPLATE.md +34 -34
- package/templates/project-pilot/REFACTOR_CONTRACT_TEMPLATE.md +32 -32
- package/templates/project-pilot/REMEDIATION_PLAN_TEMPLATE.md +33 -33
- package/templates/rules.d/deukcontext-mcp.md +31 -31
- package/templates/rules.d/platform-coexistence.md +29 -29
- package/templates/skills/context-recall/SKILL.md +3 -1
- package/templates/skills/doc-sync/SKILL.md +111 -0
- package/templates/skills/generated-file-guard/SKILL.md +3 -1
- package/templates/skills/persona-maid/SKILL.md +65 -0
- package/templates/skills/project-pilot/SKILL.md +13 -52
- package/templates/skills/safe-refactor/SKILL.md +3 -1
- package/templates/skills/ticket-status-surface/SKILL.md +17 -0
- package/core-rules/GEMINI.md +0 -7
- package/docs/badges/npm-downloads.json +0 -8
- package/scripts/cli-init-commands.mjs +0 -1750
- package/scripts/cli-init-logic.mjs +0 -64
- package/scripts/cli-skill-commands.mjs +0 -201
- package/scripts/cli-ticket-commands.mjs +0 -2427
- package/scripts/cli-ticket-index.mjs +0 -298
- package/scripts/cli-ticket-parser.mjs +0 -209
- package/scripts/cli-utils.mjs +0 -602
- package/scripts/cli.mjs +0 -256
- package/scripts/lint-rules.mjs +0 -196
- package/scripts/publish-dual-npm.mjs +0 -141
- package/scripts/smoke-npm-docker.mjs +0 -102
- package/scripts/smoke-npm-local.mjs +0 -109
- package/scripts/update-download-badge.mjs +0 -103
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deuk-agent-flow",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Keep AI coding work from vanishing between chats: repo-owned tickets, scope, verification, and memory for AGENTS.md-powered agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents-md",
|
|
@@ -39,49 +39,50 @@
|
|
|
39
39
|
"docs/how-it-works.ko.md",
|
|
40
40
|
"docs/principles.md",
|
|
41
41
|
"docs/principles.ko.md",
|
|
42
|
-
"docs/npm-publish-guide.ko.md",
|
|
43
42
|
"docs/usage-guide.ko.md",
|
|
44
43
|
"docs/assets/**/*",
|
|
44
|
+
"bundled/**/*",
|
|
45
45
|
"templates/**/*",
|
|
46
|
-
"scripts/
|
|
47
|
-
"scripts/cli
|
|
48
|
-
"scripts/cli-
|
|
49
|
-
"scripts/cli-
|
|
50
|
-
"scripts/cli-init-
|
|
51
|
-
"scripts/cli-
|
|
52
|
-
"scripts/cli-
|
|
53
|
-
"scripts/cli-
|
|
54
|
-
"scripts/cli-
|
|
55
|
-
"scripts/cli-
|
|
56
|
-
"scripts/cli-ticket-
|
|
57
|
-
"scripts/cli-ticket-
|
|
58
|
-
"scripts/cli-ticket-
|
|
59
|
-
"scripts/cli-
|
|
60
|
-
"scripts/
|
|
61
|
-
"scripts/
|
|
62
|
-
"scripts/
|
|
63
|
-
"scripts/
|
|
64
|
-
"scripts/
|
|
65
|
-
"scripts/
|
|
66
|
-
"scripts/smoke-npm-docker.mjs",
|
|
67
|
-
"scripts/update-download-badge.mjs",
|
|
46
|
+
"scripts/bundle-vscode-vsix.ts",
|
|
47
|
+
"scripts/cli.ts",
|
|
48
|
+
"scripts/cli-args.ts",
|
|
49
|
+
"scripts/cli-usage-commands.ts",
|
|
50
|
+
"scripts/cli-init-commands.ts",
|
|
51
|
+
"scripts/cli-init-logic.ts",
|
|
52
|
+
"scripts/cli-prompts.ts",
|
|
53
|
+
"scripts/cli-rule-compiler.ts",
|
|
54
|
+
"scripts/cli-skill-commands.ts",
|
|
55
|
+
"scripts/cli-telemetry-commands.ts",
|
|
56
|
+
"scripts/cli-ticket-command-shared.ts",
|
|
57
|
+
"scripts/cli-ticket-commands.ts",
|
|
58
|
+
"scripts/cli-ticket-index.ts",
|
|
59
|
+
"scripts/cli-ticket-migration.ts",
|
|
60
|
+
"scripts/cli-ticket-parser.ts",
|
|
61
|
+
"scripts/cli-utils.ts",
|
|
62
|
+
"scripts/lint-md.ts",
|
|
63
|
+
"scripts/lint-rules.ts",
|
|
64
|
+
"scripts/merge-logic.ts",
|
|
65
|
+
"scripts/plan-parser.ts",
|
|
68
66
|
"README.md",
|
|
69
67
|
"README.ko.md",
|
|
70
68
|
"CHANGELOG.md",
|
|
71
69
|
"CHANGELOG.ko.md"
|
|
72
70
|
],
|
|
73
71
|
"scripts": {
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
72
|
+
"dev:install": "npx tsc -p tsconfig.scripts.json && node scripts/out/scripts/dev-install.js",
|
|
73
|
+
"dev:install:no-bump": "npx tsc -p tsconfig.scripts.json && node scripts/out/scripts/dev-install.js --no-bump",
|
|
74
|
+
"build:vscode": "npx tsc -p tsconfig.scripts.json && node scripts/out/scripts/bundle-vscode-vsix.js",
|
|
75
|
+
"install:vscode": "node scripts/out/scripts/install-vscode-vsix.js --all",
|
|
76
|
+
"install:vscode:desktop": "node scripts/out/scripts/install-vscode-vsix.js --desktop",
|
|
77
|
+
"install:vscode:server": "node scripts/out/scripts/install-vscode-vsix.js --server",
|
|
78
|
+
"lint:md": "node scripts/out/scripts/lint-md.js",
|
|
79
|
+
"publish": "npm publish --access public",
|
|
80
|
+
"publish:dry": "npm publish --access public --dry-run",
|
|
81
|
+
"smoke:npm:local": "node scripts/out/scripts/smoke-npm-local.js",
|
|
82
|
+
"smoke:npm:docker": "node scripts/out/scripts/smoke-npm-docker.js"
|
|
82
83
|
},
|
|
83
84
|
"engines": {
|
|
84
|
-
"node": ">=
|
|
85
|
+
"node": ">=20"
|
|
85
86
|
},
|
|
86
87
|
"bin": {
|
|
87
88
|
"deuk-agent-flow": "./bin/deuk-agent-flow.js",
|
|
@@ -89,8 +90,14 @@
|
|
|
89
90
|
"deuk-agent-rule": "./bin/deuk-agent-rule.js",
|
|
90
91
|
"deukagentrule": "./bin/deuk-agent-rule.js"
|
|
91
92
|
},
|
|
93
|
+
"devDependencies": {
|
|
94
|
+
"typescript": "^6.0.3",
|
|
95
|
+
"@types/node": "^25.9.3"
|
|
96
|
+
},
|
|
92
97
|
"dependencies": {
|
|
93
98
|
"ejs": "^5.0.2",
|
|
99
|
+
"xstate": "^5.32.1",
|
|
94
100
|
"yaml": "^2.8.3"
|
|
95
|
-
}
|
|
101
|
+
},
|
|
102
|
+
"type": "module"
|
|
96
103
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Packs vscode-extension into bundled/deuk-agent-flow.vsix for the npm tarball.
|
|
4
|
+
* Temporarily sets the extension package.json version to the root package version.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import { CliOpts, makePath, resolvePackageRoot } from "./cli-utils.js";
|
|
10
|
+
|
|
11
|
+
const root = resolvePackageRoot({ fromUrl: import.meta.url });
|
|
12
|
+
const extDir = makePath(root, 'vscode-extension');
|
|
13
|
+
const extPkgPath = makePath(extDir, 'package.json');
|
|
14
|
+
const bundledDir = makePath(root, 'bundled');
|
|
15
|
+
const vsixPath = makePath(bundledDir, 'deuk-agent-flow.vsix');
|
|
16
|
+
const installAfterBundle = process.argv.includes('--install') || process.env.DEUK_AGENT_FLOW_INSTALL_VSIX === '1';
|
|
17
|
+
const skipCompile = process.argv.includes('--skip-compile') || process.env.DEUK_AGENT_FLOW_SKIP_COMPILE === '1';
|
|
18
|
+
|
|
19
|
+
const rootPkg = JSON.parse(fs.readFileSync(makePath(root, 'package.json'), 'utf8'));
|
|
20
|
+
const extPkg = JSON.parse(fs.readFileSync(extPkgPath, 'utf8'));
|
|
21
|
+
const savedPrepublish = extPkg.scripts?.['vscode:prepublish'];
|
|
22
|
+
extPkg.version = rootPkg.version;
|
|
23
|
+
extPkg.scripts = {
|
|
24
|
+
...extPkg.scripts,
|
|
25
|
+
'vscode:prepublish': 'tsc -p ./',
|
|
26
|
+
};
|
|
27
|
+
fs.writeFileSync(extPkgPath, JSON.stringify(extPkg, null, 2) + '\n');
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(bundledDir)) fs.mkdirSync(bundledDir, { recursive: true });
|
|
31
|
+
execSync('npm install --no-audit --no-fund --loglevel=silent', { cwd: extDir, stdio: 'inherit', shell: true } as any);
|
|
32
|
+
if (!skipCompile) {
|
|
33
|
+
execSync('npm run compile', { cwd: extDir, stdio: 'inherit' } as any);
|
|
34
|
+
}
|
|
35
|
+
const vsceBin = makePath(extDir, 'node_modules', '.bin', process.platform === 'win32' ? 'vsce.cmd' : 'vsce');
|
|
36
|
+
if (!fs.existsSync(vsceBin)) {
|
|
37
|
+
console.error('[deuk-agent-flow] @vscode/vsce missing. Run npm install in vscode-extension.');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const q = (s) => (s.includes(' ') ? `"${s.replace(/"/g, '\\"')}"` : s);
|
|
41
|
+
execSync(`${q(vsceBin)} package --no-dependencies --out ${q(vsixPath)}`, { cwd: extDir, stdio: 'inherit', shell: true } as any);
|
|
42
|
+
if (!fs.existsSync(vsixPath)) {
|
|
43
|
+
console.error('[deuk-agent-flow] bundle-vscode: expected VSIX missing:', vsixPath);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
console.log('[deuk-agent-flow] bundled VS Code extension:', vsixPath);
|
|
47
|
+
if (installAfterBundle) {
|
|
48
|
+
if (process.platform === 'win32') {
|
|
49
|
+
// Windows node 컨텍스트: WSL bash 스크립트로 WSL 파일시스템에 직접 설치
|
|
50
|
+
const wslScript = execSync(`wsl.exe wslpath -u "${root.replace(/\\/g, '/')}/scripts/install-vsix-wsl.sh"`, { encoding: 'utf8', shell: true } as any).toString().trim();
|
|
51
|
+
execSync(`wsl.exe bash "${wslScript}"`, { stdio: 'inherit', shell: true } as any);
|
|
52
|
+
} else {
|
|
53
|
+
execSync('node scripts/out/scripts/install-vscode-vsix.js --all', { cwd: root, stdio: 'inherit', shell: true } as any);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} finally {
|
|
57
|
+
// #version-sync: keep the extension package.json version pinned to the root version
|
|
58
|
+
// (do NOT restore savedVer) so vscode-extension/package.json auto-tracks the root on
|
|
59
|
+
// every bundle instead of drifting back to a stale value. Only prepublish is temporary.
|
|
60
|
+
extPkg.version = rootPkg.version;
|
|
61
|
+
if (savedPrepublish === undefined) {
|
|
62
|
+
delete extPkg.scripts['vscode:prepublish'];
|
|
63
|
+
} else {
|
|
64
|
+
extPkg.scripts['vscode:prepublish'] = savedPrepublish;
|
|
65
|
+
}
|
|
66
|
+
fs.writeFileSync(extPkgPath, JSON.stringify(extPkg, null, 2) + '\n');
|
|
67
|
+
}
|
|
@@ -1,12 +1,35 @@
|
|
|
1
|
+
// Parse a phase token from --phase/--to. Accepts "2", "phase2", "phase 2", "end", "start".
|
|
2
|
+
// Returns NaN when no digit is present and not a known named state so callers can detect
|
|
3
|
+
// a malformed value (e.g. "--to phaseX") instead of silently advancing. (#635)
|
|
4
|
+
const NAMED_PHASE_MAP = { start: 1, phase1: 1, phase2: 2, phase3: 3, phase4: 4, end: 4 };
|
|
5
|
+
function parsePhaseToken(raw) {
|
|
6
|
+
const s = String(raw ?? "").trim().toLowerCase();
|
|
7
|
+
if (NAMED_PHASE_MAP[s] !== undefined) return NAMED_PHASE_MAP[s];
|
|
8
|
+
const m = s.match(/\d+/);
|
|
9
|
+
return m ? Number(m[0]) : NaN;
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
export function parseTicketArgs(argv) {
|
|
2
|
-
const
|
|
13
|
+
const invocationCwd = process.cwd();
|
|
14
|
+
const out: Record<string, any> = { cwd: invocationCwd, invocationCwd, dryRun: false, nonInteractive: false, limit: 20 };
|
|
3
15
|
for (let i = 0; i < argv.length; i++) {
|
|
4
16
|
const a = argv[i];
|
|
5
|
-
if (a === "--cwd")
|
|
17
|
+
if (a === "--cwd") {
|
|
18
|
+
out.cwd = argv[++i];
|
|
19
|
+
out.cwdExplicit = true;
|
|
20
|
+
}
|
|
6
21
|
else if (a === "--dry-run") out.dryRun = true;
|
|
22
|
+
else if (a === "--apply") out.apply = true;
|
|
7
23
|
else if (a === "--non-interactive") out.nonInteractive = true;
|
|
8
|
-
else if (a === "--
|
|
24
|
+
else if (a === "--id" || a === "--ticket") out.ticketId = argv[++i];
|
|
25
|
+
else if (a === "--title") out.title = argv[++i];
|
|
26
|
+
else if (a === "--platform") out.platform = argv[++i];
|
|
9
27
|
else if (a === "--group") out.group = argv[++i];
|
|
28
|
+
else if (a === "--parent" || a === "--main-ticket") out.parent = argv[++i];
|
|
29
|
+
else if (a === "--workspace") out.workspace = argv[++i];
|
|
30
|
+
else if (a === "--allow-cwd-target") {
|
|
31
|
+
throw new Error("ticket commands no longer support cwd-based target selection. Use --workspace <id>.");
|
|
32
|
+
}
|
|
10
33
|
else if (a === "--project") out.project = argv[++i];
|
|
11
34
|
else if (a === "--content") out.content = argv[++i];
|
|
12
35
|
else if (a === "--content-file") out.contentFile = argv[++i];
|
|
@@ -19,7 +42,10 @@ export function parseTicketArgs(argv) {
|
|
|
19
42
|
else if (a === "--latest" || a === "-l") out.latest = true;
|
|
20
43
|
else if (a === "--path-only") out.pathOnly = true;
|
|
21
44
|
else if (a === "--print-content") out.printContent = true;
|
|
45
|
+
else if (a === "--with-total") out.withTotal = true;
|
|
46
|
+
else if (a === "--takeover") out.takeover = true;
|
|
22
47
|
else if (a === "--all") out.all = true;
|
|
48
|
+
else if (a === "--active") out.active = true;
|
|
23
49
|
else if (a === "--status") out.status = argv[++i];
|
|
24
50
|
else if (a === "--archived") out.archived = true;
|
|
25
51
|
else if (a === "--priority") out.priority = argv[++i];
|
|
@@ -33,13 +59,12 @@ export function parseTicketArgs(argv) {
|
|
|
33
59
|
else if (a === "--docs-language") out.docsLanguage = argv[++i];
|
|
34
60
|
else if (a === "--workflow") out.workflowMode = argv[++i];
|
|
35
61
|
else if (a === "--approval") out.approval = argv[++i];
|
|
36
|
-
else if (a === "--ticket-started") out.ticketStarted = true;
|
|
37
|
-
else if (a === "--ticket-reviewed") out.ticketReviewed = true;
|
|
38
62
|
else if (a === "--evidence") out.evidence = argv[++i];
|
|
39
63
|
else if (a === "--skip-phase0") out.skipPhase0 = true;
|
|
40
64
|
else if (a === "--summary") out.summary = argv[++i];
|
|
65
|
+
else if (a === "--type") out.ticketType = argv[++i];
|
|
41
66
|
else if (a === "--tags") out.tags = argv[++i];
|
|
42
|
-
else if (a === "--phase") out.phase =
|
|
67
|
+
else if (a === "--phase" || a === "--to") { out.phase = parsePhaseToken(argv[++i]); out.phaseRequested = true; }
|
|
43
68
|
else if (a === "--next") out.next = true;
|
|
44
69
|
else if (a === "--handoff") out.handoff = true;
|
|
45
70
|
else if (a === "--reason") out.reason = argv[++i];
|
|
@@ -48,30 +73,41 @@ export function parseTicketArgs(argv) {
|
|
|
48
73
|
else if (a === "--allow-placeholder") out.allowPlaceholder = true;
|
|
49
74
|
else if (a === "--compact") out.compact = true;
|
|
50
75
|
else if (a === "--status-detail") out.statusDetail = true;
|
|
76
|
+
else if (a === "--verbose") out.verbose = true;
|
|
51
77
|
else if (a === "-h" || a === "--help") out.help = true;
|
|
78
|
+
else if (a === "--session-id") out.sessionId = argv[++i];
|
|
79
|
+
else if (!a.startsWith("-")) out.ticketId = out.ticketId || a;
|
|
52
80
|
}
|
|
53
81
|
return out;
|
|
54
82
|
}
|
|
55
83
|
|
|
56
84
|
export function parseArgs(argv) {
|
|
57
|
-
const out = { cwd: process.cwd(), dryRun: false, backup: false };
|
|
85
|
+
const out: Record<string, any> = { cwd: process.cwd(), dryRun: false, backup: false };
|
|
58
86
|
for (let i = 0; i < argv.length; i++) {
|
|
59
87
|
const a = argv[i];
|
|
60
|
-
if (a === "--cwd")
|
|
88
|
+
if (a === "--cwd") {
|
|
89
|
+
out.cwd = argv[++i];
|
|
90
|
+
out.cwdExplicit = true;
|
|
91
|
+
}
|
|
61
92
|
else if (a === "--dry-run") out.dryRun = true;
|
|
62
93
|
else if (a === "--backup") out.backup = true;
|
|
63
94
|
else if (a === "--non-interactive") out.nonInteractive = true;
|
|
64
95
|
else if (a === "--interactive") out.interactive = true;
|
|
65
96
|
else if (a === "--clean") out.clean = true;
|
|
66
97
|
else if (a === "--tag") out.tag = argv[++i];
|
|
98
|
+
else if (a === "--platform") out.platform = argv[++i];
|
|
67
99
|
else if (a === "--marker-begin") out.markerBegin = argv[++i];
|
|
68
100
|
else if (a === "--marker-end") out.markerEnd = argv[++i];
|
|
69
|
-
else if (a === "--agents") out.agents = argv[++i];
|
|
70
101
|
else if (a === "--cursorrules") out.cursorrules = argv[++i];
|
|
71
102
|
else if (a === "--append-if-no-markers") out.appendIfNoMarkers = true;
|
|
72
103
|
else if (a === "--workflow") out.workflowMode = argv[++i];
|
|
73
104
|
else if (a === "--approval") out.approval = argv[++i];
|
|
105
|
+
else if (a === "--workspace") out.workspace = argv[++i];
|
|
106
|
+
else if (a === "--prompt") out.prompt = argv[++i];
|
|
107
|
+
else if (a === "--project") out.project = argv[++i];
|
|
108
|
+
else if (a === "--section") out.section = argv[++i];
|
|
74
109
|
else if (a === "--json") out.json = true;
|
|
110
|
+
else if (a === "--path-only") out.pathOnly = true;
|
|
75
111
|
else if (a === "--remote") out.remote = argv[++i];
|
|
76
112
|
else if (a === "--kind" || a === "--workspace-kind") out.workspaceKind = argv[++i];
|
|
77
113
|
else if (a === "--stack" || a === "--technical-surface") out.stack = argv[++i];
|
|
@@ -80,13 +116,14 @@ export function parseArgs(argv) {
|
|
|
80
116
|
else if (a === "--no-sync") out.sync = false;
|
|
81
117
|
else if (a === "--docs-language") out.docsLanguage = argv[++i];
|
|
82
118
|
else if (a === "--compact") out.compact = true;
|
|
119
|
+
else if (a === "--session-id") out.sessionId = argv[++i];
|
|
83
120
|
else if (a === "-h" || a === "--help") out.help = true;
|
|
84
121
|
}
|
|
85
122
|
return out;
|
|
86
123
|
}
|
|
87
124
|
|
|
88
125
|
export function parseSkillArgs(argv) {
|
|
89
|
-
const out = { cwd: process.cwd(), dryRun: false, nonInteractive: false };
|
|
126
|
+
const out: Record<string, any> = { cwd: process.cwd(), dryRun: false, nonInteractive: false };
|
|
90
127
|
for (let i = 0; i < argv.length; i++) {
|
|
91
128
|
const a = argv[i];
|
|
92
129
|
if (a === "--cwd") out.cwd = argv[++i];
|
|
@@ -100,7 +137,7 @@ export function parseSkillArgs(argv) {
|
|
|
100
137
|
}
|
|
101
138
|
|
|
102
139
|
export function parseUsageArgs(argv) {
|
|
103
|
-
const out = {
|
|
140
|
+
const out: Record<string, any> = {
|
|
104
141
|
cwd: process.cwd(),
|
|
105
142
|
json: false,
|
|
106
143
|
platform: "",
|
|
@@ -137,7 +174,7 @@ export function parseUsageArgs(argv) {
|
|
|
137
174
|
}
|
|
138
175
|
|
|
139
176
|
export function parseTelemetryArgs(argv) {
|
|
140
|
-
const out = {
|
|
177
|
+
const out: Record<string, any> = {
|
|
141
178
|
cwd: process.cwd(),
|
|
142
179
|
tokens: 0,
|
|
143
180
|
tdw: 0,
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { removeClaudeUserPromptSubmitHook, upsertClaudeUserPromptSubmitHook } from "./cli-init-claude.js";
|
|
2
|
+
import { CANONICAL_INIT_CLEANUP_PATHS, CLAUDE_SETTINGS_PATH, CLAUDE_USERPROMPT_SUBMIT_COMMAND, CLAUDE_USERPROMPT_SUBMIT_MATCHER, GLOBAL_POINTER_BEGIN_PREFIX, GLOBAL_POINTER_END, MANAGED_BLOCK_BEGIN, MANAGED_BLOCK_END, hashManagedContent, mergeManagedBlock, safeReadText, splitManagedBlock, wrapManagedBlock } from "./cli-init-core.js";
|
|
3
|
+
// #638: updateSiblingWorkspaceRegistry (B / ~/.agent-flow) retired — no longer called.
|
|
4
|
+
import { dirname, basename, relative, resolve } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, readdirSync, unlinkSync, rmSync, renameSync, chmodSync, statSync, symlinkSync } from "fs";
|
|
7
|
+
import { createHash } from "crypto";
|
|
8
|
+
|
|
9
|
+
import { ensureTicketDirAndGitignore } from "./cli-init-logic.js";
|
|
10
|
+
import { normalizeTicketPaths } from "./cli-ticket-migration.js";
|
|
11
|
+
import { rebuildTicketIndexFromTopicFilesIfNeeded } from "./cli-ticket-parser.js";
|
|
12
|
+
import { readTicketIndexJson, syncActiveTicketId, writeTicketIndexJson } from "./cli-ticket-index.js";
|
|
13
|
+
import { ensureWorkspaceMigrated } from "./cli-ticket-home.js";
|
|
14
|
+
import { syncExposedNativeSkills } from "./cli-skill-commands.js";
|
|
15
|
+
|
|
16
|
+
// #623/#622-D: init-time migration of a workspace's tickets to ~/.deuk-agent/tickets/
|
|
17
|
+
// and sync of native skills from the SSOT. Idempotent and best-effort — a failure
|
|
18
|
+
// here must not abort workspace setup.
|
|
19
|
+
function ensureTicketsMigratedOnInit(cwd, dryRun) {
|
|
20
|
+
try {
|
|
21
|
+
// #645: init is the ONLY entry point allowed to mint a brand-new workspace
|
|
22
|
+
// (marker + workspace.json). allowCreate gates that — without it, reconcile
|
|
23
|
+
// self-heals an already-registered workspace but never registers a new one.
|
|
24
|
+
ensureWorkspaceMigrated(cwd, { dryRun, allowCreate: true });
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.warn(`[INIT] ticket home migration skipped for ${basename(cwd)}: ${err.message}`);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
syncExposedNativeSkills(cwd, { dryRun });
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.warn(`[INIT] skill sync skipped for ${basename(cwd)}: ${err.message}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
import { cliText, resolveCliLocale } from "./cli-locales.js";
|
|
35
|
+
|
|
36
|
+
import { DEUK_ROOT_DIR, CliOpts, SPOKE_REGISTRY, TICKET_INDEX_FILENAME, TICKET_LIST_FILENAME, TICKET_SUBDIR, buildWorkspaceAliases, discoverAllWorkspaces, getWorkspaceInitConfigPath, isWorkflowExecute, loadInitConfig, makePath, normalizeRegistryPath, normalizeTicketGroup, normalizeWorkflowMode, parseFrontMatter, pruneRuleModules, resolveDocsLanguage, resolvePackageRoot, resolveUserHome, resolveWorkflowMode, stringifyFrontMatter, toPosixPath, toRepoRelativePath, writeFileLF } from "./cli-utils.js";
|
|
37
|
+
|
|
38
|
+
import { removeFragmentedLocalAgentSurfaces, removeLegacyWorkspaceConfig, removeLocalSkillCopies, removeNestedLegacyWorkspaceConfigs, removeRuntimeTemplateCopies } from "./cli-init-migrate.js";
|
|
39
|
+
import { buildGlobalClaudeInstructions, buildGlobalCodexInstructions, canonicalizeGeneratedCommandReferences, generateSpokeContent, mergeManagedRuleContent, removeDuplicateRuleCopies, runGlobalAgentInstructionSync, runSingleWorkspaceMaintenance, syncCodexMcpEndpointFromWorkspace } from "./cli-init-instructions.js";
|
|
40
|
+
|
|
41
|
+
export function runWorkspaceMaintenance(cwd = process.cwd(), dryRun = false, bundleRoot = resolvePackageRoot({ fromUrl: import.meta.url }), opts: CliOpts = {}) {
|
|
42
|
+
dryRun = Boolean(dryRun);
|
|
43
|
+
if (!cwd || !existsSync(cwd)) return { removed: 0 };
|
|
44
|
+
if (opts.silent) {
|
|
45
|
+
const originalLog = console.log;
|
|
46
|
+
console.log = () => {};
|
|
47
|
+
try {
|
|
48
|
+
return runWorkspaceMaintenance(cwd, dryRun, bundleRoot, { ...opts, silent: false });
|
|
49
|
+
} finally {
|
|
50
|
+
console.log = originalLog;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const ignoreDirs = [];
|
|
54
|
+
const recursive = Boolean(opts.recursive);
|
|
55
|
+
|
|
56
|
+
const homeDir = resolveUserHome(opts);
|
|
57
|
+
const saved = loadInitConfig(cwd, { ...opts, homeDir }) || {};
|
|
58
|
+
const maintenanceOptions = { ...saved, ...opts };
|
|
59
|
+
runGlobalAgentInstructionSync(dryRun, bundleRoot, homeDir);
|
|
60
|
+
const codexMcpResult = syncCodexMcpEndpointFromWorkspace(cwd, { dryRun, homeDir });
|
|
61
|
+
const claudeSettingsResult = upsertClaudeUserPromptSubmitHook({ dryRun, homeDir });
|
|
62
|
+
// #638: the ~/.agent-flow sibling registry (B) is retired. Workspace candidates now
|
|
63
|
+
// come solely from ~/.deuk-agent/tickets/{uuid}/workspace.json (written by reconcile),
|
|
64
|
+
// so we no longer write B here — it only went stale and spawned ambiguous ghosts.
|
|
65
|
+
removeNestedLegacyWorkspaceConfigs(cwd, dryRun, ignoreDirs);
|
|
66
|
+
removeRuntimeTemplateCopies(cwd, dryRun);
|
|
67
|
+
removeLocalSkillCopies(cwd, dryRun);
|
|
68
|
+
removeDuplicateRuleCopies(cwd, dryRun);
|
|
69
|
+
canonicalizeGeneratedCommandReferences(cwd, bundleRoot, dryRun);
|
|
70
|
+
const removed = removeFragmentedLocalAgentSurfaces(cwd, dryRun);
|
|
71
|
+
|
|
72
|
+
const submodules = recursive ? discoverAllWorkspaces(cwd, ignoreDirs) : [cwd];
|
|
73
|
+
if (recursive && !submodules.includes(cwd)) submodules.push(cwd);
|
|
74
|
+
|
|
75
|
+
for (const subCwd of submodules) {
|
|
76
|
+
try {
|
|
77
|
+
runSingleWorkspaceMaintenance(subCwd, dryRun, bundleRoot, maintenanceOptions);
|
|
78
|
+
// #623: init is one of the two migration entry points (the other is ticket
|
|
79
|
+
// command entry). Move this workspace's tickets to home as part of setup.
|
|
80
|
+
ensureTicketsMigratedOnInit(subCwd, dryRun);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.error(`[ERROR] Failed to initialize workspace ${basename(subCwd)}: ${(err as Error).message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
removed,
|
|
88
|
+
codexMcpChanged: Boolean(codexMcpResult?.changed),
|
|
89
|
+
codexMcpUrl: codexMcpResult?.url || null,
|
|
90
|
+
claudeSettingsChanged: Boolean(claudeSettingsResult?.changed),
|
|
91
|
+
claudeSettingsHash: claudeSettingsResult?.hash || null
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function formatInitCompletionMessage(cwd, dryRun = false) {
|
|
96
|
+
const label = dryRun ? "Dry-run complete" : "Init complete";
|
|
97
|
+
return `[DONE] ${label} for ${basename(cwd)}. Rules and pointers are ready. \`이슈분석 티켓\`이라고 해보세요.`;
|
|
98
|
+
}
|
|
99
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { existsSync, appendFileSync, writeFileSync, mkdirSync, readFileSync } from "fs";
|
|
2
|
+
import { LEGACY_DEUK, DEUK_ROOT_DIR, CliOpts, isInsideGitWorkTree, makePath } from "./cli-utils.js";
|
|
3
|
+
|
|
4
|
+
const GITIGNORE_AGENT_MARKER = "# deuk-agent-flow: agent hub directory (local, not committed by default)";
|
|
5
|
+
const LEGACY_GITIGNORE_AGENT_MARKER = "# deuk-agent-rule: agent hub directory (local, not committed by default)";
|
|
6
|
+
|
|
7
|
+
function hasExactGitignoreLine(content, line) {
|
|
8
|
+
return content
|
|
9
|
+
.split(/\r?\n/)
|
|
10
|
+
.map(s => s.trim())
|
|
11
|
+
.includes(line);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function removeExactGitignoreLines(content, lines) {
|
|
15
|
+
const remove = new Set(lines);
|
|
16
|
+
return content
|
|
17
|
+
.split(/\r?\n/)
|
|
18
|
+
.filter(line => !remove.has(line.trim()))
|
|
19
|
+
.join("\n");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function ensureTicketDirAndGitignore(opts) {
|
|
23
|
+
const gitignorePath = makePath(opts.cwd, ".gitignore");
|
|
24
|
+
|
|
25
|
+
if (opts.dryRun) return;
|
|
26
|
+
|
|
27
|
+
if (!isInsideGitWorkTree(opts.cwd)) return;
|
|
28
|
+
|
|
29
|
+
let gi = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf8") : "";
|
|
30
|
+
|
|
31
|
+
// Remove all legacy/old deuk ignore lines. The new .deuk-workspace-id file is committed
|
|
32
|
+
// (not ignored), so no new ignore entry is added. Legacy dir names come from the single
|
|
33
|
+
// LEGACY_DEUK.dirs SSOT — for each we strip both "<dir>/" and "<dir>/tickets/". DEUK_ROOT_DIR
|
|
34
|
+
// ("<.deuk>/") is also stripped in case an older flow ever added it.
|
|
35
|
+
const legacyIgnoreLines = LEGACY_DEUK.dirs.flatMap(dir => [`${dir}/`, `${dir}/tickets/`]);
|
|
36
|
+
const cleanedGi = removeExactGitignoreLines(gi, [
|
|
37
|
+
...legacyIgnoreLines,
|
|
38
|
+
GITIGNORE_AGENT_MARKER,
|
|
39
|
+
LEGACY_GITIGNORE_AGENT_MARKER,
|
|
40
|
+
`${DEUK_ROOT_DIR}/`, // new .deuk/ root line (if ever added)
|
|
41
|
+
]);
|
|
42
|
+
if (cleanedGi !== gi) {
|
|
43
|
+
writeFileSync(gitignorePath, cleanedGi.replace(/\n*$/, "\n"), "utf8");
|
|
44
|
+
console.log("[INIT] Removed legacy .deuk-agent/ .gitignore entries");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createInterface } from "readline";
|
|
2
2
|
import { existsSync, readFileSync } from "fs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { AGENT_TOOLS, CliOpts, WORKFLOW_MODE_EXECUTE, WORKSPACE_KINDS, makePath, normalizeWorkflowMode, resolveDocsLanguage } from "./cli-utils.js";
|
|
4
|
+
import { cliText } from "./cli-locales.js";
|
|
5
5
|
|
|
6
|
-
export async function ask(rl, question) {
|
|
7
|
-
return new Promise((resolve) => rl.question(question, resolve));
|
|
6
|
+
export async function ask(rl, question): Promise<string> {
|
|
7
|
+
return new Promise<string>((resolve) => rl.question(question, resolve));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export async function askYesNo(question, defaultYes = true) {
|
|
@@ -40,11 +40,11 @@ export async function selectMany(rl, prompt, choices) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function hasAny(cwd, names) {
|
|
43
|
-
return names.some((name) => existsSync(
|
|
43
|
+
return names.some((name) => existsSync(makePath(cwd, name)));
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export function inferInitDefaults(cwd, opts = {}) {
|
|
47
|
-
const packageJsonPath =
|
|
46
|
+
export function inferInitDefaults(cwd, opts: CliOpts = {}) {
|
|
47
|
+
const packageJsonPath = makePath(cwd, "package.json");
|
|
48
48
|
const packageJson = existsSync(packageJsonPath)
|
|
49
49
|
? readFileSync(packageJsonPath, "utf8")
|
|
50
50
|
: "";
|
|
@@ -74,26 +74,12 @@ export function inferInitDefaults(cwd, opts = {}) {
|
|
|
74
74
|
export async function runInteractive(opts) {
|
|
75
75
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
76
76
|
try {
|
|
77
|
-
|
|
77
|
+
const locale = resolveDocsLanguage(opts.docsLanguage ?? "auto");
|
|
78
|
+
console.log(`\n${cliText("prompts.init.heading", { locale })}\n`);
|
|
78
79
|
|
|
79
|
-
const workspaceKind = await selectOne(rl, "
|
|
80
|
+
const workspaceKind = await selectOne(rl, cliText("prompts.init.workspaceKind", { locale }), WORKSPACE_KINDS);
|
|
80
81
|
const defaults = inferInitDefaults(opts.cwd, opts);
|
|
81
82
|
|
|
82
|
-
const targetAgents = join(opts.cwd, "AGENTS.md");
|
|
83
|
-
let agentsDefault = "inject";
|
|
84
|
-
if (!existsSync(targetAgents)) {
|
|
85
|
-
agentsDefault = "inject"; // will append markers
|
|
86
|
-
console.log("\n No AGENTS.md found — will create with markers.");
|
|
87
|
-
} else {
|
|
88
|
-
const content = readFileSync(targetAgents, "utf8");
|
|
89
|
-
const hasMarkers = content.includes("deuk-agent-rule:begin") || content.includes("## DeukAgentFlow");
|
|
90
|
-
if (!hasMarkers) {
|
|
91
|
-
agentsDefault = "inject";
|
|
92
|
-
console.log("\n AGENTS.md exists without managed markers — will append a managed block.");
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
opts.agents = opts.agents ?? agentsDefault;
|
|
97
83
|
opts.workspaceKind = workspaceKind;
|
|
98
84
|
opts.kind = workspaceKind;
|
|
99
85
|
opts.stack = defaults.stack;
|
|
@@ -105,16 +91,15 @@ export async function runInteractive(opts) {
|
|
|
105
91
|
opts.remoteSync = defaults.remoteSync;
|
|
106
92
|
opts.pipelineUrl = defaults.pipelineUrl;
|
|
107
93
|
|
|
108
|
-
console.log(
|
|
109
|
-
console.log("
|
|
110
|
-
console.log("
|
|
111
|
-
console.log("
|
|
112
|
-
console.log("
|
|
113
|
-
console.log("
|
|
114
|
-
console.log("
|
|
115
|
-
console.log("
|
|
116
|
-
if (opts.remoteSync) console.log("
|
|
117
|
-
console.log(" AGENTS: " + opts.agents + "\n");
|
|
94
|
+
console.log(`\n ${cliText("prompts.init.summary.workspaceKind", { locale, vars: { value: workspaceKind } })}`);
|
|
95
|
+
console.log(` ${cliText("prompts.init.summary.technicalSurface", { locale, vars: { value: opts.stack } })}`);
|
|
96
|
+
console.log(` ${cliText("prompts.init.summary.aiClients", { locale, vars: { value: opts.agentTools.join(", ") || cliText("prompts.init.summary.allSupportedClients", { locale }) } })}`);
|
|
97
|
+
console.log(` ${cliText("prompts.init.summary.docsLanguage", { locale, vars: { value: opts.docsLanguage } })}`);
|
|
98
|
+
console.log(` ${cliText("prompts.init.summary.workflowMode", { locale, vars: { value: opts.workflowMode } })}`);
|
|
99
|
+
console.log(` ${cliText("prompts.init.summary.shareTickets", { locale, vars: { value: opts.shareTickets ? cliText("prompts.init.summary.shareYes", { locale }) : cliText("prompts.init.summary.shareNo", { locale }) } })}`);
|
|
100
|
+
console.log(` ${cliText("prompts.init.summary.contextMcp", { locale })}`);
|
|
101
|
+
console.log(` ${cliText("prompts.init.summary.externalSync", { locale, vars: { value: opts.remoteSync ? cliText("prompts.init.summary.enabled", { locale }) : cliText("prompts.init.summary.disabled", { locale }) } })}`);
|
|
102
|
+
if (opts.remoteSync) console.log(` ${cliText("prompts.init.summary.syncUrl", { locale, vars: { value: opts.pipelineUrl } })}\n`);
|
|
118
103
|
} finally {
|
|
119
104
|
rl.close();
|
|
120
105
|
}
|