deuk-agent-flow 4.2.7 → 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.
Files changed (80) hide show
  1. package/CHANGELOG.ko.md +259 -0
  2. package/CHANGELOG.md +769 -124
  3. package/LICENSE +0 -0
  4. package/README.ko.md +97 -17
  5. package/README.md +108 -25
  6. package/bin/deuk-agent-flow.js +11 -13
  7. package/bin/deuk-agent-rule.js +1 -1
  8. package/bundled/README.md +3 -0
  9. package/bundled/deuk-agent-flow.vsix +0 -0
  10. package/core-rules/AGENTS.md +30 -120
  11. package/docs/architecture.ko.md +155 -2
  12. package/docs/architecture.md +155 -2
  13. package/docs/assets/agentflow-panel-skills.png +0 -0
  14. package/docs/assets/agentflow-panel.png +0 -0
  15. package/docs/how-it-works.ko.md +109 -52
  16. package/docs/how-it-works.md +128 -71
  17. package/docs/principles.ko.md +68 -68
  18. package/docs/principles.md +68 -68
  19. package/docs/usage-guide.ko.md +251 -212
  20. package/package.json +42 -45
  21. package/scripts/bundle-vscode-vsix.ts +67 -0
  22. package/scripts/{cli-args.mjs → cli-args.ts} +49 -12
  23. package/scripts/cli-init-commands.ts +99 -0
  24. package/scripts/cli-init-logic.ts +46 -0
  25. package/scripts/{cli-prompts.mjs → cli-prompts.ts} +19 -34
  26. package/scripts/{cli-rule-compiler.mjs → cli-rule-compiler.ts} +128 -112
  27. package/scripts/cli-skill-commands.ts +707 -0
  28. package/scripts/{cli-telemetry-commands.mjs → cli-telemetry-commands.ts} +25 -22
  29. package/scripts/cli-ticket-command-shared.ts +4 -0
  30. package/scripts/cli-ticket-commands.ts +3723 -0
  31. package/scripts/cli-ticket-index.ts +283 -0
  32. package/scripts/{cli-ticket-migration.mjs → cli-ticket-migration.ts} +44 -68
  33. package/scripts/cli-ticket-parser.ts +100 -0
  34. package/scripts/{cli-usage-commands.mjs → cli-usage-commands.ts} +325 -326
  35. package/scripts/cli-utils.ts +1560 -0
  36. package/scripts/cli.ts +695 -0
  37. package/scripts/{lint-md.mjs → lint-md.ts} +32 -42
  38. package/scripts/lint-rules.ts +203 -0
  39. package/scripts/{merge-logic.mjs → merge-logic.ts} +44 -44
  40. package/scripts/{plan-parser.mjs → plan-parser.ts} +53 -53
  41. package/templates/MODULE_RULE_TEMPLATE.md +11 -11
  42. package/templates/PROJECT_RULE.md +46 -47
  43. package/templates/TICKET_TEMPLATE.ko.md +48 -44
  44. package/templates/TICKET_TEMPLATE.md +48 -44
  45. package/templates/project-memory.md +19 -0
  46. package/templates/project-pilot/CONFORMANCE_GATE_TEMPLATE.md +25 -25
  47. package/templates/project-pilot/DRIFT_CHECKLIST.md +27 -27
  48. package/templates/project-pilot/FLOW_CONTRACT_TEMPLATE.md +26 -26
  49. package/templates/project-pilot/IMPLEMENTATION_MATRIX_TEMPLATE.md +30 -30
  50. package/templates/project-pilot/INTEGRATION_CONTRACT_TEMPLATE.md +26 -26
  51. package/templates/project-pilot/OWNER_MAP_TEMPLATE.md +15 -15
  52. package/templates/project-pilot/PROJECT_PILOT_RULE_TEMPLATE.md +34 -34
  53. package/templates/project-pilot/REFACTOR_CONTRACT_TEMPLATE.md +32 -32
  54. package/templates/project-pilot/REMEDIATION_PLAN_TEMPLATE.md +33 -33
  55. package/templates/rules.d/deukcontext-mcp.md +31 -31
  56. package/templates/rules.d/platform-coexistence.md +29 -29
  57. package/templates/skills/context-recall/SKILL.md +3 -1
  58. package/templates/skills/doc-sync/SKILL.md +111 -0
  59. package/templates/skills/generated-file-guard/SKILL.md +3 -1
  60. package/templates/skills/persona-maid/SKILL.md +65 -0
  61. package/templates/skills/project-pilot/SKILL.md +14 -63
  62. package/templates/skills/safe-refactor/SKILL.md +3 -1
  63. package/templates/skills/ticket-status-surface/SKILL.md +17 -0
  64. package/NOTICE.md +0 -19
  65. package/core-rules/GEMINI.md +0 -7
  66. package/docs/npm-publish-guide.ko.md +0 -70
  67. package/scripts/cli-init-commands.mjs +0 -1759
  68. package/scripts/cli-init-logic.mjs +0 -64
  69. package/scripts/cli-skill-commands.mjs +0 -212
  70. package/scripts/cli-ticket-command-shared.mjs +0 -60
  71. package/scripts/cli-ticket-commands.mjs +0 -2474
  72. package/scripts/cli-ticket-index.mjs +0 -322
  73. package/scripts/cli-ticket-parser.mjs +0 -210
  74. package/scripts/cli-utils.mjs +0 -602
  75. package/scripts/cli.mjs +0 -256
  76. package/scripts/lint-rules.mjs +0 -197
  77. package/scripts/publish-dual-npm.mjs +0 -141
  78. package/scripts/smoke-npm-docker.mjs +0 -102
  79. package/scripts/smoke-npm-local.mjs +0 -110
  80. 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": "4.2.7",
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",
@@ -22,73 +22,67 @@
22
22
  "license": "Apache-2.0",
