{"id":2615,"date":"2026-02-05T09:29:08","date_gmt":"2026-02-05T13:29:08","guid":{"rendered":"https:\/\/lowtek.ca\/roo\/?p=2615"},"modified":"2026-04-06T17:52:00","modified_gmt":"2026-04-06T21:52:00","slug":"generative-ai-code-assist","status":"publish","type":"post","link":"https:\/\/lowtek.ca\/roo\/2026\/generative-ai-code-assist\/","title":{"rendered":"Generative AI code assist"},"content":{"rendered":"<p><a href=\"https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/ai-coding.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-2616\" src=\"https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/ai-coding-500x260.jpg\" alt=\"\" width=\"500\" height=\"260\" srcset=\"https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/ai-coding-500x260.jpg 500w, https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/ai-coding.jpg 600w\" sizes=\"auto, (max-width: 500px) 85vw, 500px\" \/><\/a>I considered use &#8220;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Vibe_coding\">Vibe Coding<\/a>&#8221; as the title, but it&#8217;s just such a buzz word that I decided to go with a more factual title. I&#8217;m old school enough to want to distinguish between <a href=\"https:\/\/en.wikipedia.org\/wiki\/Generative_artificial_intelligence\">generative AI<\/a> and the more broad AGI (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Artificial_general_intelligence\">Artificial General Intelligence<\/a>). I&#8217;ll also state that I consider myself a bit of an AI coding skeptic, but hopefully in a healthy way.<\/p>\n<p>Just like any computer program, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Garbage_in,_garbage_out\">garbage-in, garbage-out<\/a>. The modern buzz word for this is <a href=\"https:\/\/en.wikipedia.org\/wiki\/AI_slop\">AI-slop<\/a>. I&#8217;ll avoid bashing the technology and focus on how you can use it constructively today, even with some of it&#8217;s limitations. I will also confess that at work I&#8217;ve got access to AI for code generation and it&#8217;s been interesting learning a new set of skills, this post will focus on what you can do for free on the web.<\/p>\n<p>Perchance has a no login required, free <a href=\"https:\/\/perchance.org\/ai-code-generator\">code generator<\/a>. I was in the process of setting up <a href=\"https:\/\/karakeep.app\/\">karakeep<\/a> to replace <a href=\"https:\/\/wallabag.org\/\">wallabag<\/a>. Both of these tools perform a similar function, effectively a web based bookmark manager and offline capture of a web resource. A simple list of links + <a href=\"https:\/\/archive.org\/\">archive.org<\/a> would solve the same problem, but this is a self-hosted solution and is pretty neat.<\/p>\n<p>The task at hand is to figure out how to export all of my links from wallabag and the import them into karakeep, the more context I can preserve the better. Since there isn&#8217;t a common import\/export format between the two tools, we&#8217;ll use the aforementioned code generator to create something to convert the file.<\/p>\n<p>Luckily both support a <a href=\"https:\/\/en.wikipedia.org\/wiki\/JSON\">JSON<\/a> based format. I can export from wallabag into JSON. It looks something like this<\/p>\n<pre class=\"lang:default decode:true\">[\r\n  {\r\n    \"is_archived\": 1,\r\n    \"is_starred\": 0,\r\n    \"tags\": [],\r\n    \"is_public\": false,\r\n    \"id\": 402,\r\n    \"title\": \"BMW\u2019s \u201cProject i\u201d: How the i3 and i8 Sparked BMW\u2019s EV Future\",\r\n    \"url\": \"https:\/\/www.bmwblog.com\/2025\/07\/30\/bmw-project-i-electric-car-history\/\",\r\n    \"origin_url\": \"https:\/\/share.google\/61cfoqzvMerCTVuVy\",\r\n    \"given_url\": \"https:\/\/share.google\/61cfoqzvMerCTVuVy\",\r\n    \"archived_at\": \"2025-08-04T22:44:33+00:00\",\r\n    \"content\": \"&lt;p&gt;The \u201cNeue Klasse\u201d secured BMW\u2019s future.\",\r\n    \"created_at\": \"2025-08-04T22:44:33+00:00\",\r\n    \"updated_at\": \"2025-08-04T22:44:33+00:00\",\r\n    \"published_at\": \"2025-07-30T20:27:25+00:00\",\r\n    \"published_by\": [\r\n      \"Hugo Becker\"\r\n    ],\r\n    \"annotations\": [],\r\n    \"mimetype\": \"text\/html; charset=UTF-8\",\r\n    \"language\": \"en_US\",\r\n    \"reading_time\": 7,\r\n    \"domain_name\": \"www.bmwblog.com\",\r\n    \"preview_picture\": \"https:\/\/cdn.bmwblog.com\/wp-content\/uploads\/2017\/09\/BMW-i3-i3s-i8-03.jpg\",\r\n    \"http_status\": \"200\",\r\n    \"headers\": {\r\n      \"server\": \"nginx\",\r\n      \"date\": \"Mon, 04 Aug 2025 22:44:32 GMT\",\r\n      \"content-type\": \"text\/html; charset=UTF-8\",\r\n      \"transfer-encoding\": \"chunked\",\r\n      \"connection\": \"keep-alive\",\r\n      \"link\": \"&lt;https:\/\/www.bmwblog.com\/wp-json\/&gt;; rel=\\\"https:\/\/api.w.org\/\\\", &lt;https:\/\/www.bmwblog.com\/wp-json\/wp\/v2\/posts\/503323&gt;; rel=\\\"alternate\\\"; title=\\\"JSON\\\"; type=\\\"application\/json\\\", &lt;https:\/\/www.bmwblog.com\/?p=503323&gt;; rel=shortlink\",\r\n      \"vary\": \"Accept-Encoding, Cookie\",\r\n      \"last-modified\": \"Mon, 04 Aug 2025 22:03:42 GMT\",\r\n      \"x-presslabs-stats\": \"HIT; 0.375s; 21 queries; desktop; ttl 7200s; refresh in 4750s\",\r\n      \"x-request-id\": \"1bb113ca395f884e7a8c04abac9ac38b\",\r\n      \"strict-transport-security\": \"max-age=31536000; preload\"\r\n    }\r\n  },<\/pre>\n<p>And karakeep has both an import\/export supported into JSON format<\/p>\n<p><a href=\"https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/karakeep.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-2617\" src=\"https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/karakeep-500x129.jpg\" alt=\"\" width=\"500\" height=\"129\" srcset=\"https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/karakeep-500x129.jpg 500w, https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/karakeep-1024x265.jpg 1024w, https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/karakeep-768x199.jpg 768w, https:\/\/lowtek.ca\/roo\/wp-content\/uploads\/2026\/02\/karakeep.jpg 1090w\" sizes=\"auto, (max-width: 500px) 85vw, 500px\" \/><\/a>First we will create a few sample entries in karakeep and do an export to figure out what it&#8217;s format is. It turns out to look something like this:<\/p>\n<pre class=\"lang:default decode:true \">{\r\n  \"bookmarks\": [\r\n    {\r\n      \"createdAt\": 1769880398,\r\n      \"title\": \"The 75 Best Sci-Fi Books of All Time\",\r\n      \"tags\": [],\r\n      \"content\": {\r\n        \"type\": \"link\",\r\n        \"url\": \"https:\/\/www.esquire.com\/entertainment\/books\/g39358054\/best-sci-fi-books\/\"\r\n      },\r\n      \"note\": null,\r\n      \"archived\": false\r\n    },<\/pre>\n<p>If you look at the two formats, you can see some obvious mappings. This is good. I started with the perchance code generator and a very simple prompt:<\/p>\n<pre class=\"lang:default decode:true\">write some nodejs code to read a file that \r\ncontains JSON and output to the console<\/pre>\n<p>This let me get my feet wet, and make sure I had my environment setup to run code etc. I do have reasonable javascript experience, and that will help me use the code generator as a tool to move quickly. I tend to think of most of these AI solutions as doing pattern matching, they pick the &#8216;shape&#8217; of your solution and fill in the blanks &#8211; this is also where they will make stuff up if there is a blank and you haven&#8217;t given it enough context, it&#8217;ll just guess at a likely answer.<\/p>\n<p>Once I had the code generator creating code, and I was able to test it, things moved along fairly quickly. I iterated forwards specifying the output format JSON etc.. and I was both a bit amazed, but pleased to see that it had decided to use the <span class=\"codespan__pre-wrap\"><code>map<\/code><\/span> <a class=\"theme markdown__link\" href=\"https:\/\/stackoverflow.com\/questions\/14810506\/map-function-for-objects-instead-of-arrays\" target=\"_blank\" rel=\"noopener noreferrer\">capability<\/a> in nodejs. This made the generated code quite simple.<\/p>\n<p>My final prompt ended up being:<\/p>\n<pre class=\"lang:default decode:true\">nodejs code to read json file \"data.json\" as input. \r\nThe input file is an array of objects.\r\n\r\nFor every object in the input array, take the field \"given_url\" and map it\r\nto the field \"url\" of an output object as described below.\r\n\r\nRead the \"created_at\" field of the input which will be in this format\r\n\"2025-08-04T22:44:33+00:00\" and convert it to a \"createdAt\" timestamp \r\nin the output object\r\n\r\nThe output is a json object, which is an array called \"bookmarks\". In the \r\narray is a series of json objects which have the following format\r\n\r\n   { \r\n      \"createdAt\": 1769880398,\r\n      \"title\": \"a title\",\r\n      \"tags\": [],\r\n      \"content\": {\r\n        \"type\": \"link\",\r\n        \"url\": \"https:\/\/www.example.com\"\r\n      },\r\n      \"note\": null,\r\n      \"archived\": false\r\n    }\r\n\r\nPrint the complete output json object once done.<\/pre>\n<p>And this is the code it generated<\/p>\n<pre class=\"lang:default decode:true \">const fs = require('fs');\r\n\r\nconst data = JSON.parse(fs.readFileSync('data.json', 'utf-8'));\r\n\r\nconst bookmarks = data.map(item =&gt; {\r\n  const createdAt = Math.floor(new Date(item.created_at).getTime() \/ 1000);\r\n  \r\n  return {\r\n    createdAt,\r\n    title: item.title || '',\r\n    tags: item.tags || [],\r\n    content: {\r\n      type: 'link',\r\n      url: item.given_url\r\n    },\r\n    note: null,\r\n    archived: false\r\n  };\r\n});\r\n\r\nconsole.log(JSON.stringify({ bookmarks }, null, 2));<\/pre>\n<p>Notice anything curious here? It has, without me saying anything, decide to map <span class=\"codespan__pre-wrap\"><code>title<\/code><\/span> and <span class=\"codespan__pre-wrap\"><code>tags<\/code><\/span> into the output object. Very nice &#8211; I&#8217;m impressed.<\/p>\n<p>Was there any really smartness here? Well, I would not have arrived at the idea of using <span class=\"codespan__pre-wrap\"><code>map<\/code><\/span> in the javascript code &#8211; it&#8217;s the right and elegant solution. A stronger javascript developer would have likely landed here since it is a concise solution to the problem. Maybe I would have found a similar answer on stackoverflow, but the code generator made it easy for me.<\/p>\n<p>The date manipulation is also very slick.<\/p>\n<pre class=\"lang:default decode:true\">const createdAt = Math.floor(new Date(item.created_at).getTime() \/ 1000);<\/pre>\n<p>I would have eventually got there, but it just did it for me. A very nice time saver.<\/p>\n<div class=\"post__body\">\n<div class=\"AutoHeight\">\n<div>\n<div class=\"post-message post-message--collapsed\">\n<div class=\"post-message__text-container\">\n<div id=\"rhsPostMessageText_qw4s6xefgt858mumms5qs6w88w\" class=\"post-message__text\" dir=\"auto\" tabindex=\"0\">\n<p>This generated javascript let me export \/ import 376 entries from my wallabag &#8212; preserving the original dates, tags and titles.<\/p>\n<p>Sometimes working with AI for code is like having a book smart, and very fast, new hire. No experience, lots of enthusiasm, and cranks out code quickly. Does the code work? Not always, maybe not even often. I&#8217;ve also had to &#8216;reset&#8217; the approach being used, when multiple iterations uncovered that it was basically impossible to solve the problem using the approach that I started with. Using test driven development can help provide guide rails for &#8216;working \/ not working&#8217;, the more context you can provide the better. Learning how to guide the AI, and evaluate if you&#8217;re getting what you intended to ask for are the new skills I&#8217;ve been growing.<\/p>\n<\/div>\n<p>I feel I do need to throw down some caution flags around AI use. If you&#8217;re using something that is &#8216;free&#8217;, <a href=\"https:\/\/www.reddit.com\/r\/explainlikeimfive\/comments\/2m3f05\/eli5_if_something_is_free_you_are_the_product\/\">think<\/a> <a href=\"https:\/\/pluralistic.net\/2022\/11\/14\/luxury-surveillance\/#liar-liar\">again<\/a> why are they making it free to use? Open source projects, <a href=\"https:\/\/www.cyber.gc.ca\/en\/guidance\/security-considerations-when-using-open-source-software-itsap10059\">don&#8217;t mean that it&#8217;s safe<\/a>. Under the covers, this is all still built out of the same parts &#8211; so if you use it to open your network and data to the internet, you&#8217;ve got the same <a href=\"https:\/\/www.xda-developers.com\/please-stop-using-openclaw\/\">security problems<\/a>.<\/p>\n<div id=\"rhsPostMessageText_qw4s6xefgt858mumms5qs6w88w\" class=\"post-message__text\" dir=\"auto\" tabindex=\"0\">\n<p>Interesting times.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>I considered use &#8220;Vibe Coding&#8221; as the title, but it&#8217;s just such a buzz word that I decided to go with a more factual title. I&#8217;m old school enough to want to distinguish between generative AI and the more broad AGI (Artificial General Intelligence). I&#8217;ll also state that I consider myself a bit of an &hellip; <a href=\"https:\/\/lowtek.ca\/roo\/2026\/generative-ai-code-assist\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Generative AI code assist&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23,6,19],"tags":[],"class_list":["post-2615","post","type-post","status-publish","format-standard","hentry","category-ai","category-computing","category-musings"],"_links":{"self":[{"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/posts\/2615","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/comments?post=2615"}],"version-history":[{"count":6,"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/posts\/2615\/revisions"}],"predecessor-version":[{"id":2623,"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/posts\/2615\/revisions\/2623"}],"wp:attachment":[{"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/media?parent=2615"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/categories?post=2615"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lowtek.ca\/roo\/wp-json\/wp\/v2\/tags?post=2615"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}