0
0
Fork 0
Commit graph

28 commits

Author SHA1 Message Date
Karstein Phobic Nyvold Kvistad
6ed62f1536 fix(deps): move 'diff' from devDependencies to dependencies (v0.9.1)
The TUI subpackage imports 'diff' at runtime (computeHunks in
src/tui/shared/diff.ts), so a global install via 'npm install -g'
was breaking with:

  Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'diff'
    imported from .../dist/tui/shared/diff.js

devDependencies are not installed by 'npm install -g <pkg>', only
'dependencies' are. Move 'diff' to the right section.

This bug shipped silently in 0.5.0 onwards because the source-build
flow during dev install everything, masking it. First reproduces
when a user does a fresh global install -- which is the only flow
the published package should support.

Bumps to 0.9.1; tag + republish required to fix the live install.
2026-04-29 17:53:11 +02:00
Karstein Phobic Nyvold Kvistad
c157267b20 release: v0.9.0 -- live-values: sub-property descent + configurable interval, plus SP22 librarymanager fix
Bumps codesys-mcp-sp21-plus from 0.8.0 to 0.9.0.

Headline changes since v0.8.0:

  Live values:
    - Pump now descends one level into user-defined types. A var
      whose declared type resolves to another mirror .st file (a
      POU/FB/STRUCT/DUT) gets each of its members read as
      <var>.<member>. Caller-supplied resolveTypeMirror dep so the
      pump stays decoupled from the mirror layout.
    - --live-values-interval <ms> CLI flag (default 500, clamped
      [100, 60000]).

  add_library:
    - SP22 librarymanager attribute name fix. The SP22 stub
      documents 'library_manager' (underscore) but the actual
      injected attribute is 'librarymanager' (one word). Now
      probes both with SP-version-aware preference + adds an
      SP22-specific find_library dispatcher that walks
      lm.repositories when the documented signature is rejected.
2026-04-29 14:23:11 +02:00
Karstein Phobic Nyvold Kvistad
0962f93118 release: v0.8.0 -- phobiCS-tui v0.3 (inline live values)
Bumps codesys-mcp-sp21-plus from 0.7.1 to 0.8.0 and phobiCS-tui's
--version output from v0.2.0 to v0.3.0.

Headline changes since v0.7.1:

  TUI:
    - Viewer overlays each declared variable's live runtime value
      inline next to its declaration when the runtime is online.
    - Viewer joins multi-line (* ... *) comments across line
      boundaries (lines fully inside a block no longer get
      false-highlighted keywords).

  MCP server:
    - new --live-values CLI flag starts a 500 ms pump that reads
      runtime values for the variables of the user's current POU
      selection and writes them to tui-live-values.json next to the
      existing tui-state.json.
    - --approve-edits now also gates 4 runtime tools:
      write_variable, download_to_device, start_stop_application,
      restart_runtime_ssh (in addition to the 9 modifying project
      tools landed in v0.7.0).

No tag created here; npm publish has to be run from your terminal
because the npm 2FA passkey can't be driven through the bash tool.
2026-04-29 10:10:40 +02:00
Karstein Phobic Nyvold Kvistad
58a1573b80 release: v0.7.1 -- add_library wrapper-message fix + mock_watcher PermissionError retry
Two bug fixes since v0.7.0:
  e948922 fix(add_library): wrapper message reflects dedup vs add branch
  d01f6ed fix(test): retry on PermissionError in mock_watcher (Win race)
2026-04-29 09:07:52 +02:00
Karstein Phobic Nyvold Kvistad
7e427e93c5 release: v0.7.0 -- phobiCS-tui v0.2 (TUI keybinds + 9-tool approve-gate)
Bumps codesys-mcp-sp21-plus to 0.7.0 (main was at 0.6.4 from
intermediate releases) and phobiCS-tui's --version output to v0.2.0.

Headline changes since v0.6.4:

  TUI:
    - browser keybinds: / (filter), o (open in editor), d (cross-
      device diff), r (rescan), ? (help overlay)
    - approve mode: v toggles unified <-> side-by-side diff
    - viewer: ST syntax highlighting (keywords/types/comments/strings)
    - statusbar: stale-mirror indicator + small-terminal resize warn

  MCP server:
    - --approve-edits now gates ALL 9 modifying tools, not just
      set_pou_code: create_pou, create_property, create_method,
      create_dut, create_gvl, create_folder, delete_object,
      rename_object, add_library