23
23
  "repository": {
24
24
  "type": "git",
25
- "url": "https://github.com/joygram/DeukAgentFlow.git"
25
+ "url": "git+https://github.com/joygram/DeukAgentFlow.git"
26
26
  },
27
27
  "bugs": {
28
28
  "url": "https://github.com/joygram/DeukAgentFlow/issues"
29
29
  },
30
30
  "homepage": "https://github.com/joygram/DeukAgentFlow#readme",
31
31
  "files": [
32
+ "LICENSE",
32
33
  "bin/**/*",
33
34
  "core-rules/**/*",
34
35
  "docs/architecture.md",
35
36
  "docs/architecture.ko.md",
37
+ "docs/badges/**/*",
36
38
  "docs/how-it-works.md",
37
39
  "docs/how-it-works.ko.md",
38
40
  "docs/principles.md",
39
41
  "docs/principles.ko.md",
40
- "docs/npm-publish-guide.ko.md",
41
42
  "docs/usage-guide.ko.md",
42
43
  "docs/assets/**/*",
43
- "NOTICE.md",
44
+ "bundled/**/*",
44
45
  "templates/**/*",
45
- "scripts/cli.mjs",
46
- "scripts/cli-args.mjs",
47
- "scripts/cli-usage-commands.mjs",
48
- "scripts/cli-init-commands.mjs",
49
- "scripts/cli-init-logic.mjs",
50
- "scripts/cli-prompts.mjs",
51
- "scripts/cli-rule-compiler.mjs",
52
- "scripts/cli-skill-commands.mjs",
53
- "scripts/cli-telemetry-commands.mjs",
54
- "scripts/cli-ticket-command-shared.mjs",
55
- "scripts/cli-ticket-commands.mjs",
56
- "scripts/cli-ticket-index.mjs",
57
- "scripts/cli-ticket-migration.mjs",
58
- "scripts/cli-ticket-parser.mjs",
59
- "scripts/cli-utils.mjs",
60
- "scripts/lint-md.mjs",
61
- "scripts/lint-rules.mjs",
62
- "scripts/merge-logic.mjs",
63
- "scripts/plan-parser.mjs",
64
- "scripts/publish-dual-npm.mjs",
65
- "scripts/smoke-npm-local.mjs",
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
- "bump": "commit-and-tag-version",
75
- "bump:patch": "commit-and-tag-version -r patch",
76
- "bump:minor": "commit-and-tag-version -r minor",
77
- "bump:major": "commit-and-tag-version -r major",
78
- "merge:dry": "node scripts/cli.mjs merge --dry-run --cwd .. --agents skip",
79
- "lint:md": "node scripts/lint-md.mjs",
80
- "test": "node --test scripts/tests/*.test.mjs",
81
- "sync:oss": "node scripts/sync-oss.mjs",
82
- "publish": "node scripts/publish-dual-npm.mjs",
83
- "publish:dry": "node scripts/publish-dual-npm.mjs --dry-run",
84
- "smoke:npm:local": "node scripts/smoke-npm-local.mjs",
85
- "smoke:npm:docker": "node scripts/smoke-npm-docker.mjs",
86
- "publish:bootstrap": "node scripts/publish-dual-npm.mjs --alias-only",
87
- "publish:bootstrap:dry": "node scripts/publish-dual-npm.mjs --alias-only --dry-run",
88
- "badge:downloads": "node scripts/update-download-badge.mjs"
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"
89
83
  },
