@di-code/coding-agent 0.1.2 → 0.1.3

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 (47) hide show
  1. package/README.md +489 -143
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +15 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/clipboard-image.d.ts +13 -0
  7. package/dist/core/clipboard-image.d.ts.map +1 -0
  8. package/dist/core/clipboard-image.js +99 -0
  9. package/dist/core/clipboard-image.js.map +1 -0
  10. package/dist/core/image-input.d.ts +13 -0
  11. package/dist/core/image-input.d.ts.map +1 -0
  12. package/dist/core/image-input.js +102 -0
  13. package/dist/core/image-input.js.map +1 -0
  14. package/dist/core/session.d.ts +3 -1
  15. package/dist/core/session.d.ts.map +1 -1
  16. package/dist/core/session.js +12 -4
  17. package/dist/core/session.js.map +1 -1
  18. package/dist/main.d.ts.map +1 -1
  19. package/dist/main.js +8 -2
  20. package/dist/main.js.map +1 -1
  21. package/dist/modes/interactive-components.d.ts +1 -0
  22. package/dist/modes/interactive-components.d.ts.map +1 -1
  23. package/dist/modes/interactive-components.js +44 -20
  24. package/dist/modes/interactive-components.js.map +1 -1
  25. package/dist/modes/interactive-layout.d.ts +2 -1
  26. package/dist/modes/interactive-layout.d.ts.map +1 -1
  27. package/dist/modes/interactive-layout.js +17 -1
  28. package/dist/modes/interactive-layout.js.map +1 -1
  29. package/dist/modes/interactive-state.d.ts +12 -2
  30. package/dist/modes/interactive-state.d.ts.map +1 -1
  31. package/dist/modes/interactive-state.js +49 -2
  32. package/dist/modes/interactive-state.js.map +1 -1
  33. package/dist/modes/interactive.d.ts +8 -0
  34. package/dist/modes/interactive.d.ts.map +1 -1
  35. package/dist/modes/interactive.js +99 -4
  36. package/dist/modes/interactive.js.map +1 -1
  37. package/dist/provider-onboarding.d.ts.map +1 -1
  38. package/dist/provider-onboarding.js +7 -0
  39. package/dist/provider-onboarding.js.map +1 -1
  40. package/dist/startup.d.ts.map +1 -1
  41. package/dist/startup.js +50 -23
  42. package/dist/startup.js.map +1 -1
  43. package/dist/utils/syntax-highlight.d.ts +2 -0
  44. package/dist/utils/syntax-highlight.d.ts.map +1 -0
  45. package/dist/utils/syntax-highlight.js +55 -0
  46. package/dist/utils/syntax-highlight.js.map +1 -0
  47. package/package.json +8 -4