No tag created here; npm publish has to be run from your terminal
because the npm 2FA passkey can't be driven through the bash tool.
2026-04-29 08:23:57 +02:00
phobicdotno
065bce5b3d
Merge pull request #1 from phobicdotno/feature/phobics-tui
phobiCS-tui v0.1/v0.2: in-fork TUI for CODESYS exported ST + --approve-edits gate
2026-04-29 08:21:34 +02:00
Karstein Phobic Nyvold Kvistad
d4e71f61ca fix(compile_messages): discover real category GUIDs via system.get_message_categories()
The 0.6.3 fix replaced 'first-non-empty-pattern-wins' with category
iteration but used a HARDCODED list of 'well-known V3.5 GUIDs' that was
wrong. None of those GUIDs matched the actual Build category, so
compile errors stayed invisible (compile_project still reported '0
errors' even when the IDE-side download path saw them).

Diagnosed via a one-shot probe injected into compile_project on
2026-04-29: dumped attrs of script_engine.system, then called
script_engine.system.get_message_categories() (the METHOD) directly.
That returned the actual 7 category GUIDs in this CODESYS V3.5 SP22
Patch 1 install:

  05581bd1-66d3-4251-aff2-047cc8e9adf7  Offline Help
  936e1a33-3af8-47fa-b40b-903f0ae0b6cc  Application Composer
  a9b26e07-6ae1-4c06-9cd1-9ddddd397a2d  SVN
  0a6fcb64-7f24-43c6-a6d3-f70cb5d31114  (no parameterless ctor; Git)
  194b48a9-ab51-43ae-b9a9-51d3edaaddf3  Script Messages
  97f48d64-a2a3-4856-b640-75c046e37ea9  Build              <-- the one we needed
  220493a1-f49b-4416-9a3f-a545db707cbe  Additional code checks

Real fix: replace the hardcoded list in _enumerate_categories() in both
compile_project.py and get_compile_messages.py with a call to
system.get_message_categories(); label each one via
get_message_category_description(guid). Iterate per category as before.
GUIDs are now discovered at runtime so the same code works on any SP
and any locale.

Verification: injected `THIS_IS_NOT_VALID_IEC_KEYWORD;` into
PLC_PRG.implementation, ran compile_project; output now reads
"1 error(s), 2 warning(s). ERROR: Identifier
'THIS_IS_NOT_VALID_IEC_KEYWORD' not defined". Restored PLC_PRG;
0 error(s) again. End-to-end fix confirmed.

Bumped 0.6.3 -> 0.6.4.
2026-04-29 07:39:31 +02:00
Karstein Phobic Nyvold Kvistad
41818283f4 fix(compile_messages): probe every category + tighten _is_resolved hollow-ref check
Two real bugs surfaced today:

1. compile_project / get_compile_messages reported 0 errors even when
   the IDE's download path saw real compile errors. Root cause: both
   scripts queried get_message_objects() with no category arg, which
   returns only the IDE's last-active message tab (typically "Other"
   for the WATCHER startup messages). Build/Code-Generation errors
   live in a different category and were never queried.

   Fix: enumerate script_engine.system.message_categories AND probe a
   set of well-known V3.5 category GUIDs (Compile, Build, Online,
   LibMan); query target_app.get_message_objects(cat) and
   system.get_message_objects(cat) per category; aggregate dedup'd
   entries (severity, text, object, line). Each entry now carries its
   originating category label.

2. add_library's post-add _is_resolved() check trusted is_placeholder
   == False as proof of resolution. CODESYS lets you call
   add_placeholder(name_str) for a name that is NOT in the installed
   Library Repository -- the resulting reference reports
   is_placeholder=False yet has empty effective_version and the IDE
   shows it with a yellow-warning triangle in Library Manager (no
   Effective Version column populated). Karstein hit this with
   "OPC UA PubSub SL": list_project_libraries reported it as
   [managed] but compile failed because the IDE couldn't resolve it.

   Fix: for the non-placeholder branch, probe effective_version /
   resolved_version / version / resolved_library / managed_library /
   library. ALL must be empty/None for the ref to be considered
   hollow. Logs a DEBUG with the ref's attribute list so the next
   such bug is diagnosable without source spelunking.