90
84
  "engines": {
91
- "node": ">=18"
85
+ "node": ">=20"
92
86
  },
93
87
  "bin": {
94
88
  "deuk-agent-flow": "./bin/deuk-agent-flow.js",
@@ -97,10 +91,13 @@
97
91
  "deukagentrule": "./bin/deuk-agent-rule.js"
98
92
  },
99
93
  "devDependencies": {
100
- "commit-and-tag-version": "^12.7.1"
94
+ "typescript": "^6.0.3",
95
+ "@types/node": "^25.9.3"
101
96
  },
102
97
  "dependencies": {
103
98
  "ejs": "^5.0.2",
99
+ "xstate": "^5.32.1",
104
100
  "yaml": "^2.8.3"
105
- }
101
+ },
102
+ "type": "module"
106
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 out = { cwd: process.cwd(), dryRun: false, nonInteractive: false, limit: 20 };
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") out.cwd = argv[++i];
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 === "--topic" || a === "--id") out.topic = argv[++i];
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 = Number(argv[++i]);
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") out.cwd = argv[++i];
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 { join } from "path";
4
- import { WORKSPACE_KINDS, AGENT_TOOLS, resolveDocsLanguage, normalizeWorkflowMode, WORKFLOW_MODE_EXECUTE } from "./cli-utils.mjs";
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(join(cwd, name)));
43
+ return names.some((name) => existsSync(makePath(cwd, name)));
44
44
  }
45
45
 
46
- export function inferInitDefaults(cwd, opts = {}) {
47
- const packageJsonPath = join(cwd, "package.json");
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
- console.log("\nDeukAgentFlow init let's configure your workspace.\n");
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, "What kind of workspace is this?", WORKSPACE_KINDS);
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("\n Workspace Kind: " + workspaceKind);
109
- console.log(" Technical Surface: " + opts.stack);
110
- console.log(" AI Clients: " + (opts.agentTools.join(", ") || "all supported clients"));
111
- console.log(" Docs Language: " + opts.docsLanguage);
112
- console.log(" Workflow Mode: " + opts.workflowMode);
113
- console.log(" Share Tickets: " + (opts.shareTickets ? "Yes (Shared)" : "No (Private)"));
114
- console.log(" Deuk AgentContext MCP: hidden during init");
115
- console.log(" External Sync: " + (opts.remoteSync ? "Enabled" : "Disabled"));
116
- if (opts.remoteSync) console.log(" Sync URL: " + opts.pipelineUrl);
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
  }