package/dist/cli.d.ts CHANGED
@@ -13,6 +13,7 @@ export type CliCommand = {
13
13
  prompt: string;
14
14
  sessionPath?: string;
15
15
  continueSession?: true;
16
+ imagePaths?: readonly string[];
16
17
  noSkills?: true;
17
18
  noContextFiles?: true;
18
19
  skillPaths?: readonly string[];
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,CAAC;AAE1D,MAAM,MAAM,UAAU,GACnB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9G;IACA,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEL,qBAAa,aAAc,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI3B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAoIhE;AAoBD,MAAM,WAAW,eAAe;IAC/B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,MAAM,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,YAAY,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBpG"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,CAAC;AAE1D,MAAM,MAAM,UAAU,GACnB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9G;IACA,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEL,qBAAa,aAAc,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI3B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAkJhE;AAqBD,MAAM,WAAW,eAAe;IAC/B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,MAAM,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,YAAY,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBpG"}
package/dist/cli.js CHANGED
@@ -37,6 +37,7 @@ export function parseCliArgs(args) {
37
37
  let noContextFiles = false;
38
38
  let projectTrust;
39
39
  const skillPaths = [];
40
+ const imagePaths = [];
40
41
  const promptParts = [];
41
42
  for (let index = 0; index < args.length; index++) {
42
43
  const argument = args[index];
@@ -77,6 +78,15 @@ export function parseCliArgs(args) {
77
78
  index++;
78
79
  continue;
79
80
  }
81
+ if (argument === "--image") {
82
+ const value = args[index + 1];
83
+ if (value === undefined || value.trim().length === 0) {
84
+ throw new CliUsageError("Option --image requires a non-empty value.");
85
+ }
86
+ imagePaths.push(value);
87
+ index++;
88
+ continue;
89
+ }
80
90
  if (argument === "--mode") {
81
91
  const value = args[index + 1];
82
92
  if (value === undefined) {
@@ -114,6 +124,9 @@ export function parseCliArgs(args) {
114
124
  if (continueSession && sessionPath !== undefined) {
115
125
  throw new CliUsageError("Cannot combine --continue with --session.");
116
126
  }
127
+ if (mode === "interactive" && imagePaths.length > 0) {
128
+ throw new CliUsageError("--image is not available in interactive mode.");
129
+ }
117
130
  if (promptParts.length === 0 && mode !== "interactive") {
118
131
  throw new CliUsageError("A prompt is required.");
119
132
  }
@@ -126,6 +139,7 @@ export function parseCliArgs(args) {
126
139
  ...(noSkills ? { noSkills: true } : {}),
127
140
  ...(noContextFiles ? { noContextFiles: true } : {}),
128
141
  ...(skillPaths.length > 0 ? { skillPaths } : {}),
142
+ ...(imagePaths.length > 0 ? { imagePaths } : {}),
129
143
  ...(projectTrust === undefined ? {} : { projectTrust }),
130
144
  };
131
145
  }
@@ -137,6 +151,7 @@ Options:
137
151
  --interactive Start interactive terminal mode
138
152
  --continue, -c Continue the most recently modified session
139
153
  --session <path> Create or resume a JSONL session (relative to the work root)
154
+ --image <path> Attach a local PNG, JPEG, WebP, or GIF image (repeatable)
140
155
  --skill <path> Add a SKILL.md file or skill directory (repeatable)
141
156
  --no-skills Disable all Skill loading
142
157
  --no-context-files Disable AGENTS.md discovery and loading
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["export type OutputMode = \"print\" | \"json\" | \"interactive\";\n\nexport type CliCommand =\n\t| { kind: \"help\" }\n\t| { kind: \"version\" }\n\t| { kind: \"plugin\"; action: \"install\" | \"list\" | \"enable\" | \"disable\" | \"update\" | \"remove\"; argument?: string }\n\t| {\n\t\t\tkind: \"run\";\n\t\t\tmode: OutputMode;\n\t\t\tprompt: string;\n\t\t\tsessionPath?: string;\n\t\t\tcontinueSession?: true;\n\t\t\tnoSkills?: true;\n\t\t\tnoContextFiles?: true;\n\t\t\tskillPaths?: readonly string[];\n\t\t\tprojectTrust?: boolean;\n\t };\n\nexport class CliUsageError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CliUsageError\";\n\t}\n}\n\nexport function parseCliArgs(args: readonly string[]): CliCommand {\n\tif (args[0] === \"plugin\") {\n\t\tconst action = args[1];\n\t\tif (\n\t\t\taction !== \"install\" &&\n\t\t\taction !== \"list\" &&\n\t\t\taction !== \"enable\" &&\n\t\t\taction !== \"disable\" &&\n\t\t\taction !== \"update\" &&\n\t\t\taction !== \"remove\"\n\t\t)\n\t\t\tthrow new CliUsageError(\"Plugin command must be install, list, enable, disable, update, or remove.\");\n\t\tconst argument = args[2];\n\t\tif (\n\t\t\targs.length > 3 ||\n\t\t\t(action !== \"list\" && (argument === undefined || argument.trim() === \"\")) ||\n\t\t\t(action === \"list\" && argument !== undefined)\n\t\t)\n\t\t\tthrow new CliUsageError(\"Plugin command has invalid arguments.\");\n\t\treturn { kind: \"plugin\", action, ...(argument ? { argument } : {}) };\n\t}\n\tconst staticOption = args.find((argument) => [\"-h\", \"--help\", \"-v\", \"--version\"].includes(argument));\n\tif (staticOption !== undefined) {\n\t\tconst kind = staticOption === \"-h\" || staticOption === \"--help\" ? \"help\" : \"version\";\n\t\tif (args.length !== 1) {\n\t\t\tthrow new CliUsageError(`--${kind} must be used on its own.`);\n\t\t}\n\t\treturn { kind };\n\t}\n\n\tlet mode: OutputMode = \"print\";\n\tlet printAlias = false;\n\tlet sessionPath: string | undefined;\n\tlet continueSession = false;\n\tlet noSkills = false;\n\tlet noContextFiles = false;\n\tlet projectTrust: boolean | undefined;\n\tconst skillPaths: string[] = [];\n\tconst promptParts: string[] = [];\n\n\tfor (let index = 0; index < args.length; index++) {\n\t\tconst argument = args[index];\n\t\tif (argument === \"-p\" || argument === \"--print\") {\n\t\t\tprintAlias = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--interactive\") {\n\t\t\tmode = \"interactive\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"-c\" || argument === \"--continue\") {\n\t\t\tcontinueSession = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--no-skills\") {\n\t\t\tnoSkills = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--no-context-files\") {\n\t\t\tnoContextFiles = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--trust-project\" || argument === \"--untrust-project\") {\n\t\t\tconst nextTrust = argument === \"--trust-project\";\n\t\t\tif (projectTrust !== undefined && projectTrust !== nextTrust) {\n\t\t\t\tthrow new CliUsageError(\"Cannot combine --trust-project with --untrust-project.\");\n\t\t\t}\n\t\t\tprojectTrust = nextTrust;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--skill\") {\n\t\t\tconst value = args[index + 1];\n\t\t\tif (value === undefined || value.trim().length === 0) {\n\t\t\t\tthrow new CliUsageError(\"Option --skill requires a non-empty value.\");\n\t\t\t}\n\t\t\tskillPaths.push(value);\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--mode\") {\n\t\t\tconst value = args[index + 1];\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new CliUsageError(\"Option --mode requires a value.\");\n\t\t\t}\n\t\t\tif (value !== \"print\" && value !== \"json\" && value !== \"interactive\") {\n\t\t\t\tthrow new CliUsageError(`Unsupported mode \"${value}\". Expected print, json, or interactive.`);\n\t\t\t}\n\t\t\tmode = value;\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--session\") {\n\t\t\tconst value = args[index + 1];\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new CliUsageError(\"Option --session requires a value.\");\n\t\t\t}\n\t\t\tif (value.trim().length === 0) {\n\t\t\t\tthrow new CliUsageError(\"Option --session requires a non-empty value.\");\n\t\t\t}\n\t\t\tsessionPath = value;\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument?.startsWith(\"-\")) {\n\t\t\tthrow new CliUsageError(`Unknown option \"${argument}\".`);\n\t\t}\n\t\tif (argument !== undefined) {\n\t\t\tpromptParts.push(argument);\n\t\t}\n\t}\n\n\tif (printAlias && mode !== \"print\") {\n\t\tthrow new CliUsageError(`Cannot combine --print with --mode ${mode}.`);\n\t}\n\tif (continueSession && sessionPath !== undefined) {\n\t\tthrow new CliUsageError(\"Cannot combine --continue with --session.\");\n\t}\n\tif (promptParts.length === 0 && mode !== \"interactive\") {\n\t\tthrow new CliUsageError(\"A prompt is required.\");\n\t}\n\n\treturn {\n\t\tkind: \"run\",\n\t\tmode,\n\t\tprompt: promptParts.join(\" \"),\n\t\t...(sessionPath ? { sessionPath } : {}),\n\t\t...(continueSession ? { continueSession: true as const } : {}),\n\t\t...(noSkills ? { noSkills: true as const } : {}),\n\t\t...(noContextFiles ? { noContextFiles: true as const } : {}),\n\t\t...(skillPaths.length > 0 ? { skillPaths } : {}),\n\t\t...(projectTrust === undefined ? {} : { projectTrust }),\n\t};\n}\n\nconst HELP_TEXT = `Usage: di-code [options] <prompt>\n\nOptions:\n -p, --print Print only the final assistant text (default)\n --mode <mode> Output mode: print, json, or interactive\n --interactive Start interactive terminal mode\n --continue, -c Continue the most recently modified session\n --session <path> Create or resume a JSONL session (relative to the work root)\n --skill <path> Add a SKILL.md file or skill directory (repeatable)\n --no-skills Disable all Skill loading\n --no-context-files Disable AGENTS.md discovery and loading\n --trust-project Persist trust for project-local Skill discovery\n --untrust-project Persist denial for project-local Skill discovery\n plugin <action> Install, list, enable, disable, update, or remove a plugin\n -h, --help Show help\n -v, --version Show version\n`;\n\nexport interface CliDependencies {\n\tstdout(text: string): void;\n\tstderr(text: string): void;\n\trun(command: Extract<CliCommand, { kind: \"run\" }>): Promise<number>;\n\tplugin?(command: Extract<CliCommand, { kind: \"plugin\" }>): Promise<number>;\n\treadonly version: string;\n}\n\nexport async function runCli(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n\tlet command: CliCommand;\n\ttry {\n\t\tcommand = parseCliArgs(args);\n\t} catch (cause) {\n\t\tif (cause instanceof CliUsageError) {\n\t\t\tdependencies.stderr(`${cause.message}\\n`);\n\t\t\treturn 1;\n\t\t}\n\t\tthrow cause;\n\t}\n\n\tswitch (command.kind) {\n\t\tcase \"help\":\n\t\t\tdependencies.stdout(HELP_TEXT);\n\t\t\treturn 0;\n\t\tcase \"plugin\":\n\t\t\tif (!dependencies.plugin) throw new Error(\"Plugin command handler is unavailable.\");\n\t\t\treturn dependencies.plugin(command);\n\t\tcase \"version\":\n\t\t\tdependencies.stdout(`${dependencies.version}\\n`);\n\t\t\treturn 0;\n\t\tcase \"run\":\n\t\t\treturn dependencies.run(command);\n\t}\n}\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["export type OutputMode = \"print\" | \"json\" | \"interactive\";\n\nexport type CliCommand =\n\t| { kind: \"help\" }\n\t| { kind: \"version\" }\n\t| { kind: \"plugin\"; action: \"install\" | \"list\" | \"enable\" | \"disable\" | \"update\" | \"remove\"; argument?: string }\n\t| {\n\t\t\tkind: \"run\";\n\t\t\tmode: OutputMode;\n\t\t\tprompt: string;\n\t\t\tsessionPath?: string;\n\t\t\tcontinueSession?: true;\n\t\t\timagePaths?: readonly string[];\n\t\t\tnoSkills?: true;\n\t\t\tnoContextFiles?: true;\n\t\t\tskillPaths?: readonly string[];\n\t\t\tprojectTrust?: boolean;\n\t };\n\nexport class CliUsageError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CliUsageError\";\n\t}\n}\n\nexport function parseCliArgs(args: readonly string[]): CliCommand {\n\tif (args[0] === \"plugin\") {\n\t\tconst action = args[1];\n\t\tif (\n\t\t\taction !== \"install\" &&\n\t\t\taction !== \"list\" &&\n\t\t\taction !== \"enable\" &&\n\t\t\taction !== \"disable\" &&\n\t\t\taction !== \"update\" &&\n\t\t\taction !== \"remove\"\n\t\t)\n\t\t\tthrow new CliUsageError(\"Plugin command must be install, list, enable, disable, update, or remove.\");\n\t\tconst argument = args[2];\n\t\tif (\n\t\t\targs.length > 3 ||\n\t\t\t(action !== \"list\" && (argument === undefined || argument.trim() === \"\")) ||\n\t\t\t(action === \"list\" && argument !== undefined)\n\t\t)\n\t\t\tthrow new CliUsageError(\"Plugin command has invalid arguments.\");\n\t\treturn { kind: \"plugin\", action, ...(argument ? { argument } : {}) };\n\t}\n\tconst staticOption = args.find((argument) => [\"-h\", \"--help\", \"-v\", \"--version\"].includes(argument));\n\tif (staticOption !== undefined) {\n\t\tconst kind = staticOption === \"-h\" || staticOption === \"--help\" ? \"help\" : \"version\";\n\t\tif (args.length !== 1) {\n\t\t\tthrow new CliUsageError(`--${kind} must be used on its own.`);\n\t\t}\n\t\treturn { kind };\n\t}\n\n\tlet mode: OutputMode = \"print\";\n\tlet printAlias = false;\n\tlet sessionPath: string | undefined;\n\tlet continueSession = false;\n\tlet noSkills = false;\n\tlet noContextFiles = false;\n\tlet projectTrust: boolean | undefined;\n\tconst skillPaths: string[] = [];\n\tconst imagePaths: string[] = [];\n\tconst promptParts: string[] = [];\n\n\tfor (let index = 0; index < args.length; index++) {\n\t\tconst argument = args[index];\n\t\tif (argument === \"-p\" || argument === \"--print\") {\n\t\t\tprintAlias = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--interactive\") {\n\t\t\tmode = \"interactive\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"-c\" || argument === \"--continue\") {\n\t\t\tcontinueSession = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--no-skills\") {\n\t\t\tnoSkills = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--no-context-files\") {\n\t\t\tnoContextFiles = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--trust-project\" || argument === \"--untrust-project\") {\n\t\t\tconst nextTrust = argument === \"--trust-project\";\n\t\t\tif (projectTrust !== undefined && projectTrust !== nextTrust) {\n\t\t\t\tthrow new CliUsageError(\"Cannot combine --trust-project with --untrust-project.\");\n\t\t\t}\n\t\t\tprojectTrust = nextTrust;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--skill\") {\n\t\t\tconst value = args[index + 1];\n\t\t\tif (value === undefined || value.trim().length === 0) {\n\t\t\t\tthrow new CliUsageError(\"Option --skill requires a non-empty value.\");\n\t\t\t}\n\t\t\tskillPaths.push(value);\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--image\") {\n\t\t\tconst value = args[index + 1];\n\t\t\tif (value === undefined || value.trim().length === 0) {\n\t\t\t\tthrow new CliUsageError(\"Option --image requires a non-empty value.\");\n\t\t\t}\n\t\t\timagePaths.push(value);\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--mode\") {\n\t\t\tconst value = args[index + 1];\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new CliUsageError(\"Option --mode requires a value.\");\n\t\t\t}\n\t\t\tif (value !== \"print\" && value !== \"json\" && value !== \"interactive\") {\n\t\t\t\tthrow new CliUsageError(`Unsupported mode \"${value}\". Expected print, json, or interactive.`);\n\t\t\t}\n\t\t\tmode = value;\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument === \"--session\") {\n\t\t\tconst value = args[index + 1];\n\t\t\tif (value === undefined) {\n\t\t\t\tthrow new CliUsageError(\"Option --session requires a value.\");\n\t\t\t}\n\t\t\tif (value.trim().length === 0) {\n\t\t\t\tthrow new CliUsageError(\"Option --session requires a non-empty value.\");\n\t\t\t}\n\t\t\tsessionPath = value;\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (argument?.startsWith(\"-\")) {\n\t\t\tthrow new CliUsageError(`Unknown option \"${argument}\".`);\n\t\t}\n\t\tif (argument !== undefined) {\n\t\t\tpromptParts.push(argument);\n\t\t}\n\t}\n\n\tif (printAlias && mode !== \"print\") {\n\t\tthrow new CliUsageError(`Cannot combine --print with --mode ${mode}.`);\n\t}\n\tif (continueSession && sessionPath !== undefined) {\n\t\tthrow new CliUsageError(\"Cannot combine --continue with --session.\");\n\t}\n\tif (mode === \"interactive\" && imagePaths.length > 0) {\n\t\tthrow new CliUsageError(\"--image is not available in interactive mode.\");\n\t}\n\tif (promptParts.length === 0 && mode !== \"interactive\") {\n\t\tthrow new CliUsageError(\"A prompt is required.\");\n\t}\n\n\treturn {\n\t\tkind: \"run\",\n\t\tmode,\n\t\tprompt: promptParts.join(\" \"),\n\t\t...(sessionPath ? { sessionPath } : {}),\n\t\t...(continueSession ? { continueSession: true as const } : {}),\n\t\t...(noSkills ? { noSkills: true as const } : {}),\n\t\t...(noContextFiles ? { noContextFiles: true as const } : {}),\n\t\t...(skillPaths.length > 0 ? { skillPaths } : {}),\n\t\t...(imagePaths.length > 0 ? { imagePaths } : {}),\n\t\t...(projectTrust === undefined ? {} : { projectTrust }),\n\t};\n}\n\nconst HELP_TEXT = `Usage: di-code [options] <prompt>\n\nOptions:\n -p, --print Print only the final assistant text (default)\n --mode <mode> Output mode: print, json, or interactive\n --interactive Start interactive terminal mode\n --continue, -c Continue the most recently modified session\n --session <path> Create or resume a JSONL session (relative to the work root)\n --image <path> Attach a local PNG, JPEG, WebP, or GIF image (repeatable)\n --skill <path> Add a SKILL.md file or skill directory (repeatable)\n --no-skills Disable all Skill loading\n --no-context-files Disable AGENTS.md discovery and loading\n --trust-project Persist trust for project-local Skill discovery\n --untrust-project Persist denial for project-local Skill discovery\n plugin <action> Install, list, enable, disable, update, or remove a plugin\n -h, --help Show help\n -v, --version Show version\n`;\n\nexport interface CliDependencies {\n\tstdout(text: string): void;\n\tstderr(text: string): void;\n\trun(command: Extract<CliCommand, { kind: \"run\" }>): Promise<number>;\n\tplugin?(command: Extract<CliCommand, { kind: \"plugin\" }>): Promise<number>;\n\treadonly version: string;\n}\n\nexport async function runCli(args: readonly string[], dependencies: CliDependencies): Promise<number> {\n\tlet command: CliCommand;\n\ttry {\n\t\tcommand = parseCliArgs(args);\n\t} catch (cause) {\n\t\tif (cause instanceof CliUsageError) {\n\t\t\tdependencies.stderr(`${cause.message}\\n`);\n\t\t\treturn 1;\n\t\t}\n\t\tthrow cause;\n\t}\n\n\tswitch (command.kind) {\n\t\tcase \"help\":\n\t\t\tdependencies.stdout(HELP_TEXT);\n\t\t\treturn 0;\n\t\tcase \"plugin\":\n\t\t\tif (!dependencies.plugin) throw new Error(\"Plugin command handler is unavailable.\");\n\t\t\treturn dependencies.plugin(command);\n\t\tcase \"version\":\n\t\t\tdependencies.stdout(`${dependencies.version}\\n`);\n\t\t\treturn 0;\n\t\tcase \"run\":\n\t\t\treturn dependencies.run(command);\n\t}\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { ImageContent } from "@di-code/ai";
2
+ /** Reads a native clipboard image when the optional platform package is available. */
3
+ export declare function readClipboardImage(): Promise<ImageContent | null>;
4
+ export declare function clipboardImageDirectory(root: string): string;
5
+ export declare function isClipboardImagePath(path: string, directory: string): boolean;
6
+ /** Removes generated clipboard images that were left by an older process. */
7
+ export declare function cleanupStaleClipboardImages(root: string, maxAgeMs?: number): Promise<void>;
8
+ export declare function removeClipboardImage(path: string, directory: string): Promise<void>;
9
+ /** Writes one clipboard image to a temporary file, matching Pi's editor flow. */
10
+ export declare function writeImageContentToTempFile(image: ImageContent, directory?: string): Promise<string>;
11
+ /** Reads the clipboard and returns the temporary path that should be inserted into the editor. */
12
+ export declare function readClipboardImagePath(directory?: string): Promise<string | null>;
13
+ //# sourceMappingURL=clipboard-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard-image.d.ts","sourceRoot":"","sources":["../../src/core/clipboard-image.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsBhD,sFAAsF;AACtF,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAMvE;AAeD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAG7E;AAED,6EAA6E;AAC7E,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB9G;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzF;AAED,iFAAiF;AACjF,wBAAsB,2BAA2B,CAAC,KAAK,EAAE,YAAY,EAAE,SAAS,SAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5G;AAED,kGAAkG;AAClG,wBAAsB,sBAAsB,CAAC,SAAS,SAAW,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGzF"}
@@ -0,0 +1,99 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readdir, stat, unlink, writeFile } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import { tmpdir } from "node:os";
5
+ import { basename, dirname, join, resolve } from "node:path";
6
+ import { imageContentFromBytes } from "./image-input.js";
7
+ const CLIPBOARD_FILE_PREFIX = "di-code-clipboard-";
8
+ const CLIPBOARD_FILE_PATTERN = new RegExp(`^${CLIPBOARD_FILE_PREFIX}[0-9a-f-]+\\.(?:png|jpg|jpeg|gif|webp)$`, "i");
9
+ const DEFAULT_STALE_AGE_MS = 24 * 60 * 60 * 1000;
10
+ const require = createRequire(import.meta.url);
11
+ function loadClipboardModule() {
12
+ try {
13
+ return require("@mariozechner/clipboard");
14
+ }
15
+ catch {
16
+ return undefined;
17
+ }
18
+ }
19
+ /** Reads a native clipboard image when the optional platform package is available. */
20
+ export async function readClipboardImage() {
21
+ const clipboard = loadClipboardModule();
22
+ if (!clipboard?.hasImage())
23
+ return null;
24
+ const bytes = await clipboard.getImageBinary();
25
+ if (bytes.length === 0)
26
+ return null;
27
+ return imageContentFromBytes(Uint8Array.from(bytes), "clipboard image");
28
+ }
29
+ function extensionForMimeType(mimeType) {
30
+ switch (mimeType.toLowerCase().split(";", 1)[0]) {
31
+ case "image/jpeg":
32
+ return "jpg";
33
+ case "image/gif":
34
+ return "gif";
35
+ case "image/webp":
36
+ return "webp";
37
+ default:
38
+ return "png";
39
+ }
40
+ }
41
+ export function clipboardImageDirectory(root) {
42
+ return join(root, ".di-code", "clipboard");
43
+ }
44
+ export function isClipboardImagePath(path, directory) {
45
+ const absolutePath = resolve(path);
46
+ return dirname(absolutePath) === resolve(directory) && CLIPBOARD_FILE_PATTERN.test(basename(absolutePath));
47
+ }
48
+ /** Removes generated clipboard images that were left by an older process. */
49
+ export async function cleanupStaleClipboardImages(root, maxAgeMs = DEFAULT_STALE_AGE_MS) {
50
+ const directory = clipboardImageDirectory(root);
51
+ let entries;
52
+ try {
53
+ entries = await readdir(directory, { withFileTypes: true });
54
+ }
55
+ catch (cause) {
56
+ if (cause instanceof Error && "code" in cause && cause.code === "ENOENT")
57
+ return;
58
+ throw cause;
59
+ }
60
+ const cutoff = Date.now() - maxAgeMs;
61
+ await Promise.all(entries
62
+ .filter((entry) => entry.isFile() && CLIPBOARD_FILE_PATTERN.test(entry.name))
63
+ .map(async (entry) => {
64
+ const path = join(directory, entry.name);
65
+ try {
66
+ if ((await stat(path)).mtimeMs < cutoff)
67
+ await unlink(path);
68
+ }
69
+ catch (cause) {
70
+ if (!(cause instanceof Error && "code" in cause && cause.code === "ENOENT"))
71
+ throw cause;
72
+ }
73
+ }));
74
+ }
75
+ export async function removeClipboardImage(path, directory) {
76
+ if (!isClipboardImagePath(path, directory))
77
+ return;
78
+ try {
79
+ await unlink(path);
80
+ }
81
+ catch (cause) {
82
+ if (!(cause instanceof Error && "code" in cause && cause.code === "ENOENT"))
83
+ throw cause;
84
+ }
85
+ }
86
+ /** Writes one clipboard image to a temporary file, matching Pi's editor flow. */
87
+ export async function writeImageContentToTempFile(image, directory = tmpdir()) {
88
+ const extension = extensionForMimeType(image.mimeType);
89
+ await mkdir(directory, { recursive: true });
90
+ const path = join(directory, `${CLIPBOARD_FILE_PREFIX}${randomUUID()}.${extension}`);
91
+ await writeFile(path, Buffer.from(image.data, "base64"));
92
+ return path;
93
+ }
94
+ /** Reads the clipboard and returns the temporary path that should be inserted into the editor. */
95
+ export async function readClipboardImagePath(directory = tmpdir()) {
96
+ const image = await readClipboardImage();
97
+ return image ? writeImageContentToTempFile(image, directory) : null;
98
+ }
99
+ //# sourceMappingURL=clipboard-image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard-image.js","sourceRoot":"","sources":["../../src/core/clipboard-image.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Dirent } from \"node:fs\";\nimport { mkdir, readdir, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport type { ImageContent } from \"@di-code/ai\";\nimport { imageContentFromBytes } from \"./image-input.ts\";\n\ninterface ClipboardModule {\n\thasImage(): boolean;\n\tgetImageBinary(): Promise<Uint8Array | readonly number[]>;\n}\n\nconst CLIPBOARD_FILE_PREFIX = \"di-code-clipboard-\";\nconst CLIPBOARD_FILE_PATTERN = new RegExp(`^${CLIPBOARD_FILE_PREFIX}[0-9a-f-]+\\\\.(?:png|jpg|jpeg|gif|webp)$`, \"i\");\nconst DEFAULT_STALE_AGE_MS = 24 * 60 * 60 * 1000;\n\nconst require = createRequire(import.meta.url);\n\nfunction loadClipboardModule(): ClipboardModule | undefined {\n\ttry {\n\t\treturn require(\"@mariozechner/clipboard\") as ClipboardModule;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** Reads a native clipboard image when the optional platform package is available. */\nexport async function readClipboardImage(): Promise<ImageContent | null> {\n\tconst clipboard = loadClipboardModule();\n\tif (!clipboard?.hasImage()) return null;\n\tconst bytes = await clipboard.getImageBinary();\n\tif (bytes.length === 0) return null;\n\treturn imageContentFromBytes(Uint8Array.from(bytes), \"clipboard image\");\n}\n\nfunction extensionForMimeType(mimeType: string): string {\n\tswitch (mimeType.toLowerCase().split(\";\", 1)[0]) {\n\t\tcase \"image/jpeg\":\n\t\t\treturn \"jpg\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"gif\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"webp\";\n\t\tdefault:\n\t\t\treturn \"png\";\n\t}\n}\n\nexport function clipboardImageDirectory(root: string): string {\n\treturn join(root, \".di-code\", \"clipboard\");\n}\n\nexport function isClipboardImagePath(path: string, directory: string): boolean {\n\tconst absolutePath = resolve(path);\n\treturn dirname(absolutePath) === resolve(directory) && CLIPBOARD_FILE_PATTERN.test(basename(absolutePath));\n}\n\n/** Removes generated clipboard images that were left by an older process. */\nexport async function cleanupStaleClipboardImages(root: string, maxAgeMs = DEFAULT_STALE_AGE_MS): Promise<void> {\n\tconst directory = clipboardImageDirectory(root);\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(directory, { withFileTypes: true });\n\t} catch (cause) {\n\t\tif (cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\") return;\n\t\tthrow cause;\n\t}\n\tconst cutoff = Date.now() - maxAgeMs;\n\tawait Promise.all(\n\t\tentries\n\t\t\t.filter((entry) => entry.isFile() && CLIPBOARD_FILE_PATTERN.test(entry.name))\n\t\t\t.map(async (entry) => {\n\t\t\t\tconst path = join(directory, entry.name);\n\t\t\t\ttry {\n\t\t\t\t\tif ((await stat(path)).mtimeMs < cutoff) await unlink(path);\n\t\t\t\t} catch (cause) {\n\t\t\t\t\tif (!(cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\")) throw cause;\n\t\t\t\t}\n\t\t\t}),\n\t);\n}\n\nexport async function removeClipboardImage(path: string, directory: string): Promise<void> {\n\tif (!isClipboardImagePath(path, directory)) return;\n\ttry {\n\t\tawait unlink(path);\n\t} catch (cause) {\n\t\tif (!(cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\")) throw cause;\n\t}\n}\n\n/** Writes one clipboard image to a temporary file, matching Pi's editor flow. */\nexport async function writeImageContentToTempFile(image: ImageContent, directory = tmpdir()): Promise<string> {\n\tconst extension = extensionForMimeType(image.mimeType);\n\tawait mkdir(directory, { recursive: true });\n\tconst path = join(directory, `${CLIPBOARD_FILE_PREFIX}${randomUUID()}.${extension}`);\n\tawait writeFile(path, Buffer.from(image.data, \"base64\"));\n\treturn path;\n}\n\n/** Reads the clipboard and returns the temporary path that should be inserted into the editor. */\nexport async function readClipboardImagePath(directory = tmpdir()): Promise<string | null> {\n\tconst image = await readClipboardImage();\n\treturn image ? writeImageContentToTempFile(image, directory) : null;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { ImageContent } from "@di-code/ai";
2
+ export declare const MAX_IMAGE_INPUTS = 4;
3
+ export declare const MAX_IMAGE_INPUT_BYTES: number;
4
+ /** Converts already-read bytes into validated provider-neutral image content. */
5
+ export declare function imageContentFromBytes(data: Uint8Array, source: string): ImageContent;
6
+ /** Reads explicitly requested local image files into the provider-neutral user-message format. */
7
+ export declare function loadImageInputs(paths: readonly string[], cwd: string): Promise<ImageContent[]>;
8
+ /** Extracts @path image references while preserving a concise attachment label in the model prompt. */
9
+ export declare function extractImageAttachments(text: string, cwd: string): Promise<{
10
+ text: string;
11
+ images: ImageContent[];
12
+ }>;
13
+ //# sourceMappingURL=image-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-input.d.ts","sourceRoot":"","sources":["../../src/core/image-input.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,qBAAqB,QAAkB,CAAC;AAwCrD,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAOpF;AAkBD,kGAAkG;AAClG,wBAAsB,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAKpG;AAMD,uGAAuG;AACvG,wBAAsB,uBAAuB,CAC5C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACT,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC,CA4BnD"}
@@ -0,0 +1,102 @@
1
+ import { readFile, stat } from "node:fs/promises";
2
+ import { basename, resolve } from "node:path";
3
+ export const MAX_IMAGE_INPUTS = 4;
4
+ export const MAX_IMAGE_INPUT_BYTES = 5 * 1024 * 1024;
5
+ const IMAGE_REFERENCE = /(^|\s)@(?:"([^"\r\n]+)"|'([^'\r\n]+)'|([^\s@]+))/g;
6
+ function detectImageMimeType(data) {
7
+ if (data.length >= 8 &&
8
+ data[0] === 137 &&
9
+ data[1] === 80 &&
10
+ data[2] === 78 &&
11
+ data[3] === 71 &&
12
+ data[4] === 13 &&
13
+ data[5] === 10 &&
14
+ data[6] === 26 &&
15
+ data[7] === 10) {
16
+ return "image/png";
17
+ }
18
+ if (data.length >= 3 && data[0] === 255 && data[1] === 216 && data[2] === 255)
19
+ return "image/jpeg";
20
+ if (data.length >= 6 &&
21
+ (Buffer.from(data.subarray(0, 6)).equals(Buffer.from("GIF87a")) ||
22
+ Buffer.from(data.subarray(0, 6)).equals(Buffer.from("GIF89a")))) {
23
+ return "image/gif";
24
+ }
25
+ if (data.length >= 12 &&
26
+ Buffer.from(data.subarray(0, 4)).equals(Buffer.from("RIFF")) &&
27
+ Buffer.from(data.subarray(8, 12)).equals(Buffer.from("WEBP"))) {
28
+ return "image/webp";
29
+ }
30
+ return undefined;
31
+ }
32
+ function displayPath(path) {
33
+ return path.trim() || "<empty path>";
34
+ }
35
+ /** Converts already-read bytes into validated provider-neutral image content. */
36
+ export function imageContentFromBytes(data, source) {
37
+ if (data.byteLength > MAX_IMAGE_INPUT_BYTES) {
38
+ throw new Error(`Image exceeds the 5 MiB limit: ${displayPath(source)}`);
39
+ }
40
+ const mimeType = detectImageMimeType(data);
41
+ if (!mimeType)
42
+ throw new Error(`Unsupported image format: ${displayPath(source)}`);
43
+ return { type: "image", data: Buffer.from(data).toString("base64"), mimeType };
44
+ }
45
+ async function loadImageInput(path, cwd) {
46
+ if (path.trim().length === 0)
47
+ throw new Error("Image path must not be empty.");
48
+ const absolutePath = resolve(cwd, path);
49
+ const metadata = await stat(absolutePath);
50
+ if (!metadata.isFile())
51
+ throw new Error(`Image path is not a regular file: ${displayPath(path)}`);
52
+ if (metadata.size > MAX_IMAGE_INPUT_BYTES) {
53
+ throw new Error(`Image exceeds the 5 MiB limit: ${displayPath(path)}`);
54
+ }
55
+ const data = await readFile(absolutePath);
56
+ if (data.length > MAX_IMAGE_INPUT_BYTES) {
57
+ throw new Error(`Image exceeds the 5 MiB limit: ${displayPath(path)}`);
58
+ }
59
+ return imageContentFromBytes(data, path);
60
+ }
61
+ /** Reads explicitly requested local image files into the provider-neutral user-message format. */
62
+ export async function loadImageInputs(paths, cwd) {
63
+ if (paths.length > MAX_IMAGE_INPUTS) {
64
+ throw new Error(`At most ${MAX_IMAGE_INPUTS} images can be attached to one prompt.`);
65
+ }
66
+ return Promise.all(paths.map((path) => loadImageInput(path, cwd)));
67
+ }
68
+ function isMissingFileError(cause) {
69
+ return cause instanceof Error && "code" in cause && cause.code === "ENOENT";
70
+ }
71
+ /** Extracts @path image references while preserving a concise attachment label in the model prompt. */
72
+ export async function extractImageAttachments(text, cwd) {
73
+ const images = [];
74
+ const labels = [];
75
+ let result = "";
76
+ let cursor = 0;
77
+ for (const match of text.matchAll(IMAGE_REFERENCE)) {
78
+ const index = match.index ?? 0;
79
+ const leading = match[1] ?? "";
80
+ const path = match[2] ?? match[3] ?? match[4] ?? "";
81
+ result += `${text.slice(cursor, index)}${leading}`;
82
+ cursor = index + match[0].length;
83
+ if (images.length >= MAX_IMAGE_INPUTS) {
84
+ throw new Error(`At most ${MAX_IMAGE_INPUTS} images can be attached to one prompt.`);
85
+ }
86
+ try {
87
+ const [image] = await loadImageInputs([path], cwd);
88
+ if (!image)
89
+ throw new Error("Image attachment was not loaded.");
90
+ images.push(image);
91
+ labels.push(`[Attached image: ${basename(path)}]`);
92
+ }
93
+ catch (cause) {
94
+ if (isMissingFileError(cause))
95
+ throw new Error(`Image attachment was not found: ${path}`);
96
+ throw cause;
97
+ }
98
+ }
99
+ const prompt = `${result}${text.slice(cursor)}`.replace(/[ \t]{2,}/g, " ").trim();
100
+ return { text: [...labels, prompt].filter((value) => value.length > 0).join("\n"), images };
101
+ }
102
+ //# sourceMappingURL=image-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-input.js","sourceRoot":"","sources":["../../src/core/image-input.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, stat } from \"node:fs/promises\";\nimport { basename, resolve } from \"node:path\";\nimport type { ImageContent } from \"@di-code/ai\";\n\nexport const MAX_IMAGE_INPUTS = 4;\nexport const MAX_IMAGE_INPUT_BYTES = 5 * 1024 * 1024;\n\nconst IMAGE_REFERENCE = /(^|\\s)@(?:\"([^\"\\r\\n]+)\"|'([^'\\r\\n]+)'|([^\\s@]+))/g;\n\nfunction detectImageMimeType(data: Uint8Array): ImageContent[\"mimeType\"] | undefined {\n\tif (\n\t\tdata.length >= 8 &&\n\t\tdata[0] === 137 &&\n\t\tdata[1] === 80 &&\n\t\tdata[2] === 78 &&\n\t\tdata[3] === 71 &&\n\t\tdata[4] === 13 &&\n\t\tdata[5] === 10 &&\n\t\tdata[6] === 26 &&\n\t\tdata[7] === 10\n\t) {\n\t\treturn \"image/png\";\n\t}\n\tif (data.length >= 3 && data[0] === 255 && data[1] === 216 && data[2] === 255) return \"image/jpeg\";\n\tif (\n\t\tdata.length >= 6 &&\n\t\t(Buffer.from(data.subarray(0, 6)).equals(Buffer.from(\"GIF87a\")) ||\n\t\t\tBuffer.from(data.subarray(0, 6)).equals(Buffer.from(\"GIF89a\")))\n\t) {\n\t\treturn \"image/gif\";\n\t}\n\tif (\n\t\tdata.length >= 12 &&\n\t\tBuffer.from(data.subarray(0, 4)).equals(Buffer.from(\"RIFF\")) &&\n\t\tBuffer.from(data.subarray(8, 12)).equals(Buffer.from(\"WEBP\"))\n\t) {\n\t\treturn \"image/webp\";\n\t}\n\treturn undefined;\n}\n\nfunction displayPath(path: string): string {\n\treturn path.trim() || \"<empty path>\";\n}\n\n/** Converts already-read bytes into validated provider-neutral image content. */\nexport function imageContentFromBytes(data: Uint8Array, source: string): ImageContent {\n\tif (data.byteLength > MAX_IMAGE_INPUT_BYTES) {\n\t\tthrow new Error(`Image exceeds the 5 MiB limit: ${displayPath(source)}`);\n\t}\n\tconst mimeType = detectImageMimeType(data);\n\tif (!mimeType) throw new Error(`Unsupported image format: ${displayPath(source)}`);\n\treturn { type: \"image\", data: Buffer.from(data).toString(\"base64\"), mimeType };\n}\n\nasync function loadImageInput(path: string, cwd: string): Promise<ImageContent> {\n\tif (path.trim().length === 0) throw new Error(\"Image path must not be empty.\");\n\tconst absolutePath = resolve(cwd, path);\n\tconst metadata = await stat(absolutePath);\n\tif (!metadata.isFile()) throw new Error(`Image path is not a regular file: ${displayPath(path)}`);\n\tif (metadata.size > MAX_IMAGE_INPUT_BYTES) {\n\t\tthrow new Error(`Image exceeds the 5 MiB limit: ${displayPath(path)}`);\n\t}\n\n\tconst data = await readFile(absolutePath);\n\tif (data.length > MAX_IMAGE_INPUT_BYTES) {\n\t\tthrow new Error(`Image exceeds the 5 MiB limit: ${displayPath(path)}`);\n\t}\n\treturn imageContentFromBytes(data, path);\n}\n\n/** Reads explicitly requested local image files into the provider-neutral user-message format. */\nexport async function loadImageInputs(paths: readonly string[], cwd: string): Promise<ImageContent[]> {\n\tif (paths.length > MAX_IMAGE_INPUTS) {\n\t\tthrow new Error(`At most ${MAX_IMAGE_INPUTS} images can be attached to one prompt.`);\n\t}\n\treturn Promise.all(paths.map((path) => loadImageInput(path, cwd)));\n}\n\nfunction isMissingFileError(cause: unknown): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\";\n}\n\n/** Extracts @path image references while preserving a concise attachment label in the model prompt. */\nexport async function extractImageAttachments(\n\ttext: string,\n\tcwd: string,\n): Promise<{ text: string; images: ImageContent[] }> {\n\tconst images: ImageContent[] = [];\n\tconst labels: string[] = [];\n\tlet result = \"\";\n\tlet cursor = 0;\n\n\tfor (const match of text.matchAll(IMAGE_REFERENCE)) {\n\t\tconst index = match.index ?? 0;\n\t\tconst leading = match[1] ?? \"\";\n\t\tconst path = match[2] ?? match[3] ?? match[4] ?? \"\";\n\t\tresult += `${text.slice(cursor, index)}${leading}`;\n\t\tcursor = index + match[0].length;\n\t\tif (images.length >= MAX_IMAGE_INPUTS) {\n\t\t\tthrow new Error(`At most ${MAX_IMAGE_INPUTS} images can be attached to one prompt.`);\n\t\t}\n\t\ttry {\n\t\t\tconst [image] = await loadImageInputs([path], cwd);\n\t\t\tif (!image) throw new Error(\"Image attachment was not loaded.\");\n\t\t\timages.push(image);\n\t\t\tlabels.push(`[Attached image: ${basename(path)}]`);\n\t\t} catch (cause) {\n\t\t\tif (isMissingFileError(cause)) throw new Error(`Image attachment was not found: ${path}`);\n\t\t\tthrow cause;\n\t\t}\n\t}\n\n\tconst prompt = `${result}${text.slice(cursor)}`.replace(/[ \\t]{2,}/g, \" \").trim();\n\treturn { text: [...labels, prompt].filter((value) => value.length > 0).join(\"\\n\"), images };\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { type AgentListener } from "@di-code/agent";
2
- import type { AssistantMessage, Message, Model, Provider, Usage } from "@di-code/ai";
2
+ import type { AssistantMessage, ImageContent, Message, Model, Provider, Usage } from "@di-code/ai";
3
3
  import type { ExtensionHost } from "../extensions/runtime.ts";
4
4
  import type { SkillResource } from "./resources/types.ts";
5
5
  import type { SessionManager } from "./session/session-manager.ts";
@@ -76,6 +76,8 @@ export declare class AgentSession {
76
76
  get sessionDiagnostics(): readonly SessionDiagnostic[];
77
77
  get usage(): SessionUsage;
78
78
  prompt(text: string, signal?: AbortSignal): Promise<AssistantMessage>;
79
+ /** Sends one text prompt with explicitly supplied image attachments. */
80
+ promptWithImages(text: string, images: readonly ImageContent[], signal?: AbortSignal): Promise<AssistantMessage>;
79
81
  compact(signal?: AbortSignal): Promise<void>;
80
82
  subscribe(listener: AgentListener): () => void;
81
83
  subscribeSession(listener: AgentSessionListener): () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,aAAa,EAOlB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAe,MAAM,aAAa,CAAC;AAChH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,MAAM,WAAW,6BAA6B;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,6BAA6B,CAAC;IACpD,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACvC;AAED,MAAM,MAAM,iBAAiB,GAC1B,OAAO,gBAAgB,EAAE,UAAU,GACnC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACnG;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC;AAEjD,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtF,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAgB;IAC/C,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,sBAAsB,CAAU;IACxC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,WAAW,CAGjB;IACF,OAAO,CAAC,gBAAgB,CAAC,CAAU;IACnC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmC;gBAExD,OAAO,EAAE,mBAAmB;IA+ExC,IAAI,UAAU,IAAI,SAAS,OAAO,EAAE,CAEnC;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,eAAe,IAAI,SAAS,KAAK,EAAE,CAEtC;IAED,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK;IAchC,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IAED,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO;IAM/C,IAAI,WAAW,IAAI,MAAM,GAAG,SAAS,CAEpC;IAED,IAAI,kBAAkB,IAAI,SAAS,iBAAiB,EAAE,CAErD;IAED,IAAI,KAAK,IAAI,YAAY,CASxB;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI3E,wEAAwE;IAClE,gBAAgB,CACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,gBAAgB,CAAC;IAsBtB,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAI9C,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;YAK9C,WAAW;IAIzB,OAAO,CAAC,QAAQ;YAkBF,eAAe;YAef,UAAU;CA8CxB"}
@@ -162,6 +162,10 @@ export class AgentSession {
162
162
  };
163
163
  }
164
164
  async prompt(text, signal) {
165
+ return this.promptWithImages(text, [], signal);
166
+ }
167
+ /** Sends one text prompt with explicitly supplied image attachments. */
168
+ async promptWithImages(text, images, signal) {
165
169
  if (this.persistenceError !== undefined) {
166
170
  throw this.persistenceError;
167
171
  }
@@ -170,9 +174,13 @@ export class AgentSession {
170
174
  }
171
175
  this.promptActive = true;
172
176
  try {
177
+ if (images.length > 0 && !this.model.input.includes("image")) {
178
+ throw new Error(`Model "${this.model.id}" does not support image input.`);
179
+ }
173
180
  const prompt = await resolveSkillCommand(text, this.skills, signal);
174
- await this.compactIfNeeded(prompt, signal);
175
- return await this.agent.prompt(prompt, signal);
181
+ const content = [{ type: "text", text: prompt }, ...structuredClone([...images])];
182
+ await this.compactIfNeeded(content, signal);
183
+ return await this.agent.promptWithContent(content, signal);
176
184
  }
177
185
  finally {
178
186
  this.promptActive = false;
@@ -219,13 +227,13 @@ export class AgentSession {
219
227
  },
220
228
  };
221
229
  }
222
- async compactIfNeeded(text, signal) {
230
+ async compactIfNeeded(content, signal) {
223
231
  if (!this.compactionEnabledValue || !this.sessionManager)
224
232
  return;
225
233
  const context = buildSessionContext(this.sessionManager.entries);
226
234
  const pendingUser = {
227
235
  role: "user",
228
- content: [{ type: "text", text }],
236
+ content: structuredClone([...content]),
229
237
  timestamp: 0,
230
238
  };
231
239
  const estimatedTokens = estimateContextTokens([...context.messages, pendingUser]);