Bumped 0.6.2 -> 0.6.3.
2026-04-28 23:35:24 +02:00
Karstein Phobic Nyvold Kvistad
e8f5a3a08f add_library: distinguish 'verified missing' from 'could not verify' in pre-flight
Yesterday's 0.6.1 fix refused on every miss from _resolve_in_repo,
including the case where the IDE-level library_manager global is not
injected into the script context (which is the actual situation when
running through the MCP's script execution channel rather than the
interactive script REPL). Result: false-negative refuses for libraries
that ARE installed, just not visible from this script context.

This commit factors out _resolve_in_repo_accessible() and uses it to
distinguish three outcomes:

  (a) Found in repo                  -> proceed; managed reference
  (b) Repo accessible, name NOT hit  -> HARD REFUSE (the bricking case)
                                        Opt-in via ALLOW_UNRESOLVED=1.
  (c) Repo NOT accessible at all     -> proceed; rely on the post-add
                                        _is_resolved() guard at line ~309
                                        to catch hollow placeholders.

The (c) path is the safe relaxation: we cannot prove the library is
missing, so we defer to the post-add check rather than refuse blindly.
Adds for installed libraries succeed normally, broken adds still get
caught and removed before save.

Verified live: add_library 'OPC UA PubSub SL' against MCPTest2.project
(library was installed via Tools > Library Repository) now succeeds
cleanly with reference 'OPC UA PubSub SL [managed] ns=OPC_UA_PubSub_SL'.
2026-04-28 23:14:22 +02:00
Karstein Phobic Nyvold Kvistad
3e9f4b8403 fix(add_library): refuse hollow placeholder when library not installed in repo
The pre-resolve via library_manager.find_library() at line 218 already
detected when the requested name was not present in the installed
library repository -- it logged "Pre-resolve... returned no hit" -- but
the script then proceeded to call add_placeholder(LIBRARY_NAME) without
a managed-lib argument anyway. CODESYS happily creates such a reference
with is_placeholder=False, so the post-add _is_resolved() guard returns
True and the project gets saved with a hollow reference. The next time
the project is opened, the IDE pops:

    Library Manager: Error: Could not open library 'X'.
    (Reason: The placeholder library 'X' could not be resolved.)

...and compile fails until the user manually deletes the bad reference
from the Library Manager.

Karstein hit this on 2026-04-28 trying to add "OPC UA Pub Sub" (the
real library is "OPC UA PubSub SL", an add-on SL package not present
in the stock V3.5 SP22 install). The script returned SCRIPT_SUCCESS,
list_project_libraries showed it as `[managed]`, and only on the next
set_pou_code call did the broken-placeholder error surface.

Fix: when _resolve_in_repo returns None, hard-refuse upfront with a
clear error pointing at the Library Repository / CODESYS Installer.
Opt-in via ALLOW_UNRESOLVED=1 (mapped to the new MCP arg
`allowUnresolved: true`) for the rare case where a placeholder for a
not-yet-installed library is genuinely wanted.

Tool description and arg docs updated to mark allowUnresolved as
DANGEROUS so future agent calls don't reach for it casually.

Bumped 0.6.0 -> 0.6.1.
2026-04-28 23:05:32 +02:00
Karstein Phobic Nyvold Kvistad
f34d002601 feat(online): pre-register Device User credentials to suppress modal login dialog
WHY: connect_to_device and download_to_device against a password-protected
runtime pop a modal "Device User Login" dialog in the IDE. IronPython can't
marshal to the WPF UI thread to dismiss it, so headless / agent-driven
sessions block forever -- and even for interactive use, the dialog pops on
EVERY download, which is a constant friction point.

API: ScriptOnline.set_default_credentials(username, password) was added in
CODESYS scripting API 3.5.3.0. Effect lasts until end of the current script
execution. Source: https://content.helpme-codesys.com/en/ScriptingEngine/ScriptOnline.html

Implementation:

- New helper script src/scripts/register_device_credentials.py defines a
  register_device_credentials_if_set() function that no-ops when DEVICE_USER
  or DEVICE_PASSWORD is empty, gracefully skips on older SPs that lack
  set_default_credentials, and never raises (always falls back to the
  current dialog-prompting behaviour).

