deuk-agent-flow 4.2.7 → 5.0.4

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 (170) hide show
  1. package/CHANGELOG.ko.md +277 -0
  2. package/CHANGELOG.md +794 -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 +60 -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-context-commands.ts +35 -0
  24. package/scripts/cli-init-claude.ts +179 -0
  25. package/scripts/cli-init-commands.ts +99 -0
  26. package/scripts/cli-init-core.ts +89 -0
  27. package/scripts/cli-init-instructions.ts +489 -0
  28. package/scripts/cli-init-logic.ts +46 -0
  29. package/scripts/cli-init-migrate.ts +504 -0
  30. package/scripts/cli-init-workspace.ts +63 -0
  31. package/scripts/cli-locales.ts +85 -0
  32. package/scripts/{cli-prompts.mjs → cli-prompts.ts} +19 -34
  33. package/scripts/{cli-rule-compiler.mjs → cli-rule-compiler.ts} +128 -112
  34. package/scripts/cli-skill-commands.ts +709 -0
  35. package/scripts/{cli-telemetry-commands.mjs → cli-telemetry-commands.ts} +25 -22
  36. package/scripts/cli-ticket-command-shared.ts +4 -0
  37. package/scripts/cli-ticket-commands.ts +3781 -0
  38. package/scripts/cli-ticket-docmeta.ts +103 -0
  39. package/scripts/cli-ticket-document.ts +153 -0
  40. package/scripts/cli-ticket-evidence.ts +312 -0
  41. package/scripts/cli-ticket-home.ts +449 -0
  42. package/scripts/cli-ticket-index.ts +283 -0
  43. package/scripts/{cli-ticket-migration.mjs → cli-ticket-migration.ts} +44 -68
  44. package/scripts/cli-ticket-parser.ts +100 -0
  45. package/scripts/cli-ticket-scan.ts +102 -0
  46. package/scripts/cli-ticket-surface.ts +56 -0
  47. package/scripts/{cli-usage-commands.mjs → cli-usage-commands.ts} +325 -326
  48. package/scripts/cli-utils.ts +1560 -0
  49. package/scripts/cli.ts +695 -0
  50. package/scripts/{lint-md.mjs → lint-md.ts} +32 -42
  51. package/scripts/lint-rules.ts +203 -0
  52. package/scripts/{merge-logic.mjs → merge-logic.ts} +44 -44
  53. package/scripts/oss-prepack.js +32 -0
  54. package/scripts/out/scripts/bundle-vscode-vsix.js +68 -0
  55. package/scripts/out/scripts/bundle-vscode-vsix.js.map +1 -0
  56. package/scripts/out/scripts/cli-args.js +374 -0
  57. package/scripts/out/scripts/cli-args.js.map +1 -0
  58. package/scripts/out/scripts/cli-context-commands.js +31 -0
  59. package/scripts/out/scripts/cli-context-commands.js.map +1 -0
  60. package/scripts/out/scripts/cli-init-claude.js +176 -0
  61. package/scripts/out/scripts/cli-init-claude.js.map +1 -0
  62. package/scripts/out/scripts/cli-init-commands.js +87 -0
  63. package/scripts/out/scripts/cli-init-commands.js.map +1 -0
  64. package/scripts/out/scripts/cli-init-core.js +89 -0
  65. package/scripts/out/scripts/cli-init-core.js.map +1 -0
  66. package/scripts/out/scripts/cli-init-instructions.js +460 -0
  67. package/scripts/out/scripts/cli-init-instructions.js.map +1 -0
  68. package/scripts/out/scripts/cli-init-logic.js +41 -0
  69. package/scripts/out/scripts/cli-init-logic.js.map +1 -0
  70. package/scripts/out/scripts/cli-init-migrate.js +497 -0
  71. package/scripts/out/scripts/cli-init-migrate.js.map +1 -0
  72. package/scripts/out/scripts/cli-init-workspace.js +57 -0
  73. package/scripts/out/scripts/cli-init-workspace.js.map +1 -0
  74. package/scripts/out/scripts/cli-locales.js +83 -0
  75. package/scripts/out/scripts/cli-locales.js.map +1 -0
  76. package/scripts/out/scripts/cli-prompts.js +101 -0
  77. package/scripts/out/scripts/cli-prompts.js.map +1 -0
  78. package/scripts/out/scripts/cli-rule-compiler.js +121 -0
  79. package/scripts/out/scripts/cli-rule-compiler.js.map +1 -0
  80. package/scripts/out/scripts/cli-skill-commands.js +708 -0
  81. package/scripts/out/scripts/cli-skill-commands.js.map +1 -0
  82. package/scripts/out/scripts/cli-telemetry-commands.js +604 -0
  83. package/scripts/out/scripts/cli-telemetry-commands.js.map +1 -0
  84. package/scripts/out/scripts/cli-ticket-command-shared.js +2 -0
  85. package/scripts/out/scripts/cli-ticket-command-shared.js.map +1 -0
  86. package/scripts/out/scripts/cli-ticket-commands.js +3530 -0
  87. package/scripts/out/scripts/cli-ticket-commands.js.map +1 -0
  88. package/scripts/out/scripts/cli-ticket-docmeta.js +90 -0
  89. package/scripts/out/scripts/cli-ticket-docmeta.js.map +1 -0
  90. package/scripts/out/scripts/cli-ticket-document.js +121 -0
  91. package/scripts/out/scripts/cli-ticket-document.js.map +1 -0
  92. package/scripts/out/scripts/cli-ticket-evidence.js +286 -0
  93. package/scripts/out/scripts/cli-ticket-evidence.js.map +1 -0
  94. package/scripts/out/scripts/cli-ticket-home.js +440 -0
  95. package/scripts/out/scripts/cli-ticket-home.js.map +1 -0
  96. package/scripts/out/scripts/cli-ticket-index.js +272 -0
  97. package/scripts/out/scripts/cli-ticket-index.js.map +1 -0
  98. package/scripts/out/scripts/cli-ticket-migration.js +261 -0
  99. package/scripts/out/scripts/cli-ticket-migration.js.map +1 -0
  100. package/scripts/out/scripts/cli-ticket-parser.js +90 -0
  101. package/scripts/out/scripts/cli-ticket-parser.js.map +1 -0
  102. package/scripts/out/scripts/cli-ticket-scan.js +93 -0
  103. package/scripts/out/scripts/cli-ticket-scan.js.map +1 -0
  104. package/scripts/out/scripts/cli-ticket-surface.js +53 -0
  105. package/scripts/out/scripts/cli-ticket-surface.js.map +1 -0
  106. package/scripts/out/scripts/cli-usage-commands.js +310 -0
  107. package/scripts/out/scripts/cli-usage-commands.js.map +1 -0
  108. package/scripts/out/scripts/cli-utils.js +1544 -0
  109. package/scripts/out/scripts/cli-utils.js.map +1 -0
  110. package/scripts/out/scripts/cli.js +696 -0
  111. package/scripts/out/scripts/cli.js.map +1 -0
  112. package/scripts/out/scripts/lint-md.js +238 -0
  113. package/scripts/out/scripts/lint-md.js.map +1 -0
  114. package/scripts/out/scripts/lint-rules.js +193 -0
  115. package/scripts/out/scripts/lint-rules.js.map +1 -0
  116. package/scripts/out/scripts/merge-logic.js +38 -0
  117. package/scripts/out/scripts/merge-logic.js.map +1 -0
  118. package/scripts/out/scripts/plan-parser.js +52 -0
  119. package/scripts/out/scripts/plan-parser.js.map +1 -0
  120. package/scripts/out/scripts/ticket-machine.js +94 -0
  121. package/scripts/out/scripts/ticket-machine.js.map +1 -0
  122. package/scripts/out/scripts/ticket-workflow.js +498 -0
  123. package/scripts/out/scripts/ticket-workflow.js.map +1 -0
  124. package/scripts/{plan-parser.mjs → plan-parser.ts} +53 -53
  125. package/scripts/ticket-machine.ts +99 -0
  126. package/scripts/ticket-workflow.ts +544 -0
  127. package/templates/MODULE_RULE_TEMPLATE.md +11 -11
  128. package/templates/PROJECT_RULE.md +46 -47
  129. package/templates/TICKET_TEMPLATE.ko.md +48 -44
  130. package/templates/TICKET_TEMPLATE.md +48 -44
  131. package/templates/project-memory.md +19 -0
  132. package/templates/project-pilot/CONFORMANCE_GATE_TEMPLATE.md +25 -25
  133. package/templates/project-pilot/DRIFT_CHECKLIST.md +27 -27
  134. package/templates/project-pilot/FLOW_CONTRACT_TEMPLATE.md +26 -26
  135. package/templates/project-pilot/IMPLEMENTATION_MATRIX_TEMPLATE.md +30 -30
  136. package/templates/project-pilot/INTEGRATION_CONTRACT_TEMPLATE.md +26 -26
  137. package/templates/project-pilot/OWNER_MAP_TEMPLATE.md +15 -15
  138. package/templates/project-pilot/PROJECT_PILOT_RULE_TEMPLATE.md +34 -34
  139. package/templates/project-pilot/REFACTOR_CONTRACT_TEMPLATE.md +32 -32
  140. package/templates/project-pilot/REMEDIATION_PLAN_TEMPLATE.md +33 -33
  141. package/templates/rules.d/deukcontext-mcp.md +31 -31
  142. package/templates/rules.d/platform-coexistence.md +29 -29
  143. package/templates/skills/context-recall/SKILL.md +3 -1
  144. package/templates/skills/doc-sync/SKILL.md +111 -0
  145. package/templates/skills/generated-file-guard/SKILL.md +3 -1
  146. package/templates/skills/persona-maid/SKILL.md +65 -0
  147. package/templates/skills/project-pilot/SKILL.md +14 -63
  148. package/templates/skills/role-closer/SKILL.md +27 -0
  149. package/templates/skills/role-implementer/SKILL.md +30 -0
  150. package/templates/skills/role-planner/SKILL.md +29 -0
  151. package/templates/skills/role-qa-verifier/SKILL.md +30 -0
  152. package/templates/skills/safe-refactor/SKILL.md +3 -1
  153. package/templates/skills/ticket-status-surface/SKILL.md +17 -0
  154. package/NOTICE.md +0 -19
  155. package/core-rules/GEMINI.md +0 -7
  156. package/docs/npm-publish-guide.ko.md +0 -70
  157. package/scripts/cli-init-commands.mjs +0 -1759
  158. package/scripts/cli-init-logic.mjs +0 -64
  159. package/scripts/cli-skill-commands.mjs +0 -212
  160. package/scripts/cli-ticket-command-shared.mjs +0 -60
  161. package/scripts/cli-ticket-commands.mjs +0 -2474
  162. package/scripts/cli-ticket-index.mjs +0 -322
  163. package/scripts/cli-ticket-parser.mjs +0 -210
  164. package/scripts/cli-utils.mjs +0 -602
  165. package/scripts/cli.mjs +0 -256
  166. package/scripts/lint-rules.mjs +0 -197
  167. package/scripts/publish-dual-npm.mjs +0 -141
  168. package/scripts/smoke-npm-docker.mjs +0 -102
  169. package/scripts/smoke-npm-local.mjs +0 -110
  170. 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.4",
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,85 @@
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-context-commands.ts",
50
+ "scripts/cli-init-claude.ts",
51
+ "scripts/cli-init-commands.ts",
52
+ "scripts/cli-init-core.ts",
53
+ "scripts/cli-init-instructions.ts",
54
+ "scripts/cli-init-logic.ts",
55
+ "scripts/cli-init-migrate.ts",
56
+ "scripts/cli-init-workspace.ts",
57
+ "scripts/cli-locales.ts",
58
+ "scripts/cli-prompts.ts",
59
+ "scripts/cli-rule-compiler.ts",
60
+ "scripts/cli-skill-commands.ts",
61
+ "scripts/cli-telemetry-commands.ts",
62
+ "scripts/cli-ticket-command-shared.ts",
63
+ "scripts/cli-ticket-commands.ts",
64
+ "scripts/cli-ticket-docmeta.ts",
65
+ "scripts/cli-ticket-document.ts",
66
+ "scripts/cli-ticket-evidence.ts",
67
+ "scripts/cli-ticket-home.ts",
68
+ "scripts/cli-ticket-index.ts",
69
+ "scripts/cli-ticket-migration.ts",
70
+ "scripts/cli-ticket-parser.ts",
71
+ "scripts/cli-ticket-scan.ts",
72
+ "scripts/cli-ticket-surface.ts",
73
+ "scripts/cli-usage-commands.ts",
74
+ "scripts/cli-utils.ts",
75
+ "scripts/lint-md.ts",
76
+ "scripts/lint-rules.ts",
77
+ "scripts/merge-logic.ts",
78
+ "scripts/plan-parser.ts",
79
+ "scripts/ticket-machine.ts",
80
+ "scripts/ticket-workflow.ts",
81
+ "scripts/oss-prepack.js",
82
+ "scripts/out/**/*",
68
83
  "README.md",
