Yes, you can do this.
First, you want to change your checkboxes to reflect the current value of those variables. So that code would look like this:
<<checkbox "$skills.sword" false true `$skills.sword ? 'checked' : ''`>> Sword
<<checkbox "$skills.bow" false true `$skills.bow ? 'checked' : ''`>> Bow
<<checkbox "$skills.magic" false true `$skills.magic ? 'checked' : ''`>> Magic
The backticks/backquotes (the accent mark from the tilde ~ key) cause what is inside to be evaluated, and the result from that will be used.
The "conditional ? t : f" format basically translates as: "if the conditional is truthy, return t, otherwise return f." So if "$skills.sword" is true, then the "checked" parameter will be set.
The next thing you need to know is that a SugarCube checkbox gets its ID from the variable name. For story variables (the ones that start with "$") that will be "checkbox-" followed by the lowercase version of the variable name with any dots removed. For temporary variables (the ones that start with "_") it's almost the same, except there is a second "-".
This means that a checkbox that sets "$skills.bow" will have an ID of "checkbox-skillsbow", and a checkbox that sets "_VariableX" will have an ID of "checkbox--variablex".
Now that you know the ID, you can use that to add a listener to the checkbox. jQuery makes this really easy, though it's a bit trickier because you're using Twine. The elements on a page don't always exist in Twine, so you have to wait until they do to attach the event handler. This should done in the :passagerender event, like this:
$(document).on(':passagerender', function (ev) {
if (passage() == "Start") {
$(ev.content).find("#checkbox-skillssword").on('propertychange change', function() { Engine.play(passage()); } );
$(ev.content).find("#checkbox-skillsbow").on('propertychange change', function() { Engine.play(passage()); } );
$(ev.content).find("#checkbox-skillsmagic").on('propertychange change', function() { Engine.play(passage()); } );
}
});
(That code belongs in the JavaScript section.)
This first checks to see if it's the correct passage, using the passage() function to determine what passage we're currently in. You will need to change "Start" to the name of your passage where this is needed.
Then, if it's the correct passage, it uses jQuery to take the content returned to this function ("ev.content"), find the checkboxes within that content, and add a function to each of them that triggers on "input", "propertychange", or "change" events (which should work for detecting changes on most input elements). That function uses the Engine.play() function to play the current passage again, which will update the passage and add it to the history. You can add ", true" to the Engine.play() parameters if you don't want that to add to the history.
If those are the only checkboxes in the passage, you can simplify that code by simply connecting to all checkboxes like this:
$(document).on(':passagerender', function (ev) {
if (passage() == "Start") {
$(ev.content).find("[type=checkbox]").on('propertychange change', function() { Engine.play(passage()); } );
}
});
If you have any questions about how that works, feel free to ask.
Hope that helps!
(NOTE: Edited to remove "input" event from listener.)