- connect_to_device.py and download_to_device.py call the helper as the
  FIRST action inside their try blocks, before ensure_project_open and
  any login() attempt, so credentials are registered before any code path
  that could trigger the dialog.

- server.ts adds optional deviceUser / devicePassword args to both tools'
  input schemas. Resolution order:
    args.deviceUser     (per-call override)
    -> process.env.CODESYS_DEVICE_USER
    -> '' (empty, dialog pops as before)
  Same for devicePassword. Env-var path is the recommended config:
    claude mcp add -s user codesys-sp22-patch1 \
      -e CODESYS_DEVICE_USER=Karstein \
      -e CODESYS_DEVICE_PASSWORD=codesys123 \
      -- codesys-mcp-sp21-plus --codesys-path ... --codesys-profile ... \
         --mode persistent --no-auto-launch

Backward compat: when both creds are empty (default for existing users),
the helper short-circuits and behaviour is byte-identical to 0.5.x. No
regression. Verified by smoke-testing prepareScriptWithHelpers locally
with both filled and empty inputs -- function definition + callsite are
both wired in either case; only set_default_credentials() is suppressed
when empty.

README updated for connect_to_device and download_to_device tool rows.
Version bumped 0.5.0 -> 0.6.0.
2026-04-28 21:41:31 +02:00
Karstein Phobic Nyvold Kvistad
2ea39cb9a6 tui: scaffold phobiCS-tui ESM subpackage with ink 2026-04-28 20:52:02 +02:00
Karstein Phobic Nyvold Kvistad
722c09ed70 feat(ssh): restart_runtime_ssh -- restart codesyscontrol over SSH (password auth + sudo -S + port-listen liveness probe)
WHY: an unlicensed CODESYS Control runtime drops out of demo mode every 2
hours. systemctl is-active reports "active" even after the binary has
died, so a TCP probe on the runtime port (default 11740) is the only
honest liveness signal. The new tool gives MCP a one-call path to bring
the runtime back without dropping into a terminal.

Implementation choices:

- ssh2 (npm) instead of spawning ssh/sshpass: sshpass is not on the
  default Windows path, and the target Pi's sshd 10.x rejects pubkey
  signatures from this client environment in practice. ssh2 handles
  password auth + remote stdin + exit-code capture cross-platform.
- sudo -S with the password fed on remote stdin -- avoids a NOPASSWD
  sudoers entry on the PLC.
- After issuing the restart, polls 'ss -tln | grep :<port>' once per
  second until the listen port is up or livenessWaitSeconds expires.
  This is what catches a half-dead runtime that systemctl reports as
  fine.

Defaults match the only Pi we currently target (codesys-pi.local /
karstein / codesys123 / codesyscontrol / port 11740) but every field
is overridable.

Smoke-tested against codesys-pi.local: restart exit 0, port back up
after ~3s.
2026-04-28 16:50:19 +02:00
Karstein Phobic Nyvold Kvistad
4b54fbbd80 release: v0.4.9 -- --inspect, --ssh-version, --for-project, open_project pre-flight, per-project mirror dir, auto-mirror tool wraps, drop CODESYS Git 2026-04-27 22:33:45 +02:00
Karstein Phobic Nyvold Kvistad
f94e63ea8a release: v0.4.8 -- launcher allows multi-install (filter spawn-guard by exe path) 2026-04-27 20:58:00 +02:00
Karstein Phobic Nyvold Kvistad
1145fdc32e release: v0.4.7 -- drop broken postinstall, document the on-demand --print-config flow 2026-04-27 20:46:46 +02:00
Karstein Phobic Nyvold Kvistad
c7f103d3fd revert: drop postinstall banner -- npm 7+ buffers script stdout by design
Per https://docs.npmjs.com/cli/v11/using-npm/scripts, since npm@7 the
preinstall/install/postinstall scripts run in the background with stdout
AND stderr captured. The only way to surface them is the
`--foreground-scripts` opt-in flag (default false). Three failed
publish cycles (0.4.4, 0.4.5, 0.4.6) couldn't get around this -- the
limitation is by design, not a bug.