69
84
  "README.ko.md",
70
85
  "CHANGELOG.md",
71
86
  "CHANGELOG.ko.md"
72
87
  ],
73
88
  "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"
89
+ "dev:install": "npx tsc -p tsconfig.scripts.json && node scripts/out/scripts/dev-install.js",
90
+ "dev:install:no-bump": "npx tsc -p tsconfig.scripts.json && node scripts/out/scripts/dev-install.js --no-bump",
91
+ "build:vscode": "npx tsc -p tsconfig.scripts.json && node scripts/out/scripts/bundle-vscode-vsix.js",
92
+ "prepack": "node scripts/oss-prepack.js",
93
+ "install:vscode": "node scripts/out/scripts/install-vscode-vsix.js --all",
94
+ "install:vscode:desktop": "node scripts/out/scripts/install-vscode-vsix.js --desktop",
95
+ "install:vscode:server": "node scripts/out/scripts/install-vscode-vsix.js --server",
96
+ "lint:md": "node scripts/out/scripts/lint-md.js",
97
+ "publish": "npm publish --access public",
98
+ "publish:dry": "npm publish --access public --dry-run",
99
+ "smoke:npm:local": "node scripts/out/scripts/smoke-npm-local.js",
100
+ "smoke:npm:docker": "node scripts/out/scripts/smoke-npm-docker.js"
89
101
  },
90
102
  "engines": {
91
- "node": ">=18"
103
+ "node": ">=20"
92
104
  },
93
105
  "bin": {
94
106
  "deuk-agent-flow": "./bin/deuk-agent-flow.js",
@@ -97,10 +109,13 @@
97
109
  "deukagentrule": "./bin/deuk-agent-rule.js"
98
110
  },
99
111
  "devDependencies": {
100
- "commit-and-tag-version": "^12.7.1"
112
+ "typescript": "^6.0.3",
113
+ "@types/node": "^25.9.3"
101
114
  },
102
115
  "dependencies": {
103
116
  "ejs": "^5.0.2",
117
+ "xstate": "^5.32.1",
104
118
  "yaml": "^2.8.3"
105
- }
119
+ },
120
+ "type": "module"
106
121
  }
@@ -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, input: 'y\n', stdio: ['pipe', 'inherit', '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,35 @@
1
+ import { existsSync } from "fs";
2
+ import { dirname, resolve } from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { CliOpts, makePath, resolvePackageRoot } from "./cli-utils.js";
5
+ import { spawnSync } from "child_process";
6
+
7
+ const pkgRoot = resolvePackageRoot({ fromUrl: import.meta.url });
8
+
9
+ function contextBinCandidate() {
10
+ if (process.env.DEUK_AGENT_CONTEXT_BIN) {
11
+ return resolve(process.env.DEUK_AGENT_CONTEXT_BIN);
12
+ }
13
+ return resolve(pkgRoot, "..", "DeukAgentContext", "bin", "deuk-agent-context.js");
14
+ }
15
+
16
+ export function runContextCommand(args = []) {
17
+ const localBin = contextBinCandidate();
18
+ const result = existsSync(localBin)
19
+ ? spawnSync(process.execPath, [localBin, ...args], { stdio: "inherit", cwd: process.cwd() })
20
+ : spawnSync("deuk-agent-context", args, {
21
+ stdio: "inherit",
22
+ cwd: process.cwd(),
23
+ shell: process.platform === "win32",
24
+ });
25
+
26
+ if (result.error) {
27
+ if ((result.error as NodeJS.ErrnoException).code === "ENOENT") {
28
+ throw new Error("deuk-agent-context command not found. Install or link DeukAgentContext, or set DEUK_AGENT_CONTEXT_BIN.");
29
+ }
30
+ throw result.error;
31
+ }
32
+ if (typeof result.status === "number" && result.status !== 0) {
33
+ process.exit(result.status);
34
+ }
35
+ }
@@ -0,0 +1,179 @@
1
+ import { dirname } from "path";
2
+ import { writeFileSync, mkdirSync } from "fs";
3
+ import { createHash } from "crypto";
4
+ import { CliOpts, makePath, resolveUserHome } from "./cli-utils.js";
5
+ import { CLAUDE_SETTINGS_PATH, CLAUDE_USERPROMPT_SUBMIT_COMMAND, CLAUDE_USERPROMPT_SUBMIT_MATCHER, safeReadText } from "./cli-init-core.js";
6
+
7
+ function isManagedClaudePromptHookCommand(value) {
8
+ const command = String(value || "").trim();
9
+ if (!/\bdeuk-agent-flow\b/.test(command)) return false;
10
+ // 현재 정식 커맨드이거나, rules를 포함하는 관리형 커맨드(구버전 포함)
11
+ if (command === CLAUDE_USERPROMPT_SUBMIT_COMMAND) return true;
12
+ if (/\brules\b/.test(command)) return true;
13
+ return false;
14
+ }
15
+
16
+ function isCommandHookItem(item) {
17
+ return Boolean(item && typeof item === "object" && item.type === "command" && typeof item.command === "string");
18
+ }
19
+
20
+ function readClaudeSettings(homeDir = resolveUserHome()) {
21
+ const settingsPath = makePath(homeDir, CLAUDE_SETTINGS_PATH);
22
+ const raw = safeReadText(settingsPath);
23
+ if (!raw.trim()) return { ok: false };
24
+
25
+ try {
26
+ const parsed = JSON.parse(raw);
27
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { ok: false };
28
+ return { ok: true, path: settingsPath, parsed };
29
+ } catch {
30
+ return { ok: false };
31
+ }
32
+ }
33
+
34
+ function writeClaudeSettings(settingsPath, next, dryRun) {
35
+ if (dryRun) return;
36
+ mkdirSync(dirname(settingsPath), { recursive: true });
37
+ writeFileSync(settingsPath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
38
+ }
39
+
40
+ function hashClaudeSettingsState(settings) {
41
+ return createHash("sha256").update(JSON.stringify(settings, null, 2), "utf8").digest("hex");
42
+ }
43
+
44
+ export function upsertClaudeUserPromptSubmitHook({ homeDir = resolveUserHome(), dryRun = false } = {}) {
45
+ const state = readClaudeSettings(homeDir);
46
+ if (!state.ok) return { changed: false };
47
+
48
+ const settings = state.parsed;
49
+ const hooksRoot = (settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks))
50
+ ? { ...settings.hooks }
51
+ : {};
52
+
53
+ const rawEntries = Array.isArray(hooksRoot.UserPromptSubmit) ? [...hooksRoot.UserPromptSubmit] : [];
54
+ const nextEntries = [];
55
+ let changed = false;
56
+ let hadManagedPromptSubmitHook = false;
57
+ let inserted = false;
58
+ let matcherIndex = -1;
59
+
60
+ for (const rawEntry of rawEntries) {
61
+ if (!rawEntry || typeof rawEntry !== "object") {
62
+ nextEntries.push(rawEntry);
63
+ continue;
64
+ }
65
+
66
+ const entry = { ...rawEntry };
67
+ const hooks = Array.isArray(entry.hooks) ? entry.hooks : [];
68
+ const keptHooks = [];
69
+ for (const hook of hooks) {
70
+ if (isCommandHookItem(hook) && isManagedClaudePromptHookCommand(hook.command)) {
71
+ hadManagedPromptSubmitHook = true;
72
+ changed = true;
73
+ if (!inserted) {
74
+ keptHooks.push({ type: "command", command: CLAUDE_USERPROMPT_SUBMIT_COMMAND });
75
+ inserted = true;
76
+ }
77
+ continue;
78
+ }
79
+ keptHooks.push(hook);
80
+ }
81
+
82
+ entry.hooks = keptHooks;
83
+ if (entry.matcher === CLAUDE_USERPROMPT_SUBMIT_MATCHER && matcherIndex === -1) {
84
+ matcherIndex = nextEntries.length;
85
+ }
86
+ nextEntries.push(entry);
87
+ }
88
+
89
+ const hasCanonicalNow = nextEntries.some((entry) => {
90
+ if (!entry || typeof entry !== "object" || !Array.isArray(entry.hooks)) return false;
91
+ return entry.hooks.some((hook) => isCommandHookItem(hook) && hook.command === CLAUDE_USERPROMPT_SUBMIT_COMMAND);
92
+ });
93
+
94
+ if (!hasCanonicalNow) {
95
+ if (nextEntries.length === 0) {
96
+ nextEntries.push({
97
+ matcher: CLAUDE_USERPROMPT_SUBMIT_MATCHER,
98
+ hooks: [{ type: "command", command: CLAUDE_USERPROMPT_SUBMIT_COMMAND }]
99
+ });
100
+ changed = true;
101
+ } else {
102
+ const targetIndex = matcherIndex >= 0 ? matcherIndex : 0;
103
+ const target = nextEntries[targetIndex];
104
+ const targetHooks = Array.isArray(target.hooks) ? target.hooks : [];
105
+ target.hooks = [...targetHooks, { type: "command", command: CLAUDE_USERPROMPT_SUBMIT_COMMAND }];
106
+ if (typeof target.matcher !== "string") target.matcher = CLAUDE_USERPROMPT_SUBMIT_MATCHER;
107
+ changed = true;
108
+ }
109
+ }
110
+
111
+ const filteredEntries = nextEntries.filter((entry) => {
112
+ if (!entry || typeof entry !== "object") return true;
113
+ if (!Array.isArray(entry.hooks)) return true;
114
+ return entry.hooks.length > 0;
115
+ });
116
+ const finalEntries = filteredEntries.length > 0 ? filteredEntries : nextEntries;
117
+
118
+ const nextSettings = {
119
+ ...settings,
120
+ hooks: {
121
+ ...(hooksRoot),
122
+ UserPromptSubmit: finalEntries
123
+ }
124
+ };
125
+
126
+ const beforeHash = hashClaudeSettingsState(settings);
127
+ const afterHash = hashClaudeSettingsState(nextSettings);
128
+ if (beforeHash === afterHash) return { changed: false, hash: beforeHash };
129
+
130
+ writeClaudeSettings(state.path, nextSettings, dryRun);
131
+ return { changed: true, hash: afterHash };
132
+ }
133
+
134
+ export function removeClaudeUserPromptSubmitHook({ homeDir = resolveUserHome(), dryRun = false } = {}) {
135
+ const state = readClaudeSettings(homeDir);
136
+ if (!state.ok) return { changed: false };
137
+
138
+ const settings = state.parsed;
139
+ const hooksRoot = settings.hooks;
140
+ if (!hooksRoot || typeof hooksRoot !== "object" || Array.isArray(hooksRoot)) return { changed: false };
141
+
142
+ const current = Array.isArray(hooksRoot.UserPromptSubmit) ? hooksRoot.UserPromptSubmit : [];
143
+ const next = [];
144
+ let changed = false;
145
+
146
+ for (const entry of current) {
147
+ if (!entry || typeof entry !== "object") {
148
+ next.push(entry);
149
+ continue;
150
+ }
151
+ const hooks = Array.isArray(entry.hooks) ? entry.hooks : [];
152
+ const nextHooks = hooks.filter((hook) => !(isCommandHookItem(hook) && isManagedClaudePromptHookCommand(hook.command)));
153
+ if (nextHooks.length !== hooks.length) changed = true;
154
+ if (nextHooks.length > 0) {
155
+ next.push({ ...entry, hooks: nextHooks });
156
+ } else {
157
+ changed = true;
158
+ }
159
+ }
160
+
161
+ if (!changed) return { changed: false };
162
+
163
+ const nextHooksRoot = { ...hooksRoot };
164
+ if (next.length > 0) {
165
+ nextHooksRoot.UserPromptSubmit = next;
166
+ } else {
167
+ delete nextHooksRoot.UserPromptSubmit;
168
+ }
169
+
170
+ const nextSettings = {
171
+ ...settings,
172
+ hooks: nextHooksRoot
173
+ };
174
+ const beforeHash = hashClaudeSettingsState(settings);
175
+ const afterHash = hashClaudeSettingsState(nextSettings);
176
+ if (beforeHash === afterHash) return { changed: false, hash: beforeHash };
177
+ writeClaudeSettings(state.path, nextSettings, dryRun);
178
+ return { changed: true, hash: afterHash };
179
+ }