Removing src/postinstall.ts and the postinstall script entry from
package.json. The functionality (printing the .mcp.json snippet) is
still available -- it just runs on demand via:

  codesys-mcp-sp21-plus --print-config

Updated the README Quick Start to a numbered 4-step flow:
  1. npm install -g codesys-mcp-sp21-plus
  2. codesys-mcp-sp21-plus --print-config
  3. paste into project- or user-scoped .mcp.json
  4. restart Claude Code

Plus a footnote explaining why no banner -- so a future maintainer
doesn't try to re-add postinstall and waste another publish cycle.
2026-04-27 20:46:45 +02:00
Karstein Phobic Nyvold Kvistad
c1eabf08a9 release: v0.4.6 -- postinstall actually prints when installing from a repo cwd 2026-04-27 20:40:03 +02:00
Karstein Phobic Nyvold Kvistad
5c756147c1 release: v0.4.5 -- postinstall banner actually prints (npm 11+) 2026-04-27 20:37:29 +02:00
Karstein Phobic Nyvold Kvistad
7f7abaf996 release: v0.4.4 -- postinstall now tells you WHERE to paste the snippet 2026-04-27 20:32:31 +02:00
Karstein Phobic Nyvold Kvistad
3d75548ba9 release: v0.4.3 -- postinstall banner with .mcp.json snippet 2026-04-27 20:31:22 +02:00
Karstein Phobic Nyvold Kvistad
b9c1df25b8 feat(postinstall): dump full .mcp.json snippet after global npm install
Runs after `npm install -g codesys-mcp-sp21-plus`. Detects every
CODESYS install on PATH and prints the ready-to-paste .mcp.json
block per install (same output as `--print-config`).

Guards:
- Skipped during local installs / dev clones (npm_config_global != true)
- Skipped in CI (CI=true or npm_config_ci=true)
- Wrapped in try/catch + 'node ... || true' so a banner failure never
  blocks the install
- Non-Windows: prints a note and exits cleanly
- Zero CODESYS installs: prints a hint pointing at --print-config

Resolves the awkward 'now run these two commands to verify and get
your config' step from the README.
2026-04-27 20:31:21 +02:00
Karstein Phobic Nyvold Kvistad
a11c58062f release: v0.4.2 -- --print-config CLI for auto-generated .mcp.json snippets 2026-04-27 20:23:59 +02:00
Karstein Phobic Nyvold Kvistad
fda997e546 release: v0.4.1 -- README restructure (fork notice, what's-new, npm install) 2026-04-26 22:48:21 +02:00
Karstein Phobic Nyvold Kvistad
405be44a58 rename: Codesys-MCP-SP22+ -> Codesys-MCP-SP21+ + multi-install README + missing tools
Project rename. The 'SP21+' identifier is more accurate than the
previous 'SP22+' label -- this fork specifically carries the SP21+
migration fixes (the upstream's system.execute_on_primary_thread()
removal and downstream API drift), and is forward-compat with later
SPs. The repo on GitHub has been renamed to
phobicdotno/Codesys-MCP-SP21-plus accordingly.

Substitutions (UTF-8 preserved this time -- prior PowerShell pass
mangled em-dashes via a Win-1252 round-trip):
- package.json: name codesys-mcp-sp22-plus -> codesys-mcp-sp21-plus,
  bin entry, repository.url, homepage, description, author trailer
- README.md: title, banner, install + clone snippets, CLI invocations
- ARCHITECTURE.md: comparison-table column header + temp-dir prefix
- tests/TEST_OVERVIEW.md: title
- src/bin.ts: header comment + program().name()
- src/launcher.ts: SESSION_DIR_PREFIX
- src/types.ts: header comment

Compatibility phrase 'Works on SP19, SP21, and SP22+' and the technical
identifier 'SP22 Patch 1 fixes' (which name the actual CODESYS version)
were preserved -- those refer to CODESYS, not the project name.

README additions:
- Quick Start example switched from SP21 Patch 3 to SP22 Patch 1 to
  match the more common modern install
- New 'Multiple CODESYS installations' subsection with worked
  side-by-side example for SP21 (3.5.21.50) and SP22 (3.5.22.10) --
  one named MCP server entry per install, called by name from Claude.
  Documents the constraint that --codesys-path/--codesys-profile are
  bound at server startup, that --detect lists installs, and that
  config edits require a Claude Code restart
- Tool count corrected from 28 -> 37 in the Features bullets
- New Tools sections covering 10 previously-undocumented tools:
  Version Anchor + Release Pipeline (bump_project_version,
  release_project_version, read_running_version_online), Source
  Mirror (mirror_export), and CODESYS Git PDE-gated (git_init,
  git_status, git_commit, git_remote_add, git_branch_set_upstream_to,
  git_push). Each row carries the actual operational gotchas
  discovered during this session (UNC localRepoPath rejection,
  push-without-upstream failure, optimizer stripping unreferenced
  globals from the online symbol table, etc.)
- list_project_libraries + add_library row text updated to reflect
  the post-fix behaviour (compiler-version capture; managed-overload
  preference + resolution gate)

Local origin URL updated: phobicdotno/Codesys-MCP -> phobicdotno/Codesys-MCP-SP21-plus.

37/37 tests green.
2026-04-26 22:02:46 +02:00
Karstein Phobic Nyvold Kvistad
f157e7b61e rename: project -> Codesys-MCP-SP22+
Project identity rename. Concrete changes:

- package.json
  - name: codesys-mcp-persistent -> codesys-mcp-sp22-plus (npm-safe;
    '+' is not allowed in npm package names)
  - description: prepended 'Codesys-MCP-SP22+' branding + brief
    summary of the SP22+ fixes carried in this fork
  - bin entry: codesys-mcp-persistent -> codesys-mcp-sp22-plus
  - repository.url + homepage: now point at
    github.com/phobicdotno/Codesys-MCP-SP22-plus (using -plus
    spelling on the URL side; '+' would require URL-encoding)
  - author: noted upstream + SP22+ fork attribution
- README.md: title + about-this-fork blurb + clone instructions
- tests/TEST_OVERVIEW.md: title
- ARCHITECTURE.md: comparison-table column header + temp-dir path
- src/bin.ts: file header + program().name()
- src/launcher.ts: SESSION_DIR_PREFIX (with note on backwards compat)
- src/types.ts: file header

Display name everywhere: 'Codesys-MCP-SP22+'.
npm package name: 'codesys-mcp-sp22-plus'.

Out of scope for this commit -- need user action:
- Local directory rename (C:/Users/karstein.kvistad/Codesys-MCP ->
  ...-SP22+) cannot be done while the persistent MCP is using dist/
  from the current path; user can rename after shutting down VS Code.
- GitHub repo rename (phobicdotno/Codesys-MCP -> ...-SP22-plus or
  ...-SP22+) needs the github.com web UI; the package.json URLs
  already point at the post-rename name so a 'git remote set-url
  origin <new>' lines up after.
- MCP client config in .mcp.json or similar: the bin name changed
  from 'codesys-mcp-persistent' to 'codesys-mcp-sp22-plus' -- update
  any 'command' field that referenced the old name. (Local sessions
  invoking node dist/bin.js directly are unaffected.)

37/37 tests still green after rename.
2026-04-26 20:42:15 +02:00
Luke
aad246576c Add 17 new MCP tools for v0.4.0: compiler diagnostics, project authoring, runtime monitoring, library management
- Phase 1: Structured compiler diagnostics — compile_project now returns parsed errors/warnings with object name and line number; new get_compile_messages tool reads last build messages without recompiling
- Phase 2: Project authoring — create_dut, create_gvl, create_folder, delete_object, rename_object, get_all_pou_code
- Phase 3: Online/runtime — connect_to_device, disconnect_from_device, get_application_state, read_variable, write_variable, download_to_device, start_stop_application (with ensure_online_connection helper)
- Phase 4: Library management — list_project_libraries, add_library
- Bump version to 0.4.0, update README with full tool reference

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 21:05:12 +10:00
Luke
e374519fe1 Initial release: MCP server for CODESYS with persistent UI instance
v0.3.0 returning-watcher architecture — background thread polls for commands
and marshals execution onto the CODESYS UI thread, keeping the IDE fully
responsive between operations. File-based IPC with atomic writes, async mutex
command serialization, headless fallback, and 35 passing tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:59:16 +10:00